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

Invalidate txs with below base-fee-floor gas-price in txpool #292

Open
wants to merge 7 commits into
base: celo11
Choose a base branch
from
Open
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: 1 addition & 1 deletion common/exchange/rates.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func ConvertCurrencyToCelo(exchangeRates common.ExchangeRates, feeCurrency *comm
return currencyAmount, nil
}
if currencyAmount == nil {
return nil, fmt.Errorf("Can't convert nil amount to CELO.")
return nil, fmt.Errorf("could not convert nil amount to CELO")
}
exchangeRate, ok := exchangeRates[*feeCurrency]
if !ok {
Expand Down
34 changes: 21 additions & 13 deletions core/celo_genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,23 @@ var (
DevAddr = common.BytesToAddress(DevAddr32.Bytes())
DevAddr32 = common.HexToHash("0x42cf1bbc38BaAA3c4898ce8790e21eD2738c6A4a")

DevFeeCurrencyAddr = common.HexToAddress("0x000000000000000000000000000000000000ce16") // worth half as much as native CELO
DevFeeCurrencyAddr2 = common.HexToAddress("0x000000000000000000000000000000000000ce17") // worth twice as much as native CELO
DevBalance, _ = new(big.Int).SetString("100000000000000000000", 10)
rateNumerator, _ = new(big.Int).SetString("2000000000000000000000000", 10)
rateNumerator2, _ = new(big.Int).SetString("500000000000000000000000", 10)
rateDenominator, _ = new(big.Int).SetString("1000000000000000000000000", 10)
mockOracleAddr = common.HexToAddress("0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0001")
mockOracleAddr2 = common.HexToAddress("0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0002")
mockOracleAddr3 = common.HexToAddress("0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0003")
FaucetAddr = common.HexToAddress("0xfcf982bb4015852e706100b14e21f947a5bb718e")
DevFeeCurrencyAddr = common.HexToAddress("0x000000000000000000000000000000000000ce16") // worth half as much as native CELO
DevFeeCurrencyAddr2 = common.HexToAddress("0x000000000000000000000000000000000000ce17") // worth twice as much as native CELO
DevBalance, _ = new(big.Int).SetString("100000000000000000000", 10)
rateNumerator, _ = new(big.Int).SetString("2000000000000000000000000", 10)
rateNumerator2, _ = new(big.Int).SetString("500000000000000000000000", 10)
rateDenominator, _ = new(big.Int).SetString("1000000000000000000000000", 10)
mockOracleAddr = common.HexToAddress("0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0001")
mockOracleAddr2 = common.HexToAddress("0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0002")
mockOracleAddr3 = common.HexToAddress("0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0003")
FaucetAddr = common.HexToAddress("0xfcf982bb4015852e706100b14e21f947a5bb718e")
FeeCurrencyIntrinsicGas = uint64(50000)
)

func CeloGenesisAccounts(fundedAddr common.Address) GenesisAlloc {
return celoGenesisAccounts(fundedAddr)
}

func celoGenesisAccounts(fundedAddr common.Address) GenesisAlloc {
// Initialize Bytecodes
celoTokenBytecode, err := DecodeHex(celo.CeloTokenBytecodeRaw)
Expand All @@ -79,7 +84,7 @@ func celoGenesisAccounts(fundedAddr common.Address) GenesisAlloc {

faucetBalance, ok := new(big.Int).SetString("500000000000000000000000000", 10) // 500M
if !ok {
panic("Couldn not set faucet balance!")
panic("Could not set faucet balance!")
}
genesisAccounts := map[common.Address]GenesisAccount{
addresses.MainnetAddresses.CeloToken: {
Expand Down Expand Up @@ -133,6 +138,9 @@ func celoGenesisAccounts(fundedAddr common.Address) GenesisAlloc {
FaucetAddr: {
Balance: faucetBalance,
},
fundedAddr: {
Balance: DevBalance,
},
}

// FeeCurrencyDirectory
Expand Down Expand Up @@ -160,6 +168,6 @@ func celoGenesisAccounts(fundedAddr common.Address) GenesisAlloc {

func addFeeCurrencyToStorage(feeCurrencyAddr common.Address, oracleAddr common.Address, storage map[common.Hash]common.Hash) {
structStart := CalcMapAddr(common.HexToHash("0x1"), common.BytesToHash(feeCurrencyAddr.Bytes()))
storage[structStart] = common.BytesToHash(oracleAddr.Bytes()) // oracle
storage[incHash(structStart, 1)] = common.BigToHash(big.NewInt(50000)) // intrinsicGas
storage[structStart] = common.BytesToHash(oracleAddr.Bytes()) // oracle
storage[incHash(structStart, 1)] = common.BigToHash(big.NewInt(int64(FeeCurrencyIntrinsicGas))) // intrinsicGas
}
41 changes: 41 additions & 0 deletions core/txpool/celo_validation.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package txpool

import (
"errors"
"math/big"

"github.com/ethereum/go-ethereum/common"
Expand All @@ -9,6 +10,14 @@ import (
"github.com/ethereum/go-ethereum/params"
)

var (

// ErrGasPriceDoesNotExceedBaseFeeFloor is returned if the gas price specified is
// lower than the configured base-fee-floor
ErrGasPriceDoesNotExceedBaseFeeFloor = errors.New("gas-price is less than the base-fee-floor")
ErrMinimumEffectiveGasTipBelowMinTip = errors.New("effective gas tip at base-fee-floor is below threshold")
)

// AcceptSet is a set of accepted transaction types for a transaction subpool.
type AcceptSet = map[uint8]struct{}

Expand Down Expand Up @@ -56,5 +65,37 @@ func CeloValidateTransaction(tx *types.Transaction, head *types.Header,
return exchange.ErrUnregisteredFeeCurrency
}

if opts.Config.Celo != nil {
// Make sure that the effective gas tip at the base fee floor is at least the
// requested min-tip.
// The min-tip for local transactions is set to 0, we can skip checking here.
if opts.MinTip != nil && opts.MinTip.Cmp(new(big.Int)) != 0 {
// If not, this would never be included, so we can reject early.
minTip, err := exchange.ConvertCeloToCurrency(currencyCtx.ExchangeRates, tx.FeeCurrency(), opts.MinTip)
if err != nil {
return err
}
minBaseFee, err := exchange.ConvertCeloToCurrency(currencyCtx.ExchangeRates, tx.FeeCurrency(), new(big.Int).SetUint64(opts.Config.Celo.EIP1559BaseFeeFloor))
if err != nil {
return err
}
if tx.EffectiveGasTipIntCmp(minTip, minBaseFee) < 0 {
return ErrUnderpriced
}
}

celoGasPrice, err := exchange.ConvertCurrencyToCelo(
currencyCtx.ExchangeRates,
tx.FeeCurrency(),
tx.GasFeeCap(),
)
if err != nil {
return err
}

if new(big.Int).SetUint64(opts.Config.Celo.EIP1559BaseFeeFloor).Cmp(celoGasPrice) == 1 {
return ErrGasPriceDoesNotExceedBaseFeeFloor
}
}
return nil
}
147 changes: 147 additions & 0 deletions core/txpool/legacypool/celo_legacypool_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package legacypool

import (
"crypto/ecdsa"
"errors"
"math/big"
"testing"

"github.com/stretchr/testify/assert"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/triedb"
)

func celoConfig(baseFeeFloor uint64) *params.ChainConfig {
cpy := *params.TestChainConfig
config := &cpy
ct := uint64(0)
config.Cel2Time = &ct
config.Celo = &params.CeloConfig{EIP1559BaseFeeFloor: baseFeeFloor}
return config
}

var (
// worth half as much as native celo
feeCurrencyOne = core.DevFeeCurrencyAddr
// worth twice as much as native celo
feeCurrencyTwo = core.DevFeeCurrencyAddr2
feeCurrencyIntrinsicGas = core.FeeCurrencyIntrinsicGas
)

func pricedCip64Transaction(
config *params.ChainConfig,
nonce uint64,
gasLimit uint64,
gasFeeCap *big.Int,
gasTipCap *big.Int,
feeCurrency *common.Address,
key *ecdsa.PrivateKey,
) *types.Transaction {
tx, _ := types.SignTx(types.NewTx(&types.CeloDynamicFeeTxV2{
Nonce: nonce,
To: &common.Address{},
Value: big.NewInt(100),
Gas: gasLimit,
GasFeeCap: gasFeeCap,
GasTipCap: gasTipCap,
FeeCurrency: feeCurrency,
Data: nil,
}), types.LatestSigner(config), key)
return tx
}

func newDBWithCeloGenesis(config *params.ChainConfig, fundedAddress common.Address) (state.Database, *types.Block) {
gspec := &core.Genesis{
Config: config,
Alloc: core.CeloGenesisAccounts(fundedAddress),
}
db := rawdb.NewMemoryDatabase()
triedb := triedb.NewDatabase(db, triedb.HashDefaults)
defer triedb.Close()
block, err := gspec.Commit(db, triedb)
if err != nil {
panic(err)
}
return state.NewDatabase(db), block

Check failure on line 74 in core/txpool/legacypool/celo_legacypool_test.go

View workflow job for this annotation

GitHub Actions / Test

not enough arguments in call to state.NewDatabase

Check failure on line 74 in core/txpool/legacypool/celo_legacypool_test.go

View workflow job for this annotation

GitHub Actions / Lint

not enough arguments in call to state.NewDatabase
}

func setupCeloPoolWithConfig(config *params.ChainConfig) (*LegacyPool, *ecdsa.PrivateKey) {
key, _ := crypto.GenerateKey()
addr := crypto.PubkeyToAddress(key.PublicKey)

ddb, genBlock := newDBWithCeloGenesis(config, addr)
stateRoot := genBlock.Header().Root
statedb, err := state.New(stateRoot, ddb, nil)

Check failure on line 83 in core/txpool/legacypool/celo_legacypool_test.go

View workflow job for this annotation

GitHub Actions / Test

too many arguments in call to state.New

Check failure on line 83 in core/txpool/legacypool/celo_legacypool_test.go

View workflow job for this annotation

GitHub Actions / Lint

too many arguments in call to state.New
if err != nil {
panic(err)
}
blockchain := newTestBlockChain(config, 10000000, statedb, new(event.Feed))
pool := New(testTxPoolConfig, blockchain)

block := blockchain.CurrentBlock()
// inject the state-root from the genesis chain, so
// that the fee-currency allocs are accessible from the state
// and can be used to create the fee-currency context in the txpool
block.Root = stateRoot
if err := pool.Init(testTxPoolConfig.PriceLimit, block, makeAddressReserver()); err != nil {
panic(err)
}
// wait for the pool to initialize
<-pool.initDoneCh
return pool, key
}

func TestBelowBaseFeeFloorValidityCheck(t *testing.T) {
t.Parallel()
baseFeeFloor := 100
chainConfig := celoConfig(uint64(baseFeeFloor))

pool, key := setupCeloPoolWithConfig(chainConfig)
defer pool.Close()

// gas-price below base-fee-floor should return early
// and thus raise an error in the validation

// use local transactions here to skip the min-tip conversion
// the PriceLimit config is set to 1, so we need at least a tip of 1
tx := pricedCip64Transaction(chainConfig, 0, 21000, big.NewInt(99), big.NewInt(0), nil, key)
if err, want := pool.addLocal(tx), txpool.ErrGasPriceDoesNotExceedBaseFeeFloor; !errors.Is(err, want) {
t.Errorf("want %v have %v", want, err)
}
// also test with fee currency conversion
tx = pricedCip64Transaction(chainConfig, 0, 21000+feeCurrencyIntrinsicGas, big.NewInt(198), big.NewInt(0), &feeCurrencyOne, key)
if err, want := pool.addLocal(tx), txpool.ErrGasPriceDoesNotExceedBaseFeeFloor; !errors.Is(err, want) {
t.Errorf("want %v have %v", want, err)
}
tx = pricedCip64Transaction(chainConfig, 0, 21000+feeCurrencyIntrinsicGas, big.NewInt(48), big.NewInt(0), &feeCurrencyTwo, key)
if err, want := pool.addLocal(tx), txpool.ErrGasPriceDoesNotExceedBaseFeeFloor; !errors.Is(err, want) {
t.Errorf("want %v have %v", want, err)
}
}

func TestAboveBaseFeeFloorValidityCheck(t *testing.T) {
t.Parallel()

baseFeeFloor := 100
chainConfig := celoConfig(uint64(baseFeeFloor))
pool, key := setupCeloPoolWithConfig(chainConfig)
defer pool.Close()

// gas-price just at base-fee-floor should be valid
tx := pricedCip64Transaction(chainConfig, 0, 21000, big.NewInt(101), big.NewInt(1), nil, key)
assert.NoError(t, pool.addRemote(tx))
// also test with fee currency conversion, increase nonce because of previous tx was valid
tx = pricedCip64Transaction(chainConfig, 1, 21000+feeCurrencyIntrinsicGas, big.NewInt(202), big.NewInt(2), &feeCurrencyOne, key)
assert.NoError(t, pool.addRemote(tx))
tx = pricedCip64Transaction(chainConfig, 2, 21000+feeCurrencyIntrinsicGas, big.NewInt(51), big.NewInt(1), &feeCurrencyTwo, key)
assert.NoError(t, pool.addRemote(tx))
}
Loading