意图签名
在 Sui 中,意图是一个紧凑的结构,作为签名所承诺的消息的域分隔符。签名承诺的数据是一个意图消息。在 Sui 中,所有的签名都必须承诺一个意图消息,而不是消息本身。
动机
在先前的版本中,Sui 使用了一个特殊的 Signable
trait,它将 Rust 结构体名称作为前缀附加到序列化数据上。这不是理想的,因为它具有以下问题:
- 不紧凑: 前缀
TransactionData::
比 1 字节大得多。 - 不友好: 非 Rust 应用程序需要维护一个 Rust 结构体名称的列表。
意图签名标准为被签名的数据提供了一个紧凑的域分隔符,适用于用户签名和权威签名。它具有多个优点,包括:
- 意图范围被 u8 表示,而不是 Rust 结构标签名称字符串。
- 除了意图范围之外,还可以承诺其他重要的域分隔符(例如意图版本和应用程序 ID)。
- 数据本身不再需要实现
Signable
trait,只需要实现Serialize
。 - 所有签名都可以采用相同的意图消息结构,包括用户签名(仅用于承诺
TransactionData
)和权威签名(承诺所有内部意图范围,如TransactionEffects
、ProofOfPossession
和SenderSignedTransaction
)。
结构体
IntentMessage
结构体由意图和序列化的数据值组成。
pub struct IntentMessage<T> {
pub intent: Intent,
pub value: T,
}
要创建一个意图结构体,请包括 IntentScope
(消息类型的标识)、IntentVersion
(网络支持的版本)和 AppId
(签名所涉及的应用程序)。
pub struct Intent {
scope: IntentScope,
version: IntentVersion,
app_id: AppId,
}
要查看每个字段的详细定义,请参阅源代码中的每个枚举定义。
Intent
的序列化是一个包含每个字段一个字节的 3 字节数组。
IntentMessage<T>
的序列化是意图的 3 字节与 BCS 序列化的消息拼接而成。
用户签名
要创建用户签名,首先构建一个意图消息,然后在交易数据的意图消息的 BCS 序列化值的 32 字节 Blake2b 哈希上创建签名(intent || message
)。
以下是一个 Rust 示例:
let intent = Intent::default();
let intent_msg = IntentMessage::new(intent, data);
let signature = Signature::new_secure(&intent_msg, signer);
这是一个 TypeScript 的示例:
const intentMessage = messageWithIntent(
IntentScope.TransactionData,
transactionBytes,
);
const signature = await this.signData(intentMessage);
在底层,Rust 中的 new_secure
方法和 TypeScript 中的 signData
方法执行以下操作:
- 将意图消息序列化为包含 3 字节意图和交易数据的 BCS 序列化字节的连接。
- 应用 Blake2b 哈希以获取 32 字节摘要。
- 将摘要传递给签名 API,对应于签名者的每个支持的方案。支持的签名方案有纯 Ed25519、ECDSA Secp256k1 和 ECDSA Secp256r1。请查看Sui签名以获取每种方案的要求。
权威签名
使用协议密钥创建权威签名。它所提交的数据也是一个意图消息 intent || message
。请在源代码中查看所有可用的意图范围。
如何为权威生成拥有证明
当权威请求加入网络时,需要提交协议公钥及其拥有证明(PoP)。PoP 是为了防止恶意密钥攻击。
拥有证明是使用权威的协议私钥创建的 BLS 签名,提交的消息是:intent || pubkey || address || epoch
。这里 intent
被序列化为 [5, 0, 0]
,表示意图的范围为“拥有证明”,版本为“V0”和 app_id 为“Sui”。pubkey
是权威的 BLS 协议密钥的序列化公钥字节。address
是与权威帐户密钥关联的帐户地址。epoch
被序列化为 [0, 0, 0, 0, 0, 0, 0, 0]
。
要在 Rust 中生成拥有证明,请参见 fn generate_proof_of_possession
的实现。有关测试向量,请参见 fn test_proof_of_possession
。