区块链与以太坊基础
概述
区块链是 Web3 的底层基础设施,以太坊则是前端开发者最常接触的公链。理解区块链的运作原理和以太坊的核心概念,是开发 DApp、与智能合约交互的前提。本文从前端开发者的视角,梳理区块链与以太坊的核心知识。
面试中关于区块链的考察通常不会太深入底层密码学,而是侧重于你对账户模型、交易结构、Gas 机制、EVM 等概念的理解——这些直接影响前端 DApp 的开发方式。
如果你还没有阅读 Web3 知识体系概览,建议先看 Web3 知识体系概览,了解整体知识地图。
区块链与以太坊需要掌握什么? 前端面试不会问底层密码学,但这几个概念必须明白:
- 账户模型:以太坊是账户模型(不同于 BTC 的 UTXO),分 EOA(外部账户、有私钥)和合约账户两种。
- EVM:以太坊虚拟机,是一个栈式虚拟机,所有 EVM 兼容链(Polygon、BSC、Arbitrum、Optimism)都跑同样的字节码。
- Gas:每个操作都要消耗计算资源(gasUsed × gasPrice),合约函数越复杂 Gas 越高。
- 共识机制:以太坊已从 PoW 转为 PoS(合并后),出块交由验证者。
- Layer 2:Arbitrum/Optimism(Optimistic Rollup)、zkSync(ZK Rollup)把交易打包后上 L1,用以太坊安全但 Gas 低一两个数量级。
区块链核心概念
什么是区块链
区块链本质上是一个分布式、不可篡改的账本。交易数据被打包成「区块」,通过密码学哈希依次链接,形成一条链。
三个核心特性:
| 特性 | 说明 | 对前端开发的影响 |
|---|---|---|
| 去中心化 | 没有单一控制方,由全网节点共同维护 | 数据读取通过 RPC 节点(Infura、Alchemy),无传统后端 |
| 不可篡改 | 一旦写入,数据几乎不可被修改 | 交易一旦确认就无法撤销,需做好 UI 确认流程 |
| 透明公开 | 所有交易记录对所有人可见 | 用户行为全部链上可查,隐私需额外处理 |
区块结构
每个区块包含区块头和交易列表两部分,区块头记录元信息,交易列表存储实际交易。
Merkle Tree(默克尔树)是一种哈希二叉树,用于高效验证某笔交易是否包含在区块中。前端使用 Merkle Proof 可以实现白名单验证(如 NFT Mint 白名单),而无需将整个列表存储在链上。
区块头关键字段:
| 字段 | 说明 |
|---|---|
parentHash | 上一个区块头的哈希,形成链式结构 |
stateRoot | 世界状态树的根哈希(所有账户余额、合约存储的快照) |
transactionsRoot | 交易 Merkle Tree 的根哈希 |
receiptsRoot | 收据 Merkle Tree 的根哈希(包含交易执行结果、日志) |
number | 区块高度(从 0 开始递增) |
timestamp | 区块产生的时间戳 |
gasUsed | 该区块中所有交易消耗的 Gas 总量 |
baseFeePerGas | EIP-1559 引入的基础费用(会被销毁) |
共识机制
共识机制决定了谁有权生产新区块。以太坊经历了从 PoW 到 PoS 的重大转变:
| 特性 | PoW(工作量证明) | PoS(权益证明) |
|---|---|---|
| 原理 | 矿工通过算力竞争解题出块 | 验证者质押 ETH,被随机选中出块 |
| 以太坊使用时期 | 创世 ~ 2022.9(The Merge) | 2022.9 至今 |
| 能耗 | 极高(大量算力消耗) | 极低(降低 ~99.95% 能耗) |
| 出块时间 | ~13 秒(不稳定) | 固定 12 秒(每个 Slot) |
| 最终性 | 概率性最终性(需等多个区块确认) | 确定性最终性(约 12.8 分钟,2 个 Epoch) |
| 参与门槛 | 购买矿机 | 质押 32 ETH 成为验证者 |
The Merge(合并)后出块时间从不稳定的 ~13 秒变为固定的 12 秒,这对前端的交易等待 UI、区块确认轮询逻辑都有影响。部分依赖 block.difficulty 的旧代码也需要适配(PoS 下该字段被替换为 prevrandao)。
以太坊架构
EVM(以太坊虚拟机)
EVM(Ethereum Virtual Machine)是以太坊的执行引擎,是一个基于栈的虚拟机,所有节点运行相同的 EVM 来执行智能合约,保证结果一致。
前端开发者需要理解的 EVM 要点:
- EVM 执行合约代码需要消耗 Gas,Gas 不足会导致交易回滚
- 合约调用分为读操作(
call,免费)和写操作(transaction,消耗 Gas) - 合约执行过程中会产生事件日志(Events),前端通过监听事件来更新 UI
以太坊是一个状态机
以太坊本质上是一个交易驱动的状态机:
状态(S) + 交易(T) → 新状态(S')
世界状态(World State) 记录了所有账户的当前状态,每笔交易都会触发状态转换。前端通过 eth_call 读取当前状态,通过 eth_sendTransaction 提交交易来改变状态。
账户模型
以太坊有两种账户类型,理解它们的区别是前端 Web3 开发的基础:
EOA vs 合约账户
| 特性 | EOA(外部账户) | 合约账户 |
|---|---|---|
| 控制方式 | 由私钥控制 | 由合约代码控制 |
| 地址格式 | 20 字节,如 0xAbC...123 | 20 字节,由部署者地址 + nonce 派生 |
| 有无代码 | 无 | 有(EVM 字节码) |
| 能否发起交易 | ✅ 可以 | ❌ 不能主动发起,只能被调用 |
| 有无存储 | 仅有余额和 nonce | 有余额、nonce、代码和存储 |
| 创建方式 | 生成密钥对即可 | 通过部署交易创建 |
| 典型示例 | MetaMask 钱包地址 | Uniswap 路由合约、ERC-20 代币合约 |
ERC-4337 提出了账户抽象的概念,允许合约账户像 EOA 一样发起交易。这为社交恢复、无 Gas 交易、批量交易等功能提供了可能。前端开发者需要关注这个趋势,因为它会改变钱包连接和交易签名的方式。
账户状态字段
interface AccountState {
nonce: number; // 该账户发起的交易数(EOA)或创建的合约数(合约账户)
balance: bigint; // 账户余额(单位:Wei,1 ETH = 10^18 Wei)
storageRoot: string; // 存储树的根哈希(仅合约账户有实际内容)
codeHash: string; // 合约代码的哈希(EOA 为空哈希 keccak256(""))
}
Gas 机制
Gas 是以太坊的「燃料」,用于衡量执行操作的计算成本。前端开发者必须理解 Gas 机制,因为它直接影响交易费用估算和用户体验。
Gas 基本概念
| 概念 | 说明 |
|---|---|
| Gas | 执行操作的计量单位,每个 EVM 操作码有固定 Gas 消耗 |
| Gas Limit | 交易发送者愿意为这笔交易支付的 Gas 上限 |
| Gas Used | 交易实际消耗的 Gas(多余的会退还) |
| Gas Price | 每单位 Gas 的价格(单位:Gwei,1 Gwei = 10^9 Wei) |
| 交易费用 | Gas Used × Gas Price(用户实际支付的 ETH) |
EIP-1559 费用机制
EIP-1559(2021 年 London 升级引入)彻底改变了 Gas 定价方式:
EIP-1559 核心改变:
- Base Fee:由协议根据上一个区块的 Gas 使用率自动调整。区块越满,Base Fee 越高。Base Fee 会被销毁(burn),不给验证者
- Priority Fee(Tip):用户给验证者的额外小费,激励验证者优先打包自己的交易
- maxFeePerGas:用户愿意为每单位 Gas 支付的最高价格
- maxPriorityFeePerGas:用户愿意给验证者的最高小费
import { formatGwei, parseGwei } from 'viem';
// 获取当前 Base Fee 和建议的 Priority Fee
const block = await client.getBlock();
const baseFee = block.baseFeePerGas; // 当前 Base Fee
// 常见的前端 Gas 估算策略
const gasEstimate = {
// 慢速:Base Fee + 较低 Tip
slow: {
maxFeePerGas: baseFee * 2n, // 预留空间,防止 Base Fee 上涨
maxPriorityFeePerGas: parseGwei('0.1'),
},
// 标准:Base Fee + 中等 Tip
standard: {
maxFeePerGas: baseFee * 2n,
maxPriorityFeePerGas: parseGwei('1.5'),
},
// 快速:更高的 Tip,优先被打包
fast: {
maxFeePerGas: baseFee * 3n,
maxPriorityFeePerGas: parseGwei('3'),
},
};
- Gas 估算不准:合约交互的 Gas 消耗可能因链上状态变化而不同,建议使用
eth_estimateGas后再加 10-20% 的缓冲 - 交易卡住:Gas Price 设置过低会导致交易长时间 pending,可以通过发送相同 nonce 但更高 Gas Price 的交易来替换(Speed Up)
- Out of Gas:Gas Limit 设置过低会导致交易失败且 Gas 不退还,注意与用户余额不足区分
交易结构
每笔以太坊交易包含以下关键字段,前端 DApp 需要构造这些字段来发送交易:
interface Transaction {
// 基本字段
type: 2; // 交易类型:2 表示 EIP-1559
chainId: number; // 链 ID(以太坊主网 = 1,Sepolia = 11155111)
nonce: number; // 发送者账户的交易计数,防重放攻击
to: string | null; // 接收地址(null 表示部署合约)
value: bigint; // 转账金额(单位:Wei)
data: string; // 调用合约时的编码数据(ABI 编码)
// Gas 相关(EIP-1559)
maxFeePerGas: bigint; // 每单位 Gas 的最高价格
maxPriorityFeePerGas: bigint; // 给验证者的最高小费
gasLimit: bigint; // Gas 上限
// 签名字段(由钱包填充)
v: number;
r: string;
s: string;
}
关键字段解释:
nonce:保证交易顺序且防止重放攻击。前端需要注意,连续发送多笔交易时 nonce 必须递增,否则后续交易会 pendingto:为null时表示合约部署交易data:调用合约函数时,该字段是函数签名和参数的 ABI 编码。纯 ETH 转账时为空(0x)chainId:防止在不同链上重放同一交易
交易生命周期
交易收据
交易被打包后,节点返回交易收据(Transaction Receipt),前端通过它判断交易结果:
const receipt = await client.waitForTransactionReceipt({
hash: txHash,
});
// 关键字段
console.log(receipt.status); // 'success' 或 'reverted'
console.log(receipt.gasUsed); // 实际消耗的 Gas
console.log(receipt.effectiveGasPrice); // 实际 Gas 价格
console.log(receipt.blockNumber); // 被打包的区块高度
console.log(receipt.logs); // 交易产生的事件日志
区块确认与最终性
区块确认
交易被打包进区块后,并不意味着它是「最终的」。区块可能因为链重组(Reorg) 而被撤销。通常需要等待多个区块确认来降低风险:
| 场景 | 建议确认数 | 等待时间(PoS) |
|---|---|---|
| 小额转账 | 1-3 个区块 | 12-36 秒 |
| 中等金额 | 12 个区块 | ~2.4 分钟 |
| 大额 / 交易所入账 | 32 个区块 | ~6.4 分钟 |
| 绝对最终性 | 2 个 Epoch(64 个 Slot) | ~12.8 分钟 |
最终性(Finality)
以太坊 PoS 引入了确定性最终性:经过 2 个 Epoch(约 12.8 分钟)后,区块获得最终性,不可被回滚(除非 1/3 以上的验证者串谋攻击,这意味着巨额 ETH 被罚没)。
前端 DApp 通常的做法是:交易提交后立即给用户「交易已提交」的反馈,同时在后台轮询确认数。可以使用 waitForTransactionReceipt 等 SDK 方法来等待交易上链,再更新最终状态。
Layer 2 概念简介
以太坊主网(Layer 1)吞吐量有限(~15 TPS),Gas 费用高昂。Layer 2 是建立在以太坊之上的扩容方案,通过在链下执行交易、将结果提交回 L1 来提升性能。
Rollup 方案对比
| 特性 | Optimistic Rollup | ZK Rollup |
|---|---|---|
| 原理 | 乐观假设交易有效,出错后通过「欺诈证明」挑战 | 每批交易附带零知识证明,数学保证正确性 |
| 代表项目 | Optimism、Arbitrum、Base | zkSync、StarkNet、Scroll、Linea |
| 提款时间 | 7 天挑战期(可通过桥服务加速) | 几分钟(等待证明生成和验证) |
| 兼容性 | 与 EVM 高度兼容 | 早期兼容性较差,zkEVM 逐步改善 |
| Gas 费用 | 较低(L1 的 ~5-10%) | 更低(长期趋势) |
| 成熟度 | 较成熟 | 快速发展中 |
- L2 的 RPC 接口与 L1 基本一致,前端代码切换 L2 只需更改
chainId和 RPC URL - 需要处理跨链桥的 UI 逻辑(L1 ↔ L2 资产转移)
- L2 上的交易确认更快、Gas 更低,但最终性仍依赖 L1 确认
- 使用 viem / wagmi 时,通过配置不同的
chain即可切换网络
常见面试问题
Q1: 区块链为什么不可篡改?
答案:
区块链的不可篡改性源于哈希链接结构和共识机制:
- 哈希链接:每个区块头包含上一个区块头的哈希(
parentHash)。篡改任一区块的数据会导致其哈希变化,从而使后续所有区块的parentHash失效,需要重新计算整条链 - 共识机制:在 PoS 中,攻击者需要控制全网 2/3 以上的质押 ETH 才能篡改已最终确认的区块,这在经济上几乎不可行
- 分布式验证:全球数千个节点同时存储和验证数据,单点篡改会被其他节点拒绝
Q2: EOA 和合约账户的区别是什么?
答案:
核心区别在于控制方式和功能:
- EOA 由私钥控制,可以主动发起交易;合约账户由代码控制,只能被动响应调用
- EOA 没有代码和存储空间;合约账户有 EVM 字节码和独立的存储
- 所有链上操作的源头必须是 EOA 发起的交易,合约账户无法自己发起交易
前端开发中,可以通过 eth_getCode 判断一个地址是 EOA 还是合约:
const code = await client.getCode({ address: '0x...' });
const isContract = code !== undefined && code !== '0x';
Q3: 什么是 Gas?为什么需要 Gas 机制?
答案:
Gas 是衡量 EVM 操作计算成本的单位。需要 Gas 机制的原因:
- 防止滥用:没有成本约束,攻击者可以提交无限循环的恶意合约,耗尽网络资源
- 资源定价:不同操作消耗不同的计算资源,Gas 机制让用户为实际使用的资源付费
- 激励验证者:Gas 费用中的 Priority Fee 是验证者的收入来源
Q4: 解释 EIP-1559 的 Base Fee 和 Priority Fee
答案:
EIP-1559 将 Gas 费用分为两部分:
- Base Fee:由协议根据网络拥堵程度自动调整的基础费用。当上一区块 Gas 使用超过 50% 目标时,Base Fee 上涨(最多 12.5%);反之下降。Base Fee 会被销毁,不给验证者
- Priority Fee(Tip):用户额外支付给验证者的小费,用于激励优先打包。网络繁忙时需要更高的 Tip
用户设置 maxFeePerGas 作为最高支付上限,实际费用 = min(maxFeePerGas, baseFee + maxPriorityFeePerGas),多余部分退还。
Q5: 以太坊的 PoW 和 PoS 有什么区别?The Merge 是什么?
答案:
- PoW:矿工消耗算力竞争出块权,能耗高、出块时间不稳定
- PoS:验证者质押 ETH,被随机选中出块,能耗极低、出块时间固定 12 秒
- The Merge(2022 年 9 月)是以太坊从 PoW 切换到 PoS 的里程碑事件,将能耗降低了约 99.95%,并引入了确定性最终性
对前端的影响:出块时间固定化、block.difficulty 被弃用、区块最终性模型改变。
Q6: 交易的 nonce 是什么?有什么作用?
答案:
nonce 是该账户发出的交易计数(从 0 开始)。作用:
- 保证交易顺序:节点按 nonce 递增顺序处理交易。nonce=3 的交易必须在 nonce=2 之后执行
- 防止重放攻击:同一 nonce 只能执行一次,防止同一笔交易被重复执行
- 交易替换:发送相同 nonce 但更高 Gas Price 的交易可以替换 pending 中的交易(Speed Up / Cancel)
前端常见问题:连续快速发送多笔交易时,需要手动管理 nonce 递增,否则会出现交易卡住。
Q7: 什么是 Merkle Tree?前端开发中有哪些应用场景?
答案:
Merkle Tree 是一种哈希二叉树,叶节点是数据的哈希,非叶节点是其子节点哈希的组合哈希。只需提供一条从叶到根的哈希路径(Merkle Proof)即可验证某个数据是否在树中。
前端应用场景:
- NFT 白名单:将白名单地址构建成 Merkle Tree,链上只存储 Root Hash,用户 Mint 时提交 Merkle Proof 验证资格
- 空投验证:大规模空投时使用 Merkle Distributor 合约,用户凭 Proof 领取
- SPV 轻客户端:验证某笔交易是否在某个区块中,无需下载完整区块
Q8: 什么是 EVM?它与前端的关系是什么?
答案:
EVM 是以太坊的虚拟执行环境,所有节点运行同一份 EVM 代码,保证执行结果的确定性。
与前端的关系:
- 前端通过 ABI 编码向 EVM 发送调用指令(
data字段) - 读操作(
eth_call)在本地 EVM 模拟执行,不消耗 Gas - 写操作(
eth_sendTransaction)由验证者在 EVM 中执行,消耗 Gas - 合约的 Events/Logs 由 EVM 执行过程中产生,前端通过
eth_getLogs或 WebSocket 订阅获取
Q9: Layer 2 是什么?Optimistic Rollup 和 ZK Rollup 有什么区别?
答案:
Layer 2 是以太坊的扩容方案,在链下执行交易,将结果批量提交到 L1:
- Optimistic Rollup:乐观假设交易正确,任何人可在 7 天挑战期内提交欺诈证明来撤销无效交易。优点是 EVM 兼容性好,缺点是提款时间长
- ZK Rollup:每批交易附带数学上的零知识证明,无需信任假设。优点是安全性更强、提款更快,缺点是证明生成复杂、早期 EVM 兼容性差
对前端来说,L2 的 JSON-RPC 接口与 L1 基本一致,代码迁移成本很低。
Q10: 前端如何估算交易的 Gas 费用?
答案:
// 1. 估算 Gas Limit
const gasLimit = await client.estimateGas({
account: userAddress,
to: contractAddress,
data: encodedFunctionData,
});
// 2. 获取当前 Gas 价格建议
const gasPrice = await client.estimateFeesPerGas();
// 返回 { maxFeePerGas, maxPriorityFeePerGas }
// 3. 计算预估费用(单位:Wei)
const estimatedCost = gasLimit * gasPrice.maxFeePerGas;
// 4. 加上缓冲(推荐 10-20%),防止链上状态变化导致 Gas 不足
const safeGasLimit = gasLimit * 120n / 100n;
最佳实践:使用 eth_estimateGas 获取基准值后加缓冲,同时展示预估费用给用户确认。
Q11: 交易的 data 字段里存了什么?
答案:
data 字段包含函数选择器(4 字节)+ ABI 编码的参数:
- 函数选择器 = 函数签名的 keccak256 哈希的前 4 字节,如
transfer(address,uint256)→0xa9059cbb - 参数按 ABI 编码规则拼接在后面,每个参数占 32 字节
纯 ETH 转账时 data 为空(0x)。前端使用 viem 的 encodeFunctionData 或 ethers 的 interface.encodeFunctionData 来生成。
Q12: 什么是区块链的最终性(Finality)?以太坊 PoS 如何实现?
答案:
最终性是指交易一旦确认就绝对不可被回滚的保证:
- PoW 时代:只有概率性最终性,随着后续区块增多,回滚的概率指数下降但永不为零
- PoS 时代:以太坊引入了确定性最终性。每 32 个 Slot(约 6.4 分钟)组成一个 Epoch,经过 2 个 Epoch 的验证者投票确认后,区块获得最终性。回滚已最终确认的区块需要至少 1/3 的验证者被罚没质押的 ETH
前端在处理大额交易时,应等待最终性确认再更新 UI。
Q13: chainId 的作用是什么?前端开发中需要注意什么?
答案:
chainId 用于标识不同的区块链网络,防止跨链重放攻击(在一条链上签名的交易被拿到另一条链上执行)。
常见 chainId:以太坊主网 = 1,Sepolia 测试网 = 11155111,Arbitrum = 42161,Base = 8453。
前端注意事项:
- 必须检测用户钱包当前连接的 chainId 是否正确
- 提供切换网络功能(
wallet_switchEthereumChain) - 监听
chainChanged事件,在用户手动切换网络时更新 DApp 状态
相关链接
- 以太坊官方文档
- 以太坊白皮书
- EIP-1559 规范
- ERC-4337 账户抽象
- The Merge 官方说明
- Ethereum Yellow Paper(技术规范)
- viem 文档 - 现代 TypeScript 以太坊库
- L2Beat - Layer 2 数据面板