交易与签名
交易与签名
本页内容正在整理中,欢迎贡献。
概述
交易是以太坊状态变更的唯一方式。每一笔转账、合约调用、合约部署都是一笔交易。理解交易的结构和签名机制,是开发安全 DApp 的基础。
主要内容
交易类型
以太坊目前支持三种交易类型(EIP-2718 定义):
| 类型 | 标识 | 说明 |
|---|---|---|
| Legacy | 0x00 | 原始交易格式,使用 gasPrice |
| EIP-2930 | 0x01 | 访问列表交易,优化 Gas |
| EIP-1559 | 0x02 | 动态费用交易(最常用) |
交易字段(EIP-1559)
interface Transaction { chainId: bigint; // 链 ID,防止跨链重放攻击 nonce: number; // 发送者的交易序号 maxFeePerGas: bigint; // 愿意支付的最高 Gas 费(含 baseFee + tip) maxPriorityFeePerGas: bigint; // 给验证者的最高小费 gasLimit: bigint; // 最大 Gas 消耗量 to: string | null; // 接收地址(null 表示创建合约) value: bigint; // 转账 ETH 数量(wei 单位) data: string; // 调用数据(合约交互的编码参数) accessList: AccessList; // EIP-2930 访问列表}交易生命周期
用户签名交易 ↓广播到 P2P 网络 ↓进入节点内存池(Mempool) ↓验证者/区块构建者打包到区块 ↓区块广播与共识 ↓交易被最终包含在链上 ↓收到交易回执(Receipt)交易回执(Receipt)
interface TransactionReceipt { transactionHash: string; blockNumber: bigint; blockHash: string; from: string; to: string | null; contractAddress: string | null; // 创建合约时的地址 gasUsed: bigint; effectiveGasPrice: bigint; status: 0 | 1; // 0 = 失败,1 = 成功 logs: Log[]; // 事件日志}ECDSA 签名机制
以太坊使用 secp256k1 曲线的 ECDSA(椭圆曲线数字签名算法):
- 私钥(32 字节随机数)→ 公钥(64 字节椭圆曲线点)→ 地址(公钥 Keccak256 哈希后取后 20 字节)
- 签名过程:对交易 RLP 编码后的 Keccak256 哈希进行签名,生成
(v, r, s)三元组 - 验签恢复:从签名
(v, r, s)和消息哈希可以恢复出公钥,从而验证签名者地址
// 使用 viem 签名消息import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount("0x私钥");const signature = await account.signMessage({ message: "Hello Ethereum" });// 输出: 0x...(65 字节:r[32] + s[32] + v[1])EIP-1559 费用模型
实际费用 = Gas Used × (Base Fee + Priority Fee)其中: Base Fee → 被销毁(Burn),减少 ETH 供应 Priority Fee → 给验证者作为激励Base Fee 每个区块动态调整:
- 如果上个区块 Gas 使用量 > 目标(50%)→ Base Fee 上涨(最多 +12.5%)
- 如果上个区块 Gas 使用量 < 目标 → Base Fee 下降
Nonce 管理
- Nonce 是账户发出的第 N 笔交易的序号(从 0 开始)
- 交易必须按 nonce 顺序执行
- 如果某笔交易 nonce 跳过,后续交易会卡在 mempool 中
- 如果发现交易长时间未确认,可以发送相同 nonce 但更高 Gas 价格的交易来”替换”它
calldata 编码
合约函数调用通过 data 字段传递:
data = 函数选择器(4 字节)+ 参数 ABI 编码函数选择器 = keccak256("函数签名") 的前 4 字节例如调用 transfer(address,uint256):
选择器: keccak256("transfer(address,uint256)") = 0xa9059cbbdata: 0xa9059cbb 000000000000000000000000接收地址(32字节) 000000000000000000000000000000000000000000000转账金额(32字节)