以太坊技术
以太坊
以太坊(Ethereum)是一个支持智能合约的区块链平台,它与比特币最大的不同是,以太坊通过一个虚拟机(EVM)可以运行智能合约。
以太坊是Vitalik Buterin(维塔利克·布特林,人称V神)在2013年提出的概念,Vitalik最早参与了比特币社区的开发,并希望比特币把功能受限的脚本扩展成图灵完全的编程环境,但没有得到比特币开发社区的认同,于是他决定另起炉灶,打造一个新的区块链平台,目标是运行去中心化的程序。
以太坊从2015年正式启动并运行,期间经历过DAO攻击造成的硬分叉。和比特币类似,以太坊也通过PoW进行挖矿,后改为PoS挖矿,其挖出的平台币叫以太币(Ether),目前每个区块奖励是2 Ether,约13~15秒左右出一个块。
和比特币相比,以太坊在以下几点上有所不同:
账户模型
比特币使用的UTXO模型是一种对开发友好、易于实现清结算的模型,但对用户不友好,因为普通用户所认知的账户是一个账号、对应余额变动的模型。以太坊的账户模型和比特币不同,它就是余额模型,即交易引发账户余额的变动,这与传统金融账户一致。
智能合约
从比特币的可编程支付原理可知,任何支付实际上都是在执行比特币脚本,只有脚本成功执行,支付才能成功。
以太坊的交易与之类似,并且更进一步,它实现了一个图灵完备的脚本语言,运行在EVM(Ethereum Virtual Machine,以太坊虚拟机)中,任何人都可以编写合法的脚本来执行任意逻辑(有很多限制),例如,定义一种新的代币,抵押贷款等。
账户
以太坊账户负责存储用户的以太坊余额。对大多数普通用户来说,以太坊账户和银行账户非常类似,通常只需要一个账户即可。
确切地说,以太坊账户分为外部账户和合约账户两类:
- 外部账户:即普通用户用私钥控制的账户;
- 合约账户:一种拥有合约代码的账户,它不属于任何人,也没有私钥与之对应。
本节我们仅讨论普通用户使用的外部账户。
和比特币类似,一个以太坊账户就是一个公钥哈希后得到的地址,它是由一个私钥推导出对应的公钥,然后再计算出地址。其中,私钥与公钥算法与比特币完全相同,均为secp256k1椭圆曲线,但和比特币不同的是,以太坊采用非压缩公钥,然后直接对公钥做keccak256哈希,得到32字节的哈希值,取后20字节加上0x前缀即为地址
因为以太坊和比特币的非对称加密算法是完全相同的,不同的仅仅是公钥和地址的表示格式,因此,比特币的HD钱包体系也完全适用于以太坊。用户通过一套助记词,既可以管理比特币钱包,也可以管理以太坊钱包。
以太坊钱包的派生路径是m/44'/60'/0'/0/0
因为以太坊采用账户余额模型,通常情况下一个以太坊地址已够用。如果要生成多个地址,可继续派生m/44'/60'/0'/0/1、m/44'/60'/0'/0/2等。
区块结构
比特币的区块链是由PoW保证每个区块都指向前一个区块,而在每一个区块内部,由一个独立的Merkle Tree来保证所有交易的不可篡改。用户的比特币是以UTXO的方式存储的,因此,比特币的交易就是不断地消耗现有的UTXO,并产生新的UTXO。
而以太坊采用的是账户模型,如果小明的账户在某个区块的资产是1 ETH,当小明给小红转账0.2 ETH后,刨除手续费,他的账户还剩下约0.8 ETH。由于小明的账户地址不变,所以,以太坊的区块结构必须能在每个区块持续地跟踪并记录小明的账户余额变动。因此,和比特币相比,以太坊的区块数据结构更加复杂。
以太坊的核心数据结构是以Merkle Patricia Tree记录的世界状态,每个区块通过打包新的交易,从而导致世界状态的变化。
Merkle Patricia Tree
以太坊存储账户数据的数据结构是MPT:Merkle Patricia Tree,它是一种改进的Merkle Tree。当MPT的每个叶子结点的值确定后,计算出的Root Hash就是完全确定的。例如,在第一个区块中,4个账户的余额确定后,即可确定Root1:
┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐
Root1
│ ┌───┐ │
│ │
│ └───┘ │
│
│ ┌─────┴─────┐ │
│ │
│ ┌───┐ ┌───┐ │
│ │ │ │
│ └───┘ └───┘ │
│ │
│ ┌──┴──┐ ┌──┴──┐ │
│ │ │ │
│ ┌───┐ ┌───┐ ┌───┐ ┌───┐ │
│5.5│ │0.2│ │1.7│ │9.0│
│ └───┘ └───┘ └───┘ └───┘ │
─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
在第二个区块中,如果发生了转账,将计算出一个新的Root2
每一个区块通过Root Hash将完全确定所有账户的状态,所以,从全局看,以太坊就是一个状态机,每个区块通过记录一个stateRoot来表示一个新状态。如果给定某个区块的stateRoot,我们肯定能完全确定所有账户的所有余额等信息。因此,stateRoot就被称为当前的世界状态。
有的同学可能会思考,如果第一个区块只有几个账户,随着账户的增加,如果有数百万个账户,到后面岂不是区块存储的数据量会越来越大?
实际上,每个区块的stateRoot表示的是一个完全状态的逻辑树,但每个区块记录的数据只包括修改的部分,如果我们观察第二个区块的树,它实际上只记录修改的两个账户,以及两个账户因修改后导致的上层路径的Hash发生的变化
账户数据
一个以太坊账户由4部分数据构成:
- nonce
- balance
- storageRoot
- codeHash
其中,nonce是一个递增的整数,每发送一次交易,nonce递增1,因此,nonce记录的就是交易次数。
balance记录的就是账户余额,以wei为单位,1 Ether等于1018wei。
如果一个账户是合约账户,则storageRoot存储合约相关的状态数据,codeHash存储合约代码的Hash。对于外部账户,这两部分数据都是空。
交易
以太坊的交易需要消耗Gas,而Gas价格和实际消耗的数量决定了一个交易实际消耗的Ether,即交易成本。
合约交易无法精确地确定Gas数量,只能预估并给出Gas Limit。
基本流程
在比特币中,交易就是消耗已有UTXO,并通过执行脚本产生新的UTXO,其中隐含的新旧差额即为矿工手续费。
在以太坊中,交易也需要手续费,手续费被称为Gas(汽油),它的计算比比特币要复杂得多。
以太坊除了最基本的转账:即从一个账户支付Ether到另一个账户,还支持执行合约代码。合约代码是图灵完备的编程语言,通过EVM(以太坊虚拟机)执行。如果某个合约编写了一个无限循环,那么所有节点执行该合约代码,岂不是永远无法结束?
为了保证合约代码的可靠执行,以太坊给每一个虚拟机指令都标记了一个Gas基本费用,称为gasUsed。例如,加减计算的费用是3,计算SHA3的费用是30,输出日志的费用是375,写入存储的费用高达20000。总的来说,消耗CPU比消耗存储便宜,简单计算比复杂计算便宜,读取比写入便宜。
除了gasUsed外,用户还需要提供一个gasPrice,以Gwei(1Gwei=109Wei)为单位。通过竞价得到一个矿工愿意接受的gasPrice。如果一个交易消耗了120000的gasUsed,而gasPrice是50 Gwei,则交易费用是:
120000 x 50 Gwei = 6000000 Gwei = 0.006 Ether
但是在执行代码的时候,存在条件判断、循环等语句,同一段代码,执行的结果也可能不同,因此,事前预计一个合约执行要花费多少Gas,不现实。
所以以太坊规定,一笔交易,先给出gasPrice和gasLimit,如果执行完成后有剩余,剩余的退还,如果执行过程中消耗殆尽,那么交易执行失败,但已执行的Gas不会退。
太复杂了是不是?我们还是举个例子。
假定某个账户想执行一笔交易,他给出gasPrice为50 Gwei,预估gasUsed约120000,设定gasLimit为150000,则预支付的Ether为:
150000 x 50 Gwei = 7500000 Gwei = 0.0075 Ether
如果账户的Ether余额不足0.0075,则该交易根本无法发送。如果账户余额等于或超过0.0075,例如余额为0.008,则矿工可以将该交易打包。假设实际执行消耗的gasUsed为120000,则交易费用0.006,账户剩余0.002。
很少有交易能准确预估gasUsed,只有标准转账交易是21000,因此,标准的转账交易gasLimit可以设置为21000(即恰好消耗完毕无剩余)。
Gas Price是全网用户竞价产生的,它时刻在波动。如果交易少,Gas Price将下降,如果交易多,网络拥堵,则Gas Price将上升。以太坊的Gas价格可以在Etherscan跟踪。
交易回执
以太坊区块为每一笔交易都会产生一笔回执(Recipt),表示交易的最终状态。一个回执信息主要包括:
- status:执行结果,1表示成功,0表示失败;
- gasUsed:已消耗的Gas数量;
- txHash:交易Hash;
- logs:交易产生的日志;
- ……
转账交易
转账交易是指两个外部账号转移Ether,我们以交易0xb940…4ad7为例,可以看到:
- Transaction Hash: 0xb940…4ad7,这是交易Hash,即交易的唯一标识;
- Status: Success,表示交易成功;
- From: 0x0FFf…bBc4,交易的发送方;
- To: 0x5b2a…5a46,交易的接收方;
- Value: 1.6912 Ether,交易发送的Ether;
- Gas Price: 82 Gwei,Gas的价格;
- Gas Limit: 21,000,转账交易恰好消耗21000Gas,因此总是21000;
- Usage by Txn: 21,000 (100%),消耗的Gas占比,这里恰好全部消耗完;
- Nonce:0,发送方的nonce,0表示第1笔交易;
- Input Data: 0x,因为是转账交易,没有输入数据,因此为空。
合约交易
合约交易就是指一个外部账号调用某个合约的某个public函数。我们以交易0x8aff…8cd0为例,可以看到:
- From: 0x2329…BA3a,交易的发起方,该地址一定是外部账户;
- To: 0x7a25…488D,交易的接收方,这里地址是一个合约地址;
- Value: 4.5 Ether,即向合约发送4.5 Ether;
- Gas Limit: 152,533,这是交易发起前设定的最大Gas;
- Usage by Txn: 125,290 (82.14%),这是交易实际消耗的Gas;
- Input Data: 0x7ff36ab5…,这是交易的输入数据,其中包含了调用哪个函数,以及传递的参数,解码后可知调用函数是
swapExactETHForTokens。
可见,转账交易的Gas费用是固定的,而合约交易只能预估,具体费用以实际执行后消耗的为准。
什么是 nonce?
在以太坊中,
nonce是一个账户已经发送的交易数量,或说该账户的交易“计数器”。
它是每个账户(包括普通账户和合约账户)都维护的一个数字:
- 普通地址:
nonce = 该地址已经发出的交易数量 - 合约地址部署时:
nonce = 部署者地址已发出的交易数量,用于生成合约地址
nonce 的两个关键用途:
- 📦 防止交易重放攻击(Replay Attack)
- 每笔交易都必须带一个唯一的
nonce - 如果你重复广播一模一样的交易(同一个 nonce),网络会拒绝
- 确保每笔交易唯一且有序,不能被复制伪造
- 🏗️ 确定新部署的合约地址
当你部署一个新智能合约时,合约地址不是随机的,而是由下面这个公式确定:
合约地址 = keccak256(rlp_encode([部署者地址, 部署者的 nonce])) 的最后 20 字节
所以,不同 nonce 部署出来的合约地址也不同。
智能合约
智能合约
以太坊相比比特币的一个重大创新就是它支持智能合约(Smart Contract)。
所谓智能合约,就是一种运行在区块链上的程序。和普通程序不同的是,智能合约要保证在区块链网络的每一个节点中运行的结果完全相同,这样才能使任何一个节点都可以验证挖矿产出节点生成的区块里,智能合约执行的结果对不对。
因此,以太坊提供了一个EVM(Ethereum Virtual Machine)虚拟机来执行智能合约的字节码,并且,和普通程序相比,为了消除程序运行的不确定性,智能合约有很多限制,例如,不支持浮点运算(因为浮点数有不同的表示方法,不同架构的CPU运行的浮点计算精度都不同),不支持随机数,不支持从外部读取输入等等。
类似于Java源码被编译为JVM可执行的字节码,我们也需要一种高级语言来编写智能合约,然后编译成EVM的字节码。最常用的开发智能合约的语言是以太坊专门为其定制的Solidity语言,后续我们会详细介绍Solidity的用法。
一个智能合约被编译后就是一段EVM字节码,将它部署在以太坊的区块链时,会根据部署者的地址和该地址的nonce分配一个合约地址,合约地址和账户地址的格式是没有区别的,但合约地址没有私钥,也就没有人能直接操作该地址的合约数据。要调用合约,唯一的方法是调用合约的公共函数。
这也是合约的一个限制:合约不能主动执行,它只能被外部账户发起调用。如果一个合约要定期执行,那只能由线下服务器定期发起合约调用。
此外,合约作为地址,可以接收Ether,也可以发送Ether。合约内部也可以存储数据。合约的数据存储在合约地址关联的存储上,这就使得合约具有了状态,可以实现比较复杂的逻辑,包括存款、取款等。
合约在执行的过程中,可以调用其他已部署的合约,前提是知道其他合约的地址和函数签名,这就大大扩展了合约的功能。例如,一个合约可以调用另一个借贷合约的借款方法,再调用交易合约,最后再调用还款方法,实现所谓的“闪电贷”(即在一个合约调用中实现借款-交易-还款)功能。多个合约的嵌套调用也使得因为代码编写的漏洞导致黑客攻击的可能性大大增加。为了避免漏洞,编写合约时需要更加小心。
小结
以太坊通过EVM虚拟机执行智能合约代码;
合约被部署后将自动获得一个地址,并可像外部账户一样存取Ether,还可以存储状态数据;
合约只能被动地被外部账户调用,但在执行时可以调用其他合约的公共函数。
- 合约地址不会变。
- 可升级可以变,但使用的是代理的合约地址,不会变。
Web3 签名转账 & Gas 机制笔记
1. 签名转账上链流程
- 准备交易参数(nonce, to, value, data, gasLimit, fee 设置, chainId)。
- 本地序列化并签名(私钥签名 → ECDSA → rawTx)。
- 广播交易到网络(进入节点的 mempool)。
- 区块提议者/验证者打包并共识。
- 状态更新:ETH 余额变动 / ERC20 合约执行日志。
2. 交易参数详解(ETH / ERC20)
- nonce:交易序号,保证顺序和防重放。
- to:
- ETH 转账:收款人地址。
- ERC20 转账:代币合约地址。
- value:
- ETH 转账:金额(wei)。
- ERC20 转账:=0。
- data:
- ETH 转账:空。
- ERC20 转账:
transfer(to, amount)的 ABI 编码。
- gasLimit:最大 Gas 消耗(ETH 转账约 21,000,ERC20 转账约 50,000–80,000)。
- maxPriorityFeePerGas:小费,激励打包者优先选择你的交易。
- maxFeePerGas:最高 Gas 单价上限,防止被宰。
- chainId:链的 ID,防止跨链重放。
笔记:
- Gas = 运算量,Fee = 运算量 × 单价。
- BaseFee 销毁,PriorityFee 给打包者,MaxFee 保护用户。
- ETH 在高使用期可能通缩,冷清期可能轻微通胀。
3. BaseFee / PriorityFee / MaxFee
- BaseFee
- 每个区块自动调整的最低费率。
- 所有交易必须至少支付。
- 直接销毁(burn),不会给打包者。
- PriorityFee(tip)
- 用户额外支付的小费。
- 直接归打包者。
- 设置越高,打包越快。
- MaxFee
- 用户愿意支付的最高 Gas 单价。
- 实际支付不会超过这个值。
公式:
实际单价 = min(MaxFee , BaseFee + PriorityFee)
手续费 = gasUsed × 实际单价
4. 手续费计算示例
条件:
gasUsed = 21,000BaseFee = 20 gweiMaxFee = 40 gweiPriorityFee = 2 gwei
计算:
- 实际单价 = min(40, 20+2) = 22 gwei
- 手续费 = 21,000 × 22 gwei ≈ 0.000462 ETH
分配:
- 21,000 × 20 gwei = 0.00042 ETH → 销毁
- 21,000 × 2 gwei = 0.000042 ETH → 打包者收入
5. Gas 单位与 Fee 单位
- Gas:EVM 运算步骤数,虚拟单位,不是货币。
- Gas Price / Fee Per Gas:每单位 Gas 的价格,单位 wei/gas(常用 gwei)。
- Fee:最终手续费,= gasUsed × 实际单价,结果单位 ETH。
6. ETH 销毁机制
- BaseFee 部分被销毁,使 ETH 带有通缩属性。
- 链上活动高峰期:销毁量 > 增发量 → ETH 净通缩。
- 活动低迷期:销毁量 < 增发量 → ETH 轻微通胀。
- ETH 总量 = 增发(质押奖励) - 销毁(BaseFee)。
以太坊开发文档
以太坊货币面额
- Wei:最小单位(1 ETH = 10^18 Wei),底层实现和计算都用它
- Gwei:1 Gwei = 10^9 Wei,常见于 Gas 费表示
- ETH:人类友好的计价单位
账户类型
外部账户(EOA)
- 有私钥(由用户控制)
- 能主动发起交易
- 创建免费
- 用途:存币、转账、调用合约
合约账户
- 没有私钥,由代码控制
- 地址由部署规则决定
- 不能主动发起交易,只能在收到消息时执行代码
- 存储逻辑、状态变量在
storageRoot
基本调用规则
EOA → 合约
合约 → 合约(内部调用)
- 合约 不能 主动调用 EOA
- 一切链上执行,最终都由 EOA 发起
交易类型
- 普通转账(EOA → EOA)
- 合约部署(无
to地址,data携带代码) - 执行合约(EOA → 合约地址,
data按 ABI 编码参数)
| 模式 / 标准 | 调用函数 / 方式 | 发起者 | 谁付 Gas | 是否需要 approve | 支付 / 功能特点 | 适用场景 |
|---|---|---|---|---|---|---|
| transfer | transfer(to, value) | 用户本人 | 用户 | 不需要 | 普通转账 | 日常支付 |
| transferFrom | transferFrom(from, to) | 第三方/合约 | 第三方 | 需要 | 商户/合约代扣 | DEX、NFT 市场 |
| permit (EIP-2612) | permit(owner, spender, sig) | DApp(签名) | DApp | 隐式授权 | 链下授权 + Gasless | Gasless 授权 |
| paymaster (ERC-4337) | UserOperation → transfer | 用户 | Paymaster | 不需要 | Gas 代付,可用稳定币支付 | Gasless DApp、USDT/USDC 支付 |
| EIP-7702 | EOA 自身增强 | 用户 / EOA | 用户 | 根据操作需要 | 批量操作、账户能力升级 | 高级钱包操作 / 批量交易 |
说明:
- approve = 给额度(授权书)
- transferFrom = 商户/合约代扣
- permit = 离线签名 + 免一次上链 approve
- paymaster = Gas 代付(ERC-4337 Account Abstraction)
- EIP-7702 = EOA 能力增强(批量操作 / 自定义校验)
ERC‑4337:提供完整的账户抽象和 Gas 支付机制,适合免 Gas / 稳定币支付场景,但成本高、引入单点风险、失去 EOA 灵活性。 目前逐渐被7702替代。
| 特性 | ERC‑4337 | EIP‑7702 |
|---|---|---|
| 账户类型 | 合约账户(AA) | 传统 EOA 临时增强 |
| Gas 支付 | 可用 Paymaster 代付 / 稳定币 | 仍需 ETH(但可优化批量交易) |
| 批量操作 | 支持,EntryPoint + Bundler | 支持,逻辑合约共享 |
| 用户迁移成本 | 高,需要创建或迁移 AA | 低,仅初始化一次逻辑合约即可 |
| 交易成本 | 单笔翻倍 | 批量交易显著节省 Gas |
| 单点风险 | Bundler / EntryPoint | 无中心化单点,逻辑合约共享 |
| 适用场景 | 高级支付 / Gasless DApp | 批量操作 / 多代币交易 / Web3 Game |
以太坊 PoS 奖励机制
验证者奖励(Validator Rewards)
- 出块奖励:Priority Fee + MEV
- 证明奖励:Attestation
- 新发行 ETH:共识层增发 (归属:节点运营者)
质押奖励(Staking Rewards)
- 质押者实际拿到的收益 = 验证者奖励 × 质押比例 – 服务商抽成
- 与节点在线率、slash 风险相关
验证者角色
- 提议者(Proposer):出块,收入主要是交易费、小费、MEV
- 证明者(Attester):投票确认新区块,奖励小但频繁
- 增发分配:根据质押总量动态调整
MEV(最大可提取价值)
- 含义:交易排序权带来的额外收入
- 类型:套利、抢跑、跟单、三明治攻击
权益证明(PoS)协议核心
- 质押 32 ETH 成为验证者
- Slot(12 秒):随机选择一个提议者
- 其他验证者投票确认
- 分叉时采用最多质押支持的分支
- 不诚实行为 → Slash(质押被罚没)