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

     1  // Copyright (c) 2022 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  package chainservice
     7  
     8  import (
     9  	"context"
    10  	"math/big"
    11  	"time"
    12  
    13  	"github.com/iotexproject/iotex-address/address"
    14  	"github.com/iotexproject/iotex-election/committee"
    15  	"github.com/pkg/errors"
    16  	"go.uber.org/zap"
    17  	"google.golang.org/protobuf/proto"
    18  
    19  	"github.com/iotexproject/iotex-core/action"
    20  	"github.com/iotexproject/iotex-core/action/protocol"
    21  	"github.com/iotexproject/iotex-core/action/protocol/account"
    22  	accountutil "github.com/iotexproject/iotex-core/action/protocol/account/util"
    23  	"github.com/iotexproject/iotex-core/action/protocol/execution"
    24  	"github.com/iotexproject/iotex-core/action/protocol/execution/evm"
    25  	"github.com/iotexproject/iotex-core/action/protocol/poll"
    26  	"github.com/iotexproject/iotex-core/action/protocol/rewarding"
    27  	"github.com/iotexproject/iotex-core/action/protocol/rolldpos"
    28  	"github.com/iotexproject/iotex-core/action/protocol/staking"
    29  	"github.com/iotexproject/iotex-core/action/protocol/vote/candidatesutil"
    30  	"github.com/iotexproject/iotex-core/actpool"
    31  	"github.com/iotexproject/iotex-core/blockchain"
    32  	"github.com/iotexproject/iotex-core/blockchain/block"
    33  	"github.com/iotexproject/iotex-core/blockchain/blockdao"
    34  	"github.com/iotexproject/iotex-core/blockchain/filedao"
    35  	"github.com/iotexproject/iotex-core/blockchain/genesis"
    36  	"github.com/iotexproject/iotex-core/blockindex"
    37  	"github.com/iotexproject/iotex-core/blockindex/contractstaking"
    38  	"github.com/iotexproject/iotex-core/blocksync"
    39  	"github.com/iotexproject/iotex-core/config"
    40  	"github.com/iotexproject/iotex-core/consensus"
    41  	"github.com/iotexproject/iotex-core/consensus/consensusfsm"
    42  	rp "github.com/iotexproject/iotex-core/consensus/scheme/rolldpos"
    43  	"github.com/iotexproject/iotex-core/db"
    44  	"github.com/iotexproject/iotex-core/nodeinfo"
    45  	"github.com/iotexproject/iotex-core/p2p"
    46  	"github.com/iotexproject/iotex-core/pkg/log"
    47  	"github.com/iotexproject/iotex-core/pkg/util/blockutil"
    48  	"github.com/iotexproject/iotex-core/server/itx/nodestats"
    49  	"github.com/iotexproject/iotex-core/state/factory"
    50  )
    51  
    52  // Builder is a builder to build chainservice
    53  type Builder struct {
    54  	cfg config.Config
    55  	cs  *ChainService
    56  }
    57  
    58  // NewBuilder creates a new chainservice builder
    59  func NewBuilder(cfg config.Config) *Builder {
    60  	builder := &Builder{cfg: cfg}
    61  	builder.createInstance()
    62  
    63  	return builder
    64  }
    65  
    66  // SetActionPool sets the action pool instance
    67  func (builder *Builder) SetActionPool(ap actpool.ActPool) *Builder {
    68  	builder.createInstance()
    69  	builder.cs.actpool = ap
    70  	return builder
    71  }
    72  
    73  // SetBlockchain sets the blockchain instance
    74  func (builder *Builder) SetBlockchain(bc blockchain.Blockchain) *Builder {
    75  	builder.createInstance()
    76  	builder.cs.chain = bc
    77  	return builder
    78  }
    79  
    80  // SetFactory sets the factory instance
    81  func (builder *Builder) SetFactory(f factory.Factory) *Builder {
    82  	builder.createInstance()
    83  	builder.cs.factory = f
    84  	return builder
    85  }
    86  
    87  // SetBlockDAO sets the blockdao instance
    88  func (builder *Builder) SetBlockDAO(bd blockdao.BlockDAO) *Builder {
    89  	builder.createInstance()
    90  	builder.cs.blockdao = bd
    91  	return builder
    92  }
    93  
    94  // SetP2PAgent sets the P2PAgent instance
    95  func (builder *Builder) SetP2PAgent(agent p2p.Agent) *Builder {
    96  	builder.createInstance()
    97  	builder.cs.p2pAgent = agent
    98  	return builder
    99  }
   100  
   101  // SetRPCStats sets the RPCStats instance
   102  func (builder *Builder) SetRPCStats(stats *nodestats.APILocalStats) *Builder {
   103  	builder.createInstance()
   104  	builder.cs.apiStats = stats
   105  	return builder
   106  }
   107  
   108  // SetElectionCommittee sets the election committee instance
   109  func (builder *Builder) SetElectionCommittee(c committee.Committee) *Builder {
   110  	builder.createInstance()
   111  	builder.cs.electionCommittee = c
   112  	return builder
   113  }
   114  
   115  // SetBlockSync sets the block sync instance
   116  func (builder *Builder) SetBlockSync(bs blocksync.BlockSync) *Builder {
   117  	builder.createInstance()
   118  	builder.cs.blocksync = bs
   119  	return builder
   120  }
   121  
   122  // BuildForTest builds a chainservice for test purpose
   123  func (builder *Builder) BuildForTest() (*ChainService, error) {
   124  	builder.createInstance()
   125  	return builder.build(false, true)
   126  }
   127  
   128  // BuildForSubChain builds a chainservice for subchain
   129  func (builder *Builder) BuildForSubChain() (*ChainService, error) {
   130  	builder.createInstance()
   131  	return builder.build(true, false)
   132  }
   133  
   134  // Build builds a chainservice
   135  func (builder *Builder) Build() (*ChainService, error) {
   136  	builder.createInstance()
   137  	return builder.build(false, false)
   138  }
   139  
   140  func (builder *Builder) createInstance() {
   141  	if builder.cs == nil {
   142  		builder.cs = &ChainService{}
   143  	}
   144  }
   145  
   146  func (builder *Builder) buildFactory(forTest bool) error {
   147  	factory, err := builder.createFactory(forTest)
   148  	if err != nil {
   149  		return errors.Wrapf(err, "failed to create state factory")
   150  	}
   151  	builder.cs.factory = factory
   152  	return nil
   153  }
   154  
   155  func (builder *Builder) createFactory(forTest bool) (factory.Factory, error) {
   156  	var dao db.KVStore
   157  	var err error
   158  	if builder.cs.factory != nil {
   159  		return builder.cs.factory, nil
   160  	}
   161  	factoryCfg := factory.GenerateConfig(builder.cfg.Chain, builder.cfg.Genesis)
   162  	if builder.cfg.Chain.EnableTrielessStateDB {
   163  		if forTest {
   164  			return factory.NewStateDB(factoryCfg, db.NewMemKVStore(), factory.RegistryStateDBOption(builder.cs.registry))
   165  		}
   166  		opts := []factory.StateDBOption{
   167  			factory.RegistryStateDBOption(builder.cs.registry),
   168  			factory.DefaultPatchOption(),
   169  		}
   170  		if builder.cfg.Chain.EnableStateDBCaching {
   171  			dao, err = db.CreateKVStoreWithCache(builder.cfg.DB, builder.cfg.Chain.TrieDBPath, builder.cfg.Chain.StateDBCacheSize)
   172  		} else {
   173  			dao, err = db.CreateKVStore(builder.cfg.DB, builder.cfg.Chain.TrieDBPath)
   174  		}
   175  		if err != nil {
   176  			return nil, err
   177  		}
   178  		return factory.NewStateDB(factoryCfg, dao, opts...)
   179  	}
   180  	if forTest {
   181  		return factory.NewFactory(factoryCfg, db.NewMemKVStore(), factory.RegistryOption(builder.cs.registry))
   182  	}
   183  	dao, err = db.CreateKVStore(builder.cfg.DB, builder.cfg.Chain.TrieDBPath)
   184  	if err != nil {
   185  		return nil, err
   186  	}
   187  	return factory.NewFactory(
   188  		factoryCfg,
   189  		dao,
   190  		factory.RegistryOption(builder.cs.registry),
   191  		factory.DefaultTriePatchOption(),
   192  	)
   193  }
   194  
   195  func (builder *Builder) buildElectionCommittee() error {
   196  	ec, err := builder.createElectionCommittee()
   197  	if err != nil {
   198  		return errors.Wrapf(err, "failed to create election committee")
   199  	}
   200  	if ec != nil {
   201  		builder.cs.electionCommittee = ec
   202  		builder.cs.lifecycle.Add(ec)
   203  	}
   204  	return nil
   205  }
   206  
   207  func (builder *Builder) createElectionCommittee() (committee.Committee, error) {
   208  	if builder.cs.electionCommittee != nil {
   209  		return builder.cs.electionCommittee, nil
   210  	}
   211  	if !builder.cfg.Genesis.EnableGravityChainVoting {
   212  		return nil, nil
   213  	}
   214  	cfg := builder.cfg
   215  
   216  	committeeConfig := builder.cfg.Chain.Committee
   217  	committeeConfig.GravityChainStartHeight = builder.cfg.Genesis.GravityChainStartHeight
   218  	if committeeConfig.GravityChainStartHeight == 0 {
   219  		return nil, nil
   220  	}
   221  	committeeConfig.GravityChainCeilingHeight = cfg.Genesis.GravityChainCeilingHeight
   222  	committeeConfig.GravityChainHeightInterval = cfg.Genesis.GravityChainHeightInterval
   223  	committeeConfig.RegisterContractAddress = cfg.Genesis.RegisterContractAddress
   224  	committeeConfig.StakingContractAddress = cfg.Genesis.StakingContractAddress
   225  	committeeConfig.VoteThreshold = cfg.Genesis.VoteThreshold
   226  	committeeConfig.ScoreThreshold = "0"
   227  	committeeConfig.StakingContractAddress = cfg.Genesis.StakingContractAddress
   228  	committeeConfig.SelfStakingThreshold = cfg.Genesis.SelfStakingThreshold
   229  
   230  	arch, err := committee.NewArchive(
   231  		cfg.Chain.GravityChainDB.DbPath,
   232  		cfg.Chain.GravityChainDB.NumRetries,
   233  		committeeConfig.GravityChainStartHeight,
   234  		committeeConfig.GravityChainHeightInterval,
   235  	)
   236  	if err != nil {
   237  		return nil, err
   238  	}
   239  	return committee.NewCommittee(arch, committeeConfig)
   240  }
   241  
   242  func (builder *Builder) buildActionPool() error {
   243  	if builder.cs.actpool == nil {
   244  		ac, err := actpool.NewActPool(builder.cfg.Genesis, builder.cs.factory, builder.cfg.ActPool)
   245  		if err != nil {
   246  			return errors.Wrap(err, "failed to create actpool")
   247  		}
   248  		builder.cs.actpool = ac
   249  	}
   250  	// Add action validators
   251  	builder.cs.actpool.AddActionEnvelopeValidators(
   252  		protocol.NewGenericValidator(builder.cs.factory, accountutil.AccountState),
   253  	)
   254  
   255  	return nil
   256  }
   257  
   258  func (builder *Builder) buildBlockDAO(forTest bool) error {
   259  	if builder.cs.blockdao != nil {
   260  		return nil
   261  	}
   262  
   263  	var indexers []blockdao.BlockIndexer
   264  	// indexers in synchronizedIndexers will need to run PutBlock() one by one
   265  	// factory is dependent on sgdIndexer and contractStakingIndexer, so it should be put in the first place
   266  	synchronizedIndexers := []blockdao.BlockIndexer{builder.cs.factory}
   267  	if builder.cs.contractStakingIndexer != nil {
   268  		synchronizedIndexers = append(synchronizedIndexers, builder.cs.contractStakingIndexer)
   269  	}
   270  	if builder.cs.sgdIndexer != nil {
   271  		synchronizedIndexers = append(synchronizedIndexers, builder.cs.sgdIndexer)
   272  	}
   273  	if len(synchronizedIndexers) > 1 {
   274  		indexers = append(indexers, blockindex.NewSyncIndexers(synchronizedIndexers...))
   275  	} else {
   276  		indexers = append(indexers, builder.cs.factory)
   277  	}
   278  	if !builder.cfg.Chain.EnableAsyncIndexWrite && builder.cs.indexer != nil {
   279  		indexers = append(indexers, builder.cs.indexer)
   280  	}
   281  	if builder.cs.bfIndexer != nil {
   282  		indexers = append(indexers, builder.cs.bfIndexer)
   283  	}
   284  	var (
   285  		err   error
   286  		store blockdao.BlockDAO
   287  	)
   288  	if forTest {
   289  		store, err = filedao.NewFileDAOInMemForTest()
   290  	} else {
   291  		dbConfig := builder.cfg.DB
   292  		dbConfig.DbPath = builder.cfg.Chain.ChainDBPath
   293  		store, err = filedao.NewFileDAO(dbConfig, block.NewDeserializer(builder.cfg.Chain.EVMNetworkID))
   294  	}
   295  	if err != nil {
   296  		return err
   297  	}
   298  	builder.cs.blockdao = blockdao.NewBlockDAOWithIndexersAndCache(store, indexers, builder.cfg.DB.MaxCacheSize)
   299  
   300  	return nil
   301  }
   302  
   303  func (builder *Builder) buildSGDRegistry(forTest bool) error {
   304  	if builder.cs.sgdIndexer != nil {
   305  		return nil
   306  	}
   307  	if forTest || builder.cfg.Genesis.SystemSGDContractAddress == "" {
   308  		builder.cs.sgdIndexer = nil
   309  		return nil
   310  	}
   311  	kvStore, err := db.CreateKVStoreWithCache(builder.cfg.DB, builder.cfg.Chain.SGDIndexDBPath, 1000)
   312  	if err != nil {
   313  		return err
   314  	}
   315  	builder.cs.sgdIndexer = blockindex.NewSGDRegistry(builder.cfg.Genesis.SystemSGDContractAddress, builder.cfg.Genesis.SystemSGDContractHeight, kvStore)
   316  	return nil
   317  }
   318  
   319  func (builder *Builder) buildContractStakingIndexer(forTest bool) error {
   320  	if !builder.cfg.Chain.EnableStakingProtocol {
   321  		return nil
   322  	}
   323  	if builder.cs.contractStakingIndexer != nil {
   324  		return nil
   325  	}
   326  	if forTest || builder.cfg.Genesis.SystemStakingContractAddress == "" {
   327  		builder.cs.contractStakingIndexer = nil
   328  		return nil
   329  	}
   330  	dbConfig := builder.cfg.DB
   331  	dbConfig.DbPath = builder.cfg.Chain.ContractStakingIndexDBPath
   332  	voteCalcConsts := builder.cfg.Genesis.VoteWeightCalConsts
   333  	indexer, err := contractstaking.NewContractStakingIndexer(
   334  		db.NewBoltDB(dbConfig),
   335  		contractstaking.Config{
   336  			ContractAddress:      builder.cfg.Genesis.SystemStakingContractAddress,
   337  			ContractDeployHeight: builder.cfg.Genesis.SystemStakingContractHeight,
   338  			CalculateVoteWeight: func(v *staking.VoteBucket) *big.Int {
   339  				return staking.CalculateVoteWeight(voteCalcConsts, v, false)
   340  			},
   341  			BlockInterval: builder.cfg.DardanellesUpgrade.BlockInterval,
   342  		})
   343  	if err != nil {
   344  		return err
   345  	}
   346  	builder.cs.contractStakingIndexer = indexer
   347  	return nil
   348  }
   349  
   350  func (builder *Builder) buildGatewayComponents(forTest bool) error {
   351  	indexer, bfIndexer, candidateIndexer, candBucketsIndexer, err := builder.createGateWayComponents(forTest)
   352  	if err != nil {
   353  		return errors.Wrapf(err, "failed to create gateway components")
   354  	}
   355  	builder.cs.candidateIndexer = candidateIndexer
   356  	if builder.cs.candidateIndexer != nil {
   357  		builder.cs.lifecycle.Add(builder.cs.candidateIndexer)
   358  	}
   359  	builder.cs.candBucketsIndexer = candBucketsIndexer
   360  	if builder.cs.candBucketsIndexer != nil {
   361  		builder.cs.lifecycle.Add(builder.cs.candBucketsIndexer)
   362  	}
   363  	builder.cs.bfIndexer = bfIndexer
   364  	builder.cs.indexer = indexer
   365  
   366  	return nil
   367  }
   368  
   369  func (builder *Builder) createGateWayComponents(forTest bool) (
   370  	indexer blockindex.Indexer,
   371  	bfIndexer blockindex.BloomFilterIndexer,
   372  	candidateIndexer *poll.CandidateIndexer,
   373  	candBucketsIndexer *staking.CandidatesBucketsIndexer,
   374  	err error,
   375  ) {
   376  	_, gateway := builder.cfg.Plugins[config.GatewayPlugin]
   377  	if !gateway {
   378  		return
   379  	}
   380  
   381  	if forTest {
   382  		indexer, err = blockindex.NewIndexer(db.NewMemKVStore(), builder.cfg.Genesis.Hash())
   383  		if err != nil {
   384  			return
   385  		}
   386  		bfIndexer, err = blockindex.NewBloomfilterIndexer(db.NewMemKVStore(), builder.cfg.Indexer)
   387  		if err != nil {
   388  			return
   389  		}
   390  		candidateIndexer, err = poll.NewCandidateIndexer(db.NewMemKVStore())
   391  		if err != nil {
   392  			return
   393  		}
   394  		if builder.cfg.Chain.EnableStakingIndexer {
   395  			candBucketsIndexer, err = staking.NewStakingCandidatesBucketsIndexer(db.NewMemKVStore())
   396  		}
   397  		return
   398  	}
   399  	dbConfig := builder.cfg.DB
   400  	dbConfig.DbPath = builder.cfg.Chain.IndexDBPath
   401  	indexer, err = blockindex.NewIndexer(db.NewBoltDB(dbConfig), builder.cfg.Genesis.Hash())
   402  	if err != nil {
   403  		return
   404  	}
   405  
   406  	// create bloomfilter indexer
   407  	dbConfig.DbPath = builder.cfg.Chain.BloomfilterIndexDBPath
   408  	bfIndexer, err = blockindex.NewBloomfilterIndexer(db.NewBoltDB(dbConfig), builder.cfg.Indexer)
   409  	if err != nil {
   410  		return
   411  	}
   412  
   413  	// create candidate indexer
   414  	dbConfig.DbPath = builder.cfg.Chain.CandidateIndexDBPath
   415  	candidateIndexer, err = poll.NewCandidateIndexer(db.NewBoltDB(dbConfig))
   416  	if err != nil {
   417  		return
   418  	}
   419  
   420  	// create staking indexer
   421  	if builder.cfg.Chain.EnableStakingIndexer {
   422  		dbConfig.DbPath = builder.cfg.Chain.StakingIndexDBPath
   423  		candBucketsIndexer, err = staking.NewStakingCandidatesBucketsIndexer(db.NewBoltDB(dbConfig))
   424  	}
   425  	return
   426  }
   427  
   428  func (builder *Builder) buildBlockchain(forSubChain, forTest bool) error {
   429  	builder.cs.chain = builder.createBlockchain(forSubChain, forTest)
   430  	builder.cs.lifecycle.Add(builder.cs.chain)
   431  
   432  	if err := builder.cs.chain.AddSubscriber(builder.cs.actpool); err != nil {
   433  		return errors.Wrap(err, "failed to add actpool as subscriber")
   434  	}
   435  	if builder.cs.indexer != nil && builder.cfg.Chain.EnableAsyncIndexWrite {
   436  		// config asks for a standalone indexer
   437  		indexBuilder, err := blockindex.NewIndexBuilder(builder.cs.chain.ChainID(), builder.cfg.Genesis, builder.cs.blockdao, builder.cs.indexer)
   438  		if err != nil {
   439  			return errors.Wrap(err, "failed to create index builder")
   440  		}
   441  		builder.cs.lifecycle.Add(indexBuilder)
   442  		if err := builder.cs.chain.AddSubscriber(indexBuilder); err != nil {
   443  			return errors.Wrap(err, "failed to add index builder as subscriber")
   444  		}
   445  	}
   446  	return nil
   447  }
   448  
   449  func (builder *Builder) createBlockchain(forSubChain, forTest bool) blockchain.Blockchain {
   450  	if builder.cs.chain != nil {
   451  		return builder.cs.chain
   452  	}
   453  	var chainOpts []blockchain.Option
   454  	if !forSubChain {
   455  		chainOpts = append(chainOpts, blockchain.BlockValidatorOption(block.NewValidator(builder.cs.factory, builder.cs.actpool)))
   456  	} else {
   457  		chainOpts = append(chainOpts, blockchain.BlockValidatorOption(builder.cs.factory))
   458  	}
   459  
   460  	return blockchain.NewBlockchain(builder.cfg.Chain, builder.cfg.Genesis, builder.cs.blockdao, factory.NewMinter(builder.cs.factory, builder.cs.actpool), chainOpts...)
   461  }
   462  
   463  func (builder *Builder) buildNodeInfoManager() error {
   464  	cs := builder.cs
   465  	stk := staking.FindProtocol(cs.Registry())
   466  	if stk == nil {
   467  		return errors.New("cannot find staking protocol")
   468  	}
   469  	chain := builder.cs.chain
   470  	dm := nodeinfo.NewInfoManager(&builder.cfg.NodeInfo, cs.p2pAgent, cs.chain, builder.cfg.Chain.ProducerPrivateKey(), func() []string {
   471  		ctx := protocol.WithFeatureCtx(
   472  			protocol.WithBlockCtx(
   473  				genesis.WithGenesisContext(context.Background(), chain.Genesis()),
   474  				protocol.BlockCtx{BlockHeight: chain.TipHeight()},
   475  			),
   476  		)
   477  		candidates, err := stk.ActiveCandidates(ctx, cs.factory, 0)
   478  		if err != nil {
   479  			log.L().Error("failed to get active candidates", zap.Error(errors.WithStack(err)))
   480  			return nil
   481  		}
   482  		whiteList := make([]string, len(candidates))
   483  		for i := range whiteList {
   484  			whiteList[i] = candidates[i].Address
   485  		}
   486  		return whiteList
   487  	})
   488  	builder.cs.nodeInfoManager = dm
   489  	builder.cs.lifecycle.Add(dm)
   490  	return nil
   491  }
   492  
   493  func (builder *Builder) buildBlockSyncer() error {
   494  	if builder.cs.blocksync != nil {
   495  		return nil
   496  	}
   497  	if builder.cfg.Consensus.Scheme == config.StandaloneScheme {
   498  		builder.cs.blocksync = blocksync.NewDummyBlockSyncer()
   499  		return nil
   500  	}
   501  
   502  	p2pAgent := builder.cs.p2pAgent
   503  	chain := builder.cs.chain
   504  	consens := builder.cs.consensus
   505  
   506  	blocksync, err := blocksync.NewBlockSyncer(
   507  		builder.cfg.BlockSync,
   508  		chain.TipHeight,
   509  		builder.cs.blockdao.GetBlockByHeight,
   510  		func(blk *block.Block) error {
   511  			if err := consens.ValidateBlockFooter(blk); err != nil {
   512  				log.L().Debug("Failed to validate block footer.", zap.Error(err), zap.Uint64("height", blk.Height()))
   513  				return err
   514  			}
   515  			retries := 1
   516  			if !builder.cfg.Genesis.IsHawaii(blk.Height()) {
   517  				retries = 4
   518  			}
   519  			var err error
   520  			for i := 0; i < retries; i++ {
   521  				if err = chain.ValidateBlock(blk); err == nil {
   522  					if err = chain.CommitBlock(blk); err == nil {
   523  						break
   524  					}
   525  				}
   526  				switch errors.Cause(err) {
   527  				case blockchain.ErrInvalidTipHeight:
   528  					log.L().Debug("Skip block.", zap.Error(err), zap.Uint64("height", blk.Height()))
   529  					return nil
   530  				case block.ErrDeltaStateMismatch:
   531  					log.L().Debug("Delta state mismatched.", zap.Uint64("height", blk.Height()))
   532  				default:
   533  					log.L().Debug("Failed to commit the block.", zap.Error(err), zap.Uint64("height", blk.Height()))
   534  					return err
   535  				}
   536  			}
   537  			if err != nil {
   538  				log.L().Debug("Failed to commit block.", zap.Error(err), zap.Uint64("height", blk.Height()))
   539  				return err
   540  			}
   541  			log.L().Info("Successfully committed block.", zap.Uint64("height", blk.Height()))
   542  			consens.Calibrate(blk.Height())
   543  			return nil
   544  		},
   545  		p2pAgent.ConnectedPeers,
   546  		p2pAgent.UnicastOutbound,
   547  		p2pAgent.BlockPeer,
   548  	)
   549  	if err != nil {
   550  		return errors.Wrap(err, "failed to create block syncer")
   551  	}
   552  	builder.cs.blocksync = blocksync
   553  	builder.cs.lifecycle.Add(blocksync)
   554  
   555  	return nil
   556  }
   557  
   558  func (builder *Builder) registerStakingProtocol() error {
   559  	if !builder.cfg.Chain.EnableStakingProtocol {
   560  		return nil
   561  	}
   562  
   563  	stakingProtocol, err := staking.NewProtocol(
   564  		rewarding.DepositGas,
   565  		&staking.BuilderConfig{
   566  			Staking:                  builder.cfg.Genesis.Staking,
   567  			PersistStakingPatchBlock: builder.cfg.Chain.PersistStakingPatchBlock,
   568  			StakingPatchDir:          builder.cfg.Chain.StakingPatchDir,
   569  		},
   570  		builder.cs.candBucketsIndexer,
   571  		builder.cs.contractStakingIndexer,
   572  		builder.cfg.Genesis.OkhotskBlockHeight,
   573  		builder.cfg.Genesis.GreenlandBlockHeight,
   574  		builder.cfg.Genesis.HawaiiBlockHeight,
   575  	)
   576  	if err != nil {
   577  		return err
   578  	}
   579  
   580  	return stakingProtocol.Register(builder.cs.registry)
   581  }
   582  
   583  func (builder *Builder) registerRewardingProtocol() error {
   584  	// TODO: rewarding protocol for standalone mode is weird, rDPoSProtocol could be passed via context
   585  	return rewarding.NewProtocol(builder.cfg.Genesis.Rewarding).Register(builder.cs.registry)
   586  }
   587  
   588  func (builder *Builder) registerAccountProtocol() error {
   589  	return account.NewProtocol(rewarding.DepositGas).Register(builder.cs.registry)
   590  }
   591  
   592  func (builder *Builder) registerExecutionProtocol() error {
   593  	return execution.NewProtocol(builder.cs.blockdao.GetBlockHash, rewarding.DepositGasWithSGD, builder.cs.sgdIndexer, builder.cs.blockTimeCalculator.CalculateBlockTime).Register(builder.cs.registry)
   594  }
   595  
   596  func (builder *Builder) registerRollDPoSProtocol() error {
   597  	if builder.cfg.Consensus.Scheme != config.RollDPoSScheme {
   598  		return nil
   599  	}
   600  	if err := rolldpos.NewProtocol(
   601  		builder.cfg.Genesis.NumCandidateDelegates,
   602  		builder.cfg.Genesis.NumDelegates,
   603  		builder.cfg.Genesis.NumSubEpochs,
   604  		rolldpos.EnableDardanellesSubEpoch(builder.cfg.Genesis.DardanellesBlockHeight, builder.cfg.Genesis.DardanellesNumSubEpochs),
   605  	).Register(builder.cs.registry); err != nil {
   606  		return err
   607  	}
   608  	factory := builder.cs.factory
   609  	dao := builder.cs.blockdao
   610  	chain := builder.cs.chain
   611  	getBlockTime := builder.cs.blockTimeCalculator.CalculateBlockTime
   612  	pollProtocol, err := poll.NewProtocol(
   613  		builder.cfg.Consensus.Scheme,
   614  		builder.cfg.Chain,
   615  		builder.cfg.Genesis,
   616  		builder.cs.candidateIndexer,
   617  		func(ctx context.Context, contract string, params []byte, correctGas bool) ([]byte, error) {
   618  			gasLimit := uint64(1000000)
   619  			if correctGas {
   620  				gasLimit *= 10
   621  			}
   622  			ex, err := action.NewExecution(contract, 1, big.NewInt(0), gasLimit, big.NewInt(0), params)
   623  			if err != nil {
   624  				return nil, err
   625  			}
   626  
   627  			addr, err := address.FromString(address.ZeroAddress)
   628  			if err != nil {
   629  				return nil, err
   630  			}
   631  
   632  			// TODO: add depositGas
   633  			ctx = evm.WithHelperCtx(ctx, evm.HelperContext{
   634  				GetBlockHash: dao.GetBlockHash,
   635  				GetBlockTime: getBlockTime,
   636  			})
   637  			data, _, err := factory.SimulateExecution(ctx, addr, ex)
   638  
   639  			return data, err
   640  		},
   641  		candidatesutil.CandidatesFromDB,
   642  		candidatesutil.ProbationListFromDB,
   643  		candidatesutil.UnproductiveDelegateFromDB,
   644  		builder.cs.electionCommittee,
   645  		staking.FindProtocol(builder.cs.registry),
   646  		func(height uint64) (time.Time, error) {
   647  			header, err := chain.BlockHeaderByHeight(height)
   648  			if err != nil {
   649  				return time.Now(), errors.Wrapf(
   650  					err, "error when getting the block at height: %d",
   651  					height,
   652  				)
   653  			}
   654  			return header.Timestamp(), nil
   655  		},
   656  		func(start, end uint64) (map[string]uint64, error) {
   657  			return blockchain.Productivity(chain, start, end)
   658  		},
   659  		dao.GetBlockHash,
   660  		getBlockTime,
   661  	)
   662  	if err != nil {
   663  		return errors.Wrap(err, "failed to generate poll protocol")
   664  	}
   665  	return pollProtocol.Register(builder.cs.registry)
   666  }
   667  
   668  func (builder *Builder) buildBlockTimeCalculator() (err error) {
   669  	consensusCfg := consensusfsm.NewConsensusConfig(builder.cfg.Consensus.RollDPoS.FSM, builder.cfg.DardanellesUpgrade, builder.cfg.Genesis, builder.cfg.Consensus.RollDPoS.Delay)
   670  	dao := builder.cs.BlockDAO()
   671  	builder.cs.blockTimeCalculator, err = blockutil.NewBlockTimeCalculator(consensusCfg.BlockInterval, builder.cs.Blockchain().TipHeight, func(height uint64) (time.Time, error) {
   672  		blk, err := dao.GetBlockByHeight(height)
   673  		if err != nil {
   674  			return time.Time{}, err
   675  		}
   676  		return blk.Timestamp(), nil
   677  	})
   678  	return err
   679  }
   680  
   681  func (builder *Builder) buildConsensusComponent() error {
   682  	p2pAgent := builder.cs.p2pAgent
   683  	copts := []consensus.Option{
   684  		consensus.WithBroadcast(func(msg proto.Message) error {
   685  			return p2pAgent.BroadcastOutbound(context.Background(), msg)
   686  		}),
   687  	}
   688  	if rDPoSProtocol := rolldpos.FindProtocol(builder.cs.registry); rDPoSProtocol != nil {
   689  		copts = append(copts, consensus.WithRollDPoSProtocol(rDPoSProtocol))
   690  	}
   691  	if pollProtocol := poll.FindProtocol(builder.cs.registry); pollProtocol != nil {
   692  		copts = append(copts, consensus.WithPollProtocol(pollProtocol))
   693  	}
   694  
   695  	// TODO: explorer dependency deleted at #1085, need to revive by migrating to api
   696  	builderCfg := rp.BuilderConfig{
   697  		Chain:              builder.cfg.Chain,
   698  		Consensus:          builder.cfg.Consensus.RollDPoS,
   699  		Scheme:             builder.cfg.Consensus.Scheme,
   700  		DardanellesUpgrade: builder.cfg.DardanellesUpgrade,
   701  		DB:                 builder.cfg.DB,
   702  		Genesis:            builder.cfg.Genesis,
   703  		SystemActive:       builder.cfg.System.Active,
   704  	}
   705  	component, err := consensus.NewConsensus(builderCfg, builder.cs.chain, builder.cs.factory, copts...)
   706  	if err != nil {
   707  		return errors.Wrap(err, "failed to create consensus component")
   708  	}
   709  	builder.cs.consensus = component
   710  	builder.cs.lifecycle.Add(component)
   711  
   712  	return nil
   713  }
   714  
   715  func (builder *Builder) build(forSubChain, forTest bool) (*ChainService, error) {
   716  	builder.cs.registry = protocol.NewRegistry()
   717  	if builder.cs.p2pAgent == nil {
   718  		builder.cs.p2pAgent = p2p.NewDummyAgent()
   719  	}
   720  	if err := builder.buildFactory(forTest); err != nil {
   721  		return nil, err
   722  	}
   723  	if err := builder.buildElectionCommittee(); err != nil {
   724  		return nil, err
   725  	}
   726  	if err := builder.buildActionPool(); err != nil {
   727  		return nil, err
   728  	}
   729  	if err := builder.buildGatewayComponents(forTest); err != nil {
   730  		return nil, err
   731  	}
   732  	if err := builder.buildSGDRegistry(forTest); err != nil {
   733  		return nil, err
   734  	}
   735  	if err := builder.buildContractStakingIndexer(forTest); err != nil {
   736  		return nil, err
   737  	}
   738  	if err := builder.buildBlockDAO(forTest); err != nil {
   739  		return nil, err
   740  	}
   741  	if err := builder.buildBlockchain(forSubChain, forTest); err != nil {
   742  		return nil, err
   743  	}
   744  	if err := builder.buildBlockTimeCalculator(); err != nil {
   745  		return nil, err
   746  	}
   747  	// staking protocol need to be put in registry before poll protocol when enabling
   748  	if err := builder.registerStakingProtocol(); err != nil {
   749  		return nil, errors.Wrap(err, "failed to register staking protocol")
   750  	}
   751  	if err := builder.registerAccountProtocol(); err != nil {
   752  		return nil, errors.Wrap(err, "failed to register rewarding protocol")
   753  	}
   754  	if err := builder.registerRollDPoSProtocol(); err != nil {
   755  		return nil, errors.Wrap(err, "failed to register roll dpos related protocols")
   756  	}
   757  	if err := builder.registerExecutionProtocol(); err != nil {
   758  		return nil, errors.Wrap(err, "failed to register execution protocol")
   759  	}
   760  	if err := builder.registerRewardingProtocol(); err != nil {
   761  		return nil, errors.Wrap(err, "failed to register rewarding protocol")
   762  	}
   763  	if err := builder.buildConsensusComponent(); err != nil {
   764  		return nil, err
   765  	}
   766  	if err := builder.buildBlockSyncer(); err != nil {
   767  		return nil, err
   768  	}
   769  	if err := builder.buildNodeInfoManager(); err != nil {
   770  		return nil, err
   771  	}
   772  	cs := builder.cs
   773  	builder.cs = nil
   774  
   775  	return cs, nil
   776  }