記事の内容
ethereumをプライベートで使用する際にdifficultyを固定にする方法の紹介
※運用ベースでのテストはしてません
環境
OS:CentOS7.4
golang:1.13.3
geth:1.9.11-unstable
goやgit、g++などコンパイルに必要となるツールはインストールされている前提で進めます。
difficultyの固定と動作検証(簡易)
gethの修正&ビルド
まずはgitからコードを落として、早速対象のコードを修正します。
ディレクトリは任意の場所で作業をします。
$ cd /home
$ git clone https://github.com/ethereum/go-ethereum.git
$ vi consensus/ethash/consensus.go
454行目から
// calcDifficultyFrontier is the difficulty adjustment algorithm. It returns the
// difficulty that a new block should have when created at time given the parent
// block's time and difficulty. The calculation uses the Frontier rules.
func calcDifficultyFrontier(time uint64, parent *types.Header) *big.Int {
diff := new(big.Int)
adjust := new(big.Int).Div(parent.Difficulty, params.DifficultyBoundDivisor)
bigTime := new(big.Int)
bigParentTime := new(big.Int)
bigTime.SetUint64(time)
bigParentTime.SetUint64(parent.Time)
if bigTime.Sub(bigTime, bigParentTime).Cmp(params.DurationLimit) < 0 {
diff.Add(parent.Difficulty, adjust)
} else {
diff.Sub(parent.Difficulty, adjust)
}
if diff.Cmp(params.MinimumDifficulty) < 0 {
diff.Set(params.MinimumDifficulty)
}
periodCount := new(big.Int).Add(parent.Number, big1)
periodCount.Div(periodCount, expDiffPeriod)
if periodCount.Cmp(big1) > 0 {
// diff = diff + 2^(periodCount - 2)
expDiff := periodCount.Sub(periodCount, big2)
expDiff.Exp(big2, expDiff, nil)
diff.Add(diff, expDiff)
diff = math.BigMax(diff, params.MinimumDifficulty)
}
// return diff
return big1
}
修正箇所はreturnで返却する値を計算結果からbig1(定数 = 1)に変更する。
コードを見る限り、この関数内を「return big1」だけにしても問題無いかなと思います。
ビルドします。
$ make geth
env GO111MODULE=on go run build/ci.go install ./cmd/geth
>>> /usr/lib/golang/bin/go install -ldflags -X main.gitCommit=976a0f5558e20ed7cb7ba2cd68d7429d1ef01db9 -X main.gitDate=20200205 -v ./cmd/geth
Done building.
Run "./build/bin/geth" to launch geth.
この様な結果になればビルド成功です。
gethの起動準備
ここからはほぼ、以下のサイトの内容になります。
まずは、ジェネシスファイル(JSON)を作成します。
$ mkdir /home/eth_private_net
$ cd /home/eth_private_net
$ vi myGenesis.json
{
"config": {
"chainId": 15
},
"nonce": "0x0000000000000042",
"timestamp": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "",
"gasLimit": "0x8000000",
"difficulty": "0x0000",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x3333333333333333333333333333333333333333",
"alloc": {}
}
ここではdifficultyを0にしています。
gethを動かしてみる
ジェネシスファイルの準備が出来たら、gethを動かしてみます。
$ cd /home/go-ethereum/build/bin
$ ./geth --datadir /home/eth_private_net init /home/eth_private_net/myGenesis.json
(略)問題なければ以下のログが出力されるはず
INFO [02-07|00:26:16.907] Successfully wrote genesis state database=lightchaindata hash=9f02b9…c34631
$ ./geth --networkid "15" --nodiscover --datadir "/home/eth_private_net" console 2>> /home/eth_private_net/geth_err.log
Welcome to the Geth JavaScript console!
instance: Geth/v1.9.11-unstable-976a0f55-20200205/linux-amd64/go1.13.3
at block: 0 (Thu Jan 01 1970 00:00:00 GMT+0000 (UTC))
datadir: /home/eth_private_net
modules: admin:1.0 debug:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
マイニングを開始する
これからは「Geth Java Script console」の操作になります。
アカウントの作成、マイニング開始→マイニング終了までを操作してみます。
> eth.account
undefined
> eth.accounts
[]
> personal.newAccount("hogehoge01")
"0x6918ef5c82e837fed798af3d61f62fda54151729"
> miner.start()
null
> eth.blockNumber
15
> miner.stop()
null
>
マイニング開始後、最新のブロック番号を取得すると「15」となっており、ブロックが作成されていることを確認できました。
※私は「miner.start()」をやってもマイニングが最初出来なかったのですが、仮想マシンに割り当てているメモリが不足していることが原因でした。
1GBだとメモリ不足となりマイニングされず、4GBに増やすとマイニングされました。
調べてみるとマイニングには2GB以上必要というような情報を見つけました。
ブロック情報を確認する
ジェネシスブロックからブロックの情報をいくつか確認してみます。
> eth.getBlock(0)
{
difficulty: 0,
extraData: "0x",
gasLimit: 134217728,
gasUsed: 0,
hash: "0x9f02b99a864192b04587a44b20a9776cabf49bc664829cc7886c297155c34631",
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
miner: "0x3333333333333333333333333333333333333333",
mixHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
nonce: "0x0000000000000042",
number: 0,
parentHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 505,
stateRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
timestamp: 0,
totalDifficulty: 0,
transactions: [],
transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
uncles: []
}
> eth.getBlock(1)
{
difficulty: 1,
extraData: "0xd88301090b846765746888676f312e31332e33856c696e7578",
gasLimit: 134086657,
gasUsed: 0,
hash: "0xcde3272999885b223cfe03555098ded3695b8f40f305bcd571340b938478feec",
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
miner: "0x6918ef5c82e837fed798af3d61f62fda54151729",
mixHash: "0x8ee76bebc7e8831a892bc5bfc624b5b4c94078b6a47c7f4870faf19ba3be2b0b",
nonce: "0x25778c99f3377e77",
number: 1,
parentHash: "0x9f02b99a864192b04587a44b20a9776cabf49bc664829cc7886c297155c34631",
receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 534,
stateRoot: "0x4b9572efef79e46c6779da6432a2fda287e9ed8557c9ecc6c417eba7b925c039",
timestamp: 1581035813,
totalDifficulty: 1,
transactions: [],
transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
uncles: []
}
> eth.getBlock(2)
{
difficulty: 1,
extraData: "0xd88301090b846765746888676f312e31332e33856c696e7578",
gasLimit: 133955714,
gasUsed: 0,
hash: "0x163e0ddd27e8c84465ea4012db0677737493cacee9871ff4db97c6ce3c3a8dff",
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
miner: "0x6918ef5c82e837fed798af3d61f62fda54151729",
mixHash: "0xdf576b908fff00f745e87bb756aa8951d4edc841b55233087e484ccf78b8f4f2",
nonce: "0x1b7726eebe60e5b1",
number: 2,
parentHash: "0xcde3272999885b223cfe03555098ded3695b8f40f305bcd571340b938478feec",
receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 534,
stateRoot: "0xe1d55911fc5cc66e79e899d76a12f85907d4d2d3096076764c1008231b08681a",
timestamp: 1581035815,
totalDifficulty: 2,
transactions: [],
transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
uncles: []
}
ジェネシスブロックはdifficultyを「0」に指定していましたが、マイニングよって作られたブロックはdifficultyの値が「1」固定になっています。
参考
gethの古いバージョンだと修正するソースコードの場所が異なるようです。
geth の difficulty をあまり上げない(or 一定にする)方法
感想
思いの外、簡単に出来ました。
次回は処理待ちトランザクションがないとブロックを作らないなどの改造にチャレンジしようと思います。