From 2936b41514302ec35b420f92e06aa525b96409a7 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 9 Oct 2024 12:24:58 +0200 Subject: [PATCH] all: implement flat deposit requests encoding (#30425) This implements recent changes to EIP-7685, EIP-6110, and execution-apis. --------- Co-authored-by: lightclient Co-authored-by: Shude Li --- beacon/engine/gen_ed.go | 6 - beacon/engine/gen_epe.go | 14 ++ beacon/engine/types.go | 44 ++---- cmd/evm/internal/t8ntool/execution.go | 16 +- core/block_validator.go | 8 +- core/blockchain_test.go | 87 ---------- core/chain_makers.go | 20 ++- core/genesis.go | 8 +- core/state_processor.go | 18 ++- core/types.go | 2 +- core/types/block.go | 40 ++--- core/types/deposit.go | 79 +++------- core/types/deposit_test.go | 28 ++-- core/types/gen_deposit_json.go | 70 --------- core/types/hashes.go | 3 - core/types/request.go | 157 ------------------- eth/catalyst/api.go | 115 +++++--------- eth/catalyst/api_test.go | 71 ++++----- eth/downloader/fetchers_concurrent_bodies.go | 6 +- eth/downloader/queue.go | 15 +- eth/downloader/queue_test.go | 2 +- eth/protocols/eth/handlers.go | 2 +- eth/protocols/eth/protocol.go | 9 +- ethclient/ethclient.go | 3 +- internal/ethapi/api.go | 5 +- miner/payload_building.go | 6 + miner/worker.go | 15 +- 27 files changed, 222 insertions(+), 627 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..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"` - Deposits types.Deposits `json:"depositRequests"` 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.Deposits = e.Deposits 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"` - Deposits *types.Deposits `json:"depositRequests"` 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.Deposits != nil { - e.Deposits = *dec.Deposits - } 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 74c56f403de2..31f5e6fc2a62 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"` - Deposits types.Deposits `json:"depositRequests"` ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"` } @@ -108,6 +107,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"` } @@ -121,6 +121,7 @@ type BlobsBundleV1 struct { // JSON type overrides for ExecutionPayloadEnvelope. type executionPayloadEnvelopeMarshaling struct { BlockValue *hexutil.Big + Requests []hexutil.Bytes } type PayloadStatusV1 struct { @@ -207,8 +208,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 } @@ -221,7 +222,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 @@ -256,19 +257,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. - 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 requests != nil { + h := types.CalcRequestsHash(requests) requestsHash = &h } + header := &types.Header{ ParentHash: data.ParentHash, UncleHash: types.EmptyUncleHash, @@ -292,7 +287,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}). WithWitness(data.ExecutionWitness), nil } @@ -332,30 +327,13 @@ 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"` } // Client identifiers to support ClientVersionV1. diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 5fd1d6a4a6ad..d5beb308807e 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 { @@ -385,21 +385,15 @@ 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.DeriveSha(requests, trie.NewStackTrie(nil)) + h := types.CalcRequestsHash(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..35695d34d85e 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -145,10 +145,12 @@ 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.CalcRequestsHash(res.Requests) + if reqhash != *header.RequestsHash { + 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. diff --git a/core/blockchain_test.go b/core/blockchain_test.go index d157a7bc3c93..3d573f999998 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -4227,90 +4227,3 @@ func TestEIP3651(t *testing.T) { t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual) } } - -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..085890f0a38a 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -346,18 +346,24 @@ 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) { + var blockLogs []*types.Log for _, r := range b.receipts { - d, err := ParseDepositLogs(r.Logs, config) - if err != nil { - panic(fmt.Sprintf("failed to parse deposit log: %v", err)) - } - requests = append(requests, d...) + 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) + 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 31db49f527e4..8fef81a0a48e 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 types.Requests ) if conf := g.Config; conf != nil { num := big.NewInt(int64(g.Number)) @@ -473,11 +472,12 @@ func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block { } } if conf.IsPrague(num, g.Timestamp) { - head.RequestsHash = &types.EmptyRequestsHash - requests = make(types.Requests, 0) + emptyRequests := [][]byte{{0}} + rhash := types.CalcRequestsHash(emptyRequests) + head.RequestsHash = &rhash } } - 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/state_processor.go b/core/state_processor.go index c74a33c378f4..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 types.Requests + 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) (types.Requests, error) { - deposits := make(types.Requests, 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 { - 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..42f8c6fd04cd 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -18,6 +18,7 @@ package types import ( + "crypto/sha256" "encoding/binary" "fmt" "io" @@ -168,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. @@ -184,7 +184,6 @@ type Body struct { Transactions []*Transaction Uncles []*Header Withdrawals []*Withdrawal `rlp:"optional"` - Requests []*Request `rlp:"optional"` } // Block represents an Ethereum block. @@ -209,7 +208,6 @@ type Block struct { uncles []*Header transactions Transactions withdrawals Withdrawals - requests Requests // 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 +230,6 @@ type extblock struct { Txs []*Transaction Uncles []*Header Withdrawals []*Withdrawal `rlp:"optional"` - Requests []*Request `rlp:"optional"` } // NewBlock creates a new block. The input data is copied, changes to header and to the @@ -249,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 { @@ -288,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 = Requests{} - } else { - h := DeriveSha(Requests(requests), hasher) - b.header.RequestsHash = &h - b.requests = slices.Clone(requests) - } - return b } @@ -348,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 } @@ -360,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 @@ -376,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() Requests { return b.requests } func (b *Block) Transaction(hash common.Hash) *Transaction { for _, transaction := range b.transactions { @@ -474,6 +457,19 @@ 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 { + 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 // header data is copied, changes to header and to the field values // will not affect the block. @@ -501,7 +497,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 { @@ -516,7 +511,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/core/types/deposit.go b/core/types/deposit.go index 172acc36ed3e..3bba2c7aa4fb 100644 --- a/core/types/deposit.go +++ b/core/types/deposit.go @@ -17,52 +17,27 @@ 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 ( + depositRequestSize = 192 +) // 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 + + request := make([]byte, depositRequestSize) + const ( + pubkeyOffset = 0 + 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 +45,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(request[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(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. - d.Amount = binary.LittleEndian.Uint64(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(d.Signature[:], data[b:b+96]) + copy(request[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(request[indexOffset:], data[b:b+8]) + return request, nil } diff --git a/core/types/deposit_test.go b/core/types/deposit_test.go index ed2e18445d3f..0648920ac9a3 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,26 @@ 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, 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 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) } }) } 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..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("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") - // 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..79034fd00a83 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) (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")) } @@ -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.Deposits == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil deposits 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-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, false) + return api.newPayload(params, versionedHashes, beaconRoot, requests, 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 *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")) } @@ -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.Deposits == nil { - return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil deposits 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 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, true) + return api.newPayload(params, versionedHashes, beaconRoot, requests, 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, 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")) } @@ -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.Deposits == nil { - return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil deposits 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 [][]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) + block, err := engine.ExecutableDataToBlock(params, versionedHashes, beaconRoot, 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.Deposits)", len(params.Deposits), "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 [][]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) + block, err := engine.ExecutableDataToBlockNoHash(params, versionedHashes, beaconRoot, 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.Deposits)", len(params.Deposits), "beaconRoot", beaconRoot, + "len(requests)", len(requests), "error", err) errorMsg := err.Error() return engine.StatelessPayloadStatusV1{Status: engine.INVALID, ValidationError: &errorMsg}, nil @@ -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.Deposits = 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].Deposits = nil - } - } - return bodies, nil + return api.getBodiesByRange(start, count) } // GetPayloadBodiesByRangeV2 implements engine_getPayloadBodiesByRangeV1 which allows for retrieval of a range @@ -1256,36 +1239,18 @@ func getBody(block *types.Block) *engine.ExecutionPayloadBody { return nil } - var ( - body = block.Body() - txs = make([]hexutil.Bytes, len(body.Transactions)) - withdrawals = body.Withdrawals - depositRequests types.Deposits - ) + var result engine.ExecutionPayloadBody - for j, tx := range body.Transactions { - txs[j], _ = tx.MarshalBinary() + 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 withdrawals == nil && block.Header().WithdrawalsHash != nil { - withdrawals = make([]*types.Withdrawal, 0) + result.Withdrawals = block.Withdrawals() + if block.Withdrawals() == nil && block.Header().WithdrawalsHash != nil { + result.Withdrawals = []*types.Withdrawal{} } - 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) - } - } - } - - 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..0afbc62aa16f 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -20,6 +20,7 @@ import ( "bytes" "context" crand "crypto/rand" + "errors" "fmt" "math/big" "math/rand" @@ -324,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) } @@ -366,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) } @@ -506,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, false) + envelope := getNewEnvelope(t, api, parent, w, h) + execResp, err := api.newPayload(*envelope.ExecutionPayload, []common.Hash{}, h, envelope.Requests, 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, @@ -675,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, @@ -688,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) { @@ -751,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)}), @@ -760,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. @@ -1004,7 +1018,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) } @@ -1416,8 +1430,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 +1508,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,38 +1563,25 @@ 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 - } - - var deposits types.Deposits - if a.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. - deposits = make(types.Deposits, 0) - } - for _, r := range a.Requests { - if d, ok := r.Inner().(*types.Deposit); ok { - deposits = append(deposits, d) - } + return fmt.Errorf("withdrawals mismatch") } - return reflect.DeepEqual(deposits, b.Deposits) + return nil } func TestBlockToPayloadWithBlobs(t *testing.T) { @@ -1615,7 +1616,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) } 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 adad45020040..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 [][]*types.Request, 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/handlers.go b/eth/protocols/eth/handlers.go index 951352319ffc..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.DeriveSha(types.Requests(body.Requests), hasher) + requestsHashes[i] = types.CalcRequestsHash(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..af246251dc1b 100644 --- a/eth/protocols/eth/protocol.go +++ b/eth/protocols/eth/protocol.go @@ -224,22 +224,21 @@ 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) { 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)) ) 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. diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index a1148bcedbc6..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 []*types.Request `json:"requests,omitempty"` } func (ec *Client) getBlock(ctx context.Context, method string, args ...interface{}) (*types.Block, error) { @@ -192,12 +191,12 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface } txs[i] = tx.tx } + return types.NewBlockWithHeader(head).WithBody( types.Body{ Transactions: txs, Uncles: uncles, Withdrawals: body.Withdrawals, - Requests: body.Requests, }), nil } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index a50295289340..d66e32b8c21e 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1433,12 +1433,9 @@ 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 { - fields["requests"] = block.Requests() - } return fields } 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 930c3e8f5b1b..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,14 +116,21 @@ 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) { - 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) + } + 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} @@ -133,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, } }