From f437ea2417299b46b1ce7ba236cc13ab06d7e1f3 Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld <15726643+ezdac@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:22:48 +0200 Subject: [PATCH] Initialize MultiGasPool with currency whitelist --- common/celo_types.go | 10 +++++ core/celo_multi_gaspool.go | 68 +++++++++++++++------------------ core/celo_multi_gaspool_test.go | 64 ++++++++++++++++++++++++------- miner/worker.go | 27 +++++++------ 4 files changed, 107 insertions(+), 62 deletions(-) diff --git a/common/celo_types.go b/common/celo_types.go index 44ac2ef072..3ae8a2935a 100644 --- a/common/celo_types.go +++ b/common/celo_types.go @@ -10,6 +10,16 @@ var ( type ExchangeRates = map[Address]*big.Rat +func CurrencyWhitelist(exchangeRates ExchangeRates) []Address { + addrs := make([]Address, len(exchangeRates)) + i := 0 + for k := range exchangeRates { + addrs[i] = k + i++ + } + return addrs +} + func IsCurrencyWhitelisted(exchangeRates ExchangeRates, feeCurrency *Address) bool { if feeCurrency == nil { return true diff --git a/core/celo_multi_gaspool.go b/core/celo_multi_gaspool.go index 2f64cef4d8..6cec29a1c9 100644 --- a/core/celo_multi_gaspool.go +++ b/core/celo_multi_gaspool.go @@ -1,6 +1,8 @@ package core -import "github.com/ethereum/go-ethereum/common" +import ( + "github.com/ethereum/go-ethereum/common" +) type FeeCurrency = common.Address @@ -10,9 +12,6 @@ type FeeCurrency = common.Address type MultiGasPool struct { pools map[FeeCurrency]*GasPool defaultPool *GasPool - - blockGasLimit uint64 - defaultLimit float64 } type FeeCurrencyLimitMapping = map[FeeCurrency]float64 @@ -21,48 +20,41 @@ type FeeCurrencyLimitMapping = map[FeeCurrency]float64 // pool for CELO func NewMultiGasPool( blockGasLimit uint64, + whitelist []FeeCurrency, defaultLimit float64, limitsMapping FeeCurrencyLimitMapping, ) *MultiGasPool { - pools := make(map[FeeCurrency]*GasPool, len(limitsMapping)) + pools := make(map[FeeCurrency]*GasPool, len(whitelist)) + + for i := range whitelist { + currency := whitelist[i] + fraction, ok := limitsMapping[currency] + if !ok { + fraction = defaultLimit + } + + pools[currency] = new(GasPool).AddGas( + uint64(float64(blockGasLimit) * fraction), + ) + } + // A special case for CELO which doesn't have a limit celoPool := new(GasPool).AddGas(blockGasLimit) - mgp := &MultiGasPool{ - pools: pools, - defaultPool: celoPool, - blockGasLimit: blockGasLimit, - defaultLimit: defaultLimit, - } - for feeCurrency, fraction := range limitsMapping { - mgp.getOrInitPool(feeCurrency, &fraction) - } - return mgp -} -func (mgp MultiGasPool) getOrInitPool(c FeeCurrency, fraction *float64) *GasPool { - if gp, ok := mgp.pools[c]; ok { - return gp - } - if fraction == nil { - fraction = &mgp.defaultLimit + return &MultiGasPool{ + pools: pools, + defaultPool: celoPool, } - gp := new(GasPool).AddGas( - uint64(float64(mgp.blockGasLimit) * *fraction), - ) - mgp.pools[c] = gp - return gp } -// GetPool returns an initialised pool for the given fee currency or -// initialises and returns a new pool with a default limit. -// For a `nil` FeeCurrency value, it returns the default pool. -func (mgp MultiGasPool) GetPool(c *FeeCurrency) *GasPool { - if c == nil { +// PoolFor returns a configured pool for the given fee currency or the default +// one otherwise +func (mgp MultiGasPool) PoolFor(feeCurrency *FeeCurrency) *GasPool { + if feeCurrency == nil || mgp.pools[*feeCurrency] == nil { return mgp.defaultPool } - // Use the default fraction here because the configured limits' - // pools have been created already in the constructor. - return mgp.getOrInitPool(*c, nil) + + return mgp.pools[*feeCurrency] } func (mgp MultiGasPool) Copy() *MultiGasPool { @@ -71,9 +63,9 @@ func (mgp MultiGasPool) Copy() *MultiGasPool { gpCpy := *gp pools[fc] = &gpCpy } + gpCpy := *mgp.defaultPool return &MultiGasPool{ - pools: pools, - blockGasLimit: mgp.blockGasLimit, - defaultLimit: mgp.defaultLimit, + pools: pools, + defaultPool: &gpCpy, } } diff --git a/core/celo_multi_gaspool_test.go b/core/celo_multi_gaspool_test.go index 9ed4470625..ec79703329 100644 --- a/core/celo_multi_gaspool_test.go +++ b/core/celo_multi_gaspool_test.go @@ -16,22 +16,27 @@ func TestMultiCurrencyGasPool(t *testing.T) { testCases := []struct { name string feeCurrency *FeeCurrency + whitelist []FeeCurrency defaultLimit float64 limits FeeCurrencyLimitMapping defaultPoolExpected bool expectedValue uint64 }{ { - name: "Empty mapping, CELO uses default pool", + name: "Empty whitelist, empty mapping, CELO uses default pool", feeCurrency: nil, + whitelist: []FeeCurrency{}, defaultLimit: 0.9, limits: map[FeeCurrency]float64{}, defaultPoolExpected: true, expectedValue: 900, // blockGasLimit - subGasAmount }, { - name: "Non-empty mapping, CELO uses default pool", - feeCurrency: nil, + name: "Non-empty whitelist, non-empty mapping, CELO uses default pool", + feeCurrency: nil, + whitelist: []FeeCurrency{ + cUSDToken, + }, defaultLimit: 0.9, limits: map[FeeCurrency]float64{ cUSDToken: 0.5, @@ -40,26 +45,44 @@ func TestMultiCurrencyGasPool(t *testing.T) { expectedValue: 900, // blockGasLimit - subGasAmount }, { - name: "Empty mapping, currency fallbacks to the default limit", + name: "Empty whitelist, empty mapping, non-whitelisted currency fallbacks to the default pool", feeCurrency: &cUSDToken, + whitelist: []FeeCurrency{}, defaultLimit: 0.9, limits: map[FeeCurrency]float64{}, - defaultPoolExpected: false, - expectedValue: 800, // blockGasLimit * defaultLimit- subGasAmount + defaultPoolExpected: true, + expectedValue: 900, // blockGasLimit - subGasAmount }, { - name: "Non-empty mapping, currency uses default limit", - feeCurrency: &cEURToken, + name: "Non-empty whitelist, non-empty mapping, non-whitelisted currency uses default pool", + feeCurrency: &cEURToken, + whitelist: []FeeCurrency{ + cUSDToken, + }, defaultLimit: 0.9, limits: map[FeeCurrency]float64{ cUSDToken: 0.5, }, + defaultPoolExpected: true, + expectedValue: 900, // blockGasLimit - subGasAmount + }, + { + name: "Non-empty whitelist, empty mapping, whitelisted currency uses default limit", + feeCurrency: &cUSDToken, + whitelist: []FeeCurrency{ + cUSDToken, + }, + defaultLimit: 0.9, + limits: map[FeeCurrency]float64{}, defaultPoolExpected: false, expectedValue: 800, // blockGasLimit * defaultLimit - subGasAmount }, { - name: "Non-empty mapping, configured currency uses configured limits", - feeCurrency: &cUSDToken, + name: "Non-empty whitelist, non-empty mapping, configured whitelisted currency uses configured limits", + feeCurrency: &cUSDToken, + whitelist: []FeeCurrency{ + cUSDToken, + }, defaultLimit: 0.9, limits: map[FeeCurrency]float64{ cUSDToken: 0.5, @@ -67,26 +90,41 @@ func TestMultiCurrencyGasPool(t *testing.T) { defaultPoolExpected: false, expectedValue: 400, // blockGasLimit * 0.5 - subGasAmount }, + { + name: "Non-empty whitelist, non-empty mapping, unconfigured whitelisted currency uses default limit", + feeCurrency: &cEURToken, + whitelist: []FeeCurrency{ + cUSDToken, + cEURToken, + }, + defaultLimit: 0.9, + limits: map[FeeCurrency]float64{ + cUSDToken: 0.5, + }, + defaultPoolExpected: false, + expectedValue: 800, // blockGasLimit * 0.5 - subGasAmount + }, } for _, c := range testCases { t.Run(c.name, func(t *testing.T) { mgp := NewMultiGasPool( blockGasLimit, + c.whitelist, c.defaultLimit, c.limits, ) - pool := mgp.GetPool(c.feeCurrency) + pool := mgp.PoolFor(c.feeCurrency) pool.SubGas(uint64(subGasAmount)) if c.defaultPoolExpected { - result := mgp.GetPool(nil).Gas() + result := mgp.PoolFor(nil).Gas() if result != c.expectedValue { t.Error("Default pool expected", c.expectedValue, "got", result) } } else { - result := mgp.GetPool(c.feeCurrency).Gas() + result := mgp.PoolFor(c.feeCurrency).Gas() if result != c.expectedValue { t.Error( diff --git a/miner/worker.go b/miner/worker.go index 540e810c85..4e628f81a2 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -87,12 +87,13 @@ var ( // environment is the worker's current environment and holds all // information of the sealing block generation. type environment struct { - signer types.Signer - state *state.StateDB // apply state changes here - tcount int // tx count in cycle - gasPool *core.GasPool // available gas used to pack transactions - multiGasPool *core.MultiGasPool // available per-fee-currency gas used to pack transactions - coinbase common.Address + signer types.Signer + state *state.StateDB // apply state changes here + tcount int // tx count in cycle + gasPool *core.GasPool // available gas used to pack transactions + multiGasPool *core.MultiGasPool // available per-fee-currency gas used to pack transactions + exchangeRates common.ExchangeRates + coinbase common.Address header *types.Header txs []*types.Transaction @@ -858,6 +859,7 @@ func (w *worker) commitTransactions(env *environment, plainTxs, blobTxs *transac if env.multiGasPool == nil { env.multiGasPool = core.NewMultiGasPool( env.header.GasLimit, + common.CurrencyWhitelist(env.exchangeRates), w.config.FeeCurrencyDefault, w.config.FeeCurrencyLimits, ) @@ -917,7 +919,7 @@ func (w *worker) commitTransactions(env *environment, plainTxs, blobTxs *transac txs.Pop() continue } - if left := env.multiGasPool.GetPool(ltx.FeeCurrency).Gas(); left < ltx.Gas { + if left := env.multiGasPool.PoolFor(ltx.FeeCurrency).Gas(); left < ltx.Gas { log.Trace( "Not enough specific fee-currency gas left for transaction", "currency", ltx.FeeCurrency, "hash", ltx.Hash, @@ -957,12 +959,12 @@ func (w *worker) commitTransactions(env *environment, plainTxs, blobTxs *transac txs.Shift() case errors.Is(err, nil): - err := env.multiGasPool.GetPool(tx.FeeCurrency()).SubGas(gasUsed) + err := env.multiGasPool.PoolFor(tx.FeeCurrency()).SubGas(gasUsed) if err != nil { // Should never happen as we check it above log.Warn( "Unexpectedly reached limit for fee currency, but tx will not be skipped", - "hash", tx.Hash(), "gas", env.multiGasPool.GetPool(tx.FeeCurrency()).Gas(), + "hash", tx.Hash(), "gas", env.multiGasPool.PoolFor(tx.FeeCurrency()).Gas(), "tx gas used", gasUsed, ) // If we reach this codepath, we want to still include the transaction, @@ -1134,8 +1136,10 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) { log.Error("Failed to create sealing context", "err", err) return nil, err } + context := core.NewEVMBlockContext(header, w.chain, nil, w.chainConfig, env.state) + //FIXME: can be nil when not Cel2 + env.exchangeRates = context.ExchangeRates if header.ParentBeaconRoot != nil { - context := core.NewEVMBlockContext(header, w.chain, nil, w.chainConfig, env.state) vmenv := vm.NewEVM(context, vm.TxContext{}, env.state, w.chainConfig, vm.Config{}) core.ProcessBeaconBlockRoot(*header.ParentBeaconRoot, vmenv, env.state) } @@ -1217,6 +1221,7 @@ func (w *worker) generateWork(genParams *generateParams) *newPayloadResult { if work.multiGasPool == nil { work.multiGasPool = core.NewMultiGasPool( work.header.GasLimit, + common.CurrencyWhitelist(work.exchangeRates), w.config.FeeCurrencyDefault, w.config.FeeCurrencyLimits, ) @@ -1234,7 +1239,7 @@ func (w *worker) generateWork(genParams *generateParams) *newPayloadResult { // the non-fee currency pool in the multipool is not used, but for consistency // subtract the gas. Don't check the error either, this has been checked already // with the work.gasPool. - work.multiGasPool.GetPool(nil).SubGas(tx.Gas()) + work.multiGasPool.PoolFor(nil).SubGas(tx.Gas()) work.tcount++ }