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 }