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(&params.ChainConfig{
   496  		ChainID:       big.NewInt(2018),
   497  		UnitPrice:     25000000000,
   498  		DeriveShaImpl: 0,
   499  		Istanbul: &params.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  }