From 5db0c7b09e780fe1a706a7f663a3fbc532e24316 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 12 Sep 2024 17:47:37 +0200 Subject: [PATCH 01/32] all: implement flat deposit requests encoding --- beacon/engine/gen_ed.go | 18 ++- beacon/engine/types.go | 37 ++---- cmd/evm/internal/t8ntool/execution.go | 13 +- core/block_validator.go | 6 +- core/blockchain_test.go | 173 +++++++++++++------------- core/chain_makers.go | 6 +- core/genesis.go | 4 +- core/state_processor.go | 10 +- core/types.go | 2 +- core/types/block.go | 19 ++- core/types/deposit.go | 81 ++++-------- core/types/gen_deposit_json.go | 70 ----------- core/types/hashes.go | 2 +- core/types/request.go | 157 ----------------------- eth/catalyst/api.go | 41 +++--- eth/catalyst/api_test.go | 13 +- eth/downloader/queue.go | 2 +- eth/protocols/eth/handlers.go | 2 +- eth/protocols/eth/protocol.go | 6 +- ethclient/ethclient.go | 14 ++- 20 files changed, 198 insertions(+), 478 deletions(-) delete mode 100644 core/types/gen_deposit_json.go delete mode 100644 core/types/request.go diff --git a/beacon/engine/gen_ed.go b/beacon/engine/gen_ed.go index b2eb1dc9822f..2494bfb1f9e0 100644 --- a/beacon/engine/gen_ed.go +++ b/beacon/engine/gen_ed.go @@ -34,7 +34,7 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) { Withdrawals []*types.Withdrawal `json:"withdrawals"` BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"` ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"` - Deposits types.Deposits `json:"depositRequests"` + Requests []hexutil.Bytes `json:"depositRequests"` ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"` } var enc ExecutableData @@ -60,7 +60,12 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) { enc.Withdrawals = e.Withdrawals enc.BlobGasUsed = (*hexutil.Uint64)(e.BlobGasUsed) enc.ExcessBlobGas = (*hexutil.Uint64)(e.ExcessBlobGas) - enc.Deposits = e.Deposits + if e.Requests != nil { + enc.Requests = make([]hexutil.Bytes, len(e.Requests)) + for k, v := range e.Requests { + enc.Requests[k] = v + } + } enc.ExecutionWitness = e.ExecutionWitness return json.Marshal(&enc) } @@ -85,7 +90,7 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error { Withdrawals []*types.Withdrawal `json:"withdrawals"` BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"` ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"` - Deposits *types.Deposits `json:"depositRequests"` + Requests []hexutil.Bytes `json:"depositRequests"` ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"` } var dec ExecutableData @@ -160,8 +165,11 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error { if dec.ExcessBlobGas != nil { e.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas) } - if dec.Deposits != nil { - e.Deposits = *dec.Deposits + if dec.Requests != nil { + e.Requests = make([][]byte, len(dec.Requests)) + for k, v := range dec.Requests { + e.Requests[k] = v + } } if dec.ExecutionWitness != nil { e.ExecutionWitness = dec.ExecutionWitness diff --git a/beacon/engine/types.go b/beacon/engine/types.go index 74c56f403de2..1d0cfd736434 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -76,7 +76,7 @@ type ExecutableData struct { Withdrawals []*types.Withdrawal `json:"withdrawals"` BlobGasUsed *uint64 `json:"blobGasUsed"` ExcessBlobGas *uint64 `json:"excessBlobGas"` - Deposits types.Deposits `json:"depositRequests"` + Requests [][]byte `json:"depositRequests"` ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"` } @@ -91,6 +91,7 @@ type executableDataMarshaling struct { LogsBloom hexutil.Bytes Transactions []hexutil.Bytes BlobGasUsed *hexutil.Uint64 + Requests []hexutil.Bytes ExcessBlobGas *hexutil.Uint64 } @@ -257,16 +258,9 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H withdrawalsRoot = &h } // Compute requestsHash if any requests are non-nil. - var ( - requestsHash *common.Hash - requests types.Requests - ) - if data.Deposits != nil { - requests = make(types.Requests, 0) - for _, d := range data.Deposits { - requests = append(requests, types.NewRequest(d)) - } - h := types.DeriveSha(requests, trie.NewStackTrie(nil)) + var requestsHash *common.Hash + if data.Requests != nil { + h := types.CalcRequestHash(data.Requests) requestsHash = &h } header := &types.Header{ @@ -292,7 +286,7 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H RequestsHash: requestsHash, } return types.NewBlockWithHeader(header). - WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals, Requests: requests}). + WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals, Requests: data.Requests}). WithWitness(data.ExecutionWitness), nil } @@ -318,6 +312,7 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types. Withdrawals: block.Withdrawals(), BlobGasUsed: block.BlobGasUsed(), ExcessBlobGas: block.ExcessBlobGas(), + Requests: block.Requests(), ExecutionWitness: block.ExecutionWitness(), } bundle := BlobsBundleV1{ @@ -332,30 +327,14 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types. bundle.Proofs = append(bundle.Proofs, hexutil.Bytes(sidecar.Proofs[j][:])) } } - setRequests(block.Requests(), data) return &ExecutionPayloadEnvelope{ExecutionPayload: data, BlockValue: fees, BlobsBundle: &bundle, Override: false} } -// setRequests differentiates the different request types and -// assigns them to the associated fields in ExecutableData. -func setRequests(requests types.Requests, data *ExecutableData) { - if requests != nil { - // If requests is non-nil, it means deposits are available in block and we - // should return an empty slice instead of nil if there are no deposits. - data.Deposits = make(types.Deposits, 0) - } - for _, r := range requests { - if d, ok := r.Inner().(*types.Deposit); ok { - data.Deposits = append(data.Deposits, d) - } - } -} - // ExecutionPayloadBody is used in the response to GetPayloadBodiesByHash and GetPayloadBodiesByRange type ExecutionPayloadBody struct { TransactionData []hexutil.Bytes `json:"transactions"` Withdrawals []*types.Withdrawal `json:"withdrawals"` - Deposits types.Deposits `json:"depositRequests"` + Requests []hexutil.Bytes `json:"requests"` } // Client identifiers to support ClientVersionV1. diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 5fd1d6a4a6ad..63cd0f803602 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -67,7 +67,7 @@ type ExecutionResult struct { CurrentExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"` CurrentBlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed,omitempty"` RequestsHash *common.Hash `json:"requestsRoot,omitempty"` - DepositRequests *types.Deposits `json:"depositRequests,omitempty"` + Requests [][]byte `json:"requests,omitempty"` } type ommer struct { @@ -390,16 +390,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not parse requests logs: %v", err)) } // Calculate the requests root - h := types.DeriveSha(requests, trie.NewStackTrie(nil)) + h := types.CalcRequestHash(requests) execRs.RequestsHash = &h - // Get the deposits from the requests - deposits := make(types.Deposits, 0) - for _, req := range requests { - if dep, ok := req.Inner().(*types.Deposit); ok { - deposits = append(deposits, dep) - } - } - execRs.DepositRequests = &deposits + execRs.Requests = requests } // Re-create statedb instance with new root upon the updated database // for accessing latest states. diff --git a/core/block_validator.go b/core/block_validator.go index 4f51f5dc1788..04102a5efb37 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -145,9 +145,9 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD } // Validate the parsed requests match the expected header value. if header.RequestsHash != nil { - depositSha := types.DeriveSha(res.Requests, trie.NewStackTrie(nil)) - if depositSha != *header.RequestsHash { - return fmt.Errorf("invalid deposit root hash (remote: %x local: %x)", *header.RequestsHash, depositSha) + reqhash := types.CalcRequestHash(res.Requests) + if reqhash != *header.RequestsHash { + return fmt.Errorf("invalid deposit root hash (remote: %x local: %x)", *header.RequestsHash, reqhash) } } // Validate the state root against the received state root and throw diff --git a/core/blockchain_test.go b/core/blockchain_test.go index d157a7bc3c93..c37017201214 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -4228,89 +4228,90 @@ func TestEIP3651(t *testing.T) { } } -func TestEIP6110(t *testing.T) { - var ( - engine = beacon.NewFaker() - - // A sender who makes transactions, has some funds - key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - addr = crypto.PubkeyToAddress(key.PublicKey) - funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether)) - config = *params.AllEthashProtocolChanges - gspec = &Genesis{ - Config: &config, - Alloc: types.GenesisAlloc{ - addr: {Balance: funds}, - config.DepositContractAddress: { - // Simple deposit generator, source: https://gist.github.com/lightclient/54abb2af2465d6969fa6d1920b9ad9d7 - Code: common.Hex2Bytes("6080604052366103aa575f603067ffffffffffffffff811115610025576100246103ae565b5b6040519080825280601f01601f1916602001820160405280156100575781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f8151811061007d5761007c6103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f602067ffffffffffffffff8111156100c7576100c66103ae565b5b6040519080825280601f01601f1916602001820160405280156100f95781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f8151811061011f5761011e6103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f600867ffffffffffffffff811115610169576101686103ae565b5b6040519080825280601f01601f19166020018201604052801561019b5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f815181106101c1576101c06103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f606067ffffffffffffffff81111561020b5761020a6103ae565b5b6040519080825280601f01601f19166020018201604052801561023d5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f81518110610263576102626103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f600867ffffffffffffffff8111156102ad576102ac6103ae565b5b6040519080825280601f01601f1916602001820160405280156102df5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f81518110610305576103046103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f8081819054906101000a900460ff168092919061035090610441565b91906101000a81548160ff021916908360ff160217905550507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c585858585856040516103a09594939291906104d9565b60405180910390a1005b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f60ff82169050919050565b5f61044b82610435565b915060ff820361045e5761045d610408565b5b600182019050919050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f6104ab82610469565b6104b58185610473565b93506104c5818560208601610483565b6104ce81610491565b840191505092915050565b5f60a0820190508181035f8301526104f181886104a1565b9050818103602083015261050581876104a1565b9050818103604083015261051981866104a1565b9050818103606083015261052d81856104a1565b9050818103608083015261054181846104a1565b9050969550505050505056fea26469706673582212208569967e58690162d7d6fe3513d07b393b4c15e70f41505cbbfd08f53eba739364736f6c63430008190033"), - Nonce: 0, - Balance: big.NewInt(0), - }, - }, - } - ) - - gspec.Config.BerlinBlock = common.Big0 - gspec.Config.LondonBlock = common.Big0 - gspec.Config.TerminalTotalDifficulty = common.Big0 - gspec.Config.TerminalTotalDifficultyPassed = true - gspec.Config.ShanghaiTime = u64(0) - gspec.Config.CancunTime = u64(0) - gspec.Config.PragueTime = u64(0) - signer := types.LatestSigner(gspec.Config) - - _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) { - for i := 0; i < 5; i++ { - txdata := &types.DynamicFeeTx{ - ChainID: gspec.Config.ChainID, - Nonce: uint64(i), - To: &config.DepositContractAddress, - Gas: 500000, - GasFeeCap: newGwei(5), - GasTipCap: big.NewInt(2), - AccessList: nil, - Data: []byte{}, - } - tx := types.NewTx(txdata) - tx, _ = types.SignTx(tx, signer, key) - b.AddTx(tx) - } - }) - chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{DisableStack: true}, os.Stderr).Hooks()}, nil) - if err != nil { - t.Fatalf("failed to create tester chain: %v", err) - } - defer chain.Stop() - if n, err := chain.InsertChain(blocks); err != nil { - t.Fatalf("block %d: failed to insert into chain: %v", n, err) - } - - block := chain.GetBlockByNumber(1) - if len(block.Requests()) != 5 { - t.Fatalf("failed to retrieve deposits: have %d, want %d", len(block.Requests()), 5) - } - - // Verify each index is correct. - for want, req := range block.Requests() { - d, ok := req.Inner().(*types.Deposit) - if !ok { - t.Fatalf("expected deposit object") - } - if got := int(d.PublicKey[0]); got != want { - t.Fatalf("invalid pubkey: have %d, want %d", got, want) - } - if got := int(d.WithdrawalCredentials[0]); got != want { - t.Fatalf("invalid withdrawal credentials: have %d, want %d", got, want) - } - if d.Amount != uint64(want) { - t.Fatalf("invalid amounbt: have %d, want %d", d.Amount, want) - } - if got := int(d.Signature[0]); got != want { - t.Fatalf("invalid signature: have %d, want %d", got, want) - } - if d.Index != uint64(want) { - t.Fatalf("invalid index: have %d, want %d", d.Index, want) - } - } -} +// This test checks that deposit contract logs are turned into requests. +// func TestEIP6110(t *testing.T) { +// var ( +// engine = beacon.NewFaker() +// +// // A sender who makes transactions, has some funds +// key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") +// addr = crypto.PubkeyToAddress(key.PublicKey) +// funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether)) +// config = *params.AllEthashProtocolChanges +// gspec = &Genesis{ +// Config: &config, +// Alloc: types.GenesisAlloc{ +// addr: {Balance: funds}, +// config.DepositContractAddress: { +// // Simple deposit generator, source: https://gist.github.com/lightclient/54abb2af2465d6969fa6d1920b9ad9d7 +// Code: common.Hex2Bytes("6080604052366103aa575f603067ffffffffffffffff811115610025576100246103ae565b5b6040519080825280601f01601f1916602001820160405280156100575781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f8151811061007d5761007c6103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f602067ffffffffffffffff8111156100c7576100c66103ae565b5b6040519080825280601f01601f1916602001820160405280156100f95781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f8151811061011f5761011e6103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f600867ffffffffffffffff811115610169576101686103ae565b5b6040519080825280601f01601f19166020018201604052801561019b5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f815181106101c1576101c06103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f606067ffffffffffffffff81111561020b5761020a6103ae565b5b6040519080825280601f01601f19166020018201604052801561023d5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f81518110610263576102626103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f600867ffffffffffffffff8111156102ad576102ac6103ae565b5b6040519080825280601f01601f1916602001820160405280156102df5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f81518110610305576103046103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f8081819054906101000a900460ff168092919061035090610441565b91906101000a81548160ff021916908360ff160217905550507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c585858585856040516103a09594939291906104d9565b60405180910390a1005b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f60ff82169050919050565b5f61044b82610435565b915060ff820361045e5761045d610408565b5b600182019050919050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f6104ab82610469565b6104b58185610473565b93506104c5818560208601610483565b6104ce81610491565b840191505092915050565b5f60a0820190508181035f8301526104f181886104a1565b9050818103602083015261050581876104a1565b9050818103604083015261051981866104a1565b9050818103606083015261052d81856104a1565b9050818103608083015261054181846104a1565b9050969550505050505056fea26469706673582212208569967e58690162d7d6fe3513d07b393b4c15e70f41505cbbfd08f53eba739364736f6c63430008190033"), +// Nonce: 0, +// Balance: big.NewInt(0), +// }, +// }, +// } +// ) +// +// gspec.Config.BerlinBlock = common.Big0 +// gspec.Config.LondonBlock = common.Big0 +// gspec.Config.TerminalTotalDifficulty = common.Big0 +// gspec.Config.TerminalTotalDifficultyPassed = true +// gspec.Config.ShanghaiTime = u64(0) +// gspec.Config.CancunTime = u64(0) +// gspec.Config.PragueTime = u64(0) +// signer := types.LatestSigner(gspec.Config) +// +// _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) { +// for i := 0; i < 5; i++ { +// txdata := &types.DynamicFeeTx{ +// ChainID: gspec.Config.ChainID, +// Nonce: uint64(i), +// To: &config.DepositContractAddress, +// Gas: 500000, +// GasFeeCap: newGwei(5), +// GasTipCap: big.NewInt(2), +// AccessList: nil, +// Data: []byte{}, +// } +// tx := types.NewTx(txdata) +// tx, _ = types.SignTx(tx, signer, key) +// b.AddTx(tx) +// } +// }) +// chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{DisableStack: true}, os.Stderr).Hooks()}, nil) +// if err != nil { +// t.Fatalf("failed to create tester chain: %v", err) +// } +// defer chain.Stop() +// if n, err := chain.InsertChain(blocks); err != nil { +// t.Fatalf("block %d: failed to insert into chain: %v", n, err) +// } +// +// block := chain.GetBlockByNumber(1) +// if len(block.Requests()) != 5 { +// t.Fatalf("failed to retrieve deposits: have %d, want %d", len(block.Requests()), 5) +// } +// +// // Verify each index is correct. +// for want, req := range block.Requests() { +// d, ok := req.Inner().(*types.Deposit) +// if !ok { +// t.Fatalf("expected deposit object") +// } +// if got := int(d.PublicKey[0]); got != want { +// t.Fatalf("invalid pubkey: have %d, want %d", got, want) +// } +// if got := int(d.WithdrawalCredentials[0]); got != want { +// t.Fatalf("invalid withdrawal credentials: have %d, want %d", got, want) +// } +// if d.Amount != uint64(want) { +// t.Fatalf("invalid amounbt: have %d, want %d", d.Amount, want) +// } +// if got := int(d.Signature[0]); got != want { +// t.Fatalf("invalid signature: have %d, want %d", got, want) +// } +// if d.Index != uint64(want) { +// t.Fatalf("invalid index: have %d, want %d", d.Index, want) +// } +// } +// } diff --git a/core/chain_makers.go b/core/chain_makers.go index 8e75abdea0ad..6df935d1f7e6 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -346,14 +346,14 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse gen(i, b) } - var requests types.Requests + var requests [][]byte if config.IsPrague(b.header.Number, b.header.Time) { for _, r := range b.receipts { - d, err := ParseDepositLogs(r.Logs, config) + req, err := ParseDepositLogs(r.Logs, config) if err != nil { panic(fmt.Sprintf("failed to parse deposit log: %v", err)) } - requests = append(requests, d...) + requests = append(requests, req...) } } diff --git a/core/genesis.go b/core/genesis.go index 31db49f527e4..79055e7bff3a 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -449,7 +449,7 @@ func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block { } var ( withdrawals []*types.Withdrawal - requests types.Requests + requests [][]byte ) if conf := g.Config; conf != nil { num := big.NewInt(int64(g.Number)) @@ -474,7 +474,7 @@ func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block { } if conf.IsPrague(num, g.Timestamp) { head.RequestsHash = &types.EmptyRequestsHash - requests = make(types.Requests, 0) + requests = make([][]byte, 0) } } return types.NewBlock(head, &types.Body{Withdrawals: withdrawals, Requests: requests}, nil, trie.NewStackTrie(nil)) diff --git a/core/state_processor.go b/core/state_processor.go index c74a33c378f4..1c258ca84dcd 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -97,7 +97,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg allLogs = append(allLogs, receipt.Logs...) } // Read requests if Prague is enabled. - var requests types.Requests + var requests [][]byte if p.config.IsPrague(block.Number(), block.Time()) { requests, err = ParseDepositLogs(allLogs, p.config) if err != nil { @@ -262,15 +262,15 @@ func ProcessParentBlockHash(prevHash common.Hash, vmenv *vm.EVM, statedb *state. // ParseDepositLogs extracts the EIP-6110 deposit values from logs emitted by // BeaconDepositContract. -func ParseDepositLogs(logs []*types.Log, config *params.ChainConfig) (types.Requests, error) { - deposits := make(types.Requests, 0) +func ParseDepositLogs(logs []*types.Log, config *params.ChainConfig) ([][]byte, error) { + deposits := make([][]byte, 0) for _, log := range logs { if log.Address == config.DepositContractAddress { - d, err := types.UnpackIntoDeposit(log.Data) + request, err := types.DepositLogToRequest(log.Data) if err != nil { return nil, fmt.Errorf("unable to parse deposit data: %v", err) } - deposits = append(deposits, types.NewRequest(d)) + deposits = append(deposits, request) } } return deposits, nil diff --git a/core/types.go b/core/types.go index 65cd4973e488..bed20802ab51 100644 --- a/core/types.go +++ b/core/types.go @@ -54,7 +54,7 @@ type Processor interface { // ProcessResult contains the values computed by Process. type ProcessResult struct { Receipts types.Receipts - Requests types.Requests + Requests [][]byte Logs []*types.Log GasUsed uint64 } diff --git a/core/types/block.go b/core/types/block.go index 1c00658d5b38..e6cbc658875e 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -184,7 +184,7 @@ type Body struct { Transactions []*Transaction Uncles []*Header Withdrawals []*Withdrawal `rlp:"optional"` - Requests []*Request `rlp:"optional"` + Requests [][]byte `rlp:"optional"` } // Block represents an Ethereum block. @@ -209,7 +209,7 @@ type Block struct { uncles []*Header transactions Transactions withdrawals Withdrawals - requests Requests + requests [][]byte // witness is not an encoded part of the block body. // It is held in Block in order for easy relaying to the places @@ -232,7 +232,7 @@ type extblock struct { Txs []*Transaction Uncles []*Header Withdrawals []*Withdrawal `rlp:"optional"` - Requests []*Request `rlp:"optional"` + Requests [][]byte `rlp:"optional"` } // NewBlock creates a new block. The input data is copied, changes to header and to the @@ -292,9 +292,9 @@ func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher TrieHasher b.header.RequestsHash = nil } else if len(requests) == 0 { b.header.RequestsHash = &EmptyRequestsHash - b.requests = Requests{} + b.requests = [][]byte{} } else { - h := DeriveSha(Requests(requests), hasher) + h := CalcRequestHash(requests) b.header.RequestsHash = &h b.requests = slices.Clone(requests) } @@ -376,7 +376,7 @@ func (b *Block) Body() *Body { func (b *Block) Uncles() []*Header { return b.uncles } func (b *Block) Transactions() Transactions { return b.transactions } func (b *Block) Withdrawals() Withdrawals { return b.withdrawals } -func (b *Block) Requests() Requests { return b.requests } +func (b *Block) Requests() [][]byte { return b.requests } func (b *Block) Transaction(hash common.Hash) *Transaction { for _, transaction := range b.transactions { @@ -474,6 +474,13 @@ func CalcUncleHash(uncles []*Header) common.Hash { return rlpHash(uncles) } +func CalcRequestHash(requests [][]byte) common.Hash { + if len(requests) == 0 { + return EmptyRequestsHash + } + return rlpHash(requests) +} + // NewBlockWithHeader creates a block with the given header data. The // header data is copied, changes to header and to the field values // will not affect the block. diff --git a/core/types/deposit.go b/core/types/deposit.go index 172acc36ed3e..18f5659a3560 100644 --- a/core/types/deposit.go +++ b/core/types/deposit.go @@ -17,52 +17,29 @@ package types import ( - "bytes" - "encoding/binary" "fmt" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/rlp" ) -//go:generate go run github.com/fjl/gencodec -type Deposit -field-override depositMarshaling -out gen_deposit_json.go - -// Deposit contains EIP-6110 deposit data. -type Deposit struct { - PublicKey [48]byte `json:"pubkey"` // public key of validator - WithdrawalCredentials common.Hash `json:"withdrawalCredentials"` // beneficiary of the validator funds - Amount uint64 `json:"amount"` // deposit size in Gwei - Signature [96]byte `json:"signature"` // signature over deposit msg - Index uint64 `json:"index"` // deposit count value -} - -// field type overrides for gencodec -type depositMarshaling struct { - PublicKey hexutil.Bytes - WithdrawalCredentials hexutil.Bytes - Amount hexutil.Uint64 - Signature hexutil.Bytes - Index hexutil.Uint64 -} - -// Deposits implements DerivableList for requests. -type Deposits []*Deposit - -// Len returns the length of s. -func (s Deposits) Len() int { return len(s) } - -// EncodeIndex encodes the i'th deposit to s. -func (s Deposits) EncodeIndex(i int, w *bytes.Buffer) { - rlp.Encode(w, s[i]) -} +const ( + depositRequestType = 0x01 + depositRequestSize = 192 + 1 +) // UnpackIntoDeposit unpacks a serialized DepositEvent. -func UnpackIntoDeposit(data []byte) (*Deposit, error) { +func DepositLogToRequest(data []byte) ([]byte, error) { if len(data) != 576 { return nil, fmt.Errorf("deposit wrong length: want 576, have %d", len(data)) } - var d Deposit + + var outputRequest = make([]byte, depositRequestSize) + outputRequest[0] = depositRequestSize + const ( + pubkeyOffset = 1 + withdrawalCredOffset = pubkeyOffset + 48 + amountOffset = withdrawalCredOffset + 32 + signatureOffset = amountOffset + 8 + indexOffset = signatureOffset + 96 + ) // The ABI encodes the position of dynamic elements first. Since there are 5 // elements, skip over the positional data. The first 32 bytes of dynamic // elements also encode their actual length. Skip over that value too. @@ -70,34 +47,20 @@ func UnpackIntoDeposit(data []byte) (*Deposit, error) { // PublicKey is the first element. ABI encoding pads values to 32 bytes, so // despite BLS public keys being length 48, the value length here is 64. Then // skip over the next length value. - copy(d.PublicKey[:], data[b:b+48]) + copy(outputRequest[pubkeyOffset:], data[b:b+48]) b += 48 + 16 + 32 // WithdrawalCredentials is 32 bytes. Read that value then skip over next // length. - copy(d.WithdrawalCredentials[:], data[b:b+32]) + copy(outputRequest[withdrawalCredOffset:], data[b:b+32]) b += 32 + 32 // Amount is 8 bytes, but it is padded to 32. Skip over it and the next // length. - d.Amount = binary.LittleEndian.Uint64(data[b : b+8]) + copy(outputRequest[amountOffset:], data[b:b+8]) b += 8 + 24 + 32 // Signature is 96 bytes. Skip over it and the next length. - copy(d.Signature[:], data[b:b+96]) + copy(outputRequest[signatureOffset:], data[b:b+96]) b += 96 + 32 - // Amount is 8 bytes. - d.Index = binary.LittleEndian.Uint64(data[b : b+8]) - - return &d, nil -} - -func (d *Deposit) requestType() byte { return DepositRequestType } -func (d *Deposit) encode(b *bytes.Buffer) error { return rlp.Encode(b, d) } -func (d *Deposit) decode(input []byte) error { return rlp.DecodeBytes(input, d) } -func (d *Deposit) copy() RequestData { - return &Deposit{ - PublicKey: d.PublicKey, - WithdrawalCredentials: d.WithdrawalCredentials, - Amount: d.Amount, - Signature: d.Signature, - Index: d.Index, - } + // Index is 8 bytes. + copy(outputRequest[indexOffset:], data[b:b+8]) + return outputRequest, nil } diff --git a/core/types/gen_deposit_json.go b/core/types/gen_deposit_json.go deleted file mode 100644 index a65691188f58..000000000000 --- a/core/types/gen_deposit_json.go +++ /dev/null @@ -1,70 +0,0 @@ -// Code generated by github.com/fjl/gencodec. DO NOT EDIT. - -package types - -import ( - "encoding/json" - "errors" - - "github.com/ethereum/go-ethereum/common/hexutil" -) - -var _ = (*depositMarshaling)(nil) - -// MarshalJSON marshals as JSON. -func (d Deposit) MarshalJSON() ([]byte, error) { - type Deposit struct { - PublicKey hexutil.Bytes `json:"pubkey"` - WithdrawalCredentials hexutil.Bytes `json:"withdrawalCredentials"` - Amount hexutil.Uint64 `json:"amount"` - Signature hexutil.Bytes `json:"signature"` - Index hexutil.Uint64 `json:"index"` - } - var enc Deposit - enc.PublicKey = d.PublicKey[:] - enc.WithdrawalCredentials = d.WithdrawalCredentials[:] - enc.Amount = hexutil.Uint64(d.Amount) - enc.Signature = d.Signature[:] - enc.Index = hexutil.Uint64(d.Index) - return json.Marshal(&enc) -} - -// UnmarshalJSON unmarshals from JSON. -func (d *Deposit) UnmarshalJSON(input []byte) error { - type Deposit struct { - PublicKey *hexutil.Bytes `json:"pubkey"` - WithdrawalCredentials *hexutil.Bytes `json:"withdrawalCredentials"` - Amount *hexutil.Uint64 `json:"amount"` - Signature *hexutil.Bytes `json:"signature"` - Index *hexutil.Uint64 `json:"index"` - } - var dec Deposit - if err := json.Unmarshal(input, &dec); err != nil { - return err - } - if dec.PublicKey != nil { - if len(*dec.PublicKey) != len(d.PublicKey) { - return errors.New("field 'pubkey' has wrong length, need 48 items") - } - copy(d.PublicKey[:], *dec.PublicKey) - } - if dec.WithdrawalCredentials != nil { - if len(*dec.WithdrawalCredentials) != len(d.WithdrawalCredentials) { - return errors.New("field 'withdrawalCredentials' has wrong length, need 32 items") - } - copy(d.WithdrawalCredentials[:], *dec.WithdrawalCredentials) - } - if dec.Amount != nil { - d.Amount = uint64(*dec.Amount) - } - if dec.Signature != nil { - if len(*dec.Signature) != len(d.Signature) { - return errors.New("field 'signature' has wrong length, need 96 items") - } - copy(d.Signature[:], *dec.Signature) - } - if dec.Index != nil { - d.Index = uint64(*dec.Index) - } - return nil -} diff --git a/core/types/hashes.go b/core/types/hashes.go index cbd197072e5e..33f543e80c35 100644 --- a/core/types/hashes.go +++ b/core/types/hashes.go @@ -42,7 +42,7 @@ var ( EmptyWithdrawalsHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") // EmptyRequestsHash is the known hash of the empty requests set. - EmptyRequestsHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") + EmptyRequestsHash = common.HexToHash("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347") // EmptyVerkleHash is the known hash of an empty verkle trie. EmptyVerkleHash = common.Hash{} diff --git a/core/types/request.go b/core/types/request.go deleted file mode 100644 index 7b1cade26e75..000000000000 --- a/core/types/request.go +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2024 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package types - -import ( - "bytes" - "errors" - "fmt" - "io" - - "github.com/ethereum/go-ethereum/rlp" -) - -var ( - ErrRequestTypeNotSupported = errors.New("request type not supported") - errShortTypedRequest = errors.New("typed request too short") -) - -// Request types. -const ( - DepositRequestType = 0x00 -) - -// Request is an EIP-7685 request object. It represents execution layer -// triggered messages bound for the consensus layer. -type Request struct { - inner RequestData -} - -// Type returns the EIP-7685 type of the request. -func (r *Request) Type() byte { - return r.inner.requestType() -} - -// Inner returns the inner request data. -func (r *Request) Inner() RequestData { - return r.inner -} - -// NewRequest creates a new request. -func NewRequest(inner RequestData) *Request { - req := new(Request) - req.inner = inner.copy() - return req -} - -// Requests implements DerivableList for requests. -type Requests []*Request - -// Len returns the length of s. -func (s Requests) Len() int { return len(s) } - -// EncodeIndex encodes the i'th request to s. -func (s Requests) EncodeIndex(i int, w *bytes.Buffer) { - s[i].encode(w) -} - -// RequestData is the underlying data of a request. -type RequestData interface { - requestType() byte - encode(*bytes.Buffer) error - decode([]byte) error - copy() RequestData // creates a deep copy and initializes all fields -} - -// EncodeRLP implements rlp.Encoder -func (r *Request) EncodeRLP(w io.Writer) error { - buf := encodeBufferPool.Get().(*bytes.Buffer) - defer encodeBufferPool.Put(buf) - buf.Reset() - if err := r.encode(buf); err != nil { - return err - } - return rlp.Encode(w, buf.Bytes()) -} - -// encode writes the canonical encoding of a request to w. -func (r *Request) encode(w *bytes.Buffer) error { - w.WriteByte(r.Type()) - return r.inner.encode(w) -} - -// MarshalBinary returns the canonical encoding of the request. -func (r *Request) MarshalBinary() ([]byte, error) { - var buf bytes.Buffer - err := r.encode(&buf) - return buf.Bytes(), err -} - -// DecodeRLP implements rlp.Decoder -func (r *Request) DecodeRLP(s *rlp.Stream) error { - kind, size, err := s.Kind() - switch { - case err != nil: - return err - case kind == rlp.List: - return fmt.Errorf("untyped request") - case kind == rlp.Byte: - return errShortTypedRequest - default: - // First read the request payload bytes into a temporary buffer. - b, buf, err := getPooledBuffer(size) - if err != nil { - return err - } - defer encodeBufferPool.Put(buf) - if err := s.ReadBytes(b); err != nil { - return err - } - // Now decode the inner request. - inner, err := r.decode(b) - if err == nil { - r.inner = inner - } - return err - } -} - -// UnmarshalBinary decodes the canonical encoding of requests. -func (r *Request) UnmarshalBinary(b []byte) error { - inner, err := r.decode(b) - if err != nil { - return err - } - r.inner = inner - return nil -} - -// decode decodes a request from the canonical format. -func (r *Request) decode(b []byte) (RequestData, error) { - if len(b) <= 1 { - return nil, errShortTypedRequest - } - var inner RequestData - switch b[0] { - case DepositRequestType: - inner = new(Deposit) - default: - return nil, ErrRequestTypeNotSupported - } - err := inner.decode(b[1:]) - return inner, err -} diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 991cdf93f328..ebe51b645062 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -604,8 +604,8 @@ func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHas if params.BlobGasUsed == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun")) } - if params.Deposits == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil deposits post-prague")) + if params.Requests == nil { + return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil requests post-prague")) } if versionedHashes == nil { @@ -842,7 +842,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe "params.ExcessBlobGas", ebg, "len(params.Transactions)", len(params.Transactions), "len(params.Withdrawals)", len(params.Withdrawals), - "len(params.Deposits)", len(params.Deposits), + "len(params.Requests)", len(params.Requests), "beaconRoot", beaconRoot, "error", err) return api.invalid(err, nil), nil @@ -1189,7 +1189,7 @@ func (api *ConsensusAPI) GetPayloadBodiesByHashV1(hashes []common.Hash) []*engin if body != nil { // Nil out the V2 values, clients should know to not request V1 objects // after Prague. - body.Deposits = nil + body.Requests = nil } bodies[i] = body } @@ -1218,7 +1218,7 @@ func (api *ConsensusAPI) GetPayloadBodiesByRangeV1(start, count hexutil.Uint64) // after Prague. for i := range bodies { if bodies[i] != nil { - bodies[i].Deposits = nil + bodies[i].Requests = nil } } return bodies, nil @@ -1257,35 +1257,26 @@ func getBody(block *types.Block) *engine.ExecutionPayloadBody { } var ( - body = block.Body() - txs = make([]hexutil.Bytes, len(body.Transactions)) - withdrawals = body.Withdrawals - depositRequests types.Deposits + body = block.Body() + result engine.ExecutionPayloadBody ) + result.TransactionData = make([]hexutil.Bytes, len(body.Transactions)) for j, tx := range body.Transactions { - txs[j], _ = tx.MarshalBinary() + result.TransactionData[j], _ = tx.MarshalBinary() } // Post-shanghai withdrawals MUST be set to empty slice instead of nil - if withdrawals == nil && block.Header().WithdrawalsHash != nil { - withdrawals = make([]*types.Withdrawal, 0) + if body.Withdrawals != nil && block.Header().WithdrawalsHash != nil { + result.Withdrawals = make([]*types.Withdrawal, 0) } - if block.Header().RequestsHash != nil { - // TODO: this isn't future proof because we can't determine if a request - // type has activated yet or if there are just no requests of that type from - // only the block. - for _, req := range block.Requests() { - if d, ok := req.Inner().(*types.Deposit); ok { - depositRequests = append(depositRequests, d) - } + if reqs := block.Requests(); reqs != nil { + result.Requests = make([]hexutil.Bytes, len(reqs)) + for j, req := range reqs { + result.Requests[j] = req } } - return &engine.ExecutionPayloadBody{ - TransactionData: txs, - Withdrawals: withdrawals, - Deposits: depositRequests, - } + return &result } diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 395deef61549..ab0d2ae80872 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -1569,18 +1569,13 @@ func equalBody(a *types.Body, b *engine.ExecutionPayloadBody) bool { return false } - var deposits types.Deposits + var requests [][]byte if a.Requests != nil { - // If requests is non-nil, it means deposits are available in block and we + // If requests is non-nil, it means requests are available in block and we // should return an empty slice instead of nil if there are no deposits. - deposits = make(types.Deposits, 0) + requests = make([][]byte, 0) } - for _, r := range a.Requests { - if d, ok := r.Inner().(*types.Deposit); ok { - deposits = append(deposits, d) - } - } - return reflect.DeepEqual(deposits, b.Deposits) + return reflect.DeepEqual(requests, b.Requests) } func TestBlockToPayloadWithBlobs(t *testing.T) { diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index adad45020040..23d25ed8fc4c 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -785,7 +785,7 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, hashes []comm func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListHashes []common.Hash, uncleLists [][]*types.Header, uncleListHashes []common.Hash, withdrawalLists [][]*types.Withdrawal, withdrawalListHashes []common.Hash, - requestsLists [][]*types.Request, requestsListHashes []common.Hash) (int, error) { + requestsLists [][][]byte, requestsListHashes []common.Hash) (int, error) { q.lock.Lock() defer q.lock.Unlock() diff --git a/eth/protocols/eth/handlers.go b/eth/protocols/eth/handlers.go index 951352319ffc..b1465e6f8a72 100644 --- a/eth/protocols/eth/handlers.go +++ b/eth/protocols/eth/handlers.go @@ -326,7 +326,7 @@ func handleBlockBodies(backend Backend, msg Decoder, peer *Peer) error { withdrawalHashes[i] = types.DeriveSha(types.Withdrawals(body.Withdrawals), hasher) } if body.Requests != nil { - requestsHashes[i] = types.DeriveSha(types.Requests(body.Requests), hasher) + requestsHashes[i] = types.CalcRequestHash(body.Requests) } } return [][]common.Hash{txsHashes, uncleHashes, withdrawalHashes, requestsHashes} diff --git a/eth/protocols/eth/protocol.go b/eth/protocols/eth/protocol.go index cbc895eabb8e..6c6c0fe83967 100644 --- a/eth/protocols/eth/protocol.go +++ b/eth/protocols/eth/protocol.go @@ -224,17 +224,17 @@ type BlockBody struct { Transactions []*types.Transaction // Transactions contained within a block Uncles []*types.Header // Uncles contained within a block Withdrawals []*types.Withdrawal `rlp:"optional"` // Withdrawals contained within a block - Requests []*types.Request `rlp:"optional"` // Requests contained within a block + Requests [][]byte `rlp:"optional"` // Requests contained within a block } // Unpack retrieves the transactions and uncles from the range packet and returns // them in a split flat format that's more consistent with the internal data structures. -func (p *BlockBodiesResponse) Unpack() ([][]*types.Transaction, [][]*types.Header, [][]*types.Withdrawal, [][]*types.Request) { +func (p *BlockBodiesResponse) Unpack() ([][]*types.Transaction, [][]*types.Header, [][]*types.Withdrawal, [][][]byte) { var ( txset = make([][]*types.Transaction, len(*p)) uncleset = make([][]*types.Header, len(*p)) withdrawalset = make([][]*types.Withdrawal, len(*p)) - requestset = make([][]*types.Request, len(*p)) + requestset = make([][][]byte, len(*p)) ) for i, body := range *p { txset[i], uncleset[i], withdrawalset[i], requestset[i] = body.Transactions, body.Uncles, body.Withdrawals, body.Requests diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index a1148bcedbc6..f5a0109ab94d 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -123,7 +123,7 @@ type rpcBlock struct { Transactions []rpcTransaction `json:"transactions"` UncleHashes []common.Hash `json:"uncles"` Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` - Requests []*types.Request `json:"requests,omitempty"` + Requests []hexutil.Bytes `json:"requests,omitempty"` } func (ec *Client) getBlock(ctx context.Context, method string, args ...interface{}) (*types.Block, error) { @@ -192,12 +192,22 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface } txs[i] = tx.tx } + + // Convert requests. + var requests [][]byte + if body.Requests != nil { + requests = make([][]byte, len(body.Requests)) + for i, req := range body.Requests { + requests[i] = req + } + } + return types.NewBlockWithHeader(head).WithBody( types.Body{ Transactions: txs, Uncles: uncles, Withdrawals: body.Withdrawals, - Requests: body.Requests, + Requests: requests, }), nil } From 89a441f18727b6e9e57353588ebee371be614cc4 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 12 Sep 2024 17:55:02 +0200 Subject: [PATCH 02/32] core/types: fix depositRequestType --- core/types/deposit.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/types/deposit.go b/core/types/deposit.go index 18f5659a3560..42f90b70e659 100644 --- a/core/types/deposit.go +++ b/core/types/deposit.go @@ -21,7 +21,7 @@ import ( ) const ( - depositRequestType = 0x01 + depositRequestType = 0x00 depositRequestSize = 192 + 1 ) From 8735cf3649b33fc31fb966dda6dc61f08a5fbf7a Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 12 Sep 2024 17:58:59 +0200 Subject: [PATCH 03/32] core/types: update deposit test --- core/types/deposit_test.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/core/types/deposit_test.go b/core/types/deposit_test.go index ed2e18445d3f..3d1fc8edd4c5 100644 --- a/core/types/deposit_test.go +++ b/core/types/deposit_test.go @@ -17,8 +17,7 @@ package types import ( - "encoding/binary" - "reflect" + "bytes" "testing" "github.com/ethereum/go-ethereum/accounts/abi" @@ -71,23 +70,24 @@ func FuzzUnpackIntoDeposit(f *testing.F) { copy(sig[:], s) copy(index[:], i) - want := Deposit{ - PublicKey: pubkey, - WithdrawalCredentials: wxCred, - Amount: binary.LittleEndian.Uint64(amount[:]), - Signature: sig, - Index: binary.LittleEndian.Uint64(index[:]), - } - out, err := depositABI.Pack("DepositEvent", want.PublicKey[:], want.WithdrawalCredentials[:], amount[:], want.Signature[:], index[:]) + var enc []byte + enc = append(enc, depositRequestType) + enc = append(enc, pubkey[:]...) + enc = append(enc, wxCred[:]...) + enc = append(enc, amount[:]...) + enc = append(enc, sig[:]...) + enc = append(enc, index[:]...) + + out, err := depositABI.Pack("DepositEvent", pubkey[:], wxCred[:], amount[:], sig[:], index[:]) if err != nil { t.Fatalf("error packing deposit: %v", err) } - got, err := UnpackIntoDeposit(out[4:]) + got, err := DepositLogToRequest(out[4:]) if err != nil { t.Errorf("error unpacking deposit: %v", err) } - if !reflect.DeepEqual(want, *got) { - t.Errorf("roundtrip failed: want %v, got %v", want, got) + if !bytes.Equal(enc, got) { + t.Errorf("roundtrip failed: want %x, got %x", enc, got) } }) } From a2abb4b3a3441aee425439bfd0efddc81c85e5c6 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 12 Sep 2024 18:10:02 +0200 Subject: [PATCH 04/32] core/types: fix deposit request encoding --- core/types/deposit.go | 2 +- core/types/deposit_test.go | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/core/types/deposit.go b/core/types/deposit.go index 42f90b70e659..c97f7a4b4756 100644 --- a/core/types/deposit.go +++ b/core/types/deposit.go @@ -32,7 +32,7 @@ func DepositLogToRequest(data []byte) ([]byte, error) { } var outputRequest = make([]byte, depositRequestSize) - outputRequest[0] = depositRequestSize + outputRequest[0] = depositRequestType const ( pubkeyOffset = 1 withdrawalCredOffset = pubkeyOffset + 48 diff --git a/core/types/deposit_test.go b/core/types/deposit_test.go index 3d1fc8edd4c5..5ffad9833a49 100644 --- a/core/types/deposit_test.go +++ b/core/types/deposit_test.go @@ -86,6 +86,9 @@ func FuzzUnpackIntoDeposit(f *testing.F) { if err != nil { t.Errorf("error unpacking deposit: %v", err) } + if len(got) != depositRequestSize { + t.Errorf("wrong output size: %d, want %d", len(got), depositRequestSize) + } if !bytes.Equal(enc, got) { t.Errorf("roundtrip failed: want %x, got %x", enc, got) } From b3cff4ee5f4f31fcbd5edd7b978f4238d9064a94 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 12 Sep 2024 18:13:09 +0200 Subject: [PATCH 05/32] core/types: rename to CalcRequestsHash --- beacon/engine/types.go | 2 +- cmd/evm/internal/t8ntool/execution.go | 2 +- core/block_validator.go | 2 +- core/types/block.go | 4 ++-- eth/protocols/eth/handlers.go | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/beacon/engine/types.go b/beacon/engine/types.go index 1d0cfd736434..05924631b1ab 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -260,7 +260,7 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H // Compute requestsHash if any requests are non-nil. var requestsHash *common.Hash if data.Requests != nil { - h := types.CalcRequestHash(data.Requests) + h := types.CalcRequestsHash(data.Requests) requestsHash = &h } header := &types.Header{ diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 63cd0f803602..40a90e4a1b9c 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -390,7 +390,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not parse requests logs: %v", err)) } // Calculate the requests root - h := types.CalcRequestHash(requests) + h := types.CalcRequestsHash(requests) execRs.RequestsHash = &h execRs.Requests = requests } diff --git a/core/block_validator.go b/core/block_validator.go index 04102a5efb37..35a815b6a15f 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -145,7 +145,7 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD } // Validate the parsed requests match the expected header value. if header.RequestsHash != nil { - reqhash := types.CalcRequestHash(res.Requests) + reqhash := types.CalcRequestsHash(res.Requests) if reqhash != *header.RequestsHash { return fmt.Errorf("invalid deposit root hash (remote: %x local: %x)", *header.RequestsHash, reqhash) } diff --git a/core/types/block.go b/core/types/block.go index e6cbc658875e..6defe1e06609 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -294,7 +294,7 @@ func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher TrieHasher b.header.RequestsHash = &EmptyRequestsHash b.requests = [][]byte{} } else { - h := CalcRequestHash(requests) + h := CalcRequestsHash(requests) b.header.RequestsHash = &h b.requests = slices.Clone(requests) } @@ -474,7 +474,7 @@ func CalcUncleHash(uncles []*Header) common.Hash { return rlpHash(uncles) } -func CalcRequestHash(requests [][]byte) common.Hash { +func CalcRequestsHash(requests [][]byte) common.Hash { if len(requests) == 0 { return EmptyRequestsHash } diff --git a/eth/protocols/eth/handlers.go b/eth/protocols/eth/handlers.go index b1465e6f8a72..6a2a34870724 100644 --- a/eth/protocols/eth/handlers.go +++ b/eth/protocols/eth/handlers.go @@ -326,7 +326,7 @@ func handleBlockBodies(backend Backend, msg Decoder, peer *Peer) error { withdrawalHashes[i] = types.DeriveSha(types.Withdrawals(body.Withdrawals), hasher) } if body.Requests != nil { - requestsHashes[i] = types.CalcRequestHash(body.Requests) + requestsHashes[i] = types.CalcRequestsHash(body.Requests) } } return [][]common.Hash{txsHashes, uncleHashes, withdrawalHashes, requestsHashes} From 57f2ad1118093e418a6834db38742f301a77fb07 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 12 Sep 2024 18:18:12 +0200 Subject: [PATCH 06/32] internal/ethapi: return requests as hex --- internal/ethapi/api.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 7981f35ae391..c7429f5035f4 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1437,7 +1437,12 @@ func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *param fields["withdrawals"] = block.Withdrawals() } if block.Header().RequestsHash != nil { - fields["requests"] = block.Requests() + // Convert requests to hex. + hexreq := make([]hexutil.Bytes, len(block.Requests())) + for i, req := range block.Requests() { + hexreq[i] = req + } + fields["requests"] = hexreq } return fields } From c1c8a2c46d880798bca74968b350388889432737 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 12 Sep 2024 18:19:16 +0200 Subject: [PATCH 07/32] internal/ethapi: avoid copying header --- internal/ethapi/api.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index c7429f5035f4..79f2965b05fb 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1433,10 +1433,10 @@ func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *param uncleHashes[i] = uncle.Hash() } fields["uncles"] = uncleHashes - if block.Header().WithdrawalsHash != nil { + if block.Withdrawals() != nil { fields["withdrawals"] = block.Withdrawals() } - if block.Header().RequestsHash != nil { + if block.Requests() != nil { // Convert requests to hex. hexreq := make([]hexutil.Bytes, len(block.Requests())) for i, req := range block.Requests() { From 54ac02bf54c93d3f24733c0b672c86a125097be8 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 12 Sep 2024 20:43:58 +0200 Subject: [PATCH 08/32] eth/catalyst: fix withdrawals return --- eth/catalyst/api.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index ebe51b645062..19df408b29f2 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -1256,19 +1256,17 @@ func getBody(block *types.Block) *engine.ExecutionPayloadBody { return nil } - var ( - body = block.Body() - result engine.ExecutionPayloadBody - ) + var result engine.ExecutionPayloadBody - result.TransactionData = make([]hexutil.Bytes, len(body.Transactions)) - for j, tx := range body.Transactions { + result.TransactionData = make([]hexutil.Bytes, len(block.Transactions())) + for j, tx := range block.Transactions() { result.TransactionData[j], _ = tx.MarshalBinary() } // Post-shanghai withdrawals MUST be set to empty slice instead of nil - if body.Withdrawals != nil && block.Header().WithdrawalsHash != nil { - result.Withdrawals = make([]*types.Withdrawal, 0) + result.Withdrawals = block.Withdrawals() + if block.Withdrawals() == nil && block.Header().WithdrawalsHash != nil { + result.Withdrawals = []*types.Withdrawal{} } if reqs := block.Requests(); reqs != nil { From c6ba1dcd511f71c2505fd59ea420cd8c400dfd5e Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 12 Sep 2024 20:56:24 +0200 Subject: [PATCH 09/32] eth/catalyst: improve test --- eth/catalyst/api_test.go | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index ab0d2ae80872..6a8c41a55901 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -20,10 +20,12 @@ import ( "bytes" "context" crand "crypto/rand" + "errors" "fmt" "math/big" "math/rand" "reflect" + "slices" "sync" "testing" "time" @@ -1416,8 +1418,8 @@ func TestGetBlockBodiesByHash(t *testing.T) { for k, test := range tests { result := api.GetPayloadBodiesByHashV2(test.hashes) for i, r := range result { - if !equalBody(test.results[i], r) { - t.Fatalf("test %v: invalid response: expected %+v got %+v", k, test.results[i], r) + if err := checkEqualBody(test.results[i], r); err != nil { + t.Fatalf("test %v: invalid response: %v\nexpected %+v\ngot %+v", k, err, test.results[i], r) } } } @@ -1494,8 +1496,8 @@ func TestGetBlockBodiesByRange(t *testing.T) { } if len(result) == len(test.results) { for i, r := range result { - if !equalBody(test.results[i], r) { - t.Fatalf("test %d: invalid response: expected \n%+v\ngot\n%+v", k, test.results[i], r) + if err := checkEqualBody(test.results[i], r); err != nil { + t.Fatalf("test %d: invalid response: %v\nexpected %+v\ngot %+v", k, err, test.results[i], r) } } } else { @@ -1549,33 +1551,32 @@ func TestGetBlockBodiesByRangeInvalidParams(t *testing.T) { } } -func equalBody(a *types.Body, b *engine.ExecutionPayloadBody) bool { +func checkEqualBody(a *types.Body, b *engine.ExecutionPayloadBody) error { if a == nil && b == nil { - return true + return nil } else if a == nil || b == nil { - return false + return errors.New("nil vs. non-nil") } if len(a.Transactions) != len(b.TransactionData) { - return false + return errors.New("transactions length mismatch") } for i, tx := range a.Transactions { data, _ := tx.MarshalBinary() if !bytes.Equal(data, b.TransactionData[i]) { - return false + return fmt.Errorf("transaction %d mismatch", i) } } - if !reflect.DeepEqual(a.Withdrawals, b.Withdrawals) { - return false + return fmt.Errorf("withdrawals mismatch") } - var requests [][]byte - if a.Requests != nil { - // If requests is non-nil, it means requests are available in block and we - // should return an empty slice instead of nil if there are no deposits. - requests = make([][]byte, 0) + reqEqual := slices.EqualFunc(a.Requests, b.Requests, func(a []byte, b hexutil.Bytes) bool { + return bytes.Equal(a, b) + }) + if !reqEqual { + return fmt.Errorf("requests mismatch") } - return reflect.DeepEqual(requests, b.Requests) + return nil } func TestBlockToPayloadWithBlobs(t *testing.T) { From 0fc9841aef3c65d2aaaa24809a07ab2633e682d9 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 12 Sep 2024 23:03:38 +0200 Subject: [PATCH 10/32] core: fix test --- core/blockchain_test.go | 165 +++++++++++++++++++--------------------- 1 file changed, 79 insertions(+), 86 deletions(-) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index c37017201214..bad7b51a61ef 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -17,17 +17,20 @@ package core import ( + "bytes" "errors" "fmt" "math/big" "math/rand" "os" "path" + "slices" "sync" "testing" "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/beacon" @@ -4229,89 +4232,79 @@ func TestEIP3651(t *testing.T) { } // This test checks that deposit contract logs are turned into requests. -// func TestEIP6110(t *testing.T) { -// var ( -// engine = beacon.NewFaker() -// -// // A sender who makes transactions, has some funds -// key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") -// addr = crypto.PubkeyToAddress(key.PublicKey) -// funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether)) -// config = *params.AllEthashProtocolChanges -// gspec = &Genesis{ -// Config: &config, -// Alloc: types.GenesisAlloc{ -// addr: {Balance: funds}, -// config.DepositContractAddress: { -// // Simple deposit generator, source: https://gist.github.com/lightclient/54abb2af2465d6969fa6d1920b9ad9d7 -// Code: common.Hex2Bytes("6080604052366103aa575f603067ffffffffffffffff811115610025576100246103ae565b5b6040519080825280601f01601f1916602001820160405280156100575781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f8151811061007d5761007c6103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f602067ffffffffffffffff8111156100c7576100c66103ae565b5b6040519080825280601f01601f1916602001820160405280156100f95781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f8151811061011f5761011e6103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f600867ffffffffffffffff811115610169576101686103ae565b5b6040519080825280601f01601f19166020018201604052801561019b5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f815181106101c1576101c06103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f606067ffffffffffffffff81111561020b5761020a6103ae565b5b6040519080825280601f01601f19166020018201604052801561023d5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f81518110610263576102626103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f600867ffffffffffffffff8111156102ad576102ac6103ae565b5b6040519080825280601f01601f1916602001820160405280156102df5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f81518110610305576103046103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f8081819054906101000a900460ff168092919061035090610441565b91906101000a81548160ff021916908360ff160217905550507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c585858585856040516103a09594939291906104d9565b60405180910390a1005b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f60ff82169050919050565b5f61044b82610435565b915060ff820361045e5761045d610408565b5b600182019050919050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f6104ab82610469565b6104b58185610473565b93506104c5818560208601610483565b6104ce81610491565b840191505092915050565b5f60a0820190508181035f8301526104f181886104a1565b9050818103602083015261050581876104a1565b9050818103604083015261051981866104a1565b9050818103606083015261052d81856104a1565b9050818103608083015261054181846104a1565b9050969550505050505056fea26469706673582212208569967e58690162d7d6fe3513d07b393b4c15e70f41505cbbfd08f53eba739364736f6c63430008190033"), -// Nonce: 0, -// Balance: big.NewInt(0), -// }, -// }, -// } -// ) -// -// gspec.Config.BerlinBlock = common.Big0 -// gspec.Config.LondonBlock = common.Big0 -// gspec.Config.TerminalTotalDifficulty = common.Big0 -// gspec.Config.TerminalTotalDifficultyPassed = true -// gspec.Config.ShanghaiTime = u64(0) -// gspec.Config.CancunTime = u64(0) -// gspec.Config.PragueTime = u64(0) -// signer := types.LatestSigner(gspec.Config) -// -// _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) { -// for i := 0; i < 5; i++ { -// txdata := &types.DynamicFeeTx{ -// ChainID: gspec.Config.ChainID, -// Nonce: uint64(i), -// To: &config.DepositContractAddress, -// Gas: 500000, -// GasFeeCap: newGwei(5), -// GasTipCap: big.NewInt(2), -// AccessList: nil, -// Data: []byte{}, -// } -// tx := types.NewTx(txdata) -// tx, _ = types.SignTx(tx, signer, key) -// b.AddTx(tx) -// } -// }) -// chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{DisableStack: true}, os.Stderr).Hooks()}, nil) -// if err != nil { -// t.Fatalf("failed to create tester chain: %v", err) -// } -// defer chain.Stop() -// if n, err := chain.InsertChain(blocks); err != nil { -// t.Fatalf("block %d: failed to insert into chain: %v", n, err) -// } -// -// block := chain.GetBlockByNumber(1) -// if len(block.Requests()) != 5 { -// t.Fatalf("failed to retrieve deposits: have %d, want %d", len(block.Requests()), 5) -// } -// -// // Verify each index is correct. -// for want, req := range block.Requests() { -// d, ok := req.Inner().(*types.Deposit) -// if !ok { -// t.Fatalf("expected deposit object") -// } -// if got := int(d.PublicKey[0]); got != want { -// t.Fatalf("invalid pubkey: have %d, want %d", got, want) -// } -// if got := int(d.WithdrawalCredentials[0]); got != want { -// t.Fatalf("invalid withdrawal credentials: have %d, want %d", got, want) -// } -// if d.Amount != uint64(want) { -// t.Fatalf("invalid amounbt: have %d, want %d", d.Amount, want) -// } -// if got := int(d.Signature[0]); got != want { -// t.Fatalf("invalid signature: have %d, want %d", got, want) -// } -// if d.Index != uint64(want) { -// t.Fatalf("invalid index: have %d, want %d", d.Index, want) -// } -// } -// } +func TestEIP6110(t *testing.T) { + var ( + engine = beacon.NewFaker() + + // A sender who makes transactions, has some funds + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr = crypto.PubkeyToAddress(key.PublicKey) + funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether)) + config = *params.AllEthashProtocolChanges + gspec = &Genesis{ + Config: &config, + Alloc: types.GenesisAlloc{ + addr: {Balance: funds}, + config.DepositContractAddress: { + // Simple deposit generator, source: https://gist.github.com/lightclient/54abb2af2465d6969fa6d1920b9ad9d7 + Code: common.Hex2Bytes("6080604052366103aa575f603067ffffffffffffffff811115610025576100246103ae565b5b6040519080825280601f01601f1916602001820160405280156100575781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f8151811061007d5761007c6103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f602067ffffffffffffffff8111156100c7576100c66103ae565b5b6040519080825280601f01601f1916602001820160405280156100f95781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f8151811061011f5761011e6103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f600867ffffffffffffffff811115610169576101686103ae565b5b6040519080825280601f01601f19166020018201604052801561019b5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f815181106101c1576101c06103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f606067ffffffffffffffff81111561020b5761020a6103ae565b5b6040519080825280601f01601f19166020018201604052801561023d5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f81518110610263576102626103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f600867ffffffffffffffff8111156102ad576102ac6103ae565b5b6040519080825280601f01601f1916602001820160405280156102df5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f81518110610305576103046103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f8081819054906101000a900460ff168092919061035090610441565b91906101000a81548160ff021916908360ff160217905550507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c585858585856040516103a09594939291906104d9565b60405180910390a1005b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f60ff82169050919050565b5f61044b82610435565b915060ff820361045e5761045d610408565b5b600182019050919050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f6104ab82610469565b6104b58185610473565b93506104c5818560208601610483565b6104ce81610491565b840191505092915050565b5f60a0820190508181035f8301526104f181886104a1565b9050818103602083015261050581876104a1565b9050818103604083015261051981866104a1565b9050818103606083015261052d81856104a1565b9050818103608083015261054181846104a1565b9050969550505050505056fea26469706673582212208569967e58690162d7d6fe3513d07b393b4c15e70f41505cbbfd08f53eba739364736f6c63430008190033"), + Nonce: 0, + Balance: big.NewInt(0), + }, + }, + } + ) + + gspec.Config.BerlinBlock = common.Big0 + gspec.Config.LondonBlock = common.Big0 + gspec.Config.TerminalTotalDifficulty = common.Big0 + gspec.Config.TerminalTotalDifficultyPassed = true + gspec.Config.ShanghaiTime = u64(0) + gspec.Config.CancunTime = u64(0) + gspec.Config.PragueTime = u64(0) + signer := types.LatestSigner(gspec.Config) + + _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) { + for i := 0; i < 2; i++ { + txdata := &types.DynamicFeeTx{ + ChainID: gspec.Config.ChainID, + Nonce: uint64(i), + To: &config.DepositContractAddress, + Gas: 500000, + GasFeeCap: newGwei(5), + GasTipCap: big.NewInt(2), + AccessList: nil, + Data: []byte{}, + } + tx := types.NewTx(txdata) + tx, _ = types.SignTx(tx, signer, key) + b.AddTx(tx) + } + }) + chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{DisableStack: true}, os.Stderr).Hooks()}, nil) + if err != nil { + t.Fatalf("failed to create tester chain: %v", err) + } + defer chain.Stop() + if n, err := chain.InsertChain(blocks); err != nil { + t.Fatalf("block %d: failed to insert into chain: %v", n, err) + } + + block := chain.GetBlockByNumber(1) + if len(block.Requests()) != 2 { + t.Fatalf("failed to retrieve deposits: have %d, want %d", len(block.Requests()), 5) + } + + wantDeposits := [][]byte{ + hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), + hexutil.MustDecode("0x00010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000"), + } + ok := slices.EqualFunc(block.Requests(), wantDeposits, bytes.Equal) + if !ok { + t.Errorf("requests not equal") + for i, r := range block.Requests() { + t.Logf("%d: %x", i, r) + } + } +} + From ca1ed7689fe1a26306eeb84d610a626e617e017e Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 12 Sep 2024 23:03:45 +0200 Subject: [PATCH 11/32] core/types: rename variable --- core/types/deposit.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/core/types/deposit.go b/core/types/deposit.go index c97f7a4b4756..f96d8d47ca37 100644 --- a/core/types/deposit.go +++ b/core/types/deposit.go @@ -31,8 +31,8 @@ func DepositLogToRequest(data []byte) ([]byte, error) { return nil, fmt.Errorf("deposit wrong length: want 576, have %d", len(data)) } - var outputRequest = make([]byte, depositRequestSize) - outputRequest[0] = depositRequestType + var request = make([]byte, depositRequestSize) + request[0] = depositRequestType const ( pubkeyOffset = 1 withdrawalCredOffset = pubkeyOffset + 48 @@ -47,20 +47,20 @@ func DepositLogToRequest(data []byte) ([]byte, error) { // PublicKey is the first element. ABI encoding pads values to 32 bytes, so // despite BLS public keys being length 48, the value length here is 64. Then // skip over the next length value. - copy(outputRequest[pubkeyOffset:], data[b:b+48]) + copy(request[pubkeyOffset:], data[b:b+48]) b += 48 + 16 + 32 // WithdrawalCredentials is 32 bytes. Read that value then skip over next // length. - copy(outputRequest[withdrawalCredOffset:], data[b:b+32]) + copy(request[withdrawalCredOffset:], data[b:b+32]) b += 32 + 32 // Amount is 8 bytes, but it is padded to 32. Skip over it and the next // length. - copy(outputRequest[amountOffset:], data[b:b+8]) + copy(request[amountOffset:], data[b:b+8]) b += 8 + 24 + 32 // Signature is 96 bytes. Skip over it and the next length. - copy(outputRequest[signatureOffset:], data[b:b+96]) + copy(request[signatureOffset:], data[b:b+96]) b += 96 + 32 // Index is 8 bytes. - copy(outputRequest[indexOffset:], data[b:b+8]) - return outputRequest, nil + copy(request[indexOffset:], data[b:b+8]) + return request, nil } From e9cf6255cbddae61322591d7a155d26af3c530fb Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 30 Sep 2024 14:59:13 +0200 Subject: [PATCH 12/32] core/types: update requests hash --- core/types/block.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/core/types/block.go b/core/types/block.go index 6defe1e06609..4408ec496076 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -18,6 +18,7 @@ package types import ( + "crypto/sha256" "encoding/binary" "fmt" "io" @@ -475,10 +476,11 @@ func CalcUncleHash(uncles []*Header) common.Hash { } func CalcRequestsHash(requests [][]byte) common.Hash { - if len(requests) == 0 { - return EmptyRequestsHash + h := sha256.New() + for _, item := range requests { + h.Write(item) } - return rlpHash(requests) + return common.Hash(h.Sum(nil)) } // NewBlockWithHeader creates a block with the given header data. The From c87fd76613c2ead0819a28e65f4a75b928796c73 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 30 Sep 2024 15:01:26 +0200 Subject: [PATCH 13/32] core/types: update deposit request encoding --- core/types/deposit.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/core/types/deposit.go b/core/types/deposit.go index f96d8d47ca37..3bba2c7aa4fb 100644 --- a/core/types/deposit.go +++ b/core/types/deposit.go @@ -21,8 +21,7 @@ import ( ) const ( - depositRequestType = 0x00 - depositRequestSize = 192 + 1 + depositRequestSize = 192 ) // UnpackIntoDeposit unpacks a serialized DepositEvent. @@ -31,10 +30,9 @@ func DepositLogToRequest(data []byte) ([]byte, error) { return nil, fmt.Errorf("deposit wrong length: want 576, have %d", len(data)) } - var request = make([]byte, depositRequestSize) - request[0] = depositRequestType + request := make([]byte, depositRequestSize) const ( - pubkeyOffset = 1 + pubkeyOffset = 0 withdrawalCredOffset = pubkeyOffset + 48 amountOffset = withdrawalCredOffset + 32 signatureOffset = amountOffset + 8 From e4084888820680f9dcfe2ce5d23bda2561a6e2b5 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 30 Sep 2024 15:15:21 +0200 Subject: [PATCH 14/32] all: update deposit requests format --- cmd/evm/internal/t8ntool/execution.go | 3 ++- core/chain_makers.go | 4 ++-- core/state_processor.go | 14 +++++++++----- eth/catalyst/api.go | 10 +++++----- miner/worker.go | 6 ++++-- 5 files changed, 22 insertions(+), 15 deletions(-) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 40a90e4a1b9c..d5beb308807e 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -385,10 +385,11 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, for _, receipt := range receipts { allLogs = append(allLogs, receipt.Logs...) } - requests, err := core.ParseDepositLogs(allLogs, chainConfig) + depositRequests, err := core.ParseDepositLogs(allLogs, chainConfig) if err != nil { return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not parse requests logs: %v", err)) } + requests := [][]byte{depositRequests} // Calculate the requests root h := types.CalcRequestsHash(requests) execRs.RequestsHash = &h diff --git a/core/chain_makers.go b/core/chain_makers.go index 6df935d1f7e6..a8f5504ad24d 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -349,11 +349,11 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse var requests [][]byte if config.IsPrague(b.header.Number, b.header.Time) { for _, r := range b.receipts { - req, err := ParseDepositLogs(r.Logs, config) + depositRequests, err := ParseDepositLogs(r.Logs, config) if err != nil { panic(fmt.Sprintf("failed to parse deposit log: %v", err)) } - requests = append(requests, req...) + requests = append(requests, depositRequests) } } diff --git a/core/state_processor.go b/core/state_processor.go index 1c258ca84dcd..c4e2bcf1e57f 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -71,8 +71,9 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg var ( context vm.BlockContext signer = types.MakeSigner(p.config, header.Number, header.Time) - err error ) + + // Apply pre-execution system calls. context = NewEVMBlockContext(header, p.chain, nil) vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, p.config, cfg) if beaconRoot := block.BeaconRoot(); beaconRoot != nil { @@ -81,6 +82,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg if p.config.IsPrague(block.Number(), block.Time()) { ProcessParentBlockHash(block.ParentHash(), vmenv, statedb) } + // Iterate over and process the individual transactions for i, tx := range block.Transactions() { msg, err := TransactionToMessage(tx, signer, header.BaseFee) @@ -96,13 +98,15 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg receipts = append(receipts, receipt) allLogs = append(allLogs, receipt.Logs...) } + // Read requests if Prague is enabled. var requests [][]byte if p.config.IsPrague(block.Number(), block.Time()) { - requests, err = ParseDepositLogs(allLogs, p.config) + depositRequests, err := ParseDepositLogs(allLogs, p.config) if err != nil { return nil, err } + requests = append(requests, depositRequests) } // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) @@ -262,15 +266,15 @@ func ProcessParentBlockHash(prevHash common.Hash, vmenv *vm.EVM, statedb *state. // ParseDepositLogs extracts the EIP-6110 deposit values from logs emitted by // BeaconDepositContract. -func ParseDepositLogs(logs []*types.Log, config *params.ChainConfig) ([][]byte, error) { - deposits := make([][]byte, 0) +func ParseDepositLogs(logs []*types.Log, config *params.ChainConfig) ([]byte, error) { + deposits := make([]byte, 1) // note: first byte is 0x00 (== deposit request type) for _, log := range logs { if log.Address == config.DepositContractAddress { request, err := types.DepositLogToRequest(log.Data) if err != nil { return nil, fmt.Errorf("unable to parse deposit data: %v", err) } - deposits = append(deposits, request) + deposits = append(deposits, request...) } } return deposits, nil diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 19df408b29f2..8ee2529d2601 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -692,8 +692,8 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, v if params.BlobGasUsed == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun")) } - if params.Deposits == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil deposits post-prague")) + if params.Requests == nil { + return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil requests post-prague")) } if versionedHashes == nil { @@ -780,8 +780,8 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, if params.BlobGasUsed == nil { return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun")) } - if params.Deposits == nil { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil deposits post-prague")) + if params.Requests == nil { + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil requests post-prague")) } if versionedHashes == nil { @@ -957,7 +957,7 @@ func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, v "params.ExcessBlobGas", ebg, "len(params.Transactions)", len(params.Transactions), "len(params.Withdrawals)", len(params.Withdrawals), - "len(params.Deposits)", len(params.Deposits), + "len(params.Requests)", len(params.Requests), "beaconRoot", beaconRoot, "error", err) errorMsg := err.Error() diff --git a/miner/worker.go b/miner/worker.go index 930c3e8f5b1b..e21219a9a3a2 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -116,13 +116,15 @@ func (miner *Miner) generateWork(params *generateParams, witness bool) *newPaylo allLogs = append(allLogs, r.Logs...) } // Read requests if Prague is enabled. + var requests [][]byte if miner.chainConfig.IsPrague(work.header.Number, work.header.Time) { - requests, err := core.ParseDepositLogs(allLogs, miner.chainConfig) + depositRequests, err := core.ParseDepositLogs(allLogs, miner.chainConfig) if err != nil { return &newPayloadResult{err: err} } - body.Requests = requests + requests = append(requests, depositRequests) } + body.Requests = requests block, err := miner.engine.FinalizeAndAssemble(miner.chain, work.header, work.state, &body, work.receipts) if err != nil { return &newPayloadResult{err: err} From 6690bdd2b0fe77f8a8fcc678195ee0f5334c328d Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 30 Sep 2024 15:50:55 +0200 Subject: [PATCH 15/32] all: WIP remove requests from block body --- beacon/engine/gen_ed.go | 18 +++++------------- beacon/engine/gen_epe.go | 14 ++++++++++++++ beacon/engine/types.go | 23 +++++++++++----------- core/chain_makers.go | 7 ++++++- core/genesis.go | 4 +--- core/types/block.go | 26 +++---------------------- eth/catalyst/api.go | 40 +++++++++++++++++++++++---------------- ethclient/ethclient.go | 11 ----------- miner/payload_building.go | 6 ++++++ miner/worker.go | 11 +++++++++-- 10 files changed, 80 insertions(+), 80 deletions(-) diff --git a/beacon/engine/gen_ed.go b/beacon/engine/gen_ed.go index 2494bfb1f9e0..432ae2722e59 100644 --- a/beacon/engine/gen_ed.go +++ b/beacon/engine/gen_ed.go @@ -34,7 +34,7 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) { Withdrawals []*types.Withdrawal `json:"withdrawals"` BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"` ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"` - Requests []hexutil.Bytes `json:"depositRequests"` + RequestsHash *common.Hash `json:"executionRequestsHash"` ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"` } var enc ExecutableData @@ -60,12 +60,7 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) { enc.Withdrawals = e.Withdrawals enc.BlobGasUsed = (*hexutil.Uint64)(e.BlobGasUsed) enc.ExcessBlobGas = (*hexutil.Uint64)(e.ExcessBlobGas) - if e.Requests != nil { - enc.Requests = make([]hexutil.Bytes, len(e.Requests)) - for k, v := range e.Requests { - enc.Requests[k] = v - } - } + enc.RequestsHash = e.RequestsHash enc.ExecutionWitness = e.ExecutionWitness return json.Marshal(&enc) } @@ -90,7 +85,7 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error { Withdrawals []*types.Withdrawal `json:"withdrawals"` BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"` ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"` - Requests []hexutil.Bytes `json:"depositRequests"` + RequestsHash *common.Hash `json:"executionRequestsHash"` ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"` } var dec ExecutableData @@ -165,11 +160,8 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error { if dec.ExcessBlobGas != nil { e.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas) } - if dec.Requests != nil { - e.Requests = make([][]byte, len(dec.Requests)) - for k, v := range dec.Requests { - e.Requests[k] = v - } + if dec.RequestsHash != nil { + e.RequestsHash = dec.RequestsHash } if dec.ExecutionWitness != nil { e.ExecutionWitness = dec.ExecutionWitness diff --git a/beacon/engine/gen_epe.go b/beacon/engine/gen_epe.go index fa45d94c4c20..039884e842fd 100644 --- a/beacon/engine/gen_epe.go +++ b/beacon/engine/gen_epe.go @@ -18,6 +18,7 @@ func (e ExecutionPayloadEnvelope) MarshalJSON() ([]byte, error) { ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"` BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"` BlobsBundle *BlobsBundleV1 `json:"blobsBundle"` + Requests []hexutil.Bytes `json:"executionRequests"` Override bool `json:"shouldOverrideBuilder"` Witness *hexutil.Bytes `json:"witness"` } @@ -25,6 +26,12 @@ func (e ExecutionPayloadEnvelope) MarshalJSON() ([]byte, error) { enc.ExecutionPayload = e.ExecutionPayload enc.BlockValue = (*hexutil.Big)(e.BlockValue) enc.BlobsBundle = e.BlobsBundle + if e.Requests != nil { + enc.Requests = make([]hexutil.Bytes, len(e.Requests)) + for k, v := range e.Requests { + enc.Requests[k] = v + } + } enc.Override = e.Override enc.Witness = e.Witness return json.Marshal(&enc) @@ -36,6 +43,7 @@ func (e *ExecutionPayloadEnvelope) UnmarshalJSON(input []byte) error { ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"` BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"` BlobsBundle *BlobsBundleV1 `json:"blobsBundle"` + Requests []hexutil.Bytes `json:"executionRequests"` Override *bool `json:"shouldOverrideBuilder"` Witness *hexutil.Bytes `json:"witness"` } @@ -54,6 +62,12 @@ func (e *ExecutionPayloadEnvelope) UnmarshalJSON(input []byte) error { if dec.BlobsBundle != nil { e.BlobsBundle = dec.BlobsBundle } + if dec.Requests != nil { + e.Requests = make([][]byte, len(dec.Requests)) + for k, v := range dec.Requests { + e.Requests[k] = v + } + } if dec.Override != nil { e.Override = *dec.Override } diff --git a/beacon/engine/types.go b/beacon/engine/types.go index 05924631b1ab..83a33c64b17b 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -76,7 +76,7 @@ type ExecutableData struct { Withdrawals []*types.Withdrawal `json:"withdrawals"` BlobGasUsed *uint64 `json:"blobGasUsed"` ExcessBlobGas *uint64 `json:"excessBlobGas"` - Requests [][]byte `json:"depositRequests"` + RequestsHash *common.Hash `json:"executionRequestsHash"` ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"` } @@ -91,7 +91,6 @@ type executableDataMarshaling struct { LogsBloom hexutil.Bytes Transactions []hexutil.Bytes BlobGasUsed *hexutil.Uint64 - Requests []hexutil.Bytes ExcessBlobGas *hexutil.Uint64 } @@ -109,6 +108,7 @@ type ExecutionPayloadEnvelope struct { ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"` BlockValue *big.Int `json:"blockValue" gencodec:"required"` BlobsBundle *BlobsBundleV1 `json:"blobsBundle"` + Requests [][]byte `json:"executionRequests"` Override bool `json:"shouldOverrideBuilder"` Witness *hexutil.Bytes `json:"witness"` } @@ -122,6 +122,7 @@ type BlobsBundleV1 struct { // JSON type overrides for ExecutionPayloadEnvelope. type executionPayloadEnvelopeMarshaling struct { BlockValue *hexutil.Big + Requests []hexutil.Bytes } type PayloadStatusV1 struct { @@ -208,8 +209,8 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { // and that the blockhash of the constructed block matches the parameters. Nil // Withdrawals value will propagate through the returned block. Empty // Withdrawals value must be passed via non-nil, length 0 value in data. -func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (*types.Block, error) { - block, err := ExecutableDataToBlockNoHash(data, versionedHashes, beaconRoot) +func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte) (*types.Block, error) { + block, err := ExecutableDataToBlockNoHash(data, versionedHashes, beaconRoot, requests) if err != nil { return nil, err } @@ -222,7 +223,7 @@ func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, b // ExecutableDataToBlockNoHash is analogous to ExecutableDataToBlock, but is used // for stateless execution, so it skips checking if the executable data hashes to // the requested hash (stateless has to *compute* the root hash, it's not given). -func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (*types.Block, error) { +func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte) (*types.Block, error) { txs, err := decodeTransactions(data.Transactions) if err != nil { return nil, err @@ -257,12 +258,13 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H h := types.DeriveSha(types.Withdrawals(data.Withdrawals), trie.NewStackTrie(nil)) withdrawalsRoot = &h } - // Compute requestsHash if any requests are non-nil. + // Compute requestsHash if any requests were passed. var requestsHash *common.Hash - if data.Requests != nil { - h := types.CalcRequestsHash(data.Requests) + if requests != nil { + h := types.CalcRequestsHash(requests) requestsHash = &h } + header := &types.Header{ ParentHash: data.ParentHash, UncleHash: types.EmptyUncleHash, @@ -283,10 +285,10 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H ExcessBlobGas: data.ExcessBlobGas, BlobGasUsed: data.BlobGasUsed, ParentBeaconRoot: beaconRoot, - RequestsHash: requestsHash, + RequestsHash: requestsHash, // note: need to use recomputed hash value here to ensure validation. } return types.NewBlockWithHeader(header). - WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals, Requests: data.Requests}). + WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals}). WithWitness(data.ExecutionWitness), nil } @@ -312,7 +314,6 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types. Withdrawals: block.Withdrawals(), BlobGasUsed: block.BlobGasUsed(), ExcessBlobGas: block.ExcessBlobGas(), - Requests: block.Requests(), ExecutionWitness: block.ExecutionWitness(), } bundle := BlobsBundleV1{ diff --git a/core/chain_makers.go b/core/chain_makers.go index a8f5504ad24d..761477f549e0 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -348,6 +348,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse var requests [][]byte if config.IsPrague(b.header.Number, b.header.Time) { + requests := [][]byte{} for _, r := range b.receipts { depositRequests, err := ParseDepositLogs(r.Logs, config) if err != nil { @@ -356,8 +357,12 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse requests = append(requests, depositRequests) } } + if requests != nil { + reqHash := types.CalcRequestsHash(requests) + b.header.RequestsHash = &reqHash + } - body := types.Body{Transactions: b.txs, Uncles: b.uncles, Withdrawals: b.withdrawals, Requests: requests} + body := types.Body{Transactions: b.txs, Uncles: b.uncles, Withdrawals: b.withdrawals} block, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, &body, b.receipts) if err != nil { panic(err) diff --git a/core/genesis.go b/core/genesis.go index 79055e7bff3a..33693b2169dc 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -449,7 +449,6 @@ func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block { } var ( withdrawals []*types.Withdrawal - requests [][]byte ) if conf := g.Config; conf != nil { num := big.NewInt(int64(g.Number)) @@ -474,10 +473,9 @@ func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block { } if conf.IsPrague(num, g.Timestamp) { head.RequestsHash = &types.EmptyRequestsHash - requests = make([][]byte, 0) } } - return types.NewBlock(head, &types.Body{Withdrawals: withdrawals, Requests: requests}, nil, trie.NewStackTrie(nil)) + return types.NewBlock(head, &types.Body{Withdrawals: withdrawals}, nil, trie.NewStackTrie(nil)) } // Commit writes the block and state of a genesis specification to the database. diff --git a/core/types/block.go b/core/types/block.go index 4408ec496076..05f6407937ca 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -169,9 +169,8 @@ func (h *Header) SanityCheck() error { func (h *Header) EmptyBody() bool { var ( emptyWithdrawals = h.WithdrawalsHash == nil || *h.WithdrawalsHash == EmptyWithdrawalsHash - emptyRequests = h.RequestsHash == nil || *h.RequestsHash == EmptyReceiptsHash ) - return h.TxHash == EmptyTxsHash && h.UncleHash == EmptyUncleHash && emptyWithdrawals && emptyRequests + return h.TxHash == EmptyTxsHash && h.UncleHash == EmptyUncleHash && emptyWithdrawals } // EmptyReceipts returns true if there are no receipts for this header/block. @@ -185,7 +184,6 @@ type Body struct { Transactions []*Transaction Uncles []*Header Withdrawals []*Withdrawal `rlp:"optional"` - Requests [][]byte `rlp:"optional"` } // Block represents an Ethereum block. @@ -210,7 +208,6 @@ type Block struct { uncles []*Header transactions Transactions withdrawals Withdrawals - requests [][]byte // witness is not an encoded part of the block body. // It is held in Block in order for easy relaying to the places @@ -233,7 +230,6 @@ type extblock struct { Txs []*Transaction Uncles []*Header Withdrawals []*Withdrawal `rlp:"optional"` - Requests [][]byte `rlp:"optional"` } // NewBlock creates a new block. The input data is copied, changes to header and to the @@ -250,7 +246,6 @@ func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher TrieHasher txs = body.Transactions uncles = body.Uncles withdrawals = body.Withdrawals - requests = body.Requests ) if len(txs) == 0 { @@ -289,17 +284,6 @@ func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher TrieHasher b.withdrawals = slices.Clone(withdrawals) } - if requests == nil { - b.header.RequestsHash = nil - } else if len(requests) == 0 { - b.header.RequestsHash = &EmptyRequestsHash - b.requests = [][]byte{} - } else { - h := CalcRequestsHash(requests) - b.header.RequestsHash = &h - b.requests = slices.Clone(requests) - } - return b } @@ -349,7 +333,7 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error { if err := s.Decode(&eb); err != nil { return err } - b.header, b.uncles, b.transactions, b.withdrawals, b.requests = eb.Header, eb.Uncles, eb.Txs, eb.Withdrawals, eb.Requests + b.header, b.uncles, b.transactions, b.withdrawals = eb.Header, eb.Uncles, eb.Txs, eb.Withdrawals b.size.Store(rlp.ListSize(size)) return nil } @@ -361,14 +345,13 @@ func (b *Block) EncodeRLP(w io.Writer) error { Txs: b.transactions, Uncles: b.uncles, Withdrawals: b.withdrawals, - Requests: b.requests, }) } // Body returns the non-header content of the block. // Note the returned data is not an independent copy. func (b *Block) Body() *Body { - return &Body{b.transactions, b.uncles, b.withdrawals, b.requests} + return &Body{b.transactions, b.uncles, b.withdrawals} } // Accessors for body data. These do not return a copy because the content @@ -377,7 +360,6 @@ func (b *Block) Body() *Body { func (b *Block) Uncles() []*Header { return b.uncles } func (b *Block) Transactions() Transactions { return b.transactions } func (b *Block) Withdrawals() Withdrawals { return b.withdrawals } -func (b *Block) Requests() [][]byte { return b.requests } func (b *Block) Transaction(hash common.Hash) *Transaction { for _, transaction := range b.transactions { @@ -510,7 +492,6 @@ func (b *Block) WithBody(body Body) *Block { transactions: slices.Clone(body.Transactions), uncles: make([]*Header, len(body.Uncles)), withdrawals: slices.Clone(body.Withdrawals), - requests: slices.Clone(body.Requests), witness: b.witness, } for i := range body.Uncles { @@ -525,7 +506,6 @@ func (b *Block) WithWitness(witness *ExecutionWitness) *Block { transactions: b.transactions, uncles: b.uncles, withdrawals: b.withdrawals, - requests: b.requests, witness: witness, } } diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 8ee2529d2601..864b1627f55d 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -594,7 +594,7 @@ func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHas // NewPayloadV4 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. // NewPayloadV4 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. -func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) { +func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests []hexutil.Bytes) (engine.PayloadStatusV1, error) { if params.Withdrawals == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) } @@ -604,9 +604,6 @@ func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHas if params.BlobGasUsed == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun")) } - if params.Requests == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil requests post-prague")) - } if versionedHashes == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun")) @@ -614,11 +611,14 @@ func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHas if beaconRoot == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun")) } + if requests == nil { + return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequests post-cancun")) + } if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadV4 must only be called for prague payloads")) } - return api.newPayload(params, versionedHashes, beaconRoot, false) + return api.newPayload(params, versionedHashes, beaconRoot, requests, false) } // NewPayloadWithWitnessV1 is analogous to NewPayloadV1, only it also generates @@ -770,7 +770,7 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV3(params engine.ExecutableData, // ExecuteStatelessPayloadV4 is analogous to NewPayloadV4, only it operates in // a stateless mode on top of a provided witness instead of the local database. -func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { +func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests []hexutil.Bytes, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { if params.Withdrawals == nil { return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) } @@ -780,9 +780,6 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, if params.BlobGasUsed == nil { return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun")) } - if params.Requests == nil { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil requests post-prague")) - } if versionedHashes == nil { return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun")) @@ -790,14 +787,17 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, if beaconRoot == nil { return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun")) } + if requests == nil { + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequests post-prague")) + } if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague { return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("executeStatelessPayloadV4 must only be called for prague payloads")) } - return api.executeStatelessPayload(params, versionedHashes, beaconRoot, opaqueWitness) + return api.executeStatelessPayload(params, versionedHashes, beaconRoot, requests, opaqueWitness) } -func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, witness bool) (engine.PayloadStatusV1, error) { +func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests []hexutil.Bytes, witness bool) (engine.PayloadStatusV1, error) { // The locking here is, strictly, not required. Without these locks, this can happen: // // 1. NewPayload( execdata-N ) is invoked from the CL. It goes all the way down to @@ -815,7 +815,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe defer api.newPayloadLock.Unlock() log.Trace("Engine API request received", "method", "NewPayload", "number", params.Number, "hash", params.BlockHash) - block, err := engine.ExecutableDataToBlock(params, versionedHashes, beaconRoot) + block, err := engine.ExecutableDataToBlock(params, versionedHashes, beaconRoot, convertRequests(requests)) if err != nil { bgu := "nil" if params.BlobGasUsed != nil { @@ -842,8 +842,8 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe "params.ExcessBlobGas", ebg, "len(params.Transactions)", len(params.Transactions), "len(params.Withdrawals)", len(params.Withdrawals), - "len(params.Requests)", len(params.Requests), "beaconRoot", beaconRoot, + "len(requests)", len(requests), "error", err) return api.invalid(err, nil), nil } @@ -927,10 +927,10 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe return engine.PayloadStatusV1{Status: engine.VALID, Witness: ow, LatestValidHash: &hash}, nil } -func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { +func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests []hexutil.Bytes, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { log.Trace("Engine API request received", "method", "ExecuteStatelessPayload", "number", params.Number, "hash", params.BlockHash) - block, err := engine.ExecutableDataToBlockNoHash(params, versionedHashes, beaconRoot) + block, err := engine.ExecutableDataToBlockNoHash(params, versionedHashes, beaconRoot, converRequests(requests)) if err != nil { bgu := "nil" if params.BlobGasUsed != nil { @@ -957,8 +957,8 @@ func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, v "params.ExcessBlobGas", ebg, "len(params.Transactions)", len(params.Transactions), "len(params.Withdrawals)", len(params.Withdrawals), - "len(params.Requests)", len(params.Requests), "beaconRoot", beaconRoot, + "len(executionRequests)", len(requests), "error", err) errorMsg := err.Error() return engine.StatelessPayloadStatusV1{Status: engine.INVALID, ValidationError: &errorMsg}, nil @@ -1278,3 +1278,11 @@ func getBody(block *types.Block) *engine.ExecutionPayloadBody { return &result } + +func convertRequests(requests []hexutil.Bytes) [][]byte { + res := make([][]byte, len(requests)) + for i := range requests { + res[i] = requests[i] + } + return res +} diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index f5a0109ab94d..0972644d800e 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -123,7 +123,6 @@ type rpcBlock struct { Transactions []rpcTransaction `json:"transactions"` UncleHashes []common.Hash `json:"uncles"` Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` - Requests []hexutil.Bytes `json:"requests,omitempty"` } func (ec *Client) getBlock(ctx context.Context, method string, args ...interface{}) (*types.Block, error) { @@ -193,21 +192,11 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface txs[i] = tx.tx } - // Convert requests. - var requests [][]byte - if body.Requests != nil { - requests = make([][]byte, len(body.Requests)) - for i, req := range body.Requests { - requests[i] = req - } - } - return types.NewBlockWithHeader(head).WithBody( types.Body{ Transactions: txs, Uncles: uncles, Withdrawals: body.Withdrawals, - Requests: requests, }), nil } diff --git a/miner/payload_building.go b/miner/payload_building.go index d48ce0faa601..ce4836d8a933 100644 --- a/miner/payload_building.go +++ b/miner/payload_building.go @@ -75,6 +75,7 @@ type Payload struct { full *types.Block fullWitness *stateless.Witness sidecars []*types.BlobTxSidecar + requests [][]byte fullFees *big.Int stop chan struct{} lock sync.Mutex @@ -111,6 +112,7 @@ func (payload *Payload) update(r *newPayloadResult, elapsed time.Duration) { payload.full = r.block payload.fullFees = r.fees payload.sidecars = r.sidecars + payload.requests = r.requests payload.fullWitness = r.witness feesInEther := new(big.Float).Quo(new(big.Float).SetInt(r.fees), big.NewFloat(params.Ether)) @@ -142,6 +144,7 @@ func (payload *Payload) Resolve() *engine.ExecutionPayloadEnvelope { } if payload.full != nil { envelope := engine.BlockToExecutableData(payload.full, payload.fullFees, payload.sidecars) + envelope.Requests = payload.requests if payload.fullWitness != nil { envelope.Witness = new(hexutil.Bytes) *envelope.Witness, _ = rlp.EncodeToBytes(payload.fullWitness) // cannot fail @@ -149,6 +152,7 @@ func (payload *Payload) Resolve() *engine.ExecutionPayloadEnvelope { return envelope } envelope := engine.BlockToExecutableData(payload.empty, big.NewInt(0), nil) + envelope.Requests = payload.requests if payload.emptyWitness != nil { envelope.Witness = new(hexutil.Bytes) *envelope.Witness, _ = rlp.EncodeToBytes(payload.emptyWitness) // cannot fail @@ -163,6 +167,7 @@ func (payload *Payload) ResolveEmpty() *engine.ExecutionPayloadEnvelope { defer payload.lock.Unlock() envelope := engine.BlockToExecutableData(payload.empty, big.NewInt(0), nil) + envelope.Requests = payload.requests if payload.emptyWitness != nil { envelope.Witness = new(hexutil.Bytes) *envelope.Witness, _ = rlp.EncodeToBytes(payload.emptyWitness) // cannot fail @@ -194,6 +199,7 @@ func (payload *Payload) ResolveFull() *engine.ExecutionPayloadEnvelope { close(payload.stop) } envelope := engine.BlockToExecutableData(payload.full, payload.fullFees, payload.sidecars) + envelope.Requests = payload.requests if payload.fullWitness != nil { envelope.Witness = new(hexutil.Bytes) *envelope.Witness, _ = rlp.EncodeToBytes(payload.fullWitness) // cannot fail diff --git a/miner/worker.go b/miner/worker.go index e21219a9a3a2..51df2764fa95 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -76,6 +76,7 @@ type newPayloadResult struct { sidecars []*types.BlobTxSidecar // collected blobs of blob transactions stateDB *state.StateDB // StateDB after executing the transactions receipts []*types.Receipt // Receipts collected during construction + requests [][]byte // Consensus layer requests collected during block construction witness *stateless.Witness // Witness is an optional stateless proof } @@ -115,7 +116,8 @@ func (miner *Miner) generateWork(params *generateParams, witness bool) *newPaylo for _, r := range work.receipts { allLogs = append(allLogs, r.Logs...) } - // Read requests if Prague is enabled. + + // Collect consensus-layer requests if Prague is enabled. var requests [][]byte if miner.chainConfig.IsPrague(work.header.Number, work.header.Time) { depositRequests, err := core.ParseDepositLogs(allLogs, miner.chainConfig) @@ -124,7 +126,11 @@ func (miner *Miner) generateWork(params *generateParams, witness bool) *newPaylo } requests = append(requests, depositRequests) } - body.Requests = requests + if requests != nil { + reqHash := types.CalcRequestsHash(requests) + work.header.RequestsHash = &reqHash + } + block, err := miner.engine.FinalizeAndAssemble(miner.chain, work.header, work.state, &body, work.receipts) if err != nil { return &newPayloadResult{err: err} @@ -135,6 +141,7 @@ func (miner *Miner) generateWork(params *generateParams, witness bool) *newPaylo sidecars: work.sidecars, stateDB: work.state, receipts: work.receipts, + requests: requests, witness: work.witness, } } From eb5aaa25797ff88059bbbaf806030cc17c07f572 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 30 Sep 2024 15:54:44 +0200 Subject: [PATCH 16/32] core/types: fix tests issue --- core/types/deposit_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/core/types/deposit_test.go b/core/types/deposit_test.go index 5ffad9833a49..0648920ac9a3 100644 --- a/core/types/deposit_test.go +++ b/core/types/deposit_test.go @@ -71,7 +71,6 @@ func FuzzUnpackIntoDeposit(f *testing.F) { copy(index[:], i) var enc []byte - enc = append(enc, depositRequestType) enc = append(enc, pubkey[:]...) enc = append(enc, wxCred[:]...) enc = append(enc, amount[:]...) From ff99699c817b5b38595e86f74f2f4ee580c7643a Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 30 Sep 2024 15:54:58 +0200 Subject: [PATCH 17/32] internal/ethapi: remove requests --- internal/ethapi/api.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 79f2965b05fb..bc6e85c3e659 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1436,14 +1436,6 @@ func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *param if block.Withdrawals() != nil { fields["withdrawals"] = block.Withdrawals() } - if block.Requests() != nil { - // Convert requests to hex. - hexreq := make([]hexutil.Bytes, len(block.Requests())) - for i, req := range block.Requests() { - hexreq[i] = req - } - fields["requests"] = hexreq - } return fields } From 0be1fe8c49bdea9c473f99f32c3023dbcced2d15 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 30 Sep 2024 15:55:10 +0200 Subject: [PATCH 18/32] eth/downloader: remove requests --- eth/downloader/fetchers_concurrent_bodies.go | 6 +++--- eth/downloader/queue.go | 15 +-------------- eth/downloader/queue_test.go | 2 +- eth/protocols/eth/protocol.go | 7 +++---- 4 files changed, 8 insertions(+), 22 deletions(-) diff --git a/eth/downloader/fetchers_concurrent_bodies.go b/eth/downloader/fetchers_concurrent_bodies.go index 709df7757507..56359b33c94e 100644 --- a/eth/downloader/fetchers_concurrent_bodies.go +++ b/eth/downloader/fetchers_concurrent_bodies.go @@ -88,10 +88,10 @@ func (q *bodyQueue) request(peer *peerConnection, req *fetchRequest, resCh chan // deliver is responsible for taking a generic response packet from the concurrent // fetcher, unpacking the body data and delivering it to the downloader's queue. func (q *bodyQueue) deliver(peer *peerConnection, packet *eth.Response) (int, error) { - txs, uncles, withdrawals, requests := packet.Res.(*eth.BlockBodiesResponse).Unpack() - hashsets := packet.Meta.([][]common.Hash) // {txs hashes, uncle hashes, withdrawal hashes, requests hashes} + txs, uncles, withdrawals := packet.Res.(*eth.BlockBodiesResponse).Unpack() + hashsets := packet.Meta.([][]common.Hash) // {txs hashes, uncle hashes, withdrawal hashes} - accepted, err := q.queue.DeliverBodies(peer.id, txs, hashsets[0], uncles, hashsets[1], withdrawals, hashsets[2], requests, hashsets[3]) + accepted, err := q.queue.DeliverBodies(peer.id, txs, hashsets[0], uncles, hashsets[1], withdrawals, hashsets[2]) switch { case err == nil && len(txs) == 0: peer.log.Trace("Requested bodies delivered") diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index 23d25ed8fc4c..a2f916ebbcc7 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -785,7 +785,7 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, hashes []comm func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListHashes []common.Hash, uncleLists [][]*types.Header, uncleListHashes []common.Hash, withdrawalLists [][]*types.Withdrawal, withdrawalListHashes []common.Hash, - requestsLists [][][]byte, requestsListHashes []common.Hash) (int, error) { +) (int, error) { q.lock.Lock() defer q.lock.Unlock() @@ -809,19 +809,6 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListH return errInvalidBody } } - if header.RequestsHash == nil { - // nil hash means that requests should not be present in body - if requestsLists[index] != nil { - return errInvalidBody - } - } else { // non-nil hash: body must have requests - if requestsLists[index] == nil { - return errInvalidBody - } - if requestsListHashes[index] != *header.RequestsHash { - return errInvalidBody - } - } // Blocks must have a number of blobs corresponding to the header gas usage, // and zero before the Cancun hardfork. var blobs int diff --git a/eth/downloader/queue_test.go b/eth/downloader/queue_test.go index e29d23f80b7a..857ac4813a7d 100644 --- a/eth/downloader/queue_test.go +++ b/eth/downloader/queue_test.go @@ -341,7 +341,7 @@ func XTestDelivery(t *testing.T) { uncleHashes[i] = types.CalcUncleHash(uncles) } time.Sleep(100 * time.Millisecond) - _, err := q.DeliverBodies(peer.id, txset, txsHashes, uncleset, uncleHashes, nil, nil, nil, nil) + _, err := q.DeliverBodies(peer.id, txset, txsHashes, uncleset, uncleHashes, nil, nil) if err != nil { fmt.Printf("delivered %d bodies %v\n", len(txset), err) } diff --git a/eth/protocols/eth/protocol.go b/eth/protocols/eth/protocol.go index 6c6c0fe83967..af246251dc1b 100644 --- a/eth/protocols/eth/protocol.go +++ b/eth/protocols/eth/protocol.go @@ -229,17 +229,16 @@ type BlockBody struct { // Unpack retrieves the transactions and uncles from the range packet and returns // them in a split flat format that's more consistent with the internal data structures. -func (p *BlockBodiesResponse) Unpack() ([][]*types.Transaction, [][]*types.Header, [][]*types.Withdrawal, [][][]byte) { +func (p *BlockBodiesResponse) Unpack() ([][]*types.Transaction, [][]*types.Header, [][]*types.Withdrawal) { var ( txset = make([][]*types.Transaction, len(*p)) uncleset = make([][]*types.Header, len(*p)) withdrawalset = make([][]*types.Withdrawal, len(*p)) - requestset = make([][][]byte, len(*p)) ) for i, body := range *p { - txset[i], uncleset[i], withdrawalset[i], requestset[i] = body.Transactions, body.Uncles, body.Withdrawals, body.Requests + txset[i], uncleset[i], withdrawalset[i] = body.Transactions, body.Uncles, body.Withdrawals } - return txset, uncleset, withdrawalset, requestset + return txset, uncleset, withdrawalset } // GetReceiptsRequest represents a block receipts query. From 11e82ef581407c09622c71feccc1423f64b3d250 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 30 Sep 2024 15:59:30 +0200 Subject: [PATCH 19/32] core/types: update empty hash --- core/types/hashes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/types/hashes.go b/core/types/hashes.go index 33f543e80c35..f9a37f12c394 100644 --- a/core/types/hashes.go +++ b/core/types/hashes.go @@ -42,7 +42,7 @@ var ( EmptyWithdrawalsHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") // EmptyRequestsHash is the known hash of the empty requests set. - EmptyRequestsHash = common.HexToHash("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347") + EmptyRequestsHash = common.HexToHash("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") // EmptyVerkleHash is the known hash of an empty verkle trie. EmptyVerkleHash = common.Hash{} From f9cc214c06037dfee6b767c33c33c11112d864e8 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 1 Oct 2024 12:37:38 +0200 Subject: [PATCH 20/32] eth/catalyst: change newPayload parameter to requestsHash --- beacon/engine/types.go | 14 +++----- eth/catalyst/api.go | 71 ++++++++++++++++------------------------ eth/catalyst/api_test.go | 18 +++------- 3 files changed, 37 insertions(+), 66 deletions(-) diff --git a/beacon/engine/types.go b/beacon/engine/types.go index 83a33c64b17b..e6254f58f595 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -209,8 +209,8 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { // and that the blockhash of the constructed block matches the parameters. Nil // Withdrawals value will propagate through the returned block. Empty // Withdrawals value must be passed via non-nil, length 0 value in data. -func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte) (*types.Block, error) { - block, err := ExecutableDataToBlockNoHash(data, versionedHashes, beaconRoot, requests) +func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, beaconRoot, requestsHash *common.Hash) (*types.Block, error) { + block, err := ExecutableDataToBlockNoHash(data, versionedHashes, beaconRoot, requestsHash) if err != nil { return nil, err } @@ -223,7 +223,7 @@ func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, b // ExecutableDataToBlockNoHash is analogous to ExecutableDataToBlock, but is used // for stateless execution, so it skips checking if the executable data hashes to // the requested hash (stateless has to *compute* the root hash, it's not given). -func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte) (*types.Block, error) { +func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.Hash, beaconRoot, requestsHash *common.Hash) (*types.Block, error) { txs, err := decodeTransactions(data.Transactions) if err != nil { return nil, err @@ -258,12 +258,6 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H h := types.DeriveSha(types.Withdrawals(data.Withdrawals), trie.NewStackTrie(nil)) withdrawalsRoot = &h } - // Compute requestsHash if any requests were passed. - var requestsHash *common.Hash - if requests != nil { - h := types.CalcRequestsHash(requests) - requestsHash = &h - } header := &types.Header{ ParentHash: data.ParentHash, @@ -285,7 +279,7 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H ExcessBlobGas: data.ExcessBlobGas, BlobGasUsed: data.BlobGasUsed, ParentBeaconRoot: beaconRoot, - RequestsHash: requestsHash, // note: need to use recomputed hash value here to ensure validation. + RequestsHash: requestsHash, } return types.NewBlockWithHeader(header). WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals}). diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 864b1627f55d..ef4534e943d2 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -541,7 +541,7 @@ func (api *ConsensusAPI) NewPayloadV1(params engine.ExecutableData) (engine.Payl if params.Withdrawals != nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("withdrawals not supported in V1")) } - return api.newPayload(params, nil, nil, false) + return api.newPayload(params, nil, nil, nil, false) } // NewPayloadV2 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. @@ -564,7 +564,7 @@ func (api *ConsensusAPI) NewPayloadV2(params engine.ExecutableData) (engine.Payl if params.BlobGasUsed != nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil blobGasUsed pre-cancun")) } - return api.newPayload(params, nil, nil, false) + return api.newPayload(params, nil, nil, nil, false) } // NewPayloadV3 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. @@ -589,12 +589,12 @@ func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHas if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadV3 must only be called for cancun payloads")) } - return api.newPayload(params, versionedHashes, beaconRoot, false) + return api.newPayload(params, versionedHashes, beaconRoot, nil, false) } // NewPayloadV4 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. // NewPayloadV4 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. -func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests []hexutil.Bytes) (engine.PayloadStatusV1, error) { +func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot, requestsHash *common.Hash) (engine.PayloadStatusV1, error) { if params.Withdrawals == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) } @@ -611,14 +611,14 @@ func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHas if beaconRoot == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun")) } - if requests == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequests post-cancun")) + if requestsHash == nil { + return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequestsHash post-prague")) } if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadV4 must only be called for prague payloads")) } - return api.newPayload(params, versionedHashes, beaconRoot, requests, false) + return api.newPayload(params, versionedHashes, beaconRoot, requestsHash, false) } // NewPayloadWithWitnessV1 is analogous to NewPayloadV1, only it also generates @@ -627,7 +627,7 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV1(params engine.ExecutableData) ( if params.Withdrawals != nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("withdrawals not supported in V1")) } - return api.newPayload(params, nil, nil, true) + return api.newPayload(params, nil, nil, nil, true) } // NewPayloadWithWitnessV2 is analogous to NewPayloadV2, only it also generates @@ -651,7 +651,7 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV2(params engine.ExecutableData) ( if params.BlobGasUsed != nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil blobGasUsed pre-cancun")) } - return api.newPayload(params, nil, nil, true) + return api.newPayload(params, nil, nil, nil, true) } // NewPayloadWithWitnessV3 is analogous to NewPayloadV3, only it also generates @@ -677,12 +677,12 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV3(params engine.ExecutableData, v if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadWithWitnessV3 must only be called for cancun payloads")) } - return api.newPayload(params, versionedHashes, beaconRoot, true) + return api.newPayload(params, versionedHashes, beaconRoot, nil, true) } // NewPayloadWithWitnessV4 is analogous to NewPayloadV4, only it also generates // and returns a stateless witness after running the payload. -func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) { +func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot, requestsHash *common.Hash) (engine.PayloadStatusV1, error) { if params.Withdrawals == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) } @@ -692,9 +692,6 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, v if params.BlobGasUsed == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil blobGasUsed post-cancun")) } - if params.Requests == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil requests post-prague")) - } if versionedHashes == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun")) @@ -702,11 +699,14 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, v if beaconRoot == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun")) } + if requestsHash == nil { + return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequestsHash post-prague")) + } if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadWithWitnessV4 must only be called for prague payloads")) } - return api.newPayload(params, versionedHashes, beaconRoot, true) + return api.newPayload(params, versionedHashes, beaconRoot, requestsHash, true) } // ExecuteStatelessPayloadV1 is analogous to NewPayloadV1, only it operates in @@ -715,7 +715,7 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV1(params engine.ExecutableData, if params.Withdrawals != nil { return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("withdrawals not supported in V1")) } - return api.executeStatelessPayload(params, nil, nil, opaqueWitness) + return api.executeStatelessPayload(params, nil, nil, nil, opaqueWitness) } // ExecuteStatelessPayloadV2 is analogous to NewPayloadV2, only it operates in @@ -739,7 +739,7 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV2(params engine.ExecutableData, if params.BlobGasUsed != nil { return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil blobGasUsed pre-cancun")) } - return api.executeStatelessPayload(params, nil, nil, opaqueWitness) + return api.executeStatelessPayload(params, nil, nil, nil, opaqueWitness) } // ExecuteStatelessPayloadV3 is analogous to NewPayloadV3, only it operates in @@ -765,12 +765,12 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV3(params engine.ExecutableData, if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Cancun { return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("executeStatelessPayloadV3 must only be called for cancun payloads")) } - return api.executeStatelessPayload(params, versionedHashes, beaconRoot, opaqueWitness) + return api.executeStatelessPayload(params, versionedHashes, beaconRoot, nil, opaqueWitness) } // ExecuteStatelessPayloadV4 is analogous to NewPayloadV4, only it operates in // a stateless mode on top of a provided witness instead of the local database. -func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests []hexutil.Bytes, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { +func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requestsHash *common.Hash, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { if params.Withdrawals == nil { return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) } @@ -787,17 +787,17 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, if beaconRoot == nil { return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun")) } - if requests == nil { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequests post-prague")) + if requestsHash == nil { + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequestsHash post-prague")) } if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague { return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("executeStatelessPayloadV4 must only be called for prague payloads")) } - return api.executeStatelessPayload(params, versionedHashes, beaconRoot, requests, opaqueWitness) + return api.executeStatelessPayload(params, versionedHashes, beaconRoot, requestsHash, opaqueWitness) } -func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests []hexutil.Bytes, witness bool) (engine.PayloadStatusV1, error) { +func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot, requestsHash *common.Hash, witness bool) (engine.PayloadStatusV1, error) { // The locking here is, strictly, not required. Without these locks, this can happen: // // 1. NewPayload( execdata-N ) is invoked from the CL. It goes all the way down to @@ -815,7 +815,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe defer api.newPayloadLock.Unlock() log.Trace("Engine API request received", "method", "NewPayload", "number", params.Number, "hash", params.BlockHash) - block, err := engine.ExecutableDataToBlock(params, versionedHashes, beaconRoot, convertRequests(requests)) + block, err := engine.ExecutableDataToBlock(params, versionedHashes, beaconRoot, requestsHash) if err != nil { bgu := "nil" if params.BlobGasUsed != nil { @@ -843,7 +843,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe "len(params.Transactions)", len(params.Transactions), "len(params.Withdrawals)", len(params.Withdrawals), "beaconRoot", beaconRoot, - "len(requests)", len(requests), + "requestsHash", requestsHash, "error", err) return api.invalid(err, nil), nil } @@ -927,10 +927,10 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe return engine.PayloadStatusV1{Status: engine.VALID, Witness: ow, LatestValidHash: &hash}, nil } -func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests []hexutil.Bytes, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { +func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot, requestsHash *common.Hash, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { log.Trace("Engine API request received", "method", "ExecuteStatelessPayload", "number", params.Number, "hash", params.BlockHash) - block, err := engine.ExecutableDataToBlockNoHash(params, versionedHashes, beaconRoot, converRequests(requests)) + block, err := engine.ExecutableDataToBlockNoHash(params, versionedHashes, beaconRoot, requestsHash) if err != nil { bgu := "nil" if params.BlobGasUsed != nil { @@ -958,7 +958,7 @@ func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, v "len(params.Transactions)", len(params.Transactions), "len(params.Withdrawals)", len(params.Withdrawals), "beaconRoot", beaconRoot, - "len(executionRequests)", len(requests), + "requestsHash", requestsHash, "error", err) errorMsg := err.Error() return engine.StatelessPayloadStatusV1{Status: engine.INVALID, ValidationError: &errorMsg}, nil @@ -1269,20 +1269,5 @@ func getBody(block *types.Block) *engine.ExecutionPayloadBody { result.Withdrawals = []*types.Withdrawal{} } - if reqs := block.Requests(); reqs != nil { - result.Requests = make([]hexutil.Bytes, len(reqs)) - for j, req := range reqs { - result.Requests[j] = req - } - } - return &result } - -func convertRequests(requests []hexutil.Bytes) [][]byte { - res := make([][]byte, len(requests)) - for i := range requests { - res[i] = requests[i] - } - return res -} diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 6a8c41a55901..d3332e514269 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -25,7 +25,6 @@ import ( "math/big" "math/rand" "reflect" - "slices" "sync" "testing" "time" @@ -326,7 +325,7 @@ func TestEth2NewBlock(t *testing.T) { if err != nil { t.Fatalf("Failed to create the executable data, block %d: %v", i, err) } - block, err := engine.ExecutableDataToBlock(*execData, nil, nil) + block, err := engine.ExecutableDataToBlock(*execData, nil, nil, nil) if err != nil { t.Fatalf("Failed to convert executable data to block %v", err) } @@ -368,7 +367,7 @@ func TestEth2NewBlock(t *testing.T) { if err != nil { t.Fatalf("Failed to create the executable data %v", err) } - block, err := engine.ExecutableDataToBlock(*execData, nil, nil) + block, err := engine.ExecutableDataToBlock(*execData, nil, nil, nil) if err != nil { t.Fatalf("Failed to convert executable data to block %v", err) } @@ -509,7 +508,7 @@ func setupBlocks(t *testing.T, ethservice *eth.Ethereum, n int, parent *types.He } payload := getNewPayload(t, api, parent, w, h) - execResp, err := api.newPayload(*payload, []common.Hash{}, h, false) + execResp, err := api.newPayload(*payload, []common.Hash{}, h, nil, false) if err != nil { t.Fatalf("can't execute payload: %v", err) } @@ -1006,7 +1005,7 @@ func TestSimultaneousNewBlock(t *testing.T) { t.Fatal(testErr) } } - block, err := engine.ExecutableDataToBlock(*execData, nil, nil) + block, err := engine.ExecutableDataToBlock(*execData, nil, nil, nil) if err != nil { t.Fatalf("Failed to convert executable data to block %v", err) } @@ -1569,13 +1568,6 @@ func checkEqualBody(a *types.Body, b *engine.ExecutionPayloadBody) error { if !reflect.DeepEqual(a.Withdrawals, b.Withdrawals) { return fmt.Errorf("withdrawals mismatch") } - - reqEqual := slices.EqualFunc(a.Requests, b.Requests, func(a []byte, b hexutil.Bytes) bool { - return bytes.Equal(a, b) - }) - if !reqEqual { - return fmt.Errorf("requests mismatch") - } return nil } @@ -1611,7 +1603,7 @@ func TestBlockToPayloadWithBlobs(t *testing.T) { if got := len(envelope.BlobsBundle.Blobs); got != want { t.Fatalf("invalid number of blobs: got %v, want %v", got, want) } - _, err := engine.ExecutableDataToBlock(*envelope.ExecutionPayload, make([]common.Hash, 1), nil) + _, err := engine.ExecutableDataToBlock(*envelope.ExecutionPayload, make([]common.Hash, 1), nil, nil) if err != nil { t.Error(err) } From ded9d6b6668820f41fc5926e3631d5fc6829b70c Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 1 Oct 2024 12:40:19 +0200 Subject: [PATCH 21/32] beacon/engine: remove requestsHash in payload --- beacon/engine/gen_ed.go | 6 ------ beacon/engine/types.go | 1 - 2 files changed, 7 deletions(-) diff --git a/beacon/engine/gen_ed.go b/beacon/engine/gen_ed.go index 432ae2722e59..0ae5a3b8f1f1 100644 --- a/beacon/engine/gen_ed.go +++ b/beacon/engine/gen_ed.go @@ -34,7 +34,6 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) { Withdrawals []*types.Withdrawal `json:"withdrawals"` BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"` ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"` - RequestsHash *common.Hash `json:"executionRequestsHash"` ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"` } var enc ExecutableData @@ -60,7 +59,6 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) { enc.Withdrawals = e.Withdrawals enc.BlobGasUsed = (*hexutil.Uint64)(e.BlobGasUsed) enc.ExcessBlobGas = (*hexutil.Uint64)(e.ExcessBlobGas) - enc.RequestsHash = e.RequestsHash enc.ExecutionWitness = e.ExecutionWitness return json.Marshal(&enc) } @@ -85,7 +83,6 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error { Withdrawals []*types.Withdrawal `json:"withdrawals"` BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"` ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"` - RequestsHash *common.Hash `json:"executionRequestsHash"` ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"` } var dec ExecutableData @@ -160,9 +157,6 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error { if dec.ExcessBlobGas != nil { e.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas) } - if dec.RequestsHash != nil { - e.RequestsHash = dec.RequestsHash - } if dec.ExecutionWitness != nil { e.ExecutionWitness = dec.ExecutionWitness } diff --git a/beacon/engine/types.go b/beacon/engine/types.go index e6254f58f595..2a56151edc4b 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -76,7 +76,6 @@ type ExecutableData struct { Withdrawals []*types.Withdrawal `json:"withdrawals"` BlobGasUsed *uint64 `json:"blobGasUsed"` ExcessBlobGas *uint64 `json:"excessBlobGas"` - RequestsHash *common.Hash `json:"executionRequestsHash"` ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"` } From 168d3eb921ffee08fcd1bfb8c7cf5c01de4cca08 Mon Sep 17 00:00:00 2001 From: lightclient Date: Wed, 2 Oct 2024 10:53:36 -0600 Subject: [PATCH 22/32] beacon/engine,eth/catalyst: ensure RequestsHash is passed to newPayload in engine tests --- beacon/engine/types.go | 10 ++++++++++ eth/catalyst/api_test.go | 27 ++++++++++++++++++++------- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/beacon/engine/types.go b/beacon/engine/types.go index 2a56151edc4b..50eccfd2a8ab 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -112,6 +112,16 @@ type ExecutionPayloadEnvelope struct { Witness *hexutil.Bytes `json:"witness"` } +// RequestsHash computes the hash of the requests contained in the envelope or +// returns nil if the list of requests is also nil. +func (e *ExecutionPayloadEnvelope) RequestsHash() *common.Hash { + if e.Requests != nil { + h := types.CalcRequestsHash(e.Requests) + return &h + } + return nil +} + type BlobsBundleV1 struct { Commitments []hexutil.Bytes `json:"commitments"` Proofs []hexutil.Bytes `json:"proofs"` diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index d3332e514269..c7007dac3287 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -507,14 +507,15 @@ func setupBlocks(t *testing.T, ethservice *eth.Ethereum, n int, parent *types.He h = &beaconRoots[i] } - payload := getNewPayload(t, api, parent, w, h) - execResp, err := api.newPayload(*payload, []common.Hash{}, h, nil, false) + envelope := getNewEnvelope(t, api, parent, w, h) + execResp, err := api.newPayload(*envelope.ExecutionPayload, []common.Hash{}, h, envelope.RequestsHash(), false) if err != nil { t.Fatalf("can't execute payload: %v", err) } if execResp.Status != engine.VALID { t.Fatalf("invalid status: %v %s", execResp.Status, *execResp.ValidationError) } + payload := envelope.ExecutionPayload fcState := engine.ForkchoiceStateV1{ HeadBlockHash: payload.BlockHash, SafeBlockHash: payload.ParentHash, @@ -676,7 +677,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) { } } -func assembleBlock(api *ConsensusAPI, parentHash common.Hash, params *engine.PayloadAttributes) (*engine.ExecutableData, error) { +func assembleEnvelope(api *ConsensusAPI, parentHash common.Hash, params *engine.PayloadAttributes) (*engine.ExecutionPayloadEnvelope, error) { args := &miner.BuildPayloadArgs{ Parent: parentHash, Timestamp: params.Timestamp, @@ -689,7 +690,15 @@ func assembleBlock(api *ConsensusAPI, parentHash common.Hash, params *engine.Pay if err != nil { return nil, err } - return payload.ResolveFull().ExecutionPayload, nil + return payload.ResolveFull(), nil +} + +func assembleBlock(api *ConsensusAPI, parentHash common.Hash, params *engine.PayloadAttributes) (*engine.ExecutableData, error) { + envelope, err := assembleEnvelope(api, parentHash, params) + if err != nil { + return nil, err + } + return envelope.ExecutionPayload, nil } func TestEmptyBlocks(t *testing.T) { @@ -752,7 +761,7 @@ func TestEmptyBlocks(t *testing.T) { } } -func getNewPayload(t *testing.T, api *ConsensusAPI, parent *types.Header, withdrawals []*types.Withdrawal, beaconRoot *common.Hash) *engine.ExecutableData { +func getNewEnvelope(t *testing.T, api *ConsensusAPI, parent *types.Header, withdrawals []*types.Withdrawal, beaconRoot *common.Hash) *engine.ExecutionPayloadEnvelope { params := engine.PayloadAttributes{ Timestamp: parent.Time + 1, Random: crypto.Keccak256Hash([]byte{byte(1)}), @@ -761,11 +770,15 @@ func getNewPayload(t *testing.T, api *ConsensusAPI, parent *types.Header, withdr BeaconRoot: beaconRoot, } - payload, err := assembleBlock(api, parent.Hash(), ¶ms) + envelope, err := assembleEnvelope(api, parent.Hash(), ¶ms) if err != nil { t.Fatal(err) } - return payload + return envelope +} + +func getNewPayload(t *testing.T, api *ConsensusAPI, parent *types.Header, withdrawals []*types.Withdrawal, beaconRoot *common.Hash) *engine.ExecutableData { + return getNewEnvelope(t, api, parent, withdrawals, beaconRoot).ExecutionPayload } // setBlockhash sets the blockhash of a modified ExecutableData. From 9181bd36817b566d889d9ebaf4b149c235875860 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 7 Oct 2024 11:43:07 +0200 Subject: [PATCH 23/32] core: remove deposits test --- core/blockchain_test.go | 81 ----------------------------------------- 1 file changed, 81 deletions(-) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index bad7b51a61ef..3d573f999998 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -17,20 +17,17 @@ package core import ( - "bytes" "errors" "fmt" "math/big" "math/rand" "os" "path" - "slices" "sync" "testing" "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/beacon" @@ -4230,81 +4227,3 @@ func TestEIP3651(t *testing.T) { t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual) } } - -// This test checks that deposit contract logs are turned into requests. -func TestEIP6110(t *testing.T) { - var ( - engine = beacon.NewFaker() - - // A sender who makes transactions, has some funds - key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - addr = crypto.PubkeyToAddress(key.PublicKey) - funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether)) - config = *params.AllEthashProtocolChanges - gspec = &Genesis{ - Config: &config, - Alloc: types.GenesisAlloc{ - addr: {Balance: funds}, - config.DepositContractAddress: { - // Simple deposit generator, source: https://gist.github.com/lightclient/54abb2af2465d6969fa6d1920b9ad9d7 - Code: common.Hex2Bytes("6080604052366103aa575f603067ffffffffffffffff811115610025576100246103ae565b5b6040519080825280601f01601f1916602001820160405280156100575781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f8151811061007d5761007c6103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f602067ffffffffffffffff8111156100c7576100c66103ae565b5b6040519080825280601f01601f1916602001820160405280156100f95781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f8151811061011f5761011e6103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f600867ffffffffffffffff811115610169576101686103ae565b5b6040519080825280601f01601f19166020018201604052801561019b5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f815181106101c1576101c06103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f606067ffffffffffffffff81111561020b5761020a6103ae565b5b6040519080825280601f01601f19166020018201604052801561023d5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f81518110610263576102626103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f600867ffffffffffffffff8111156102ad576102ac6103ae565b5b6040519080825280601f01601f1916602001820160405280156102df5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f81518110610305576103046103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f8081819054906101000a900460ff168092919061035090610441565b91906101000a81548160ff021916908360ff160217905550507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c585858585856040516103a09594939291906104d9565b60405180910390a1005b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f60ff82169050919050565b5f61044b82610435565b915060ff820361045e5761045d610408565b5b600182019050919050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f6104ab82610469565b6104b58185610473565b93506104c5818560208601610483565b6104ce81610491565b840191505092915050565b5f60a0820190508181035f8301526104f181886104a1565b9050818103602083015261050581876104a1565b9050818103604083015261051981866104a1565b9050818103606083015261052d81856104a1565b9050818103608083015261054181846104a1565b9050969550505050505056fea26469706673582212208569967e58690162d7d6fe3513d07b393b4c15e70f41505cbbfd08f53eba739364736f6c63430008190033"), - Nonce: 0, - Balance: big.NewInt(0), - }, - }, - } - ) - - gspec.Config.BerlinBlock = common.Big0 - gspec.Config.LondonBlock = common.Big0 - gspec.Config.TerminalTotalDifficulty = common.Big0 - gspec.Config.TerminalTotalDifficultyPassed = true - gspec.Config.ShanghaiTime = u64(0) - gspec.Config.CancunTime = u64(0) - gspec.Config.PragueTime = u64(0) - signer := types.LatestSigner(gspec.Config) - - _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) { - for i := 0; i < 2; i++ { - txdata := &types.DynamicFeeTx{ - ChainID: gspec.Config.ChainID, - Nonce: uint64(i), - To: &config.DepositContractAddress, - Gas: 500000, - GasFeeCap: newGwei(5), - GasTipCap: big.NewInt(2), - AccessList: nil, - Data: []byte{}, - } - tx := types.NewTx(txdata) - tx, _ = types.SignTx(tx, signer, key) - b.AddTx(tx) - } - }) - chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{DisableStack: true}, os.Stderr).Hooks()}, nil) - if err != nil { - t.Fatalf("failed to create tester chain: %v", err) - } - defer chain.Stop() - if n, err := chain.InsertChain(blocks); err != nil { - t.Fatalf("block %d: failed to insert into chain: %v", n, err) - } - - block := chain.GetBlockByNumber(1) - if len(block.Requests()) != 2 { - t.Fatalf("failed to retrieve deposits: have %d, want %d", len(block.Requests()), 5) - } - - wantDeposits := [][]byte{ - hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), - hexutil.MustDecode("0x00010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000"), - } - ok := slices.EqualFunc(block.Requests(), wantDeposits, bytes.Equal) - if !ok { - t.Errorf("requests not equal") - for i, r := range block.Requests() { - t.Logf("%d: %x", i, r) - } - } -} - From 9df0a64611760b2485a70624b29d9c423301da81 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 7 Oct 2024 11:45:20 +0200 Subject: [PATCH 24/32] core/types: remove empty requests hash --- core/genesis.go | 4 +++- core/types/hashes.go | 3 --- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/core/genesis.go b/core/genesis.go index 33693b2169dc..8fef81a0a48e 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -472,7 +472,9 @@ func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block { } } if conf.IsPrague(num, g.Timestamp) { - head.RequestsHash = &types.EmptyRequestsHash + emptyRequests := [][]byte{{0}} + rhash := types.CalcRequestsHash(emptyRequests) + head.RequestsHash = &rhash } } return types.NewBlock(head, &types.Body{Withdrawals: withdrawals}, nil, trie.NewStackTrie(nil)) diff --git a/core/types/hashes.go b/core/types/hashes.go index f9a37f12c394..43e9130fd170 100644 --- a/core/types/hashes.go +++ b/core/types/hashes.go @@ -41,9 +41,6 @@ var ( // EmptyWithdrawalsHash is the known hash of the empty withdrawal set. EmptyWithdrawalsHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") - // EmptyRequestsHash is the known hash of the empty requests set. - EmptyRequestsHash = common.HexToHash("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") - // EmptyVerkleHash is the known hash of an empty verkle trie. EmptyVerkleHash = common.Hash{} ) From d250deb70a7aea1f7805206a6f289021a38e1692 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 7 Oct 2024 11:45:40 +0200 Subject: [PATCH 25/32] core/types: update requests hash function --- core/types/block.go | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/core/types/block.go b/core/types/block.go index 05f6407937ca..61877c4db70f 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -457,12 +457,26 @@ func CalcUncleHash(uncles []*Header) common.Hash { return rlpHash(uncles) } +// CalcRequestsHash creates the block requestsHash value for a list of requests. func CalcRequestsHash(requests [][]byte) common.Hash { + // compute intermediate hashes + hashes := make([][32]byte, len(requests)) h := sha256.New() - for _, item := range requests { + for i, item := range requests { + h.Reset() h.Write(item) - } - return common.Hash(h.Sum(nil)) + var sum [32]byte + h.Sum(sum[:0]) + hashes[i] = sum + } + // final hash + h.Reset() + for _, hash := range hashes { + h.Write(hash[:]) + } + var result common.Hash + h.Sum(result[:0]) + return result } // NewBlockWithHeader creates a block with the given header data. The From 0310778b867cd825d9add3949522707b131aeb85 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 7 Oct 2024 19:27:15 +0200 Subject: [PATCH 26/32] core: remove requests var --- core/chain_makers.go | 1 - 1 file changed, 1 deletion(-) diff --git a/core/chain_makers.go b/core/chain_makers.go index 761477f549e0..ea4246ebce34 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -348,7 +348,6 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse var requests [][]byte if config.IsPrague(b.header.Number, b.header.Time) { - requests := [][]byte{} for _, r := range b.receipts { depositRequests, err := ParseDepositLogs(r.Logs, config) if err != nil { From cb08ea963802005dfb4966fc795a01a945cc1269 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 8 Oct 2024 13:09:10 +0200 Subject: [PATCH 27/32] eth/catalyst: remove requests in GetPayloadBodies --- beacon/engine/types.go | 1 - eth/catalyst/api.go | 21 ++------------------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/beacon/engine/types.go b/beacon/engine/types.go index 50eccfd2a8ab..4ef15c37f1f8 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -338,7 +338,6 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types. type ExecutionPayloadBody struct { TransactionData []hexutil.Bytes `json:"transactions"` Withdrawals []*types.Withdrawal `json:"withdrawals"` - Requests []hexutil.Bytes `json:"requests"` } // Client identifiers to support ClientVersionV1. diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index ef4534e943d2..cf75269ff621 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -1185,13 +1185,7 @@ func (api *ConsensusAPI) GetPayloadBodiesByHashV1(hashes []common.Hash) []*engin bodies := make([]*engine.ExecutionPayloadBody, len(hashes)) for i, hash := range hashes { block := api.eth.BlockChain().GetBlockByHash(hash) - body := getBody(block) - if body != nil { - // Nil out the V2 values, clients should know to not request V1 objects - // after Prague. - body.Requests = nil - } - bodies[i] = body + bodies[i] = getBody(block) } return bodies } @@ -1210,18 +1204,7 @@ func (api *ConsensusAPI) GetPayloadBodiesByHashV2(hashes []common.Hash) []*engin // GetPayloadBodiesByRangeV1 implements engine_getPayloadBodiesByRangeV1 which allows for retrieval of a range // of block bodies by the engine api. func (api *ConsensusAPI) GetPayloadBodiesByRangeV1(start, count hexutil.Uint64) ([]*engine.ExecutionPayloadBody, error) { - bodies, err := api.getBodiesByRange(start, count) - if err != nil { - return nil, err - } - // Nil out the V2 values, clients should know to not request V1 objects - // after Prague. - for i := range bodies { - if bodies[i] != nil { - bodies[i].Requests = nil - } - } - return bodies, nil + return api.getBodiesByRange(start, count) } // GetPayloadBodiesByRangeV2 implements engine_getPayloadBodiesByRangeV1 which allows for retrieval of a range From f27902e05c6e7892e16a305d8baba1b6f2d57814 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 8 Oct 2024 13:22:04 +0200 Subject: [PATCH 28/32] core: add check for requests before prague --- core/block_validator.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/block_validator.go b/core/block_validator.go index 35a815b6a15f..35695d34d85e 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -147,8 +147,10 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD if header.RequestsHash != nil { reqhash := types.CalcRequestsHash(res.Requests) if reqhash != *header.RequestsHash { - return fmt.Errorf("invalid deposit root hash (remote: %x local: %x)", *header.RequestsHash, reqhash) + return fmt.Errorf("invalid requests hash (remote: %x local: %x)", *header.RequestsHash, reqhash) } + } else if res.Requests != nil { + return fmt.Errorf("block has requests before prague fork") } // Validate the state root against the received state root and throw // an error if they don't match. From b87d53679f4124523ce1fd232a8b744e57867a5a Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 9 Oct 2024 10:23:36 +0200 Subject: [PATCH 29/32] core/types: simplify CalcRequestsHash Co-authored-by: Shude Li --- core/types/block.go | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/core/types/block.go b/core/types/block.go index 61877c4db70f..42f8c6fd04cd 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -459,24 +459,15 @@ func CalcUncleHash(uncles []*Header) common.Hash { // CalcRequestsHash creates the block requestsHash value for a list of requests. func CalcRequestsHash(requests [][]byte) common.Hash { - // compute intermediate hashes - hashes := make([][32]byte, len(requests)) - h := sha256.New() - for i, item := range requests { - h.Reset() - h.Write(item) - var sum [32]byte - h.Sum(sum[:0]) - hashes[i] = sum - } - // final hash - h.Reset() - for _, hash := range hashes { - h.Write(hash[:]) - } - var result common.Hash - h.Sum(result[:0]) - return result + h1, h2 := sha256.New(), sha256.New() + var buf common.Hash + for _, item := range requests { + h1.Reset() + h1.Write(item) + h2.Write(h1.Sum(buf[:0])) + } + h2.Sum(buf[:0]) + return buf } // NewBlockWithHeader creates a block with the given header data. The From 2509bded0eb8462bc94f055dc37886367ee5cfaf Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 9 Oct 2024 10:36:10 +0200 Subject: [PATCH 30/32] core: fix deposit requests in GenerateChain --- core/chain_makers.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/core/chain_makers.go b/core/chain_makers.go index ea4246ebce34..085890f0a38a 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -348,13 +348,15 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse var requests [][]byte if config.IsPrague(b.header.Number, b.header.Time) { + var blockLogs []*types.Log for _, r := range b.receipts { - depositRequests, err := ParseDepositLogs(r.Logs, config) - if err != nil { - panic(fmt.Sprintf("failed to parse deposit log: %v", err)) - } - requests = append(requests, depositRequests) + blockLogs = append(blockLogs, r.Logs...) + } + depositRequests, err := ParseDepositLogs(blockLogs, config) + if err != nil { + panic(fmt.Sprintf("failed to parse deposit log: %v", err)) } + requests = append(requests, depositRequests) } if requests != nil { reqHash := types.CalcRequestsHash(requests) From 92dc664cf26268075fc38753e758750d4d1905bc Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 9 Oct 2024 11:08:44 +0200 Subject: [PATCH 31/32] eth/catalyst: change back to passing requests --- beacon/engine/types.go | 12 +++++++++--- eth/catalyst/api.go | 36 ++++++++++++++++++------------------ eth/catalyst/api_test.go | 2 +- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/beacon/engine/types.go b/beacon/engine/types.go index 4ef15c37f1f8..cc8ac7ba3148 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -218,8 +218,8 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { // and that the blockhash of the constructed block matches the parameters. Nil // Withdrawals value will propagate through the returned block. Empty // Withdrawals value must be passed via non-nil, length 0 value in data. -func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, beaconRoot, requestsHash *common.Hash) (*types.Block, error) { - block, err := ExecutableDataToBlockNoHash(data, versionedHashes, beaconRoot, requestsHash) +func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte) (*types.Block, error) { + block, err := ExecutableDataToBlockNoHash(data, versionedHashes, beaconRoot, requests) if err != nil { return nil, err } @@ -232,7 +232,7 @@ func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, b // ExecutableDataToBlockNoHash is analogous to ExecutableDataToBlock, but is used // for stateless execution, so it skips checking if the executable data hashes to // the requested hash (stateless has to *compute* the root hash, it's not given). -func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.Hash, beaconRoot, requestsHash *common.Hash) (*types.Block, error) { +func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte) (*types.Block, error) { txs, err := decodeTransactions(data.Transactions) if err != nil { return nil, err @@ -268,6 +268,12 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H withdrawalsRoot = &h } + var requestsHash *common.Hash + if requests != nil { + h := types.CalcRequestsHash(requests) + requestsHash = &h + } + header := &types.Header{ ParentHash: data.ParentHash, UncleHash: types.EmptyUncleHash, diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index cf75269ff621..79034fd00a83 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -594,7 +594,7 @@ func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHas // NewPayloadV4 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. // NewPayloadV4 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. -func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot, requestsHash *common.Hash) (engine.PayloadStatusV1, error) { +func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte) (engine.PayloadStatusV1, error) { if params.Withdrawals == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) } @@ -611,14 +611,14 @@ func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHas if beaconRoot == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun")) } - if requestsHash == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequestsHash post-prague")) + if requests == nil { + return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequests post-prague")) } if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadV4 must only be called for prague payloads")) } - return api.newPayload(params, versionedHashes, beaconRoot, requestsHash, false) + return api.newPayload(params, versionedHashes, beaconRoot, requests, false) } // NewPayloadWithWitnessV1 is analogous to NewPayloadV1, only it also generates @@ -682,7 +682,7 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV3(params engine.ExecutableData, v // NewPayloadWithWitnessV4 is analogous to NewPayloadV4, only it also generates // and returns a stateless witness after running the payload. -func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot, requestsHash *common.Hash) (engine.PayloadStatusV1, error) { +func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte) (engine.PayloadStatusV1, error) { if params.Withdrawals == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) } @@ -699,14 +699,14 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, v if beaconRoot == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun")) } - if requestsHash == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequestsHash post-prague")) + if requests == nil { + return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequests post-prague")) } if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadWithWitnessV4 must only be called for prague payloads")) } - return api.newPayload(params, versionedHashes, beaconRoot, requestsHash, true) + return api.newPayload(params, versionedHashes, beaconRoot, requests, true) } // ExecuteStatelessPayloadV1 is analogous to NewPayloadV1, only it operates in @@ -770,7 +770,7 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV3(params engine.ExecutableData, // ExecuteStatelessPayloadV4 is analogous to NewPayloadV4, only it operates in // a stateless mode on top of a provided witness instead of the local database. -func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requestsHash *common.Hash, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { +func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { if params.Withdrawals == nil { return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) } @@ -787,17 +787,17 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, if beaconRoot == nil { return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun")) } - if requestsHash == nil { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequestsHash post-prague")) + if requests == nil { + return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequests post-prague")) } if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague { return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("executeStatelessPayloadV4 must only be called for prague payloads")) } - return api.executeStatelessPayload(params, versionedHashes, beaconRoot, requestsHash, opaqueWitness) + return api.executeStatelessPayload(params, versionedHashes, beaconRoot, requests, opaqueWitness) } -func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot, requestsHash *common.Hash, witness bool) (engine.PayloadStatusV1, error) { +func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte, witness bool) (engine.PayloadStatusV1, error) { // The locking here is, strictly, not required. Without these locks, this can happen: // // 1. NewPayload( execdata-N ) is invoked from the CL. It goes all the way down to @@ -815,7 +815,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe defer api.newPayloadLock.Unlock() log.Trace("Engine API request received", "method", "NewPayload", "number", params.Number, "hash", params.BlockHash) - block, err := engine.ExecutableDataToBlock(params, versionedHashes, beaconRoot, requestsHash) + block, err := engine.ExecutableDataToBlock(params, versionedHashes, beaconRoot, requests) if err != nil { bgu := "nil" if params.BlobGasUsed != nil { @@ -843,7 +843,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe "len(params.Transactions)", len(params.Transactions), "len(params.Withdrawals)", len(params.Withdrawals), "beaconRoot", beaconRoot, - "requestsHash", requestsHash, + "len(requests)", len(requests), "error", err) return api.invalid(err, nil), nil } @@ -927,10 +927,10 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe return engine.PayloadStatusV1{Status: engine.VALID, Witness: ow, LatestValidHash: &hash}, nil } -func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot, requestsHash *common.Hash, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { +func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { log.Trace("Engine API request received", "method", "ExecuteStatelessPayload", "number", params.Number, "hash", params.BlockHash) - block, err := engine.ExecutableDataToBlockNoHash(params, versionedHashes, beaconRoot, requestsHash) + block, err := engine.ExecutableDataToBlockNoHash(params, versionedHashes, beaconRoot, requests) if err != nil { bgu := "nil" if params.BlobGasUsed != nil { @@ -958,7 +958,7 @@ func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, v "len(params.Transactions)", len(params.Transactions), "len(params.Withdrawals)", len(params.Withdrawals), "beaconRoot", beaconRoot, - "requestsHash", requestsHash, + "len(requests)", len(requests), "error", err) errorMsg := err.Error() return engine.StatelessPayloadStatusV1{Status: engine.INVALID, ValidationError: &errorMsg}, nil diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index c7007dac3287..0afbc62aa16f 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -508,7 +508,7 @@ func setupBlocks(t *testing.T, ethservice *eth.Ethereum, n int, parent *types.He } envelope := getNewEnvelope(t, api, parent, w, h) - execResp, err := api.newPayload(*envelope.ExecutionPayload, []common.Hash{}, h, envelope.RequestsHash(), false) + execResp, err := api.newPayload(*envelope.ExecutionPayload, []common.Hash{}, h, envelope.Requests, false) if err != nil { t.Fatalf("can't execute payload: %v", err) } From b67f40b62586369206d0f916f54ab4e7673bf0c6 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 9 Oct 2024 12:15:50 +0200 Subject: [PATCH 32/32] beacon/engine: remove RequestsHash in EPE --- beacon/engine/types.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/beacon/engine/types.go b/beacon/engine/types.go index cc8ac7ba3148..31f5e6fc2a62 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -112,16 +112,6 @@ type ExecutionPayloadEnvelope struct { Witness *hexutil.Bytes `json:"witness"` } -// RequestsHash computes the hash of the requests contained in the envelope or -// returns nil if the list of requests is also nil. -func (e *ExecutionPayloadEnvelope) RequestsHash() *common.Hash { - if e.Requests != nil { - h := types.CalcRequestsHash(e.Requests) - return &h - } - return nil -} - type BlobsBundleV1 struct { Commitments []hexutil.Bytes `json:"commitments"` Proofs []hexutil.Bytes `json:"proofs"`