github.com/klaytn/klaytn@v1.10.2/tests/klay_test_blockchain_test.go (about) 1 // Copyright 2018 The klaytn Authors 2 // This file is part of the klaytn library. 3 // 4 // The klaytn library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The klaytn library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the klaytn library. If not, see <http://www.gnu.org/licenses/>. 16 17 package tests 18 19 import ( 20 "bytes" 21 "crypto/ecdsa" 22 "encoding/json" 23 "errors" 24 "fmt" 25 "math/big" 26 "os" 27 "time" 28 29 "github.com/klaytn/klaytn/blockchain" 30 "github.com/klaytn/klaytn/blockchain/types" 31 "github.com/klaytn/klaytn/blockchain/vm" 32 "github.com/klaytn/klaytn/common" 33 "github.com/klaytn/klaytn/common/profile" 34 "github.com/klaytn/klaytn/consensus" 35 "github.com/klaytn/klaytn/consensus/istanbul" 36 istanbulBackend "github.com/klaytn/klaytn/consensus/istanbul/backend" 37 istanbulCore "github.com/klaytn/klaytn/consensus/istanbul/core" 38 "github.com/klaytn/klaytn/consensus/misc" 39 "github.com/klaytn/klaytn/crypto" 40 "github.com/klaytn/klaytn/crypto/sha3" 41 "github.com/klaytn/klaytn/governance" 42 "github.com/klaytn/klaytn/params" 43 "github.com/klaytn/klaytn/reward" 44 "github.com/klaytn/klaytn/rlp" 45 "github.com/klaytn/klaytn/storage/database" 46 "github.com/klaytn/klaytn/work" 47 ) 48 49 const transactionsJournalFilename = "transactions.rlp" 50 51 // If you don't want to remove 'chaindata', set removeChaindataOnExit = false 52 const removeChaindataOnExit = true 53 54 var errEmptyPending = errors.New("pending is empty") 55 56 type BCData struct { 57 bc *blockchain.BlockChain 58 addrs []*common.Address 59 privKeys []*ecdsa.PrivateKey 60 db database.DBManager 61 rewardBase *common.Address 62 validatorAddresses []common.Address 63 validatorPrivKeys []*ecdsa.PrivateKey 64 engine consensus.Istanbul 65 genesis *blockchain.Genesis 66 governance governance.Engine 67 rewardDistributor *reward.RewardDistributor 68 } 69 70 var ( 71 dir = "chaindata" 72 nodeAddr = common.StringToAddress("nodeAddr") 73 ) 74 75 func NewBCData(maxAccounts, numValidators int) (*BCData, error) { 76 if numValidators > maxAccounts { 77 return nil, errors.New("maxAccounts should be bigger numValidators!!") 78 } 79 80 // Remove leveldb dir if exists 81 if _, err := os.Stat(dir); err == nil { 82 os.RemoveAll(dir) 83 } 84 85 // Remove transactions.rlp if exists 86 if _, err := os.Stat(transactionsJournalFilename); err == nil { 87 os.RemoveAll(transactionsJournalFilename) 88 } 89 90 //////////////////////////////////////////////////////////////////////////////// 91 // Create a database 92 chainDb := NewDatabase(dir, database.LevelDB) 93 94 //////////////////////////////////////////////////////////////////////////////// 95 // Create a governance 96 gov := generateGovernaceDataForTest() 97 gov.SetNodeAddress(nodeAddr) 98 //////////////////////////////////////////////////////////////////////////////// 99 // Create accounts as many as maxAccounts 100 addrs, privKeys, err := createAccounts(maxAccounts) 101 if err != nil { 102 return nil, err 103 } 104 105 //////////////////////////////////////////////////////////////////////////////// 106 // Set the genesis address 107 genesisAddr := *addrs[0] 108 109 //////////////////////////////////////////////////////////////////////////////// 110 // Use the first `numValidators` accounts as validators 111 validatorAddresses, validatorPrivKeys := getValidatorAddrsAndKeys(addrs, privKeys, numValidators) 112 113 //////////////////////////////////////////////////////////////////////////////// 114 // Setup istanbul consensus backend 115 engine := istanbulBackend.New(genesisAddr, istanbul.DefaultConfig, validatorPrivKeys[0], chainDb, gov, common.CONSENSUSNODE) 116 117 //////////////////////////////////////////////////////////////////////////////// 118 // Make a blockchain 119 bc, genesis, err := initBlockChain(chainDb, nil, addrs, validatorAddresses, nil, engine) 120 if err != nil { 121 return nil, err 122 } 123 124 engine.Start(bc, bc.CurrentBlock, bc.HasBadBlock) 125 126 governance.AddGovernanceCacheForTest(gov, 0, genesis.Config) 127 rewardDistributor := reward.NewRewardDistributor(gov) 128 129 return &BCData{ 130 bc, addrs, privKeys, chainDb, 131 &genesisAddr, validatorAddresses, 132 validatorPrivKeys, engine, genesis, gov, rewardDistributor, 133 }, nil 134 } 135 136 func (bcdata *BCData) Shutdown() { 137 bcdata.bc.Stop() 138 139 bcdata.db.Close() 140 // Remove leveldb dir which was created for this test. 141 if removeChaindataOnExit { 142 os.RemoveAll(dir) 143 os.RemoveAll(transactionsJournalFilename) 144 } 145 } 146 147 func (bcdata *BCData) prepareHeader() (*types.Header, error) { 148 tstart := time.Now() 149 parent := bcdata.bc.CurrentBlock() 150 151 tstamp := tstart.Unix() 152 if parent.Time().Cmp(new(big.Int).SetInt64(tstamp)) >= 0 { 153 tstamp = parent.Time().Int64() + 1 154 } 155 // this will ensure we're not going off too far in the future 156 if now := time.Now().Unix(); tstamp > now { 157 wait := time.Duration(tstamp-now) * time.Second 158 time.Sleep(wait) 159 } 160 161 num := new(big.Int).Add(parent.Number(), common.Big1) 162 header := &types.Header{ 163 ParentHash: parent.Hash(), 164 Number: num, 165 Time: big.NewInt(tstamp), 166 Governance: common.Hex2Bytes("b8dc7b22676f7665726e696e676e6f6465223a22307865373333636234643237396461363936663330643437306638633034646563623534666362306432222c22676f7665726e616e63656d6f6465223a2273696e676c65222c22726577617264223a7b226d696e74696e67616d6f756e74223a393630303030303030303030303030303030302c22726174696f223a2233342f33332f3333227d2c22626674223a7b2265706f6368223a33303030302c22706f6c696379223a302c22737562223a32317d2c22756e69745072696365223a32353030303030303030307d"), 167 Vote: common.Hex2Bytes("e194e733cb4d279da696f30d470f8c04decb54fcb0d28565706f6368853330303030"), 168 } 169 if bcdata.bc.Config().IsMagmaForkEnabled(num) { 170 header.BaseFee = misc.NextMagmaBlockBaseFee(parent.Header(), bcdata.bc.Config().Governance.KIP71) 171 } 172 173 if err := bcdata.engine.Prepare(bcdata.bc, header); err != nil { 174 return nil, errors.New(fmt.Sprintf("Failed to prepare header for mining %s.\n", err)) 175 } 176 177 return header, nil 178 } 179 180 func (bcdata *BCData) MineABlock(transactions types.Transactions, signer types.Signer, prof *profile.Profiler) (*types.Block, types.Receipts, error) { 181 // Set the block header 182 start := time.Now() 183 header, err := bcdata.prepareHeader() 184 if err != nil { 185 return nil, nil, err 186 } 187 prof.Profile("mine_prepareHeader", time.Now().Sub(start)) 188 189 statedb, err := bcdata.bc.State() 190 if err != nil { 191 return nil, nil, err 192 } 193 194 // Group transactions by the sender address 195 start = time.Now() 196 txs := make(map[common.Address]types.Transactions) 197 for _, tx := range transactions { 198 acc, err := types.Sender(signer, tx) 199 if err != nil { 200 return nil, nil, err 201 } 202 txs[acc] = append(txs[acc], tx) 203 } 204 prof.Profile("mine_groupTransactions", time.Now().Sub(start)) 205 206 // Create a transaction set where transactions are sorted by price and nonce 207 start = time.Now() 208 txset := types.NewTransactionsByTimeAndNonce(signer, txs) 209 prof.Profile("mine_NewTransactionsByPriceAndNonce", time.Now().Sub(start)) 210 211 // Apply the set of transactions 212 start = time.Now() 213 task := work.NewTask(bcdata.bc.Config(), signer, statedb, header) 214 task.ApplyTransactions(txset, bcdata.bc, *bcdata.rewardBase) 215 newtxs := task.Transactions() 216 receipts := task.Receipts() 217 prof.Profile("mine_ApplyTransactions", time.Now().Sub(start)) 218 219 // Finalize the block 220 start = time.Now() 221 b, err := bcdata.engine.Finalize(bcdata.bc, header, statedb, newtxs, receipts) 222 if err != nil { 223 return nil, nil, err 224 } 225 prof.Profile("mine_finalize_block", time.Now().Sub(start)) 226 227 //////////////////////////////////////////////////////////////////////////////// 228 229 start = time.Now() 230 b, err = sealBlock(b, bcdata.validatorPrivKeys) 231 if err != nil { 232 return nil, nil, err 233 } 234 prof.Profile("mine_seal_block", time.Now().Sub(start)) 235 236 return b, receipts, nil 237 } 238 239 func (bcdata *BCData) GenABlock(accountMap *AccountMap, opt *testOption, 240 numTransactions int, prof *profile.Profiler, 241 ) error { 242 // Make a set of transactions 243 start := time.Now() 244 signer := types.MakeSigner(bcdata.bc.Config(), bcdata.bc.CurrentHeader().Number) 245 transactions, err := opt.makeTransactions(bcdata, accountMap, signer, numTransactions, nil, opt.txdata) 246 if err != nil { 247 return err 248 } 249 prof.Profile("main_makeTransactions", time.Now().Sub(start)) 250 251 return bcdata.GenABlockWithTransactions(accountMap, transactions, prof) 252 } 253 254 func (bcdata *BCData) GenABlockWithTxpool(accountMap *AccountMap, txpool *blockchain.TxPool, 255 prof *profile.Profiler, 256 ) error { 257 signer := types.MakeSigner(bcdata.bc.Config(), bcdata.bc.CurrentHeader().Number) 258 259 pending, err := txpool.Pending() 260 if err != nil { 261 return err 262 } 263 if len(pending) == 0 { 264 return errEmptyPending 265 } 266 pooltxs := types.NewTransactionsByTimeAndNonce(signer, pending) 267 268 // Set the block header 269 start := time.Now() 270 header, err := bcdata.prepareHeader() 271 if err != nil { 272 return err 273 } 274 prof.Profile("mine_prepareHeader", time.Now().Sub(start)) 275 276 statedb, err := bcdata.bc.State() 277 if err != nil { 278 return err 279 } 280 281 start = time.Now() 282 task := work.NewTask(bcdata.bc.Config(), signer, statedb, header) 283 task.ApplyTransactions(pooltxs, bcdata.bc, *bcdata.rewardBase) 284 newtxs := task.Transactions() 285 receipts := task.Receipts() 286 prof.Profile("mine_ApplyTransactions", time.Now().Sub(start)) 287 288 // Finalize the block 289 start = time.Now() 290 b, err := bcdata.engine.Finalize(bcdata.bc, header, statedb, newtxs, receipts) 291 if err != nil { 292 return err 293 } 294 prof.Profile("mine_finalize_block", time.Now().Sub(start)) 295 296 start = time.Now() 297 b, err = sealBlock(b, bcdata.validatorPrivKeys) 298 if err != nil { 299 return err 300 } 301 prof.Profile("mine_seal_block", time.Now().Sub(start)) 302 303 // Update accountMap 304 start = time.Now() 305 if err := accountMap.Update(newtxs, signer, statedb, b.NumberU64()); err != nil { 306 return err 307 } 308 prof.Profile("main_update_accountMap", time.Now().Sub(start)) 309 310 // Insert the block into the blockchain 311 start = time.Now() 312 if n, err := bcdata.bc.InsertChain(types.Blocks{b}); err != nil { 313 return fmt.Errorf("err = %s, n = %d\n", err, n) 314 } 315 prof.Profile("main_insert_blockchain", time.Now().Sub(start)) 316 317 // Apply reward 318 config := bcdata.bc.Config() 319 rules := config.Rules(bcdata.bc.CurrentHeader().Number) 320 pset, err := params.NewGovParamSetChainConfig(config) 321 if err != nil { 322 return err 323 } 324 start = time.Now() 325 spec, err := reward.CalcDeferredRewardSimple(header, rules, pset) 326 if err != nil { 327 return err 328 } 329 reward.DistributeBlockReward(accountMap, spec.Rewards) 330 prof.Profile("main_apply_reward", time.Now().Sub(start)) 331 332 // Verification with accountMap 333 start = time.Now() 334 statedbNew, err := bcdata.bc.State() 335 if err != nil { 336 return err 337 } 338 if err := accountMap.Verify(statedbNew); err != nil { 339 return err 340 } 341 prof.Profile("main_verification", time.Now().Sub(start)) 342 343 return nil 344 } 345 346 func (bcdata *BCData) GenABlockWithTransactions(accountMap *AccountMap, transactions types.Transactions, 347 prof *profile.Profiler, 348 ) error { 349 signer := types.MakeSigner(bcdata.bc.Config(), bcdata.bc.CurrentHeader().Number) 350 351 statedb, err := bcdata.bc.State() 352 if err != nil { 353 return err 354 } 355 356 // Update accountMap 357 start := time.Now() 358 if err := accountMap.Update(transactions, signer, statedb, bcdata.bc.CurrentBlock().NumberU64()); err != nil { 359 return err 360 } 361 prof.Profile("main_update_accountMap", time.Now().Sub(start)) 362 363 // Mine a block! 364 start = time.Now() 365 b, _, err := bcdata.MineABlock(transactions, signer, prof) 366 if err != nil { 367 return err 368 } 369 prof.Profile("main_mineABlock", time.Now().Sub(start)) 370 371 txs := make(types.Transactions, len(b.Transactions())) 372 for i, tt := range b.Transactions() { 373 encodedTx, err := rlp.EncodeToBytes(tt) 374 if err != nil { 375 return err 376 } 377 decodedTx := types.Transaction{} 378 rlp.DecodeBytes(encodedTx, &decodedTx) 379 txs[i] = &decodedTx 380 } 381 b = b.WithBody(txs) 382 383 // Insert the block into the blockchain 384 start = time.Now() 385 if n, err := bcdata.bc.InsertChain(types.Blocks{b}); err != nil { 386 return fmt.Errorf("err = %s, n = %d\n", err, n) 387 } 388 prof.Profile("main_insert_blockchain", time.Now().Sub(start)) 389 390 // Apply reward 391 config := bcdata.bc.Config() 392 rules := config.Rules(bcdata.bc.CurrentHeader().Number) 393 pset, err := params.NewGovParamSetChainConfig(config) 394 if err != nil { 395 return err 396 } 397 start = time.Now() 398 spec, err := reward.CalcDeferredRewardSimple(bcdata.bc.CurrentHeader(), rules, pset) 399 if err != nil { 400 return err 401 } 402 reward.DistributeBlockReward(accountMap, spec.Rewards) 403 prof.Profile("main_apply_reward", time.Now().Sub(start)) 404 405 // Verification with accountMap 406 start = time.Now() 407 statedb, err = bcdata.bc.State() 408 if err != nil { 409 return err 410 } 411 if err := accountMap.Verify(statedb); err != nil { 412 return err 413 } 414 prof.Profile("main_verification", time.Now().Sub(start)) 415 416 return nil 417 } 418 419 // ////////////////////////////////////////////////////////////////////////////// 420 func NewDatabase(dir string, dbType database.DBType) database.DBManager { 421 if dir == "" { 422 return database.NewMemoryDBManager() 423 } else { 424 dbc := &database.DBConfig{ 425 Dir: dir, DBType: dbType, LevelDBCacheSize: 768, 426 OpenFilesLimit: 1024, SingleDB: false, NumStateTrieShards: 4, ParallelDBWrite: true, 427 LevelDBCompression: database.AllNoCompression, LevelDBBufferPool: true, 428 } 429 return database.NewDBManager(dbc) 430 } 431 } 432 433 // Copied from consensus/istanbul/backend/engine.go 434 func prepareIstanbulExtra(validators []common.Address) ([]byte, error) { 435 var buf bytes.Buffer 436 437 buf.Write(bytes.Repeat([]byte{0x0}, types.IstanbulExtraVanity)) 438 439 ist := &types.IstanbulExtra{ 440 Validators: validators, 441 Seal: []byte{}, 442 CommittedSeal: [][]byte{}, 443 } 444 445 payload, err := rlp.EncodeToBytes(&ist) 446 if err != nil { 447 return nil, err 448 } 449 return append(buf.Bytes(), payload...), nil 450 } 451 452 func initBlockChain(db database.DBManager, cacheConfig *blockchain.CacheConfig, coinbaseAddrs []*common.Address, validators []common.Address, 453 genesis *blockchain.Genesis, engine consensus.Engine, 454 ) (*blockchain.BlockChain, *blockchain.Genesis, error) { 455 extraData, err := prepareIstanbulExtra(validators) 456 457 if genesis == nil { 458 genesis = blockchain.DefaultGenesisBlock() 459 genesis.Config = Forks["Byzantium"] 460 genesis.ExtraData = extraData 461 genesis.BlockScore = big.NewInt(1) 462 genesis.Config.Governance = params.GetDefaultGovernanceConfig() 463 genesis.Config.Istanbul = params.GetDefaultIstanbulConfig() 464 genesis.Config.UnitPrice = 25 * params.Ston 465 } 466 467 alloc := make(blockchain.GenesisAlloc) 468 for _, a := range coinbaseAddrs { 469 alloc[*a] = blockchain.GenesisAccount{Balance: new(big.Int).Mul(big.NewInt(1e16), big.NewInt(params.KLAY))} 470 } 471 472 genesis.Alloc = alloc 473 474 chainConfig, _, err := blockchain.SetupGenesisBlock(db, genesis, params.UnusedNetworkId, false, false) 475 if _, ok := err.(*params.ConfigCompatError); err != nil && !ok { 476 return nil, nil, err 477 } 478 479 // The chainConfig value has been modified while executing test. (ex, The test included executing applyTransaction()) 480 // Therefore, a deep copy is required to prevent the chainConfing value from being modified. 481 var cfg params.ChainConfig 482 b, err := json.Marshal(chainConfig) 483 if err != nil { 484 return nil, nil, err 485 } 486 487 err = json.Unmarshal(b, &cfg) 488 if err != nil { 489 return nil, nil, err 490 } 491 492 genesis.Config = &cfg 493 494 chain, err := blockchain.NewBlockChain(db, cacheConfig, genesis.Config, engine, vm.Config{}) 495 if err != nil { 496 return nil, nil, err 497 } 498 499 return chain, genesis, nil 500 } 501 502 func createAccounts(numAccounts int) ([]*common.Address, []*ecdsa.PrivateKey, error) { 503 accs := make([]*common.Address, numAccounts) 504 privKeys := make([]*ecdsa.PrivateKey, numAccounts) 505 506 for i := 0; i < numAccounts; i++ { 507 k, err := crypto.GenerateKey() 508 if err != nil { 509 return nil, nil, err 510 } 511 keyAddr := crypto.PubkeyToAddress(k.PublicKey) 512 513 accs[i] = &keyAddr 514 privKeys[i] = k 515 } 516 517 return accs, privKeys, nil 518 } 519 520 // Copied from consensus/istanbul/backend/engine.go 521 func sigHash(header *types.Header) (hash common.Hash) { 522 hasher := sha3.NewKeccak256() 523 524 // Clean seal is required for calculating proposer seal. 525 rlp.Encode(hasher, types.IstanbulFilteredHeader(header, false)) 526 hasher.Sum(hash[:0]) 527 return hash 528 } 529 530 // writeSeal writes the extra-data field of the given header with the given seals. 531 // Copied from consensus/istanbul/backend/engine.go 532 func writeSeal(h *types.Header, seal []byte) error { 533 if len(seal)%types.IstanbulExtraSeal != 0 { 534 return errors.New("invalid signature") 535 } 536 537 istanbulExtra, err := types.ExtractIstanbulExtra(h) 538 if err != nil { 539 return err 540 } 541 542 istanbulExtra.Seal = seal 543 payload, err := rlp.EncodeToBytes(&istanbulExtra) 544 if err != nil { 545 return err 546 } 547 548 h.Extra = append(h.Extra[:types.IstanbulExtraVanity], payload...) 549 return nil 550 } 551 552 // writeCommittedSeals writes the extra-data field of a block header with given committed seals. 553 // Copied from consensus/istanbul/backend/engine.go 554 func writeCommittedSeals(h *types.Header, committedSeals [][]byte) error { 555 errInvalidCommittedSeals := errors.New("invalid committed seals") 556 557 if len(committedSeals) == 0 { 558 return errInvalidCommittedSeals 559 } 560 561 for _, seal := range committedSeals { 562 if len(seal) != types.IstanbulExtraSeal { 563 return errInvalidCommittedSeals 564 } 565 } 566 567 istanbulExtra, err := types.ExtractIstanbulExtra(h) 568 if err != nil { 569 return err 570 } 571 572 istanbulExtra.CommittedSeal = make([][]byte, len(committedSeals)) 573 copy(istanbulExtra.CommittedSeal, committedSeals) 574 575 payload, err := rlp.EncodeToBytes(&istanbulExtra) 576 if err != nil { 577 return err 578 } 579 580 h.Extra = append(h.Extra[:types.IstanbulExtraVanity], payload...) 581 return nil 582 } 583 584 // sign implements istanbul.backend.Sign 585 // Copied from consensus/istanbul/backend/backend.go 586 func sign(data []byte, privkey *ecdsa.PrivateKey) ([]byte, error) { 587 hashData := crypto.Keccak256([]byte(data)) 588 return crypto.Sign(hashData, privkey) 589 } 590 591 func makeCommittedSeal(h *types.Header, privKeys []*ecdsa.PrivateKey) ([][]byte, error) { 592 committedSeals := make([][]byte, 0, 3) 593 594 for i := 1; i < 4; i++ { 595 seal := istanbulCore.PrepareCommittedSeal(h.Hash()) 596 committedSeal, err := sign(seal, privKeys[i]) 597 if err != nil { 598 return nil, err 599 } 600 committedSeals = append(committedSeals, committedSeal) 601 } 602 603 return committedSeals, nil 604 } 605 606 func sealBlock(b *types.Block, privKeys []*ecdsa.PrivateKey) (*types.Block, error) { 607 header := b.Header() 608 609 seal, err := sign(sigHash(header).Bytes(), privKeys[0]) 610 if err != nil { 611 return nil, err 612 } 613 614 err = writeSeal(header, seal) 615 if err != nil { 616 return nil, err 617 } 618 619 committedSeals, err := makeCommittedSeal(header, privKeys) 620 if err != nil { 621 return nil, err 622 } 623 624 err = writeCommittedSeals(header, committedSeals) 625 if err != nil { 626 return nil, err 627 } 628 629 return b.WithSeal(header), nil 630 }