From c11669c4ef401520b2e5019f44849b8e4214f631 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 9 Oct 2024 12:24:58 +0200 Subject: [PATCH 1/3] 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 b2eb1dc982..0ae5a3b8f1 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 fa45d94c4c..039884e842 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 74c56f403d..31f5e6fc2a 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 a2a034974d..960c25202b 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 { @@ -388,21 +388,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 4f51f5dc17..35695d34d8 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 7ab647fe42..a7c4f65706 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, 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 66c9bb1961..622e2acef8 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 7f47b6ee24..871d70c7e9 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)) @@ -474,11 +473,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 ceda6a731b..72c7968299 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -70,8 +70,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 { @@ -80,6 +81,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, MessageReplayMode) @@ -95,13 +97,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) @@ -273,15 +277,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 65cd4973e4..bed20802ab 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 1c00658d5b..42f8c6fd04 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 172acc36ed..3bba2c7aa4 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 ed2e18445d..0648920ac9 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 a65691188f..0000000000 --- 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 cbd197072e..43e9130fd1 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 7b1cade26e..0000000000 --- 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 638fdad252..d1a27f6e16 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -543,7 +543,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. @@ -567,7 +567,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. @@ -593,12 +593,12 @@ func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHas if api.eth.BlockChain().Config().LatestFork(params.Timestamp, arbosVersion) != 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")) } @@ -608,9 +608,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")) @@ -618,11 +615,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, types.DeserializeHeaderExtraInformation(api.eth.BlockChain().CurrentHeader()).ArbOSFormatVersion) != 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 @@ -631,7 +631,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 @@ -656,7 +656,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 @@ -682,12 +682,12 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV3(params engine.ExecutableData, v if api.eth.BlockChain().Config().LatestFork(params.Timestamp, types.DeserializeHeaderExtraInformation(api.eth.BlockChain().CurrentHeader()).ArbOSFormatVersion) != 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")) } @@ -697,9 +697,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")) @@ -707,11 +704,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, types.DeserializeHeaderExtraInformation(api.eth.BlockChain().CurrentHeader()).ArbOSFormatVersion) != 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 @@ -720,7 +720,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 @@ -745,7 +745,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 @@ -771,12 +771,12 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV3(params engine.ExecutableData, if api.eth.BlockChain().Config().LatestFork(params.Timestamp, types.DeserializeHeaderExtraInformation(api.eth.BlockChain().CurrentHeader()).ArbOSFormatVersion) != 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")) } @@ -786,9 +786,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")) @@ -796,14 +793,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, types.DeserializeHeaderExtraInformation(api.eth.BlockChain().CurrentHeader()).ArbOSFormatVersion) != 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 @@ -821,7 +821,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 { @@ -848,8 +848,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 } @@ -933,10 +933,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 { @@ -963,8 +963,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 @@ -1191,13 +1191,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 } @@ -1216,18 +1210,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 @@ -1262,36 +1245,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 13973a2e89..50f0ff5ab1 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 709df77575..56359b33c9 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 adad450200..a2f916ebbc 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 e29d23f80b..857ac4813a 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 951352319f..6a2a348707 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 cbc895eabb..af246251dc 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 dde4e6d783..df6c09f540 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 1dd27be426..1f1ef8fbe3 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1563,12 +1563,9 @@ func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *param fillArbitrumNitroHeaderInfo(block.Header(), fields) } - 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 d48ce0faa6..ce4836d8a9 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 a0d635722a..51e36a09ae 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, } } From 609b165a2faf177eb35ff108d845d247b8e71462 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 11 Oct 2024 21:36:13 +0200 Subject: [PATCH 2/3] all: implement EIP-7002 & EIP-7251 (#30571) This is a redo of #29052 based on newer specs. Here we implement EIPs scheduled for the Prague fork: - EIP-7002: Execution layer triggerable withdrawals - EIP-7251: Increase the MAX_EFFECTIVE_BALANCE Co-authored-by: lightclient --- cmd/evm/internal/t8ntool/execution.go | 21 ++++- core/blockchain_test.go | 84 +++++++++++++++++++ core/chain_makers.go | 10 +++ core/genesis.go | 11 +-- core/state_processor.go | 53 +++++++++++- core/types/block.go | 3 +- eth/catalyst/simulated_beacon.go | 2 +- eth/catalyst/simulated_beacon_test.go | 2 +- eth/tracers/internal/tracetest/supply_test.go | 2 +- ...h_getBlockReceipts-block-with-blob-tx.json | 2 +- ...eceipts-block-with-contract-create-tx.json | 2 +- ...ockReceipts-block-with-dynamic-fee-tx.json | 2 +- ...ts-block-with-legacy-contract-call-tx.json | 4 +- ...eceipts-block-with-legacy-transfer-tx.json | 2 +- .../eth_getBlockReceipts-tag-latest.json | 2 +- .../eth_getTransactionReceipt-blob-tx.json | 2 +- ...TransactionReceipt-create-contract-tx.json | 2 +- ...eipt-create-contract-with-access-list.json | 2 +- ...ansactionReceipt-dynamic-tx-with-logs.json | 2 +- ...TransactionReceipt-normal-transfer-tx.json | 2 +- .../eth_getTransactionReceipt-with-logs.json | 4 +- miner/worker.go | 10 +++ params/config.go | 3 +- params/protocol_params.go | 28 +++++-- 24 files changed, 216 insertions(+), 41 deletions(-) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 960c25202b..1d5ea24e9f 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -66,7 +66,7 @@ type ExecutionResult struct { WithdrawalsRoot *common.Hash `json:"withdrawalsRoot,omitempty"` CurrentExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"` CurrentBlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed,omitempty"` - RequestsHash *common.Hash `json:"requestsRoot,omitempty"` + RequestsHash *common.Hash `json:"requestsHash,omitempty"` Requests [][]byte `json:"requests,omitempty"` } @@ -382,8 +382,10 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, execRs.CurrentExcessBlobGas = (*math.HexOrDecimal64)(&excessBlobGas) execRs.CurrentBlobGasUsed = (*math.HexOrDecimal64)(&blobGasUsed) } + + var requests [][]byte if chainConfig.IsPrague(vmContext.BlockNumber, vmContext.Time) { - // Parse the requests from the logs + // EIP-6110 deposits var allLogs []*types.Log for _, receipt := range receipts { allLogs = append(allLogs, receipt.Logs...) @@ -392,12 +394,23 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.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 + requests = append(requests, depositRequests) + // create EVM for system calls + vmenv := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vm.Config{}) + // EIP-7002 withdrawals + withdrawalRequests := core.ProcessWithdrawalQueue(vmenv, statedb) + requests = append(requests, withdrawalRequests) + // EIP-7251 consolidations + consolidationRequests := core.ProcessConsolidationQueue(vmenv, statedb) + requests = append(requests, consolidationRequests) + } + if requests != nil { + // Set requestsHash on block. h := types.CalcRequestsHash(requests) execRs.RequestsHash = &h execRs.Requests = requests } + // Re-create statedb instance with new root upon the updated database // for accessing latest states. statedb, err = state.New(root, statedb.Database()) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index a7c4f65706..20419b1281 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -4227,3 +4227,87 @@ func TestEIP3651(t *testing.T) { t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual) } } + +// Simple deposit generator, source: https://gist.github.com/lightclient/54abb2af2465d6969fa6d1920b9ad9d7 +var depositsGeneratorCode = common.FromHex("6080604052366103aa575f603067ffffffffffffffff811115610025576100246103ae565b5b6040519080825280601f01601f1916602001820160405280156100575781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f8151811061007d5761007c6103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f602067ffffffffffffffff8111156100c7576100c66103ae565b5b6040519080825280601f01601f1916602001820160405280156100f95781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f8151811061011f5761011e6103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f600867ffffffffffffffff811115610169576101686103ae565b5b6040519080825280601f01601f19166020018201604052801561019b5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f815181106101c1576101c06103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f606067ffffffffffffffff81111561020b5761020a6103ae565b5b6040519080825280601f01601f19166020018201604052801561023d5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f81518110610263576102626103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f600867ffffffffffffffff8111156102ad576102ac6103ae565b5b6040519080825280601f01601f1916602001820160405280156102df5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f81518110610305576103046103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f8081819054906101000a900460ff168092919061035090610441565b91906101000a81548160ff021916908360ff160217905550507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c585858585856040516103a09594939291906104d9565b60405180910390a1005b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f60ff82169050919050565b5f61044b82610435565b915060ff820361045e5761045d610408565b5b600182019050919050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f6104ab82610469565b6104b58185610473565b93506104c5818560208601610483565b6104ce81610491565b840191505092915050565b5f60a0820190508181035f8301526104f181886104a1565b9050818103602083015261050581876104a1565b9050818103604083015261051981866104a1565b9050818103606083015261052d81856104a1565b9050818103608083015261054181846104a1565b9050969550505050505056fea26469706673582212208569967e58690162d7d6fe3513d07b393b4c15e70f41505cbbfd08f53eba739364736f6c63430008190033") + +// This is a smoke test for EIP-7685 requests added in the Prague fork. The test first +// creates a block containing requests, and then inserts it into the chain to run +// validation. +func TestPragueRequests(t *testing.T) { + var ( + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + config = *params.MergedTestChainConfig + signer = types.LatestSigner(&config) + engine = beacon.NewFaker() + ) + gspec := &Genesis{ + Config: &config, + Alloc: types.GenesisAlloc{ + addr1: {Balance: big.NewInt(9999900000000000)}, + config.DepositContractAddress: {Code: depositsGeneratorCode}, + params.WithdrawalQueueAddress: {Code: params.WithdrawalQueueCode}, + params.ConsolidationQueueAddress: {Code: params.ConsolidationQueueCode}, + }, + } + + _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) { + // create deposit + depositTx := types.MustSignNewTx(key1, signer, &types.DynamicFeeTx{ + ChainID: gspec.Config.ChainID, + Nonce: 0, + To: &config.DepositContractAddress, + Gas: 500_000, + GasFeeCap: newGwei(5), + GasTipCap: big.NewInt(2), + }) + b.AddTx(depositTx) + + // create withdrawal request + withdrawalTx := types.MustSignNewTx(key1, signer, &types.DynamicFeeTx{ + ChainID: gspec.Config.ChainID, + Nonce: 1, + To: ¶ms.WithdrawalQueueAddress, + Gas: 500_000, + GasFeeCap: newGwei(5), + GasTipCap: big.NewInt(2), + Value: newGwei(1), + Data: common.FromHex("b917cfdc0d25b72d55cf94db328e1629b7f4fde2c30cdacf873b664416f76a0c7f7cc50c9f72a3cb84be88144cde91250000000000000d80"), + }) + b.AddTx(withdrawalTx) + + // create consolidation request + consolidationTx := types.MustSignNewTx(key1, signer, &types.DynamicFeeTx{ + ChainID: gspec.Config.ChainID, + Nonce: 2, + To: ¶ms.ConsolidationQueueAddress, + Gas: 500_000, + GasFeeCap: newGwei(5), + GasTipCap: big.NewInt(2), + Value: newGwei(1), + Data: common.FromHex("b917cfdc0d25b72d55cf94db328e1629b7f4fde2c30cdacf873b664416f76a0c7f7cc50c9f72a3cb84be88144cde9125b9812f7d0b1f2f969b52bbb2d316b0c2fa7c9dba85c428c5e6c27766bcc4b0c6e874702ff1eb1c7024b08524a9771601"), + }) + b.AddTx(consolidationTx) + }) + + // Check block has the correct requests hash. + rh := blocks[0].RequestsHash() + if rh == nil { + t.Fatal("block has nil requests hash") + } + expectedRequestsHash := common.HexToHash("0x06ffb72b9f0823510b128bca6cd4f96f59b745de6791e9fc350b596e7605101e") + if *rh != expectedRequestsHash { + t.Fatalf("block has wrong requestsHash %v, want %v", *rh, expectedRequestsHash) + } + + // Insert block to check validation. + chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, 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) + } +} diff --git a/core/chain_makers.go b/core/chain_makers.go index 622e2acef8..130651dfb8 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) { + // EIP-6110 deposits var blockLogs []*types.Log for _, r := range b.receipts { blockLogs = append(blockLogs, r.Logs...) @@ -357,6 +358,15 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse panic(fmt.Sprintf("failed to parse deposit log: %v", err)) } requests = append(requests, depositRequests) + // create EVM for system calls + blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase) + vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, cm.config, vm.Config{}) + // EIP-7002 withdrawals + withdrawalRequests := ProcessWithdrawalQueue(vmenv, statedb) + requests = append(requests, withdrawalRequests) + // EIP-7251 consolidations + consolidationRequests := ProcessConsolidationQueue(vmenv, statedb) + requests = append(requests, consolidationRequests) } if requests != nil { reqHash := types.CalcRequestsHash(requests) diff --git a/core/genesis.go b/core/genesis.go index 871d70c7e9..8312ecf4f8 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -473,7 +473,7 @@ func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block { } } if conf.IsPrague(num, g.Timestamp) { - emptyRequests := [][]byte{{0}} + emptyRequests := [][]byte{{0x00}, {0x01}, {0x02}} rhash := types.CalcRequestsHash(emptyRequests) head.RequestsHash = &rhash } @@ -596,10 +596,11 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis { common.BytesToAddress([]byte{7}): {Balance: big.NewInt(1)}, // ECScalarMul common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b - // Pre-deploy EIP-4788 system contract - params.BeaconRootsAddress: {Nonce: 1, Code: params.BeaconRootsCode, Balance: common.Big0}, - // Pre-deploy EIP-2935 history contract. - params.HistoryStorageAddress: {Nonce: 1, Code: params.HistoryStorageCode, Balance: common.Big0}, + // Pre-deploy system contracts + params.BeaconRootsAddress: {Nonce: 1, Code: params.BeaconRootsCode, Balance: common.Big0}, + params.HistoryStorageAddress: {Nonce: 1, Code: params.HistoryStorageCode, Balance: common.Big0}, + params.WithdrawalQueueAddress: {Nonce: 1, Code: params.WithdrawalQueueCode, Balance: common.Big0}, + params.ConsolidationQueueAddress: {Nonce: 1, Code: params.ConsolidationQueueCode, Balance: common.Big0}, }, } if faucet != nil { diff --git a/core/state_processor.go b/core/state_processor.go index 72c7968299..470e017fea 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -101,11 +101,18 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // Read requests if Prague is enabled. var requests [][]byte if p.config.IsPrague(block.Number(), block.Time()) { + // EIP-6110 deposits depositRequests, err := ParseDepositLogs(allLogs, p.config) if err != nil { return nil, err } requests = append(requests, depositRequests) + // EIP-7002 withdrawals + withdrawalRequests := ProcessWithdrawalQueue(vmenv, statedb) + requests = append(requests, withdrawalRequests) + // EIP-7251 consolidations + consolidationRequests := ProcessConsolidationQueue(vmenv, statedb) + requests = append(requests, consolidationRequests) } // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) @@ -230,9 +237,6 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *stat defer tracer.OnSystemCallEnd() } } - - // If EIP-4788 is enabled, we need to invoke the beaconroot storage contract with - // the new root msg := &Message{ From: params.SystemAddress, GasLimit: 30_000_000, @@ -259,7 +263,6 @@ func ProcessParentBlockHash(prevHash common.Hash, vmenv *vm.EVM, statedb *state. defer tracer.OnSystemCallEnd() } } - msg := &Message{ From: params.SystemAddress, GasLimit: 30_000_000, @@ -275,6 +278,48 @@ func ProcessParentBlockHash(prevHash common.Hash, vmenv *vm.EVM, statedb *state. statedb.Finalise(true) } +// ProcessWithdrawalQueue calls the EIP-7002 withdrawal queue contract. +// It returns the opaque request data returned by the contract. +func ProcessWithdrawalQueue(vmenv *vm.EVM, statedb *state.StateDB) []byte { + return processRequestsSystemCall(vmenv, statedb, 0x01, params.WithdrawalQueueAddress) +} + +// ProcessConsolidationQueue calls the EIP-7251 consolidation queue contract. +// It returns the opaque request data returned by the contract. +func ProcessConsolidationQueue(vmenv *vm.EVM, statedb *state.StateDB) []byte { + return processRequestsSystemCall(vmenv, statedb, 0x02, params.ConsolidationQueueAddress) +} + +func processRequestsSystemCall(vmenv *vm.EVM, statedb *state.StateDB, requestType byte, addr common.Address) []byte { + if tracer := vmenv.Config.Tracer; tracer != nil { + if tracer.OnSystemCallStart != nil { + tracer.OnSystemCallStart() + } + if tracer.OnSystemCallEnd != nil { + defer tracer.OnSystemCallEnd() + } + } + + msg := &Message{ + From: params.SystemAddress, + GasLimit: 30_000_000, + GasPrice: common.Big0, + GasFeeCap: common.Big0, + GasTipCap: common.Big0, + To: &addr, + } + vmenv.Reset(NewEVMTxContext(msg), statedb) + statedb.AddAddressToAccessList(addr) + ret, _, _ := vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560) + statedb.Finalise(true) + + // Create withdrawals requestsData with prefix 0x01 + requestsData := make([]byte, len(ret)+1) + requestsData[0] = requestType + copy(requestsData[1:], ret) + return requestsData +} + // ParseDepositLogs extracts the EIP-6110 deposit values from logs emitted by // BeaconDepositContract. func ParseDepositLogs(logs []*types.Log, config *params.ChainConfig) ([]byte, error) { diff --git a/core/types/block.go b/core/types/block.go index 42f8c6fd04..f20fc7d778 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -402,7 +402,8 @@ func (b *Block) BaseFee() *big.Int { return new(big.Int).Set(b.header.BaseFee) } -func (b *Block) BeaconRoot() *common.Hash { return b.header.ParentBeaconRoot } +func (b *Block) BeaconRoot() *common.Hash { return b.header.ParentBeaconRoot } +func (b *Block) RequestsHash() *common.Hash { return b.header.RequestsHash } func (b *Block) ExcessBlobGas() *uint64 { var excessBlobGas *uint64 diff --git a/eth/catalyst/simulated_beacon.go b/eth/catalyst/simulated_beacon.go index 7e6377cbf3..ca8146020d 100644 --- a/eth/catalyst/simulated_beacon.go +++ b/eth/catalyst/simulated_beacon.go @@ -223,7 +223,7 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal, timestamp u } } // Mark the payload as canon - if _, err = c.engineAPI.NewPayloadV3(*payload, blobHashes, &common.Hash{}); err != nil { + if _, err = c.engineAPI.NewPayloadV4(*payload, blobHashes, &common.Hash{}, envelope.Requests); err != nil { return err } c.setCurrentState(payload.BlockHash, finalizedHash) diff --git a/eth/catalyst/simulated_beacon_test.go b/eth/catalyst/simulated_beacon_test.go index 711e8f1d60..7db1769234 100644 --- a/eth/catalyst/simulated_beacon_test.go +++ b/eth/catalyst/simulated_beacon_test.go @@ -40,7 +40,7 @@ func startSimulatedBeaconEthService(t *testing.T, genesis *core.Genesis, period n, err := node.New(&node.Config{ P2P: p2p.Config{ - ListenAddr: "127.0.0.1:8545", + ListenAddr: "127.0.0.1:0", NoDiscovery: true, MaxPeers: 0, }, diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index 0d9e7faa64..12a67f624a 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -86,7 +86,7 @@ func TestSupplyOmittedFields(t *testing.T) { expected := supplyInfo{ Number: 0, - Hash: common.HexToHash("0x52f276d96f0afaaf2c3cb358868bdc2779c4b0cb8de3e7e5302e247c0b66a703"), + Hash: common.HexToHash("0xc02ee8ee5b54a40e43f0fa827d431e1bd4f217e941790dda10b2521d1925a20b"), ParentHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), } actual := out[expected.Number] diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json index 09fb734d39..d315353ec6 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json @@ -2,7 +2,7 @@ { "blobGasPrice": "0x1", "blobGasUsed": "0x20000", - "blockHash": "0xd1392771155ce83f6403c6af275efd22bed567030c21168fcc9dbad5004eb245", + "blockHash": "0x11e6318d77a45c01f89f76b56d36c6936c5250f4e2bd238cb7b09df73cf0cb7d", "blockNumber": "0x6", "contractAddress": null, "cumulativeGasUsed": "0x5208", diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json index ab14d56394..f2e5ced2be 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json @@ -1,6 +1,6 @@ [ { - "blockHash": "0x56ea26cf955d7f2e08e194ad212ca4d5f99ee8e0b19dec3c71d8faafa33b1d22", + "blockHash": "0x5526cd89bc188f20fd5e9bb50d8054dc5a51a81a74ed07eacf36a4a8b10de4b1", "blockNumber": "0x2", "contractAddress": "0xae9bea628c4ce503dcfd7e305cab4e29e7476592", "cumulativeGasUsed": "0xcf50", diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json index 9e137e241f..71afd85e54 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json @@ -1,6 +1,6 @@ [ { - "blockHash": "0xf41e7a7a716382f20464cf76c6ae1fa701e9d32f5cc550ebfd2391b9642ae6bc", + "blockHash": "0x3e946aa9e252873af511b257d9d89a1bcafa54ce7c6a6442f8407ecdf81e288d", "blockNumber": "0x4", "contractAddress": null, "cumulativeGasUsed": "0x538d", diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json index 1db7d02b1c..f089ac45ae 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json @@ -1,6 +1,6 @@ [ { - "blockHash": "0xa1410af902e98b32e0bbe464f8637ff464f1d4344b585127d2ce71f9cb39cb8a", + "blockHash": "0xc281d4299fc4e8ce5bba7ecb8deb50f5403d604c806b36aa887dfe2ff84c064f", "blockNumber": "0x3", "contractAddress": null, "cumulativeGasUsed": "0x5e28", @@ -19,7 +19,7 @@ "blockNumber": "0x3", "transactionHash": "0xeaf3921cbf03ba45bad4e6ab807b196ce3b2a0b5bacc355b6272fa96b11b4287", "transactionIndex": "0x0", - "blockHash": "0xa1410af902e98b32e0bbe464f8637ff464f1d4344b585127d2ce71f9cb39cb8a", + "blockHash": "0xc281d4299fc4e8ce5bba7ecb8deb50f5403d604c806b36aa887dfe2ff84c064f", "logIndex": "0x0", "removed": false } diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json index 9a55927839..8b69dddd66 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json @@ -1,6 +1,6 @@ [ { - "blockHash": "0x797d0c5603eccb33cc8ebd1300e977746512ec49e6b89087c7aad28ff760a26f", + "blockHash": "0xda50d57d8802553b00bb8e4d777bd5c4114086941119ca04edb15429f4818ed9", "blockNumber": "0x1", "contractAddress": null, "cumulativeGasUsed": "0x5208", diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json b/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json index 09fb734d39..d315353ec6 100644 --- a/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json +++ b/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json @@ -2,7 +2,7 @@ { "blobGasPrice": "0x1", "blobGasUsed": "0x20000", - "blockHash": "0xd1392771155ce83f6403c6af275efd22bed567030c21168fcc9dbad5004eb245", + "blockHash": "0x11e6318d77a45c01f89f76b56d36c6936c5250f4e2bd238cb7b09df73cf0cb7d", "blockNumber": "0x6", "contractAddress": null, "cumulativeGasUsed": "0x5208", diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json b/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json index 58f5657429..5debbd4447 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json @@ -1,7 +1,7 @@ { "blobGasPrice": "0x1", "blobGasUsed": "0x20000", - "blockHash": "0xd1392771155ce83f6403c6af275efd22bed567030c21168fcc9dbad5004eb245", + "blockHash": "0x11e6318d77a45c01f89f76b56d36c6936c5250f4e2bd238cb7b09df73cf0cb7d", "blockNumber": "0x6", "contractAddress": null, "cumulativeGasUsed": "0x5208", diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json index 48aa567f23..8cf2ead10f 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json @@ -1,5 +1,5 @@ { - "blockHash": "0x56ea26cf955d7f2e08e194ad212ca4d5f99ee8e0b19dec3c71d8faafa33b1d22", + "blockHash": "0x5526cd89bc188f20fd5e9bb50d8054dc5a51a81a74ed07eacf36a4a8b10de4b1", "blockNumber": "0x2", "contractAddress": "0xae9bea628c4ce503dcfd7e305cab4e29e7476592", "cumulativeGasUsed": "0xcf50", diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json index a679972b8e..34c318faca 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json @@ -1,5 +1,5 @@ { - "blockHash": "0x69bf6ba924d95b6c50b0357768e5c892bd1b00cdf2f97e2e81fc06a76dfa57e3", + "blockHash": "0xa04ad6be58c45fe483991b89416572bc50426b0de44b769757e95c704250f874", "blockNumber": "0x5", "contractAddress": "0xfdaa97661a584d977b4d3abb5370766ff5b86a18", "cumulativeGasUsed": "0xe01c", diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json b/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json index 1cd5656d6f..9f023ed6e3 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json @@ -1,5 +1,5 @@ { - "blockHash": "0xf41e7a7a716382f20464cf76c6ae1fa701e9d32f5cc550ebfd2391b9642ae6bc", + "blockHash": "0x3e946aa9e252873af511b257d9d89a1bcafa54ce7c6a6442f8407ecdf81e288d", "blockNumber": "0x4", "contractAddress": null, "cumulativeGasUsed": "0x538d", diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json b/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json index 2400bd8252..f180a21977 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json @@ -1,5 +1,5 @@ { - "blockHash": "0x797d0c5603eccb33cc8ebd1300e977746512ec49e6b89087c7aad28ff760a26f", + "blockHash": "0xda50d57d8802553b00bb8e4d777bd5c4114086941119ca04edb15429f4818ed9", "blockNumber": "0x1", "contractAddress": null, "cumulativeGasUsed": "0x5208", diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json b/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json index 596bcdaa0d..61aed4b7bd 100644 --- a/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json +++ b/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json @@ -1,5 +1,5 @@ { - "blockHash": "0xa1410af902e98b32e0bbe464f8637ff464f1d4344b585127d2ce71f9cb39cb8a", + "blockHash": "0xc281d4299fc4e8ce5bba7ecb8deb50f5403d604c806b36aa887dfe2ff84c064f", "blockNumber": "0x3", "contractAddress": null, "cumulativeGasUsed": "0x5e28", @@ -18,7 +18,7 @@ "blockNumber": "0x3", "transactionHash": "0xeaf3921cbf03ba45bad4e6ab807b196ce3b2a0b5bacc355b6272fa96b11b4287", "transactionIndex": "0x0", - "blockHash": "0xa1410af902e98b32e0bbe464f8637ff464f1d4344b585127d2ce71f9cb39cb8a", + "blockHash": "0xc281d4299fc4e8ce5bba7ecb8deb50f5403d604c806b36aa887dfe2ff84c064f", "logIndex": "0x0", "removed": false } diff --git a/miner/worker.go b/miner/worker.go index 51e36a09ae..8476838d35 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -120,11 +120,21 @@ func (miner *Miner) generateWork(params *generateParams, witness bool) *newPaylo // Collect consensus-layer requests if Prague is enabled. var requests [][]byte if miner.chainConfig.IsPrague(work.header.Number, work.header.Time) { + // EIP-6110 deposits depositRequests, err := core.ParseDepositLogs(allLogs, miner.chainConfig) if err != nil { return &newPayloadResult{err: err} } requests = append(requests, depositRequests) + // create EVM for system calls + blockContext := core.NewEVMBlockContext(work.header, miner.chain, &work.header.Coinbase) + vmenv := vm.NewEVM(blockContext, vm.TxContext{}, work.state, miner.chainConfig, vm.Config{}) + // EIP-7002 withdrawals + withdrawalRequests := core.ProcessWithdrawalQueue(vmenv, work.state) + requests = append(requests, withdrawalRequests) + // EIP-7251 consolidations + consolidationRequests := core.ProcessConsolidationQueue(vmenv, work.state) + requests = append(requests, consolidationRequests) } if requests != nil { reqHash := types.CalcRequestsHash(requests) diff --git a/params/config.go b/params/config.go index 1647785866..a4b61c4d47 100644 --- a/params/config.go +++ b/params/config.go @@ -159,6 +159,7 @@ var ( GrayGlacierBlock: big.NewInt(0), ShanghaiTime: newUint64(0), CancunTime: newUint64(0), + PragueTime: newUint64(0), TerminalTotalDifficulty: big.NewInt(0), TerminalTotalDifficultyPassed: true, } @@ -247,7 +248,7 @@ var ( MergeNetsplitBlock: big.NewInt(0), ShanghaiTime: newUint64(0), CancunTime: newUint64(0), - PragueTime: nil, + PragueTime: newUint64(0), VerkleTime: nil, TerminalTotalDifficulty: big.NewInt(0), TerminalTotalDifficultyPassed: true, diff --git a/params/protocol_params.go b/params/protocol_params.go index d3d22d91f9..b53ab2edb3 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -186,22 +186,32 @@ var Bls12381G1MultiExpDiscountTable = [128]uint64{1000, 949, 848, 797, 764, 750, // Bls12381G2MultiExpDiscountTable is the gas discount table for BLS12-381 G2 multi exponentiation operation var Bls12381G2MultiExpDiscountTable = [128]uint64{1000, 1000, 923, 884, 855, 832, 812, 796, 782, 770, 759, 749, 740, 732, 724, 717, 711, 704, 699, 693, 688, 683, 679, 674, 670, 666, 663, 659, 655, 652, 649, 646, 643, 640, 637, 634, 632, 629, 627, 624, 622, 620, 618, 615, 613, 611, 609, 607, 606, 604, 602, 600, 598, 597, 595, 593, 592, 590, 589, 587, 586, 584, 583, 582, 580, 579, 578, 576, 575, 574, 573, 571, 570, 569, 568, 567, 566, 565, 563, 562, 561, 560, 559, 558, 557, 556, 555, 554, 553, 552, 552, 551, 550, 549, 548, 547, 546, 545, 545, 544, 543, 542, 541, 541, 540, 539, 538, 537, 537, 536, 535, 535, 534, 533, 532, 532, 531, 530, 530, 529, 528, 528, 527, 526, 526, 525, 524, 524} +// Difficulty parameters. var ( DifficultyBoundDivisor = big.NewInt(2048) // The bound divisor of the difficulty, used in the update calculations. GenesisDifficulty = big.NewInt(131072) // Difficulty of the Genesis block. MinimumDifficulty = big.NewInt(131072) // The minimum that the difficulty may ever be. DurationLimit = big.NewInt(13) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not. +) - // BeaconRootsAddress is the address where historical beacon roots are stored as per EIP-4788 - BeaconRootsAddress = common.HexToAddress("0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02") - - // BeaconRootsCode is the code where historical beacon roots are stored as per EIP-4788 - BeaconRootsCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500") - +// System contracts. +var ( // SystemAddress is where the system-transaction is sent from as per EIP-4788 SystemAddress = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe") - // HistoryStorageAddress is where the historical block hashes are stored. + + // EIP-4788 - Beacon block root in the EVM + BeaconRootsAddress = common.HexToAddress("0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02") + BeaconRootsCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500") + + // EIP-2935 - Serve historical block hashes from state HistoryStorageAddress = common.HexToAddress("0x0aae40965e6800cd9b1f4b05ff21581047e3f91e") - // HistoryStorageCode is the code with getters for historical block hashes. - HistoryStorageCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe1460575767ffffffffffffffff5f3511605357600143035f3511604b575f35612000014311604b57611fff5f3516545f5260205ff35b5f5f5260205ff35b5f5ffd5b5f35611fff60014303165500") + HistoryStorageCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe1460575767ffffffffffffffff5f3511605357600143035f3511604b575f35612000014311604b57611fff5f3516545f5260205ff35b5f5f5260205ff35b5f5ffd5b5f35611fff60014303165500") + + // EIP-7002 - Execution layer triggerable withdrawals + WithdrawalQueueAddress = common.HexToAddress("0x09Fc772D0857550724b07B850a4323f39112aAaA") + WithdrawalQueueCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe1460c7573615156028575f545f5260205ff35b36603814156101f05760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff146101f057600182026001905f5b5f821115608057810190830284830290049160010191906065565b9093900434106101f057600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260385f601437604c5fa0600101600355005b6003546002548082038060101160db575060105b5f5b81811461017f5780604c02838201600302600401805490600101805490600101549160601b83528260140152807fffffffffffffffffffffffffffffffff0000000000000000000000000000000016826034015260401c906044018160381c81600701538160301c81600601538160281c81600501538160201c81600401538160181c81600301538160101c81600201538160081c81600101535360010160dd565b9101809214610191579060025561019c565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14156101c957505f5b6001546002828201116101de5750505f6101e4565b01600290035b5f555f600155604c025ff35b5f5ffd") + + // EIP-7251 - Increase the MAX_EFFECTIVE_BALANCE + ConsolidationQueueAddress = common.HexToAddress("0x01aBEa29659e5e97C95107F20bb753cD3e09bBBb") + ConsolidationQueueCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe1460cf573615156028575f545f5260205ff35b366060141561019a5760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1461019a57600182026001905f5b5f821115608057810190830284830290049160010191906065565b90939004341061019a57600154600101600155600354806004026004013381556001015f358155600101602035815560010160403590553360601b5f5260605f60143760745fa0600101600355005b6003546002548082038060011160e3575060015b5f5b8181146101295780607402838201600402600401805490600101805490600101805490600101549260601b84529083601401528260340152906054015260010160e5565b910180921461013b5790600255610146565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff141561017357505f5b6001546001828201116101885750505f61018e565b01600190035b5f555f6001556074025ff35b5f5ffd") ) From f2b63b28a3a50a648567d98176b94abed2c83e34 Mon Sep 17 00:00:00 2001 From: Pepper Lebeck-Jobe Date: Fri, 28 Feb 2025 15:25:21 +0100 Subject: [PATCH 3/3] Fix a missing argument in blockchain_test.go --- core/blockchain_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 20419b1281..ea8d53aa12 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -4302,7 +4302,7 @@ func TestPragueRequests(t *testing.T) { } // Insert block to check validation. - chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil) + chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, nil, gspec, nil, engine, vm.Config{}, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) }