SNIP-12 链下签名:Starknet 订单为什么能显示成人话 图 1
SNIP-12 链下签名:Starknet 订单为什么能显示成人话 · 图 1

在 Starknet 钱包里点确认时,弹窗显示的是一份能读懂的订单而不是天书哈希,这份体感来自一份叫 SNIP-12 的提案:给 Starknet 的链下签名定义结构化的哈希与展示规范,思路直接借鉴以太坊的 EIP-712。它在 Starknet 生态里的地位类似签名界的普通话——挂单、授权、代付等一切需要用户签字但不必上链的场景都靠它统一格式。这一篇拆解它的类型系统设计,以及为什么一个哈希方案能同时管住钱包显示与合约验证两端。

先理解问题。 Starknet 交易签名传统上对一串字段值做哈希,用户签的是裸数字列表,钱包没法把它还原成人类语言,签名钓鱼因此格外容易:同一串数字在不同合约眼里可以有不同的解释。SNIP-12 的解法与 EIP-712 同构——签名前先声明类型:定义一个域(协议名、版本、目标合约地址、链 ID),再定义一条消息类型,字段可以有嵌套结构与数组;签名对象是对域与方法名分别做 Pedersen 哈希后拼接、再把消息数据哈希并入的最终散列。数字没变少,但数字有了类型上下文,钱包能按声明的类型表逐字段渲染,合约端能按同一规则重算验证。

设计上有三处值得细看。第一是域分隔符:协议名加版本加链 ID 加合约地址的组合,让同一份签名不可能在另一条链或另一个部署上重放——这是签名隔离性的锚点。第二是方法名(message hash 里的 method name 字段)取代了 EIP-712 的 domain 结构里的部分职责,Starknet 把它单列出来作为业务动作标识。第三是类型编码规则:每个类型(包括嵌套类型)先算类型哈希(名称加字段名列表的哈希),字段值若是数组则对元素哈希再哈希,规则全部确定,没有实现自由度——规范特意强调这不是协议设计指南,只统一数据怎么哈希、怎么展示、怎么验证这三件事。

对普通用户,SNIP-12 的价值全部体现在签名弹窗上。合规实现里,钱包显示的是字段名与可读值(数量、到期区块、目标市场地址),恶意签名想伪装成普通操作,必须在字段层做手脚,被肉眼识破的概率大得多。这也是选钱包时的实用标准:同样支持 Starknet,弹窗有没有逐字段可读渲染,反映的是实现对标准的完成度。反向提醒同样成立:标准管不了字段内容的真假,订单字段写得再清楚,指向假市场合约的挂单依然是骗局——可读性降低的是误签,不是被骗。

对开发者与审读者,检查点也清楚:签名的验证端必须重算同样的分层哈希,任何偷换域字段、方法名或类型定义的验证实现都是重放漏洞的温床;订单有效性除了签名还要看到期字段与 nonce 设计(规范不强制 nonce 方案,防重放策略由协议自选);最后,签名是链下的、订单撮合是市场的事,SNIP-12 只保证被签的内容可解释、可验证,不对订单本身的安全性负责。

版本与状态顺带交代:该提案处于评审状态,生态的主流钱包与交易合同按它实现,而任何标准的现实约束力取决于工具链采纳率——在 Starknet 上遇到弹窗只显示原始哈希的场景,多半是旧实现或未跟进规范的钱包。把它记成一句话就行:你为 NFT 与 DeFi 签的每一份链下订单,都应该以可读类型渲染的形态出现在你面前,弹窗越像合同,钓鱼越难成。对普通用户再加一条操作纪律:把链下签名当成转账来对待。SNIP-12 的可读渲染降低了读的成本,但读什么仍要自己把关——订单的目标市场地址是否你熟悉、到期字段是区块高度还是时间、数量字段是否包含你根本同意的批量授权,三处都过一遍再确认。挂单类场景永远配合到期与取消路径设计:签名即公开要约,撮合可能发生在你想撤之后的窗口里,任何挂单签名都应按带有效期的委托来理解,而不是按可反悔的意向来理解。

本文为机制说明,不构成任何投资建议。

SNIP-12 链下签名:Starknet 订单为什么能显示成人话 图 2
SNIP-12 链下签名:Starknet 订单为什么能显示成人话 · 图 2