Skip to content

Norito

Norito 是 Iroha 的规范序列化层。当对等节点、SDKs、CLI 工具、Torii、Kura 和生成的工件必须对完全相同的载荷达成一致时,使用的就是这种字节格式。

当数据涉及共识、签名、哈希、持久化或跨 SDK 互操作性时,请使用 Norito。只有在端点明确为运维人员、仪表板或快速调试提供人类可读投影时,才使用 JSON。

Norito 的使用位置

接口Norito 的用途
交易和查询通过 Torii 提交的已签名交易和查询载荷编码为 Norito。
创世区块kagami genesis sign 生成已签名的 .nrt 区块,供对等节点在启动时加载。
Torii 类型化响应支持类型化二进制响应的端点使用 Accept: application/x-norito
SDKsRust、Python、JavaScript、Kotlin/Java、Swift 和 Android 客户端使用 Norito 构建器或绑定,而不是手动组装字节。
Kura 存储区块载荷、恢复辅助文件、名册和提交标记以 Norito 帧数据的形式存储。
清单Nexus、数据可用性、SoraFS、流媒体和面向应用的清单在需要签名或哈希时使用 Norito。
流媒体Norito Streaming 使用 Norito 清单、分段头、控制帧和一致性测试夹具。

Norito 不是智能合约语言。它是承载交易、合约调用、清单和类型化 API 载荷的确定性信封与编解码器。

载荷模型

每个在线传输或磁盘存储的 Norito 载荷都由一个头部成帧,后跟编码后的载荷字节。无头部的裸载荷仅保留用于内部哈希、基准测试,以及会在传输前立即为结果添加头部的辅助 APIs。

头部字段大小用途
Magic4 字节ASCII NRT0,用于尽早拒绝非 Norito 数据。
Major1 字节格式主版本;当前载荷使用 0
Minor1 字节v1 的解码提示;当前值为 0x00。布局由 Flags 描述。
Schema hash16 字节类型标识,供类型化解码器拒绝意外的载荷类型。
Compression1 字节0 = None1 = Zstd;未知值会被拒绝。
Payload length8 字节未压缩载荷的长度,以小端序 u64 表示。
CRC648 字节未压缩载荷的 CRC64-XZ 校验和。
Flags1 字节紧凑长度、打包序列和打包结构的布局标志。

头部共 40 字节。解码器在重建类型化值之前,会验证 Magic、版本、受支持的标志掩码、载荷长度、校验和及模式哈希。

布局标志

Norito 将布局选项存储在头部的最后一个字节中。默认 v1 辅助函数发出 COMPACT_LEN0x02),为每个值使用紧凑长度前缀。调用方以 flags = 0x00 编码时,显式固定宽度长度前缀仍然可读。

标志十六进制状态效果
PACKED_SEQ0x01支持使用偏移表和连续数据块对大小可变的集合进行编码。
COMPACT_LEN0x02默认每个值的长度前缀使用规范无符号 varint。
PACKED_STRUCT0x04支持将 derive 生成的结构编码为打包字段载荷。
VARINT_OFFSETS0x08保留v1 中会被拒绝;打包序列偏移量是固定宽度 u64
COMPACT_SEQ_LEN0x10保留v1 中会被拒绝;顶层序列长度头为固定宽度 u64
FIELD_BITSET0x20有条件支持为打包结构添加位集,使只有需要显式大小的字段才携带大小前缀。要求同时设置 PACKED_STRUCTCOMPACT_LEN

这些标志是显式的。解码器不会根据载荷形状、次版本或启发式方法推断布局。未知或无效的组合会被拒绝,从而确保所有对等节点以相同方式解释载荷。

编码规则

