Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port the block limit per fee currency #119

Merged
merged 4 commits into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/geth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ var (
utils.MinerExtraDataFlag,
utils.MinerRecommitIntervalFlag,
utils.MinerNewPayloadTimeout,
utils.CeloFeeCurrencyDefault,
utils.CeloFeeCurrencyLimits,
utils.NATFlag,
utils.NoDiscoverFlag,
utils.DiscoveryV4Flag,
Expand Down
46 changes: 46 additions & 0 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,17 @@ var (
Value: ethconfig.Defaults.Miner.NewPayloadTimeout,
Category: flags.MinerCategory,
}
CeloFeeCurrencyDefault = &cli.Float64Flag{
Name: "celo.feecurrency.default",
Usage: "Default fraction of block gas limit available for TXs paid with a whitelisted alternative currency",
Value: ethconfig.Defaults.Miner.FeeCurrencyDefault,
Category: flags.MinerCategory,
}
CeloFeeCurrencyLimits = &cli.StringFlag{
Name: "celo.feecurrency.limits",
Usage: "Comma separated currency address-to-block percentage mappings (<address>=<fraction>)",
Category: flags.MinerCategory,
}

// Account settings
UnlockedAccountFlag = &cli.StringFlag{
Expand Down Expand Up @@ -1642,6 +1653,39 @@ func setMiner(ctx *cli.Context, cfg *miner.Config) {
}
}

func setCeloMiner(ctx *cli.Context, cfg *miner.Config, networkId uint64) {
cfg.FeeCurrencyDefault = ctx.Float64(CeloFeeCurrencyDefault.Name)

defaultLimits, ok := miner.DefaultFeeCurrencyLimits[networkId]
if !ok {
defaultLimits = make(map[common.Address]float64)
}

cfg.FeeCurrencyLimits = defaultLimits

if ctx.IsSet(CeloFeeCurrencyLimits.Name) {
feeCurrencyLimits := ctx.String(CeloFeeCurrencyLimits.Name)

for _, entry := range strings.Split(feeCurrencyLimits, ",") {
parts := strings.Split(entry, "=")
if len(parts) != 2 {
Fatalf("Invalid fee currency limits entry: %s", entry)
}
var address common.Address
if err := address.UnmarshalText([]byte(parts[0])); err != nil {
Fatalf("Invalid fee currency address hash %s: %v", parts[0], err)
}

fraction, err := strconv.ParseFloat(parts[1], 64)
if err != nil {
Fatalf("Invalid block limit fraction %s: %v", parts[1], err)
}

cfg.FeeCurrencyLimits[address] = fraction
}
}
}

func setRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) {
requiredBlocks := ctx.String(EthRequiredBlocksFlag.Name)
if requiredBlocks == "" {
Expand Down Expand Up @@ -1991,6 +2035,8 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
if err := kzg4844.UseCKZG(ctx.String(CryptoKZGFlag.Name) == "ckzg"); err != nil {
Fatalf("Failed to set KZG library implementation to %s: %v", ctx.String(CryptoKZGFlag.Name), err)
}

setCeloMiner(ctx, &cfg.Miner, cfg.NetworkId)
}

// SetDNSDiscoveryDefaults configures DNS discovery with the given URL if
Expand Down
10 changes: 10 additions & 0 deletions common/celo_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
71 changes: 71 additions & 0 deletions core/celo_multi_gaspool.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package core

import (
"github.com/ethereum/go-ethereum/common"
)

type FeeCurrency = common.Address

// MultiGasPool tracks the amount of gas available during execution
// of the transactions in a block per fee currency. The zero value is a pool
// with zero gas available.
type MultiGasPool struct {
pools map[FeeCurrency]*GasPool
defaultPool *GasPool
}

type FeeCurrencyLimitMapping = map[FeeCurrency]float64

// NewMultiGasPool creates a multi-fee currency gas pool and a default fallback
// pool for CELO
func NewMultiGasPool(
blockGasLimit uint64,
whitelist []FeeCurrency,
defaultLimit float64,
limitsMapping FeeCurrencyLimitMapping,
ezdac marked this conversation as resolved.
Show resolved Hide resolved
) *MultiGasPool {
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)

return &MultiGasPool{
pools: pools,
defaultPool: celoPool,
}
}

// 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
}

return mgp.pools[*feeCurrency]
}

func (mgp MultiGasPool) Copy() *MultiGasPool {
pools := make(map[FeeCurrency]*GasPool, len(mgp.pools))
for fc, gp := range mgp.pools {
gpCpy := *gp
pools[fc] = &gpCpy
}
gpCpy := *mgp.defaultPool
return &MultiGasPool{
pools: pools,
defaultPool: &gpCpy,
}
}
138 changes: 138 additions & 0 deletions core/celo_multi_gaspool_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package core

import (
"testing"

"github.com/ethereum/go-ethereum/common"
)

