github.com/iotexproject/iotex-core@v1.14.1-rc1/tools/minicluster/minicluster.go (about)

     1  // Copyright (c) 2019 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  // usage: make minicluster
     7  
     8  package main
     9  
    10  import (
    11  	"context"
    12  	"flag"
    13  	"fmt"
    14  	"math"
    15  	"math/big"
    16  	"math/rand"
    17  	"os"
    18  	"sync"
    19  	"time"
    20  
    21  	"github.com/iotexproject/go-pkgs/cache/ttl"
    22  	"github.com/iotexproject/go-pkgs/crypto"
    23  	"github.com/iotexproject/iotex-proto/golang/iotexapi"
    24  	"github.com/iotexproject/iotex-proto/golang/iotextypes"
    25  	"go.uber.org/zap"
    26  	"google.golang.org/grpc"
    27  
    28  	"github.com/iotexproject/iotex-core/action/protocol"
    29  	"github.com/iotexproject/iotex-core/action/protocol/rewarding"
    30  	"github.com/iotexproject/iotex-core/blockchain"
    31  	"github.com/iotexproject/iotex-core/blockchain/genesis"
    32  	"github.com/iotexproject/iotex-core/config"
    33  	"github.com/iotexproject/iotex-core/pkg/log"
    34  	"github.com/iotexproject/iotex-core/pkg/probe"
    35  	"github.com/iotexproject/iotex-core/pkg/unit"
    36  	"github.com/iotexproject/iotex-core/pkg/util/fileutil"
    37  	"github.com/iotexproject/iotex-core/server/itx"
    38  	"github.com/iotexproject/iotex-core/state/factory"
    39  	"github.com/iotexproject/iotex-core/testutil"
    40  	"github.com/iotexproject/iotex-core/tools/executiontester/assetcontract"
    41  	bc "github.com/iotexproject/iotex-core/tools/executiontester/blockchain"
    42  	"github.com/iotexproject/iotex-core/tools/util"
    43  )
    44  
    45  const (
    46  	_numNodes  = 4
    47  	_numAdmins = 2
    48  )
    49  
    50  func main() {
    51  	// timeout indicates the duration of running nightly build in seconds. Default is 300
    52  	var timeout int
    53  	// aps indicates how many actions to be injected in one second. Default is 0
    54  	var aps float64
    55  	// smart contract deployment data. Default is "608060405234801561001057600080fd5b506102f5806100206000396000f3006080604052600436106100615763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416632885ad2c8114610066578063797d9fbd14610070578063cd5e3c5d14610091578063d0e30db0146100b8575b600080fd5b61006e6100c0565b005b61006e73ffffffffffffffffffffffffffffffffffffffff600435166100cb565b34801561009d57600080fd5b506100a6610159565b60408051918252519081900360200190f35b61006e610229565b6100c9336100cb565b565b60006100d5610159565b6040805182815290519192507fbae72e55df73720e0f671f4d20a331df0c0dc31092fda6c573f35ff7f37f283e919081900360200190a160405173ffffffffffffffffffffffffffffffffffffffff8316906305f5e100830280156108fc02916000818181858888f19350505050158015610154573d6000803e3d6000fd5b505050565b604080514460208083019190915260001943014082840152825180830384018152606090920192839052815160009360059361021a9360029391929182918401908083835b602083106101bd5780518252601f19909201916020918201910161019e565b51815160209384036101000a600019018019909216911617905260405191909301945091925050808303816000865af11580156101fe573d6000803e3d6000fd5b5050506040513d602081101561021357600080fd5b5051610261565b81151561022357fe5b06905090565b60408051348152905133917fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c919081900360200190a2565b600080805b60208110156102c25780600101602060ff160360080260020a848260208110151561028d57fe5b7f010000000000000000000000000000000000000000000000000000000000000091901a810204029190910190600101610266565b50929150505600a165627a7a72305820a426929891673b0a04d7163b60113d28e7d0f48ea667680ba48126c182b872c10029"
    56  	var deployExecData string
    57  	// smart contract interaction data. Default is "d0e30db0"
    58  	var interactExecData string
    59  	// switch of fp token smart contract test. Default is false
    60  	var testFpToken bool
    61  
    62  	flag.IntVar(&timeout, "timeout", 100, "duration of running nightly build")
    63  	flag.Float64Var(&aps, "aps", 1, "actions to be injected per second")
    64  	flag.StringVar(&deployExecData, "deploy-data", "608060405234801561001057600080fd5b506102f5806100206000396000f3006080604052600436106100615763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416632885ad2c8114610066578063797d9fbd14610070578063cd5e3c5d14610091578063d0e30db0146100b8575b600080fd5b61006e6100c0565b005b61006e73ffffffffffffffffffffffffffffffffffffffff600435166100cb565b34801561009d57600080fd5b506100a6610159565b60408051918252519081900360200190f35b61006e610229565b6100c9336100cb565b565b60006100d5610159565b6040805182815290519192507fbae72e55df73720e0f671f4d20a331df0c0dc31092fda6c573f35ff7f37f283e919081900360200190a160405173ffffffffffffffffffffffffffffffffffffffff8316906305f5e100830280156108fc02916000818181858888f19350505050158015610154573d6000803e3d6000fd5b505050565b604080514460208083019190915260001943014082840152825180830384018152606090920192839052815160009360059361021a9360029391929182918401908083835b602083106101bd5780518252601f19909201916020918201910161019e565b51815160209384036101000a600019018019909216911617905260405191909301945091925050808303816000865af11580156101fe573d6000803e3d6000fd5b5050506040513d602081101561021357600080fd5b5051610261565b81151561022357fe5b06905090565b60408051348152905133917fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c919081900360200190a2565b600080805b60208110156102c25780600101602060ff160360080260020a848260208110151561028d57fe5b7f010000000000000000000000000000000000000000000000000000000000000091901a810204029190910190600101610266565b50929150505600a165627a7a72305820a426929891673b0a04d7163b60113d28e7d0f48ea667680ba48126c182b872c10029",
    65  		"smart contract deployment data")
    66  	flag.StringVar(&interactExecData, "interact-data", "d0e30db0", "smart contract interaction data")
    67  	flag.BoolVar(&testFpToken, "fp-token", false, "switch of fp token smart contract test")
    68  	flag.Parse()
    69  
    70  	// path of config file containing all the public/private key paris of addresses getting transfers
    71  	// from Creator in genesis block
    72  	injectorConfigPath := "./tools/minicluster/gentsfaddrs.yaml"
    73  
    74  	chainAddrs, err := util.LoadAddresses(injectorConfigPath, uint32(1))
    75  	if err != nil {
    76  		log.L().Fatal("Failed to load addresses from config path", zap.Error(err))
    77  	}
    78  	admins := chainAddrs[len(chainAddrs)-_numAdmins:]
    79  	delegates := chainAddrs[:len(chainAddrs)-_numAdmins]
    80  
    81  	dbFilePaths := make([]string, 0)
    82  	//a flag to indicate whether the DB files should be cleaned up upon completion of the minicluster.
    83  	deleteDBFiles := false
    84  
    85  	// Set mini-cluster configurations
    86  	configs := make([]config.Config, _numNodes)
    87  	for i := 0; i < _numNodes; i++ {
    88  		chainDBPath := fmt.Sprintf("./chain%d.db", i+1)
    89  		dbFilePaths = append(dbFilePaths, chainDBPath)
    90  		trieDBPath := fmt.Sprintf("./trie%d.db", i+1)
    91  		dbFilePaths = append(dbFilePaths, trieDBPath)
    92  		indexDBPath := fmt.Sprintf("./index%d.db", i+1)
    93  		dbFilePaths = append(dbFilePaths, indexDBPath)
    94  		bloomfilterIndexDBPath := fmt.Sprintf("./bloomfilter.index%d.db", i+1)
    95  		dbFilePaths = append(dbFilePaths, bloomfilterIndexDBPath)
    96  		consensusDBPath := fmt.Sprintf("./consensus%d.db", i+1)
    97  		dbFilePaths = append(dbFilePaths, consensusDBPath)
    98  		systemLogDBPath := fmt.Sprintf("./systemlog%d.db", i+1)
    99  		dbFilePaths = append(dbFilePaths, systemLogDBPath)
   100  		candidateIndexDBPath := fmt.Sprintf("./candidate.index%d.db", i+1)
   101  		dbFilePaths = append(dbFilePaths, candidateIndexDBPath)
   102  		networkPort := config.Default.Network.Port + i
   103  		apiPort := config.Default.API.GRPCPort + i
   104  		web3APIPort := config.Default.API.HTTPPort + i
   105  		web3SocketPort := config.Default.API.WebSocketPort + i
   106  		HTTPAdminPort := config.Default.System.HTTPAdminPort + i
   107  		config := newConfig(chainAddrs[i].PriKey, networkPort, apiPort, web3APIPort, web3SocketPort, HTTPAdminPort)
   108  		config.Chain.ChainDBPath = chainDBPath
   109  		config.Chain.TrieDBPatchFile = ""
   110  		config.Chain.TrieDBPath = trieDBPath
   111  		config.Chain.IndexDBPath = indexDBPath
   112  		config.Chain.BloomfilterIndexDBPath = bloomfilterIndexDBPath
   113  		config.Chain.CandidateIndexDBPath = candidateIndexDBPath
   114  		config.Consensus.RollDPoS.ConsensusDBPath = consensusDBPath
   115  		config.System.SystemLogDBPath = systemLogDBPath
   116  		if i == 0 {
   117  			config.Network.BootstrapNodes = []string{}
   118  			config.Network.MasterKey = "bootnode"
   119  		}
   120  		config.Genesis.AleutianBlockHeight = 1
   121  		config.Genesis.PacificBlockHeight = 1
   122  		configs[i] = config
   123  	}
   124  
   125  	// Create mini-cluster
   126  	svrs := make([]*itx.Server, _numNodes)
   127  	for i := 0; i < _numNodes; i++ {
   128  		svr, err := itx.NewServer(configs[i])
   129  		if err != nil {
   130  			log.L().Fatal("Failed to create server.", zap.Error(err))
   131  		}
   132  		svrs[i] = svr
   133  	}
   134  	defer func() {
   135  		if !deleteDBFiles {
   136  			return
   137  		}
   138  		for _, dbFilePath := range dbFilePaths {
   139  			if fileutil.FileExists(dbFilePath) && os.RemoveAll(dbFilePath) != nil {
   140  				log.L().Error("Failed to delete db file")
   141  			}
   142  		}
   143  	}()
   144  
   145  	// Start mini-cluster
   146  	for i := 0; i < _numNodes; i++ {
   147  		ctx, cancel := context.WithCancel(context.Background())
   148  		defer cancel()
   149  		go itx.StartServer(ctx, svrs[i], probe.New(7788+i), configs[i])
   150  	}
   151  
   152  	// target address for grpc connection. Default is "127.0.0.1:14014"
   153  	grpcAddr := "127.0.0.1:14014"
   154  
   155  	grpcctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
   156  	defer cancel()
   157  	conn, err := grpc.DialContext(grpcctx, grpcAddr, grpc.WithBlock(), grpc.WithInsecure())
   158  	if err != nil {
   159  		log.L().Error("Failed to connect to API server.")
   160  	}
   161  	defer conn.Close()
   162  
   163  	client := iotexapi.NewAPIServiceClient(conn)
   164  
   165  	counter, err := util.InitCounter(client, chainAddrs)
   166  	if err != nil {
   167  		log.L().Fatal("Failed to initialize nonce counter", zap.Error(err))
   168  	}
   169  
   170  	// Inject actions to first node
   171  	if aps > 0 {
   172  		// transfer gas limit. Default is 1000000
   173  		transferGasLimit := 1000000
   174  		// transfer gas price. Default is 10
   175  		transferGasPrice := unit.Qev
   176  		// transfer payload. Default is ""
   177  		transferPayload := ""
   178  		// vote gas limit. Default is 1000000
   179  		voteGasLimit := 1000000
   180  		// vote gas price. Default is 10
   181  		voteGasPrice := unit.Qev
   182  		// execution amount. Default is 0
   183  		executionAmount := 0
   184  		// execution gas limit. Default is 1200000
   185  		executionGasLimit := 1200000
   186  		// execution gas price. Default is 10
   187  		executionGasPrice := unit.Qev
   188  		// maximum number of rpc retries. Default is 5
   189  		retryNum := 5
   190  		// sleeping period between two consecutive rpc retries in seconds. Default is 1
   191  		retryInterval := 1
   192  		// reset interval indicates the interval to reset nonce counter in seconds. Default is 60
   193  		resetInterval := 60
   194  		// fpTotal indicates the total amount value of a fp token
   195  		fpTotal := int64(20000)
   196  		// fpRisk indicates the risk amount value of a fp token
   197  		fpRisk := int64(1000)
   198  
   199  		d := time.Duration(timeout) * time.Second
   200  
   201  		// First deploy a user specified smart contract which can be interacted by injected executions
   202  		eHash, err := util.DeployContract(client, counter, delegates, executionGasLimit, executionGasPrice,
   203  			deployExecData, retryNum, retryInterval)
   204  		if err != nil {
   205  			log.L().Fatal("Failed to deploy smart contract", zap.Error(err))
   206  		}
   207  		// Wait until the smart contract is successfully deployed
   208  		var (
   209  			receipt *iotextypes.Receipt
   210  			as      = svrs[0].APIServer(1)
   211  		)
   212  		if err := testutil.WaitUntil(100*time.Millisecond, 60*time.Second, func() (bool, error) {
   213  			receipt, err = util.GetReceiptByAction(as.CoreService(), eHash)
   214  			return receipt != nil, nil
   215  		}); err != nil {
   216  			log.L().Fatal("Failed to get receipt of execution deployment", zap.Error(err))
   217  		}
   218  		contract := receipt.ContractAddress
   219  
   220  		var fpToken bc.FpToken
   221  		var fpContract string
   222  		var debtor *util.AddressKey
   223  		var creditor *util.AddressKey
   224  		if testFpToken {
   225  			// Deploy asset smart contracts
   226  			ret, err := assetcontract.StartContracts(configs[0])
   227  			if err != nil {
   228  				log.L().Fatal("Failed to deploy asset contracts.", zap.Error(err))
   229  			}
   230  			fpToken = ret.FpToken
   231  			// Randomly pick two accounts from delegate list as fp_token debtor and creditor
   232  			first := rand.Intn(len(admins))
   233  			second := first
   234  			for second == first {
   235  				second = rand.Intn(len(admins))
   236  			}
   237  			debtor = admins[first]
   238  			creditor = admins[second]
   239  
   240  			// Create fp token
   241  			assetID := assetcontract.GenerateAssetID()
   242  			open := time.Now().Unix()
   243  			exp := open + 100000
   244  
   245  			if _, err := fpToken.CreateToken(assetID, debtor.EncodedAddr, creditor.EncodedAddr, fpTotal, fpRisk, open,
   246  				exp); err != nil {
   247  				log.L().Fatal("Failed to create fp token", zap.Error(err))
   248  			}
   249  
   250  			fpContract, err = fpToken.TokenAddress(assetID)
   251  			if err != nil {
   252  				log.L().Fatal("Failed to get token contract address", zap.Error(err))
   253  			}
   254  
   255  			// Transfer full amount from debtor to creditor
   256  			debtorPriKey := debtor.PriKey.HexString()
   257  			if _, err := fpToken.Transfer(fpContract, debtor.EncodedAddr, debtorPriKey,
   258  				creditor.EncodedAddr, fpTotal); err != nil {
   259  				log.L().Fatal("Failed to transfer total amount from debtor to creditor", zap.Error(err))
   260  			}
   261  
   262  			// Transfer amount of risk from creditor to contract
   263  			creditorPriKey := creditor.PriKey.HexString()
   264  			if _, err := fpToken.RiskLock(fpContract, creditor.EncodedAddr, creditorPriKey,
   265  				fpRisk); err != nil {
   266  				log.L().Fatal("Failed to transfer amount of risk from creditor to contract", zap.Error(err))
   267  			}
   268  		}
   269  
   270  		expectedBalancesMap := util.GetAllBalanceMap(client, chainAddrs)
   271  		pendingActionMap, _ := ttl.NewCache(ttl.EvictOnErrorOption())
   272  
   273  		log.L().Info("Start action injections.")
   274  
   275  		wg := &sync.WaitGroup{}
   276  		util.InjectByAps(wg, aps, counter, transferGasLimit, transferGasPrice, transferPayload, voteGasLimit,
   277  			voteGasPrice, contract, executionAmount, executionGasLimit, executionGasPrice, interactExecData, fpToken,
   278  			fpContract, debtor, creditor, client, admins, delegates, d, retryNum, retryInterval, resetInterval,
   279  			expectedBalancesMap, as.CoreService(), pendingActionMap)
   280  		wg.Wait()
   281  
   282  		err = testutil.WaitUntil(100*time.Millisecond, 60*time.Second, func() (bool, error) {
   283  			empty, err := util.CheckPendingActionList(
   284  				as.CoreService(),
   285  				pendingActionMap,
   286  				expectedBalancesMap,
   287  			)
   288  			if err != nil {
   289  				log.L().Error(err.Error())
   290  				return false, err
   291  			}
   292  			return empty, nil
   293  		})
   294  
   295  		totalPendingActions := 0
   296  		pendingActionMap.Range(func(selphash, vi interface{}) error {
   297  			totalPendingActions++
   298  			return nil
   299  		})
   300  
   301  		if err != nil {
   302  			log.L().Error("Not all actions are settled")
   303  		}
   304  
   305  		chains := make([]blockchain.Blockchain, _numNodes)
   306  		sfs := make([]factory.Factory, _numNodes)
   307  		stateHeights := make([]uint64, _numNodes)
   308  		bcHeights := make([]uint64, _numNodes)
   309  		idealHeight := make([]uint64, _numNodes)
   310  
   311  		var netTimeout int
   312  		var minTimeout int
   313  
   314  		for i := 0; i < _numNodes; i++ {
   315  			chains[i] = svrs[i].ChainService(configs[i].Chain.ID).Blockchain()
   316  			sfs[i] = svrs[i].ChainService(configs[i].Chain.ID).StateFactory()
   317  
   318  			stateHeights[i], err = sfs[i].Height()
   319  			if err != nil {
   320  				log.S().Errorf("Node %d: Can not get State height", i)
   321  			}
   322  			bcHeights[i] = chains[i].TipHeight()
   323  			minTimeout = int(configs[i].Consensus.RollDPoS.Delay/time.Second - configs[i].Genesis.BlockInterval/time.Second)
   324  			netTimeout = 0
   325  			if timeout > minTimeout {
   326  				netTimeout = timeout - minTimeout
   327  			}
   328  			idealHeight[i] = uint64((time.Duration(netTimeout) * time.Second) / configs[i].Genesis.BlockInterval)
   329  
   330  			log.S().Infof("Node#%d blockchain height: %d", i, bcHeights[i])
   331  			log.S().Infof("Node#%d state      height: %d", i, stateHeights[i])
   332  			log.S().Infof("Node#%d ideal      height: %d", i, idealHeight[i])
   333  
   334  			if bcHeights[i] != stateHeights[i] {
   335  				log.S().Errorf("Node#%d: State height does not match blockchain height", i)
   336  			}
   337  			if bcHeights[i] < idealHeight[i] {
   338  				log.S().Errorf("blockchain in Node#%d is behind the expected height", i)
   339  			}
   340  		}
   341  
   342  		for i := 0; i < _numNodes; i++ {
   343  			for j := i + 1; j < _numNodes; j++ {
   344  				if math.Abs(float64(bcHeights[i]-bcHeights[j])) > 1 {
   345  					log.S().Errorf("blockchain in Node#%d and blockchain in Node#%d are not sync", i, j)
   346  				} else {
   347  					log.S().Infof("blockchain in Node#%d and blockchain in Node#%d are sync", i, j)
   348  				}
   349  			}
   350  		}
   351  
   352  		m := util.GetAllBalanceMap(client, chainAddrs)
   353  		balanceCheckPass := true
   354  		for k, v := range m {
   355  			if len(expectedBalancesMap) != 0 && v.Cmp(expectedBalancesMap[k]) != 0 {
   356  				balanceCheckPass = false
   357  				log.S().Info("Balance mismatch on account ", k)
   358  				log.S().Info("Real balance: ", v.String(), " Expected balance: ", expectedBalancesMap[k].String())
   359  
   360  			}
   361  		}
   362  		if balanceCheckPass {
   363  			log.S().Info("Balance Check PASS")
   364  		} else {
   365  			log.S().Fatal("Balance Mismatch")
   366  		}
   367  
   368  		log.S().Info("Total Transfer created: ", util.GetTotalTsfCreated())
   369  		log.S().Info("Total Transfer inject through grpc: ", util.GetTotalTsfSentToAPI())
   370  		log.S().Info("Total Transfer succeed: ", util.GetTotalTsfSucceeded())
   371  		log.S().Info("Total Transfer failed: ", util.GetTotalTsfFailed())
   372  		log.S().Info("Total pending actions: ", totalPendingActions)
   373  
   374  		if testFpToken {
   375  			// Check fp token asset balance
   376  			debtorBalance, err := fpToken.ReadValue(fpContract, "70a08231", debtor.EncodedAddr)
   377  			if err != nil {
   378  				log.S().Error("Failed to get debtor's asset balance.", zap.Error(err))
   379  			}
   380  			log.L().Info("Debtor's asset balance: ", zap.Int64("balance", debtorBalance))
   381  
   382  			creditorBalance, err := fpToken.ReadValue(fpContract, "70a08231", creditor.EncodedAddr)
   383  			if err != nil {
   384  				log.S().Error("Failed to get creditor's asset balance.", zap.Error(err))
   385  			}
   386  			log.L().Info("Creditor's asset balance: ", zap.Int64("balance", creditorBalance))
   387  
   388  			if debtorBalance+creditorBalance != fpTotal-fpRisk {
   389  				log.S().Error("Sum of asset balance is incorrect.")
   390  				return
   391  			}
   392  
   393  			log.S().Info("Fp token transfer test pass!")
   394  		}
   395  
   396  		registries := make([]*protocol.Registry, _numNodes)
   397  		for i := 0; i < _numNodes; i++ {
   398  			registries[i] = svrs[i].ChainService(configs[i].Chain.ID).Registry()
   399  
   400  			ctx := protocol.WithBlockCtx(context.Background(), protocol.BlockCtx{
   401  				BlockHeight: bcHeights[i],
   402  			})
   403  			ctx = genesis.WithGenesisContext(
   404  				protocol.WithRegistry(ctx, registries[i]),
   405  				chains[i].Genesis(),
   406  			)
   407  			ctx = protocol.WithFeatureCtx(protocol.WithFeatureWithHeightCtx(ctx))
   408  
   409  			rp := rewarding.FindProtocol(registries[i])
   410  			if rp == nil {
   411  				log.S().Fatal("rolldpos is not registered.")
   412  			}
   413  
   414  			blockReward, err := rp.BlockReward(ctx, sfs[i])
   415  			if err != nil {
   416  				log.S().Fatal("Failed to get block reward.", zap.Error(err))
   417  			}
   418  			if blockReward == configs[i].Genesis.BlockReward() {
   419  				log.S().Fatal("actual block reward is incorrect.")
   420  			}
   421  
   422  			epochReward, err := rp.EpochReward(ctx, sfs[i])
   423  			if err != nil {
   424  				log.S().Fatal("Failed to get epoch reward.", zap.Error(err))
   425  			}
   426  			if epochReward == configs[i].Genesis.AleutianEpochReward() {
   427  				log.S().Fatal("actual epoch reward is incorrect.")
   428  			}
   429  		}
   430  
   431  		deleteDBFiles = true
   432  	}
   433  }
   434  
   435  func newConfig(
   436  	producerPriKey crypto.PrivateKey,
   437  	networkPort,
   438  	apiPort int,
   439  	web3APIPort int,
   440  	webSocketPort int,
   441  	HTTPAdminPort int,
   442  ) config.Config {
   443  	cfg := config.Default
   444  
   445  	cfg.Plugins[config.GatewayPlugin] = true
   446  	cfg.Chain.EnableAsyncIndexWrite = false
   447  
   448  	cfg.System.HTTPAdminPort = HTTPAdminPort
   449  	cfg.Network.Port = networkPort
   450  	cfg.Network.BootstrapNodes = []string{"/ip4/127.0.0.1/tcp/4689/ipfs/12D3KooWJwW6pUpTkxPTMv84RPLPMQVEAjZ6fvJuX4oZrvW5DAGQ"}
   451  
   452  	cfg.Chain.ID = 1
   453  	cfg.Chain.ProducerPrivKey = producerPriKey.HexString()
   454  
   455  	cfg.ActPool.MinGasPriceStr = big.NewInt(0).String()
   456  
   457  	cfg.Consensus.Scheme = config.RollDPoSScheme
   458  	cfg.Consensus.RollDPoS.FSM.UnmatchedEventInterval = 2400 * time.Millisecond
   459  	cfg.Consensus.RollDPoS.FSM.AcceptBlockTTL = 1800 * time.Millisecond
   460  	cfg.Consensus.RollDPoS.FSM.AcceptProposalEndorsementTTL = 1800 * time.Millisecond
   461  	cfg.Consensus.RollDPoS.FSM.AcceptLockEndorsementTTL = 1800 * time.Millisecond
   462  	cfg.Consensus.RollDPoS.FSM.CommitTTL = 600 * time.Millisecond
   463  	cfg.Consensus.RollDPoS.FSM.EventChanSize = 100000
   464  	cfg.Consensus.RollDPoS.ToleratedOvertime = 1200 * time.Millisecond
   465  	cfg.Consensus.RollDPoS.Delay = 6 * time.Second
   466  
   467  	cfg.API.GRPCPort = apiPort
   468  	cfg.API.HTTPPort = web3APIPort
   469  	cfg.API.WebSocketPort = webSocketPort
   470  
   471  	cfg.Genesis.BlockInterval = 6 * time.Second
   472  	cfg.Genesis.Blockchain.NumSubEpochs = 2
   473  	cfg.Genesis.Blockchain.NumDelegates = _numNodes
   474  	cfg.Genesis.Blockchain.TimeBasedRotation = true
   475  	cfg.Genesis.Delegates = cfg.Genesis.Delegates[3 : _numNodes+3]
   476  	cfg.Genesis.EnableGravityChainVoting = false
   477  	cfg.Genesis.PollMode = "lifeLong"
   478  
   479  	return cfg
   480  }