Norito 对 Iroha 数据模型中的常见数据形状使用确定性布局:

  • 字符串格式为 [len][utf8-bytes];启用 COMPACT_LEN 时,len 遵循该标志。
  • 设置 COMPACT_LEN 时,每个值的长度使用紧凑 varint。
  • 未设置 COMPACT_LEN 时,每个值的长度是 8 字节小端序 u64
  • v1 的序列长度头是固定 8 字节小端序 u64
  • Vec<u8> 编码为 [len_u64][raw-bytes],而不是为每个字节各写一个长度。
  • 打包序列使用 (len + 1) 个单调递增的 u64 偏移量,后跟拼接的元素载荷。
  • 映射以固定 u64 编码条目数,并使用确定性的键顺序。HashMap 条目在编码前按键排序;BTreeMap 使用其自然顺序。
  • BigInt 使用小端序二进制补码字节,以 u32 表示字节长度,上限为 512 位。
  • Numeric 编码为 (mantissa, scale);mantissa 存储整数值,scale 存储小数位数。

这些规则对签名和哈希至关重要。两个 SDKs 构建相同逻辑交易时,必须生成相同的规范字节。

模式哈希

类型化 Norito 载荷的头部包含 16 字节模式哈希。默认哈希由完全限定类型名称派生;启用结构化模式哈希的构建则改为从规范模式派生哈希。

类型化解码器会拒绝模式不匹配。这可防止客户端意外地将有效 Norito 帧解码为错误类型;当 SDK 测试夹具包与节点数据模型不同步时,通常会以这种方式失败。

压缩和加速

Norito 支持显式压缩和自适应压缩,而不会改变逻辑载荷:

功能用途
to_bytes编码头部,后跟未压缩载荷。
to_compressed_bytes使用 Zstd 编码,并在头部记录压缩标签。
to_bytes_auto使用确定性启发法判断压缩是否值得。
CRC64 加速所有平台均使用可移植的 CRC64-XZ;可用时,x86_64 使用 CLMUL,aarch64 使用 PMULL。
GPU CRC64 与压缩可选的 Metal 或 CUDA 辅助函数可以加速大型载荷,并在需要时回退到 CPU 路径。

硬件加速绝不会改变解码后的内容。CRC 和 JSON 加速器必须与可移植实现的输出逐位匹配。CPU 与 GPU 编码器生成的 Zstd 帧字节可能不同,但解码后的载荷和 Norito 头部元数据仍然保持验证所需的确定性。

JSON 支持

Norito 包含原生 JSON 栈,使需要 JSON 的端点和工具无需离开 Norito 类型系统。

JSON 功能用例
norito::json::{to_json, from_json}确定性的类型化 JSON 编码与解码。
美化输出和 writer 辅助函数CLI 输出、测试夹具和流式 std::io 集成。
DOM 值通过 Norito 的 JSON 值模型进行编程操作。
快速类型化 JSON基于结构磁带,为高频 DTO 路径进行解码和编码。
零拷贝读取器扫描 token,并在可行时直接借用输入中的字符串。
Stage-1 加速器可选的 AVX2、NEON、Metal 或 CUDA 结构索引,并提供标量回退。

Iroha 代码处理类型化 API 载荷时,应优先使用 norito::json 辅助函数。在生产路径中直接添加 serde_json,可能偏离 SDKs 和 Torii extractor 预期的模式及字段处理行为。

Derive 支持

Rust 数据类型通常使用 derive 宏,而不是手写编解码代码。derive 层可以生成 Norito 二进制编解码器、模式和 JSON 辅助函数。

常用字段属性如下:

属性效果
#[norito(rename = "other")]使用稳定的序列化名称,以保持模式和 JSON 兼容性。
#[norito(skip)]编码器省略该字段;解码器提供其 Default 值。
#[norito(default)]解码后的载荷不含该字段时,使用 Default
#[norito(skip_serializing_if = "...")]谓词匹配时从 JSON 中省略字段,同时保留确定性的解码默认值。

在可行时,derive 实现还会公开编码长度提示和精确长度计算。编码器使用这些提示预留缓冲区并避免额外拷贝。

Crate 功能族

从源代码构建 Iroha 或 SDK 绑定时,Norito 功能决定可用的辅助函数和加速器:

