Skip to content
This repository was archived by the owner on Mar 16, 2023. It is now read-only.
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
381 changes: 6 additions & 375 deletions README.md

Large diffs are not rendered by default.

379 changes: 379 additions & 0 deletions README.original.md

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions cmd/geth/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/accounts/usbwallet"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/core/rawdb"
blockvalidationapi "github.com/ethereum/go-ethereum/eth/block-validation"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/internal/flags"
Expand Down Expand Up @@ -182,6 +183,10 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
}
}

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

// Configure GraphQL if requested
if ctx.IsSet(utils.GraphQLEnabledFlag.Name) {
utils.RegisterGraphQLService(stack, backend, cfg.Node)
Expand Down
2 changes: 1 addition & 1 deletion cmd/geth/consolecmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (
)

const (
ipcAPIs = "admin:1.0 debug:1.0 engine:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0"
ipcAPIs = "admin:1.0 debug:1.0 engine:1.0 eth:1.0 ethash:1.0 flashbots:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0"
httpAPIs = "eth:1.0 net:1.0 rpc:1.0 web3:1.0"
)

Expand Down
34 changes: 34 additions & 0 deletions core/beacon/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/trie"

boostTypes "github.com/flashbots/go-boost-utils/types"
)

//go:generate go run github.com/fjl/gencodec -type PayloadAttributesV1 -field-override payloadAttributesMarshaling -out gen_blockparams.go
Expand Down Expand Up @@ -179,6 +181,38 @@ func ExecutableDataToBlock(params ExecutableDataV1) (*types.Block, error) {
return block, nil
}

func ExecutionPayloadToBlock(payload *boostTypes.ExecutionPayload) (*types.Block, error) {
// TODO: separate decode function to avoid allocating twice
transactionBytes := make([][]byte, len(payload.Transactions))
for i, txHexBytes := range payload.Transactions {
transactionBytes[i] = txHexBytes[:]
}
txs, err := decodeTransactions(transactionBytes)
if err != nil {
return nil, err
}

header := &types.Header{
ParentHash: common.Hash(payload.ParentHash),
UncleHash: types.EmptyUncleHash,
Coinbase: common.Address(payload.FeeRecipient),
Root: common.Hash(payload.StateRoot),
TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
ReceiptHash: common.Hash(payload.ReceiptsRoot),
Bloom: types.BytesToBloom(payload.LogsBloom[:]),
Difficulty: common.Big0,
Number: new(big.Int).SetUint64(payload.BlockNumber),
GasLimit: payload.GasLimit,
GasUsed: payload.GasUsed,
Time: payload.Timestamp,
BaseFee: payload.BaseFeePerGas.BigInt(),
Extra: payload.ExtraData,
MixDigest: common.Hash(payload.Random),
}
block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */)
return block, nil
}

// BlockToExecutableData constructs the executableDataV1 structure by filling the
// fields from the given block. It assumes the given block is post-merge block.
func BlockToExecutableData(block *types.Block) *ExecutableDataV1 {
Expand Down
46 changes: 46 additions & 0 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -2417,3 +2417,49 @@ func (bc *BlockChain) SetBlockValidatorAndProcessorForTesting(v Validator, p Pro
bc.validator = v
bc.processor = p
}

func (bc *BlockChain) ValidatePayload(block *types.Block, feeRecipient common.Address, expectedProfit *big.Int, vmConfig vm.Config) error {
header := block.Header()
abort, results := bc.engine.VerifyHeaders(bc, []*types.Header{header}, []bool{false}) // TODO: verify seals
defer close(abort)
err := <-results

if err != nil {
return err
}

current := bc.CurrentBlock()
reorg, err := bc.forker.ReorgNeeded(current.Header(), header)
if reorg {
return errors.New("block requires a reorg")
}
parent := bc.GetHeader(block.ParentHash(), block.NumberU64()-1)

statedb, err := state.New(parent.Root, bc.stateCache, bc.snaps)
if err != nil {
// The chain importer is starting and stopping trie prefetchers. If a bad
// block or other error is hit however, an early return may not properly
// terminate the background threads. This defer ensures that we clean up
// and dangling prefetcher, without defering each and holding on live refs.
defer statedb.StopPrefetcher()
}

// verify profit
balanceBefore := statedb.GetBalance(feeRecipient)
receipts, _, usedGas, err := bc.processor.Process(block, statedb, vmConfig)
if err != nil {
return err
}

if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil {
return err
}

balanceAfter := statedb.GetBalance(feeRecipient)
feeRecipientDiff := new(big.Int).Sub(balanceAfter, balanceBefore)
if feeRecipientDiff.Cmp(expectedProfit) != 0 {
return fmt.Errorf("inaccurate payment %s, expected %s", feeRecipientDiff.String(), expectedProfit.String())
}

return nil
}
86 changes: 86 additions & 0 deletions eth/block-validation/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package blockvalidation

import (
"errors"
"fmt"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/beacon"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth"
"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

// Register adds catalyst APIs to the full node.
func Register(stack *node.Node, backend *eth.Ethereum, ctx *cli.Context) error {
stack.RegisterAPIs([]rpc.API{
{
Namespace: "flashbots",
Service: NewBlockValidationAPI(backend),
},
})
return nil
}

type BlockValidationAPI struct {
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) *BlockValidationAPI {
return &BlockValidationAPI{
eth: eth,
}
}

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

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
}

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