Skip to content

Commit

Permalink
Merge pull request #32 from celo-org/karlb/celo-tx-validations
Browse files Browse the repository at this point in the history
Validations for celo txs in txpool (rebase)
  • Loading branch information
carterqw2 authored Nov 22, 2023
2 parents 1ae96ce + a74feb3 commit 270843e
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 19 deletions.
29 changes: 21 additions & 8 deletions core/txpool/blobpool/blobpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(),
}
}

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
110 changes: 110 additions & 0 deletions core/txpool/celo_validation.go
Original file line number Diff line number Diff line change
@@ -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
}
28 changes: 21 additions & 7 deletions core/txpool/legacypool/legacypool.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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<<types.LegacyTxType |
1<<types.AccessListTxType |
1<<types.DynamicFeeTxType,
AcceptSet: txpool.NewAcceptSet(
types.LegacyTxType,
types.AccessListTxType,
types.DynamicFeeTxType,
types.CeloDynamicFeeTxType),
MaxSize: txMaxSize,
MinTip: pool.gasTip.Load(),
}
if local {
opts.MinTip = new(big.Int)
}
if err := txpool.ValidateTransaction(tx, pool.currentHead.Load(), pool.signer, opts); err != nil {
if err := txpool.CeloValidateTransaction(tx, pool.currentHead.Load(), pool.signer, opts, pool.currentState, pool.feeCurrencyValidator); err != nil {
return err
}
return nil
Expand Down Expand Up @@ -675,7 +682,14 @@ func (pool *LegacyPool) validateTx(tx *types.Transaction, local bool) error {
},
L1CostFn: pool.l1CostFn,
}
if err := txpool.ValidateTransactionWithState(tx, pool.signer, opts); err != nil {

// Adapt to celo validation options
celoOpts := &txpool.CeloValidationOptionsWithState{
ValidationOptionsWithState: *opts,
FeeCurrencyValidator: pool.feeCurrencyValidator,
}

if err := txpool.ValidateTransactionWithState(tx, pool.signer, celoOpts); err != nil {
return err
}
return nil
Expand Down
9 changes: 5 additions & 4 deletions core/txpool/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,16 @@ type ValidationOptions struct {
//
// 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 ValidateTransaction(tx *types.Transaction, head *types.Header, signer types.Signer, opts *ValidationOptions) error {
// ONLY TO BE CALLED FROM "CeloValidateTransaction"
func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types.Signer, opts *CeloValidationOptions) error {
// No unauthenticated deposits allowed in the transaction pool.
// This is for spam protection, not consensus,
// as the external engine-API user authenticates deposits.
if tx.Type() == types.DepositTxType {
return core.ErrTxTypeNotSupported
}
// Ensure transactions not implemented by the calling pool are rejected
if opts.Accept&(1<<tx.Type()) == 0 {
if !opts.Accepts(tx.Type()) {
return fmt.Errorf("%w: tx type %v not supported by this pool", core.ErrTxTypeNotSupported, tx.Type())
}
// Before performing any expensive validations, sanity check that the tx is
Expand Down Expand Up @@ -223,7 +224,7 @@ type ValidationOptionsWithState struct {
//
// This check is public to allow different transaction pools to check the stateful
// rules without duplicating code and running the risk of missed updates.
func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, opts *ValidationOptionsWithState) error {
func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, opts *CeloValidationOptionsWithState) error {
// Ensure the transaction adheres to nonce ordering
from, err := signer.Sender(tx) // already validated (and cached), but cleaner to check
if err != nil {
Expand All @@ -243,7 +244,7 @@ func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, op
}
// Ensure the transactor has enough funds to cover the transaction costs
var (
balance = opts.State.GetBalance(from)
balance = opts.FeeCurrencyValidator.Balance(opts.State, from, tx.FeeCurrency())
cost = tx.Cost()
)
if opts.L1CostFn != nil {
Expand Down

0 comments on commit 270843e

Please sign in to comment.