
交易模拟:在你签名之前,这笔交易究竟会做什么
地址是对的。金额是对的。两样你都核过了。可这笔交易依然不是你以为的那笔。
这不是假设。在牵涉到一个谨慎之人的加密货币损失里,大多数都是这个形状。没有人劝他把钱转给陌生人。他批准了一样东西:可见的部分全都正确,后果却完全在别处——在一笔代币花费授权里,在一次合约调用里,在一个他已经看过一百遍的地址的某一个字符里。
模拟就是试图弥合这道缝隙:在你的签名把它变成现实之前,把一笔交易的后果而不是它的内容展示给你。
你想做的事与你正在签的东西之间的缝隙
一笔交易不是一句话。它是字节。
当这些字节是一次普通转账时,它们所说的与它们所做的之间缝隙很窄:一个地址、一个金额、一笔手续费。当它们是一次合约调用时,这道缝隙可以大得惊人。approve 看上去像一项许可。它是另一个地址长期持有的、动用你代币的权利,而在 ERC-20 标准之下,这次调用中没有任何部分要求限定额度或期限。一次授权,签过一回,几个月后仍可能被搬空。
你签名时所盯着的那个界面,必须把这些字节翻译成一句话。这道翻译就是攻击面。如果翻译由那个想要你签名的人来生成,它可以说出任何话。
一次排练,而非一份承诺
模拟意味着:把交易放到链的当前状态上执行,但不广播,并记录下什么发生了改变。
在 SSP Enterprise 中,这发生在提案被创建之时,早于任何人签名。模拟是依据提案构建器的产出搭起来的——收款人、金额、代币合约与 SSP 自己组装的调用数据——绝不依据客户端递交的原始已签名十六进制。它以只读方式运行。三个引擎覆盖不同的链系:
- EVM 链对节点发起一次只读调用,读回由此产生的余额变化与解码后的调用。
- UTXO 链完全不需要节点。输入与输出已然已知,所以前后对比就是在所选币上的算术,而收款人会被划分为找零回到你自己,还是真正的外部地址。
- Solana 会把构建器产出的未签名交易反序列化,运行网络自身的
simulateTransaction,并关闭签名校验,然后从结果中读出金库在交易前后的代币余额。
回来的是一份预览:每种资产此前的余额是多少、此后会是多少,以及——在 EVM 上——这次调用究竟是什么,被解码成一个方法及其参数。
它有两样东西不是。它不是保证:链的状态在移动,而在提案时刻跑出的模拟,是那一刻的链的一张照片。它也不是关卡。而这恰是整个设计里更有意思的那一半,我们稍后回到它。
十四件值得被告知的事
一份只显示数字的预览,仍然是在要求你自己发现问题。所以预览之外还有警告,一共十四项,分四种严重级别。
授权——掏空最多钱包的那一类。无限额或不设上限的授权是「严重」。任何面向不在你许可名单上的地址、额度非零的花费授权是「高」。而把授权给一个外部持有账户而非合约,同样是「高」,因为这么做几乎不存在正当理由:花掉授权的是合约,不是人。
收款人——既不在你所属组织的联系人、也不在金库白名单上的收款人会被标出。你的金库从未向其发送过的地址同样会。单独看,两者都不算错。两者都值得再读一遍。
合约风险——源码未经验证的目标合约、在最近七天内才部署的合约、把原生代币价值发往一个合约这件事本身,以及任何被供应商标记为直接恶意的地址。
执行——交易在模拟中回滚,意味着它在链上必然失败,白白烧掉手续费。或者模拟出的资金流出与提案所声称的金额对不上:金库损失得比屏幕上写的更多,或更少。
降级——以及被清楚标注出来的那种情形:模拟根本没能跑起来。一个连不上的节点产出的是「不可用」,而不是沉默,更不是一纸编造的健康证明。
这些严重级别之间的区分,比它们的总数更要紧。「严重」与「高」谈的是一笔很可能并非其表象的交易。「中」与「提示」谈的是一笔不寻常的交易,而正当交易偶尔就是不寻常的。
地址投毒:那种能击败谨慎之人的攻击
有一条警告值得单开一节,因为它瞄准的正是几乎所有安全建议都在推荐的那个习惯。
地址投毒是这样运作的。攻击者从一个精心构造的地址向你的金库发来一笔极小额或零额的交易,这个地址与你本就打交道的某个地址共享前六位和后四位字符。什么都没被偷。事实上什么都算不上真的做了。这笔交易存在的意义,只是让那个仿冒地址出现在你的历史记录里。
后来——数日之后,当你再次向同一个对手方付款时——你像谨慎的人那样,不去重新键入,而是从自己的交易历史里复制那个地址。你像谨慎的人那样核对:开头几位、结尾几位。两处都对得上。中间对不上,而钱正是往中间去的。
SSP 会把提案中的每一个收款人,与你的金库已经认识的那些地址逐一比对,用的正是人眼所执行的那种前缀加后缀的比较,并发出一条点名被仿冒地址的严重警告。同样的检查也在相反方向上跑在入账历史上,于是一个被投毒的地址在抵达时就被标记,而不是等到你即将使用它的那一刻。
即便你从不使用 SSP,也值得知道这种攻击的理由是:那个能拦下其他一切地址攻击的核对习惯,恰恰是这种攻击专为击破它而建的。要么比对整个地址,要么就什么都别比。
为什么警告在服务器上计算——又为什么它依然拦不住任何东西
这一组警告是在服务器一侧裁定的,以你所属组织的联系人和金库白名单作为事实来源。这是刻意的:如果由客户端来决定什么算作「已许可」,那么一个被攻破的客户端就能悄悄决定一切都算。
然而这一切都无法阻止一笔交易。模拟从不对签名或广播设限。一个报错、超时或够不着节点的提供方会返回「不可用」,而提案依然完全可签。整个子系统都做了故障隔离,好让预览里的一次崩溃永远不能把一份提案搁浅在那里。
这听上去会像个奇怪的选择,所以有必要把道理直说。一个能拦截的预览,就是一个能被迫去拦截的预览——只要打掉一个节点,只要造出一条假的「严重」,只要用软件出错的一千种方式里的任何一种。因为一项建议性服务生病而动不了的资金,是你已经部分失去的资金。守住钱的是多签里的那个门槛;模拟在那里,是为了告知手握钥匙的人。究竟什么能拦下一笔交易是这个论证的长篇版本。
当服务器与你的设备意见不合
以上的一切,都是服务器的读法。你的设备有它自己的读法。

