# 协议总则

[Meshline Protocol 1.0](README.md)

本文件定义 Meshline Protocol 1.0 所有顶层协议共同适用的范围、规范性用语、数据表示和密码学约定。协议角色、业务对象、网络方法、状态机、错误处理、资源策略和传输行为由其所属协议定义。

## 适用范围

本规范定义客户端、账户设备与公共中继之间的网络互操作规则。

本规范不涉及：

- 用户界面，以及钱包与密钥的本地存储或保护方式；
- 应用自定义业务对象、正文展示的具体排版和附件托管服务；
- 中继运营者之间的商业结算；
- 昵称模糊搜索、全局用户目录和联系人推荐；
- 区块链 RPC 节点的选择与运维；
- 具体的 libp2p、数据库、Web 框架、编程语言或 SDK 实现。

## 规范性用语

本规范用“必须”“不得”“应”“不应”“可以”区分强制要求、禁止行为、建议行为、不建议行为和可选行为。

## 网络上下文

### 格式与表示

一个符合本规范的 Meshline Network 由 Neo N3 网络的 network magic 和该网络上的 `MeshlineRegistry` 合约 script hash 共同确定。其规范字符串表示为：

```text
neo:<reference>:<registry>
```

- `neo` 是固定的小写前缀；
- `reference` 为 Neo N3 网络的 network magic，使用无前导零的十进制整数，范围为 `0..4294967295`；
- `registry` 为该网络上 `MeshlineRegistry` 合约的 script hash，使用小写 `0x` 后接 40 个小写十六进制字符；Neo 使用 `UInt160` 表示该值。

完整字符串必须匹配 `^neo:(0|[1-9][0-9]{0,9}):0x[0-9a-f]{40}$`，且 network magic 必须满足上述数值范围。非法格式必须拒绝，不得通过裁剪空白、大小写转换、去除前导零或其他规范化操作后接受。

密码学输入使用保留字段 `$context` 携带该字符串。例如：

```json
{
  "$context": "neo:860833102:0x5979ba79431672a38a18a32cdc48fd7317818b70"
}
```

### 可信来源与隔离要求

不同网络上下文中的协议对象、签名、哈希和网络记录必须相互隔离。验证方必须从可信配置取得预期的网络编号和 Registry 合约地址，构造规范上下文字符串并进行完整字符串比较，不得采用待验证对象或远端节点自行声明的值建立预期上下文，也不得仅凭网络名称判断两个上下文相同。由此，同一账户密钥对同一对象内容生成的签名无法在另一 Neo 网络或同一 Neo 网络上的另一套 Registry 部署中通过验证。

Neo JSON-RPC 客户端必须确认所连接节点的 network magic 与预期网络上下文一致。

## JSON 与字段表示

