cmd/evm/internal/t8ntool: add execution witness as t8n output

Signed-off-by: jsign <jsign.uy@gmail.com>
This commit is contained in:
jsign 2026-06-22 17:05:54 -03:00
parent 9700b5b10e
commit 8b93e43ecc
No known key found for this signature in database
4 changed files with 346 additions and 68 deletions

View file

@ -17,12 +17,15 @@
package t8ntool package t8ntool
import ( import (
"bytes"
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
stdmath "math" stdmath "math"
"math/big" "math/big"
"os" "os"
"sort"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
@ -33,6 +36,7 @@ import (
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state" "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/tracing"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/types/bal" "github.com/ethereum/go-ethereum/core/types/bal"
@ -77,12 +81,20 @@ type ExecutionResult struct {
RequestsHash *common.Hash `json:"requestsHash,omitempty"` RequestsHash *common.Hash `json:"requestsHash,omitempty"`
Requests [][]byte `json:"requests"` Requests [][]byte `json:"requests"`
BlockAccessList hexutil.Bytes `json:"blockAccessList,omitempty"` BlockAccessList hexutil.Bytes `json:"blockAccessList,omitempty"`
BlockAccessListHash *common.Hash `json:"blockAccessListHash,omitempty"` BlockAccessListHash *common.Hash `json:"blockAccessListHash,omitempty"`
ExecutionWitness *executionWitness `json:"executionWitness,omitempty"`
} }
type executionResultMarshaling struct { 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 { 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 //go:generate go run github.com/fjl/gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go
type stEnv struct { type stEnv struct {
Coinbase common.Address `json:"currentCoinbase" gencodec:"required"` Coinbase common.Address `json:"currentCoinbase" gencodec:"required"`
Difficulty *big.Int `json:"currentDifficulty"` Difficulty *big.Int `json:"currentDifficulty"`
Random *big.Int `json:"currentRandom"` Random *big.Int `json:"currentRandom"`
ParentDifficulty *big.Int `json:"parentDifficulty"` ParentDifficulty *big.Int `json:"parentDifficulty"`
ParentBaseFee *big.Int `json:"parentBaseFee,omitempty"` ParentBaseFee *big.Int `json:"parentBaseFee,omitempty"`
ParentGasUsed uint64 `json:"parentGasUsed,omitempty"` ParentGasUsed uint64 `json:"parentGasUsed,omitempty"`
ParentGasLimit uint64 `json:"parentGasLimit,omitempty"` ParentGasLimit uint64 `json:"parentGasLimit,omitempty"`
GasLimit uint64 `json:"currentGasLimit" gencodec:"required"` GasLimit uint64 `json:"currentGasLimit" gencodec:"required"`
Number uint64 `json:"currentNumber" gencodec:"required"` Number uint64 `json:"currentNumber" gencodec:"required"`
Timestamp uint64 `json:"currentTimestamp" gencodec:"required"` Timestamp uint64 `json:"currentTimestamp" gencodec:"required"`
ParentTimestamp uint64 `json:"parentTimestamp,omitempty"` ParentTimestamp uint64 `json:"parentTimestamp,omitempty"`
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
Ommers []ommer `json:"ommers,omitempty"` Ommers []ommer `json:"ommers,omitempty"`
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
BaseFee *big.Int `json:"currentBaseFee,omitempty"` BaseFee *big.Int `json:"currentBaseFee,omitempty"`
ParentUncleHash common.Hash `json:"parentUncleHash"` ParentUncleHash common.Hash `json:"parentUncleHash"`
ExcessBlobGas *uint64 `json:"currentExcessBlobGas,omitempty"` ExcessBlobGas *uint64 `json:"currentExcessBlobGas,omitempty"`
ParentExcessBlobGas *uint64 `json:"parentExcessBlobGas,omitempty"` ParentExcessBlobGas *uint64 `json:"parentExcessBlobGas,omitempty"`
ParentBlobGasUsed *uint64 `json:"parentBlobGasUsed,omitempty"` ParentBlobGasUsed *uint64 `json:"parentBlobGasUsed,omitempty"`
ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"` ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"`
SlotNumber *uint64 `json:"slotNumber"` SlotNumber *uint64 `json:"slotNumber"`
BlockHeaders map[math.HexOrDecimal64]hexutil.Bytes `json:"blockHeaders,omitempty"`
} }
type stEnvMarshaling struct { type stEnvMarshaling struct {
@ -132,6 +145,7 @@ type stEnvMarshaling struct {
ParentExcessBlobGas *math.HexOrDecimal64 ParentExcessBlobGas *math.HexOrDecimal64
ParentBlobGasUsed *math.HexOrDecimal64 ParentBlobGasUsed *math.HexOrDecimal64
SlotNumber *math.HexOrDecimal64 SlotNumber *math.HexOrDecimal64
BlockHeaders map[math.HexOrDecimal64]hexutil.Bytes
} }
type rejectedTx struct { type rejectedTx struct {
@ -139,6 +153,105 @@ type rejectedTx struct {
Err string `json:"error"` 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 // 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) { 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 // Capture errors for BLOCKHASH operation, if we haven't been supplied the
@ -243,6 +356,29 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
misc.ApplyDAOHardFork(statedb) misc.ApplyDAOHardFork(statedb)
} }
evm := vm.NewEVM(vmContext, statedb, chainConfig, vmConfig) 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 { if beaconRoot := pre.Env.ParentBeaconBlockRoot; beaconRoot != nil {
core.ProcessBeaconBlockRoot(*beaconRoot, evm, blockAccessList) core.ProcessBeaconBlockRoot(*beaconRoot, evm, blockAccessList)
} }
@ -304,13 +440,14 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
if hashError != nil { if hashError != nil {
return nil, nil, nil, NewError(ErrorMissingBlockhash, hashError) return nil, nil, nil, NewError(ErrorMissingBlockhash, hashError)
} }
if err := headerReader.Err(); err != nil {
return nil, nil, nil, NewError(ErrorMissingBlockhash, err)
}
blobGasUsed += txBlobGas blobGasUsed += txBlobGas
receipts = append(receipts, receipt) receipts = append(receipts, receipt)
blockAccessList.Merge(bal) blockAccessList.Merge(bal)
} }
statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber))
// TODO(rjl493456442) call engine.Finalize() instead
// Add mining reward? (-1 means rewards are disabled) // Add mining reward? (-1 means rewards are disabled)
if miningReward >= 0 { if miningReward >= 0 {
// Add mining reward. The mining reward may be `0`, which only makes a difference in the cases // Add mining reward. The mining reward may be `0`, which only makes a difference in the cases
@ -367,6 +504,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)) return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("failed to process post-execution: %v", err))
} }
blockAccessList.Merge(bal) blockAccessList.Merge(bal)
if err := headerReader.Err(); err != nil {
return nil, nil, nil, NewError(ErrorMissingBlockhash, err)
}
// Commit block // Commit block
root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber), chainConfig.IsCancun(vmContext.BlockNumber, vmContext.Time)) root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber), chainConfig.IsCancun(vmContext.BlockNumber, vmContext.Time))
@ -408,6 +548,12 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
balHash := encoded.Hash() balHash := encoded.Hash()
execRs.BlockAccessListHash = &balHash execRs.BlockAccessListHash = &balHash
execRs.BlockAccessList = balRLP 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 // Re-create statedb instance with new root for MPT mode

View file

@ -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 <http://www.gnu.org/licenses/>.
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)
}
}

View file

@ -34,6 +34,7 @@ func (e ExecutionResult) MarshalJSON() ([]byte, error) {
Requests []hexutil.Bytes `json:"requests"` Requests []hexutil.Bytes `json:"requests"`
BlockAccessList hexutil.Bytes `json:"blockAccessList,omitempty"` BlockAccessList hexutil.Bytes `json:"blockAccessList,omitempty"`
BlockAccessListHash *common.Hash `json:"blockAccessListHash,omitempty"` BlockAccessListHash *common.Hash `json:"blockAccessListHash,omitempty"`
ExecutionWitness *executionWitness `json:"executionWitness,omitempty"`
} }
var enc ExecutionResult var enc ExecutionResult
enc.StateRoot = e.StateRoot enc.StateRoot = e.StateRoot
@ -58,6 +59,7 @@ func (e ExecutionResult) MarshalJSON() ([]byte, error) {
} }
enc.BlockAccessList = e.BlockAccessList enc.BlockAccessList = e.BlockAccessList
enc.BlockAccessListHash = e.BlockAccessListHash enc.BlockAccessListHash = e.BlockAccessListHash
enc.ExecutionWitness = e.ExecutionWitness
return json.Marshal(&enc) return json.Marshal(&enc)
} }
@ -81,6 +83,7 @@ func (e *ExecutionResult) UnmarshalJSON(input []byte) error {
Requests []hexutil.Bytes `json:"requests"` Requests []hexutil.Bytes `json:"requests"`
BlockAccessList *hexutil.Bytes `json:"blockAccessList,omitempty"` BlockAccessList *hexutil.Bytes `json:"blockAccessList,omitempty"`
BlockAccessListHash *common.Hash `json:"blockAccessListHash,omitempty"` BlockAccessListHash *common.Hash `json:"blockAccessListHash,omitempty"`
ExecutionWitness *executionWitness `json:"executionWitness,omitempty"`
} }
var dec ExecutionResult var dec ExecutionResult
if err := json.Unmarshal(input, &dec); err != nil { if err := json.Unmarshal(input, &dec); err != nil {
@ -142,5 +145,8 @@ func (e *ExecutionResult) UnmarshalJSON(input []byte) error {
if dec.BlockAccessListHash != nil { if dec.BlockAccessListHash != nil {
e.BlockAccessListHash = dec.BlockAccessListHash e.BlockAccessListHash = dec.BlockAccessListHash
} }
if dec.ExecutionWitness != nil {
e.ExecutionWitness = dec.ExecutionWitness
}
return nil return nil
} }

View file

@ -8,6 +8,7 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "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/common/math"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
) )
@ -17,27 +18,28 @@ var _ = (*stEnvMarshaling)(nil)
// MarshalJSON marshals as JSON. // MarshalJSON marshals as JSON.
func (s stEnv) MarshalJSON() ([]byte, error) { func (s stEnv) MarshalJSON() ([]byte, error) {
type stEnv struct { type stEnv struct {
Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"` Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"`
Random *math.HexOrDecimal256 `json:"currentRandom"` Random *math.HexOrDecimal256 `json:"currentRandom"`
ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"` ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"`
ParentBaseFee *math.HexOrDecimal256 `json:"parentBaseFee,omitempty"` ParentBaseFee *math.HexOrDecimal256 `json:"parentBaseFee,omitempty"`
ParentGasUsed math.HexOrDecimal64 `json:"parentGasUsed,omitempty"` ParentGasUsed math.HexOrDecimal64 `json:"parentGasUsed,omitempty"`
ParentGasLimit math.HexOrDecimal64 `json:"parentGasLimit,omitempty"` ParentGasLimit math.HexOrDecimal64 `json:"parentGasLimit,omitempty"`
GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
ParentTimestamp math.HexOrDecimal64 `json:"parentTimestamp,omitempty"` ParentTimestamp math.HexOrDecimal64 `json:"parentTimestamp,omitempty"`
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
Ommers []ommer `json:"ommers,omitempty"` Ommers []ommer `json:"ommers,omitempty"`
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"` BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
ParentUncleHash common.Hash `json:"parentUncleHash"` ParentUncleHash common.Hash `json:"parentUncleHash"`
ExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"` ExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"`
ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"` ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"`
ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"` ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"`
ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"` ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"`
SlotNumber *math.HexOrDecimal64 `json:"slotNumber"` SlotNumber *math.HexOrDecimal64 `json:"slotNumber"`
BlockHeaders map[math.HexOrDecimal64]hexutil.Bytes `json:"blockHeaders,omitempty"`
} }
var enc stEnv var enc stEnv
enc.Coinbase = common.UnprefixedAddress(s.Coinbase) enc.Coinbase = common.UnprefixedAddress(s.Coinbase)
@ -61,33 +63,35 @@ func (s stEnv) MarshalJSON() ([]byte, error) {
enc.ParentBlobGasUsed = (*math.HexOrDecimal64)(s.ParentBlobGasUsed) enc.ParentBlobGasUsed = (*math.HexOrDecimal64)(s.ParentBlobGasUsed)
enc.ParentBeaconBlockRoot = s.ParentBeaconBlockRoot enc.ParentBeaconBlockRoot = s.ParentBeaconBlockRoot
enc.SlotNumber = (*math.HexOrDecimal64)(s.SlotNumber) enc.SlotNumber = (*math.HexOrDecimal64)(s.SlotNumber)
enc.BlockHeaders = s.BlockHeaders
return json.Marshal(&enc) return json.Marshal(&enc)
} }
// UnmarshalJSON unmarshals from JSON. // UnmarshalJSON unmarshals from JSON.
func (s *stEnv) UnmarshalJSON(input []byte) error { func (s *stEnv) UnmarshalJSON(input []byte) error {
type stEnv struct { type stEnv struct {
Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"` Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"` Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"`
Random *math.HexOrDecimal256 `json:"currentRandom"` Random *math.HexOrDecimal256 `json:"currentRandom"`
ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"` ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"`
ParentBaseFee *math.HexOrDecimal256 `json:"parentBaseFee,omitempty"` ParentBaseFee *math.HexOrDecimal256 `json:"parentBaseFee,omitempty"`
ParentGasUsed *math.HexOrDecimal64 `json:"parentGasUsed,omitempty"` ParentGasUsed *math.HexOrDecimal64 `json:"parentGasUsed,omitempty"`
ParentGasLimit *math.HexOrDecimal64 `json:"parentGasLimit,omitempty"` ParentGasLimit *math.HexOrDecimal64 `json:"parentGasLimit,omitempty"`
GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"` GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"` Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"` Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
ParentTimestamp *math.HexOrDecimal64 `json:"parentTimestamp,omitempty"` ParentTimestamp *math.HexOrDecimal64 `json:"parentTimestamp,omitempty"`
BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"` BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
Ommers []ommer `json:"ommers,omitempty"` Ommers []ommer `json:"ommers,omitempty"`
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"` Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"` BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
ParentUncleHash *common.Hash `json:"parentUncleHash"` ParentUncleHash *common.Hash `json:"parentUncleHash"`
ExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"` ExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"`
ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"` ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"`
ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"` ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"`
ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"` ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"`
SlotNumber *math.HexOrDecimal64 `json:"slotNumber"` SlotNumber *math.HexOrDecimal64 `json:"slotNumber"`
BlockHeaders map[math.HexOrDecimal64]hexutil.Bytes `json:"blockHeaders,omitempty"`
} }
var dec stEnv var dec stEnv
if err := json.Unmarshal(input, &dec); err != nil { if err := json.Unmarshal(input, &dec); err != nil {
@ -160,5 +164,8 @@ func (s *stEnv) UnmarshalJSON(input []byte) error {
if dec.SlotNumber != nil { if dec.SlotNumber != nil {
s.SlotNumber = (*uint64)(dec.SlotNumber) s.SlotNumber = (*uint64)(dec.SlotNumber)
} }
if dec.BlockHeaders != nil {
s.BlockHeaders = dec.BlockHeaders
}
return nil return nil
} }