当 SSP Wallet 把一份提案摆到你面前等你签名时,它会自行解码那些字节,并显示它所发现的东西——而不是服务器的摘要。然后它把两者相互比对。如果服务器解码出的调用所隐含的收款人集合,与设备自己推导出来的不同,设备就会发出属于它自己的一条严重不一致警告,并在视觉上把服务器的预览降级。
设备的解码才是权威的那一份。这项检查刻意保守:缺失或仍在等待中的服务器模拟属于降级,而不是矛盾,也不会被当作矛盾报出。它同样只适用于 EVM 链,因为只有在那里服务器才会产出解码后的调用;在 UTXO 链上没有可比之物,设备自己的解码就只是独自成立。
无论你用哪个钱包,这都是值得带走的性质。第二个意见只有在它来自一个不会被攻破第一个意见的同一个动作所攻破的地方时,才值一点东西。来自同一台服务器的两份摘要,就是一份摘要。
如何读一条风险提示条,而不至于学会无视它
警告只在尚未变成墙纸之前有效。几个习惯能让它们保持有用。
先读严重级别,再读细节。 「严重」和「高」值得你停下来。给一家新供应商的第一笔付款上出现「中」或「提示」,那是系统在正确工作,不是担忧的理由。
把任何一条授权警告当作彻底停下。 转账搬走的就是它说要搬走的。授权则给出一项比这笔交易活得更久的权利。如果你并不打算给出一项长期许可,答案就是不。
信设备胜过信屏幕。 如果两者不一致,你手里的那部手机才是运行在攻击者必须另行攻破的硬件之上的那一个。
别把「不可用」读成「没问题」。 它的意思是没有人检查过。这正是你该自己看得更仔细的理由,在一笔大额或不寻常的付款上尤其如此。
这里所讲的一切,都关乎签名之前的那一刻。至于之后发生的事——谁可以签、需要几个人签、哪些操作会再次索要两台设备——请从设置你的第一个企业金库和关键操作与重新签名开始。而关于这些警告是围绕哪些攻击套路塑形的,针对加密用户的钓鱼攻击讲的是这个问题里属于人的那一半。


