mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-18 10:50:44 +00:00
cmd/evm/internal/t8ntool: add execution witness as t8n output
Signed-off-by: jsign <jsign.uy@gmail.com>
This commit is contained in:
parent
9700b5b10e
commit
8b93e43ecc
4 changed files with 346 additions and 68 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
119
cmd/evm/internal/t8ntool/execution_test.go
Normal file
119
cmd/evm/internal/t8ntool/execution_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue