Skip to content
This repository was archived by the owner on Oct 25, 2024. 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
5 changes: 4 additions & 1 deletion builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,10 +319,13 @@ func (b *Builder) submitCapellaBlock(block *types.Block, blockValue *big.Int, or
}

if b.dryRun {
err = b.validator.ValidateBuilderSubmissionV2(&blockvalidation.BuilderBlockValidationRequestV2{SubmitBlockRequest: blockSubmitReq, RegisteredGasLimit: vd.GasLimit})
resp, err := b.validator.ValidateBuilderSubmissionV2(&blockvalidation.BuilderBlockValidationRequestV2{SubmitBlockRequest: blockSubmitReq, RegisteredGasLimit: vd.GasLimit})
if err != nil {
log.Error("could not validate block for capella", "err", err)
}
if resp.NewGasLimit != payload.GasLimit {
log.Error("gas limit adjusted needed for capella", "gasLimit", payload.GasLimit, "newGasLimit", resp.NewGasLimit)
}
} else {
go b.ds.ConsumeBuiltBlock(block, blockValue, ordersClosedAt, sealedAt, commitedBundles, allBundles, usedSbundles, &blockBidMsg)
err = b.relay.SubmitBlockCapella(&blockSubmitReq, vd)
Expand Down
22 changes: 22 additions & 0 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -2494,6 +2494,28 @@ func (bc *BlockChain) SetBlockValidatorAndProcessorForTesting(v Validator, p Pro
bc.processor = p
}

// AdjustedGasLimit calculates the gas limit for the given block and returns
// the calculated limit.
func (bc *BlockChain) AdjustedGasLimit(block *types.Block, registeredGasLimit uint64) (uint64, error) {
header := block.Header()
if err := bc.engine.VerifyHeader(bc, header, true); err != nil {
return 0, err
}

current := bc.CurrentBlock()
reorg, err := bc.forker.ReorgNeeded(current, header)
if err == nil && reorg {
return 0, errors.New("block requires a reorg")
}

parent := bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
if parent == nil {
return 0, errors.New("parent not found")
}

return utils.CalcGasLimit(parent.GasLimit, registeredGasLimit), nil
}

// ValidatePayload validates the payload of the block.
// It returns nil if the payload is valid, otherwise it returns an error.
// - `useBalanceDiffProfit` if set to false, proposer payment is assumed to be in the last transaction of the block
Expand Down
59 changes: 45 additions & 14 deletions eth/block-validation/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,32 +228,42 @@ func (r *BuilderBlockValidationRequestV2) UnmarshalJSON(data []byte) error {
return nil
}

func (api *BlockValidationAPI) ValidateBuilderSubmissionV2(params *BuilderBlockValidationRequestV2) error {
// Response object for the block validation v2 request. Contains a gas limit
// and a block hash that are valid for the `RegisteredGasLimit` property of the
// request. This is to allow post-building adjustment of the gas limit in the
// built block.
type BuilderBlockValidationResponseV2 struct {
NewGasLimit uint64 `json:"new_gas_limit,string"`
NewBlockHash string `json:"new_block_hash"`
}

func (api *BlockValidationAPI) ValidateBuilderSubmissionV2(params *BuilderBlockValidationRequestV2) (*BuilderBlockValidationResponseV2, error) {
// TODO: fuzztest, make sure the validation is sound
// TODO: handle context!
if params.ExecutionPayload == nil {
return errors.New("nil execution payload")
return nil, errors.New("nil execution payload")
}

payload := params.ExecutionPayload
block, err := engine.ExecutionPayloadV2ToBlock(payload)
if err != nil {
return err
return nil, err
}

if params.Message.ParentHash != phase0.Hash32(block.ParentHash()) {
return fmt.Errorf("incorrect ParentHash %s, expected %s", params.Message.ParentHash.String(), block.ParentHash().String())
return nil, fmt.Errorf("incorrect ParentHash %s, expected %s", params.Message.ParentHash.String(), block.ParentHash().String())
}

if params.Message.BlockHash != phase0.Hash32(block.Hash()) {
return fmt.Errorf("incorrect BlockHash %s, expected %s", params.Message.BlockHash.String(), block.Hash().String())
return nil, 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())
return nil, 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())
return nil, fmt.Errorf("incorrect GasUsed %d, expected %d", params.Message.GasUsed, block.GasUsed())
}

feeRecipient := common.BytesToAddress(params.Message.ProposerFeeRecipient[:])
Expand All @@ -263,32 +273,53 @@ func (api *BlockValidationAPI) ValidateBuilderSubmissionV2(params *BuilderBlockV
var tracer *logger.AccessListTracer = nil
if api.accessVerifier != nil {
if err := api.accessVerifier.isBlacklisted(block.Coinbase()); err != nil {
return err
return nil, err
}
if err := api.accessVerifier.isBlacklisted(feeRecipient); err != nil {
return err
return nil, err
}
if err := api.accessVerifier.verifyTransactions(types.LatestSigner(api.eth.BlockChain().Config()), block.Transactions()); err != nil {
return err
return nil, 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, api.useBalanceDiffProfit)
bc := api.eth.BlockChain()

// After validation of the message, we modify the block's gas limit
adjustedGasLimit, err := bc.AdjustedGasLimit(block, params.RegisteredGasLimit)
if err != nil {
return nil, err
}
// If the gas limit was adjusted, we need to regenerate the block
// as mutating them is generally unsupported
if payload.GasLimit != adjustedGasLimit {
payload.GasLimit = adjustedGasLimit
block, err = engine.ExecutionPayloadV2ToBlock(payload)
if err != nil {
return nil, err
}
}

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

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

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

return &BuilderBlockValidationResponseV2{
NewGasLimit: block.GasLimit(),
NewBlockHash: block.Hash().String(),
}, nil
}
37 changes: 24 additions & 13 deletions eth/block-validation/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,16 +246,16 @@ func TestValidateBuilderSubmissionV2(t *testing.T) {
WithdrawalsRoot: withdrawalsRoot,
}

require.ErrorContains(t, api.ValidateBuilderSubmissionV2(blockRequest), "inaccurate payment")
_, err = api.ValidateBuilderSubmissionV2(blockRequest)
require.ErrorContains(t, err, "inaccurate payment")
blockRequest.Message.Value = uint256.NewInt(149842511727212)
require.NoError(t, api.ValidateBuilderSubmissionV2(blockRequest))
_, err = api.ValidateBuilderSubmissionV2(blockRequest)
require.NoError(t, err)

blockRequest.Message.GasLimit += 1
blockRequest.ExecutionPayload.GasLimit += 1
updatePayloadHashV2(t, blockRequest)

require.ErrorContains(t, api.ValidateBuilderSubmissionV2(blockRequest), "incorrect gas limit set")

blockRequest.Message.GasLimit -= 1
blockRequest.ExecutionPayload.GasLimit -= 1
updatePayloadHashV2(t, blockRequest)
Expand All @@ -267,20 +267,25 @@ func TestValidateBuilderSubmissionV2(t *testing.T) {
testAddr: {},
},
}
require.ErrorContains(t, api.ValidateBuilderSubmissionV2(blockRequest), "transaction from blacklisted address 0x71562b71999873DB5b286dF957af199Ec94617F7")

_, err = api.ValidateBuilderSubmissionV2(blockRequest)
require.ErrorContains(t, err, "transaction from blacklisted address 0x71562b71999873DB5b286dF957af199Ec94617F7")

// Test tx to blacklisted address
api.accessVerifier = &AccessVerifier{
blacklistedAddresses: map[common.Address]struct{}{
{0x16}: {},
},
}
require.ErrorContains(t, api.ValidateBuilderSubmissionV2(blockRequest), "transaction to blacklisted address 0x1600000000000000000000000000000000000000")

_, err = api.ValidateBuilderSubmissionV2(blockRequest)
require.ErrorContains(t, err, "transaction to blacklisted address 0x1600000000000000000000000000000000000000")

api.accessVerifier = nil

blockRequest.Message.GasUsed = 10
require.ErrorContains(t, api.ValidateBuilderSubmissionV2(blockRequest), "incorrect GasUsed 10, expected 119996")
_, err = api.ValidateBuilderSubmissionV2(blockRequest)
require.ErrorContains(t, err, "incorrect GasUsed 10, expected 119996")
blockRequest.Message.GasUsed = execData.GasUsed

newTestKey, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f290")
Expand All @@ -297,7 +302,8 @@ func TestValidateBuilderSubmissionV2(t *testing.T) {
copy(invalidPayload.ReceiptsRoot[:], hexutil.MustDecode("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")[:32])
blockRequest.ExecutionPayload = invalidPayload
updatePayloadHashV2(t, blockRequest)
require.ErrorContains(t, api.ValidateBuilderSubmissionV2(blockRequest), "could not apply tx 4", "insufficient funds for gas * price + value")
_, err = api.ValidateBuilderSubmissionV2(blockRequest)
require.ErrorContains(t, err, "could not apply tx 4", "insufficient funds for gas * price + value")
}

func updatePayloadHash(t *testing.T, blockRequest *BuilderBlockValidationRequest) {
Expand Down Expand Up @@ -703,21 +709,24 @@ func TestValidateBuilderSubmissionV2_CoinbasePaymentDefault(t *testing.T) {

req, err := executableDataToBlockValidationRequest(execData, testValidatorAddr, value, withdrawalsRoot)
require.NoError(t, err)
require.NoError(t, api.ValidateBuilderSubmissionV2(req))
_, err = api.ValidateBuilderSubmissionV2(req)
require.NoError(t, err)

// try to claim less profit than expected, should work
value.SetUint64(expectedProfit - 1)

req, err = executableDataToBlockValidationRequest(execData, testValidatorAddr, value, withdrawalsRoot)
require.NoError(t, err)
require.NoError(t, api.ValidateBuilderSubmissionV2(req))
_, err = api.ValidateBuilderSubmissionV2(req)
require.NoError(t, err)

// try to claim more profit than expected, should fail
value.SetUint64(expectedProfit + 1)

req, err = executableDataToBlockValidationRequest(execData, testValidatorAddr, value, withdrawalsRoot)
require.NoError(t, err)
require.ErrorContains(t, api.ValidateBuilderSubmissionV2(req), "payment")
_, err = api.ValidateBuilderSubmissionV2(req)
require.ErrorContains(t, err, "payment")
}

func TestValidateBuilderSubmissionV2_Blocklist(t *testing.T) {
Expand Down Expand Up @@ -777,8 +786,10 @@ func TestValidateBuilderSubmissionV2_Blocklist(t *testing.T) {
req, err := executableDataToBlockValidationRequest(execData, testValidatorAddr, common.Big0, withdrawalsRoot)
require.NoError(t, err)

require.NoError(t, apiNoBlock.ValidateBuilderSubmissionV2(req))
require.ErrorContains(t, apiWithBlock.ValidateBuilderSubmissionV2(req), "blacklisted")
_, err = apiNoBlock.ValidateBuilderSubmissionV2(req)
require.NoError(t, err)
_, err = apiWithBlock.ValidateBuilderSubmissionV2(req)
require.ErrorContains(t, err, "blacklisted")
})
}
}