在区块链的世界里,以太坊(Ethereum)无疑是最具影响力的平台之一,它不仅仅是一个加密货币,更是一个全球性的、去中心化的计算机,要真正理解以太坊的运作机制,深入其技术核心是必不可少的途径,本文将带您踏上一段探索之旅,从构建一个以太坊全节点开始,逐步揭开其挖矿机制背后的源码奥秘,揭示新区块是如何被创造并添加到区块链上的。
第一部分:以太坊节点——网络的基石
在讨论挖矿之前,我们必须先理解以太坊节点(ETH Node)的重要性,以太坊网络并非由单一服务器控制,而是由成千上万个分布在全球的节点共同维护的,每个节点都保存着一份完整的以太坊区块链副本,并参与网络的共识过程。

节点类型:
- 全节点: 存储了从创世块至今的所有区块和交易数据,它可以独立验证所有交易和区块的有效性,是网络去中心化程度最高的体现。
- 归档节点: 在全节点的基础上,还存储了所有历史状态数据,可以查询任何历史时刻的账户状态,它对存储空间要求极高。
- 轻节点: 只下载区块头,不下载交易详情,它依赖全节点来获取交易数据,功能有限,但资源消耗少。
为什么运行一个全节点?
- 去中心化: 您不再需要信任任何第三方,自己验证所有交易。
- 隐私: 您的交易数据不会发送给第三方服务商。
- 完全控制: 您可以自由地与以太坊网络进行交互,例如部署智能合约、发送交易等。
构建一个以太坊全节点:
最常用的方式是通过运行以太坊官方客户端,目前最主流的是基于Go语言开发的 Geth (Go-Ethereum)。
- 安装Geth: 您可以从其官方GitHub仓库下载对应您操作系统的二进制文件,或者使用包管理器(如
apt,brew)进行安装。 - 初始化节点: 在命令行中执行
geth --datadir ./myethchain init genesis.json,这里的genesis.json是创世块配置文件,定义了链的初始参数。 - 启动节点: 执行
geth --datadir ./myethchain --syncmode snap --http --http.addr "0.0.0.0" --http.port "8545" --http.api "personal,eth,net,web3"。--syncmode snap: 使用快速同步模式,可以快速同步最近的区块状态,而不是从创世块开始。--http: 启动HTTP-RPC服务,允许其他应用(如MetaMask、Remix)连接到您的节点。--http.api: 暴露给HTTP API的模块列表。
启动后,您的节点就会开始连接到以太坊网络,下载并验证区块,成为这个去中心化网络中的一份子。
第二部分:挖矿——价值创造的引擎
挖矿是以太坊从工作量证明走向权益证明之前,其共识机制的核心,在PoW中,矿工们通过强大的计算能力来解决一个复杂的数学难题,第一个解决问题的矿工将获得创建新区块的权利,并获得相应的以太币作为奖励。

挖矿的基本流程:
- 收集交易: 矿工从交易池中收集待处理的交易,并将它们打包成一个候选区块。
- 计算难度值: 候选区块的头部包含一个特殊的字段:
nonce(数字),矿工的目标是找到一个合适的nonce值,使得整个区块头的哈希值小于一个目标值。 - 哈希计算: 这是一个纯粹的暴力计算过程,矿工不断尝试不同的
nonce值,并对区块头进行哈希运算(通常是Keccak-256算法)。 - 广播获胜区块: 一旦找到符合条件的
nonce值,矿工立即将这个新区块广播到整个网络。 - 网络验证: 网络中的其他节点会验证这个新区块的有效性(包括交易有效性、难度值是否达标等)。
- 确认上链: 如果验证通过,其他节点将这个新区块添加到自己的区块链副本的末端,挖矿成功。
第三部分:源码剖析——Geth中的挖矿实现
了解了理论,我们再来看看在Geth的源码中,这个过程是如何被实现的,Geth的挖矿功能主要集中在 miner 包中。
挖矿的入口
在Geth的命令行或API中,启动挖矿通常通过 miner.start() 命令,在源码中,这个命令会调用 miner.go 文件中的 Start() 方法。
// miner.go
func (m *Miner) Start() {
// ... 一些前置检查 ...
m.mu.Lock()
defer m.mu.Unlock()
if m.thread != nil {
return // 挖矿已经在运行
}
// 创建一个用于接收挖矿结果的通道
if m.pending != nil {
m.pending.Subscribe(m.newTxsCh)
}
// 创建一个worker,它是实际执行挖矿任务的实体
m.worker = newWorker(m.config, m.chainConfig, m.engine, m.coinbase, m, m.eux, m.pow)
// 启动一个goroutine来运行挖矿循环
m.thread = gofunc() {
// ... 挖矿主循环 ...
}
}
核心是 worker 对象。 它负责管理整个挖矿过程,包括获取交易、构建候选区块、以及与共识引擎(PoW)的交互。
核心挖矿循环

在 worker 的 loop() 方法中,有一个核心的循环,它不断地执行以下操作:
- 获取交易: 从交易池中获取交易。
- 构建候选区块: 调用
worker.commitNewWork(),将交易和最新的状态信息打包成一个候选区块。 - 提交挖矿任务: 将候选区块提交给本地的
ethash共识引擎(PoW的实现)。 - 等待结果: 监听来自共识引擎的“挖矿成功”或“挖矿失败”的信号。
共识引擎——ethash
以太坊的PoW算法是 ethash,它的核心特点是“内存硬性”(Memory Hardness),即它需要大量的内存来进行计算,这使得在专用硬件(如ASIC矿机)上获得巨大优势变得困难,从而鼓励了CPU和GPU挖矿。
在 miner.go 中,我们看到了 m.pow,这个对象就是 ethash 引擎的实例,当 worker 将一个候选区块提交给它后,ethash 引擎会启动计算过程。
ethash 的核心实现在 ethash 包中,特别是 search 函数,这个函数接收候选区块的哈希和难度,然后开始暴力计算 nonce。
// ethash/ethash.go 中的核心逻辑简化
func (ethash *Ethash) Search(block *types.Block, stop <-chan struct{}) (uint64, []byte, error) {
// 获取区块头
header := block.Header()
// 获取当前难度
number := header.Number.Uint64()
difficulty := header.Difficulty
// 循环尝试不同的nonce
for nonce := uint64(0); ; nonce++ {
// 设置区块头的nonce
header.Nonce = nonce
// 计算哈希
hash := header.Hash()
// 检查哈希是否满足难度要求
if new(big.Int).Div(difficulty, big.NewInt(1)).Cmp(hash.Big()) > 0 {
// 找到了!返回nonce和哈希
return nonce, hash.Bytes(), nil
}
// 检查是否收到停止信号
select {
case <-stop:
return 0, nil, nil
default:
// 继续下一次循环
}
}
}
这个 Search 函数就是挖矿“暴力计算”的直接体现,它会一直运行,直到找到一个满足条件的 nonce,或者收到外部停止的信号。
奖励与交易
当一个区块被成功挖出后,worker 会通过 chain.InsertChain() 方法将区块提交给区块链,之后,它会执行区块中的所有交易,并将挖矿奖励(由区块的 coinbase 地址接收)写入状态数据库,这部分逻辑在 worker 的 update() 方法中处理。
