以太坊作为全球第二大公链,其共识机制从工作量证明(PoW)向权益证明(PoS)的过渡虽已启动,但PoW阶段形成的挖矿逻辑仍是理解区块链共识机制的重要基础,本文将以以太坊PoW时代的核心挖矿流程为切入点,结合关键源码片段,解析从交易收集、区块构建、哈希计算到区块确认的全过程,帮助读者深入理解以太坊挖矿的技术本质。
挖矿是以太坊PoW阶段的核心共识机制,其目标是让矿工通过计算竞争,将新的交易打包成区块并添加到区块链中,同时获得区块奖励和交易手续费作为激励,要参与挖矿,需满足两个前提条件:
以太坊挖矿流程可拆解为六个核心步骤:交易收集与验证、区块构建、哈希计算(挖矿竞争)、区块广播、共识验证、区块确认与奖励结算,以下结合以太坊核心客户端Geth的源码(以v1.10.x版本为例),逐步解析各环节的实现逻辑。
挖矿的第一步是从本地交易池(Mempool)中筛选有效交易,以太坊节点会接收网络上广播的交易,并存储在待处理交易池中,矿工需从中选取优先级高(手续费高)且状态有效的交易进行打包。

core/tx_list.go)// txList represents a sorted list of transactions and implements heap.Interface.
type txList struct {
// 其他字段...
items map[common.Hash]*types.Transaction // 交易哈希到交易的映射
// ...
}
// Peek returns the next transaction by nonce.
func (l *txList) Peek() *types.Transaction {
if l.Len() == 0 {
return nil
}
return l.txs[l.txs[0]] // 按nonce排序后,取下一个nonce的交易
}
// Underlying returns all transactions contained in the list.
func (l *txList) Underlying() []*types.Transaction {
txs := make([]*types.Transaction, 0, len(l.items))
for _, tx := range l.items {
txs = append(txs, tx)
}
return txs
}
矿工通过txList结构管理交易池,交易按nonce(发送方交易序号)排序,确保打包顺序的正确性,Geth还会通过core/tx_pool.go中的validateTx函数对交易进行验证,包括:

交易筛选完成后,矿工需将这些交易与当前区块链状态组装成候选区块(Candidate Block),以太坊区块结构定义在types/block.go中,包含以下关键字段:
type Block struct {
Header Header // 区块头(核心共识数据)
Transactions []*Transaction // 交易列表
Uncles []*Header // 叔块(叔块奖励机制)
// ...
}
type Header struct {
ParentHash common.Hash // 父区块哈希
Number *big.Int // 区块高度
Time uint64 // 时间戳
Difficulty *big.Int // 区块难度(动态调整)
MixHash common.Hash // Ethash算法的mix哈希
Nonce uint64 // 随机数(挖矿目标值)
// 其他字段:Coinbase(矿工地址)、Root(状态根)、Extra(额外数据)等
}
miner/worker.go)func (w *worker) newWork() *work {
// 1. 获取最新区块头作为父区块
parent := w.currentBlock
header := &types.Header{
ParentHash: parent.Hash(),
Number: new(big.Int).Add(parent.Number, common.Big1),
Time: uint64(time.Now().Unix()),
// 其他字段初始化...
}
// 2. 从交易池中选取交易,计算GasLimit和状态根
txs := w.txs.GetTransactions() // 获取交易池中的交易
block := types.NewBlock(header, txs, nil, nil)
// 3. 计算状态根(Merkle Patricia Trie根)
statedb, err := w.eth.BlockChain().StateAt(parent.Root(), nil)
if err != nil {
log.Error("Failed to get state", "err", err)
return nil
}
for _, tx := range txs {
// 执行交易,更新状态
if _, err := statedb.ProcessTx(w.eth.BlockChain().Config(), tx, w.eth.BlockChain().GetVMConfig()); err != nil {
log.Error("Failed to process tx", "err", err)
continue
}
}
header.Root = statedb.IntermediateRoot(false) // 更新状态根
return &work{
header: header,
txs: txs,
createdAt: time.Now(),
}
}
newWork函数是候选区块构建的核心,主要完成三件事:
header.Root),确保区块状态的有效性。区块构建完成后,矿工的核心任务是找到满足“难度目标”的Nonce值,以太坊使用Ethash算法(一种改良的DAG算法),其核心是通过两个数据集(全数据集DAG和缓存数据集Cache)生成区块头的哈希,并寻找Nonce使得哈希值小于当前难度的目标值。
consensus/ethash/ethash.go)// Hashimoto aggregates data from full dataset to compute the final hash.
func Hashimoto(hash, nonce []byte, fullSize uint64, cache []uint64, lookup func([]byte) []uint32) ([]byte, []byte) {
// 1. 计算DAG的迭代次数
mixHash := make([]byte, 64)
digest := make([]byte, 32)
// 2. 生成初始mix
mix := new(big.Int).SetBytes(hash)
for i := 0; i < 32; i {
mix.Add(mix, big.NewInt(int64(nonce[i%8])))
}
// 3. 通过Cache和DAG计算mixHash
for i := uint64(0); i < datasetParents(fullSize); i {
parent := mixHashimotoFull(fullSize, cache, mix.Uint64()%fullSize, lookup)
mix.Add(mix, new(big.Int).SetBytes(parent))
}
// 4. 计算最终哈希
digest = crypto.Keccak256(mix.Bytes(), nonce)
mixHash = crypto.Keccak256(mix.Bytes())
return digest, mixHash
}
// VerifyHash verifies if a nonce satisfies the difficulty requirement.
func VerifyHash(header *types.Header) bool {
target := new(big.Int).Div(two256, header.Difficulty)
hash := crypto.Keccak256(
header.ParentHash[:],
header.UncleHash[:],
header.Coinbase[:],
header.Root[:],
header.TxHash[:],
header.ReceiptHash[:],
header.Bloom.Bytes(),
header.Number.Bytes(),
header.GasLimit.Bytes(),
header.GasUsed.Bytes(),
header.Time.Bytes(),
header.Extra,
header.MixHash[:],
common.Uint64ToBytes(header.Nonce),
)
return new(big.Int).SetBytes(hash).Cmp(target) <= 0
}
挖矿过程本质是暴力破解Nonce:矿工不断尝试不同的Nonce值,调用Hashimoto函数计算区块头的哈