- 协议 JSON 使用 UTF-8；`$type` 和 `$context` 是协议保留字段。
- 二进制值使用无 `=` padding 的规范 base64url；发送方和接收方均须遵循下述[base64url 编码](#base64url-编码)规则。
- 表示时间点的字段默认使用 UTC Unix 秒整数；字段明确规定其他单位时，以字段定义为准。
- 对象不得包含重复字段名。JSON 树中任意节点的深度不得超过 16；节点深度按 [RFC 9535 第 1.1 节](https://www.rfc-editor.org/rfc/rfc9535.html#section-1.1) 定义。非法 UTF-8、非 Unicode scalar value、尾随非空白数据和类型不匹配必须被拒绝。
- 完整对象签名或网络绑定摘要覆盖范围内的未知字段必须连同名称、JSON 值和字段存在性递归保留；向目标状态复制或合并未知字段须遵循下文的[字段映射规则](#字段映射)。

字段表中的 `array<T>` 表示元素类型为 `T` 的 JSON 数组。“必需”列说明字段是否必须出现：“是”表示必须出现，“否”表示可以省略，“条件”表示按字段语义决定。省略字段不等于显式 `null`。除方法、对象或传输规则明确允许外，`null` 无效。

- 完整对象中，允许省略且允许空数组的字段可以省略或编码为 `[]`，业务处理把两者视为同一空集合，但不得以 `null` 代替。字段是否必须出现由“必需”列决定；要求非空的数组字段出现时不得使用 `[]`。
- 表示集合的数组不得包含重复项。
- 除字段定义明确规定顺序外，数组顺序不承载业务语义；签名和哈希仍按对象实际数组顺序生成。
- 面向用户的文本按字段定义的 UTF-8 byte 上限验证；文本上限不得套用于密文、base64url bytes 或外部内容。

### 安全整数与计数器推进

JSON integer 必须使用 `-?(0|[1-9][0-9]*)` 的最短十进制形式，`-0`、小数和指数形式均无效；值必须位于 `[-(2^53-1), 2^53-1]`。

协议字段使用安全整数推进时，生成方必须执行精确整数运算并在签名、发布或写入前确认结果仍在允许范围。不存在合规下一值时必须停止本次需要推进该值的操作并保留当前有效状态，不得溢出、回绕、截断、使用浮点近似或复用既有值。客户端在本地生成阶段发现耗尽时，停止生成并向调用方报告原因，不构造越界对象提交。

### 文本空白字符

用户文本字段规定“不得仅包含空白”或“必须至少包含一个非空白字符”时，空白字符固定采用 [Unicode 17.0.0 `White_Space`](https://www.unicode.org/Public/17.0.0/ucd/PropList.txt) 的以下范围，共 25 个 Unicode 码点；范围包含两端：

```text
U+0009..U+000D  U+0020  U+0085  U+00A0  U+1680
U+2000..U+200A  U+2028  U+2029  U+202F  U+205F  U+3000
```

非空字符串的全部码点均在该集合中时，属于仅包含空白；至少一个码点不在集合中时，通过非空白字符检查。空字符串是否允许仍由字段定义决定。

### base64url 编码

本规范明确使用 base64url 的字段或内容，必须使用 [RFC 4648 第 3.5、5 节](https://www.rfc-editor.org/rfc/rfc4648.html#section-3.5)定义的规范编码，并省略 `=` padding：

- 编码文本只允许 ASCII 字符 `A-Z`、`a-z`、`0-9`、`-` 和 `_`，不得含 `=`、空白、`+`、`/` 或其他字符；
- 字符数除以 4 的余数只能为 0、2 或 3；
- 余数为 2 时，末字符对应的 6-bit 值的低 4 位必须为 0；余数为 3 时，其低 2 位必须为 0。

接收方必须拒绝不满足上述条件的文本，不能删除字符、忽略非零尾部填充位或改写编码后接受。规范编码具有唯一性：例如字节 `00` 编码为 `AA`，`AB` 即使能被宽松解码器解成同一字节也必须拒绝。仅通过字段正则表达式不表示已经满足本节要求。空字符串是空字节串的规范编码，但只在字段允许空值时有效。

### 对象类型

定义了 `$type` 的对象，其 `$type` 必须等于对象定义给出的固定字符串。接收方按完整、大小写敏感的字符串匹配选择对应的结构、字段语义和验证规则；不同字符串表示不同类型。

接收方不得仅凭字段形状、当前接口或未定义属性推断对象类型。未知类型按相应消息、方法或事件规则保存、拒绝或暂停状态应用，不得作为已知类型处理或触发未支持的协议状态变更。

未定义独立 `$type` 的子结构按父对象选定的规则解释；具有独立 `$type` 的嵌套对象必须按自身类型验证，外层类型不替代内层类型。

构造签名、哈希、身份或资源标识派生、AAD 和 KDF info 的输入时，必须使用各输入定义的固定 `$type`，不得补入默认格式版本。验证方必须保留覆盖范围内的 `$type` 和未知字段，除规定的输入构造操作外，不得增删或改写字段以匹配另一种类型。

### 增量编辑

增量编辑中的可修改字段省略时保持当前值，显式 `null` 表示删除该字段，其他值表示新增或替换。不能删除的字段不接受 `null`，更新后的对象仍须满足自身约束。

### 字段映射

从请求参数重建另一个对象，或把增量请求应用于已有状态时，只能按方法定义映射字段。

只有方法明确允许时，才可将请求的扩展属性复制或合并到目标对象；这些扩展属性不得与目标对象的已定义字段或只属于网络绑定输入的字段重名，即使值相同或为 `null` 也必须拒绝。

签名请求中的其他未知属性仍按原签名规则保留，不得作为目标状态的补丁。

### JSON-RPC 扩展字段

客户端 WebSocket 与中继 RPC 的 JSON-RPC 请求、响应和通知可以在根对象中携带未定义的扩展字段。接收方必须忽略这些字段，不得仅因其存在拒绝消息。扩展字段仍须满足[JSON 与字段表示](#json-与字段表示)规则，并计入 JSON 深度限制；重复字段及其他非法 JSON 表示仍须拒绝。已定义字段的类型、必需性、取值和互斥规则继续适用，未知字段不得代替已定义字段或改变方法分派、响应关联及业务授权。

本规则仅适用于 JSON-RPC 封装的根对象；`params`、`result` 和 `error.data` 内的业务对象继续按各自规则验证。签名业务对象中的未知字段仍须保留并参与相应签名或摘要。

## Canonical JSON

本规范中的 Canonical JSON 必须按 RFC 8785 JSON Canonicalization Scheme 生成，并遵守仅允许安全整数的约束：

1. 对象字段递归按字段名的 UTF-16 code unit 无符号字典序排列；
2. 数组顺序保持不变；
3. 省略的字段不进入 Canonical JSON；允许的显式 `null` 按原值保留；
4. 字段存在性、数组元素和全部未知字段按输入保留；
5. 不输出额外空白；
6. 字符串不执行 Unicode 归一化；U+0000 至 U+001F 按 RFC 8785 使用小写 `\uhhhh` 或规定的短转义，双引号和反斜杠分别编码为 `\"` 和 `\\`，其他 Unicode scalar value 原样输出后编码为 UTF-8；
7. number 只允许安全整数，并保持最短十进制形式。

省略字段、显式 `null` 和 `[]` 产生不同的 Canonical JSON。验证方不得在验签或验哈希前互相转换这些表示，也不得在本规范规定的输入构造与规范化操作之外增删字段、改写字段值或改变数组顺序。

## 网络绑定 JSON 输入

协议 JSON 对象的签名和网络绑定哈希使用同一种输入。签名方、哈希计算方和验证方必须从可信[网络上下文](#网络上下文)构造 `$context`，直接加入待处理对象根。

网络传输的协议对象根不得携带 `$context`。`$context` 只存在于本地构造的密码学输入中，不加入网络传输对象。

### 输入构造

网络绑定对象按以下规则构造：

1. 复制输入对象的全部根字段；生成签名输入前先按对象规则删除签名字段，生成哈希输入时使用对象章节指定的完整哈希目标；
2. 在同一根对象中加入值为预期规范上下文字符串的 `$context`；
3. 对合并后的单个对象生成 [Canonical JSON](#canonical-json)。

网络绑定 JSON 输入是合并后对象的 Canonical JSON UTF-8 bytes：

```text
network_bound_json_bytes(object) =
  UTF8(Canonical JSON(network_bound_object(object)))

network_bound_json_hash(object) =
  "sha256:" + base64url(SHA-256(network_bound_json_bytes(object)))
```

### 签名与摘要使用规则

完整 JSON 对象的通用签名输入是 `network_bound_json_bytes(object_without_signature)`。签名字段只从待签名对象的根删除；嵌套对象及其独立签名必须原样保留。

对象章节未另行指定时，协议 JSON 对象的签名、内容摘要、请求体摘要和防重放摘要都必须使用上述输入。外部内容、附件等原始 bytes 的摘要直接对相应 bytes 计算，不构造网络绑定 JSON 输入。

## SHA-256 摘要表示

SHA-256 摘要的统一文本表示为：

```text
sha256:<base64url(SHA-256(bytes))>
```

## 账户签名

### 签名生成与公钥表示

账户签名以[网络绑定 JSON 输入](#网络绑定-json-输入)为消息输入，各对象和方法定义的字段排除规则继续适用。签名算法及公钥、签名的字节格式必须遵循[账户 ID](client-relay/core-objects/accounts-and-devices.md#账户-id)中 `namespace` 对应链的规范。签名方使用账户私钥签署这一完整字节序列，不得再添加其他消息封装。账户公钥和签名在协议字段中均使用无 padding [base64url](#base64url-编码)。

所用公钥编码规范支持压缩表示时，账户公钥必须采用该表示。

ECDSA nonce 应按 RFC 6979 使用签名方案指定的哈希算法确定性生成，也可使用满足 ECDSA 要求的密码学安全随机方式生成；两种方式使用相同的验签规则。

### 验签与签名复用

验证方必须对同一消息输入验签，并按对应链的[账户规则](client-relay/core-objects/accounts-and-devices.md#账户-id)核对公钥与账户 ID 的绑定。中继使用 Neo N3 账户签名，并核对公钥派生的 Neo N3 单签 script hash 与[中继 ID](registry/core-objects.md#中继-id)的绑定。

签名有效性由所选算法的验签结果确定，不以签名字节是否等于另一次生成的签名判断。签名仍按对象规则参与完整对象的哈希、嵌套签名和请求内容比较；方法要求原样重试时，必须复用原始完整对象及其签名。

## X25519 共享秘密校验

消息密钥盒、客户端群秘密盒和中继秘密盒使用 [RFC 7748](https://www.rfc-editor.org/rfc/rfc7748.html) 定义的 X25519。发送方封装和接收方解封时，每次 X25519 运算都必须在把结果用于 HKDF 前检查共享秘密；结果为 32-byte 全零值时，必须中止本次密钥盒操作，不得生成或接受该盒，不得继续派生封装密钥或交付解密结果。

该规则将 RFC 7748 第 6.1 节的可选全零检查收紧为本协议的强制要求。检查针对运算结果，不能只拒绝全零公钥编码；非零的低阶输入也可能产生全零共享秘密。公钥长度、base64url 编码、设备证书、业务签名及密钥盒认证的其他验证要求继续适用。

## 消息长度前缀编码

中继 RPC 和 DHT 消息的外层长度前缀统一使用 [Multiformats unsigned-varint](https://github.com/multiformats/unsigned-varint) 编码，表示后续消息 payload 的 byte 数：

- 无符号整数从最低有效位开始，每次编码 7 bit；每个 byte 的低 7 bit 为数据，最高 bit 为 1 表示仍有后续 byte，为 0 表示前缀结束；
- 数值范围为 `0` 至 `2^63 - 1`，前缀最多 9 bytes；第 9 个 byte 的最高 bit 仍为 1 时，前缀无效；
- 发送方必须使用最短编码，接收方必须拒绝非最短编码。多 byte 编码的最后一个 byte 不得为 `00`；零的唯一编码是单个 `00`。例如 `01` 表示 1，`81 00` 虽可被宽松解码器解成同一数值，也必须拒绝；
- 流已经结束但仍未取得完整前缀、编码超长或数值超出范围时，必须拒绝。

上述范围只说明二进制前缀的表示能力；编码合法的声明长度仍须满足相应传输的消息大小上限和 payload 要求。非法前缀属于消息分帧失败，接收方必须关闭或 reset 对应 stream，不产生应用层响应。

## 外部规范

未被所属协议明确收紧或替换的编码和密码学行为按以下规范执行：

- [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119) 与 [RFC 8174](https://www.rfc-editor.org/rfc/rfc8174)：规范性关键词；
- [RFC 8259](https://www.rfc-editor.org/rfc/rfc8259)：JSON；
- [RFC 8785](https://www.rfc-editor.org/rfc/rfc8785)：JSON Canonicalization Scheme；
- [RFC 4648](https://www.rfc-editor.org/rfc/rfc4648)：base64url；
- [FIPS 180-4](https://csrc.nist.gov/pubs/fips/180-4/upd1/final)：SHA-256；
- [SEC 1 v2.0](https://www.secg.org/sec1-v2.pdf)：椭圆曲线公钥表示及 ECDSA；
- [RFC 6979](https://www.rfc-editor.org/rfc/rfc6979)：确定性 ECDSA nonce 生成。
