github.com/klaytn/klaytn@v1.12.1/consensus/istanbul/backend/testutil_test.go (about) 1 // Modifications Copyright 2020 The klaytn Authors 2 // Copyright 2017 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 18 package backend 19 20 import ( 21 "crypto/ecdsa" 22 "flag" 23 "math/big" 24 "testing" 25 "time" 26 27 "github.com/klaytn/klaytn/blockchain" 28 "github.com/klaytn/klaytn/blockchain/types" 29 "github.com/klaytn/klaytn/blockchain/vm" 30 "github.com/klaytn/klaytn/common" 31 "github.com/klaytn/klaytn/consensus" 32 "github.com/klaytn/klaytn/consensus/istanbul" 33 "github.com/klaytn/klaytn/consensus/istanbul/core" 34 "github.com/klaytn/klaytn/crypto" 35 "github.com/klaytn/klaytn/crypto/bls" 36 "github.com/klaytn/klaytn/governance" 37 "github.com/klaytn/klaytn/log" 38 "github.com/klaytn/klaytn/params" 39 "github.com/klaytn/klaytn/reward" 40 "github.com/klaytn/klaytn/rlp" 41 "github.com/klaytn/klaytn/storage/database" 42 ) 43 44 var ( 45 testBaseConfig *params.ChainConfig 46 testKoreConfig *params.ChainConfig 47 testRandaoConfig *params.ChainConfig 48 ) 49 50 func init() { 51 testBaseConfig = ¶ms.ChainConfig{ 52 Istanbul: params.GetDefaultIstanbulConfig(), 53 Governance: params.GetDefaultGovernanceConfig(), 54 } 55 56 testKoreConfig = testBaseConfig.Copy() 57 testKoreConfig.IstanbulCompatibleBlock = common.Big0 58 testKoreConfig.LondonCompatibleBlock = common.Big0 59 testKoreConfig.EthTxTypeCompatibleBlock = common.Big0 60 testKoreConfig.MagmaCompatibleBlock = common.Big0 61 testKoreConfig.KoreCompatibleBlock = common.Big0 62 63 testRandaoConfig = testKoreConfig.Copy() 64 testRandaoConfig.ShanghaiCompatibleBlock = common.Big0 65 testRandaoConfig.CancunCompatibleBlock = common.Big0 66 testRandaoConfig.RandaoCompatibleBlock = common.Big0 67 testRandaoConfig.RandaoRegistry = ¶ms.RegistryConfig{} 68 } 69 70 type testOverrides struct { 71 node0Key *ecdsa.PrivateKey // Override node[0] key 72 node0BlsKey bls.SecretKey // Override node[0] bls key 73 blockPeriod *uint64 // Override block period. If not set, 1 second is used. 74 stakingAmounts []uint64 // Override staking amounts. If not set, 0 for all nodes. 75 } 76 77 // Mock BlsPubkeyProvider that replaces KIP-113 contract query. 78 type mockBlsPubkeyProvider struct { 79 infos map[common.Address]bls.PublicKey 80 } 81 82 func newMockBlsPubkeyProvider(addrs []common.Address, blsKeys []bls.SecretKey) *mockBlsPubkeyProvider { 83 infos := make(map[common.Address]bls.PublicKey) 84 for i := 0; i < len(addrs); i++ { 85 infos[addrs[i]] = blsKeys[i].PublicKey() 86 } 87 return &mockBlsPubkeyProvider{infos} 88 } 89 90 func (m *mockBlsPubkeyProvider) GetBlsPubkey(chain consensus.ChainReader, proposer common.Address, num *big.Int) (bls.PublicKey, error) { 91 if pub, ok := m.infos[proposer]; ok { 92 return pub, nil 93 } else { 94 return nil, errNoBlsPub 95 } 96 } 97 98 func (m *mockBlsPubkeyProvider) ResetBlsCache() {} 99 100 type testContext struct { 101 config *params.ChainConfig 102 nodeKeys []*ecdsa.PrivateKey // Generated node keys 103 nodeAddrs []common.Address // Generated node addrs 104 nodeBlsKeys []bls.SecretKey // Generated node bls keys 105 106 chain *blockchain.BlockChain 107 engine *backend 108 sm *reward.StakingManager 109 } 110 111 func newTestContext(numNodes int, config *params.ChainConfig, overrides *testOverrides) *testContext { 112 if config == nil { 113 config = testBaseConfig 114 } 115 if overrides == nil { 116 overrides = &testOverrides{} 117 } 118 if overrides.node0Key == nil { 119 overrides.node0Key, _ = crypto.GenerateKey() 120 } 121 if overrides.node0BlsKey == nil { 122 overrides.node0BlsKey, _ = bls.DeriveFromECDSA(overrides.node0Key) 123 } 124 if overrides.blockPeriod == nil { 125 one := uint64(1) 126 overrides.blockPeriod = &one 127 } 128 if overrides.stakingAmounts == nil { 129 overrides.stakingAmounts = make([]uint64, numNodes) 130 } 131 132 // Create node keys 133 var ( 134 nodeKeys = make([]*ecdsa.PrivateKey, numNodes) 135 nodeAddrs = make([]common.Address, numNodes) 136 nodeBlsKeys = make([]bls.SecretKey, numNodes) 137 138 dbm = database.NewMemoryDBManager() 139 gov = governance.NewMixedEngine(config, dbm) 140 ) 141 nodeKeys[0] = overrides.node0Key 142 nodeAddrs[0] = crypto.PubkeyToAddress(nodeKeys[0].PublicKey) 143 nodeBlsKeys[0] = overrides.node0BlsKey 144 for i := 1; i < numNodes; i++ { 145 nodeKeys[i], _ = crypto.GenerateKey() 146 nodeAddrs[i] = crypto.PubkeyToAddress(nodeKeys[i].PublicKey) 147 nodeBlsKeys[i], _ = bls.DeriveFromECDSA(nodeKeys[i]) 148 } 149 150 // Create genesis block 151 if config.Governance.GovernanceMode == "single" { 152 config.Governance.GoverningNode = nodeAddrs[0] 153 } 154 genesis := blockchain.DefaultBaobabGenesisBlock() 155 genesis.Config = config 156 genesis.ExtraData = makeGenesisExtra(nodeAddrs) 157 genesis.Timestamp = uint64(time.Now().Unix()) 158 genesis.MustCommit(dbm) 159 160 // Create istanbul engine 161 istanbulConfig := &istanbul.Config{ 162 Timeout: 10000, 163 BlockPeriod: *overrides.blockPeriod, 164 ProposerPolicy: istanbul.ProposerPolicy(config.Istanbul.ProposerPolicy), 165 Epoch: config.Istanbul.Epoch, 166 SubGroupSize: config.Istanbul.SubGroupSize, 167 } 168 engine := New(&BackendOpts{ 169 IstanbulConfig: istanbulConfig, 170 Rewardbase: common.HexToAddress("0x2A35FE72F847aa0B509e4055883aE90c87558AaD"), 171 PrivateKey: nodeKeys[0], 172 BlsSecretKey: nodeBlsKeys[0], 173 DB: dbm, 174 Governance: gov, 175 BlsPubkeyProvider: newMockBlsPubkeyProvider(nodeAddrs, nodeBlsKeys), 176 NodeType: common.CONSENSUSNODE, 177 }).(*backend) 178 gov.SetNodeAddress(engine.Address()) 179 180 // Override StakingManager 181 sm := makeTestStakingManager(nodeAddrs, overrides.stakingAmounts) 182 183 // Create blockchain 184 cacheConfig := &blockchain.CacheConfig{ 185 ArchiveMode: false, 186 CacheSize: 512, 187 BlockInterval: blockchain.DefaultBlockInterval, 188 TriesInMemory: blockchain.DefaultTriesInMemory, 189 SnapshotCacheSize: 0, // Disable state snapshot 190 } 191 chain, err := blockchain.NewBlockChain(dbm, cacheConfig, config, engine, vm.Config{}) 192 if err != nil { 193 panic(err) 194 } 195 gov.SetBlockchain(chain) 196 197 // Start the engine 198 if err := engine.Start(chain, chain.CurrentBlock, chain.HasBadBlock); err != nil { 199 panic(err) 200 } 201 202 return &testContext{ 203 config: config, 204 nodeKeys: nodeKeys, 205 nodeAddrs: nodeAddrs, 206 207 chain: chain, 208 engine: engine, 209 sm: sm, 210 } 211 } 212 213 // Make empty header 214 func (ctx *testContext) MakeHeader(parent *types.Block) *types.Header { 215 header := &types.Header{ 216 ParentHash: parent.Hash(), 217 Number: parent.Number().Add(parent.Number(), common.Big1), 218 GasUsed: 0, 219 Extra: parent.Extra(), 220 Time: new(big.Int).Add(parent.Time(), new(big.Int).SetUint64(ctx.engine.config.BlockPeriod)), 221 BlockScore: defaultBlockScore, 222 } 223 if parent.Header().BaseFee != nil { 224 // Assume BaseFee does not change 225 header.BaseFee = parent.Header().BaseFee 226 } 227 return header 228 } 229 230 // Block with no signature. 231 func (ctx *testContext) MakeBlock(parent *types.Block) *types.Block { 232 chain, engine := ctx.chain, ctx.engine 233 header := ctx.MakeHeader(parent) 234 if err := engine.Prepare(chain, header); err != nil { 235 panic(err) 236 } 237 state, _ := chain.StateAt(parent.Root()) 238 block, _ := engine.Finalize(chain, header, state, nil, nil) 239 return block 240 } 241 242 // Block with proposer seal (no committed seals). 243 func (ctx *testContext) MakeBlockWithSeal(parent *types.Block) *types.Block { 244 chain, engine := ctx.chain, ctx.engine 245 block := ctx.MakeBlock(parent) 246 result, err := engine.Seal(chain, block, make(chan struct{})) 247 if err != nil { 248 panic(err) 249 } 250 return result 251 } 252 253 // Block with proposer seal and all committed seals. 254 func (ctx *testContext) MakeBlockWithCommittedSeals(parent *types.Block) *types.Block { 255 blockWithoutSeal := ctx.MakeBlock(parent) 256 257 // add proposer seal for the block 258 block, err := ctx.engine.updateBlock(blockWithoutSeal) 259 if err != nil { 260 panic(err) 261 } 262 263 // write validators committed seals to the block 264 header := block.Header() 265 committedSeals := ctx.MakeCommittedSeals(block.Hash()) 266 err = writeCommittedSeals(header, committedSeals) 267 if err != nil { 268 panic(err) 269 } 270 block = block.WithSeal(header) 271 272 return block 273 } 274 275 func (ctx *testContext) MakeCommittedSeals(hash common.Hash) [][]byte { 276 committedSeals := make([][]byte, len(ctx.nodeKeys)) 277 hashData := crypto.Keccak256(core.PrepareCommittedSeal(hash)) 278 for i, key := range ctx.nodeKeys { 279 sig, _ := crypto.Sign(hashData, key) 280 committedSeals[i] = make([]byte, types.IstanbulExtraSeal) 281 copy(committedSeals[i][:], sig) 282 } 283 return committedSeals 284 } 285 286 func (ctx *testContext) Cleanup() { 287 ctx.chain.Stop() 288 ctx.engine.Stop() 289 reward.SetTestStakingManager(ctx.sm) 290 } 291 292 func makeGenesisExtra(addrs []common.Address) []byte { 293 extra := &types.IstanbulExtra{ 294 Validators: addrs, 295 Seal: []byte{}, 296 CommittedSeal: [][]byte{}, 297 } 298 encoded, err := rlp.EncodeToBytes(&extra) 299 if err != nil { 300 panic(err) 301 } 302 303 vanity := make([]byte, types.IstanbulExtraVanity) 304 return append(vanity, encoded...) 305 } 306 307 // Set StakingInfo with given addresses and amounts, returns the original (old) StakingManager. 308 // Must call `reward.SetTestStakingManager(oldStakingManager)` after testing 309 // because StakingManager is a global singleton. 310 func makeTestStakingManager(addrs []common.Address, amounts []uint64) *reward.StakingManager { 311 info := &reward.StakingInfo{BlockNum: 0} 312 for i, addr := range addrs { 313 // Assign random reward address 314 rewardKey, _ := crypto.GenerateKey() 315 rewardAddr := crypto.PubkeyToAddress(rewardKey.PublicKey) 316 317 info.CouncilNodeAddrs = append(info.CouncilNodeAddrs, addr) 318 info.CouncilStakingAddrs = append(info.CouncilStakingAddrs, addr) 319 info.CouncilStakingAmounts = append(info.CouncilStakingAmounts, amounts[i]) 320 info.CouncilRewardAddrs = append(info.CouncilRewardAddrs, rewardAddr) 321 } 322 323 // Save old StakingManager, overwrite with the fake one. 324 oldStakingManager := reward.GetStakingManager() 325 reward.SetTestStakingManagerWithStakingInfoCache(info) 326 return oldStakingManager 327 } 328 329 func TestTestContext(t *testing.T) { 330 ctx := newTestContext(1, nil, nil) 331 defer ctx.Cleanup() 332 } 333 334 func TestMain(m *testing.M) { 335 // Because api/debug/flag.go sets the global logger Info level, 336 // and BlockChain test generates a lot of Info logs, override to Warn level here. 337 flag.Parse() // needed for testing.Verbose() 338 log.EnableLogForTest(log.LvlCrit, log.LvlWarn) 339 340 m.Run() 341 }