diff --git a/core/core.go b/core/core.go index 7f6f82bd7..e513cc6bd 100644 --- a/core/core.go +++ b/core/core.go @@ -1301,8 +1301,14 @@ func (c *Core) Nonce(addr common.Address) uint64 { return c.sl.txPool.Nonce(internal) } +// GetMinGasPrice returns the minimum gas price needed to be included in the current block +func (c *Core) GetMinGasPrice() *big.Int { + return c.sl.txPool.GetMinGasPrice() +} + +// GetPoolGasPrice returns the pool gas price set by the node func (c *Core) GetPoolGasPrice() *big.Int { - return c.sl.txPool.gasPrice + return c.sl.txPool.GasPrice() } func (c *Core) Stats() (int, int, int) { diff --git a/core/headerchain.go b/core/headerchain.go index 720d1edf2..e9939ff53 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -14,6 +14,7 @@ import ( "github.com/dominant-strategies/go-quai/common" "github.com/dominant-strategies/go-quai/consensus" + "github.com/dominant-strategies/go-quai/consensus/misc" "github.com/dominant-strategies/go-quai/core/rawdb" "github.com/dominant-strategies/go-quai/core/state" "github.com/dominant-strategies/go-quai/core/types" @@ -1246,6 +1247,37 @@ func (hc *HeaderChain) ComputeEfficiencyScore(parent *types.WorkObject) (uint16, return ewma, nil } +// CalcMaxBaseFee takes an average of the base fee over past 100 blocks +func (hc *HeaderChain) CalcMaxBaseFee(block *types.WorkObject) (*big.Int, error) { + // get the parent block + parent := hc.GetBlockByHash(block.ParentHash(hc.NodeCtx())) + if parent == nil { + return nil, errors.New("parent cannot be found in the CalcMaxBaseFee") + } + parentBaseFee := parent.BaseFee() + // Adjust the max base fee calculation for next block using this formula, + // nextBlockBaseFee = blockBaseFee/OneOverBaseFeeControllerAlpha + parentBaseFee + baseFee := new(big.Int).Div(block.BaseFee(), params.OneOverBaseFeeControllerAlpha) + baseFee = new(big.Int).Add(baseFee, parentBaseFee) + + minBaseFee := hc.CalcMinBaseFee(block) + if minBaseFee.Cmp(baseFee) >= 0 { + baseFee = minBaseFee + } + + return baseFee, nil +} + +// CalcMinBaseFee calculates the mininum base fee supplied by the transaction +// to get inclusion in the next block +func (hc *HeaderChain) CalcMinBaseFee(block *types.WorkObject) *big.Int { + // If the base fee is calculated is less than the min base fee, then set + // this to min base fee + minBaseFee := misc.QiToQuai(block, params.MinBaseFeeInQits) + minBaseFee = new(big.Int).Div(minBaseFee, big.NewInt(int64(params.TxGas))) + return minBaseFee +} + // UpdateEtxEligibleSlices returns the updated etx eligible slices field func (hc *HeaderChain) UpdateEtxEligibleSlices(header *types.WorkObject, location common.Location) common.Hash { // After 5 days of the start of a new chain, the chain becomes eligible to receive etxs diff --git a/core/state_processor.go b/core/state_processor.go index 74bcaaa18..a3d494e9d 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -322,6 +322,14 @@ func (p *StateProcessor) Process(block *types.WorkObject, batch ethdb.Batch) (ty nonEtxExists := false + // Calculate the min base fee from the parent + minBaseFee := p.hc.CalcMinBaseFee(parent) + // Check the min base fee, and max base fee + maxBaseFee, err := p.hc.CalcMaxBaseFee(parent) + if maxBaseFee == nil && !p.hc.IsGenesisHash(parent.Hash()) { + return nil, nil, nil, nil, 0, 0, 0, nil, fmt.Errorf("could not calculate max base fee %s", err) + } + primeTerminus := p.hc.GetHeaderByHash(header.PrimeTerminusHash()) if primeTerminus == nil { return nil, nil, nil, nil, 0, 0, 0, nil, fmt.Errorf("could not find prime terminus header %032x", header.PrimeTerminusHash()) @@ -358,6 +366,17 @@ func (p *StateProcessor) Process(block *types.WorkObject, batch ethdb.Batch) (ty qiTxFeeInQuai := misc.QiToQuai(parent, qiTxFee) // get the gas price by dividing the fee by qiTxGas qiGasPrice := new(big.Int).Div(qiTxFeeInQuai, big.NewInt(int64(types.CalculateBlockQiTxGas(tx, qiScalingFactor, p.hc.NodeLocation())))) + + if qiGasPrice.Cmp(minBaseFee) < 0 { + return nil, nil, nil, nil, 0, 0, 0, nil, fmt.Errorf("qi tx has base fee less than min base fee not apply tx %d [%v]", i, tx.Hash().Hex()) + } + + // If the gas price from this qi tx is greater than the max base fee + // set the qi gas price to the max base fee + if qiGasPrice.Cmp(maxBaseFee) > 0 { + qiGasPrice = new(big.Int).Set(maxBaseFee) + } + if minGasPrice == nil { minGasPrice = new(big.Int).Set(qiGasPrice) } else { @@ -580,13 +599,25 @@ func (p *StateProcessor) Process(block *types.WorkObject, batch ethdb.Batch) (ty quaiFees.Add(quaiFees, fees) + gasPrice := tx.GasPrice() + + if gasPrice.Cmp(minBaseFee) < 0 { + return nil, nil, nil, nil, 0, 0, 0, nil, fmt.Errorf("quai tx has gas price less than min base fee not apply tx %d [%v]", i, tx.Hash().Hex()) + } + + // If the gas price from this quai tx is greater than the max base fee + // set the quai gas price to the max base fee + if gasPrice.Cmp(maxBaseFee) > 0 { + gasPrice = new(big.Int).Set(maxBaseFee) + } + // update the min gas price if the gas price in the tx is less than // the min gas price if minGasPrice == nil { - minGasPrice = new(big.Int).Set(tx.GasPrice()) + minGasPrice = new(big.Int).Set(gasPrice) } else { if minGasPrice.Cmp(tx.GasPrice()) > 0 { - minGasPrice = new(big.Int).Set(tx.GasPrice()) + minGasPrice = new(big.Int).Set(gasPrice) } } diff --git a/core/tx_pool.go b/core/tx_pool.go index 7b3902649..c9df7ef01 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -169,6 +169,7 @@ type blockChain interface { GetMaxTxInWorkShare() uint64 CheckInCalcOrderCache(common.Hash) (*big.Int, int, bool) AddToCalcOrderCache(common.Hash, int, *big.Int) + CalcMinBaseFee(block *types.WorkObject) *big.Int } // TxPoolConfig are the configuration parameters of the transaction pool. @@ -609,6 +610,16 @@ func (pool *TxPool) SetGasPrice(price *big.Int) { pool.logger.WithField("price", price).Info("Transaction pool price threshold updated") } +func (pool *TxPool) GetMinGasPrice() *big.Int { + currentHeader := pool.chain.CurrentBlock() + if currentHeader == nil { + return big.NewInt(0) + } + baseFeeMin := pool.chain.CalcMinBaseFee(currentHeader) + baseFeeMinInGwei := new(big.Int).Div(baseFeeMin, new(big.Int).SetInt64(int64(params.GWei))) + return baseFeeMinInGwei +} + // Nonce returns the next nonce of an account, with all transactions executable // by the pool already applied on top. func (pool *TxPool) Nonce(addr common.InternalAddress) uint64 { @@ -707,12 +718,15 @@ func (pool *TxPool) TxPoolPending(enforceTips bool) (map[common.AddressBytes]typ // If the miner requests tip enforcement, cap the lists now if enforceTips && !pool.locals.contains(addr) { for i, tx := range txs { - if pool.gasPrice.Cmp(tx.GasPrice()) > 0 { + // make sure that the tx has atleast min base fee as the gas + // price + currentBlock := pool.chain.CurrentBlock() + minBaseFee := pool.chain.CalcMinBaseFee(currentBlock) + if minBaseFee.Cmp(tx.GasPrice()) > 0 { pool.logger.WithFields(log.Fields{ - "tx": tx.Hash().String(), - "gasPrice": tx.GasPrice().String(), - "poolGasPrice": pool.gasPrice.String(), - "baseFee": pool.priced.urgent.baseFee.String(), + "tx": tx.Hash().String(), + "gasPrice": tx.GasPrice().String(), + "minBaseFee": minBaseFee.String(), }).Debug("TX has incorrect or low gas price") txs = txs[:i] break diff --git a/core/worker.go b/core/worker.go index b490b99bc..c16b4d31b 100644 --- a/core/worker.go +++ b/core/worker.go @@ -1030,6 +1030,11 @@ func (w *worker) commitTransaction(env *environment, parent *types.WorkObject, t if tx.Type() != types.ExternalTxType && tx.GasPrice().Cmp(env.wo.BaseFee()) < 0 { return nil, false, errors.New("gas price fee less than base fee") } + + minBaseFee := w.hc.CalcMinBaseFee(parent) + if tx.Type() != types.ExternalTxType && tx.GasPrice().Cmp(minBaseFee) < 0 { + return nil, false, errors.New("gas price fee less than min base fee") + } // coinbase tx // 1) is a external tx type // 2) do not consume any gas @@ -1733,12 +1738,21 @@ func (w *worker) fillTransactions(env *environment, primeTerminus *types.WorkObj txByPriceAndNonce := types.TransactionsByPriceAndNonce{} txByPriceAndNonce.SetHead(orderedTxs) - if baseFee.Cmp(big.NewInt(0)) == 0 { - return errors.New("ordered txs had min gas price of zero") - } + minBaseFee := w.hc.CalcMinBaseFee(block) + if baseFee.Cmp(minBaseFee) < 0 { + return errors.New("ordered txs had price less than the min base fee allowed") + } + // Check the min base fee, and max base fee + maxBaseFee, err := w.hc.CalcMaxBaseFee(block) + if maxBaseFee == nil && !w.hc.IsGenesisHash(block.Hash()) { + return fmt.Errorf("could not calculate the max base fee, err: %s", err) + } + if baseFee.Cmp(maxBaseFee) > 0 { + baseFee = maxBaseFee + } env.wo.Header().SetBaseFee(baseFee) - err := w.commitTransactions(env, primeTerminus, block, &txByPriceAndNonce, false) + err = w.commitTransactions(env, primeTerminus, block, &txByPriceAndNonce, false) if err != nil { return err } @@ -1771,8 +1785,9 @@ func (w *worker) fillTransactions(env *environment, primeTerminus *types.WorkObj // update the fee qiFeeInQuai := misc.QiToQuai(block, tx.MinerFee()) minerFeeInQuai := new(big.Int).Div(qiFeeInQuai, big.NewInt(int64(types.CalculateBlockQiTxGas(tx.Tx(), env.qiGasScalingFactor, w.hc.NodeLocation())))) - if minerFeeInQuai.Cmp(big.NewInt(0)) == 0 { - w.logger.Error("rejecting qi tx that has zero gas price") + minBaseFee := w.hc.CalcMinBaseFee(block) + if minerFeeInQuai.Cmp(minBaseFee) < 0 { + w.logger.Debug("qi tx has less fee than min base fee") continue } qiTx, err := types.NewTxWithMinerFee(tx.Tx(), minerFeeInQuai, time.Now()) @@ -1799,9 +1814,20 @@ func (w *worker) fillTransactions(env *environment, primeTerminus *types.WorkObj // read the gas price of the lowest fee transaction and set the base // fee for the pending header on each iteration baseFee = lowestFeeTx.PeekAndGetFee().MinerFee() - if baseFee.Cmp(big.NewInt(0)) == 0 { + minBaseFee := w.hc.CalcMinBaseFee(block) + if baseFee.Cmp(minBaseFee) < 0 { + w.logger.Debug("baseFee is less than the minBaseFee") + count++ continue } + // Check the min base fee, and max base fee + maxBaseFee, err := w.hc.CalcMaxBaseFee(block) + if maxBaseFee == nil && !w.hc.IsGenesisHash(block.Hash()) { + return fmt.Errorf("could not calculate the max base fee, err %s", err) + } + if baseFee.Cmp(maxBaseFee) > 0 { + baseFee = maxBaseFee + } env.wo.Header().SetBaseFee(baseFee) w.commitTransactions(env, primeTerminus, block, lowestFeeTx, etxIncluded) // After the first run the etxs are included diff --git a/internal/quaiapi/backend.go b/internal/quaiapi/backend.go index ab46f0257..67cab8b09 100644 --- a/internal/quaiapi/backend.go +++ b/internal/quaiapi/backend.go @@ -136,6 +136,7 @@ type Backend interface { Stats() (pending int, queued int, qi int) TxPoolContent() (map[common.InternalAddress]types.Transactions, map[common.InternalAddress]types.Transactions) TxPoolContentFrom(addr common.Address) (types.Transactions, types.Transactions) + GetMinGasPrice() *big.Int GetPoolGasPrice() *big.Int // Filter API diff --git a/internal/quaiapi/quai_api.go b/internal/quaiapi/quai_api.go index 57b269f30..a78a81eac 100644 --- a/internal/quaiapi/quai_api.go +++ b/internal/quaiapi/quai_api.go @@ -56,7 +56,7 @@ func NewPublicQuaiAPI(b Backend) *PublicQuaiAPI { // GasPrice returns a suggestion for a gas price for legacy transactions. func (s *PublicQuaiAPI) GasPrice(ctx context.Context) (*hexutil.Big, error) { - return (*hexutil.Big)(s.b.GetPoolGasPrice()), nil + return (*hexutil.Big)(s.b.GetMinGasPrice()), nil } // MinerTip returns the gas price of the pool diff --git a/params/protocol_params.go b/params/protocol_params.go index 4ee7ec3ef..1d0e41a64 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -169,7 +169,7 @@ var ( OrchardDurationLimit = big.NewInt(5) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not. LighthouseDurationLimit = big.NewInt(5) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not. LocalDurationLimit = big.NewInt(1) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not. - TimeToStartTx uint64 = 100 + TimeToStartTx uint64 = 10 BlocksPerDay uint64 = new(big.Int).Div(big.NewInt(86400), DurationLimit).Uint64() // BlocksPerDay is the number of blocks per day assuming 12 second block time DifficultyAdjustmentPeriod = big.NewInt(720) // This is the number of blocks over which the average has to be taken DifficultyAdjustmentFactor int64 = 40 // This is the factor that divides the log of the change in the difficulty @@ -187,6 +187,9 @@ var ( MaxTimeDiffBetweenBlocks int64 = 100 // Max time difference between the blocks to 100 secs OneOverAlpha = big.NewInt(200) // The alpha value for the quai to qi conversion ControllerKickInBlock uint64 = 1000000000 + + MinBaseFeeInQits = big.NewInt(5) + OneOverBaseFeeControllerAlpha = big.NewInt(100) ) func init() { diff --git a/quai/api_backend.go b/quai/api_backend.go index b6bed5f97..e1dcd4548 100644 --- a/quai/api_backend.go +++ b/quai/api_backend.go @@ -386,6 +386,10 @@ func (b *QuaiAPIBackend) GetPoolNonce(ctx context.Context, addr common.Address) return b.quai.core.Nonce(addr), nil } +func (b *QuaiAPIBackend) GetMinGasPrice() *big.Int { + return b.quai.core.GetMinGasPrice() +} + func (b *QuaiAPIBackend) GetPoolGasPrice() *big.Int { return b.quai.core.GetPoolGasPrice() } diff --git a/quai/quaiconfig/config.go b/quai/quaiconfig/config.go index 6437d081d..ec0c5dfbd 100644 --- a/quai/quaiconfig/config.go +++ b/quai/quaiconfig/config.go @@ -64,7 +64,7 @@ var Defaults = Config{ }, TxPool: core.DefaultTxPoolConfig, RPCGasCap: params.GasCeil, - RPCTxFeeCap: 1, // 1 ether + RPCTxFeeCap: 100, // 100 quai } //go:generate gencodec -type Config -formats toml -out gen_config.go