链绑定签名
ZooBC 的签名只在一条链上有效。签署交易之前,钱包会计算一个包含该链创世哈希的摘要,因此在 testnet 上签署的付款在 mainnet 或 devnet 上无法通过验证,尽管同一个密钥在每条链上都给出相同的地址。同样的绑定也保护着节点之间的连接以及在链下签名的消息。
摘要
签名者从不直接对原始交易字节签名。它签署的是如下摘要:
digest = SHA3-256("ZBC-TX" ‖ genesis hash ‖ unsigned transaction bytes)
transaction hash = SHA3-256(unsigned transaction bytes ‖ signature)
transaction id = the first 8 bytes of the transaction hash, little-endian
标签“ZBC-TX”表明该摘要属于一笔交易;其他签名操作使用其他标签(见下文)。创世哈希把签名与一条链绑定在一起。只有这种链绑定的摘要才会被接受:只对裸字节签名、未绑定任何链的签名,在每条链上都会被拒绝。完整的签署流程见 签署交易。
创世哈希就是链的身份
创世哈希是一条链最初那个区块的哈希。它由该链的创世配置推导而来,因此更改该配置中任何一个已承诺的值都会得到一个新的哈希,也就是另一条链(见 创世区块)。ZooBC 中任何地方都没有单独的网络 ID 字节:创世哈希是链唯一的身份标识。Devnet、testnet 和 mainnet 各有自己的创世哈希(见 devnet、testnet 与 mainnet),重新启动的测试网络会得到一个新的创世哈希。
节点会在其信息路由 /api/v1/node/info 上报告本链的 genesis_hash 和 signing_version(见 节点 API),这样钱包或工具就能知道自己是在为哪条链签名。
跨链重放
假设一笔付款在 testnet 上签署,而有人把它复制到 mainnet。mainnet 节点用 mainnet 的创世哈希计算摘要,得到一个不同的值,签名无法通过验证:该交易会以“并非为本链签署”为由被拒绝。金额在这里不起任何作用,钱包也无需额外添加任何东西。
同一个密钥在每条 ZooBC 链上都给出相同的地址,在一条链上有余额的地址在另一条链上只会显示零余额。这正是绑定重要的原因:如果没有绑定,在一条链上发起的付款就可以被复制到另一条链上,并在那里转移币。
每种账户类型,每个签名者
ZooBC 账户可以由多种密钥控制:ZooBC 自己的 Ed25519 密钥、比特币和以太坊密钥以及其他密钥(见 密钥与账户类型)。每种账户类型签署的都是同一个链绑定摘要;有些类型会按其原生链签名方案的要求,先对它再做一次哈希(例如,以太坊式密钥签署的是它的 Keccak-256 哈希)。多重签名账户的参与方签署的是内部交易的链绑定摘要。
其他签名操作使用各自的摘要,同样与链绑定:链接或控制账户组的同意签名(“ZBC-GROUP-LINK”、“ZBC-GROUP-CONTROL”)、多方托管的联署签名,以及取消节点交易池中某笔待处理交易的请求(“ZBC-CANCEL”)。
节点与消息
链绑定并不止于交易:
- 对等节点握手。 两个节点建立连接时,握手由节点密钥签名,并携带创世哈希。另一条链的对等节点会以“Genesis hash mismatch”被拒绝,因此一条链的节点永远不会与另一条链的节点建立对等连接。
- 签名消息。 用工具在链下签名的消息使用摘要 SHA3-256("ZBC-MSG-v2" ‖ 创世哈希 ‖ 消息),因此在 devnet 上签名的消息在 testnet 或 mainnet 上永远无法通过验证(见 签名消息与加密消息)。
回执是协议内部的例外:回执不指明任何链,因此本身不带链绑定。不过它会通过高度和哈希指明一个参考区块,验证者会对其进行核对。