diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 3b6292d642..9966d5528a 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -315,6 +315,9 @@ type BlobPool struct { insertFeed event.Feed // Event feed to send out new tx events on pool inclusion (reorg included) lock sync.RWMutex // Mutex protecting the pool during reorg handling + + // Celo + feeCurrencyValidator txpool.FeeCurrencyValidator } // New creates a new blob transaction pool to gather, sort and filter inbound @@ -331,6 +334,8 @@ func New(config Config, chain BlockChain) *BlobPool { lookup: make(map[common.Hash]uint64), index: make(map[common.Address][]*blobTxMeta), spent: make(map[common.Address]*uint256.Int), + + feeCurrencyValidator: txpool.NewFeeCurrencyValidator(), } } @@ -1039,13 +1044,14 @@ func (p *BlobPool) SetGasTip(tip *big.Int) { func (p *BlobPool) validateTx(tx *types.Transaction) error { // Ensure the transaction adheres to basic pool filters (type, size, tip) and // consensus rules - baseOpts := &txpool.ValidationOptions{ - Config: p.chain.Config(), - Accept: 1 << types.BlobTxType, - MaxSize: txMaxSize, - MinTip: p.gasTip.ToBig(), - } - if err := txpool.ValidateTransaction(tx, p.head, p.signer, baseOpts); err != nil { + baseOpts := &txpool.CeloValidationOptions{ + Config: p.chain.Config(), + AcceptSet: txpool.NewAcceptSet(types.BlobTxType), + MaxSize: txMaxSize, + MinTip: p.gasTip.ToBig(), + } + var fcv txpool.FeeCurrencyValidator = nil // TODO: create with proper value + if err := txpool.CeloValidateTransaction(tx, p.head, p.signer, baseOpts, p.state, fcv); err != nil { return err } // Ensure the transaction adheres to the stateful pool filters (nonce, balance) @@ -1079,7 +1085,14 @@ func (p *BlobPool) validateTx(tx *types.Transaction) error { return nil }, } - if err := txpool.ValidateTransactionWithState(tx, p.signer, stateOpts); err != nil { + + // Adapt to celo validation options + celoOpts := &txpool.CeloValidationOptionsWithState{ + ValidationOptionsWithState: *stateOpts, + FeeCurrencyValidator: p.feeCurrencyValidator, + } + + if err := txpool.ValidateTransactionWithState(tx, p.signer, celoOpts); err != nil { return err } // If the transaction replaces an existing one, ensure that price bumps are diff --git a/core/txpool/celo_validation.go b/core/txpool/celo_validation.go new file mode 100644 index 0000000000..5c0f7b27ac --- /dev/null +++ b/core/txpool/celo_validation.go @@ -0,0 +1,110 @@ +package txpool + +import ( + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" +) + +var NonWhitelistedFeeCurrencyError = errors.New("Fee currency given is not whitelisted at current block") + +// FeeCurrencyValidator validates currency whitelisted status at the specified +// block number. +type FeeCurrencyValidator interface { + IsWhitelisted(st *state.StateDB, feeCurrency *common.Address) bool + // Balance returns the feeCurrency balance of the address specified, in the given state. + // If feeCurrency is nil, the native currency balance has to be returned. + Balance(st *state.StateDB, address common.Address, feeCurrency *common.Address) *big.Int +} + +func NewFeeCurrencyValidator() FeeCurrencyValidator { + return &feeval{} +} + +type feeval struct { +} + +func (f *feeval) IsWhitelisted(st *state.StateDB, feeCurrency *common.Address) bool { + // TODO: implement proper validation for all currencies + // Hardcoded for the moment + return true + //return feeCurrency == nil +} + +func (f *feeval) Balance(st *state.StateDB, address common.Address, feeCurrency *common.Address) *big.Int { + // TODO: implement proper balance retrieval for fee currencies + return st.GetBalance(address) +} + +// AcceptSet is a set of accepted transaction types for a transaction subpool. +type AcceptSet = map[uint8]struct{} + +// CeloValidationOptions define certain differences between transaction validation +// across the different pools without having to duplicate those checks. +// In comparison to the standard ValidationOptions, the Accept field has been +// changed to allow to test for CeloDynamicFeeTx types. +type CeloValidationOptions struct { + Config *params.ChainConfig // Chain configuration to selectively validate based on current fork rules + + AcceptSet AcceptSet // Set of transaction types that should be accepted for the calling pool + MaxSize uint64 // Maximum size of a transaction that the caller can meaningfully handle + MinTip *big.Int // Minimum gas tip needed to allow a transaction into the caller pool +} + +// NewAcceptSet creates a new AcceptSet with the types provided. +func NewAcceptSet(types ...uint8) AcceptSet { + m := make(AcceptSet, len(types)) + for _, t := range types { + m[t] = struct{}{} + } + return m +} + +// Accepts returns true iff txType is accepted by this CeloValidationOptions. +func (cvo *CeloValidationOptions) Accepts(txType uint8) bool { + _, ok := cvo.AcceptSet[txType] + return ok +} + +// CeloValidateTransaction is a helper method to check whether a transaction is valid +// according to the consensus rules, but does not check state-dependent validation +// (balance, nonce, etc). +// +// This check is public to allow different transaction pools to check the basic +// rules without duplicating code and running the risk of missed updates. +func CeloValidateTransaction(tx *types.Transaction, head *types.Header, + signer types.Signer, opts *CeloValidationOptions, st *state.StateDB, fcv FeeCurrencyValidator) error { + if err := ValidateTransaction(tx, head, signer, opts); err != nil { + return err + } + if IsFeeCurrencyTx(tx) { + if !fcv.IsWhitelisted(st, tx.FeeCurrency()) { + return NonWhitelistedFeeCurrencyError + } + } + return nil +} + +// IsFeeCurrencyTxType returns true if and only if the transaction type +// given can handle custom gas fee currencies. +func IsFeeCurrencyTxType(t uint8) bool { + return t == types.CeloDynamicFeeTxType +} + +// IsFeeCurrencyTx returns true if this transaction specifies a custom +// gas fee currency. +func IsFeeCurrencyTx(tx *types.Transaction) bool { + return IsFeeCurrencyTxType(tx.Type()) && tx.FeeCurrency() != nil +} + +// See: txpool.ValidationOptionsWithState +type CeloValidationOptionsWithState struct { + ValidationOptionsWithState + + // FeeCurrencyValidator allows for balance check of non native fee currencies. + FeeCurrencyValidator FeeCurrencyValidator +} diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index caff323191..2b58db6dd3 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -240,6 +240,9 @@ type LegacyPool struct { changesSinceReorg int // A counter for how many drops we've performed in-between reorg. l1CostFn txpool.L1CostFunc // To apply L1 costs as rollup, optional field, may be nil. + + // Celo + feeCurrencyValidator txpool.FeeCurrencyValidator } type txpoolResetRequest struct { @@ -268,6 +271,9 @@ func New(config Config, chain BlockChain) *LegacyPool { reorgDoneCh: make(chan chan struct{}), reorgShutdownCh: make(chan struct{}), initDoneCh: make(chan struct{}), + + // CELO fields + feeCurrencyValidator: txpool.NewFeeCurrencyValidator(), } pool.locals = newAccountSet(pool.signer) for _, addr := range config.Locals { @@ -618,19 +624,20 @@ func (pool *LegacyPool) local() map[common.Address]types.Transactions { // This check is meant as an early check which only needs to be performed once, // and does not require the pool mutex to be held. func (pool *LegacyPool) validateTxBasics(tx *types.Transaction, local bool) error { - opts := &txpool.ValidationOptions{ + opts := &txpool.CeloValidationOptions{ Config: pool.chainconfig, - Accept: 0 | - 1<