以太坊挖矿流程解析
字数 738 2025-08-22 12:23:12
以太坊挖矿流程与智能合约部署详解
一、以太坊挖矿核心概念
以太坊挖矿是通过工作量证明(PoW)机制来验证交易和创建新区块的过程,矿工通过解决复杂的数学难题来获得区块奖励和交易费用。
1.1 挖矿参数配置
挖矿配置参数定义在miner/miner.go中:
type Config struct {
Etherbase common.Address `toml:",omitempty"` // 挖矿奖励接收地址
Notify []string `toml:",omitempty"` // 新工作包通知URL列表
NotifyFull bool `toml:",omitempty"` // 是否使用完整区块头通知
ExtraData hexutil.Bytes `toml:",omitempty"` // 矿工添加的额外数据
GasFloor uint64 // 区块最低Gas限制
GasCeil uint64 // 区块最高Gas限制
GasPrice *big.Int // 最低交易Gas价格
Recommit time.Duration // 重新创建挖矿工作的时间间隔
Noverify bool // 是否禁用远程挖矿验证
}
1.2 矿工核心数据结构
type Miner struct {
mux *event.TypeMux
worker *worker
coinbase common.Address
eth Backend
engine consensus.Engine
exitCh chan struct{}
startCh chan common.Address
stopCh chan struct{}
}
type worker struct {
config *Config
chainConfig *params.ChainConfig
engine consensus.Engine
eth Backend
chain *core.BlockChain
// 其他字段...
}
二、挖矿流程详解
2.1 矿工实例创建
func New(eth Backend, config *Config, chainConfig *params.ChainConfig,
mux *event.TypeMux, engine consensus.Engine,
isLocalBlock func(block *types.Block) bool) *Miner {
miner := &Miner{
eth: eth,
mux: mux,
engine: engine,
exitCh: make(chan struct{}),
startCh: make(chan common.Address),
stopCh: make(chan struct{}),
worker: newWorker(config, chainConfig, engine, eth, mux, isLocalBlock, true),
}
go miner.update()
return miner
}
2.2 挖矿核心循环
2.2.1 newWorkLoop
负责在收到事件时提交新的挖矿工作:
func (w *worker) newWorkLoop(recommit time.Duration) {
// 初始化定时器...
commit := func(noempty bool, s int32) {
// 中断当前任务并提交新工作
if interrupt != nil {
atomic.StoreInt32(interrupt, s)
}
interrupt = new(int32)
w.newWorkCh <- &newWorkReq{
interrupt: interrupt,
noempty: noempty,
timestamp: timestamp,
}
timer.Reset(recommit)
atomic.StoreInt32(&w.newTxs, 0)
}
// 清理过期的待处理任务
clearPending := func(number uint64) {
w.pendingMu.Lock()
for h, t := range w.pendingTasks {
if t.block.NumberU64()+staleThreshold <= number {
delete(w.pendingTasks, h)
}
}
w.pendingMu.Unlock()
}
// 主循环
for {
select {
case <-w.startCh:
clearPending(w.chain.CurrentBlock().NumberU64())
timestamp = time.Now().Unix()
commit(false, commitInterruptNewHead)
case head := <-w.chainHeadCh:
clearPending(head.Block.NumberU64())
timestamp = time.Now().Unix()
commit(false, commitInterruptNewHead)
// 其他case处理...
}
}
}
2.2.2 mainLoop
处理新工作请求、分叉事件和交易池更新:
func (w *worker) mainLoop() {
defer w.txsSub.Unsubscribe()
defer w.chainHeadSub.Unsubscribe()
defer w.chainSideSub.Unsubscribe()
for {
select {
case req := <-w.newWorkCh:
w.commitNewWork(req.interrupt, req.noempty, req.timestamp)
case ev := <-w.chainSideCh:
// 处理叔块
if w.isLocalBlock != nil && w.isLocalBlock(ev.Block) {
w.localUncles[ev.Block.Hash()] = ev.Block
} else {
w.remoteUncles[ev.Block.Hash()] = ev.Block
}
// 如果当前区块叔块少于2个,尝试添加
if w.isRunning() && w.current != nil && w.current.uncles.Cardinality() < 2 {
if err := w.commitUncle(w.current, ev.Block.Header()); err == nil {
// 提交新区块
}
}
case ev := <-w.txsCh:
// 处理交易池更新
if !w.isRunning() && w.current != nil {
// 执行交易并更新状态
}
// 其他case处理...
}
}
}
2.3 区块打包流程
2.3.1 commitNewWork
func (w *worker) commitNewWork(interrupt *int32, noempty bool, timestamp int64) {
// 获取父区块
parent := w.chain.CurrentBlock()
// 创建新区块头
header := &types.Header{
ParentHash: parent.Hash(),
Number: num.Add(num, common.Big1),
GasLimit: core.CalcGasLimit(parent, w.config.GasFloor, w.config.GasCeil),
Extra: w.extra,
Time: uint64(timestamp),
}
// 设置矿工地址
if w.isRunning() {
header.Coinbase = w.coinbase
}
// 准备共识引擎
if err := w.engine.Prepare(w.chain, header); err != nil {
return
}
// 处理DAO分叉
if daoBlock := w.chainConfig.DAOForkBlock; daoBlock != nil {
// 处理分叉逻辑...
}
// 创建工作环境
if err := w.makeCurrent(parent, header); err != nil {
return
}
// 添加叔块
uncles := make([]*types.Header, 0, 2)
commitUncles := func(blocks map[common.Hash]*types.Block) {
// 清理过期叔块并添加新叔块
}
commitUncles(w.localUncles)
commitUncles(w.remoteUncles)
// 创建空区块(如果需要)
if !noempty && atomic.LoadUint32(&w.noempty) == 0 {
w.commit(uncles, nil, false, tstart)
}
// 从交易池获取待处理交易
pending, err := w.eth.TxPool().Pending()
if err != nil {
return
}
// 分离本地和远程交易
localTxs, remoteTxs := make(map[common.Address]types.Transactions), pending
for _, account := range w.eth.TxPool().Locals() {
if txs := remoteTxs[account]; len(txs) > 0 {
delete(remoteTxs, account)
localTxs[account] = txs
}
}
// 提交交易
if len(localTxs) > 0 {
txs := types.NewTransactionsByPriceAndNonce(w.current.signer, localTxs)
if w.commitTransactions(txs, w.coinbase, interrupt) {
return
}
}
if len(remoteTxs) > 0 {
txs := types.NewTransactionsByPriceAndNonce(w.current.signer, remoteTxs)
if w.commitTransactions(txs, w.coinbase, interrupt) {
return
}
}
// 最终提交
w.commit(uncles, w.fullTaskHook, true, tstart)
}
2.3.2 commitTransactions
func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coinbase common.Address, interrupt *int32) bool {
for {
// 检查中断信号
if interrupt != nil && atomic.LoadInt32(interrupt) != commitInterruptNone {
return true
}
// 获取下一个交易
tx := txs.Peek()
if tx == nil {
break
}
// 执行交易
w.current.state.Prepare(tx.Hash(), common.Hash{}, w.current.tcount)
logs, err := w.commitTransaction(tx, coinbase)
// 处理执行结果
switch {
case err == nil:
// 交易成功,添加到收据
w.current.tcount++
w.current.receipts = append(w.current.receipts, receipt)
w.current.logs = append(w.current.logs, logs...)
// 其他错误处理...
}
}
return false
}
2.4 挖矿任务处理
2.4.1 taskLoop
func (w *worker) taskLoop() {
var (
stopCh chan struct{}
prev common.Hash
)
interrupt := func() {
if stopCh != nil {
close(stopCh)
stopCh = nil
}
}
for {
select {
case task := <-w.taskCh:
// 检查重复任务
sealHash := w.engine.SealHash(task.block.Header())
if sealHash == prev {
continue
}
// 中断之前的挖矿操作
interrupt()
stopCh, prev = make(chan struct{}), sealHash
// 添加到待处理任务
w.pendingMu.Lock()
w.pendingTasks[sealHash] = task
w.pendingMu.Unlock()
// 开始挖矿
if err := w.engine.Seal(w.chain, task.block, w.resultCh, stopCh); err != nil {
log.Warn("Block sealing failed", "err", err)
}
// 其他case处理...
}
}
}
2.4.2 resultLoop
func (w *worker) resultLoop() {
for {
select {
case block := <-w.resultCh:
// 获取相关任务
var (
sealhash = w.engine.SealHash(block.Header())
hash = block.Hash()
)
w.pendingMu.RLock()
task, exist := w.pendingTasks[sealhash]
w.pendingMu.RUnlock()
if !exist {
continue
}
// 更新收据和日志
var (
receipts = make([]*types.Receipt, len(task.receipts))
logs []*types.Log
)
for i, receipt := range task.receipts {
receipt.BlockHash = hash
receipt.BlockNumber = block.Number()
receipt.TransactionIndex = uint(i)
receipts[i] = new(types.Receipt)
*receipts[i] = *receipt
for _, log := range receipt.Logs {
log.BlockHash = hash
}
logs = append(logs, receipt.Logs...)
}
// 写入区块链
_, err := w.chain.WriteBlockWithState(block, receipts, logs, task.state, true)
if err != nil {
log.Error("Failed writing block to chain", "err", err)
continue
}
// 广播新区块
w.mux.Post(core.NewMinedBlockEvent{Block: block})
w.unconfirmed.Insert(block.NumberU64(), block.Hash())
// 其他case处理...
}
}
}
三、智能合约部署实战
3.1 合约编写
pragma solidity ^0.8.4;
contract test {
function multiply(uint a) public returns (uint d) {
return a * 7;
}
}
3.2 合约编译
编译后得到ABI接口定义:
[{
"inputs": [{"internalType": "uint256", "name": "a", "type": "uint256"}],
"name": "multiply",
"outputs": [{"internalType": "uint256", "name": "d", "type": "uint256"}],
"stateMutability": "nonpayable",
"type": "function"
}]
3.3 合约部署步骤
- 启动Geth节点:
sudo geth --networkid 666 --datadir /home/ubuntu/Private_eth/eth1 \
--identity "node1" --rpc --rpcport "8545" --rpcaddr "192.168.174.212" \
--nodiscover --rpcapi "eth,net,web3,txpool,debug,miner" \
--allow-insecure-unlock console
- 创建合约对象:
var abi = JSON.parse('[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"payable":false,"type":"function","stateMutability":"nonpayable"}]')
myContract = web3.eth.contract(abi)
- 检查账户余额:
eth.coinbase
// "0x8b52dc34762a2a951406d3189bdba1b920e7cde9"
web3.fromWei(eth.getBalance(eth.accounts[0]), "ether")
// 20.000021
- 解锁账户:
personal.unlockAccount(eth.accounts[0], "123456", 0)
- 预估部署手续费:
// 149143 wei
- 部署合约:
// 部署代码...
- 开始挖矿:
miner.start()
- 验证合约部署:
// 验证代码...
四、关键点总结
-
挖矿流程:
- 矿工实例创建 → 工作循环启动 → 区块打包 → 交易执行 → 工作量证明 → 区块提交
-
区块打包:
- 创建区块头 → 设置共识参数 → 添加叔块 → 执行交易 → 计算状态根 → 生成最终区块
-
智能合约部署:
- 编写合约 → 编译获取ABI → 预估Gas → 发送部署交易 → 挖矿确认 → 验证部署
-
性能优化:
- 合理设置GasFloor和GasCeil
- 调整Recommit间隔平衡响应速度和资源消耗
- 优先处理本地交易减少网络延迟
-
安全注意事项:
- 确保矿工地址安全
- 谨慎处理DAO分叉等特殊情况
- 验证远程叔块的有效性