This repository has been archived by the owner on Oct 25, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 138
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
552 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
package blockvalidation | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"math/big" | ||
"os" | ||
|
||
"github.com/ethereum/go-ethereum/cmd/utils" | ||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/ethereum/go-ethereum/core/beacon" | ||
"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" | ||
"github.com/urfave/cli/v2" | ||
|
||
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.Info("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) 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 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 | ||
} | ||
|
||
// Register adds catalyst APIs to the full node. | ||
func Register(stack *node.Node, backend *eth.Ethereum, ctx *cli.Context) error { | ||
var accessVerifier *AccessVerifier | ||
if ctx.IsSet(utils.BuilderBlockValidationBlacklistSourceFilePath.Name) { | ||
var err error | ||
accessVerifier, err = NewAccessVerifierFromFile(ctx.String(utils.BuilderBlockValidationBlacklistSourceFilePath.Name)) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
stack.RegisterAPIs([]rpc.API{ | ||
{ | ||
Namespace: "flashbots", | ||
Service: NewBlockValidationAPI(backend, accessVerifier), | ||
}, | ||
}) | ||
return nil | ||
} | ||
|
||
type BlockValidationAPI struct { | ||
eth *eth.Ethereum | ||
accessVerifier *AccessVerifier | ||
} | ||
|
||
// 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 { | ||
return &BlockValidationAPI{ | ||
eth: eth, | ||
accessVerifier: accessVerifier, | ||
} | ||
} | ||
|
||
func (api *BlockValidationAPI) ValidateBuilderSubmissionV1(params *boostTypes.BuilderSubmitBlockRequest) error { | ||
// TODO: fuzztest, make sure the validation is sound | ||
// TODO: handle context! | ||
|
||
if params.ExecutionPayload == nil { | ||
return errors.New("nil execution payload") | ||
} | ||
payload := params.ExecutionPayload | ||
block, err := beacon.ExecutionPayloadToBlock(payload) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if params.Message.ParentHash != boostTypes.Hash(block.ParentHash()) { | ||
return fmt.Errorf("incorrect ParentHash %s, expected %s", params.Message.ParentHash.String(), block.ParentHash().String()) | ||
} | ||
|
||
if params.Message.BlockHash != boostTypes.Hash(block.Hash()) { | ||
return fmt.Errorf("incorrect BlockHash %s, expected %s", params.Message.BlockHash.String(), block.Hash().String()) | ||
} | ||
|
||
if params.Message.GasLimit != block.GasLimit() { | ||
return fmt.Errorf("incorrect GasLimit %d, expected %d", params.Message.GasLimit, block.GasLimit()) | ||
} | ||
|
||
if params.Message.GasUsed != block.GasUsed() { | ||
return fmt.Errorf("incorrect GasUsed %d, expected %d", params.Message.GasUsed, block.GasUsed()) | ||
} | ||
|
||
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.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)) | ||
tracer = logger.NewAccessListTracer(nil, common.Address{}, common.Address{}, precompiles) | ||
vmconfig = vm.Config{Tracer: tracer, Debug: true} | ||
} | ||
|
||
err = api.eth.BlockChain().ValidatePayload(block, feeRecipient, expectedProfit, vmconfig) | ||
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 | ||
} |
Oops, something went wrong.