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