以太坊上的 USDT 是一份智能合约,去中心化交易所里用来换币的资金池是一份智能合约,钓鱼页面背后那段等着收币的程序,也是一份智能合约。三者的共同点只有一条:都是存放在区块链上、照写好的代码执行的程序。程序拿来做什么,取决于写它的人。

智能合约是存储在区块链上的计算机程序,照「如果这样,那么那样」的逻辑、按代码写定的规则执行。至于「智能合约盗 U」(U 指 USDT),链上能看到的过程是:你签了一笔授权,或者自己把币转给了一个合约地址,而发出去的交易撤不回来。反过来说,没有你的许可,任何应用都动不了你钱包里的代币。

智能合约和合约交易的区别

中文里两样东西都被简称「合约」,指的却不是一类东西。

按词来分:「合约爆仓」「合约手续费」「合约杠杆」找的是后者;「合约地址」「合约授权」「合约审计」说的是前者。「智能合约盗 U」「智能合约诈骗」里的合约,是链上的程序。

部署后的智能合约还能改吗:可升级合约与代理模式

常见的说法是智能合约「一旦创建便无法更改」,ethereum.org 的入门介绍里就是这么写的。

这句话没错,但不完整。给开发者的合约升级文档也说合约部署后业务逻辑无法更新,随后却讲了一组「升级模式」:部署到某个地址上的程序确实改不了,能换的是用户与合约交互时实际被执行的那段代码。可升级(upgradeability)和可变(mutability)是两个概念。

其中一种做法叫代理模式(proxy pattern):

  1. 用户打交道的是代理合约,它存数据,不含业务逻辑。
  2. 代理合约里记着逻辑合约的地址,把收到的函数调用都转交给逻辑合约去执行。
  3. 升级时另部署一份新的逻辑合约,再把代理合约里记的那个地址换掉。

这样升级逻辑时,用户不用换到新合约。别的做法还有合约迁移、数据分离、策略模式和钻石模式。

所以两种说法并不打架:某个地址上的那段代码不会变;用户通过某个地址实际跑到的规则,在采用了升级写法的合约上会变。

对用户来说,好处是漏洞可以更快修掉。代价有几条:升级否定了「代码不可变」这一点;用户必须信任开发者不会随意修改合约;访问控制做得不严,恶意者更容易实施未经授权的升级。可用的约束手段有:把升级函数限定为只有合约所有者能调用,用多重签名钱包或去中心化自治组织(DAO)的投票来控制升级,以及加时间锁——提案通过后要等预设的延迟期才执行,给不同意的用户留出退出的时间。

有人拿「合约不可篡改」当卖点时,你可以追问两句:这份合约用没用可升级的写法,升级权在谁手里。

智能合约盗 U 是怎么回事,币是怎么被转走的

币被转走有两条路,离开钱包之前都有一步是你自己签的。签的是什么,决定了走的是哪一条。

签的是一笔授权

授权不转币,它给某个合约一个额度,额度之内合约可以把钱包里的某种代币划走。代币授权是诈骗常走的一条路:所谓恶意授权,就是把过大或危险的权限交给了有问题的应用或合约,不少情况下是无限额度,对方可以花掉该种代币的全部。骗子利用的是用户的信任,或者对授权流程的不了解。

授过的额度怎么查、怎么撤:钱包授权。

自己把币转给了合约地址

「把 U 打进合约」说的就是这一步。这是一笔普通转账,只不过收款方是合约地址。之后币能不能出来、谁能取、什么条件下取,全看合约代码怎么写。正面的例子:可以写一份合约替孩子托管资金,到特定日期之后才允许提取,提前去取,合约不会执行。计算机程序能做的事智能合约基本都能做,所以提取条件同样可以写成只对部署者开放。

签的时候看不懂自己在签什么

这是眼下的普遍情况:合约条款谁都可以查看,但原始交易数据是给应用程序和钱包解读的,人很难读懂,用户经常是在「盲签名」,也就是没真正弄懂意图就批准了交易。以太坊生态正在向明文签名(Clear Signing)标准过渡,用人能读懂的描述来显示交易内容(ethereum.org 的介绍)。

