diff --git a/action/protocol/execution/protocol_test.go b/action/protocol/execution/protocol_test.go index a9fa5d005d..24478437a3 100644 --- a/action/protocol/execution/protocol_test.go +++ b/action/protocol/execution/protocol_test.go @@ -478,7 +478,7 @@ func (sct *SmartContractTest) prepareBlockchain( cfg.Chain, cfg.Genesis, dao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator( sf, protocol.NewGenericValidator(sf, accountutil.AccountState), @@ -730,7 +730,7 @@ func TestProtocol_Handle(t *testing.T) { cfg.Chain, cfg.Genesis, dao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator( sf, protocol.NewGenericValidator(sf, accountutil.AccountState), diff --git a/api/serverV2_integrity_test.go b/api/serverV2_integrity_test.go index 3dbdcfe080..88f59fdc1b 100644 --- a/api/serverV2_integrity_test.go +++ b/api/serverV2_integrity_test.go @@ -325,7 +325,7 @@ func setupChain(cfg testConfig) (blockchain.Blockchain, blockdao.BlockDAO, block cfg.chain, cfg.genesis, dao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator( sf, protocol.NewGenericValidator(sf, accountutil.AccountState), diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index efc32b84b9..8e77707df7 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -20,12 +20,12 @@ import ( "github.com/prometheus/client_golang/prometheus" "go.uber.org/zap" - "github.com/iotexproject/iotex-core/v2/action" "github.com/iotexproject/iotex-core/v2/action/protocol" "github.com/iotexproject/iotex-core/v2/blockchain/block" "github.com/iotexproject/iotex-core/v2/blockchain/blockdao" "github.com/iotexproject/iotex-core/v2/blockchain/filedao" "github.com/iotexproject/iotex-core/v2/blockchain/genesis" + "github.com/iotexproject/iotex-core/v2/db" "github.com/iotexproject/iotex-core/v2/pkg/lifecycle" "github.com/iotexproject/iotex-core/v2/pkg/log" "github.com/iotexproject/iotex-core/v2/pkg/prometheustimer" @@ -98,6 +98,8 @@ type ( CommitBlock(blk *block.Block) error // ValidateBlock validates a new block before adding it to the blockchain ValidateBlock(*block.Block, ...BlockValidationOption) error + // Fork returns a new blockchain instance with the given tip hash + Fork(hash.Hash256) (Blockchain, error) // AddSubscriber make you listen to every single produced block AddSubscriber(BlockCreationSubscriber) error @@ -108,8 +110,12 @@ type ( // BlockBuilderFactory is the factory interface of block builder BlockBuilderFactory interface { - // NewBlockBuilder creates block builder - NewBlockBuilder(context.Context, func(action.Envelope) (*action.SealedEnvelope, error)) (*block.Builder, error) + Mint(ctx context.Context) (*block.Block, error) + ReceiveBlock(*block.Block) error + Init(hash.Hash256) + AddProposal(*block.Block) error + Block(hash.Hash256) *block.Block + BlockByHeight(uint64) *block.Block } // blockchain implements the Blockchain interface @@ -126,6 +132,9 @@ type ( // used by account-based model bbf BlockBuilderFactory + // head is the head block of the blockchain + head *block.Header + headLocker sync.RWMutex } ) @@ -208,7 +217,7 @@ func NewBlockchain(cfg Config, g genesis.Genesis, dao blockdao.BlockDAO, bbf Blo } chain.lifecycle.Add(chain.dao) chain.lifecycle.Add(chain.pubSubManager) - + chain.pubSubManager.AddBlockListener(chain.bbf) return chain } @@ -238,7 +247,20 @@ func (bc *blockchain) Start(ctx context.Context) error { EvmNetworkID: bc.EvmNetworkID(), }, ), bc.genesis)) - return bc.lifecycle.OnStart(ctx) + if err := bc.lifecycle.OnStart(ctx); err != nil { + return err + } + tip, err := bc.dao.Height() + if err != nil { + return err + } + header, err := bc.dao.HeaderByHeight(tip) + if err != nil { + return err + } + bc.head = header + bc.bbf.Init(header.HashBlock()) + return nil } // Stop stops the blockchain. @@ -249,7 +271,20 @@ func (bc *blockchain) Stop(ctx context.Context) error { } func (bc *blockchain) BlockHeaderByHeight(height uint64) (*block.Header, error) { - return bc.dao.HeaderByHeight(height) + header, err := bc.dao.HeaderByHeight(height) + switch errors.Cause(err) { + case nil: + return header, nil + case db.ErrNotExist: + bc.mu.RLock() + defer bc.mu.RUnlock() + if blk := bc.draftBlockByHeight(height); blk != nil { + return &blk.Header, nil + } + return nil, err + default: + return nil, err + } } func (bc *blockchain) BlockFooterByHeight(height uint64) (*block.Footer, error) { @@ -258,24 +293,12 @@ func (bc *blockchain) BlockFooterByHeight(height uint64) (*block.Footer, error) // TipHash returns tip block's hash func (bc *blockchain) TipHash() hash.Hash256 { - tipHeight, err := bc.dao.Height() - if err != nil { - return hash.ZeroHash256 - } - tipHash, err := bc.dao.GetBlockHash(tipHeight) - if err != nil { - return hash.ZeroHash256 - } - return tipHash + return bc.tipHash() } // TipHeight returns tip block's height func (bc *blockchain) TipHeight() uint64 { - tipHeight, err := bc.dao.Height() - if err != nil { - log.L().Panic("failed to get tip height", zap.Error(err)) - } - return tipHeight + return bc.tipHeight() } // ValidateBlock validates a new block before adding it to the blockchain @@ -287,10 +310,7 @@ func (bc *blockchain) ValidateBlock(blk *block.Block, opts ...BlockValidationOpt if blk == nil { return ErrInvalidBlock } - tipHeight, err := bc.dao.Height() - if err != nil { - return err - } + tipHeight := bc.tipHeight() tip, err := bc.tipInfo(tipHeight) if err != nil { return err @@ -355,7 +375,14 @@ func (bc *blockchain) ValidateBlock(blk *block.Block, opts ...BlockValidationOpt if bc.blockValidator == nil { return nil } - return bc.blockValidator.Validate(ctx, blk) + err = bc.blockValidator.Validate(ctx, blk) + if err != nil { + return err + } + if err = bc.bbf.AddProposal(blk); err != nil { + log.L().Warn("failed to add block to proposal pool", zap.Error(err), zap.Uint64("height", blk.Height()), zap.Time("timestamp", blk.Timestamp())) + } + return nil } func (bc *blockchain) Context(ctx context.Context) (context.Context, error) { @@ -392,7 +419,10 @@ func (bc *blockchain) context(ctx context.Context, height uint64) (context.Conte if err != nil { return nil, err } + return bc.contextWithTipInfo(ctx, tip), nil +} +func (bc *blockchain) contextWithTipInfo(ctx context.Context, tip *protocol.TipInfo) context.Context { ctx = genesis.WithGenesisContext( protocol.WithBlockchainCtx( ctx, @@ -404,7 +434,7 @@ func (bc *blockchain) context(ctx context.Context, height uint64) (context.Conte ), bc.genesis, ) - return protocol.WithFeatureWithHeightCtx(ctx), nil + return protocol.WithFeatureWithHeightCtx(ctx) } func (bc *blockchain) MintNewBlock(timestamp time.Time) (*block.Block, error) { @@ -422,30 +452,23 @@ func (bc *blockchain) MintNewBlock(timestamp time.Time) (*block.Block, error) { return nil, err } tip := protocol.MustGetBlockchainCtx(ctx).Tip + // create a new block + log.L().Debug("Produce a new block.", zap.Uint64("height", newblockHeight), zap.Time("timestamp", timestamp)) ctx = bc.contextWithBlock(ctx, bc.config.ProducerAddress(), newblockHeight, timestamp, protocol.CalcBaseFee(genesis.MustExtractGenesisContext(ctx).Blockchain, &tip), protocol.CalcExcessBlobGas(tip.ExcessBlobGas, tip.BlobGasUsed)) ctx = protocol.WithFeatureCtx(ctx) // run execution and update state trie root hash - minterPrivateKey := bc.config.ProducerPrivateKey() - blockBuilder, err := bc.bbf.NewBlockBuilder( - ctx, - func(elp action.Envelope) (*action.SealedEnvelope, error) { - return action.Sign(elp, minterPrivateKey) - }, - ) - if err != nil { - return nil, errors.Wrapf(err, "failed to create block builder at new block height %d", newblockHeight) - } - blk, err := blockBuilder.SignAndBuild(minterPrivateKey) + blk, err := bc.bbf.Mint(ctx) if err != nil { return nil, errors.Wrapf(err, "failed to create block") } _blockMtc.WithLabelValues("MintGas").Set(float64(blk.GasUsed())) _blockMtc.WithLabelValues("MintActions").Set(float64(len(blk.Actions))) - return &blk, nil + return blk, nil } // CommitBlock validates and appends a block to the chain func (bc *blockchain) CommitBlock(blk *block.Block) error { + log.L().Debug("Commit a block.", zap.Uint64("height", blk.Height()), zap.String("ioAddr", bc.config.ProducerAddress().String())) bc.mu.Lock() defer bc.mu.Unlock() timer := bc.timerFactory.NewTimer("CommitBlock") @@ -466,6 +489,31 @@ func (bc *blockchain) RemoveSubscriber(s BlockCreationSubscriber) error { return bc.pubSubManager.RemoveBlockListener(s) } +func (bc *blockchain) Fork(hash hash.Hash256) (Blockchain, error) { + if bc.tipHash() == hash { + return bc, nil + } + bc.mu.RLock() + defer bc.mu.RUnlock() + headBlk := bc.bbf.Block(hash) + if headBlk == nil { + return nil, errors.Errorf("block %x is not in the proposal pool, tip height %d", hash, bc.tipHeight()) + } + fork := &blockchain{ + dao: bc.dao, + config: bc.config, + genesis: bc.genesis, + blockValidator: bc.blockValidator, + lifecycle: bc.lifecycle, + clk: bc.clk, + pubSubManager: bc.pubSubManager, + timerFactory: bc.timerFactory, + bbf: bc.bbf, + head: &headBlk.Header, + } + return fork, nil +} + //====================================== // internal functions //===================================== @@ -486,11 +534,10 @@ func (bc *blockchain) tipInfo(tipHeight uint64) (*protocol.TipInfo, error) { Timestamp: time.Unix(bc.genesis.Timestamp, 0), }, nil } - header, err := bc.dao.HeaderByHeight(tipHeight) + header, err := bc.blockByHeight(tipHeight) if err != nil { return nil, err } - return &protocol.TipInfo{ Height: tipHeight, GasUsed: header.GasUsed(), @@ -526,6 +573,9 @@ func (bc *blockchain) commitBlock(blk *block.Block) error { default: return err } + bc.headLocker.Lock() + bc.head = &blk.Header + bc.headLocker.Unlock() blkHash := blk.HashBlock() if blk.Height()%100 == 0 { blk.HeaderLogger(log.L()).Info("Committed a block.", log.Hex("tipHash", blkHash[:])) @@ -548,3 +598,38 @@ func (bc *blockchain) emitToSubscribers(blk *block.Block) { } bc.pubSubManager.SendBlockToSubscribers(blk) } + +func (bc *blockchain) draftBlockByHeight(height uint64) *block.Block { + for blk := bc.bbf.Block(bc.tipHash()); blk != nil && blk.Height() > height; blk = bc.bbf.Block(blk.PrevHash()) { + if blk.Height() == height { + return blk + } + } + return nil +} + +func (bc *blockchain) blockByHeight(height uint64) (*block.Block, error) { + daoHeight, err := bc.dao.Height() + if err != nil { + return nil, err + } + if height > daoHeight { + if blk := bc.draftBlockByHeight(height); blk != nil { + return blk, nil + } + return nil, errors.Wrapf(db.ErrNotExist, "block %d not found", height) + } + return bc.dao.GetBlockByHeight(height) +} + +func (bc *blockchain) tipHeight() uint64 { + bc.headLocker.RLock() + defer bc.headLocker.RUnlock() + return bc.head.Height() +} + +func (bc *blockchain) tipHash() hash.Hash256 { + bc.headLocker.Lock() + defer bc.headLocker.Unlock() + return bc.head.HashBlock() +} diff --git a/blockchain/integrity/benchmark_test.go b/blockchain/integrity/benchmark_test.go index d74e80cb89..1ac1d73df8 100644 --- a/blockchain/integrity/benchmark_test.go +++ b/blockchain/integrity/benchmark_test.go @@ -273,7 +273,7 @@ func newChainInDB() (blockchain.Blockchain, actpool.ActPool, error) { cfg.Chain, cfg.Genesis, dao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator( sf, protocol.NewGenericValidator(sf, accountutil.AccountState), diff --git a/blockchain/integrity/integrity_test.go b/blockchain/integrity/integrity_test.go index 34f36f52dd..66c12d48b3 100644 --- a/blockchain/integrity/integrity_test.go +++ b/blockchain/integrity/integrity_test.go @@ -501,7 +501,7 @@ func TestCreateBlockchain(t *testing.T) { cfg.Chain, cfg.Genesis, dao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator( sf, protocol.NewGenericValidator(sf, accountutil.AccountState), @@ -557,7 +557,7 @@ func TestGetBlockHash(t *testing.T) { cfg.Chain, cfg.Genesis, dao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator( sf, protocol.NewGenericValidator(sf, accountutil.AccountState), @@ -722,7 +722,7 @@ func TestBlockchain_MintNewBlock(t *testing.T) { cfg.Chain, cfg.Genesis, dao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator( sf, protocol.NewGenericValidator(sf, accountutil.AccountState), @@ -792,7 +792,7 @@ func TestBlockchain_MintNewBlock_PopAccount(t *testing.T) { cfg.Chain, cfg.Genesis, dao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator( sf, protocol.NewGenericValidator(sf, accountutil.AccountState), @@ -935,7 +935,7 @@ func createChain(cfg config.Config, inMem bool) (blockchain.Blockchain, factory. cfg.Chain, cfg.Genesis, dao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator( sf, protocol.NewGenericValidator(sf, accountutil.AccountState), @@ -1480,7 +1480,7 @@ func TestConstantinople(t *testing.T) { cfg.Chain, cfg.Genesis, dao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator( sf, protocol.NewGenericValidator(sf, accountutil.AccountState), @@ -1578,7 +1578,7 @@ func TestConstantinople(t *testing.T) { blkHash, err := dao.GetBlockHash(v.height) require.NoError(err) - require.Equal(v.blkHash, hex.EncodeToString(blkHash[:])) + require.Equal(v.blkHash, hex.EncodeToString(blkHash[:]), "height %d", v.height) if v.topic != nil { funcSig := hash.Hash256b([]byte("Set(uint256)")) @@ -1690,6 +1690,7 @@ func TestConstantinople(t *testing.T) { cfg.Genesis.BeringBlockHeight = 8 cfg.Genesis.GreenlandBlockHeight = 9 cfg.Genesis.InitBalanceMap[identityset.Address(27).String()] = unit.ConvertIotxToRau(10000000000).String() + block.LoadGenesisHash(&cfg.Genesis) t.Run("test Constantinople contract", func(t *testing.T) { testValidateBlockchain(cfg, t) @@ -1734,7 +1735,7 @@ func TestLoadBlockchainfromDB(t *testing.T) { cfg.Chain, cfg.Genesis, dao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator( sf, protocol.NewGenericValidator(sf, accountutil.AccountState), @@ -1763,7 +1764,7 @@ func TestLoadBlockchainfromDB(t *testing.T) { cfg.Chain, cfg.Genesis, dao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator( sf, protocol.NewGenericValidator(sf, accountutil.AccountState), @@ -2044,7 +2045,7 @@ func TestBlockchainInitialCandidate(t *testing.T) { cfg.Chain, cfg.Genesis, dao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(sf), ) rolldposProtocol := rolldpos.NewProtocol( @@ -2087,7 +2088,7 @@ func TestBlockchain_AccountState(t *testing.T) { store, err := filedao.NewFileDAOInMemForTest() require.NoError(err) dao := blockdao.NewBlockDAOWithIndexersAndCache(store, []blockdao.BlockIndexer{sf}, cfg.DB.MaxCacheSize) - bc := blockchain.NewBlockchain(cfg.Chain, cfg.Genesis, dao, factory.NewMinter(sf, ap)) + bc := blockchain.NewBlockchain(cfg.Chain, cfg.Genesis, dao, factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey()))) require.NoError(bc.Start(ctx)) require.NotNil(bc) defer func() { @@ -2119,7 +2120,7 @@ func TestNewAccountAction(t *testing.T) { store, err := filedao.NewFileDAOInMemForTest() require.NoError(err) dao := blockdao.NewBlockDAOWithIndexersAndCache(store, []blockdao.BlockIndexer{sf}, cfg.DB.MaxCacheSize) - bc := blockchain.NewBlockchain(cfg.Chain, cfg.Genesis, dao, factory.NewMinter(sf, ap)) + bc := blockchain.NewBlockchain(cfg.Chain, cfg.Genesis, dao, factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey()))) require.NoError(bc.Start(ctx)) require.NotNil(bc) defer func() { @@ -2164,7 +2165,7 @@ func TestNewAccountAction(t *testing.T) { store, err := filedao.NewFileDAOInMemForTest() require.NoError(err) dao1 := blockdao.NewBlockDAOWithIndexersAndCache(store, []blockdao.BlockIndexer{sf1}, cfg.DB.MaxCacheSize) - bc1 := blockchain.NewBlockchain(cfg.Chain, cfg.Genesis, dao1, factory.NewMinter(sf1, ap)) + bc1 := blockchain.NewBlockchain(cfg.Chain, cfg.Genesis, dao1, factory.NewMinter(sf1, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey()))) require.NoError(bc1.Start(ctx)) require.NotNil(bc1) defer func() { @@ -2233,7 +2234,7 @@ func TestBlocks(t *testing.T) { dao := blockdao.NewBlockDAOWithIndexersAndCache(store, []blockdao.BlockIndexer{sf}, dbcfg.MaxCacheSize) // Create a blockchain from scratch - bc := blockchain.NewBlockchain(cfg.Chain, cfg.Genesis, dao, factory.NewMinter(sf, ap)) + bc := blockchain.NewBlockchain(cfg.Chain, cfg.Genesis, dao, factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey()))) require.NoError(bc.Start(context.Background())) defer func() { require.NoError(bc.Stop(context.Background())) @@ -2311,7 +2312,7 @@ func TestActions(t *testing.T) { cfg.Chain, cfg.Genesis, dao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator( sf, protocol.NewGenericValidator(sf, accountutil.AccountState), @@ -2371,7 +2372,7 @@ func TestBlockchain_AddRemoveSubscriber(t *testing.T) { store, err := filedao.NewFileDAOInMemForTest() req.NoError(err) dao := blockdao.NewBlockDAOWithIndexersAndCache(store, []blockdao.BlockIndexer{sf}, cfg.DB.MaxCacheSize) - bc := blockchain.NewBlockchain(cfg.Chain, cfg.Genesis, dao, factory.NewMinter(sf, ap)) + bc := blockchain.NewBlockchain(cfg.Chain, cfg.Genesis, dao, factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey()))) // mock ctrl := gomock.NewController(t) mb := mock_blockcreationsubscriber.NewMockBlockCreationSubscriber(ctrl) @@ -2606,7 +2607,7 @@ func newChain(t *testing.T, stateTX bool) (blockchain.Blockchain, factory.Factor cfg.Chain, cfg.Genesis, dao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator( sf, protocol.NewGenericValidator(sf, accountutil.AccountState), diff --git a/blocksync/blocksync_test.go b/blocksync/blocksync_test.go index 26808a22b9..d5edebde28 100644 --- a/blocksync/blocksync_test.go +++ b/blocksync/blocksync_test.go @@ -208,7 +208,7 @@ func TestBlockSyncerProcessBlockTipHeight(t *testing.T) { cfg.Chain, cfg.Genesis, dao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator(sf, ap)), ) require.NoError(chain.Start(ctx)) @@ -274,7 +274,7 @@ func TestBlockSyncerProcessBlockOutOfOrder(t *testing.T) { cfg.Chain, cfg.Genesis, dao, - factory.NewMinter(sf, ap1), + factory.NewMinter(sf, ap1, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator(sf, ap1)), ) require.NotNil(chain1) @@ -301,7 +301,7 @@ func TestBlockSyncerProcessBlockOutOfOrder(t *testing.T) { cfg.Chain, cfg.Genesis, dao2, - factory.NewMinter(sf2, ap2), + factory.NewMinter(sf2, ap2, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator(sf2, ap2)), ) require.NotNil(chain2) @@ -376,7 +376,7 @@ func TestBlockSyncerProcessBlock(t *testing.T) { cfg.Chain, cfg.Genesis, dao, - factory.NewMinter(sf, ap1), + factory.NewMinter(sf, ap1, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator(sf, ap1)), ) require.NoError(chain1.Start(ctx)) @@ -402,7 +402,7 @@ func TestBlockSyncerProcessBlock(t *testing.T) { cfg.Chain, cfg.Genesis, dao2, - factory.NewMinter(sf2, ap2), + factory.NewMinter(sf2, ap2, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator(sf2, ap2)), ) require.NoError(chain2.Start(ctx)) @@ -470,7 +470,7 @@ func TestBlockSyncerSync(t *testing.T) { cfg.Chain, cfg.Genesis, dao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator(sf, ap)), ) require.NoError(chain.Start(ctx)) @@ -523,7 +523,7 @@ func newTestConfig() (testConfig, error) { cfg := testConfig{ BlockSync: DefaultConfig, - Genesis: genesis.TestDefault(), + Genesis: genesis.Default, Chain: blockchain.DefaultConfig, ActPool: actpool.DefaultConfig, } diff --git a/blocksync/buffer_test.go b/blocksync/buffer_test.go index 0d05f1d695..03674c9a13 100644 --- a/blocksync/buffer_test.go +++ b/blocksync/buffer_test.go @@ -55,7 +55,7 @@ func TestBlockBufferFlush(t *testing.T) { cfg.Chain, cfg.Genesis, dao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator(sf, ap)), ) require.NoError(chain.Start(ctx)) @@ -149,7 +149,7 @@ func TestBlockBufferGetBlocksIntervalsToSync(t *testing.T) { cfg.Chain, cfg.Genesis, dao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), ) require.NotNil(chain) require.NoError(chain.Start(ctx)) diff --git a/chainservice/builder.go b/chainservice/builder.go index 65a0d531a7..d457fe29e3 100644 --- a/chainservice/builder.go +++ b/chainservice/builder.go @@ -514,7 +514,7 @@ func (builder *Builder) createBlockchain(forSubChain, forTest bool) blockchain.B chainOpts = append(chainOpts, blockchain.BlockValidatorOption(builder.cs.factory)) } - var mintOpts []factory.MintOption + mintOpts := []factory.MintOption{factory.WithPrivateKeyOption(builder.cfg.Chain.ProducerPrivateKey())} if builder.cfg.Consensus.Scheme == config.RollDPoSScheme { mintOpts = append(mintOpts, factory.WithTimeoutOption(builder.cfg.Chain.MintTimeout)) } @@ -785,7 +785,14 @@ func (builder *Builder) registerRollDPoSProtocol() error { func (builder *Builder) buildBlockTimeCalculator() (err error) { consensusCfg := consensusfsm.NewConsensusConfig(builder.cfg.Consensus.RollDPoS.FSM, builder.cfg.DardanellesUpgrade, builder.cfg.Genesis, builder.cfg.Consensus.RollDPoS.Delay) dao := builder.cs.BlockDAO() - builder.cs.blockTimeCalculator, err = blockutil.NewBlockTimeCalculator(consensusCfg.BlockInterval, builder.cs.Blockchain().TipHeight, func(height uint64) (time.Time, error) { + builder.cs.blockTimeCalculator, err = blockutil.NewBlockTimeCalculator(consensusCfg.BlockInterval, func() uint64 { + tip, err := dao.Height() + if err != nil { + log.L().Error("failed to get tip height", zap.Error(err)) + return 0 + } + return tip + }, func(height uint64) (time.Time, error) { blk, err := dao.GetBlockByHeight(height) if err != nil { return time.Time{}, err diff --git a/consensus/scheme/rolldpos/rolldpos_test.go b/consensus/scheme/rolldpos/rolldpos_test.go index d93db94f62..42edbf8b7e 100644 --- a/consensus/scheme/rolldpos/rolldpos_test.go +++ b/consensus/scheme/rolldpos/rolldpos_test.go @@ -468,7 +468,7 @@ func TestRollDPoSConsensus(t *testing.T) { bc, g, dao, - factory.NewMinter(sf, actPool), + factory.NewMinter(sf, actPool, factory.WithPrivateKeyOption(chainAddrs[i].priKey)), blockchain.BlockValidatorOption(block.NewValidator( sf, protocol.NewGenericValidator(sf, accountutil.AccountState), diff --git a/consensus/scheme/rolldpos/roundcalculator_test.go b/consensus/scheme/rolldpos/roundcalculator_test.go index 99cc8726da..9da3c5c54f 100644 --- a/consensus/scheme/rolldpos/roundcalculator_test.go +++ b/consensus/scheme/rolldpos/roundcalculator_test.go @@ -193,7 +193,7 @@ func makeChain(t *testing.T) (blockchain.Blockchain, factory.Factory, actpool.Ac cfg, g, dao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(sk)), blockchain.BlockValidatorOption(block.NewValidator( sf, protocol.NewGenericValidator(sf, accountutil.AccountState), diff --git a/e2etest/bigint_test.go b/e2etest/bigint_test.go index 9148a7ff4f..b9c99e1fa2 100644 --- a/e2etest/bigint_test.go +++ b/e2etest/bigint_test.go @@ -106,7 +106,7 @@ func prepareBlockchain(ctx context.Context, _executor string, r *require.Asserti cfg.Chain, cfg.Genesis, dao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator( sf, genericValidator, diff --git a/e2etest/contract_staking_test.go b/e2etest/contract_staking_test.go index 86bc4552e0..ec8fa92df0 100644 --- a/e2etest/contract_staking_test.go +++ b/e2etest/contract_staking_test.go @@ -1984,7 +1984,7 @@ func prepareContractStakingBlockchain(ctx context.Context, cfg config.Config, r cfg.Chain, cfg.Genesis, dao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator( sf, protocol.NewGenericValidator(sf, accountutil.AccountState), diff --git a/e2etest/local_test.go b/e2etest/local_test.go index 7ffa74d865..93f8cbfb83 100644 --- a/e2etest/local_test.go +++ b/e2etest/local_test.go @@ -167,7 +167,7 @@ func TestLocalCommit(t *testing.T) { cfg.Chain, cfg.Genesis, dao, - factory.NewMinter(sf2, ap2), + factory.NewMinter(sf2, ap2, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator( sf2, protocol.NewGenericValidator(sf2, accountutil.AccountState), diff --git a/e2etest/local_transfer_test.go b/e2etest/local_transfer_test.go index db1f466ad0..762b6ec75b 100644 --- a/e2etest/local_transfer_test.go +++ b/e2etest/local_transfer_test.go @@ -679,7 +679,7 @@ func TestEnforceChainID(t *testing.T) { cfg.Chain, cfg.Genesis, blkMemDao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), ) require.NoError(bc.Start(ctx)) diff --git a/gasstation/gasstattion_test.go b/gasstation/gasstattion_test.go index 9780c190de..f840ec3782 100644 --- a/gasstation/gasstattion_test.go +++ b/gasstation/gasstattion_test.go @@ -93,7 +93,7 @@ func TestSuggestGasPriceForUserAction(t *testing.T) { cfg.Chain, cfg.Genesis, blkMemDao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator( sf, protocol.NewGenericValidator(sf, accountutil.AccountState), @@ -164,7 +164,7 @@ func TestSuggestGasPriceForSystemAction(t *testing.T) { cfg.Chain, cfg.Genesis, blkMemDao, - factory.NewMinter(sf, ap), + factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())), blockchain.BlockValidatorOption(block.NewValidator( sf, protocol.NewGenericValidator(sf, accountutil.AccountState), diff --git a/state/factory/blockpreparer.go b/state/factory/blockpreparer.go new file mode 100644 index 0000000000..61399c40d0 --- /dev/null +++ b/state/factory/blockpreparer.go @@ -0,0 +1,85 @@ +package factory + +import ( + "context" + "sync" + "time" + + "github.com/iotexproject/go-pkgs/hash" + "github.com/pkg/errors" + "go.uber.org/zap" + + "github.com/iotexproject/iotex-core/v2/blockchain/block" + "github.com/iotexproject/iotex-core/v2/pkg/log" +) + +type ( + blockPreparer struct { + tasks map[hash.Hash256]map[int64]chan struct{} + results map[hash.Hash256]map[int64]*mintResult + mu sync.Mutex + } + mintResult struct { + blk *block.Block + err error + } +) + +func newBlockPreparer() *blockPreparer { + return &blockPreparer{ + tasks: make(map[hash.Hash256]map[int64]chan struct{}), + results: make(map[hash.Hash256]map[int64]*mintResult), + } +} + +func (d *blockPreparer) PrepareOrWait(ctx context.Context, prevHash []byte, timestamp time.Time, fn func() (*block.Block, error)) (*block.Block, error) { + d.mu.Lock() + task := d.prepare(prevHash, timestamp, fn) + d.mu.Unlock() + + select { + case <-task: + case <-ctx.Done(): + var null *block.Block + return null, errors.Wrapf(ctx.Err(), "wait for draft block timeout %v", timestamp) + } + + d.mu.Lock() + res := d.results[hash.Hash256(prevHash)][timestamp.UnixNano()] + d.mu.Unlock() + return res.blk, res.err +} + +func (d *blockPreparer) prepare(prevHash []byte, timestamp time.Time, mintFn func() (*block.Block, error)) chan struct{} { + if forks, ok := d.tasks[hash.BytesToHash256(prevHash)]; ok { + if ch, ok := forks[timestamp.UnixNano()]; ok { + log.L().Debug("draft block already exists", log.Hex("prevHash", prevHash)) + return ch + } + } else { + d.tasks[hash.BytesToHash256(prevHash)] = make(map[int64]chan struct{}) + } + task := make(chan struct{}) + d.tasks[hash.BytesToHash256(prevHash)][timestamp.UnixNano()] = task + + go func() { + blk, err := mintFn() + d.mu.Lock() + if _, ok := d.results[hash.BytesToHash256(prevHash)]; !ok { + d.results[hash.BytesToHash256(prevHash)] = make(map[int64]*mintResult) + } + d.results[hash.BytesToHash256(prevHash)][timestamp.UnixNano()] = &mintResult{blk: blk, err: err} + d.mu.Unlock() + close(task) + log.L().Debug("prepare mint returned", zap.Error(err)) + }() + + return task +} + +func (d *blockPreparer) ReceiveBlock(blk *block.Block) error { + d.mu.Lock() + delete(d.tasks, blk.PrevHash()) + d.mu.Unlock() + return nil +} diff --git a/state/factory/minter.go b/state/factory/minter.go index ef5b301d64..681976b583 100644 --- a/state/factory/minter.go +++ b/state/factory/minter.go @@ -7,13 +7,20 @@ package factory import ( "context" + "sync" "time" + "go.uber.org/zap" + + "github.com/iotexproject/go-pkgs/crypto" + "github.com/iotexproject/go-pkgs/hash" + "github.com/iotexproject/iotex-core/v2/action" "github.com/iotexproject/iotex-core/v2/action/protocol" "github.com/iotexproject/iotex-core/v2/actpool" "github.com/iotexproject/iotex-core/v2/blockchain" "github.com/iotexproject/iotex-core/v2/blockchain/block" + "github.com/iotexproject/iotex-core/v2/pkg/log" ) type MintOption func(*minter) @@ -25,26 +32,113 @@ func WithTimeoutOption(timeout time.Duration) MintOption { } } +func WithPrivateKeyOption(sk crypto.PrivateKey) MintOption { + return func(m *minter) { + m.sk = sk + } +} + type minter struct { - f Factory - ap actpool.ActPool - timeout time.Duration + f Factory + ap actpool.ActPool + timeout time.Duration + blockPreparer *blockPreparer + proposalPool *proposalPool + sk crypto.PrivateKey + mu sync.Mutex } // NewMinter creates a wrapper instance func NewMinter(f Factory, ap actpool.ActPool, opts ...MintOption) blockchain.BlockBuilderFactory { - m := &minter{f: f, ap: ap} + m := &minter{ + f: f, + ap: ap, + blockPreparer: newBlockPreparer(), + proposalPool: newProposalPool(), + } for _, opt := range opts { opt(m) } return m } -// NewBlockBuilder implements the BlockMinter interface -func (m *minter) NewBlockBuilder(ctx context.Context, sign func(action.Envelope) (*action.SealedEnvelope, error)) (*block.Builder, error) { +func (m *minter) Init(root hash.Hash256) { + m.proposalPool.Init(root) +} + +func (m *minter) Mint(ctx context.Context) (*block.Block, error) { + bcCtx := protocol.MustGetBlockchainCtx(ctx) + blkCtx := protocol.MustGetBlockCtx(ctx) + + // create a new block + blk, err := m.blockPreparer.PrepareOrWait(ctx, bcCtx.Tip.Hash[:], blkCtx.BlockTimeStamp, func() (*block.Block, error) { + blk, err := m.mint(ctx) + if err != nil { + return nil, err + } + if err = m.proposalPool.AddBlock(blk); err != nil { + log.L().Error("failed to add block to proposal pool", zap.Error(err)) + } + return blk, nil + }) + if err != nil { + return nil, err + } + return blk, nil +} + +func (m *minter) AddProposal(blk *block.Block) error { + return m.proposalPool.AddBlock(blk) +} + +func (m *minter) ReceiveBlock(blk *block.Block) error { + prevHash := blk.PrevHash() + l := log.L().With(zap.Uint64("height", blk.Height()), log.Hex("prevHash", prevHash[:]), zap.Time("timestamp", blk.Timestamp())) + if err := m.blockPreparer.ReceiveBlock(blk); err != nil { + l.Error("failed to receive block", zap.Error(err)) + } + if err := m.proposalPool.ReceiveBlock(blk); err != nil { + l.Error("failed to receive block", zap.Error(err)) + } + return nil +} + +func (m *minter) Block(hash hash.Hash256) *block.Block { + return m.proposalPool.BlockByHash(hash) +} + +func (m *minter) BlockByHeight(height uint64) *block.Block { + return m.proposalPool.Block(height) +} + +func (m *minter) mint(ctx context.Context) (*block.Block, error) { + builder, err := m.newBlockBuilder(ctx, func(e action.Envelope) (*action.SealedEnvelope, error) { + return action.Sign(e, m.sk) + }) + if err != nil { + return nil, err + } + blk, err := builder.SignAndBuild(m.sk) + if err != nil { + return nil, err + } + return &blk, nil +} + +func (m *minter) newBlockBuilder(ctx context.Context, sign func(action.Envelope) (*action.SealedEnvelope, error)) (*block.Builder, error) { if m.timeout > 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithDeadline(ctx, protocol.MustGetBlockCtx(ctx).BlockTimeStamp.Add(m.timeout)) + // set deadline for NewBlockBuilder + // ensure that minting finishes before `block start time + timeout` and that its duration does not exceed the timeout. + var ( + cancel context.CancelFunc + now = time.Now() + blkTs = protocol.MustGetBlockCtx(ctx).BlockTimeStamp + ddl = blkTs.Add(m.timeout) + ) + if now.Before(blkTs) { + ddl = now.Add(m.timeout) + } + ctx, cancel = context.WithDeadline(ctx, ddl) defer cancel() } return m.f.NewBlockBuilder(ctx, m.ap, sign) diff --git a/state/factory/proposalpool.go b/state/factory/proposalpool.go new file mode 100644 index 0000000000..94b527937c --- /dev/null +++ b/state/factory/proposalpool.go @@ -0,0 +1,148 @@ +package factory + +import ( + "sync" + "time" + + "github.com/iotexproject/go-pkgs/hash" + "github.com/pkg/errors" + "go.uber.org/zap" + + "github.com/iotexproject/iotex-core/v2/blockchain/block" + "github.com/iotexproject/iotex-core/v2/pkg/log" +) + +// proposalPool is a pool of draft blocks +type proposalPool struct { + // blocks is a map of draft blocks + // key is the hash of the block + blocks map[hash.Hash256]*block.Block + // forks is a map of draft blocks that are forks + // key is the hash of the tip block of the fork + // value is the timestamp of the block + forks map[hash.Hash256]time.Time + // root is the hash of the tip block of the blockchain + root hash.Hash256 + mu sync.Mutex +} + +func newProposalPool() *proposalPool { + return &proposalPool{ + blocks: make(map[hash.Hash256]*block.Block), + forks: make(map[hash.Hash256]time.Time), + } +} + +func (d *proposalPool) Init(root hash.Hash256) { + d.mu.Lock() + defer d.mu.Unlock() + d.root = root + log.L().Info("proposal pool initialized", log.Hex("root", root[:])) +} + +// AddBlock adds a block to the draft pool +func (d *proposalPool) AddBlock(blk *block.Block) error { + d.mu.Lock() + defer d.mu.Unlock() + // nothing to do if the block already exists + hash := blk.HashBlock() + if _, ok := d.blocks[hash]; ok { + return nil + } + // it must be a new tip of any fork, or make a new fork + prevHash := blk.PrevHash() + if _, ok := d.forks[prevHash]; ok { + delete(d.forks, prevHash) + } else if prevHash != d.root { + return errors.Errorf("block %x is not a tip of any fork", prevHash[:]) + } + d.forks[hash] = blk.Timestamp() + d.blocks[hash] = blk + log.L().Info("added block to draft pool", log.Hex("hash", hash[:]), zap.Uint64("height", blk.Height()), zap.Time("timestamp", blk.Timestamp())) + return nil +} + +// ReceiveBlock a block has been confirmed and remove all the blocks that is previous to it, or other forks +func (d *proposalPool) ReceiveBlock(blk *block.Block) error { + d.mu.Lock() + defer d.mu.Unlock() + + // find the fork that contains this block + fork, err := d.forkAt(blk) + if err != nil { + return err + } + + // remove blocks in other forks or older blocks in the same fork + for f := range d.forks { + start := d.blocks[f] + if f == fork { + start = blk + } + for b := start; b != nil; b = d.blocks[b.PrevHash()] { + ha := b.HashBlock() + log.L().Info("remove block from draft pool", log.Hex("hash", ha[:]), zap.Uint64("height", b.Height()), zap.Time("timestamp", b.Timestamp())) + delete(d.blocks, b.HashBlock()) + } + } + // reset forks to only this one + if forkTime, ok := d.forks[fork]; ok && d.blocks[fork] != nil { + d.forks = map[hash.Hash256]time.Time{fork: forkTime} + } else { + d.forks = map[hash.Hash256]time.Time{} + } + d.root = blk.HashBlock() + return nil +} + +// Block returns the latest block at the given height +func (d *proposalPool) Block(height uint64) *block.Block { + d.mu.Lock() + defer d.mu.Unlock() + var blk *block.Block + for _, b := range d.blocks { + if b.Height() != height { + continue + } else if blk == nil { + blk = b + } else if b.Timestamp().After(blk.Timestamp()) { + blk = b + } + } + return blk +} + +// BlockByHash returns the block by hash +func (d *proposalPool) BlockByHash(hash hash.Hash256) *block.Block { + d.mu.Lock() + defer d.mu.Unlock() + return d.blocks[hash] +} + +func (d *proposalPool) BlockByTime(prevHash hash.Hash256, timestamp time.Time) *block.Block { + d.mu.Lock() + defer d.mu.Unlock() + for _, b := range d.blocks { + if b.PrevHash() == prevHash && b.Timestamp().Equal(timestamp) { + return b + } + } + return nil +} + +func (d *proposalPool) forkAt(blk *block.Block) (hash.Hash256, error) { + blkHash := blk.HashBlock() + // If this block isn't in the pool, just return it + if _, ok := d.blocks[blkHash]; !ok { + return blkHash, nil + } + // Otherwise, find which fork chain contains it + for forkTip := range d.forks { + for b := d.blocks[forkTip]; b != nil; b = d.blocks[b.PrevHash()] { + if blkHash == b.HashBlock() { + return forkTip, nil + } + } + } + return hash.ZeroHash256, errors.Errorf("block %x exists in the draft pool but not in the fork pool, this should not happen", blkHash[:]) +} diff --git a/test/mock/mock_blockchain/mock_blockchain.go b/test/mock/mock_blockchain/mock_blockchain.go index 42b5a75e15..19e397a37f 100644 --- a/test/mock/mock_blockchain/mock_blockchain.go +++ b/test/mock/mock_blockchain/mock_blockchain.go @@ -11,7 +11,6 @@ import ( gomock "github.com/golang/mock/gomock" hash "github.com/iotexproject/go-pkgs/hash" - action "github.com/iotexproject/iotex-core/v2/action" blockchain "github.com/iotexproject/iotex-core/v2/blockchain" block "github.com/iotexproject/iotex-core/v2/blockchain/block" genesis "github.com/iotexproject/iotex-core/v2/blockchain/genesis" @@ -170,6 +169,21 @@ func (mr *MockBlockchainMockRecorder) EvmNetworkID() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EvmNetworkID", reflect.TypeOf((*MockBlockchain)(nil).EvmNetworkID)) } +// Fork mocks base method. +func (m *MockBlockchain) Fork(arg0 hash.Hash256) (blockchain.Blockchain, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Fork", arg0) + ret0, _ := ret[0].(blockchain.Blockchain) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Fork indicates an expected call of Fork. +func (mr *MockBlockchainMockRecorder) Fork(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Fork", reflect.TypeOf((*MockBlockchain)(nil).Fork), arg0) +} + // Genesis mocks base method. func (m *MockBlockchain) Genesis() genesis.Genesis { m.ctrl.T.Helper() @@ -311,17 +325,85 @@ func (m *MockBlockBuilderFactory) EXPECT() *MockBlockBuilderFactoryMockRecorder return m.recorder } -// NewBlockBuilder mocks base method. -func (m *MockBlockBuilderFactory) NewBlockBuilder(arg0 context.Context, arg1 func(action.Envelope) (*action.SealedEnvelope, error)) (*block.Builder, error) { +// AddProposal mocks base method. +func (m *MockBlockBuilderFactory) AddProposal(arg0 *block.Block) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AddProposal", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// AddProposal indicates an expected call of AddProposal. +func (mr *MockBlockBuilderFactoryMockRecorder) AddProposal(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddProposal", reflect.TypeOf((*MockBlockBuilderFactory)(nil).AddProposal), arg0) +} + +// Block mocks base method. +func (m *MockBlockBuilderFactory) Block(arg0 hash.Hash256) *block.Block { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Block", arg0) + ret0, _ := ret[0].(*block.Block) + return ret0 +} + +// Block indicates an expected call of Block. +func (mr *MockBlockBuilderFactoryMockRecorder) Block(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Block", reflect.TypeOf((*MockBlockBuilderFactory)(nil).Block), arg0) +} + +// BlockByHeight mocks base method. +func (m *MockBlockBuilderFactory) BlockByHeight(arg0 uint64) *block.Block { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NewBlockBuilder", arg0, arg1) - ret0, _ := ret[0].(*block.Builder) + ret := m.ctrl.Call(m, "BlockByHeight", arg0) + ret0, _ := ret[0].(*block.Block) + return ret0 +} + +// BlockByHeight indicates an expected call of BlockByHeight. +func (mr *MockBlockBuilderFactoryMockRecorder) BlockByHeight(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BlockByHeight", reflect.TypeOf((*MockBlockBuilderFactory)(nil).BlockByHeight), arg0) +} + +// Init mocks base method. +func (m *MockBlockBuilderFactory) Init(arg0 hash.Hash256) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Init", arg0) +} + +// Init indicates an expected call of Init. +func (mr *MockBlockBuilderFactoryMockRecorder) Init(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockBlockBuilderFactory)(nil).Init), arg0) +} + +// Mint mocks base method. +func (m *MockBlockBuilderFactory) Mint(ctx context.Context) (*block.Block, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Mint", ctx) + ret0, _ := ret[0].(*block.Block) ret1, _ := ret[1].(error) return ret0, ret1 } -// NewBlockBuilder indicates an expected call of NewBlockBuilder. -func (mr *MockBlockBuilderFactoryMockRecorder) NewBlockBuilder(arg0, arg1 interface{}) *gomock.Call { +// Mint indicates an expected call of Mint. +func (mr *MockBlockBuilderFactoryMockRecorder) Mint(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Mint", reflect.TypeOf((*MockBlockBuilderFactory)(nil).Mint), ctx) +} + +// ReceiveBlock mocks base method. +func (m *MockBlockBuilderFactory) ReceiveBlock(arg0 *block.Block) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReceiveBlock", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// ReceiveBlock indicates an expected call of ReceiveBlock. +func (mr *MockBlockBuilderFactoryMockRecorder) ReceiveBlock(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewBlockBuilder", reflect.TypeOf((*MockBlockBuilderFactory)(nil).NewBlockBuilder), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceiveBlock", reflect.TypeOf((*MockBlockBuilderFactory)(nil).ReceiveBlock), arg0) }