功能族启用内容
derive重新导出用于二进制、模式和 JSON derive 的过程宏。
compression为带头部帧的载荷提供 Zstd 支持。
packed-seq使用偏移表的打包集合布局。
packed-struct打包由 derive 生成的结构布局。
compact-len每个值使用 varint 长度前缀。
columnarNorito Column Blocks、自适应 AoS/NCB 行编解码器,以及扫描密集路径使用的借用视图;包含在默认 node-codec 功能集中。
strict-safe将可失败路径中的解码 panic 转换为结构化错误。
simd-accel在可用处使用 CPU 加速,并提供确定性回退。
json原生 JSON parser、writer、DOM、类型化 derive 和快速路径。
json-std-io构建在 JSON 栈之上的 reader 和 writer 辅助函数。
metal-stage1, cuda-stage1选用的 GPU JSON 结构索引后端。
metal-stage2可选的 Metal 元数据分类,用于 JSON 结构磁带。
metal-crc64, cuda-crc64大型载荷可选用的 GPU CRC64 辅助函数。
gpu-compression大型载荷可选用的 Metal 或 CUDA Zstd 加速。
stage1-validate调试验证,将加速后的 JSON 结构索引与标量输出进行比较。

不同 SDKs 和发布配置可用的功能可能不同。传输格式仍由头部和模式规定,而不是由本地构建标志决定。

Torii 和 Norito RPC

Torii 的许多运维路由提供 JSON,但类型化二进制路由使用 Norito。当前类型化 Norito HTTP 正文的媒体类型是 application/x-norito

当端点接受或返回类型化 Norito 时,请使用以下头部:

http
Content-Type: application/x-norito
Accept: application/x-norito

当端点同时支持两种表示形式时,客户端可以发送显式偏好列表:

http
Accept: application/x-norito, application/json

解码失败会显示为类型化 Torii 错误,并由遥测进行计数。常见原因包括 Magic 无效、版本不受支持、功能标志不受支持、校验和不匹配、UTF-8 格式错误、枚举标签无效和模式不匹配。

Norito RPC 传输由传输配置选择。运维仪表板应跟踪请求延迟、失败、活动连接、响应字节和 torii_norito_decode_failures_total,并与 JSON 流量分开统计。

Norito Streaming

Norito Streaming 将同样的确定性方法扩展到媒体和实时传输接口。其关键组成如下:

流媒体功能用途
清单声明分段承诺、隐私路由、能力、编解码器配置文件、加密套件和内容密钥元数据。
分段头绑定分段编号、持续时间、chunk 数量、时序、熵模式、音频摘要和 Merkle 根。
Chunk 承诺使观看者和中继在提供或解码数据前,能够对照清单验证载荷 chunk。
控制帧用于承载清单公告、接收方反馈信息、传输密钥更新信息以及端点之间的能力协商。
HPKE 密钥更新使用协商后的套件和单调递增计数器轮换传输秘密。
能力协商对受支持的功能位、数据报限制、反馈频率和隐私要求取交集。
FEC 与反馈对有损实时路径使用确定性的接收方报告和奇偶校验决策。
一致性向量跨语言测试夹具证明各 SDKs 会解码出相同的清单、分段和熵数据流。

流媒体专用编解码器和熵配置文件与核心 Norito 交易/查询格式相互独立,但其清单和控制数据仍使用 Norito,因此路由、计费、重放和审计证据保持可重现。

运维指南

  • 优先使用 SDK 构建器和生成的绑定,而不是手工制作 Norito 字节。
  • 将模式不匹配视为版本或测试夹具问题,而不是暂时性网络故障。
  • .nrt.norito 和清单工件归档到生成它们的发布包或事件包中。
  • 对于已签名、已哈希或持久化的数据,以 Norito 为事实来源;JSON 投影仅用于仪表板和手动检查。
  • 添加新的类型化 Torii 端点时,记录它接受 JSON、Norito 还是两者,并在 /openapi 中公开受支持的内容类型。
  • 启用加速器前,请针对标量输出运行一致性测试。如果加速器失败,请使用确定性的标量回退;载荷语义必须保持不变。

上游参考