交易发出去撤不回

区块链是去中心化、不可更改的记录,许可一旦上链就成了既成事实。交易无法撤销,丢失的资金也无法恢复;钱包已经被转空的话,能做的很少,自主托管钱包的开发方也介入不了(MetaMask 帮助中心)。

合约「已审计」「源码已验证」就安全吗

不能这样推。这类说法常见的有三种,各自能说明的范围都有限:

页面上的说法 能说明的 说明不了的
源码已验证 公开的源码能编译出该地址上运行的字节码 这份代码对使用者有利还是不利;注释和变量名是否可信(Etherscan 做的是部分匹配)
做过审计 有独立的一方审查过代码 代码里没有漏洞;审计之后逻辑没有换过
区块浏览器上没有风险标记 暂时没有被标成疑似诈骗 这个合约可以放心交互

先说「源码已验证」。它的意思是公布出来的合约代码和该地址上实际运行的代码是同一份,做法是把源码重新编译,再和链上的字节码比对(ethereum.org 的 Verifying smart contracts)。这样做的用意是「无需信任」:用户在交互之前不必先信任开发者或公司。

这项验证有它的范围。Etherscan 除了当区块浏览器,也提供源码验证服务,是用得最多的验证工具,但它不比对元数据哈希,得到的是部分匹配。注释和变量名不影响编译出来的字节码,部分匹配之下,它们可以和原始源码不同,有人可以借此写上误导的注释、起误导的变量名;不做完整验证,还有可能插入在验证后的源码里反映不出来的恶意代码。就算匹配无误,验证回答的也只是「贴出来的能不能编译成链上跑的那份」,一份把资金提取权只留给部署者的合约,同样可以通过验证。

再说审计。委托审计是做独立代码审查的一种方式,但别把它当万能药:审计抓不出每一个漏洞,主要是多提供一轮审查,帮助发现开发和测试阶段漏掉的问题(ethereum.org 智能合约安全文档的说法)。审计对应的又是审计时的那份代码,可升级的合约,逻辑换过之后跑的就不再是被审的那一份。

最后是「没有风险标记」。把合约地址贴到 Etherscan 这样的区块浏览器上,可以看有没有异常活动、是否被标成疑似诈骗;查不到标记,不能反过来当作安全的证明。

三项加起来,能确认的是代码公开、有人看过、暂时没被标记;这份合约对转币进去的人是否有利,不在其中。

「打进合约自动分红」的说法,自己能核对的五处

「自动」这个词本身没有错,智能合约的长处确实是自动执行:条件满足时,代码确定性地执行,不需要等人来解释或协商。但自动执行的是代码里写的规则,不是口头许诺的收益。你可以自己核对下面五处。

  1. 要合约地址。拿到地址后贴进区块浏览器,看源码是否已验证、有没有被标记。对方给不出地址,或者只给一个网页链接,后面几项就都无从核对。
  2. 看对方要的是哪个动作。钱包弹窗里是 Approve(授权)还是转账,要先分清。是授权,就看被批准的合约地址和额度;MetaMask 的弹窗里可以点「verify contract details」查看地址,也可以在「custom spending cap」里把额度改小。
  3. 是否要求先转币进去。币进了合约,能不能取回由代码决定。源码没有验证,或者验证了但自己读不懂,就等于不知道取回的条件。
  4. 收益说得有多好。催促和设截止时间、许诺不现实的回报、冒充知名项目,都是恶意请求常带的特征。好得不像真的,多半就不是真的。
  5. 「不可篡改」是否属实。采用可升级写法的合约,规则可以由握有升级权的一方更换。

合约是真的、池子也是真的,项目方仍然可能抽走资金,这就是 Rug Pull。已经签过授权的,先去把额度撤掉。

地址拿到以后,第 2、第 3 项最容易自己核:另外几项要读得懂链上数据才核得了,这两项只需要看清钱包弹窗上写的动作名称和收款地址。