go-ethereumを読む(1) 準備編の続きで、
今回はローカルでgethを起動するために必要なGemesisブロックの生成辺りを解説します。
ローカルで動かすときはPOAで動かした方が処理が早いのでPOAの説明がメインです。
v1.8.15ベースに解説します
puppeth
gethを起動するときにdevフラグを付けない場合、gensis.jsonを作成する必要があります。
genesis.json作成についてはこの記事が詳しいです
geth init
gethをdevフラグをつけて起動する場合 geth init
は必要ありません。
その場合下記の状態で起動されます。
- POAになる
- アカウントを作成・アンロックする
- GasPriceが1になる
geth init実行時に処理されるコードを追っていきます。
makeFullNode > makeConfigNode > SetEthConfig
// cmd/utils/flags.go
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
// 省略
// Override any default configs for hard coded networks.
switch {
// 省略
case ctx.GlobalBool(DeveloperFlag.Name):
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
cfg.NetworkId = 1337
}
// Create new developer account or reuse existing one
var (
developer accounts.Account
err error
)
if accs := ks.Accounts(); len(accs) > 0 {
developer = ks.Accounts()[0]
} else {
developer, err = ks.NewAccount("")
if err != nil {
Fatalf("Failed to create developer account: %v", err)
}
}
if err := ks.Unlock(developer, ""); err != nil {
Fatalf("Failed to unlock developer account: %v", err)
}
log.Info("Using developer account", "address", developer.Address)
cfg.Genesis = core.DeveloperGenesisBlock(uint64(ctx.GlobalInt(DeveloperPeriodFlag.Name)), developer.Address)
if !ctx.GlobalIsSet(MinerGasPriceFlag.Name) && !ctx.GlobalIsSet(MinerLegacyGasPriceFlag.Name) {
cfg.MinerGasPrice = big.NewInt(1)
}
}
// 省略
}
// core/genesis.go
func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis {
// POAになる(Clique)
// Override the default period to the user requested one
config := *params.AllCliqueProtocolChanges
config.Clique.Period = period
// Assemble and return the genesis with the precompiles and faucet pre-funded
return &Genesis{
Config: &config,
ExtraData: append(append(make([]byte, 32), faucet[:]...), make([]byte, 65)...),
GasLimit: 6283185,
Difficulty: big.NewInt(1),
Alloc: map[common.Address]GenesisAccount{
common.BytesToAddress([]byte{1}): {Balance: big.NewInt(1)}, // ECRecover
common.BytesToAddress([]byte{2}): {Balance: big.NewInt(1)}, // SHA256
common.BytesToAddress([]byte{3}): {Balance: big.NewInt(1)}, // RIPEMD
common.BytesToAddress([]byte{4}): {Balance: big.NewInt(1)}, // Identity
common.BytesToAddress([]byte{5}): {Balance: big.NewInt(1)}, // ModExp
common.BytesToAddress([]byte{6}): {Balance: big.NewInt(1)}, // ECAdd
common.BytesToAddress([]byte{7}): {Balance: big.NewInt(1)}, // ECScalarMul
common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing
faucet: {Balance: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9))},
},
}
}
// 下のURLが詳しいので、ほとんど、ここを読めば事足りる
// https://arvanaghi.com/blog/explaining-the-genesis-block-in-ethereum/
type Genesis struct {
Config *params.ChainConfig `json:"config"` // ブロックチェーンの設定
Nonce uint64 `json:"nonce"`
Timestamp uint64 `json:"timestamp"`
ExtraData []byte `json:"extraData"`
GasLimit uint64 `json:"gasLimit" gencodec:"required"`
Difficulty *big.Int `json:"difficulty" gencodec:"required"`
Mixhash common.Hash `json:"mixHash"`
Coinbase common.Address `json:"coinbase"`
Alloc GenesisAlloc `json:"alloc" gencodec:"required"`
// These fields are used for consensus tests. Please don't use them
// in actual genesis blocks.
Number uint64 `json:"number"`
GasUsed uint64 `json:"gasUsed"`
ParentHash common.Hash `json:"parentHash"`
}
// GenesisAlloc specifies the initial state that is part of the genesis block.
type GenesisAlloc map[common.Address]GenesisAccount
type ChainConfig struct {
// blockchainの識別ID geth起動時に networkidとはべつもの
// EIP155に定義されているのとは被らないようにする https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md
ChainId *big.Int `json:"chainId"`
HomesteadBlock *big.Int `json:"homesteadBlock,omitempty"` // 0に設定しておけばHomesteadになる main netも0らしい
DAOForkBlock *big.Int `json:"daoForkBlock,omitempty"` // 指定の必要なし。TheDAO事件のHard Forkしたblock番号
DAOForkSupport bool `json:"daoForkSupport,omitempty"` // 指定の必要なし。TheDAO事件のHard Forkに賛成か反対かを指定
// EIP150で Gas priceの変更があったのでそのための項目 (https://github.com/ethereum/EIPs/issues/150)
// storage readのGas priceが他と比べて安すぎてトランザクション拒否攻撃やトランザクションspamを防ぐため?
EIP150Block *big.Int `json:"eip150Block,omitempty"` // 0を指定しておけばOK
EIP150Hash common.Hash `json:"eip150Hash,omitempty"` // 指定の必要なし。 main netで --syncmode=fastで使用する --syncmode=fastがデフォルト
EIP155Block *big.Int `json:"eip155Block,omitempty"` // 0を指定しておけばOK EIP155 Hard Fork 別環境のtransactionが使えてしまう問題があって、TransactionHashにChainIdを付けるようになった
EIP158Block *big.Int `json:"eip158Block,omitempty"` // 0を指定しておけばOK EIP158 Hard Fork 空のアカウントを無視して容量を削減するようにした
ByzantiumBlock *big.Int `json:"byzantiumBlock,omitempty"` // 指定の必要なし。 Byzantiumに切り替えたブロック
ConstantinopleBlock *big.Int `json:"constantinopleBlock,omitempty"` // 指定の必要なし。 Constantinopleに切り替えたブロック
Ethash *EthashConfig `json:"ethash,omitempty"` // 指定の必要なし。 Ethashの設定 今のところ無し
Clique *CliqueConfig `json:"clique,omitempty"` // POAにするときに必要
}
type CliqueConfig struct {
Period uint64 `json:"period"` // ブロックの生成時間のインターバル
// Epoch 投票(vote)のリセット間隔 checkpointはcheckpointIntervalだと思うけど、プログラムが怪しい?
// 投票はblockのheaderのNonceを使って正しい署名者かをチェックする?
// checkpointInterval毎のblockでsnapshotをdbに書き出す
// それまでは、lruにインメモリで持っている
Epoch uint64 `json:"epoch"`
}
// params/config.go
var (
// 省略
// AllEthashProtocolChanges contains every protocol change (EIPs) introduced
// and accepted by the Ethereum core developers into the Ethash consensus.
//
// This configuration is intentionally not using keyed fields to force anyone
// adding flags to the config to also have to set these fields.
AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil}
// AllCliqueProtocolChanges contains every protocol change (EIPs) introduced
// and accepted by the Ethereum core developers into the Clique consensus.
//
// This configuration is intentionally not using keyed fields to force anyone
// adding flags to the config to also have to set these fields.
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}}
// 省略
)
$ geth --datadir testnet init genesis.json
普通の場合のサンプル
{
"config": {
"chainId": 33,
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"clique": {
"period": 33,
"epoch": 30000
}
},
"difficulty": "0x400000",
"gasLimit": "0x8000000",
"alloc": {}
}
}
POA(clique)にする場合のサンプル
{
"config": {
"chainId": 33,
"homesteadBlock": 1,
"eip150Block": 2,
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"eip155Block": 3,
"eip158Block": 3,
"byzantiumBlock": 4,
"clique": {
"period": 15,
"epoch": 30000
}
},
"nonce": "0x0",
"timestamp": "0x5afe4952",
"extraData": "0x00000000000000000000000000000000000000000000000000000000000000004528fc478bb156f0e79988a1f5fca25bfa43039c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x47b760",
"difficulty": "0x1",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x0000000000000000000000000000000000000000",
"alloc": {
"0000000000000000000000000000000000000000": {
"balance": "0x1"
},
},
"number": "0x0",
"gasUsed": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}