github.com/klaytn/klaytn@v1.12.1/tests/pregenerated_data_util_test.go (about) 1 // Copyright 2019 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 "bufio" 21 "crypto/ecdsa" 22 "encoding/hex" 23 "errors" 24 "fmt" 25 "math" 26 "math/big" 27 "os" 28 "path" 29 "strconv" 30 "sync" 31 "time" 32 33 "github.com/klaytn/klaytn/blockchain" 34 "github.com/klaytn/klaytn/blockchain/types" 35 "github.com/klaytn/klaytn/blockchain/vm" 36 "github.com/klaytn/klaytn/common" 37 "github.com/klaytn/klaytn/consensus/istanbul" 38 istanbulBackend "github.com/klaytn/klaytn/consensus/istanbul/backend" 39 "github.com/klaytn/klaytn/crypto" 40 "github.com/klaytn/klaytn/governance" 41 "github.com/klaytn/klaytn/log" 42 "github.com/klaytn/klaytn/params" 43 "github.com/klaytn/klaytn/reward" 44 "github.com/klaytn/klaytn/storage/database" 45 "github.com/klaytn/klaytn/storage/statedb" 46 "github.com/klaytn/klaytn/work" 47 "github.com/syndtr/goleveldb/leveldb/opt" 48 ) 49 50 const ( 51 numValidatorsForTest = 4 52 53 addressDirectory = "addrs" 54 privateKeyDirectory = "privatekeys" 55 56 addressFilePrefix = "addrs_" 57 privateKeyFilePrefix = "privateKeys_" 58 59 chainDataDir = "chaindata" 60 ) 61 62 var totalTxs = 0 63 64 // writeToFile writes addresses and private keys to designated directories with given fileNum. 65 // Addresses are stored in a file like `addrs_0` and private keys are stored in a file like `privateKeys_0`. 66 func writeToFile(addrs []*common.Address, privKeys []*ecdsa.PrivateKey, fileNum int, dir string) { 67 _ = os.Mkdir(path.Join(dir, addressDirectory), os.ModePerm) 68 _ = os.Mkdir(path.Join(dir, privateKeyDirectory), os.ModePerm) 69 70 addrsFile, err := os.Create(path.Join(dir, addressDirectory, addressFilePrefix+strconv.Itoa(fileNum))) 71 if err != nil { 72 panic(err) 73 } 74 75 privateKeysFile, err := os.Create(path.Join(dir, privateKeyDirectory, privateKeyFilePrefix+strconv.Itoa(fileNum))) 76 if err != nil { 77 panic(err) 78 } 79 80 wg := sync.WaitGroup{} 81 82 wg.Add(2) 83 84 syncSize := len(addrs) / 2 85 86 go func() { 87 for i, b := range addrs { 88 addrsFile.WriteString(b.String() + "\n") 89 if (i+1)%syncSize == 0 { 90 addrsFile.Sync() 91 } 92 } 93 94 addrsFile.Close() 95 wg.Done() 96 }() 97 98 go func() { 99 for i, key := range privKeys { 100 privateKeysFile.WriteString(hex.EncodeToString(crypto.FromECDSA(key)) + "\n") 101 if (i+1)%syncSize == 0 { 102 privateKeysFile.Sync() 103 } 104 } 105 106 privateKeysFile.Close() 107 wg.Done() 108 }() 109 110 wg.Wait() 111 } 112 113 func readAddrsFromFile(dir string, num int) ([]*common.Address, error) { 114 var addrs []*common.Address 115 116 addrsFile, err := os.Open(path.Join(dir, addressDirectory, addressFilePrefix+strconv.Itoa(num))) 117 if err != nil { 118 return nil, err 119 } 120 121 defer addrsFile.Close() 122 123 scanner := bufio.NewScanner(addrsFile) 124 for scanner.Scan() { 125 keyStr := scanner.Text() 126 addr := common.HexToAddress(keyStr) 127 addrs = append(addrs, &addr) 128 } 129 130 return addrs, nil 131 } 132 133 func readPrivateKeysFromFile(dir string, num int) ([]*ecdsa.PrivateKey, error) { 134 var privKeys []*ecdsa.PrivateKey 135 privateKeysFile, err := os.Open(path.Join(dir, privateKeyDirectory, privateKeyFilePrefix+strconv.Itoa(num))) 136 if err != nil { 137 return nil, err 138 } 139 140 defer privateKeysFile.Close() 141 142 scanner := bufio.NewScanner(privateKeysFile) 143 for scanner.Scan() { 144 keyStr := scanner.Text() 145 146 key, err := hex.DecodeString(keyStr) 147 if err != nil { 148 return nil, fmt.Errorf("%v", err) 149 } 150 151 if pk, err := crypto.ToECDSA(key); err != nil { 152 return nil, fmt.Errorf("%v", err) 153 } else { 154 privKeys = append(privKeys, pk) 155 } 156 } 157 158 return privKeys, nil 159 } 160 161 func readAddrsAndPrivateKeysFromFile(dir string, num int) ([]*common.Address, []*ecdsa.PrivateKey, error) { 162 addrs, err := readAddrsFromFile(dir, num) 163 if err != nil { 164 return nil, nil, err 165 } 166 167 privateKeys, err := readPrivateKeysFromFile(dir, num) 168 if err != nil { 169 return nil, nil, err 170 } 171 172 return addrs, privateKeys, nil 173 } 174 175 // getAddrsAndKeysFromFile extracts the address stored in file by numAccounts. 176 func getAddrsAndKeysFromFile(numAccounts int, testDataDir string, run int, filePicker func(int, int) int) ([]*common.Address, []*ecdsa.PrivateKey, error) { 177 addrs := make([]*common.Address, 0, numAccounts) 178 privKeys := make([]*ecdsa.PrivateKey, 0, numAccounts) 179 180 files, err := os.ReadDir(path.Join(testDataDir, addressDirectory)) 181 if err != nil { 182 return nil, nil, err 183 } 184 185 numFiles := len(files) 186 remain := numAccounts 187 for i := run; remain > 0; i++ { 188 fileIndex := filePicker(i, numFiles) 189 190 // Read recipient addresses from file. 191 addrsPerFile, privKeysPerFile, err := readAddrsAndPrivateKeysFromFile(testDataDir, fileIndex) 192 if err != nil { 193 return nil, nil, err 194 } 195 196 fmt.Println("Read addresses from " + addressFilePrefix + strconv.Itoa(fileIndex) + ", len(addrs)=" + strconv.Itoa(len(addrsPerFile))) 197 198 partSize := int(math.Min(float64(len(addrsPerFile)), float64(remain))) 199 addrs = append(addrs, addrsPerFile[:partSize]...) 200 privKeys = append(privKeys, privKeysPerFile[:partSize]...) 201 remain -= partSize 202 } 203 204 return addrs, privKeys, nil 205 } 206 207 func makeOrGenerateAddrsAndKeys(testDataDir string, run int, tc *preGeneratedTC) ([]*common.Address, []*ecdsa.PrivateKey, error) { 208 // If it is execution test, makeOrGenerateAddrsAndKeys is called to build validator list. 209 if !tc.isGenerateTest { 210 return getAddrsAndKeysFromFile(numValidatorsForTest, testDataDir, run, tc.filePicker) 211 } 212 213 wd, err := os.Getwd() 214 if err != nil { 215 return nil, nil, err 216 } 217 218 // If addrs directory exists in the current working directory for reuse, 219 // use it instead of generating addresses. 220 addrPathInWD := path.Join(wd, addressDirectory) 221 if _, err := os.Stat(addrPathInWD); err == nil { 222 return getAddrsAndKeysFromFile(tc.numReceiversPerRun, wd, run, tc.filePicker) 223 } 224 225 // No addrs directory exists. Generating and saving addresses and keys. 226 var newPrivateKeys []*ecdsa.PrivateKey 227 toAddrs, newPrivateKeys, err := createAccounts(tc.numTxsPerGen) 228 if err != nil { 229 return nil, nil, err 230 } 231 232 writeToFile(toAddrs, newPrivateKeys, run, testDataDir) 233 return toAddrs, newPrivateKeys, nil 234 } 235 236 // getValidatorAddrsAndKeys returns the first `numValidators` addresses and private keys 237 // for validators. 238 func getValidatorAddrsAndKeys(addrs []*common.Address, privateKeys []*ecdsa.PrivateKey, numValidators int) ([]common.Address, []*ecdsa.PrivateKey) { 239 validatorAddresses := make([]common.Address, numValidators) 240 validatorPrivateKeys := make([]*ecdsa.PrivateKey, numValidators) 241 242 for i := 0; i < numValidators; i++ { 243 validatorPrivateKeys[i] = privateKeys[i] 244 validatorAddresses[i] = *addrs[i] 245 } 246 247 return validatorAddresses, validatorPrivateKeys 248 } 249 250 // GenABlockWithTxPoolWithoutAccountMap basically does the same thing with GenABlockWithTxPool, 251 // however, it does not accept AccountMap which validates the outcome with stateDB. 252 // This is to remove the overhead of AccountMap management. 253 func (bcdata *BCData) GenABlockWithTxPoolWithoutAccountMap(txPool *blockchain.TxPool) error { 254 signer := types.MakeSigner(bcdata.bc.Config(), bcdata.bc.CurrentHeader().Number) 255 256 pending, err := txPool.Pending() 257 if err != nil { 258 return err 259 } 260 if len(pending) == 0 { 261 return errEmptyPending 262 } 263 264 pooltxs := types.NewTransactionsByTimeAndNonce(signer, pending) 265 266 // Set the block header 267 header, err := bcdata.prepareHeader() 268 if err != nil { 269 return err 270 } 271 272 stateDB, err := bcdata.bc.StateAt(bcdata.bc.CurrentBlock().Root()) 273 if err != nil { 274 return err 275 } 276 277 task := work.NewTask(bcdata.bc.Config(), signer, stateDB, header) 278 task.ApplyTransactions(pooltxs, bcdata.bc, *bcdata.rewardBase) 279 newtxs := task.Transactions() 280 receipts := task.Receipts() 281 282 if len(newtxs) == 0 { 283 return errEmptyPending 284 } 285 286 // Finalize the block. 287 b, err := bcdata.engine.Finalize(bcdata.bc, header, stateDB, newtxs, receipts) 288 if err != nil { 289 return err 290 } 291 292 // Seal the block. 293 b, err = sealBlock(b, bcdata.validatorPrivKeys) 294 if err != nil { 295 return err 296 } 297 298 // Write the block with state. 299 result, err := bcdata.bc.WriteBlockWithState(b, receipts, stateDB) 300 if err != nil { 301 return fmt.Errorf("err = %s", err) 302 } 303 304 if result.Status == blockchain.SideStatTy { 305 return fmt.Errorf("forked block is generated! number: %v, hash: %v, txs: %v", b.Number(), b.Hash(), len(b.Transactions())) 306 } 307 308 // Trigger post chain events after successful writing. 309 logs := stateDB.Logs() 310 events := []interface{}{blockchain.ChainEvent{Block: b, Hash: b.Hash(), Logs: logs}} 311 events = append(events, blockchain.ChainHeadEvent{Block: b}) 312 bcdata.bc.PostChainEvents(events, logs) 313 314 totalTxs += len(newtxs) 315 fmt.Println("blockNum", b.NumberU64(), "numTxs", len(newtxs), "totalTxs", totalTxs) 316 317 return nil 318 } 319 320 // NewBCDataForPreGeneratedTest returns a new BCData pointer constructed either 1) from the scratch or 2) from the existing data. 321 func NewBCDataForPreGeneratedTest(testDataDir string, tc *preGeneratedTC) (*BCData, error) { 322 totalTxs = 0 323 324 if numValidatorsForTest > tc.numTotalSenders { 325 return nil, errors.New("numTotalSenders should be bigger numValidatorsForTest") 326 } 327 328 // Remove test data directory if 1) exists and and 2) generating test. 329 if _, err := os.Stat(testDataDir); err == nil && tc.isGenerateTest { 330 os.RemoveAll(testDataDir) 331 } 332 333 // Remove transactions.rlp if exists 334 if _, err := os.Stat(transactionsJournalFilename); err == nil { 335 os.RemoveAll(transactionsJournalFilename) 336 } 337 338 //////////////////////////////////////////////////////////////////////////////// 339 // Create a database 340 tc.dbc.Dir = path.Join(testDataDir, chainDataDir) 341 fmt.Println("DBDir", tc.dbc.Dir) 342 343 var chainDB database.DBManager 344 var err error 345 if tc.dbc.DBType == database.LevelDB { 346 chainDB, err = database.NewLevelDBManagerForTest(tc.dbc, tc.levelDBOption) 347 } else if tc.dbc.DBType == database.BadgerDB { 348 chainDB = database.NewDBManager(tc.dbc) 349 } 350 351 if err != nil { 352 return nil, err 353 } 354 355 //////////////////////////////////////////////////////////////////////////////// 356 // Create a governance 357 gov := generateGovernaceDataForTest() 358 359 //////////////////////////////////////////////////////////////////////////////// 360 // Prepare sender addresses and private keys 361 // 1) If generating test, create accounts and private keys as many as numTotalSenders 362 // 2) If executing test, load accounts and private keys from file as many as numTotalSenders 363 addrs, privKeys, err := makeOrGenerateAddrsAndKeys(testDataDir, 0, tc) 364 if err != nil { 365 return nil, err 366 } 367 368 //////////////////////////////////////////////////////////////////////////////// 369 // Set the genesis address 370 genesisAddr := *addrs[0] 371 372 //////////////////////////////////////////////////////////////////////////////// 373 // Use the first `numValidatorsForTest` accounts as validators 374 validatorAddresses, validatorPrivKeys := getValidatorAddrsAndKeys(addrs, privKeys, numValidatorsForTest) 375 376 //////////////////////////////////////////////////////////////////////////////// 377 // Setup istanbul consensus backend 378 engine := istanbulBackend.New(&istanbulBackend.BackendOpts{ 379 IstanbulConfig: istanbul.DefaultConfig, 380 Rewardbase: genesisAddr, 381 PrivateKey: validatorPrivKeys[0], 382 DB: chainDB, 383 Governance: gov, 384 NodeType: common.CONSENSUSNODE, 385 }) 386 387 //////////////////////////////////////////////////////////////////////////////// 388 // Make a blockChain 389 // 1) If generating test, call initBlockChain 390 // 2) If executing test, call blockchain.NewBlockChain 391 var bc *blockchain.BlockChain 392 var genesis *blockchain.Genesis 393 if tc.isGenerateTest { 394 bc, genesis, err = initBlockChain(chainDB, tc.cacheConfig, addrs, validatorAddresses, nil, engine) 395 } else { 396 chainConfig, err := getChainConfig(chainDB) 397 if err != nil { 398 return nil, err 399 } 400 genesis = blockchain.DefaultGenesisBlock() 401 genesis.Config = chainConfig 402 bc, err = blockchain.NewBlockChain(chainDB, tc.cacheConfig, chainConfig, engine, vm.Config{}) 403 } 404 405 if err != nil { 406 return nil, err 407 } 408 409 rewardDistributor := reward.NewRewardDistributor(gov) 410 411 return &BCData{ 412 bc, addrs, privKeys, chainDB, 413 &genesisAddr, validatorAddresses, 414 validatorPrivKeys, engine, genesis, gov, rewardDistributor, 415 }, nil 416 } 417 418 // genAspenOptions returns database configurations of Aspen network. 419 func genAspenOptions() (*database.DBConfig, *opt.Options) { 420 aspenDBConfig := defaultDBConfig() 421 aspenDBConfig.DBType = database.LevelDB 422 aspenDBConfig.LevelDBCompression = database.AllSnappyCompression 423 424 aspenLevelDBOptions := &opt.Options{WriteBuffer: 256 * opt.MiB} 425 426 return aspenDBConfig, aspenLevelDBOptions 427 } 428 429 // genBaobabOptions returns database configurations of Baobab network. 430 func genBaobabOptions() (*database.DBConfig, *opt.Options) { 431 dbc, opts := genAspenOptions() 432 433 opts.CompactionTableSize = 4 * opt.MiB 434 opts.CompactionTableSizeMultiplier = 2 435 436 dbc.LevelDBCompression = database.AllSnappyCompression 437 438 return dbc, opts 439 } 440 441 // genCandidateLevelDBOptions returns candidate database configurations of main-net, using LevelDB. 442 func genCandidateLevelDBOptions() (*database.DBConfig, *opt.Options) { 443 dbc, opts := genAspenOptions() 444 445 dbc.LevelDBBufferPool = true 446 dbc.LevelDBCompression = database.AllNoCompression 447 448 return dbc, opts 449 } 450 451 // genCandidateLevelDBOptions returns candidate database configurations of main-net, using BadgerDB. 452 func genCandidateBadgerDBOptions() (*database.DBConfig, *opt.Options) { 453 dbc := defaultDBConfig() 454 dbc.DBType = database.BadgerDB 455 456 return dbc, &opt.Options{} 457 } 458 459 // defaultDBConfig returns default database.DBConfig for pre-generated tests. 460 func defaultDBConfig() *database.DBConfig { 461 return &database.DBConfig{SingleDB: false, ParallelDBWrite: true, NumStateTrieShards: 4} 462 } 463 464 // getChainConfig returns chain config from chainDB. 465 func getChainConfig(chainDB database.DBManager) (*params.ChainConfig, error) { 466 stored := chainDB.ReadBlockByNumber(0) 467 if stored == nil { 468 return nil, errors.New("chainDB.ReadBlockByNumber(0) == nil") 469 } 470 471 chainConfig := chainDB.ReadChainConfig(stored.Hash()) 472 if chainConfig == nil { 473 return nil, errors.New("chainConfig == nil") 474 } 475 476 return chainConfig, nil 477 } 478 479 // defaultCacheConfig returns cache config for data generation tests. 480 func defaultCacheConfig() *blockchain.CacheConfig { 481 return &blockchain.CacheConfig{ 482 ArchiveMode: false, 483 CacheSize: 512, 484 BlockInterval: blockchain.DefaultBlockInterval, 485 TriesInMemory: blockchain.DefaultTriesInMemory, 486 TrieNodeCacheConfig: &statedb.TrieNodeCacheConfig{ 487 CacheType: statedb.CacheTypeLocal, 488 LocalCacheSizeMiB: 4096, 489 FastCacheFileDir: "", 490 RedisEndpoints: nil, 491 RedisClusterEnable: false, 492 }, 493 SnapshotCacheSize: 512, 494 } 495 } 496 497 // generateGovernaceDataForTest returns governance.Engine for test. 498 func generateGovernaceDataForTest() governance.Engine { 499 dbm := database.NewDBManager(&database.DBConfig{DBType: database.MemoryDB}) 500 501 return governance.NewMixedEngine(¶ms.ChainConfig{ 502 ChainID: big.NewInt(2018), 503 UnitPrice: 25000000000, 504 DeriveShaImpl: 0, 505 Istanbul: ¶ms.IstanbulConfig{ 506 Epoch: istanbul.DefaultConfig.Epoch, 507 ProposerPolicy: uint64(istanbul.DefaultConfig.ProposerPolicy), 508 SubGroupSize: istanbul.DefaultConfig.SubGroupSize, 509 }, 510 Governance: params.GetDefaultGovernanceConfig(), 511 }, dbm) 512 } 513 514 // setUpTest sets up test data directory, verbosity and profile file. 515 func setUpTest(tc *preGeneratedTC) (string, *os.File, error) { 516 log.EnableLogForTest(log.LvlCrit, log.LvlTrace) 517 518 testDataDir, err := setupTestDir(tc.originalDataDir, tc.isGenerateTest) 519 if err != nil { 520 return "", nil, fmt.Errorf("err: %v, dir: %v", err, testDataDir) 521 } 522 523 timeNow := time.Now() 524 f, err := os.Create(tc.testName + "_" + timeNow.Format("2006-01-02-1504") + ".cpu.out") 525 if err != nil { 526 return "", nil, fmt.Errorf("failed to create file for cpu profiling, err: %v", err) 527 } 528 529 return testDataDir, f, nil 530 }