func TestMultiCurrencyGasPool(t *testing.T) {
blockGasLimit := uint64(1_000)
subGasAmount := 100

cUSDToken := common.HexToAddress("0x765DE816845861e75A25fCA122bb6898B8B1282a")
cEURToken := common.HexToAddress("0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73")

testCases := []struct {
name string
feeCurrency *FeeCurrency
whitelist []FeeCurrency
defaultLimit float64
limits FeeCurrencyLimitMapping
defaultPoolExpected bool
expectedValue uint64
}{
{
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 whitelist, non-empty mapping, CELO uses default pool",
feeCurrency: nil,
whitelist: []FeeCurrency{
cUSDToken,
},
defaultLimit: 0.9,
limits: map[FeeCurrency]float64{
cUSDToken: 0.5,
},
defaultPoolExpected: true,
expectedValue: 900, // blockGasLimit - subGasAmount
},
{
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: true,
expectedValue: 900, // blockGasLimit - subGasAmount
},
{
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 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,
},
defaultPoolExpected: false,
expectedValue: 400, // blockGasLimit * 0.5 - subGasAmount
},
{
ezdac marked this conversation as resolved.
Show resolved Hide resolved
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.PoolFor(c.feeCurrency)
pool.SubGas(uint64(subGasAmount))

if c.defaultPoolExpected {
result := mgp.PoolFor(nil).Gas()
if result != c.expectedValue {
t.Error("Default pool expected", c.expectedValue, "got", result)
}
} else {
result := mgp.PoolFor(c.feeCurrency).Gas()

if result != c.expectedValue {
t.Error(
"Expected pool", c.feeCurrency, "value", c.expectedValue,
"got", result,
)
}
}
})
}
}
17 changes: 9 additions & 8 deletions core/txpool/legacypool/legacypool.go
Original file line number Diff line number Diff line change
Expand Up @@ -573,14 +573,15 @@ func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address]
lazies := make([]*txpool.LazyTransaction, len(txs))
for i := 0; i < len(txs); i++ {
lazies[i] = &txpool.LazyTransaction{
Pool: pool,
Hash: txs[i].Hash(),
Tx: txs[i],
Time: txs[i].Time(),
GasFeeCap: uint256.MustFromBig(txs[i].GasFeeCap()),
GasTipCap: uint256.MustFromBig(txs[i].GasTipCap()),
Gas: txs[i].Gas(),
BlobGas: txs[i].BlobGas(),
Pool: pool,
Hash: txs[i].Hash(),
Tx: txs[i],
Time: txs[i].Time(),
GasFeeCap: uint256.MustFromBig(txs[i].GasFeeCap()),
GasTipCap: uint256.MustFromBig(txs[i].GasTipCap()),
Gas: txs[i].Gas(),
BlobGas: txs[i].BlobGas(),
FeeCurrency: txs[i].FeeCurrency(),
}
}
pending[addr] = lazies
Expand Down
3 changes: 3 additions & 0 deletions core/txpool/subpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ type LazyTransaction struct {

Gas uint64 // Amount of gas required by the transaction
BlobGas uint64 // Amount of blob gas required by the transaction

// Celo
FeeCurrency *common.Address
}

// Resolve retrieves the full transaction belonging to a lazy handle if it is still
Expand Down
15 changes: 8 additions & 7 deletions eth/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,14 @@ func (p *testTxPool) Pending(filter txpool.PendingFilter) map[common.Address][]*
for addr, batch := range batches {
for _, tx := range batch {
pending[addr] = append(pending[addr], &txpool.LazyTransaction{
Hash: tx.Hash(),
Tx: tx,
Time: tx.Time(),
GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()),
GasTipCap: uint256.MustFromBig(tx.GasTipCap()),
Gas: tx.Gas(),
BlobGas: tx.BlobGas(),
Hash: tx.Hash(),
Tx: tx,
Time: tx.Time(),
GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()),
GasTipCap: uint256.MustFromBig(tx.GasTipCap()),
Gas: tx.Gas(),
BlobGas: tx.BlobGas(),
FeeCurrency: tx.FeeCurrency(),
})
}
}
Expand Down
29 changes: 29 additions & 0 deletions miner/celo_defaults.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package miner

import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
)

// cStables addresses on mainnet
var (
cUSD_TOKEN = common.HexToAddress("0x765DE816845861e75A25fCA122bb6898B8B1282a")
cEUR_TOKEN = common.HexToAddress("0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73")
cREAL_TOKEN = common.HexToAddress("0xe8537a3d056DA446677B9E9d6c5dB704EaAb4787")
ezdac marked this conversation as resolved.
Show resolved Hide resolved
USDC_TOKEN = common.HexToAddress("0xcebA9300f2b948710d2653dD7B07f33A8B32118C")
USDT_TOKEN = common.HexToAddress("0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e")
)

// default limits default fraction
const DefaultFeeCurrencyLimit = 0.5

// default limits configuration
var DefaultFeeCurrencyLimits = map[uint64]map[common.Address]float64{
params.CeloMainnetChainID: {
cUSD_TOKEN: 0.9,
USDT_TOKEN: 0.9,
USDC_TOKEN: 0.9,
cEUR_TOKEN: 0.5,
cREAL_TOKEN: 0.5,
},
}
Loading