以太坊
含AI生成内容
第3章 以太坊
以太坊是目前市场份额仅次于比特币的第二大区块链系统,它在比特币原有的性能和应用场景基础上进行了拓展,是第一个支持智能合约的区块链系统。区块链的应用场景,因以太坊的诞生,从原本单一的加密数字货币交易,延伸到灵活多样的自定义应用设计。本章将介绍以太坊的设计原理,主要包括以太坊的简介和架构、账户及交易、数据结构与存储、共识机制等部分。
🎯 核心考点速览
- 以太坊由 Vitalik Buterin 于 2013 年提出,2015 年 7 月主网上线,正式开启以智能合约为代表的区块链 2.0 时代。
- 以太坊最大的创新是提供了对智能合约的支持,内置图灵完备的编程语言,允许用户开发分布式应用(DApp)及发布代币。
- 以太坊采用账户模型(而非比特币的 UTXO 模型),通过燃料费(Gas)限制合约指令执行,降低被攻击的风险。
- 以太坊有两种账户:外部账户(EOA,由私钥控制)和合约账户(地址由创建者地址+Nonce 计算生成,不由私钥控制)。
- Nonce 是账户累计发起的交易次数计数器,用于防止交易重放攻击和控制交易顺序。
- 智能合约 = 状态模型上的代码执行:状态通过交易来改变,EVM 作为统一执行环境保证各节点执行结果一致。
- Gas 机制解决停机问题:每个操作消耗 Gas,交易预支付 GasLimit,耗尽则交易失败,保证合约在有限时间内终止。
- EVM 是一个 256 位的栈虚拟机,高级语言(Solidity/Vyper)编译为 EVM 机器码执行。
- 交易 9 个字段:From(由签名计算)、To、Value、Data、Nonce、GasPrice、GasLimit、Hash、r/s/v。
- MPT(Merkle Patricia Trie):16 叉压缩前缀树 + 默克尔树,用于组织以太坊的状态树、合约存储树、交易树、收据树。
- 叔块(Uncle Block):不在主链但满足难度、被主链区块记录的合法区块,获得一定奖励(7/8 或 6/8 区块奖励),维护矿工积极性。
- 共识机制:初期采用基于 PoW 的 Ethash 变种算法(增加叔块奖励、内存密集型降低 ASIC 优势);后逐步变更为 PoS(Casper 共识算法)。
3.1 以太坊简介
3.1.1 以太坊的诞生
- 比特币诞生后,出现了莱特币(Litecoin,更快交易确认)、点点币(Peercoin,引入 PoS)、门罗币(Monero,注重安全隐私)等项目,但可扩展性有限,不是面向多种使用场景自定义数字资产的通用系统。
- 比特币脚本语言是图灵不完备的,不足以构建更高级的去中心化应用。
- 2013 年,年仅 19 岁的 Vitalik Buterin 初步提出以太坊设计思想:内置成熟的图灵完备编程语言,将区块链拓展到更多应用场景。
- 2014 年,许多开发者加入,成立以太坊基金会并开始研发。
- 2015 年 7 月,以太坊主网上线,正式开启以智能合约为代表的区块链 2.0 时代。
- 用户可以基于公共区块链平台开发**分布式应用(DApp)**以及发布代币,使区块链商业应用成为可能。
- 以太币(Ether)发展成为继比特币之后的第二大加密数字货币。
- 以太坊经历了多次硬分叉和版本升级,未来计划增加分片技术、新一代虚拟机 Ewasm 等。
3.1.2 以太坊与比特币对比
- 相同点:都是基于区块链技术的公有链平台,节点之间彼此对等而无特权,交易由矿工验证并打包成区块,通过全网广播和共识机制达成分布式账本的一致性,交易数据公开透明、不可篡改。
- 不同点主要体现在技术特点、共识机制和社区3 方面:
(1) 技术方面
- 以太坊最大的创新是提供对智能合约的支持,作为更通用的底层区块链平台为各类 DApp 提供运行环境支撑。
- 以太坊使用账户模型(不同于比特币的 UTXO 模型),账户状态可以实时保存,不需要回溯交易历史。
- 通过**燃料费(Gas)**的设置,对合约指令执行进行限制,降低被攻击的风险。
(2) 共识机制方面
- 初期采用基于 PoW 的 Ethash 变种算法。
- 增加叔块奖励,使未被纳入主链而挖矿成功的区块也能获得奖励,对矿工更加友好和公平。
- Ethash 在执行过程中需要消耗大量内存,从而降低强算力矿机(ASIC)在采矿中的优势,减少受矿池攻击的风险。
- 近年来通过硬分叉升级将共识机制逐步变更为 PoS(Casper 共识算法),并计划通过 Danksharding 分片技术等提高网络可扩展性。
(3) 社区方面
- 以太坊社区相对于比特币社区更为活跃,成员积极探索新技术,多次召开讨论会议,按照既定路线对系统进行版本更新。
- 至本书写作为止,以太坊官方已有 227 个 GitHub 开源项目以及各种技术文档。
3.1.3 以太坊的特色与应用
- 以太坊的出现开启了区块链 2.0 时代——以智能合约为核心的可编程金融时代。
- 在以太坊上,交易不只是加密数字货币的转账,还可以是智能合约的创建和调用。
- 智能合约本质上是一段可以根据预先指定的条件被触发执行的代码,概念早在 1995 年被提出,但因缺乏可靠执行环境而没有被推广使用。
- 以太坊提供**以太坊虚拟机(EVM)**作为智能合约的执行环境,支持图灵完备的高级语言,其中 Solidity 应用最为广泛。
- 合约部署后,系统会为部署的合约对象生成一个合约账户。用户通过向合约账户发送交易并指定调用的函数和参数,进行智能合约的调用。
- 智能合约的执行过程和执行结果通过各节点达成共识,随同交易和账户状态存储在区块链中,一经确认,无法篡改。
基于智能合约的应用场景:
(1) 溯源存证:区块链的交易数据不可篡改、公开、分布式存储,溯源取证数据以交易形式存储在区块链中,用户通过 DApp 接口查询溯源,方便快捷且保证数据防伪可信。
(2) 数字资产发行和流通:用户可以按自己需要定义数字资产(积分、股权、游戏币等),在区块链用户之间自由流通。
(3) 数据共享:区块链数据公开透明、多方验证、不可伪造,适用于征信黑名单共享、车辆路况信息共享、联邦学习场景下的梯度共享等。
(4) DApp 集中于金融交易、游戏等领域。表 3.1 展示以太坊上较流行的 10 个区块链应用,包括:
-
MakerDAO(金融,去中心化借贷应用,成立于 2014 年,采用稳定币 Dai 以及权益和管理代币 MKR 双币模式,Dai 通过超额抵押加密数字货币获得价值,MKR 在用户赎回抵押的 Ether 时会被销毁,累计交易额已高达 20 亿美元)
-
Chainlink(安全,去中心化预言机)
-
KyberNetwork(交换,代币交换协议)
-
Status(钱包,DApp 浏览器,发行代币)
-
My Crypto Heroes(游戏,角色扮演游戏)
-
Uniswap(交换,代币交换协议)
-
CryptoKitties(游戏,宠物养成游戏)
-
Synthetix(金融,去中心化合成资产平台)
-
Basic Attention Token(钱包,Brave 浏览器的激励代币)
-
Knight Story(游戏,角色扮演游戏)
-
Brave 浏览器基于以太坊平台利用 Basic Attention Token 打造了去中心化、透明的数字广告平台,通过加密安全算法保护用户隐私,让用户有选择地观看广告,并使用代币奖励浏览广告的用户和优质的内容商。
3.2 以太坊基本架构及原理
状态转换模型
- 所有账户、余额、智能合约代码、智能合约状态等统称为状态。
- 在区块 N 执行前状态为 S,经过区块 N 的交易进行状态转换后变为 S’,再经过区块 N+1 的转换后变为 S”。
- 智能合约即是作用于该状态机转换的代码,执行状态转换代码的虚拟机是以太坊虚拟机(EVM)。
- 相比比特币的 UTXO 模型,状态转换模型使智能合约的各种变量存储、传参等更加灵活,但也带来了多方共识上的困难(如分叉时的处理)。
分叉与状态回滚
- 公有链上由于网络延迟,区块链分叉极为常见。
- 比特币系统分叉时只需重新计算回滚 UTXO 集合即可继续验证新区块。
- 以太坊状态转换模型中,分叉时需要回到分叉前的状态,重新验证另一条分支上的区块。快速回滚成为难点。
- 以太坊采用独立于区块链存在的树状结构存储状态,该状态树的树根登记在区块中,记为 stateRoot,使状态能够在全网得到共识确认,并在分叉时能够快速回滚。
简易架构组成
(1) 区块链数据:由前后相连的区块组成,每个区块包含区块头和区块体(即交易),此外还包含状态数据、收据数据等。
(2) 智能合约:按一定预先设定的逻辑改变以太坊状态的代码,存储在以太坊状态数据中,由区块链交易进行触发,使以太坊状态发生改变。
(3) 节点:保存有区块链数据的节点,每个节点独立地维护区块链数据,执行智能合约,并通过 P2P 网络进行通信,采用共识机制(如 PoW)达成共识,维护全网的状态一致。
基本原理示例(张三买电影票)
- 网络中所有节点独立地维护区块链数据。
- 每当有新的区块链交易产生时,各节点从各自冗余的区块链数据中读取智能合约代码、状态等信息,并独立地在 EVM 中进行执行。每个节点都会独立完成校验和计算。
- EVM 的执行结果将写回到区块链数据中,每个区块中保留状态根的摘要(stateRoot),任一子状态的不同都将导致 stateRoot 完全不同的。
- 如果节点受到非法攻击或篡改,执行结果及区块链数据将与网络中其他节点不符,无法参与网络的下一步共识。
3.3 账户模型与转账
3.3.1 账户模型
1. 地址与账户
- 以太坊采用账户模型(类似银行系统),账户记录保存在所有以太坊节点中,由整个以太坊系统来确定记录用户的账户行为,整个过程是去中心化的。
- 用户通过非对称密码学进行认证和控制:私钥 → 公钥 → 以太坊地址。
- 地址是用户的身份标识,账户代表这个地址对应的信息(如持有的以太币数额)。
2. 地址的生成
- 计算椭圆曲线下的私钥与公钥(与比特币采用相同的椭圆曲线)。
- 对公钥进行 Keccak-256 哈希运算,得到一个 256 位的哈希值。
- 截取哈希值的**最后 40 位(160 位)**作为一个以太坊地址。
3. 账户结构
- 以太坊账户结构保存了地址对应账户的数据信息,主要有:
- 余额(Balance):记录当前地址持有的以太币数额,单位是 Wei(以太坊最小货币单位,1 Ether = 10^18 Wei)。
- Nonce:记录这个地址创建以来累计发起的交易次数,每当从该地址发起的交易得到确认之后,账户的 Nonce 值便会相应增加。
3.3.2 转账
- 以太坊的转账使用交易来进行,类似日常生活中的转账行为。
- Alice 向 Bob 转账 1 ETH:生成一个
From: Alice, To: Bob, Value: 1 ETH的转账记录。确认后,Alice 账户金额减少 1 ETH,Bob 账户增加 1 ETH。 - 交易数据通过 Alice 的私钥进行签名后得到数字签名。节点通过签名验证流程还原得到 Alice 的公钥,进而计算出 Alice 的地址。如果交易受到篡改,计算得到的地址便是错误的,无法通过篡改 Alice 签名过的交易来攻击 Alice 的账户。
3.3.3 Nonce
- 在比特币中,交易一旦执行,交易的输入便会被花费导致无法再次使用。
- 以太坊模型中,交易合法性检验在于转账发起者的账户余额。如果没有其他手段使发起过的交易失效,那么交易可以被无限次地重新发起而不需要发起者的同意(因为签名始终有效)——这就是交易重放攻击。
- 为此,以太坊系统增加了用于计算交易次数和序列的 Nonce 计数器:只有账户的 Nonce 和交易的 Nonce 能够对应的情况下,交易才是合法的。
- 交易执行完毕后,账户的 Nonce 值增加,原本交易中的 Nonce 值无法与现在账户的 Nonce 值匹配,已执行的交易便失效。
- 如果修改 Nonce 值,那么原有交易的签名失效,需要发起者的重新签名。
- 通过 Nonce 值的设计,可以有效地保证交易最多只能够被执行一次,避免了交易重放攻击。
- 额外功能:Nonce 值还能用来控制账户发起交易的顺序。可以通过重复提交一个相同 Nonce 值的交易来使一个已经提交但是尚未被确认的交易变得不合法,从而实现一定程度的撤销功能。
3.4 智能合约
3.4.1 状态模型
- 账户模型的优点是简单。相比于比特币的 UTXO 模型,账户模型可以不用维护大量 UTXO 数据,还能方便地扩展实现智能合约的完整体系——这个扩展便是状态模型。
- 把账户的余额泛化成一种账户的状态,把转账交易当作改变账户状态的变换规则(售出是一种状态变换规则,签到也是一种变换规则)。
- 只要约定好的变换规则是固定的,不会产生二义性的结果,就可以不断地通过变换规则不停作用到原有状态上产生新的状态。
- 对于有着相同初始状态的参与者来说,经过完全相同的变换过程,最后必然得到完全相同的当前状态。只要在所有参与者之间对执行的交易达成统一,对于当前的状态便可以达成一致的认识。
- 原有账户模型系统只是状态模型的一个有限子集(存储账户余额和实现账户转账)。扩充完毕的状态模型不仅能够实现原有的账户和转账功能,还能够实现以太坊对于智能合约的支持。
3.4.2 智能合约简介
- 智能合约是一段在区块链上执行的代码,它依托于区块链系统在参与者之间实现对执行的一致认可。
- 以太坊对于区块链技术体系最突出的贡献莫过于将智能合约引入区块链中。
1. 状态模型上的代码执行
- 把计算机程序执行过程中的变量等数据一同存储到区块链上,而交易的过程则是执行一段大家约定好的计算机程序。
- 这些存储的数据便是一个状态,而执行的代码便是一种特定的变换——符合状态模型的语义。
- 唯一需要规定的是执行的过程只能是一个单射:不能出现类似于随机数的实现,因为随机数对于同一个输入得到的是不同的输出。
- 智能合约利用提前约定好的代码来管理和变化存储在以太坊上的状态变量,利用智能合约的代码来自定义交易过程中的状态变换过程,实现”世界状态机”。
2. 合约账户与数据存储
- 表示合约的账户称为合约账户,它的地址并不是由公钥产生,而是通过特定的算法在创建合约时生成的。
- 原本的表示用户余额的账户则被称为外部账户(Externally Owned Accounts, EOA),因为受外部私钥控制。
- 合约账户的数据结构在外部账户的基础上,主要扩展了存储合约代码和合约存储的字段:
- 合约机器码保存在合约机器码字段中。
- 合约状态存储另外保存在一个存储映射表中,账户内部只保留整个存储表的哈希值(Storage Root)。如果存储表中变量的数值发生了变化,那么对应整个存储表的哈希值也会发生变化。
- 注意:合约账户不由公钥和私钥进行控制,不能从合约地址发起交易(指记录到区块中的交易)。合约账户在受到外部账户的触发后可以产生新的内部调用,一般被称为内部交易——不体现在区块中,而是执行过程内部产生的交易。
3. 合约地址的生成
- 有两种方法:
- 方法一:通过合约创建者的地址和 Nonce 计算得到。将创建者地址和 Nonce 通过 RLP(Recursive Length Prefix)编码序列化,再通过 SHA256 计算得到 256 位哈希值,取最后 160 位作为新建合约的地址。
- 方法二:通过合约创建者地址、指定的初始化值和合约代码的哈希值计算得到,使开发者能更好地确定部署合约的地址。
- 由于创建合约需要使用 Nonce 值,合约创建合约时,创建其他合约的合约账户的 Nonce 值需要改变,否则每一次计算得到的新合约地址都是相同的。
3.4.3 驱动智能合约
- 在状态模型的框架下,以太坊的状态通过交易来改变,智能合约的状态变化同样使用交易来实现。
- 智能合约的每次运行都是通过交易来驱动的。
1. 调用合约
- 在一次交易中,以太坊按照事先的约定执行智能合约的代码,最后得到运行的结果(如修改了合约的数据)。
- 智能合约作为交易的接收方,按照交易发起者指定的函数和参数进行执行,这个过程也称为合约调用。
- 交易中加入了 data 字段用于存放调用函数和参数。
- ABI(Application Binary Interface,合约二进制接口规范):编译时使用函数名与参数类型构成的字符串的哈希值作为调用过程中函数的索引(取 SHA256 哈希值的前 64 位/前 4 字节,即函数选择器),调用函数需在这个哈希值后面附上经过序列化编码的参数。
2. 创建合约
- 合约的创建(部署)也是一种状态变化,同样通过发送交易来实现。
- 特殊处理:
- To 字段始终为 0(空地址)。对于接收地址为 0 的交易,以太坊认为是一个创建合约的交易。然后根据发送者地址和 Nonce 值计算出合约账户地址。
- 交易 data 字段中存放的是合约代码和初始化代码,EVM 直接运行 data 字段中的内容,运行之后得到合约的初始状态。
3. 停机问题与 Gas
- 如果交易调用的智能合约存在死循环,那么这个交易的执行将不会停止——这是著名的停机问题,图灵(Alan Mathison Turing)在 1936 年证明停机问题是不可解的。
- 为了规避停机问题,以太坊使用 Gas 机制来保证智能合约能够在有限时间内终止。
- 类比:汽车运行消耗燃油,携带燃油有限,运行时间必然有上限。如果没有燃油的永动车,不知道它何时会自己停下来。
- 以太坊智能合约运行的每一个操作(计算、存储等)都规定了需要消耗的 Gas 数值,并要求交易发起者预先支付 Gas 额度。每次运行智能合约代码时,每一步操作都会消耗一些预先支付的 Gas 值,直到交易中预支付的 Gas 额度被消耗殆尽。
4. 以太坊虚拟机(EVM)
- EVM 是一个 256 位的栈虚拟机:
- 256 位:指执行过程中的数据宽度是 256 位(相比之下普通 x86 或 ARM 架构通常是 32 位或 64 位)。
- 栈虚拟机:指 EVM 的执行流程基于一个栈结构,所有的指令都是操作栈顶的数据。
- 开发者通常使用 Solidity 或 Vyper 等高级语言编写智能合约代码,然后将其编译成 EVM 能够识别的机器码指令,最后才能够在以太坊平台上使用 EVM 执行。
3.5 交易
3.5.1 交易内容
以太坊交易承载了账户转账和创建、调用合约等功能,包含以下属性:
| 字段 | 说明 |
|---|---|
| From | 交易发送者的地址。通过合约的签名信息 r、s、v 计算得到,交易数据结构中并不会存储发送者地址 |
| To | 交易接收者的地址。转账时是接收转账金额的地址;创建合约时设置为空(0x0000…000);调用合约时是合约的地址 |
| Value | 交易的金额,单位是 Wei(1 Ether = 10^18 Wei) |
| Data | 交易附带数据,传递创建合约的代码和构造函数,或调用合约的函数及参数 |
| Nonce | 交易发送者累计发出的交易数量,用于区分一个账户的不同交易及顺序 |
| GasPrice | 发送者支付给矿工的 Gas 价格,用于实现从 Gas 到以太坊货币单位的转换 |
| GasLimit | 该交易允许消耗的最大 Gas,用于解决智能合约不能停机的问题 |
| Hash | 由以上字段生成的哈希值,也作为交易 ID |
| r, s, v | 用于 ECDSA 验证的参数,由发送者的私钥对交易的哈希做数字签名生成,用于确认转账的合法性 |
- 注意:交易本身不携带时间戳(Timestamp)属性,智能合约开发者会以交易打包进区块的时间戳作为其执行时获取的时间戳。
3.5.2 交易费用
- Gas 机制不仅可以保证合约停机,同时可以对交易执行的成本进行归一化计算。
四个主要概念:
| 概念 | 说明 |
|---|---|
| Gas | 以太坊中资源消耗的基础单位 |
| GasLimit | 允许消耗的最大 Gas 值 |
| GasUsed | 执行后交易消耗的 Gas 值 |
| GasPrice | 用户为消耗的每个 Gas 单位支付的以太币 |
- 每笔交易都带有基础 Gas 消耗值,用户创建或调用智能合约的过程中,对 EVM 的不同操作都将消耗不同值的 Gas。基础交易 Gas 值加上 EVM 运行时的 Gas 消耗值,构成交易的 GasUsed。
- GasUsed 是实时计算的,如果交易的 GasUsed 超过了用户定义的 GasLimit,则判定为 Gas 不足,交易执行失败。
- 交易执行完成后:手续费 = GasUsed × GasPrice,从交易发起账户扣除,加到区块 Coinbase 账户中。打包区块的节点除了得到区块奖励外,还将得到运行以太坊智能合约的手续费。
- Gas 机制的弊端:EVM 每个操作的定价是社区开发者决定的,定价的合理性时常受到质疑。在以太坊 200 多万的区块高度上,曾经出现过针对 Gas 定价不合理的攻击。
- EIP-1559:社区提出对 Gas 计费机制进行了更新,但对该机制的采纳态度不一,本书不做过多叙述。
3.5.3 交易的周期
以太坊交易在网络中的周期分为发起、广播、打包与执行、验证与执行4 个阶段。
1. 发起
- 用户在本地钱包软件中选择发送地址(From)、输入目标地址(To)、金额(Value)、是否部署或调用合约(Data)、手续费单价(GasPrice)等,确认发送至以太坊节点。
- 钱包软件自动得出 GasLimit 和 Nonce 值,使用私钥得到 r、s、v,最终将交易序列化后发送到网络中。部分客户端中 GasLimit 与 Nonce 也可以是用户自定义的。
2. 广播
- 节点收到交易后,对交易进行验证:交易的签名、发起账号的余额是否能支付转账金额与手续费、交易的 Nonce 值是否为账号已发出的交易数等。
- 验证通过后,将交易加入节点的交易池中。交易池存储着待打包的交易,这一过程对区块链数据结构本身没有影响。
- 节点还会根据 P2P 网络广播的策略向相邻节点继续广播该交易。
3. 打包与执行
- 具有挖矿功能的全节点开始打包下一个区块,一般按 GasPrice 取出具有较高手续费的交易。
- 节点将交易逐个执行,根据目标地址(To)值的不同分为三类:
| 类型 | To 字段 | 执行过程 |
|---|---|---|
| 创建合约交易 | 空(0x000…000) | EVM 根据 From 及 Nonce 生成合约地址,执行 Data 中对应的智能合约代码(含合约本身及其构造函数),最终将 EVM 代码存储到合约地址中 |
| 调用合约交易 | 合约账户地址 | EVM 从世界状态中获取 To 地址中存储的 EVM 代码,并执行 Data 字段中包含的调用函数及参数。本质是对合约状态的修改 |
| 普通转账交易 | 外部账户地址 | 直接将以太币金额从 From 转到 To |
- 每笔交易执行后会生成交易的收据(Receipt),其中带有新建的合约地址、消耗的 Gas 总量、交易生成的事件日志(event 或 log)等。
- 记账节点在打包交易并获得合法的区块后,将区块(含交易数据)广播到网络中的相邻节点。
4. 验证与执行
-
未获得记账权的节点收到广播的区块后,对区块进行合法性验证,并进行交易的执行。验证内容与执行过程与打包阶段相同,目的是保证智能合约执行的去中心化。
-
注意:在实际通信的原始报文中,不需要包括 Hash 和 From——Hash 可以根据交易本身内容得到,From 是通过结合 r、s、v 等综合计算得到的。交易 Nonce 值的存在及验证,则是为了保证交易发送者能够控制交易的确认顺序(P2P 网络中交易可能乱序到达节点)。
3.6 数据结构与存储
3.6.1 区块与叔块
- 以太坊的区块同样分成区块头和区块体。
- 区块体中除了交易列表之外,还保存了收据列表(交易执行信息组成)以及叔块列表。
- 区块头中增加了收据列表和叔块列表对应的哈希值,以及用于记录以太坊状态的状态根(State Root),此外还有一个最长不超过 100KB 的额外数据,可以在挖矿时填入自定义信息。
1. 世界状态
- 将以太坊中所有存在账户的状态全部汇总到一起,可以得到一个全局的状态,称为世界状态(World State)。
- 区块头中保存了对世界状态的信息摘要,即状态的哈希值,称为状态根(State Root),这是通过一个特殊的树状哈希数据结构(MPT)实现的。
2. 叔块(Uncle Block)
- 定义:不在主链但被主链区块记录的满足难度的区块。合法但被发现得晚些或网络传输慢些,导致没能进入主链。
- 目的:在尽可能减少两个相邻区块产生时间的条件下,尽量收缩和统一整个区块链的主链,同时通过叔块激励维护矿工积极性。
- 奖励:叔块被纳入主链后,获得一定收益补偿(上面的区块获得 7/8 区块奖励,下面的获得 6/8 区块奖励)。
- 矿工打包区块时可以自主选择合法的 0~2 个叔块打包到新区块的区块头。
- 拜占庭硬分叉(Byzantium Fork)之后,拥有叔块的区块在进行难度计算时会有更多优势,在主链选举上有优势,进一步提高矿工纳入叔块的积极性。
3. 收据(Receipt)
- 收据是对应交易的数据结构,代表交易执行的一些中间状态的写入和交易的执行结果等信息。
- 智能合约向虚拟机输出执行日志(event/log),这些日志保存在收据中。
- 收据中还保存智能合约运行的 Gas 信息,以及单个交易执行完毕后以太坊的状态根。
- 如果交易创建智能合约(To 为空)且执行成功,还会把新建合约的地址写到收据中。
- 注意:对于一个以太坊节点来说,收据完全可以通过执行对应的交易来得到,因此收据的保存通常是额外的部分,不保存收据并不影响区块的完整性。网络传输时区块链数据结构里不会包含收据列表,但节点通常会保存收据以方便下次直接获取。
3.6.2 Merkle Patricia Trie(MPT)
为什么需要 MPT?
- 以太坊需要在每个区块头中对全体地址和账户的哈希值进行计算,得到世界状态的状态根。
- 直接把整个地址到账户的映射表拼接成二进制进行哈希计算开销巨大——单个区块中的全部交易可能仅仅改变了几十个账户中的状态,但仍要重新遍历数万甚至数千万的账户来计算状态根。
- 比特币中的默克尔树(Merkle Tree)可以在更改列表元素的情况下快速重新计算哈希值,但对于以太坊全体账户这种映射表,潜在列表长度是 2^160(因为以太坊地址长度是 160 位),构成的默克尔树将非常大,总共有 2^161 个节点,根本不可能存储。
- 以太坊使用了 **16 叉压缩前缀树(Radix Tree)**作为地址到账户的索引,然后利用默克尔树的思想对每一层节点合并计算所有子节点的哈希值,最终得到一个根哈希值。这种新的数据结构被称为 Merkle Patricia Trie(MPT),兼具默克尔树和压缩前缀树的优点。
1. 压缩前缀树
- 压缩前缀树是指由公共前缀组成的一棵字典树,来源于日常生活中查字典的概念。
- 将共同前缀的部分组成一个子树,然后随着层级向下逐层细分(如单词 Alice、Airline、Airplane 构成的前缀树)。
2. MPT 的构建
- 首先,按照所有数据的地址(或键值)来构建一棵压缩前缀树。地址以十六进制编码,使用 0
9、af 这 16 个字符作为每一个编码单元(每个单元大小正好是 4 位,即半字节,以太坊中称为 Nibble)。 - 然后,按照构建得到的压缩前缀树,从叶子节点开始,逐步计算每一层的哈希值,并将其汇合到父节点中(与 Merkle Tree 的计算过程类似)。
- 计算步骤:
- 计算叶子节点的哈希值:空前缀 + 数据的哈希值。
- 计算父节点的哈希值:前缀 + 所有子分支的哈希值一同进行哈希运算(不存在的分支值为空)。
- 逐层计算得到根哈希值。
3. 以太坊中的三棵 MPT 树
| 树 | 键 | 值 | 用途 |
|---|---|---|---|
| 状态树(State Trie) | 账户地址 | 账户详细信息(Nonce、余额、Storage Root、代码哈希) | 记录各账户状态,每个区块的状态根记录在区块头中 |
| 合约存储树(Storage Trie) | 存储地址(256 位整数) | 存储值 | 记录合约账户的存储映射表,Storage Root 保存在合约账户数据结构中。由于存储单元大小正好是 256 位,叶子节点可以直接使用同等长度的存储数据作为计算 |
| 交易树/收据树 | 交易/收据在区块中的序号 | 交易/收据内容 | 快速查找和证明。序号连续,MPT 的压缩前缀没有起到作用,数据结构上等价于一棵 16 叉 Merkle Tree |
4. 状态树的变换
- 例如当前区块世界状态中有 A、B、C 三个账户,经过交易 T 之后,账户 C 的状态变成了 C’,那么下一个状态树的根为 S’,将交易 T 和状态树根 S’ 记录在下一个区块中。
3.6.3 布隆过滤器(Bloom Filter)
- 以太坊中通过布隆过滤器对收据的日志进行索引。
- 布隆过滤器可以用于检索一个值是否在一个集合中。在容忍一定的误识别率的条件下,它有着远超一般算法的空间效率和时间效率。
- 原理:通过多个哈希函数将键值映射到位图中,并在位图中合并集合中所有键值的映射结果。
- 对于一个键值,如果经过同样的哈希函数映射之后,出现了在位图中没有出现的标记位,那么这个键值必定不存在于集合之中。
- 即使不会出现新的标记位,它也不一定存在于集合之中,这时需要进一步搜索。
3.7 共识机制(要点总结)
- 以太坊初期采用基于 PoW 的 Ethash 变种算法作为共识机制。
- 叔块奖励:增加对未纳入主链但满足难度区块的奖励,使矿工更加友好和公平。
- 内存密集型设计(Ethash):Ethash 在执行过程中需要消耗大量内存,从而降低强算力矿机(ASIC)在采矿中的优势,减少矿池攻击的风险。
- 演进方向:通过硬分叉升级将共识机制逐步变更为 PoS 机制,采用更高效的 Casper 共识算法,并计划通过 Danksharding 分片技术等提高网络可扩展性。
📌 名词解释(高频)
| 术语 | 定义 |
|---|---|
| 以太坊 Ethereum | 由 Vitalik Buterin 提出的支持智能合约的区块链系统,目前市场份额仅次于比特币的第二大区块链平台 |
| 以太币 Ether | 以太坊的主要加密数字货币,继比特币之后的第二大加密数字货币 |
| 以太坊虚拟机 EVM | Ethereum Virtual Machine,256 位的栈虚拟机,为智能合约提供统一执行环境 |
| 智能合约 Smart Contract | 一段在区块链上执行的代码,可以根据预先指定的条件被触发执行,依托于区块链系统在参与者之间实现对执行的一致认可 |
| 账户模型 | 以太坊采用的类似银行账户的记录方式,用地址对应的余额和 Nonce 记录用户状态,区别于比特币的 UTXO 模型 |
| 外部账户 EOA | Externally Owned Accounts,由外部私钥控制的用户账户,可以主动发起交易 |
| 合约账户 | 表示智能合约实例的账户,不由私钥控制,地址由创建者地址和 Nonce 计算生成,包含合约代码和存储 |
| Nonce | 账户累计发起的交易次数计数器,用于防止交易重放攻击和控制交易顺序 |
| Gas | 以太坊中资源消耗的基础单位,每个 EVM 操作都需要消耗一定量的 Gas |
| GasLimit | 交易允许消耗的最大 Gas 值,用于解决智能合约不能停机的问题 |
| GasPrice | 用户为消耗的每个 Gas 单位支付的价格(以以太币计) |
| GasUsed | 执行后交易实际消耗的 Gas 值 |
| 世界状态 World State | 以太坊中所有账户状态的全局汇总 |
| 状态根 State Root | 区块头中保存的世界状态的哈希值,通过 MPT 计算得到 |
| 叔块 Uncle Block | 不在主链但被主链区块记录的满足难度的合法区块,可获 7/8 或 6/8 区块奖励 |
| 收据 Receipt | 对应交易的数据结构,记录了交易执行结果、Gas 信息、日志等中间状态 |
| MPT | Merkle Patricia Trie,16 叉压缩前缀树 + 默克尔树,用于组织以太坊状态、存储、交易和收据数据 |
| 布隆过滤器 Bloom Filter | 用于高效检索一个值是否在集合中的概率型数据结构,以太坊中用于收据日志索引 |
| RLP | Recursive Length Prefix,以太坊自定义的序列化编码格式 |
| ABI | Application Binary Interface,合约二进制接口规范,定义函数调用的编码方式 |
| DApp | Decentralized Application,基于区块链平台的分布式应用 |
| EOA | Externally Owned Accounts,外部账户 |
| Ethash | 以太坊初期采用的内存密集型 PoW 共识算法变种 |
| Casper | 以太坊向 PoS 过渡的共识算法 |
| Wei | 以太坊最小货币单位,1 Ether = 10^18 Wei |
| Solidity | 以太坊智能合约最广泛使用的高级编程语言 |
⚖️ 重点对比 / 易混区分
以太坊 vs 比特币
| 对比项 | 比特币 | 以太坊 |
|---|---|---|
| 创始人 | 中本聪(Satoshi Nakamoto) | Vitalik Buterin(2013 年提出时仅 19 岁) |
| 上线时间 | 2009 年 1 月 | 2015 年 7 月 |
| 数据模型 | UTXO 模型 | 账户模型 |
| 脚本/合约 | 图灵不完备的脚本语言 | 图灵完备的智能合约(Solidity 等) |
| 虚拟机 | 无通用虚拟机 | EVM(256 位栈虚拟机) |
| 资源限制 | 区块大小 1MB | Gas 机制(GasLimit) |
| 交易费用 | 交易手续费 | GasUsed × GasPrice |
| 共识机制 | PoW(SHA-256) | PoW Ethash(初期)→ PoS Casper(演进) |
| 分叉处理 | 回滚 UTXO 集合 | 状态树 stateRoot 快速回滚 |
| 区块结构 | 区块头 + 交易列表 | 区块头 + 交易列表 + 收据列表 + 叔块列表 |
| 出块奖励 | 仅主链区块有奖励 | 主链区块 + 叔块也有奖励 |
| 特色功能 | 加密数字货币流通 | 智能合约 + DApp + 自定义数字资产发行 |
| 吞吐量 | 约 3~7 笔/秒 | 约 10~20 笔/秒 |
| 最小货币单位 | 1 BTC = 10^8 聪 | 1 Ether = 10^18 Wei |
外部账户(EOA)vs 合约账户
| 对比项 | 外部账户(EOA) | 合约账户 |
|---|---|---|
| 控制方式 | 由私钥控制 | 由合约代码控制 |
| 地址生成 | 公钥 → Keccak-256 → 取后 160 位 | 创建者地址 + Nonce → RLP 编码 → SHA256 → 取后 160 位(方法一);或创建者地址 + 初始化值 + 代码哈希(方法二) |
| 能否主动发起交易 | 能 | 不能(只能由外部账户触发后产生内部交易) |
| 数据结构 | Nonce + Balance | Nonce + Balance + 合约机器码 + Storage Root |
| 用途 | 用户持有以太币、发起转账/部署合约/调用合约 | 存储和执行智能合约代码与状态 |
账户模型 vs UTXO 模型
| 对比项 | UTXO 模型(比特币) | 账户模型(以太坊) |
|---|---|---|
| 设计理念 | 基于未花费交易输出 | 类似银行账户系统 |
| 状态查询 | 需回溯交易历史 | 状态实时保存在账户中 |
| 防止双花/重放 | UTXO 被花费后不可再用 | 通过 Nonce 计数器防止交易重放 |
| 智能合约支持 | 不支持(脚本语言图灵不完备) | 支持(状态模型扩展实现智能合约) |
| 数据量 | 需维护大量 UTXO 数据 | 相对简洁,但需要维护状态树 |
交易类型对比(按 To 字段)
| 交易类型 | To 字段 | Data 字段 | 执行过程 |
|---|---|---|---|
| 创建合约交易 | 空(0x000…000) | 合约代码 + 初始化代码 | 生成合约地址,执行 Data 中的代码,存储到合约地址 |
| 调用合约交易 | 合约账户地址 | 函数选择器(4 字节)+ 编码参数 | 获取合约 EVM 代码并执行,修改合约状态 |
| 普通转账交易 | 外部账户地址 | 通常为空 | 直接修改 From 和 To 账户的余额 |
以太坊中的三棵 MPT 树
| 树 | 键 | 值 | 保存在 |
|---|---|---|---|
| 状态树 | 账户地址 | 账户详细信息 | 区块头(State Root) |
| 合约存储树 | 存储位置(256 位) | 存储值 | 合约账户(Storage Root) |
| 交易树 / 收据树 | 交易/收据序号 | 交易/收据内容 | 区块头 |
❓ 可能考题
选择题(课后题)
- EVM 是一个(C. 256)位的栈虚拟机。
- 以太坊使用的交易模型是(B. 账户模型)。
- 以太坊使用的序列化方法是(C. RLP)。
- 以太坊交易字段中(D. Nonce)是用来区别同一用户发出的不同交易的标记。
- 关于以太坊用户地址说法正确的是(B. 可能重复但概率很小)。
- 当前以太坊使用了(D. 16)叉压缩前缀树,作为地址到账户的索引。
- 关于以太坊的匿名性,说法正确的是(C. 中心化交易所交易不是匿名的)。
- 以太坊中一笔交易发送出去后(B. 只能发送同一个 Nonce 值的交易来替代)。
- 以太坊用户创建合约时应向(C. 空地址)发送交易。
- Alice 给 Bob 转了 1 个 ETH,那么为他们记账的是(B. 矿工)。
填空题(课后题)
- 表示交易发送者累计发出的交易数量,用于区分一个账户的不同交易及顺序的是:Nonce
- 以太坊中通过___对收据的日志进行高效查询:布隆过滤器(Bloom Filter)
- 与比特币相比,以太坊的一大创新是提供了___,它是图灵完备的:智能合约
- 用于限制该交易允许消耗的最大 Gas,也用于解决智能合约不能停机的问题的参数是:GasLimit
- 以太坊定义了不在主链但被主链区块记录的满足难度的区块为:叔块(Uncle Block)
- 以太坊用___来保证智能合约能够在有限时间内终止:Gas 机制
- 对于一个接收地址为___的交易,以太坊都会认为是一个创建合约的交易:0(空地址)
- 以太坊外部账户由用户用___控制:私钥
- 以太坊的合约账户包括四个字段:Nonce、账户的余额、合约代码、合约存储(Storage Root)
- 以太坊虚拟机的主要作用是:为智能合约提供统一的执行环境,保证各节点执行结果一致
简答题
-
以太坊的一笔交易主要包含哪些信息?
- From(发送者地址)、To(接收者地址)、Value(金额)、Data(附带数据)、Nonce(交易计数)、GasPrice、GasLimit、Hash(交易 ID)、r/s/v(ECDSA 签名参数)。
-
什么是以太坊虚拟机?该虚拟机是基于什么结构的?
- EVM 是 256 位的栈虚拟机,为智能合约提供统一的执行环境。“256 位”指执行过程中的数据宽度是 256 位;“栈虚拟机”指执行流程基于一个栈结构,所有指令操作栈顶的数据。
-
为什么以太坊要有状态根 stateRoot?
- 因为以太坊采用状态模型,需要在区块中记录全网状态的一致性摘要。状态根使状态能够在全网得到共识确认,并在分叉时能够快速回滚到分叉前的状态,而不需要复制存储每个区块对应的完整状态。
-
以太坊中的三棵树的作用是什么?
- 状态树:记录各账户状态,状态根记录在区块头中。
- 合约存储树:记录合约账户的存储映射表,Storage Root 保存在合约账户数据结构中。
- 交易树/收据树:对交易和收据进行快速查找和证明。
-
以太坊账户的 Nonce 值和区块的 Nonce 值有什么区别?
- 以太坊账户的 Nonce 值:记录该账户累计发起的交易次数,用于防止交易重放攻击和控制交易顺序。
- 区块的 Nonce 值:PoW 挖矿过程中不断尝试的随机数,用于使区块哈希值满足难度要求。
-
以太坊与比特币有什么共同点和不同点?
- 共同点:都是基于区块链技术的公有链平台,节点对等无特权,交易由矿工验证打包,通过共识机制达成分布式账本一致性,数据公开透明不可篡改。
- 不同点:技术方面(以太坊支持智能合约、使用账户模型、Gas 机制);共识机制(Ethash vs SHA-256 挖矿,增加叔块奖励);社区(以太坊社区更活跃)。
-
用户在以太坊中发出一笔交易后,在交易被真正确认前,可以如何反悔?
- 可以通过发送一个相同 Nonce 值但更高 GasPrice 的交易来替代原交易,使原交易变得不合法,从而实现一定程度上的撤销功能。
-
简单介绍以太坊交易的周期。
- 分为四个阶段:发起(用户用钱包创建并签名交易)→ 广播(节点验证后加入交易池并广播给相邻节点)→ 打包与执行(矿工按 GasPrice 选取交易打包成区块,执行交易并生成收据)→ 验证与执行(其他节点收到区块后重新验证和执行,确保去中心化一致性)。
-
以太坊中叔块的作用是什么?
- 在尽可能减少两个相邻区块产生时间的条件下,尽量收缩和统一整个区块链的主链;通过叔块激励(7/8 或 6/8 区块奖励)来维护矿工的积极性;拥有叔块的区块在难度计算中有优势,在主链选举上有优势。
-
以太坊中外部账户和合约账户的区别是什么?
- 外部账户由私钥控制,可以主动发起交易;合约账户由合约代码控制,不能主动发起交易,只能在被外部账户触发后产生内部交易。
- 外部账户数据结构为 Nonce + Balance;合约账户为 Nonce + Balance + 合约机器码 + Storage Root。
- 外部账户地址由公钥计算得到;合约账户地址由创建者地址和 Nonce 计算得到。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!










