以太坊,作为全球领先的智能合约平台,其核心不仅仅在于交易和代币的转移,更在于维护一个不断变化的、全局共享的“状态”,理解以太坊如何在这些状态之间进行转移,是掌握其工作原理的关键,本文将通过图解的方式,直观地解析以太坊状态转移的机制。

什么是以太坊的“状态”

我们需要明确什么是“状态”,在以太坊中,状态可以被看作是在特定区块高度下,整个以太坊网络中所有账户信息的快照,这个状态存储在一个被称为状态树(State Tree)世界状态(World State)的Merkle Patricia Trie数据结构中。

状态主要包括:

  1. 账户(Accounts)
    • 外部账户(EOA, Externally Owned Account):由用户私钥控制的账户,用于发送交易、持有资产,其状态包括:nonce(发送交易次数)、balance(余额)、storageRoot(存储根哈希,指向该账户的存储数据)、codeHash(代码哈希,如果是合约账户则为代码的哈希)。
    • 合约账户(Contract Account):由代码部署创建,由代码逻辑控制,其状态除了包含与EOA相同的nonce、balance、storageRoot、codeHash外,更重要的是其代码和存储在storage中的变量值。
  2. 合约存储(Contract Storage):合约账户内部存储的变量数据,同样组织在一个Merkle Patricia Trie中,其根哈希存储在合约账户的storageRoot字段。

状态 = 所有的账户信息 + 所有合约的存储信息

什么是“状态转移”

以太坊是一个持续运行的区块链网络,新的交易被不断打包进新的区块,每当一个新区块被确认,网络中的状态就会发生一次更新。状态转移(State Transition)就是指从一个已确认区块的状态(S_prev),通过执行该区块包含的所有交易,演变为下一个区块的状态(S_current)的过程。

这个过程可以抽象为一个数学函数: S_current = STATE_TRANSITION_FUNC(S_prev, block_transactions)

这个函数是确定性的:对于给定的S_prev和一组交易,S_current的结果是唯一且可验证的。

状态转移的核心:交易执行

状态转移的核心是交易执行,以太坊虚拟机(EVM)是执行这段“状态转移函数”的引擎,一笔交易从被打包到执行完毕,改变状态,大致流程如下:

  1. 交易发起与广播:EOA创建交易,指定接收方、金额、数据(可选)、gasLimit等,并用私钥签名后广播到网络。
  2. 交易打包入块:矿工(或验证者)从交易池中选取交易,打包进一个新的区块,并进行挖矿(或PoA/PoSV共识)。
  3. EVM执行交易:当新区块被网络接受后,网络中的每个全节点都会执行该区块中的所有交易(按顺序),EVM会读取当前状态S_prev,然后逐笔执行交易,并可能修改状态。
  4. 状态变更与根哈希更新:交易执行过程中,如果涉及账户余额变化、nonce变化、合约存储写入等,状态树中的相应节点数据会被修改,状态树的所有修改完成后,会计算出一个新的根哈希(state_root)。
  5. 状态确认:新区块被打上时间戳,连同新的state_root一起添加到区块链上。S_current成为新的全局状态。

以太坊状态转移图解

为了更直观地理解,我们来看一个简化的状态转移图解:

+----------------+       +-----------------------+       +----------------+
|  前一区块 N     |       |       区块 N+1        |       |  后一区块 N+1   |
|  (状态 S_prev)  | ----> | (包含交易 Tx1, Tx2, ...)| ----> |  (状态 S_current) |
|                |       |                       |       |                |
| - state_root:  |       | - header:             |       | - state_root:  |
|   0xabc123...   |       |   - parent_hash:      |       |   0xdef456...   |  <-- 新状态根
| - accounts:    |       |     0xabc123... (区块N)|       | - accounts:    |
|   - accA:      |       |   - number: N+1       |       |   - accA:      |
|     balance:100|       |   - state_root:       |       |     balance:80  |  <-- 余额变化
|   - accB:      |       |     0x???... (执行后)  |       |   - accB:      |
|     balance:50 |       |   - transactions:     |       |     balance:70  |  <-- 余额变化
|   - ContractC: |       |     - Tx1: accA->accB  |       |   - ContractC:  |
|     storage:   |       |       20 ETH, data:   |       |     storage:   |
|     {x:10}     |       |     - Tx2: 调用C.func |       |     {x:15}     |  <-- 存储变化
+----------------+       |       (修改storage)    |       +----------------+
                         +-----------------------+

图解步骤详解:

  1. 初始状态 (S_prev - 区块 N)

    • 区块N的状态根是随机配图