Skip to content

Commit

Permalink
Added a min and max base fee
Browse files Browse the repository at this point in the history
Min base fee will be 5 Qits converted to Quai over 21000 at the parent block rate

Max base fee will be a rolling average of the past 100 blocks calculated
at the parent blocks rate
  • Loading branch information
gameofpointers committed Oct 15, 2024
1 parent 0a97c51 commit 3a5ddd4
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 17 deletions.
8 changes: 7 additions & 1 deletion core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
36 changes: 36 additions & 0 deletions core/headerchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -1246,6 +1247,41 @@ 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 {
// Max Base fee can only be calculated in zone
if hc.NodeCtx() != common.ZONE_CTX {
return nil
}
// get the parent block
parent := hc.GetBlockByHash(block.ParentHash(hc.NodeCtx()))
if parent == nil {
return nil
}
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
}

// 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
Expand Down
35 changes: 33 additions & 2 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 := 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")
}

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())
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}
}

Expand Down
24 changes: 19 additions & 5 deletions core/tx_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
38 changes: 32 additions & 6 deletions core/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1733,10 +1738,19 @@ 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 := w.hc.CalcMaxBaseFee(block)
if maxBaseFee == nil && !w.hc.IsGenesisHash(block.Hash()) {
return errors.New("could not calculate the max base fee")
}
if baseFee.Cmp(maxBaseFee) > 0 {
baseFee = maxBaseFee
}
env.wo.Header().SetBaseFee(baseFee)
err := w.commitTransactions(env, primeTerminus, block, &txByPriceAndNonce, false)
if err != nil {
Expand Down Expand Up @@ -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())
Expand All @@ -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 := w.hc.CalcMaxBaseFee(block)
if maxBaseFee == nil && !w.hc.IsGenesisHash(block.Hash()) {
return errors.New("could not calculate the max base fee")
}
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
Expand Down
1 change: 1 addition & 0 deletions internal/quaiapi/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion internal/quaiapi/quai_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion params/protocol_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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() {
Expand Down
4 changes: 4 additions & 0 deletions quai/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
2 changes: 1 addition & 1 deletion quai/quaiconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 3a5ddd4

Please sign in to comment.