跳转到内容

交易与签名

交易与签名

本页内容正在整理中,欢迎贡献

概述

交易是以太坊状态变更的唯一方式。每一笔转账、合约调用、合约部署都是一笔交易。理解交易的结构和签名机制,是开发安全 DApp 的基础。

主要内容

交易类型

以太坊目前支持三种交易类型(EIP-2718 定义):

类型标识说明
Legacy0x00原始交易格式,使用 gasPrice
EIP-29300x01访问列表交易,优化 Gas
EIP-15590x02动态费用交易(最常用)

交易字段(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(椭圆曲线数字签名算法):

  1. 私钥(32 字节随机数)→ 公钥(64 字节椭圆曲线点)→ 地址(公钥 Keccak256 哈希后取后 20 字节)
  2. 签名过程:对交易 RLP 编码后的 Keccak256 哈希进行签名,生成 (v, r, s) 三元组
  3. 验签恢复:从签名 (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)") = 0xa9059cbb
data: 0xa9059cbb
000000000000000000000000接收地址(32字节)
000000000000000000000000000000000000000000000转账金额(32字节)

深入阅读