diff --git a/blockchain/config.go b/blockchain/config.go index be30b04b89..8993e92adc 100644 --- a/blockchain/config.go +++ b/blockchain/config.go @@ -76,6 +76,8 @@ type ( PersistStakingPatchBlock uint64 `yaml:"persistStakingPatchBlock"` // FactoryDBType is the type of factory db FactoryDBType string `yaml:"factoryDBType"` + // MintTimeout is the timeout for minting + MintTimeout time.Duration `yaml:"-"` } ) @@ -119,6 +121,7 @@ var ( StreamingBlockBufferSize: 200, PersistStakingPatchBlock: 19778037, FactoryDBType: db.DBBolt, + MintTimeout: 1500 * time.Millisecond, // valued with block accept ttl - 500ms(tolerate network delay) } // ErrConfig config error diff --git a/chainservice/builder.go b/chainservice/builder.go index bf6c589a45..69664e37f9 100644 --- a/chainservice/builder.go +++ b/chainservice/builder.go @@ -506,7 +506,11 @@ func (builder *Builder) createBlockchain(forSubChain, forTest bool) blockchain.B chainOpts = append(chainOpts, blockchain.BlockValidatorOption(builder.cs.factory)) } - return blockchain.NewBlockchain(builder.cfg.Chain, builder.cfg.Genesis, builder.cs.blockdao, factory.NewMinter(builder.cs.factory, builder.cs.actpool), chainOpts...) + var mintOpts []factory.MintOption + if builder.cfg.Consensus.Scheme == config.RollDPoSScheme { + mintOpts = append(mintOpts, factory.WithTimeoutOption(builder.cfg.Chain.MintTimeout)) + } + return blockchain.NewBlockchain(builder.cfg.Chain, builder.cfg.Genesis, builder.cs.blockdao, factory.NewMinter(builder.cs.factory, builder.cs.actpool, mintOpts...), chainOpts...) } func (builder *Builder) buildNodeInfoManager() error { diff --git a/config/config.go b/config/config.go index d9d4b71647..7a50fd5095 100644 --- a/config/config.go +++ b/config/config.go @@ -167,6 +167,8 @@ func New(configPaths []string, _plugins []string, validates ...Validate) (Config if err := cfg.Chain.SetProducerPrivKey(); err != nil { return Config{}, errors.Wrap(err, "failed to set producer private key") } + // set default value for mint timeout + cfg.Chain.MintTimeout = Default.Chain.MintTimeout // set network master key to private key if cfg.Network.MasterKey == "" { diff --git a/e2etest/local_test.go b/e2etest/local_test.go index ab669efa4f..b73201ccc7 100644 --- a/e2etest/local_test.go +++ b/e2etest/local_test.go @@ -13,6 +13,7 @@ import ( "time" "github.com/libp2p/go-libp2p/core/peer" + "github.com/mohae/deepcopy" "github.com/multiformats/go-multiaddr" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" @@ -548,10 +549,11 @@ func TestStartExistingBlockchain(t *testing.T) { } func newTestConfig() (config.Config, error) { - cfg := config.Default + cfg := deepcopy.Copy(config.Default).(config.Config) cfg.Chain.TrieDBPath = _triePath cfg.Chain.ChainDBPath = _dBPath cfg.Chain.BlobStoreDBPath = _blobPath + cfg.Chain.MintTimeout = 0 cfg.ActPool.MinGasPriceStr = "0" cfg.Consensus.Scheme = config.NOOPScheme cfg.Network.Port = testutil.RandomPort() diff --git a/e2etest/staking_test.go b/e2etest/staking_test.go index 3fc4680145..ea058bb457 100644 --- a/e2etest/staking_test.go +++ b/e2etest/staking_test.go @@ -15,6 +15,7 @@ import ( "time" "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/mohae/deepcopy" "github.com/stretchr/testify/require" "github.com/iotexproject/go-pkgs/hash" @@ -184,7 +185,7 @@ func TestStakingContract(t *testing.T) { } } - cfg := config.Default + cfg := deepcopy.Copy(config.Default).(config.Config) initDBPaths(require, &cfg) defer func() { @@ -196,6 +197,7 @@ func TestStakingContract(t *testing.T) { cfg.ActPool.MinGasPriceStr = "0" cfg.Chain.TrieDBPatchFile = "" cfg.Chain.ProducerPrivKey = "a000000000000000000000000000000000000000000000000000000000000000" + cfg.Chain.MintTimeout = 0 cfg.Consensus.Scheme = config.RollDPoSScheme cfg.Genesis.NumDelegates = 1 cfg.Genesis.NumSubEpochs = 10 diff --git a/state/factory/minter.go b/state/factory/minter.go index cbed4859e8..ef5b301d64 100644 --- a/state/factory/minter.go +++ b/state/factory/minter.go @@ -7,24 +7,45 @@ package factory import ( "context" + "time" "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" ) +type MintOption func(*minter) + +// WithTimeoutOption sets the timeout for NewBlockBuilder +func WithTimeoutOption(timeout time.Duration) MintOption { + return func(m *minter) { + m.timeout = timeout + } +} + type minter struct { - f Factory - ap actpool.ActPool + f Factory + ap actpool.ActPool + timeout time.Duration } // NewMinter creates a wrapper instance -func NewMinter(f Factory, ap actpool.ActPool) blockchain.BlockBuilderFactory { - return &minter{f: f, ap: ap} +func NewMinter(f Factory, ap actpool.ActPool, opts ...MintOption) blockchain.BlockBuilderFactory { + m := &minter{f: f, ap: ap} + 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) { + if m.timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithDeadline(ctx, protocol.MustGetBlockCtx(ctx).BlockTimeStamp.Add(m.timeout)) + defer cancel() + } return m.f.NewBlockBuilder(ctx, m.ap, sign) } diff --git a/state/factory/workingset.go b/state/factory/workingset.go index d4c258285b..b5c2bc273f 100644 --- a/state/factory/workingset.go +++ b/state/factory/workingset.go @@ -9,6 +9,7 @@ import ( "context" "math/big" "sort" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/params" @@ -40,6 +41,13 @@ var ( }, []string{"type"}, ) + _mintAbility = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "iotex_mint_ability", + Help: "IoTeX Mint Ability", + }, + []string{"type"}, + ) errInvalidSystemActionLayout = errors.New("system action layout is invalid") errUnfoldTxContainer = errors.New("failed to unfold tx container") @@ -48,6 +56,7 @@ var ( func init() { prometheus.MustRegister(_stateDBMtc) + prometheus.MustRegister(_mintAbility) } type ( @@ -658,10 +667,23 @@ func (ws *workingSet) pickAndRunActions( fCtx = protocol.MustGetFeatureCtx(ctx) blobCnt = uint64(0) blobLimit = params.MaxBlobGasPerBlock / params.BlobTxBlobGasPerBlob + deadline *time.Time + fullGas = blkCtx.GasLimit ) if ap != nil { + if dl, ok := ctx.Deadline(); ok { + deadline = &dl + } actionIterator := actioniterator.NewActionIterator(ap.PendingActionMap()) for { + if deadline != nil && time.Now().After(*deadline) { + duration := time.Since(blkCtx.BlockTimeStamp) + log.L().Warn("Stop processing actions due to deadline, please consider increasing hardware", zap.Time("deadline", *deadline), zap.Duration("duration", duration), zap.Int("actions", len(executedActions)), zap.Uint64("gas", fullGas-blkCtx.GasLimit)) + _mintAbility.WithLabelValues("action").Set(float64(len(executedActions))) + _mintAbility.WithLabelValues("gas").Set(float64(fullGas - blkCtx.GasLimit)) + _mintAbility.WithLabelValues("duration").Set(float64(duration.Milliseconds())) + break + } nextAction, ok := actionIterator.Next() if !ok { break