diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index d711ae56ac..c1c557913f 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -17,12 +17,15 @@ package t8ntool import ( + "bytes" "context" "encoding/json" + "errors" "fmt" stdmath "math" "math/big" "os" + "sort" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -33,6 +36,7 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/stateless" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types/bal" @@ -77,12 +81,20 @@ type ExecutionResult struct { RequestsHash *common.Hash `json:"requestsHash,omitempty"` Requests [][]byte `json:"requests"` - BlockAccessList hexutil.Bytes `json:"blockAccessList,omitempty"` - BlockAccessListHash *common.Hash `json:"blockAccessListHash,omitempty"` + BlockAccessList hexutil.Bytes `json:"blockAccessList,omitempty"` + BlockAccessListHash *common.Hash `json:"blockAccessListHash,omitempty"` + ExecutionWitness *executionWitness `json:"executionWitness,omitempty"` } type executionResultMarshaling struct { - Requests []hexutil.Bytes `json:"requests"` + Requests []hexutil.Bytes `json:"requests"` + BlockAccessList hexutil.Bytes `json:"blockAccessList,omitempty"` +} + +type executionWitness struct { + State []hexutil.Bytes `json:"state"` + Codes []hexutil.Bytes `json:"codes"` + Headers []hexutil.Bytes `json:"headers"` } type ommer struct { @@ -92,27 +104,28 @@ type ommer struct { //go:generate go run github.com/fjl/gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go type stEnv struct { - Coinbase common.Address `json:"currentCoinbase" gencodec:"required"` - Difficulty *big.Int `json:"currentDifficulty"` - Random *big.Int `json:"currentRandom"` - ParentDifficulty *big.Int `json:"parentDifficulty"` - ParentBaseFee *big.Int `json:"parentBaseFee,omitempty"` - ParentGasUsed uint64 `json:"parentGasUsed,omitempty"` - ParentGasLimit uint64 `json:"parentGasLimit,omitempty"` - GasLimit uint64 `json:"currentGasLimit" gencodec:"required"` - Number uint64 `json:"currentNumber" gencodec:"required"` - Timestamp uint64 `json:"currentTimestamp" gencodec:"required"` - ParentTimestamp uint64 `json:"parentTimestamp,omitempty"` - BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` - Ommers []ommer `json:"ommers,omitempty"` - Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` - BaseFee *big.Int `json:"currentBaseFee,omitempty"` - ParentUncleHash common.Hash `json:"parentUncleHash"` - ExcessBlobGas *uint64 `json:"currentExcessBlobGas,omitempty"` - ParentExcessBlobGas *uint64 `json:"parentExcessBlobGas,omitempty"` - ParentBlobGasUsed *uint64 `json:"parentBlobGasUsed,omitempty"` - ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"` - SlotNumber *uint64 `json:"slotNumber"` + Coinbase common.Address `json:"currentCoinbase" gencodec:"required"` + Difficulty *big.Int `json:"currentDifficulty"` + Random *big.Int `json:"currentRandom"` + ParentDifficulty *big.Int `json:"parentDifficulty"` + ParentBaseFee *big.Int `json:"parentBaseFee,omitempty"` + ParentGasUsed uint64 `json:"parentGasUsed,omitempty"` + ParentGasLimit uint64 `json:"parentGasLimit,omitempty"` + GasLimit uint64 `json:"currentGasLimit" gencodec:"required"` + Number uint64 `json:"currentNumber" gencodec:"required"` + Timestamp uint64 `json:"currentTimestamp" gencodec:"required"` + ParentTimestamp uint64 `json:"parentTimestamp,omitempty"` + BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` + Ommers []ommer `json:"ommers,omitempty"` + Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` + BaseFee *big.Int `json:"currentBaseFee,omitempty"` + ParentUncleHash common.Hash `json:"parentUncleHash"` + ExcessBlobGas *uint64 `json:"currentExcessBlobGas,omitempty"` + ParentExcessBlobGas *uint64 `json:"parentExcessBlobGas,omitempty"` + ParentBlobGasUsed *uint64 `json:"parentBlobGasUsed,omitempty"` + ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"` + SlotNumber *uint64 `json:"slotNumber"` + BlockHeaders map[math.HexOrDecimal64]hexutil.Bytes `json:"blockHeaders,omitempty"` } type stEnvMarshaling struct { @@ -132,6 +145,7 @@ type stEnvMarshaling struct { ParentExcessBlobGas *math.HexOrDecimal64 ParentBlobGasUsed *math.HexOrDecimal64 SlotNumber *math.HexOrDecimal64 + BlockHeaders map[math.HexOrDecimal64]hexutil.Bytes } type rejectedTx struct { @@ -139,6 +153,105 @@ type rejectedTx struct { Err string `json:"error"` } +type t8nHeaderReader struct { + headers map[uint64]*types.Header + err error +} + +func newT8nHeaderReader(raw map[math.HexOrDecimal64]hexutil.Bytes) (*t8nHeaderReader, error) { + reader := &t8nHeaderReader{headers: make(map[uint64]*types.Header)} + for rawNumber, encoded := range raw { + number := uint64(rawNumber) + var header types.Header + if err := rlp.DecodeBytes(encoded, &header); err != nil { + return nil, fmt.Errorf("failed to decode blockHeaders[%d]: %w", number, err) + } + if header.Number == nil || !header.Number.IsUint64() || header.Number.Uint64() != number { + return nil, fmt.Errorf("blockHeaders[%d] decodes to header number %v", number, header.Number) + } + reader.headers[number] = &header + } + return reader, nil +} + +func (reader *t8nHeaderReader) GetHeader(hash common.Hash, number uint64) *types.Header { + header, ok := reader.headers[number] + if !ok { + reader.setErr(fmt.Errorf("missing blockHeaders[%d] for hash %s", number, hash)) + return nil + } + if got := header.Hash(); got != hash { + reader.setErr(fmt.Errorf("blockHeaders[%d] hash mismatch: have %s, want %s", number, got, hash)) + return nil + } + return header +} + +func (reader *t8nHeaderReader) setErr(err error) { + if reader.err == nil { + reader.err = err + } +} + +func (reader *t8nHeaderReader) Err() error { + if reader == nil { + return nil + } + return reader.err +} + +func (pre *Prestate) witnessContextHeader() (*types.Header, error) { + if pre.Env.Number == 0 { + return nil, errors.New("cannot build execution witness for block 0") + } + if pre.Env.BlockHashes == nil { + return nil, fmt.Errorf("missing blockHashes[%d] for execution witness parent", pre.Env.Number-1) + } + parentNumber := math.HexOrDecimal64(pre.Env.Number - 1) + parentHash, ok := pre.Env.BlockHashes[parentNumber] + if !ok { + return nil, fmt.Errorf("missing blockHashes[%d] for execution witness parent", pre.Env.Number-1) + } + return &types.Header{ + ParentHash: parentHash, + Number: new(big.Int).SetUint64(pre.Env.Number), + Time: pre.Env.Timestamp, + }, nil +} + +func convertExecutionWitness(witness *stateless.Witness) (*executionWitness, error) { + ext := witness.ToExtWitness() + sort.Slice(ext.State, func(i, j int) bool { + return bytes.Compare(ext.State[i], ext.State[j]) < 0 + }) + sort.Slice(ext.Codes, func(i, j int) bool { + return bytes.Compare(ext.Codes[i], ext.Codes[j]) < 0 + }) + + result := &executionWitness{ + State: make([]hexutil.Bytes, len(ext.State)), + Codes: make([]hexutil.Bytes, len(ext.Codes)), + Headers: make([]hexutil.Bytes, len(ext.Headers)), + } + for i, state := range ext.State { + result.State[i] = state + } + for i, code := range ext.Codes { + result.Codes[i] = code + } + for i, header := range ext.Headers { + if header == nil { + return nil, errors.New("execution witness contains a nil header") + } + encoded, err := rlp.EncodeToBytes(header) + if err != nil { + return nil, err + } + result.Headers[i] = encoded + } + return result, nil +} + // Apply applies a set of transactions to a pre-state func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, txIt txIterator, miningReward int64) (*state.StateDB, *ExecutionResult, []byte, error) { // Capture errors for BLOCKHASH operation, if we haven't been supplied the @@ -250,6 +363,29 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, misc.ApplyEIP7997(statedb) } evm := vm.NewEVM(vmContext, statedb, chainConfig, vmConfig) + var ( + witness *stateless.Witness + headerReader *t8nHeaderReader + ) + if isAmsterdam { + var err error + headerReader, err = newT8nHeaderReader(pre.Env.BlockHeaders) + if err != nil { + return nil, nil, nil, NewError(ErrorMissingBlockhash, err) + } + contextHeader, err := pre.witnessContextHeader() + if err != nil { + return nil, nil, nil, NewError(ErrorMissingBlockhash, err) + } + witness, err = stateless.NewWitness(contextHeader, headerReader, false) + if err != nil { + if headerErr := headerReader.Err(); headerErr != nil { + err = headerErr + } + return nil, nil, nil, NewError(ErrorMissingBlockhash, err) + } + statedb.StartPrefetcher("t8n", witness) + } if beaconRoot := pre.Env.ParentBeaconBlockRoot; beaconRoot != nil { core.ProcessBeaconBlockRoot(*beaconRoot, evm, blockAccessList) } @@ -311,13 +447,14 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, if hashError != nil { return nil, nil, nil, NewError(ErrorMissingBlockhash, hashError) } + if err := headerReader.Err(); err != nil { + return nil, nil, nil, NewError(ErrorMissingBlockhash, err) + } blobGasUsed += txBlobGas receipts = append(receipts, receipt) blockAccessList.Merge(bal) } - statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber)) - // TODO(rjl493456442) call engine.Finalize() instead // Add mining reward? (-1 means rewards are disabled) if miningReward >= 0 { // Add mining reward. The mining reward may be `0`, which only makes a difference in the cases @@ -374,6 +511,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("failed to process post-execution: %v", err)) } blockAccessList.Merge(bal) + if err := headerReader.Err(); err != nil { + return nil, nil, nil, NewError(ErrorMissingBlockhash, err) + } // Commit block root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber), chainConfig.IsCancun(vmContext.BlockNumber, vmContext.Time)) @@ -415,6 +555,12 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, balHash := encoded.Hash() execRs.BlockAccessListHash = &balHash execRs.BlockAccessList = balRLP + + executionWitness, err := convertExecutionWitness(witness) + if err != nil { + return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not encode execution witness: %v", err)) + } + execRs.ExecutionWitness = executionWitness } // Re-create statedb instance with new root for MPT mode diff --git a/cmd/evm/internal/t8ntool/execution_test.go b/cmd/evm/internal/t8ntool/execution_test.go new file mode 100644 index 0000000000..e9e2af330e --- /dev/null +++ b/cmd/evm/internal/t8ntool/execution_test.go @@ -0,0 +1,119 @@ +// Copyright 2026 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . + +package t8ntool + +import ( + "bytes" + "math/big" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + gethmath "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/core/stateless" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" +) + +func mustEncodeHeader(t *testing.T, header *types.Header) []byte { + t.Helper() + + encoded, err := rlp.EncodeToBytes(header) + if err != nil { + t.Fatal(err) + } + return encoded +} + +func TestConvertExecutionWitnessOrdersForEEST(t *testing.T) { + parent := &types.Header{Number: big.NewInt(2)} + grandparent := &types.Header{Number: big.NewInt(1)} + witness := &stateless.Witness{ + Headers: []*types.Header{parent, grandparent}, + Codes: map[string]struct{}{ + string([]byte{0x02}): {}, + string([]byte{0x01}): {}, + }, + State: map[string]struct{}{ + string([]byte{0x20}): {}, + string([]byte{0x10}): {}, + }, + } + + got, err := convertExecutionWitness(witness) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(got.State[0], []byte{0x10}) || !bytes.Equal(got.State[1], []byte{0x20}) { + t.Fatalf("state not sorted lexicographically: %#x", got.State) + } + if !bytes.Equal(got.Codes[0], []byte{0x01}) || !bytes.Equal(got.Codes[1], []byte{0x02}) { + t.Fatalf("codes not sorted lexicographically: %#x", got.Codes) + } + wantHeaders := [][]byte{ + mustEncodeHeader(t, grandparent), + mustEncodeHeader(t, parent), + } + for i, want := range wantHeaders { + if !bytes.Equal(got.Headers[i], want) { + t.Fatalf("header %d mismatch: have %#x want %#x", i, got.Headers[i], want) + } + } +} + +func TestT8NHeaderReaderValidatesHash(t *testing.T) { + header := &types.Header{Number: big.NewInt(7)} + reader, err := newT8nHeaderReader(map[gethmath.HexOrDecimal64]hexutil.Bytes{ + 7: mustEncodeHeader(t, header), + }) + if err != nil { + t.Fatal(err) + } + if got := reader.GetHeader(header.Hash(), 7); got == nil || got.Hash() != header.Hash() { + t.Fatalf("failed to retrieve known header: %v", got) + } + if got := reader.GetHeader(common.Hash{0x01}, 7); got != nil { + t.Fatalf("retrieved header for mismatched hash: %v", got) + } + if err := reader.Err(); err == nil || !strings.Contains(err.Error(), "hash mismatch") { + t.Fatalf("unexpected reader error: %v", err) + } +} + +func TestT8NHeaderReaderReportsMissingAncestor(t *testing.T) { + reader, err := newT8nHeaderReader(nil) + if err != nil { + t.Fatal(err) + } + if got := reader.GetHeader(common.Hash{}, 1); got != nil { + t.Fatalf("retrieved missing header: %v", got) + } + if err := reader.Err(); err == nil || !strings.Contains(err.Error(), "missing blockHeaders[1]") { + t.Fatalf("unexpected reader error: %v", err) + } +} + +func TestT8NHeaderReaderRejectsNumberMismatch(t *testing.T) { + header := &types.Header{Number: big.NewInt(2)} + _, err := newT8nHeaderReader(map[gethmath.HexOrDecimal64]hexutil.Bytes{ + 1: mustEncodeHeader(t, header), + }) + if err == nil || !strings.Contains(err.Error(), "decodes to header number") { + t.Fatalf("unexpected error: %v", err) + } +} diff --git a/cmd/evm/internal/t8ntool/gen_execresult.go b/cmd/evm/internal/t8ntool/gen_execresult.go index 2fc6bd134f..6d6aefc63c 100644 --- a/cmd/evm/internal/t8ntool/gen_execresult.go +++ b/cmd/evm/internal/t8ntool/gen_execresult.go @@ -34,6 +34,7 @@ func (e ExecutionResult) MarshalJSON() ([]byte, error) { Requests []hexutil.Bytes `json:"requests"` BlockAccessList hexutil.Bytes `json:"blockAccessList,omitempty"` BlockAccessListHash *common.Hash `json:"blockAccessListHash,omitempty"` + ExecutionWitness *executionWitness `json:"executionWitness,omitempty"` } var enc ExecutionResult enc.StateRoot = e.StateRoot @@ -58,6 +59,7 @@ func (e ExecutionResult) MarshalJSON() ([]byte, error) { } enc.BlockAccessList = e.BlockAccessList enc.BlockAccessListHash = e.BlockAccessListHash + enc.ExecutionWitness = e.ExecutionWitness return json.Marshal(&enc) } @@ -81,6 +83,7 @@ func (e *ExecutionResult) UnmarshalJSON(input []byte) error { Requests []hexutil.Bytes `json:"requests"` BlockAccessList *hexutil.Bytes `json:"blockAccessList,omitempty"` BlockAccessListHash *common.Hash `json:"blockAccessListHash,omitempty"` + ExecutionWitness *executionWitness `json:"executionWitness,omitempty"` } var dec ExecutionResult if err := json.Unmarshal(input, &dec); err != nil { @@ -142,5 +145,8 @@ func (e *ExecutionResult) UnmarshalJSON(input []byte) error { if dec.BlockAccessListHash != nil { e.BlockAccessListHash = dec.BlockAccessListHash } + if dec.ExecutionWitness != nil { + e.ExecutionWitness = dec.ExecutionWitness + } return nil } diff --git a/cmd/evm/internal/t8ntool/gen_stenv.go b/cmd/evm/internal/t8ntool/gen_stenv.go index 1b8e14abc6..99fa33df95 100644 --- a/cmd/evm/internal/t8ntool/gen_stenv.go +++ b/cmd/evm/internal/t8ntool/gen_stenv.go @@ -8,6 +8,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/types" ) @@ -17,27 +18,28 @@ var _ = (*stEnvMarshaling)(nil) // MarshalJSON marshals as JSON. func (s stEnv) MarshalJSON() ([]byte, error) { type stEnv struct { - Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` - Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"` - Random *math.HexOrDecimal256 `json:"currentRandom"` - ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"` - ParentBaseFee *math.HexOrDecimal256 `json:"parentBaseFee,omitempty"` - ParentGasUsed math.HexOrDecimal64 `json:"parentGasUsed,omitempty"` - ParentGasLimit math.HexOrDecimal64 `json:"parentGasLimit,omitempty"` - GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` - Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` - Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` - ParentTimestamp math.HexOrDecimal64 `json:"parentTimestamp,omitempty"` - BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` - Ommers []ommer `json:"ommers,omitempty"` - Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` - BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"` - ParentUncleHash common.Hash `json:"parentUncleHash"` - ExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"` - ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"` - ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"` - ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"` - SlotNumber *math.HexOrDecimal64 `json:"slotNumber"` + Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` + Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"` + Random *math.HexOrDecimal256 `json:"currentRandom"` + ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"` + ParentBaseFee *math.HexOrDecimal256 `json:"parentBaseFee,omitempty"` + ParentGasUsed math.HexOrDecimal64 `json:"parentGasUsed,omitempty"` + ParentGasLimit math.HexOrDecimal64 `json:"parentGasLimit,omitempty"` + GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` + Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` + Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` + ParentTimestamp math.HexOrDecimal64 `json:"parentTimestamp,omitempty"` + BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` + Ommers []ommer `json:"ommers,omitempty"` + Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` + BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"` + ParentUncleHash common.Hash `json:"parentUncleHash"` + ExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"` + ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"` + ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"` + ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"` + SlotNumber *math.HexOrDecimal64 `json:"slotNumber"` + BlockHeaders map[math.HexOrDecimal64]hexutil.Bytes `json:"blockHeaders,omitempty"` } var enc stEnv enc.Coinbase = common.UnprefixedAddress(s.Coinbase) @@ -61,33 +63,35 @@ func (s stEnv) MarshalJSON() ([]byte, error) { enc.ParentBlobGasUsed = (*math.HexOrDecimal64)(s.ParentBlobGasUsed) enc.ParentBeaconBlockRoot = s.ParentBeaconBlockRoot enc.SlotNumber = (*math.HexOrDecimal64)(s.SlotNumber) + enc.BlockHeaders = s.BlockHeaders return json.Marshal(&enc) } // UnmarshalJSON unmarshals from JSON. func (s *stEnv) UnmarshalJSON(input []byte) error { type stEnv struct { - Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` - Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"` - Random *math.HexOrDecimal256 `json:"currentRandom"` - ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"` - ParentBaseFee *math.HexOrDecimal256 `json:"parentBaseFee,omitempty"` - ParentGasUsed *math.HexOrDecimal64 `json:"parentGasUsed,omitempty"` - ParentGasLimit *math.HexOrDecimal64 `json:"parentGasLimit,omitempty"` - GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` - Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` - Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` - ParentTimestamp *math.HexOrDecimal64 `json:"parentTimestamp,omitempty"` - BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` - Ommers []ommer `json:"ommers,omitempty"` - Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` - BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"` - ParentUncleHash *common.Hash `json:"parentUncleHash"` - ExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"` - ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"` - ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"` - ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"` - SlotNumber *math.HexOrDecimal64 `json:"slotNumber"` + Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` + Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"` + Random *math.HexOrDecimal256 `json:"currentRandom"` + ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"` + ParentBaseFee *math.HexOrDecimal256 `json:"parentBaseFee,omitempty"` + ParentGasUsed *math.HexOrDecimal64 `json:"parentGasUsed,omitempty"` + ParentGasLimit *math.HexOrDecimal64 `json:"parentGasLimit,omitempty"` + GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` + Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` + Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` + ParentTimestamp *math.HexOrDecimal64 `json:"parentTimestamp,omitempty"` + BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` + Ommers []ommer `json:"ommers,omitempty"` + Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` + BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"` + ParentUncleHash *common.Hash `json:"parentUncleHash"` + ExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"` + ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"` + ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"` + ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"` + SlotNumber *math.HexOrDecimal64 `json:"slotNumber"` + BlockHeaders map[math.HexOrDecimal64]hexutil.Bytes `json:"blockHeaders,omitempty"` } var dec stEnv if err := json.Unmarshal(input, &dec); err != nil { @@ -160,5 +164,8 @@ func (s *stEnv) UnmarshalJSON(input []byte) error { if dec.SlotNumber != nil { s.SlotNumber = (*uint64)(dec.SlotNumber) } + if dec.BlockHeaders != nil { + s.BlockHeaders = dec.BlockHeaders + } return nil }