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

Validations for celo txs in txpool #31

Merged
merged 6 commits into from
Nov 21, 2023
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
29 changes: 21 additions & 8 deletions core/txpool/blobpool/blobpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,9 @@ type BlobPool struct {
eventScope event.SubscriptionScope // Event scope to track and mass unsubscribe on termination

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 @@ -327,6 +330,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 @@ -1024,13 +1029,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 @@ -1064,7 +1070,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 {
}
karlb marked this conversation as resolved.
Show resolved Hide resolved

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.
hbandura marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -241,6 +241,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 @@ -269,6 +272,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 @@ -603,19 +609,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 @@ -660,7 +667,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 @@ -221,7 +222,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 @@ -241,7 +242,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