在以太坊私有鏈的開(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 (PoW) 共識(shí)算法,獎(jiǎng)勵(lì)邏輯位于 Geth 代碼庫(kù)的共識(shí)層中,簡(jiǎn)單的配置文件修改無(wú)法生效,必須修改 Golang 源碼并重新編譯客戶(hù)端。
環(huán)境準(zhǔn)備
- 操作系統(tǒng):Linux (Ubuntu/CentOS) 或 macOS。
- 依賴(lài)環(huán)境:Go 語(yǔ)言環(huán)境 (1.19+ 或根據(jù) Geth 版本要求)。
- 源碼:從 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 的編譯命令。
-
編譯 Geth: 在項(xiàng)目根目錄下執(zhí)行:
make geth
編譯成功后,生成的二進(jìn)制文件通常位于
build/bin/geth。 -
部署私鏈:
- 停止當(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)景非常有用:
- 測(cè)試環(huán)境搭建:快速生成大量代幣用于測(cè)試。
- 聯(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ú)法同步或分叉。