Custom tx validator logic WIP

This commit is contained in:
Kirill Maksimov 2023-12-21 16:31:06 +02:00
parent 65e11e1ee1
commit 59008b6541
5 changed files with 137 additions and 57 deletions

View file

@ -0,0 +1,42 @@
package txpool
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
type CustomValidationOptions struct {
}
type CustomValidator interface {
Validate(tx *types.Transaction, head *types.Header, signer types.Signer, opts *CustomValidationOptions) error
}
type CustomValidatorHypernative struct {
Config *CustomValidatorConfigHypernative
}
type CustomValidatorConfigHypernative struct {
BannedAddresses []common.Address
}
func NewHypernativeValidator(config *CustomValidatorConfigHypernative) CustomValidator {
return &CustomValidatorHypernative{
Config: config,
}
}
func (v *CustomValidatorHypernative) Validate(tx *types.Transaction, head *types.Header, signer types.Signer, opts *CustomValidationOptions) error {
// ban certain senders just for the fun of it
if address, err := signer.Sender(tx); err == nil {
for _, bannedAddress := range v.Config.BannedAddresses {
if address == bannedAddress {
return ErrCustomValidationFailed
}
}
} else {
return err
}
return nil
}

View file

@ -136,6 +136,7 @@ type Config struct {
Lifetime time.Duration // Maximum amount of time non-executable transaction are queued
CustomValidationEnabled bool
CustomValidator txpool.CustomValidator
}
// DefaultConfig contains the default configurations for the transaction pool.
@ -192,6 +193,9 @@ func (config *Config) sanitize() Config {
log.Warn("Sanitizing invalid txpool lifetime", "provided", conf.Lifetime, "updated", DefaultConfig.Lifetime)
conf.Lifetime = DefaultConfig.Lifetime
}
if config.CustomValidationEnabled && conf.CustomValidator == nil {
log.Warn("Custom transaction validator is enabled but not configured")
}
return conf
}
@ -613,7 +617,7 @@ func (pool *LegacyPool) validateTxBasics(tx *types.Transaction, local bool) erro
func (pool *LegacyPool) validateTxWithCustomValidator(tx *types.Transaction, local bool) error {
if pool.config.CustomValidationEnabled {
opts := &txpool.CustomValidationOptions{}
if err := txpool.ValidateTransactionWithCustomValidator(tx, pool.currentHead.Load(), pool.signer, opts); err != nil {
if err := pool.config.CustomValidator.Validate(tx, pool.currentHead.Load(), pool.signer, opts); err != nil {
return err
}
}

View file

@ -0,0 +1,90 @@
package legacypool
import (
"errors"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"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"
)
// Max returns the larger of x or y.
func max(x, y int64) int64 {
if x < y {
return y
}
return x
}
func min(x, y int64) int64 {
if x > y {
return y
}
return x
}
func TestCustomTxValidationEnforced(t *testing.T) {
t.Parallel()
// Create the pool to test the pricing enforcement with
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
blockchain := newTestBlockChain(eip1559Config, 10000000, statedb, new(event.Feed))
txPoolConfig := DefaultConfig
if txPoolConfig.CustomValidationEnabled {
t.Fatalf("Custom validation should be disabled by default")
}
txPoolConfig.NoLocals = true
pool := New(txPoolConfig, blockchain)
pool.Init(new(big.Int).SetUint64(txPoolConfig.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver())
defer pool.Close()
key, _ := crypto.GenerateKey()
testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000))
tx := pricedTransaction(0, 100000, big.NewInt(3), key)
pool.SetGasTip(big.NewInt(tx.GasPrice().Int64()))
if err := pool.Add([]*types.Transaction{tx}, true, false)[0]; err != nil {
t.Fatalf("TxValidation enforced in default config despite being disabled by default")
}
// Modifying local config to enable custom validation
poolWithCustomValidationConfig := DefaultConfig
poolWithCustomValidationConfig.CustomValidationEnabled = true
poolWithCustomValidationConfig.NoLocals = true
bannedKey, _ := crypto.GenerateKey()
bannedTx := pricedTransaction(0, 990000, big.NewInt(4), bannedKey)
bannedAddresses := []common.Address{crypto.PubkeyToAddress(bannedKey.PublicKey)}
hypernativeCustomValidatorConfig := &txpool.CustomValidatorConfigHypernative{
BannedAddresses: bannedAddresses,
}
poolWithCustomValidationConfig.CustomValidator = txpool.NewHypernativeValidator(hypernativeCustomValidatorConfig)
poolWithCustomValidation := New(poolWithCustomValidationConfig, blockchain)
poolWithCustomValidation.Init(new(big.Int).SetUint64(poolWithCustomValidationConfig.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver())
defer poolWithCustomValidation.Close()
testAddBalance(pool, crypto.PubkeyToAddress(bannedKey.PublicKey), big.NewInt(1000000))
testAddBalance(poolWithCustomValidation, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000))
gasTip := min(tx.GasPrice().Int64(), bannedTx.GasPrice().Int64())
poolWithCustomValidation.SetGasTip(big.NewInt(gasTip))
if err := poolWithCustomValidation.Add([]*types.Transaction{tx}, true, false)[0]; err != nil {
t.Fatalf("Custom TxValidation enforced wrongly")
}
if err := poolWithCustomValidation.Add([]*types.Transaction{bannedTx}, true, false)[0]; !errors.Is(err, txpool.ErrCustomValidationFailed) {
t.Fatalf("Custom TxValidation not enforced for banned address")
}
}

View file

@ -1536,52 +1536,6 @@ func TestMinGasPriceEnforced(t *testing.T) {
}
}
func TestCustomTxValidationEnforced(t *testing.T) {
t.Parallel()
// Create the pool to test the pricing enforcement with
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
blockchain := newTestBlockChain(eip1559Config, 10000000, statedb, new(event.Feed))
txPoolConfig := DefaultConfig
if txPoolConfig.CustomValidationEnabled {
t.Fatalf("Custom validation should be disabled by default")
}
txPoolConfig.NoLocals = true
pool := New(txPoolConfig, blockchain)
pool.Init(new(big.Int).SetUint64(txPoolConfig.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver())
defer pool.Close()
key, _ := crypto.GenerateKey()
testAddBalance(pool, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000))
tx := pricedTransaction(0, 100000, big.NewInt(3), key)
pool.SetGasTip(big.NewInt(tx.GasPrice().Int64()))
// yo := pool.Add([]*types.Transaction{tx}, true, false)[0]
// t.Fatal(yo)
if err := pool.Add([]*types.Transaction{tx}, true, false)[0]; err != nil {
t.Fatalf("TxValidation enforced in default config despite being disabled by default")
}
// Modifying local config to enable custom validation
poolWithCustomValidationConfig := DefaultConfig
poolWithCustomValidationConfig.CustomValidationEnabled = true
poolWithCustomValidationConfig.NoLocals = true
poolWithCustomValidation := New(poolWithCustomValidationConfig, blockchain)
poolWithCustomValidation.Init(new(big.Int).SetUint64(poolWithCustomValidationConfig.PriceLimit), blockchain.CurrentBlock(), makeAddressReserver())
defer poolWithCustomValidation.Close()
testAddBalance(poolWithCustomValidation, crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000))
poolWithCustomValidation.SetGasTip(big.NewInt(tx.GasPrice().Int64()))
if err := poolWithCustomValidation.Add([]*types.Transaction{tx}, true, false)[0]; !errors.Is(err, txpool.ErrCustomValidationFailed) {
t.Fatalf("Custom TxValidation not enforced")
}
}
// Tests that setting the transaction pool gas price to a higher value correctly
// discards everything cheaper (legacy & dynamic fee) than that and moves any
// gapped transactions back from the pending pool to the queue.

View file

@ -40,9 +40,6 @@ type ValidationOptions struct {
MinTip *big.Int // Minimum gas tip needed to allow a transaction into the caller pool
}
type CustomValidationOptions struct {
}
// ValidateTransaction 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).
@ -248,10 +245,3 @@ func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, op
}
return nil
}
// FIXME add proper explanation (Kirill)
func ValidateTransactionWithCustomValidator(tx *types.Transaction, head *types.Header, signer types.Signer, opts *CustomValidationOptions) error {
// Ensure transactions not implemented by the calling pool are rejected
return ErrCustomValidationFailed
// return nil
}