概述

现实中合约的有效性通常是通过法律来保证的,当合约出现纠纷,或者一方违约,可以通过法律手段解决,如起诉违约人等。

BTC 实现了去中心化的货币,而 ETH 则实现了去中心化的合约,即智能合约。

智能合约是存储在区块链上的一段代码,可以被调用,代码一般使用 Solidity 语言编写,运行在 EVM(Ethereum Virtual Machine)上,是图灵完备的语言,且代码一旦上链,则极难更改,这有效地保证了合约的有效性,但是如果合约代码有漏洞,也极难有修复,ETH 社区就曾经因为这个出现了 Hard Fork。

智能合约对帐户的更改,形成的交易和收据会存储在状态树、交易树和收据树中,并最终通过将三棵树的根哈希值写入 block header 来与其它全节点达成共识。

将介绍下列内容:

  • 合约账户
  • 嵌套调用
  • 汽油费
  • 其它特性

合约账户

智能合约的账户保存了合约的当前状态:

  • balance:余额
  • nonce:交易次数
  • code:合约代码
  • storage:存储,数据结构是一颗 MPT。

嵌套调用

  • 智能合约的执行具有原子性,执行过程中出现错误会导致回滚。
  • 嵌套调用是指一个合约调用另一个合约中的函数。
  • 嵌套调用有时会触发连锁式的回滚,有时则不会。

汽油费

  • 每条语句会消耗一定汽油费(gas)。
  • 复杂的指令消耗的多,简单的指令消耗少,也有一些是免费的。
  • 汽油费由发起调用的外部账户一次性支付,如果没有用完则退还。
  • 如果汽油费耗尽但是合约未执行完毕,则回滚更改,但是不退汽油费。
  • 最终获取记账权的矿工会获得执行合约的汽油费。
  • 执行发生错误的交易依然要支付汽油费。

  • Q:为什么汽油费不足时不退还?
  • A:为了避免恶意的合约调用,如果退还的话,我就可以发布一个合约,里面是死循环代码,然后一直调用这个合约让矿工白白浪费算力,而我却几乎没有任何损失。

  • Q:为什么执行发生错误的交易依然要支付汽油费?
  • A:原因同上,主要是为了反之恶意的合约和恶意的调用。

  • Q:每个区块内部的合约调用消耗的总汽油费是否有限制?
  • A:有限制,记录在header.gasLimit字段,每个矿工在决定了要发布的区块时可以微调这个值,上调或下调\(\frac{1}{1024}\)

其它特性

  • 一个合约内的函数可以调用另一个合约内的函数。
  • 一个外部账户也可以调用一个合约内的函数。
  • 合约代码不支持try....catch,若执行时出现异常会触发回滚。
  • 代码禁止syscall
  • 合约代码只有被外部账户或者其它合约调用时才会执行,无法实现如定时执行等功能。
  • 合约代码不允许多线程。

  • Q:为什么禁止syscall
  • A:因为执行代码的环境不同,syscall返回的结果也可能不同,容易导致最终的状态不一致,不同全节点之间难以达成共识。

  • Q:为什么合约代码不允许多线程?
  • A:多个合约并发/并行执行可能会导致最终的状态无法一致。一个例子:
    1. 合约 A:如果张三账户中至少有 100 个币则转给李四 10 个币。
    2. 合约 B:如果张三账户中不足 100 个币则转给李四 5 个币。
    3. 合约 A 中现在恰好有 100 个币。
    4. 正常的执行顺序是先执行 A,后执行 B。运行结果是李四收到 15 个币,张三的账户剩余 85 个币。
    5. 但是如果是并发/并行执行,则可能先执行 B,等待 B 完成后才执行 A,这种情况下李四收到 10 个币,张三的账户剩余 90 个币。
    6. 也就是最终相关的状态会不一致,也就是状态的转移不确定。各个全节点之间无法达成共识。