Skip to content
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
9 changes: 1 addition & 8 deletions builder/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,14 +156,7 @@ func Register(stack *node.Node, backend *eth.Ethereum, cfg *Config) error {

var validator *blockvalidation.BlockValidationAPI
if cfg.DryRun {
var accessVerifier *blockvalidation.AccessVerifier
if cfg.ValidationBlocklist != "" {
accessVerifier, err = blockvalidation.NewAccessVerifierFromFile(cfg.ValidationBlocklist)
if err != nil {
return fmt.Errorf("failed to load validation blocklist %w", err)
}
}
validator = blockvalidation.NewBlockValidationAPI(backend, accessVerifier)
validator = blockvalidation.NewBlockValidationAPI(backend)
}

// TODO: move to proper flags
Expand Down
7 changes: 1 addition & 6 deletions cmd/geth/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,7 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
// Configure log filter RPC API.
filterSystem := utils.RegisterFilterAPI(stack, backend, &cfg.Eth)

bvConfig := blockvalidationapi.BlockValidationConfig{}
if ctx.IsSet(utils.BuilderBlockValidationBlacklistSourceFilePath.Name) {
bvConfig.BlacklistSourceFilePath = ctx.String(utils.BuilderBlockValidationBlacklistSourceFilePath.Name)
}

if err := blockvalidationapi.Register(stack, eth, bvConfig); err != nil {
if err := blockvalidationapi.Register(stack, eth); err != nil {
utils.Fatalf("Failed to register the Block Validation API: %v", err)
}

Expand Down
13 changes: 13 additions & 0 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -2502,6 +2502,8 @@ func (bc *BlockChain) ValidatePayload(block *types.Block, feeRecipient common.Ad
// and dangling prefetcher, without defering each and holding on live refs.
defer statedb.StopPrefetcher()

balanceBefore := statedb.GetBalance(feeRecipient)

receipts, _, usedGas, err := bc.processor.Process(block, statedb, vmConfig)
if err != nil {
return err
Expand All @@ -2515,6 +2517,17 @@ func (bc *BlockChain) ValidatePayload(block *types.Block, feeRecipient common.Ad
return err
}

// First just check the balance delta to see if it matches.
balanceAfter := statedb.GetBalance(feeRecipient)
feeRecipientDiff := new(big.Int).Sub(balanceAfter, balanceBefore)

// If diff is sufficiently large, just return success.
if feeRecipientDiff.Cmp(expectedProfit) >= 0 {
return nil
}
log.Warn(fmt.Sprintf("fee recipient diff %s is less than expected %s. checking for last transaction", feeRecipientDiff.String(), expectedProfit.String()))

// Flashbots logic for last transaction checks.
if len(receipts) == 0 {
return errors.New("no proposer payment receipt")
}
Expand Down
147 changes: 7 additions & 140 deletions eth/block-validation/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,13 @@ import (
"encoding/json"
"errors"
"fmt"
"math/big"
"os"

capellaapi "github.com/attestantio/go-builder-client/api/capella"
"github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc"
Expand All @@ -23,50 +19,6 @@ import (
boostTypes "github.com/flashbots/go-boost-utils/types"
)

type BlacklistedAddresses []common.Address

type AccessVerifier struct {
blacklistedAddresses map[common.Address]struct{}
}

func (a *AccessVerifier) verifyTraces(tracer *logger.AccessListTracer) error {
log.Trace("x", "tracer.AccessList()", tracer.AccessList())
for _, accessTuple := range tracer.AccessList() {
// TODO: should we ignore common.Address{}?
if _, found := a.blacklistedAddresses[accessTuple.Address]; found {
log.Info("bundle accesses blacklisted address", "address", accessTuple.Address)
return fmt.Errorf("blacklisted address %s in execution trace", accessTuple.Address.String())
}
}

return nil
}

func (a *AccessVerifier) isBlacklisted(addr common.Address) error {
if _, present := a.blacklistedAddresses[addr]; present {
return fmt.Errorf("transaction from blacklisted address %s", addr.String())
}
return nil
}

func (a *AccessVerifier) verifyTransactions(signer types.Signer, txs types.Transactions) error {
for _, tx := range txs {
from, err := signer.Sender(tx)
if err == nil {
if _, present := a.blacklistedAddresses[from]; present {
return fmt.Errorf("transaction from blacklisted address %s", from.String())
}
}
to := tx.To()
if to != nil {
if _, present := a.blacklistedAddresses[*to]; present {
return fmt.Errorf("transaction to blacklisted address %s", to.String())
}
}
}
return nil
}

func verifyWithdrawals(withdrawals types.Withdrawals, expectedWithdrawalsRoot common.Hash, isShanghai bool) error {
if !isShanghai {
// Reject payload attributes with withdrawals before shanghai
Expand All @@ -83,62 +35,26 @@ func verifyWithdrawals(withdrawals types.Withdrawals, expectedWithdrawalsRoot co
return nil
}

func NewAccessVerifierFromFile(path string) (*AccessVerifier, error) {
bytes, err := os.ReadFile(path)
if err != nil {
return nil, err
}

var ba BlacklistedAddresses
if err := json.Unmarshal(bytes, &ba); err != nil {
return nil, err
}

blacklistedAddresses := make(map[common.Address]struct{}, len(ba))
for _, address := range ba {
blacklistedAddresses[address] = struct{}{}
}

return &AccessVerifier{
blacklistedAddresses: blacklistedAddresses,
}, nil
}

type BlockValidationConfig struct {
BlacklistSourceFilePath string
}

// Register adds catalyst APIs to the full node.
func Register(stack *node.Node, backend *eth.Ethereum, cfg BlockValidationConfig) error {
var accessVerifier *AccessVerifier
if cfg.BlacklistSourceFilePath != "" {
var err error
accessVerifier, err = NewAccessVerifierFromFile(cfg.BlacklistSourceFilePath)
if err != nil {
return err
}
}

func Register(stack *node.Node, backend *eth.Ethereum) error {
stack.RegisterAPIs([]rpc.API{
{
Namespace: "flashbots",
Service: NewBlockValidationAPI(backend, accessVerifier),
Service: NewBlockValidationAPI(backend),
},
})
return nil
}

type BlockValidationAPI struct {
eth *eth.Ethereum
accessVerifier *AccessVerifier
eth *eth.Ethereum
}

// NewConsensusAPI creates a new consensus api for the given backend.
// The underlying blockchain needs to have a valid terminal total difficulty set.
func NewBlockValidationAPI(eth *eth.Ethereum, accessVerifier *AccessVerifier) *BlockValidationAPI {
func NewBlockValidationAPI(eth *eth.Ethereum) *BlockValidationAPI {
return &BlockValidationAPI{
eth: eth,
accessVerifier: accessVerifier,
eth: eth,
}
}

Expand Down Expand Up @@ -179,37 +95,12 @@ func (api *BlockValidationAPI) ValidateBuilderSubmissionV1(params *BuilderBlockV
feeRecipient := common.BytesToAddress(params.Message.ProposerFeeRecipient[:])
expectedProfit := params.Message.Value.BigInt()

var vmconfig vm.Config
var tracer *logger.AccessListTracer = nil
if api.accessVerifier != nil {
if err := api.accessVerifier.isBlacklisted(block.Coinbase()); err != nil {
return err
}
if err := api.accessVerifier.isBlacklisted(feeRecipient); err != nil {
return err
}
if err := api.accessVerifier.verifyTransactions(types.LatestSigner(api.eth.BlockChain().Config()), block.Transactions()); err != nil {
return err
}
isPostMerge := true // the call is PoS-native
timestamp := params.BuilderSubmitBlockRequest.ExecutionPayload.Timestamp
precompiles := vm.ActivePrecompiles(api.eth.APIBackend.ChainConfig().Rules(new(big.Int).SetUint64(params.ExecutionPayload.BlockNumber), isPostMerge, timestamp))
tracer = logger.NewAccessListTracer(nil, common.Address{}, common.Address{}, precompiles)
vmconfig = vm.Config{Tracer: tracer, Debug: true}
}

err = api.eth.BlockChain().ValidatePayload(block, feeRecipient, expectedProfit, params.RegisteredGasLimit, vmconfig)
err = api.eth.BlockChain().ValidatePayload(block, feeRecipient, expectedProfit, params.RegisteredGasLimit, *api.eth.BlockChain().GetVMConfig())
if err != nil {
log.Error("invalid payload", "hash", payload.BlockHash.String(), "number", payload.BlockNumber, "parentHash", payload.ParentHash.String(), "err", err)
return err
}

if api.accessVerifier != nil && tracer != nil {
if err := api.accessVerifier.verifyTraces(tracer); err != nil {
return err
}
}

log.Info("validated block", "hash", block.Hash(), "number", block.NumberU64(), "parentHash", block.ParentHash())
return nil
}
Expand Down Expand Up @@ -278,36 +169,12 @@ func (api *BlockValidationAPI) ValidateBuilderSubmissionV2(params *BuilderBlockV
feeRecipient := common.BytesToAddress(params.Message.ProposerFeeRecipient[:])
expectedProfit := params.Message.Value.ToBig()

var vmconfig vm.Config
var tracer *logger.AccessListTracer = nil
if api.accessVerifier != nil {
if err := api.accessVerifier.isBlacklisted(block.Coinbase()); err != nil {
return err
}
if err := api.accessVerifier.isBlacklisted(feeRecipient); err != nil {
return err
}
if err := api.accessVerifier.verifyTransactions(types.LatestSigner(api.eth.BlockChain().Config()), block.Transactions()); err != nil {
return err
}
isPostMerge := true // the call is PoS-native
precompiles := vm.ActivePrecompiles(api.eth.APIBackend.ChainConfig().Rules(new(big.Int).SetUint64(params.ExecutionPayload.BlockNumber), isPostMerge, params.ExecutionPayload.Timestamp))
tracer = logger.NewAccessListTracer(nil, common.Address{}, common.Address{}, precompiles)
vmconfig = vm.Config{Tracer: tracer, Debug: true}
}

err = api.eth.BlockChain().ValidatePayload(block, feeRecipient, expectedProfit, params.RegisteredGasLimit, vmconfig)
err = api.eth.BlockChain().ValidatePayload(block, feeRecipient, expectedProfit, params.RegisteredGasLimit, *api.eth.BlockChain().GetVMConfig())
if err != nil {
log.Error("invalid payload", "hash", payload.BlockHash.String(), "number", payload.BlockNumber, "parentHash", payload.ParentHash.String(), "err", err)
return err
}

if api.accessVerifier != nil && tracer != nil {
if err := api.accessVerifier.verifyTraces(tracer); err != nil {
return err
}
}

log.Info("validated block", "hash", block.Hash(), "number", block.NumberU64(), "parentHash", block.ParentHash())
return nil
}