mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 14:46:42 +00:00
Refactored tests and added new based on "prestate" testing
This commit is contained in:
parent
dccb2f36cd
commit
bc4162e99f
8 changed files with 845 additions and 288 deletions
139
eth/tracers/internal/tracetest/firehose/blockchain_test.go
Normal file
139
eth/tracers/internal/tracetest/firehose/blockchain_test.go
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
package firehose_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"hash"
|
||||||
|
"math/big"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
|
"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/tracing"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/ethereum/go-ethereum/tests"
|
||||||
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"golang.org/x/crypto/sha3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func runPrestateBlock(t *testing.T, prestatePath string, hooks *tracing.Hooks) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
prestate := readPrestateData(t, prestatePath)
|
||||||
|
|
||||||
|
tx := new(types.Transaction)
|
||||||
|
require.NoError(t, rlp.DecodeBytes(common.FromHex(prestate.Input), tx))
|
||||||
|
|
||||||
|
context := prestate.Context.toBlockContext(prestate.Genesis)
|
||||||
|
|
||||||
|
state := tests.MakePreState(rawdb.NewMemoryDatabase(), prestate.Genesis.Alloc, false, rawdb.HashScheme)
|
||||||
|
defer state.Close()
|
||||||
|
|
||||||
|
state.StateDB.SetLogger(hooks)
|
||||||
|
state.StateDB.SetTxContext(tx.Hash(), 0)
|
||||||
|
|
||||||
|
block := types.NewBlock(&types.Header{
|
||||||
|
ParentHash: prestate.Genesis.ToBlock().Hash(),
|
||||||
|
Number: context.BlockNumber,
|
||||||
|
Difficulty: context.Difficulty,
|
||||||
|
Coinbase: context.Coinbase,
|
||||||
|
Time: context.Time,
|
||||||
|
GasLimit: context.GasLimit,
|
||||||
|
BaseFee: context.BaseFee,
|
||||||
|
ParentBeaconRoot: ptr(common.Hash{}),
|
||||||
|
}, []*types.Transaction{tx}, nil, nil, trie.NewStackTrie(nil))
|
||||||
|
|
||||||
|
hooks.OnBlockchainInit(prestate.Genesis.Config)
|
||||||
|
hooks.OnBlockStart(tracing.BlockEvent{
|
||||||
|
Block: block,
|
||||||
|
TD: prestate.TotalDifficulty,
|
||||||
|
})
|
||||||
|
|
||||||
|
usedGas := uint64(0)
|
||||||
|
_, err := core.ApplyTransaction(
|
||||||
|
prestate.Genesis.Config,
|
||||||
|
prestate,
|
||||||
|
&context.Coinbase,
|
||||||
|
new(core.GasPool).AddGas(block.GasLimit()),
|
||||||
|
state.StateDB,
|
||||||
|
block.Header(),
|
||||||
|
tx,
|
||||||
|
&usedGas,
|
||||||
|
vm.Config{Tracer: hooks},
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
hooks.OnBlockEnd(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBlockchain(t *testing.T, alloc types.GenesisAlloc, context vm.BlockContext, tracer *tracing.Hooks) (*core.Genesis, *core.BlockChain) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
genesis := &core.Genesis{
|
||||||
|
Difficulty: new(big.Int).Sub(context.Difficulty, big.NewInt(1)),
|
||||||
|
Timestamp: context.Time - 1,
|
||||||
|
Number: new(big.Int).Sub(context.BlockNumber, big.NewInt(1)).Uint64(),
|
||||||
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
|
Coinbase: context.Coinbase,
|
||||||
|
Config: params.AllEthashProtocolChanges,
|
||||||
|
Alloc: alloc,
|
||||||
|
}
|
||||||
|
|
||||||
|
log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, false)))
|
||||||
|
defer log.SetDefault(log.NewLogger(log.DiscardHandler()))
|
||||||
|
|
||||||
|
blockchain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), core.DefaultCacheConfigWithScheme(rawdb.HashScheme), genesis, nil, ethash.NewFullFaker(), vm.Config{
|
||||||
|
Tracer: tracer,
|
||||||
|
}, nil, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return genesis, blockchain
|
||||||
|
}
|
||||||
|
|
||||||
|
// testHasher is the helper tool for transaction/receipt list hashing.
|
||||||
|
// The original hasher is trie, in order to get rid of import cycle,
|
||||||
|
// use the testing hasher instead.
|
||||||
|
type testHasher struct {
|
||||||
|
hasher hash.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHasher returns a new testHasher instance.
|
||||||
|
func NewHasher() *testHasher {
|
||||||
|
return &testHasher{hasher: sha3.NewLegacyKeccak256()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset resets the hash state.
|
||||||
|
func (h *testHasher) Reset() {
|
||||||
|
h.hasher.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update updates the hash state with the given key and value.
|
||||||
|
func (h *testHasher) Update(key, val []byte) error {
|
||||||
|
h.hasher.Write(key)
|
||||||
|
h.hasher.Write(val)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash returns the hash value.
|
||||||
|
func (h *testHasher) Hash() common.Hash {
|
||||||
|
return common.BytesToHash(h.hasher.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
type ignoreValidateStateValidator struct {
|
||||||
|
core.Validator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v ignoreValidateStateValidator) ValidateBody(block *types.Block) error {
|
||||||
|
return v.Validator.ValidateBody(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v ignoreValidateStateValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
98
eth/tracers/internal/tracetest/firehose/firehose_test.go
Normal file
98
eth/tracers/internal/tracetest/firehose/firehose_test.go
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
package firehose_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"math/big"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||||
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFirehoseChain(t *testing.T) {
|
||||||
|
context := vm.BlockContext{
|
||||||
|
CanTransfer: core.CanTransfer,
|
||||||
|
Transfer: core.Transfer,
|
||||||
|
Coinbase: common.Address{},
|
||||||
|
BlockNumber: new(big.Int).SetUint64(uint64(1)),
|
||||||
|
Time: 1,
|
||||||
|
Difficulty: big.NewInt(2),
|
||||||
|
GasLimit: uint64(1000000),
|
||||||
|
BaseFee: big.NewInt(8),
|
||||||
|
}
|
||||||
|
|
||||||
|
tracer, err := tracers.NewFirehoseFromRawJSON(json.RawMessage(`{
|
||||||
|
"applyBackwardsCompatibility": true,
|
||||||
|
"_private": {
|
||||||
|
"flushToTestBuffer": true,
|
||||||
|
"ignoreGenesisBlock": true
|
||||||
|
}
|
||||||
|
}`))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
hooks := tracers.NewTracingHooksFromFirehose(tracer)
|
||||||
|
|
||||||
|
genesis, blockchain := newBlockchain(t, types.GenesisAlloc{}, context, hooks)
|
||||||
|
|
||||||
|
block := types.NewBlock(&types.Header{
|
||||||
|
ParentHash: genesis.ToBlock().Hash(),
|
||||||
|
Number: context.BlockNumber,
|
||||||
|
Difficulty: context.Difficulty,
|
||||||
|
Coinbase: context.Coinbase,
|
||||||
|
Time: context.Time,
|
||||||
|
GasLimit: context.GasLimit,
|
||||||
|
BaseFee: context.BaseFee,
|
||||||
|
ParentBeaconRoot: ptr(common.Hash{}),
|
||||||
|
}, nil, nil, nil, trie.NewStackTrie(nil))
|
||||||
|
|
||||||
|
blockchain.SetBlockValidatorAndProcessorForTesting(
|
||||||
|
ignoreValidateStateValidator{core.NewBlockValidator(genesis.Config, blockchain, blockchain.Engine())},
|
||||||
|
core.NewStateProcessor(genesis.Config, blockchain, blockchain.Engine()),
|
||||||
|
)
|
||||||
|
|
||||||
|
n, err := blockchain.InsertChain(types.Blocks{block})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, n)
|
||||||
|
|
||||||
|
genesisLine, blockLines, unknownLines := readTracerFirehoseLines(t, tracer)
|
||||||
|
require.Len(t, unknownLines, 0, "Lines:\n%s", strings.Join(slicesMap(unknownLines, func(l unknwonLine) string { return "- '" + string(l) + "'" }), "\n"))
|
||||||
|
require.NotNil(t, genesisLine)
|
||||||
|
blockLines.assertEquals(t, filepath.Join("testdata", t.Name()),
|
||||||
|
firehoseBlockLineParams{"1", "8e6ee4b1054d94df1d8a51fb983447dc2e27a854590c3ac0061f994284be8150", "0", "845bad515694a416bab4b8d44e22cf97a8c894a8502110ab807883940e185ce0", "0", "1000000000"},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFirehosePrestate(t *testing.T) {
|
||||||
|
testFolders := []string{
|
||||||
|
"./testdata/TestFirehosePrestate/keccak256_too_few_memory_bytes_get_padded",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, folder := range testFolders {
|
||||||
|
name := filepath.Base(folder)
|
||||||
|
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
tracer, err := tracers.NewFirehoseFromRawJSON(json.RawMessage(`{
|
||||||
|
"applyBackwardsCompatibility": true,
|
||||||
|
"_private": {
|
||||||
|
"flushToTestBuffer": true
|
||||||
|
}
|
||||||
|
}`))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
runPrestateBlock(t, filepath.Join(folder, "prestate.json"), tracers.NewTracingHooksFromFirehose(tracer))
|
||||||
|
|
||||||
|
genesisLine, blockLines, unknownLines := readTracerFirehoseLines(t, tracer)
|
||||||
|
require.Len(t, unknownLines, 0, "Lines:\n%s", strings.Join(slicesMap(unknownLines, func(l unknwonLine) string { return "- '" + string(l) + "'" }), "\n"))
|
||||||
|
require.NotNil(t, genesisLine)
|
||||||
|
blockLines.assertOnlyBlockEquals(t, folder, 1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
159
eth/tracers/internal/tracetest/firehose/helpers_test.go
Normal file
159
eth/tracers/internal/tracetest/firehose/helpers_test.go
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
package firehose_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||||
|
pbeth "github.com/ethereum/go-ethereum/pb/sf/ethereum/type/v2"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"google.golang.org/protobuf/encoding/protojson"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
type firehoseInitLine struct {
|
||||||
|
ProtocolVersion string
|
||||||
|
NodeName string
|
||||||
|
NodeVersion string
|
||||||
|
}
|
||||||
|
|
||||||
|
type firehoseBlockLines []firehoseBlockLine
|
||||||
|
|
||||||
|
func (lines firehoseBlockLines) assertEquals(t *testing.T, goldenDir string, expected ...firehoseBlockLineParams) {
|
||||||
|
actualParams := slicesMap(lines, func(l firehoseBlockLine) firehoseBlockLineParams { return l.Params })
|
||||||
|
require.Equal(t, expected, actualParams, "Actual lines block params do not match expected lines block params")
|
||||||
|
|
||||||
|
lines.assertOnlyBlockEquals(t, goldenDir, len(expected))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lines firehoseBlockLines) assertOnlyBlockEquals(t *testing.T, goldenDir string, expectedBlockCount int) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
require.Len(t, lines, expectedBlockCount, "Expected %d blocks, got %d", expectedBlockCount, len(lines))
|
||||||
|
goldenUpdate := os.Getenv("GOLDEN_UPDATE") == "true"
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
goldenPath := filepath.Join(goldenDir, fmt.Sprintf("block.%d.golden.json", line.Block.Header.Number))
|
||||||
|
if !goldenUpdate && !fileExists(t, goldenPath) {
|
||||||
|
t.Fatalf("the golden file %q does not exist, re-run with 'GOLDEN_UPDATE=true go test ./... -run %q' to generate the intial version", goldenPath, t.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err := protojson.MarshalOptions{Indent: " "}.Marshal(line.Block)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
if goldenUpdate {
|
||||||
|
require.NoError(t, os.MkdirAll(filepath.Dir(goldenPath), 0755))
|
||||||
|
require.NoError(t, os.WriteFile(goldenPath, content, 0644))
|
||||||
|
}
|
||||||
|
|
||||||
|
expected, err := os.ReadFile(goldenPath)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expectedBlock := &pbeth.Block{}
|
||||||
|
require.NoError(t, protojson.Unmarshal(expected, expectedBlock))
|
||||||
|
|
||||||
|
if !proto.Equal(expectedBlock, line.Block) {
|
||||||
|
assert.EqualExportedValues(t, expectedBlock, line.Block, "Run 'GOLDEN_UPDATE=true go test ./... -run %q' to update golden file", t.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileExists(t *testing.T, path string) bool {
|
||||||
|
t.Helper()
|
||||||
|
stat, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return !stat.IsDir()
|
||||||
|
}
|
||||||
|
|
||||||
|
func slicesMap[T any, U any](s []T, f func(T) U) []U {
|
||||||
|
result := make([]U, len(s))
|
||||||
|
for i, v := range s {
|
||||||
|
result[i] = f(v)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
type firehoseBlockLine struct {
|
||||||
|
// We split params and block to make it easier to compare stuff
|
||||||
|
Params firehoseBlockLineParams
|
||||||
|
Block *pbeth.Block
|
||||||
|
}
|
||||||
|
|
||||||
|
type firehoseBlockLineParams struct {
|
||||||
|
Number string
|
||||||
|
Hash string
|
||||||
|
PreviousNum string
|
||||||
|
PreviousHash string
|
||||||
|
LibNum string
|
||||||
|
Time string
|
||||||
|
}
|
||||||
|
|
||||||
|
type unknwonLine string
|
||||||
|
|
||||||
|
func readTracerFirehoseLines(t *testing.T, tracer *tracers.Firehose) (genesisLine *firehoseInitLine, blockLines firehoseBlockLines, unknownLines []unknwonLine) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
lines := bytes.Split(tracer.InternalTestingBuffer().Bytes(), []byte{'\n'})
|
||||||
|
for _, line := range lines {
|
||||||
|
if len(line) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := bytes.Split(line, []byte{' '})
|
||||||
|
if len(parts) == 0 || string(parts[0]) != "FIRE" {
|
||||||
|
unknownLines = append(unknownLines, unknwonLine(line))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
action := string(parts[1])
|
||||||
|
fireParts := parts[2:]
|
||||||
|
switch action {
|
||||||
|
case "INIT":
|
||||||
|
genesisLine = &firehoseInitLine{
|
||||||
|
ProtocolVersion: string(fireParts[0]),
|
||||||
|
NodeName: string(fireParts[1]),
|
||||||
|
NodeVersion: string(fireParts[2]),
|
||||||
|
}
|
||||||
|
|
||||||
|
case "BLOCK":
|
||||||
|
protoBytes, err := base64.StdEncoding.DecodeString(string(fireParts[6]))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
block := &pbeth.Block{}
|
||||||
|
require.NoError(t, proto.Unmarshal(protoBytes, block))
|
||||||
|
|
||||||
|
blockLines = append(blockLines, firehoseBlockLine{
|
||||||
|
Params: firehoseBlockLineParams{
|
||||||
|
Number: string(fireParts[0]),
|
||||||
|
Hash: string(fireParts[1]),
|
||||||
|
PreviousNum: string(fireParts[2]),
|
||||||
|
PreviousHash: string(fireParts[3]),
|
||||||
|
LibNum: string(fireParts[4]),
|
||||||
|
Time: string(fireParts[5]),
|
||||||
|
},
|
||||||
|
Block: block,
|
||||||
|
})
|
||||||
|
|
||||||
|
default:
|
||||||
|
unknownLines = append(unknownLines, unknwonLine(line))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ptr[T any](v T) *T {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
108
eth/tracers/internal/tracetest/firehose/prestate_test.go
Normal file
108
eth/tracers/internal/tracetest/firehose/prestate_test.go
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
package firehose_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"math/big"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func readPrestateData(t *testing.T, path string) *prestateData {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
// Call tracer test found, read if from disk
|
||||||
|
blob, err := os.ReadFile(path)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
test := new(prestateData)
|
||||||
|
require.NoError(t, json.Unmarshal(blob, test))
|
||||||
|
|
||||||
|
var genesisWithTD struct {
|
||||||
|
Genesis struct {
|
||||||
|
TotalDifficulty *math.HexOrDecimal256 `json:"totalDifficulty"`
|
||||||
|
} `json:"genesis"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(blob, &genesisWithTD); err == nil {
|
||||||
|
test.TotalDifficulty = (*big.Int)(genesisWithTD.Genesis.TotalDifficulty)
|
||||||
|
}
|
||||||
|
|
||||||
|
return test
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ core.ChainContext = (*prestateData)(nil)
|
||||||
|
|
||||||
|
type prestateData struct {
|
||||||
|
Genesis *core.Genesis `json:"genesis"`
|
||||||
|
Context *callContext `json:"context"`
|
||||||
|
Input string `json:"input"`
|
||||||
|
TotalDifficulty *big.Int `json:"-"`
|
||||||
|
TracerConfig json.RawMessage `json:"tracerConfig"`
|
||||||
|
|
||||||
|
// Populated after loading
|
||||||
|
genesisBlock *types.Block
|
||||||
|
}
|
||||||
|
|
||||||
|
// Engine implements core.ChainContext.
|
||||||
|
func (p *prestateData) Engine() consensus.Engine {
|
||||||
|
return ethash.NewFullFaker()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHeader implements core.ChainContext.
|
||||||
|
func (p *prestateData) GetHeader(hash common.Hash, number uint64) *types.Header {
|
||||||
|
if p.Genesis == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.genesisBlock == nil {
|
||||||
|
p.genesisBlock = p.Genesis.ToBlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
if hash == p.genesisBlock.Hash() {
|
||||||
|
return p.genesisBlock.Header()
|
||||||
|
}
|
||||||
|
|
||||||
|
if number == p.genesisBlock.NumberU64() {
|
||||||
|
return p.genesisBlock.Header()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type callContext struct {
|
||||||
|
Number math.HexOrDecimal64 `json:"number"`
|
||||||
|
Difficulty *math.HexOrDecimal256 `json:"difficulty"`
|
||||||
|
Time math.HexOrDecimal64 `json:"timestamp"`
|
||||||
|
GasLimit math.HexOrDecimal64 `json:"gasLimit"`
|
||||||
|
Miner common.Address `json:"miner"`
|
||||||
|
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *callContext) toBlockContext(genesis *core.Genesis) vm.BlockContext {
|
||||||
|
context := vm.BlockContext{
|
||||||
|
CanTransfer: core.CanTransfer,
|
||||||
|
Transfer: core.Transfer,
|
||||||
|
Coinbase: c.Miner,
|
||||||
|
BlockNumber: new(big.Int).SetUint64(uint64(c.Number)),
|
||||||
|
Time: uint64(c.Time),
|
||||||
|
Difficulty: (*big.Int)(c.Difficulty),
|
||||||
|
GasLimit: uint64(c.GasLimit),
|
||||||
|
}
|
||||||
|
if genesis.Config.IsLondon(context.BlockNumber) {
|
||||||
|
context.BaseFee = (*big.Int)(c.BaseFee)
|
||||||
|
}
|
||||||
|
if genesis.ExcessBlobGas != nil && genesis.BlobGasUsed != nil {
|
||||||
|
excessBlobGas := eip4844.CalcExcessBlobGas(*genesis.ExcessBlobGas, *genesis.BlobGasUsed)
|
||||||
|
context.BlobBaseFee = eip4844.CalcBlobFee(excessBlobGas)
|
||||||
|
}
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,167 @@
|
||||||
|
{
|
||||||
|
"hash": "SKLPDUOjSrOGLDSmQ92rvI79Tx7rbpi+MnMdw1ta0jQ=",
|
||||||
|
"number": "3727495",
|
||||||
|
"size": "723",
|
||||||
|
"header": {
|
||||||
|
"parentHash": "aYf8zdg87hj7kTMGYReWUn8vvkT0i92fQ04NAsNDcDc=",
|
||||||
|
"uncleHash": "HcxN6N7HXXqrhbVntszUGtMSRRuUinQT8KFC/UDUk0c=",
|
||||||
|
"coinbase": "AAAAaRaoe4IzP0JFBGYjsjeUxlw=",
|
||||||
|
"stateRoot": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
|
||||||
|
"transactionsRoot": "bDCtoar4SKuDMt3K7Ay0vDV8FJj/S9YTAtfuy7yJYL0=",
|
||||||
|
"receiptRoot": "VugfFxvMVab/g0XmksD4bltI4BuZbK3AAWIvteNjtCE=",
|
||||||
|
"logsBloom": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
|
||||||
|
"difficulty": {
|
||||||
|
"bytes": "AA=="
|
||||||
|
},
|
||||||
|
"totalDifficulty": {
|
||||||
|
"bytes": "PGVtIwKasA=="
|
||||||
|
},
|
||||||
|
"number": "3727495",
|
||||||
|
"gasLimit": "30000000",
|
||||||
|
"timestamp": "2023-06-19T23:23:48Z",
|
||||||
|
"mixHash": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
|
||||||
|
"hash": "SKLPDUOjSrOGLDSmQ92rvI79Tx7rbpi+MnMdw1ta0jQ=",
|
||||||
|
"baseFeePerGas": {
|
||||||
|
"bytes": "DQ=="
|
||||||
|
},
|
||||||
|
"parentBeaconRoot": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
|
||||||
|
},
|
||||||
|
"transactionTraces": [
|
||||||
|
{
|
||||||
|
"to": "3Z+V4sEFOnYIm1AXgpuyMwy2rkc=",
|
||||||
|
"nonce": "5",
|
||||||
|
"gasPrice": {
|
||||||
|
"bytes": "AoQ="
|
||||||
|
},
|
||||||
|
"gasLimit": "200000",
|
||||||
|
"input": "QMEPGQAAAAAAAAAAAAAAAOs9PWFvPkPp7TYe3S0OZE0Y0Xm3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPo=",
|
||||||
|
"v": "AVRtcg==",
|
||||||
|
"r": "Xww4NKZlcXSDEHF8P5UnXU9PLqL6aBoOU5VFipWpVaM=",
|
||||||
|
"s": "BOdJVNNAB7fPBAA48slaL2uQd1vOwb423lDacpucotM=",
|
||||||
|
"gasUsed": "69865",
|
||||||
|
"hash": "tF3PNXHZNWA+icLD8DyDTxT8x76iGVd8sDejy7wEQx8=",
|
||||||
|
"from": "6z09YW8+Q+ntNh7dLQ5kTRjRebc=",
|
||||||
|
"beginOrdinal": "1",
|
||||||
|
"endOrdinal": "13",
|
||||||
|
"status": "SUCCEEDED",
|
||||||
|
"receipt": {
|
||||||
|
"cumulativeGasUsed": "69865",
|
||||||
|
"logsBloom": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAIAACAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAIAAAAAAAAAAAAIAAAAAAAAAAAAAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAA==",
|
||||||
|
"logs": [
|
||||||
|
{
|
||||||
|
"address": "3Z+V4sEFOnYIm1AXgpuyMwy2rkc=",
|
||||||
|
"topics": [
|
||||||
|
"3fJSrRviyJtpwrBo/DeNqpUrp/FjxKEWKPVaTfUjs+8=",
|
||||||
|
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
|
||||||
|
"AAAAAAAAAAAAAAAA6z09YW8+Q+ntNh7dLQ5kTRjRebc="
|
||||||
|
],
|
||||||
|
"data": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPo=",
|
||||||
|
"ordinal": "9"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"calls": [
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"callType": "CALL",
|
||||||
|
"caller": "6z09YW8+Q+ntNh7dLQ5kTRjRebc=",
|
||||||
|
"address": "3Z+V4sEFOnYIm1AXgpuyMwy2rkc=",
|
||||||
|
"gasLimit": "178428",
|
||||||
|
"gasConsumed": "48293",
|
||||||
|
"input": "QMEPGQAAAAAAAAAAAAAAAOs9PWFvPkPp7TYe3S0OZE0Y0Xm3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPo=",
|
||||||
|
"executedCode": true,
|
||||||
|
"keccakPreimages": {
|
||||||
|
"bb65ff7a03b11b79143b011da0de7c67f6d9bb8f57df2381d88d503cdb07b0b6": "000000000000000000000000eb3d3d616f3e43e9ed361edd2d0e644d18d179b70000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
},
|
||||||
|
"storageChanges": [
|
||||||
|
{
|
||||||
|
"address": "3Z+V4sEFOnYIm1AXgpuyMwy2rkc=",
|
||||||
|
"key": "u2X/egOxG3kUOwEdoN58Z/bZu49X3yOB2I1QPNsHsLY=",
|
||||||
|
"oldValue": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
|
||||||
|
"newValue": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPo=",
|
||||||
|
"ordinal": "6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "3Z+V4sEFOnYIm1AXgpuyMwy2rkc=",
|
||||||
|
"key": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI=",
|
||||||
|
"oldValue": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
|
||||||
|
"newValue": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPo=",
|
||||||
|
"ordinal": "7"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"balanceChanges": [
|
||||||
|
{
|
||||||
|
"address": "6z09YW8+Q+ntNh7dLQ5kTRjRebc=",
|
||||||
|
"oldValue": {
|
||||||
|
"bytes": "B5sluWe3fF0="
|
||||||
|
},
|
||||||
|
"newValue": {
|
||||||
|
"bytes": "B5sluWAKJ10="
|
||||||
|
},
|
||||||
|
"reason": "REASON_GAS_BUY",
|
||||||
|
"ordinal": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "6z09YW8+Q+ntNh7dLQ5kTRjRebc=",
|
||||||
|
"oldValue": {
|
||||||
|
"bytes": "B5sluWAKJ10="
|
||||||
|
},
|
||||||
|
"newValue": {
|
||||||
|
"bytes": "B5sluWUI8jk="
|
||||||
|
},
|
||||||
|
"reason": "REASON_GAS_REFUND",
|
||||||
|
"ordinal": "11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "AAAAaRaoe4IzP0JFBGYjsjeUxlw=",
|
||||||
|
"oldValue": {
|
||||||
|
"bytes": "AecopRVH6fvBC6c="
|
||||||
|
},
|
||||||
|
"newValue": {
|
||||||
|
"bytes": "AecopRVH6f5hufY="
|
||||||
|
},
|
||||||
|
"reason": "REASON_REWARD_TRANSACTION_FEE",
|
||||||
|
"ordinal": "12"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nonceChanges": [
|
||||||
|
{
|
||||||
|
"address": "6z09YW8+Q+ntNh7dLQ5kTRjRebc=",
|
||||||
|
"oldValue": "5",
|
||||||
|
"newValue": "6",
|
||||||
|
"ordinal": "4"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"logs": [
|
||||||
|
{
|
||||||
|
"address": "3Z+V4sEFOnYIm1AXgpuyMwy2rkc=",
|
||||||
|
"topics": [
|
||||||
|
"3fJSrRviyJtpwrBo/DeNqpUrp/FjxKEWKPVaTfUjs+8=",
|
||||||
|
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
|
||||||
|
"AAAAAAAAAAAAAAAA6z09YW8+Q+ntNh7dLQ5kTRjRebc="
|
||||||
|
],
|
||||||
|
"data": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPo=",
|
||||||
|
"ordinal": "9"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"gasChanges": [
|
||||||
|
{
|
||||||
|
"oldValue": "200000",
|
||||||
|
"newValue": "178428",
|
||||||
|
"reason": "REASON_INTRINSIC_GAS",
|
||||||
|
"ordinal": "3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"oldValue": "131897",
|
||||||
|
"newValue": "130141",
|
||||||
|
"reason": "REASON_EVENT_LOG",
|
||||||
|
"ordinal": "8"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"endOrdinal": "10"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ver": 3
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,174 @@
|
||||||
|
{
|
||||||
|
"genesis": {
|
||||||
|
"baseFeePerGas": "14",
|
||||||
|
"difficulty": "0",
|
||||||
|
"extraData": "0x6769756c696f207761732068657265",
|
||||||
|
"gasLimit": "30000000",
|
||||||
|
"hash": "0xff41623ef4e84cea4e957676a0b7e5868f1480e343ad533914133c448157764c",
|
||||||
|
"miner": "0x008b3b2f992c0e14edaa6e2c662bec549caa8df1",
|
||||||
|
"mixHash": "0xf04431d1d4ccb65f5d1b2e88ec010737ae82d68426b287a7158b9b16aa45a0bc",
|
||||||
|
"nonce": "0x0000000000000000",
|
||||||
|
"number": "3727494",
|
||||||
|
"stateRoot": "0xe3f9e4ff1577f21330eded302784f548f6fed4d4ca7a1f6884dde9ad38743279",
|
||||||
|
"timestamp": "1687217016",
|
||||||
|
"totalDifficulty": "17000018015853232",
|
||||||
|
"withdrawals": [
|
||||||
|
{
|
||||||
|
"index": "0xb3bf91",
|
||||||
|
"validatorIndex": "0x38b",
|
||||||
|
"address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e",
|
||||||
|
"amount": "0x244a5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": "0xb3bf92",
|
||||||
|
"validatorIndex": "0x38c",
|
||||||
|
"address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e",
|
||||||
|
"amount": "0x2d66a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": "0xb3bf93",
|
||||||
|
"validatorIndex": "0x38d",
|
||||||
|
"address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e",
|
||||||
|
"amount": "0x2d66a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": "0xb3bf94",
|
||||||
|
"validatorIndex": "0x38e",
|
||||||
|
"address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e",
|
||||||
|
"amount": "0x2d66a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": "0xb3bf95",
|
||||||
|
"validatorIndex": "0x38f",
|
||||||
|
"address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e",
|
||||||
|
"amount": "0x2d66a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": "0xb3bf96",
|
||||||
|
"validatorIndex": "0x390",
|
||||||
|
"address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e",
|
||||||
|
"amount": "0x2d66a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": "0xb3bf97",
|
||||||
|
"validatorIndex": "0x391",
|
||||||
|
"address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e",
|
||||||
|
"amount": "0x244a5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": "0xb3bf98",
|
||||||
|
"validatorIndex": "0x392",
|
||||||
|
"address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e",
|
||||||
|
"amount": "0x2d66a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": "0xb3bf99",
|
||||||
|
"validatorIndex": "0x393",
|
||||||
|
"address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e",
|
||||||
|
"amount": "0x25b2b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": "0xb3bf9a",
|
||||||
|
"validatorIndex": "0x394",
|
||||||
|
"address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e",
|
||||||
|
"amount": "0x244a5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": "0xb3bf9b",
|
||||||
|
"validatorIndex": "0x395",
|
||||||
|
"address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e",
|
||||||
|
"amount": "0x2d66a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": "0xb3bf9c",
|
||||||
|
"validatorIndex": "0x396",
|
||||||
|
"address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e",
|
||||||
|
"amount": "0x244a5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": "0xb3bf9d",
|
||||||
|
"validatorIndex": "0x397",
|
||||||
|
"address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e",
|
||||||
|
"amount": "0x244a5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": "0xb3bf9e",
|
||||||
|
"validatorIndex": "0x398",
|
||||||
|
"address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e",
|
||||||
|
"amount": "0x2d66a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": "0xb3bf9f",
|
||||||
|
"validatorIndex": "0x399",
|
||||||
|
"address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e",
|
||||||
|
"amount": "0x2d66a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": "0xb3bfa0",
|
||||||
|
"validatorIndex": "0x39a",
|
||||||
|
"address": "0xe276bc378a527a8792b353cdca5b5e53263dfb9e",
|
||||||
|
"amount": "0x2ecf0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"withdrawalsRoot": "0xceabf2dc1561db0f29da25b6282082e5353c2f594942cfdcdccfeb176542411c",
|
||||||
|
"alloc": {
|
||||||
|
"0x0000006916a87b82333f4245046623b23794c65c": {
|
||||||
|
"balance": "0x1e728a51547e9fbc10ba7",
|
||||||
|
"nonce": "5"
|
||||||
|
},
|
||||||
|
"0xdd9f95e2c1053a76089b5017829bb2330cb6ae47": {
|
||||||
|
"balance": "0x0",
|
||||||
|
"code": "0x60003560e01c8063095ea7b31461005c57806340c10f1914610062578063a9059cbb146100d957806323b872dd146100df57806318160ddd1461015d57806370a082311461016e5763dd62ed3e1461018e573461018e5760006000f35b60006000f35b60043573ffffffffffffffffffffffffffffffffffffffff1660007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206000602435856000526040600020805482018083119155600254820180831190600255600154331415171761018e57600052a360006000f35b60006000f35b60043573ffffffffffffffffffffffffffffffffffffffff16337fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206000602435856000526040600020805482018083119155336000526040600020805483900380841091553417176101535760006000fd5b600052a360206000f35b3461018e5760025460005260206000f35b3461018e5760043560005260406000205460005260206000f35b60006000f35b60006000fd",
|
||||||
|
"nonce": "1",
|
||||||
|
"storage": {
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000000eb3d3d616f3e43e9ed361edd2d0e644d18d179b7",
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"0xbb65ff7a03b11b79143b011da0de7c67f6d9bb8f57df2381d88d503cdb07b0b6": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0xeb3d3d616f3e43e9ed361edd2d0e644d18d179b7": {
|
||||||
|
"balance": "0x79b25b967b77c5d",
|
||||||
|
"nonce": "5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"chainId": 11155111,
|
||||||
|
"homesteadBlock": 0,
|
||||||
|
"daoForkSupport": true,
|
||||||
|
"eip150Block": 0,
|
||||||
|
"eip155Block": 0,
|
||||||
|
"eip158Block": 0,
|
||||||
|
"byzantiumBlock": 0,
|
||||||
|
"constantinopleBlock": 0,
|
||||||
|
"petersburgBlock": 0,
|
||||||
|
"istanbulBlock": 0,
|
||||||
|
"muirGlacierBlock": 0,
|
||||||
|
"berlinBlock": 0,
|
||||||
|
"londonBlock": 0,
|
||||||
|
"mergeNetsplitBlock": 1735371,
|
||||||
|
"shanghaiTime": 1677557088,
|
||||||
|
"cancunTime": 1706655072,
|
||||||
|
"terminalTotalDifficulty": 17000000000000000,
|
||||||
|
"terminalTotalDifficultyPassed": true,
|
||||||
|
"ethash": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"context": {
|
||||||
|
"number": "3727495",
|
||||||
|
"difficulty": "0",
|
||||||
|
"timestamp": "1687217028",
|
||||||
|
"gasLimit": "30000000",
|
||||||
|
"miner": "0x0000006916a87b82333f4245046623b23794c65c",
|
||||||
|
"baseFeePerGas": "13"
|
||||||
|
},
|
||||||
|
"input": "0xf8ab0582028483030d4094dd9f95e2c1053a76089b5017829bb2330cb6ae4780b84440c10f19000000000000000000000000eb3d3d616f3e43e9ed361edd2d0e644d18d179b700000000000000000000000000000000000000000000000000000000000000fa8401546d72a05f0c3834a66571748310717c3f95275d4f4f2ea2fa681a0e5395458a95a955a3a004e74954d34007b7cf040038f2c95a2f6b90775bcec1be36de50da729b9ca2d3",
|
||||||
|
"result": {
|
||||||
|
"from": "0xeb3d3d616f3e43e9ed361edd2d0e644d18d179b7",
|
||||||
|
"gas": "0x30d40",
|
||||||
|
"gasUsed": "0x110e9",
|
||||||
|
"to": "0xdd9f95e2c1053a76089b5017829bb2330cb6ae47",
|
||||||
|
"input": "0x40c10f19000000000000000000000000eb3d3d616f3e43e9ed361edd2d0e644d18d179b700000000000000000000000000000000000000000000000000000000000000fa",
|
||||||
|
"value": "0x0",
|
||||||
|
"type": "CALL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,288 +0,0 @@
|
||||||
package firehose_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"hash"
|
|
||||||
"math/big"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
|
||||||
"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/tracing"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
pbeth "github.com/ethereum/go-ethereum/pb/sf/ethereum/type/v2"
|
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"golang.org/x/crypto/sha3"
|
|
||||||
"google.golang.org/protobuf/encoding/protojson"
|
|
||||||
"google.golang.org/protobuf/proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFirehoseIntegrationTest(t *testing.T) {
|
|
||||||
context := vm.BlockContext{
|
|
||||||
CanTransfer: core.CanTransfer,
|
|
||||||
Transfer: core.Transfer,
|
|
||||||
Coinbase: common.Address{},
|
|
||||||
BlockNumber: new(big.Int).SetUint64(uint64(1)),
|
|
||||||
Time: 1,
|
|
||||||
Difficulty: big.NewInt(2),
|
|
||||||
GasLimit: uint64(1000000),
|
|
||||||
BaseFee: big.NewInt(8),
|
|
||||||
}
|
|
||||||
|
|
||||||
tracer, err := tracers.NewFirehoseFromRawJSON(json.RawMessage(`{
|
|
||||||
"applyBackwardsCompatibility": true,
|
|
||||||
"_private": {
|
|
||||||
"flushToTestBuffer": true,
|
|
||||||
"ignoreGenesisBlock": true
|
|
||||||
}
|
|
||||||
}`))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(fmt.Errorf("failed to create firehose tracer: %w", err))
|
|
||||||
}
|
|
||||||
hooks := tracers.NewTracingHooksFromFirehose(tracer)
|
|
||||||
|
|
||||||
genesis, blockchain := newCanonical(t, types.GenesisAlloc{}, context, hooks)
|
|
||||||
|
|
||||||
block := types.NewBlock(&types.Header{
|
|
||||||
ParentHash: genesis.ToBlock().Hash(),
|
|
||||||
Number: context.BlockNumber,
|
|
||||||
Difficulty: context.Difficulty,
|
|
||||||
Coinbase: context.Coinbase,
|
|
||||||
Time: context.Time,
|
|
||||||
GasLimit: context.GasLimit,
|
|
||||||
BaseFee: context.BaseFee,
|
|
||||||
ParentBeaconRoot: ptr(common.Hash{}),
|
|
||||||
}, nil, nil, nil, trie.NewStackTrie(nil))
|
|
||||||
|
|
||||||
blockchain.SetBlockValidatorAndProcessorForTesting(
|
|
||||||
ignoreValidateStateValidator{core.NewBlockValidator(genesis.Config, blockchain, blockchain.Engine())},
|
|
||||||
core.NewStateProcessor(genesis.Config, blockchain, blockchain.Engine()),
|
|
||||||
)
|
|
||||||
|
|
||||||
n, err := blockchain.InsertChain(types.Blocks{block})
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, 1, n)
|
|
||||||
|
|
||||||
genesisLine, blockLines, unknownLines := readTracerFirehoseLines(t, tracer)
|
|
||||||
require.Len(t, unknownLines, 0, "Lines:\n%s", strings.Join(slicesMap(unknownLines, func(l unknwonLine) string { return "- '" + string(l) + "'" }), "\n"))
|
|
||||||
require.NotNil(t, genesisLine)
|
|
||||||
blockLines.assertEquals(t,
|
|
||||||
firehoseBlockLineParams{"1", "8e6ee4b1054d94df1d8a51fb983447dc2e27a854590c3ac0061f994284be8150", "0", "845bad515694a416bab4b8d44e22cf97a8c894a8502110ab807883940e185ce0", "0", "1000000000"},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
type firehoseInitLine struct {
|
|
||||||
ProtocolVersion string
|
|
||||||
NodeName string
|
|
||||||
NodeVersion string
|
|
||||||
}
|
|
||||||
|
|
||||||
type firehoseBlockLines []firehoseBlockLine
|
|
||||||
|
|
||||||
func (lines firehoseBlockLines) assertEquals(t *testing.T, expected ...firehoseBlockLineParams) {
|
|
||||||
actualParams := slicesMap(lines, func(l firehoseBlockLine) firehoseBlockLineParams { return l.Params })
|
|
||||||
require.Equal(t, expected, actualParams, "Actual lines block params do not match expected lines block params")
|
|
||||||
|
|
||||||
goldenUpdate := os.Getenv("GOLDEN_UPDATE") == "true"
|
|
||||||
|
|
||||||
for _, line := range lines {
|
|
||||||
goldenPath := fmt.Sprintf("testdata/%s/block.%d.golden.json", t.Name(), line.Block.Header.Number)
|
|
||||||
if !goldenUpdate && !fileExists(t, goldenPath) {
|
|
||||||
t.Fatalf("the golden file %q does not exist, re-run with 'GOLDEN_UPDATE=true go test ./... -run %q' to generate the intial version", goldenPath, t.Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
content, err := protojson.MarshalOptions{Indent: " "}.Marshal(line.Block)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
if goldenUpdate {
|
|
||||||
require.NoError(t, os.MkdirAll(filepath.Dir(goldenPath), 0755))
|
|
||||||
require.NoError(t, os.WriteFile(goldenPath, content, 0644))
|
|
||||||
}
|
|
||||||
|
|
||||||
expected, err := os.ReadFile(goldenPath)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
expectedBlock := &pbeth.Block{}
|
|
||||||
require.NoError(t, protojson.Unmarshal(expected, expectedBlock))
|
|
||||||
|
|
||||||
if !proto.Equal(expectedBlock, line.Block) {
|
|
||||||
assert.EqualExportedValues(t, expectedBlock, line.Block, "Run 'GOLDEN_UPDATE=true go test ./... -run %q' to update golden file", t.Name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func fileExists(t *testing.T, path string) bool {
|
|
||||||
t.Helper()
|
|
||||||
stat, err := os.Stat(path)
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return !stat.IsDir()
|
|
||||||
}
|
|
||||||
|
|
||||||
func slicesMap[T any, U any](s []T, f func(T) U) []U {
|
|
||||||
result := make([]U, len(s))
|
|
||||||
for i, v := range s {
|
|
||||||
result[i] = f(v)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
type firehoseBlockLine struct {
|
|
||||||
// We split params and block to make it easier to compare stuff
|
|
||||||
Params firehoseBlockLineParams
|
|
||||||
Block *pbeth.Block
|
|
||||||
}
|
|
||||||
|
|
||||||
type firehoseBlockLineParams struct {
|
|
||||||
Number string
|
|
||||||
Hash string
|
|
||||||
PreviousNum string
|
|
||||||
PreviousHash string
|
|
||||||
LibNum string
|
|
||||||
Time string
|
|
||||||
}
|
|
||||||
|
|
||||||
type unknwonLine string
|
|
||||||
|
|
||||||
func readTracerFirehoseLines(t *testing.T, tracer *tracers.Firehose) (genesisLine *firehoseInitLine, blockLines firehoseBlockLines, unknownLines []unknwonLine) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
lines := bytes.Split(tracer.InternalTestingBuffer().Bytes(), []byte{'\n'})
|
|
||||||
for _, line := range lines {
|
|
||||||
if len(line) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
parts := bytes.Split(line, []byte{' '})
|
|
||||||
if len(parts) == 0 || string(parts[0]) != "FIRE" {
|
|
||||||
unknownLines = append(unknownLines, unknwonLine(line))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
action := string(parts[1])
|
|
||||||
fireParts := parts[2:]
|
|
||||||
switch action {
|
|
||||||
case "INIT":
|
|
||||||
genesisLine = &firehoseInitLine{
|
|
||||||
ProtocolVersion: string(fireParts[0]),
|
|
||||||
NodeName: string(fireParts[1]),
|
|
||||||
NodeVersion: string(fireParts[2]),
|
|
||||||
}
|
|
||||||
|
|
||||||
case "BLOCK":
|
|
||||||
protoBytes, err := base64.StdEncoding.DecodeString(string(fireParts[6]))
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
block := &pbeth.Block{}
|
|
||||||
require.NoError(t, proto.Unmarshal(protoBytes, block))
|
|
||||||
|
|
||||||
blockLines = append(blockLines, firehoseBlockLine{
|
|
||||||
Params: firehoseBlockLineParams{
|
|
||||||
Number: string(fireParts[0]),
|
|
||||||
Hash: string(fireParts[1]),
|
|
||||||
PreviousNum: string(fireParts[2]),
|
|
||||||
PreviousHash: string(fireParts[3]),
|
|
||||||
LibNum: string(fireParts[4]),
|
|
||||||
Time: string(fireParts[5]),
|
|
||||||
},
|
|
||||||
Block: block,
|
|
||||||
})
|
|
||||||
|
|
||||||
default:
|
|
||||||
unknownLines = append(unknownLines, unknwonLine(line))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func newCanonical(t *testing.T, alloc types.GenesisAlloc, context vm.BlockContext, tracer *tracing.Hooks) (*core.Genesis, *core.BlockChain) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
var (
|
|
||||||
engine = ethash.NewFullFaker()
|
|
||||||
genesis = &core.Genesis{
|
|
||||||
Difficulty: new(big.Int).Sub(context.Difficulty, big.NewInt(1)),
|
|
||||||
Timestamp: context.Time - 1,
|
|
||||||
Number: new(big.Int).Sub(context.BlockNumber, big.NewInt(1)).Uint64(),
|
|
||||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
|
||||||
Coinbase: context.Coinbase,
|
|
||||||
Config: params.AllEthashProtocolChanges,
|
|
||||||
Alloc: alloc,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
// Initialize a fresh chain with only a genesis block
|
|
||||||
blockchain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), core.DefaultCacheConfigWithScheme(rawdb.HashScheme), genesis, nil, engine, vm.Config{
|
|
||||||
Tracer: tracer,
|
|
||||||
}, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return genesis, blockchain
|
|
||||||
}
|
|
||||||
|
|
||||||
// testHasher is the helper tool for transaction/receipt list hashing.
|
|
||||||
// The original hasher is trie, in order to get rid of import cycle,
|
|
||||||
// use the testing hasher instead.
|
|
||||||
type testHasher struct {
|
|
||||||
hasher hash.Hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewHasher returns a new testHasher instance.
|
|
||||||
func NewHasher() *testHasher {
|
|
||||||
return &testHasher{hasher: sha3.NewLegacyKeccak256()}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset resets the hash state.
|
|
||||||
func (h *testHasher) Reset() {
|
|
||||||
h.hasher.Reset()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update updates the hash state with the given key and value.
|
|
||||||
func (h *testHasher) Update(key, val []byte) error {
|
|
||||||
h.hasher.Write(key)
|
|
||||||
h.hasher.Write(val)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash returns the hash value.
|
|
||||||
func (h *testHasher) Hash() common.Hash {
|
|
||||||
return common.BytesToHash(h.hasher.Sum(nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
type ignoreValidateStateValidator struct {
|
|
||||||
core.Validator
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v ignoreValidateStateValidator) ValidateBody(block *types.Block) error {
|
|
||||||
return v.Validator.ValidateBody(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v ignoreValidateStateValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ptr[T any](v T) *T {
|
|
||||||
return &v
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue