在以太坊私有鏈的開(kāi)發(fā)或測(cè)試過(guò)程中,出于壓力測(cè)試、經(jīng)濟(jì)模型模擬或特定業(yè)務(wù)邏輯驗(yàn)證的需求,我們可能需要修改礦工打包區(qū)塊所獲得的獎(jiǎng)勵(lì)。

雖然以太坊主網(wǎng)目前已經(jīng)轉(zhuǎn)為 PoS(權(quán)益證明),且歷史上也曾經(jīng)歷過(guò)“拜占庭”、“君士坦丁堡”等硬分叉導(dǎo)致的挖礦獎(jiǎng)勵(lì)衰減(從 5 ETH 降至 3 ETH 再降至 2 ETH),但在私鏈環(huán)境(通?;?Geth 客戶(hù)端)中,我們可以通過(guò)修改底層代碼來(lái)實(shí)現(xiàn)自定義的出塊獎(jiǎng)勵(lì)。

本文將以 Go-Ethereum (Geth) 的常見(jiàn)版本為例,詳細(xì)介紹如何通過(guò)修改源碼來(lái)調(diào)整私鏈的出塊獎(jiǎng)勵(lì)。


為什么需要修改源碼

在 Geth 的默認(rèn)配置中,出塊獎(jiǎng)勵(lì)并不是在 genesis.json 文件中配置的,而是被硬編碼在共識(shí)引擎的代碼邏輯中。

如果你使用的是 eth

隨機(jī)配圖
ash (PoW) 共識(shí)算法,獎(jiǎng)勵(lì)邏輯位于 Geth 代碼庫(kù)的共識(shí)層中,簡(jiǎn)單的配置文件修改無(wú)法生效,必須修改 Golang 源碼并重新編譯客戶(hù)端。

環(huán)境準(zhǔn)備

  1. 操作系統(tǒng):Linux (Ubuntu/CentOS) 或 macOS。
  2. 依賴(lài)環(huán)境:Go 語(yǔ)言環(huán)境 (1.19+ 或根據(jù) Geth 版本要求)。
  3. 源碼:從 GitHub 克隆 Go-Ethereum 倉(cāng)庫(kù)。
git clone https://github.com/ethereum/go-ethereum.git
cd go-ethereum

定位核心代碼

出塊獎(jiǎng)勵(lì)的邏輯主要位于 consensus/ethash 目錄下,我們需要修改 consensus.go 文件。

文件路徑consensus/ethash/consensus.go

在該文件中,搜索關(guān)鍵字 FinalizeAndAssemble 或者直接查找數(shù)字(如當(dāng)前的獎(jiǎng)勵(lì)基數(shù))。

在較新的 Geth 版本中,你會(huì)看到類(lèi)似以下的邏輯(為了演示,代碼可能有簡(jiǎn)略):

// consensus/ethash/consensus.go
// ...
// 計(jì)算區(qū)塊獎(jiǎng)勵(lì)
var (
    blockReward *big.Int
)
// 這里的邏輯通常判斷區(qū)塊號(hào),根據(jù)以太坊歷史上的硬分叉規(guī)則設(shè)定獎(jiǎng)勵(lì)
// Byzantium, Constantinople 等分叉點(diǎn)
if chainConfig.IsByzantium(blockNumber) {
    blockReward = big.NewInt(3e+18) // 3 ETH (3 * 10^18 wei)
}
// ...

修改獎(jiǎng)勵(lì)數(shù)值

假設(shè)我們需要將出塊獎(jiǎng)勵(lì)修改為 100 ETH,我們需要修改上述代碼邏輯。

注意:以太坊的最小單位是 Wei,1 ETH = 10^18 Wei,100 ETH = 100 * 10^18。

修改方案 A:簡(jiǎn)單暴力法(忽略分叉規(guī)則) 如果你希望無(wú)論區(qū)塊高度是多少,獎(jiǎng)勵(lì)永遠(yuǎn)是 100 ETH,可以直接注釋掉復(fù)雜的判斷邏輯,強(qiáng)制賦值:

// 修改前
// if chainConfig.IsByzantium(blockNumber) {
//     blockReward = big.NewInt(3e+18)
// }
// 修改后
// 強(qiáng)制設(shè)定獎(jiǎng)勵(lì)為 100 ETH
blockReward = big.NewInt(100e+18) 

修改方案 B:保留邏輯法 如果你希望保留代碼結(jié)構(gòu),可以修改特定分叉的數(shù)值:

if chainConfig.IsByzantium(blockNumber) {
    // 修改這里,將 3e+18 改為你想要的數(shù)值,50 ETH
    blockReward = big.NewInt(50e+18) 
}

關(guān)于叔塊獎(jiǎng)勵(lì)

如果你在私鏈中也會(huì)產(chǎn)生叔塊,并且希望修改叔塊的獎(jiǎng)勵(lì)規(guī)則,同樣在這個(gè)文件中搜索 accumulateRewards 函數(shù)(視 Geth 版本而定,有些版本邏輯直接寫(xiě)在 Finalize 中)。

通常代碼會(huì)包含計(jì)算叔塊獎(jiǎng)勵(lì)的數(shù)學(xué)公式: numReward := new(big.Int).Div(blockReward, big.NewInt(8))

如果你完全不需要叔塊獎(jiǎng)勵(lì)邏輯,或者想簡(jiǎn)化它,也可以在此處調(diào)整。

編譯與部署

修改完代碼后,保存文件,執(zhí)行 Geth 的編譯命令。

  1. 編譯 Geth: 在項(xiàng)目根目錄下執(zhí)行:

    make geth

    編譯成功后,生成的二進(jìn)制文件通常位于 build/bin/geth

  2. 部署私鏈

    • 停止當(dāng)前運(yùn)行的私鏈節(jié)點(diǎn)。
    • 備份并替換舊的 Geth 可執(zhí)行文件。
    • 使用修改后的 Geth 啟動(dòng)你的私鏈節(jié)點(diǎn)。
    • 注意:如果是在已有的鏈上修改獎(jiǎng)勵(lì),建議清理舊數(shù)據(jù)(刪除 --datadir 目錄)并重新創(chuàng)世,因?yàn)樾薷莫?jiǎng)勵(lì)屬于共識(shí)變更,舊節(jié)點(diǎn)可能會(huì)因?yàn)楣沧R(shí)不匹配而無(wú)法同步,但在私鏈單節(jié)點(diǎn)或所有節(jié)點(diǎn)同步升級(jí)的情況下是可行的。

驗(yàn)證結(jié)果

啟動(dòng)節(jié)點(diǎn)并開(kāi)始挖礦(miner.start())。

等待生成幾個(gè)區(qū)塊后,查詢(xún)當(dāng)前區(qū)塊的 Coinbase(礦工地址)余額:

// 在 Geth Console 中
eth.getBalance(eth.coinbase)

如果返回的余額增長(zhǎng)速度符合你設(shè)定的 100 ETH/塊,說(shuō)明修改成功。

修改以太坊私鏈的出塊獎(jiǎng)勵(lì)本質(zhì)上是對(duì) consensus/ethash/consensus.go 源碼的“Hack”,這對(duì)于以下場(chǎng)景非常有用:

  1. 測(cè)試環(huán)境搭建:快速生成大量代幣用于測(cè)試。
  2. 聯(lián)盟鏈開(kāi)發(fā):根據(jù)聯(lián)盟內(nèi)部的激勵(lì)規(guī)則定制經(jīng)濟(jì)模型。

?? 風(fēng)險(xiǎn)提示:請(qǐng)務(wù)必僅在私有鏈或開(kāi)發(fā)環(huán)境中使用此修改版客戶(hù)端,切勿連接以太坊主網(wǎng)或公共測(cè)試網(wǎng),否則將因共識(shí)不匹配導(dǎo)致節(jié)點(diǎn)無(wú)法同步或分叉。