Merge branch 'gethmaster' into gethintegration

This commit is contained in:
Chen Kai 2024-12-08 22:31:04 +08:00
commit b209acdc7d
106 changed files with 2491 additions and 1432 deletions

39
.github/CODEOWNERS vendored
View file

@ -1,25 +1,36 @@
# Lines starting with '#' are comments. # Lines starting with '#' are comments.
# Each line is a file pattern followed by one or more owners. # Each line is a file pattern followed by one or more owners.
accounts/usbwallet @karalabe accounts/usbwallet/ @gballet
accounts/scwallet @gballet accounts/scwallet/ @gballet
accounts/abi @gballet @MariusVanDerWijden accounts/abi/ @gballet @MariusVanDerWijden
beacon/engine @lightclient beacon/engine/ @MariusVanDerWijden @lightclient @fjl
cmd/clef @holiman beacon/light/ @zsfelfoldi
cmd/evm @holiman @MariusVanDerWijden @lightclient beacon/merkle/ @zsfelfoldi
consensus @karalabe beacon/types/ @zsfelfoldi @fjl
core/ @karalabe @holiman @rjl493456442 beacon/params/ @zsfelfoldi @fjl
eth/ @karalabe @holiman @rjl493456442 cmd/clef/ @holiman
eth/catalyst/ @gballet @lightclient cmd/evm/ @holiman @MariusVanDerWijden @lightclient
core/state/ @rjl493456442 @holiman
crypto/ @gballet @jwasinger @holiman @fjl
core/ @holiman @rjl493456442
eth/ @holiman @rjl493456442
eth/catalyst/ @MariusVanDerWijden @lightclient @fjl @jwasinger
eth/tracers/ @s1na eth/tracers/ @s1na
ethclient/ @fjl
ethdb/ @rjl493456442
event/ @fjl
trie/ @rjl493456442
triedb/ @rjl493456442
core/tracing/ @s1na core/tracing/ @s1na
graphql/ @s1na graphql/ @s1na
internal/ethapi @lightclient internal/ethapi/ @fjl @s1na @lightclient
internal/era @lightclient internal/era/ @lightclient
les/ @zsfelfoldi @rjl493456442 metrics/ @holiman
light/ @zsfelfoldi @rjl493456442 miner/ @MariusVanDerWijden @holiman @fjl @rjl493456442
node/ @fjl node/ @fjl
p2p/ @fjl @zsfelfoldi p2p/ @fjl @zsfelfoldi
rlp/ @fjl
params/ @fjl @holiman @karalabe @gballet @rjl493456442 @zsfelfoldi params/ @fjl @holiman @karalabe @gballet @rjl493456442 @zsfelfoldi
rpc/ @fjl @holiman rpc/ @fjl @holiman
signer/ @holiman signer/ @holiman

View file

@ -265,15 +265,7 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H
var requestsHash *common.Hash var requestsHash *common.Hash
if requests != nil { if requests != nil {
// Put back request type byte. h := types.CalcRequestsHash(requests)
typedRequests := make([][]byte, len(requests))
for i, reqdata := range requests {
typedReqdata := make([]byte, len(reqdata)+1)
typedReqdata[0] = byte(i)
copy(typedReqdata[1:], reqdata)
typedRequests[i] = typedReqdata
}
h := types.CalcRequestsHash(typedRequests)
requestsHash = &h requestsHash = &h
} }
@ -343,20 +335,11 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.
} }
} }
// Remove type byte in requests.
var plainRequests [][]byte
if requests != nil {
plainRequests = make([][]byte, len(requests))
for i, reqdata := range requests {
plainRequests[i] = reqdata[1:]
}
}
return &ExecutionPayloadEnvelope{ return &ExecutionPayloadEnvelope{
ExecutionPayload: data, ExecutionPayload: data,
BlockValue: fees, BlockValue: fees,
BlobsBundle: &bundle, BlobsBundle: &bundle,
Requests: plainRequests, Requests: requests,
Override: false, Override: false,
} }
} }

View file

@ -74,7 +74,6 @@ var (
allToolsArchiveFiles = []string{ allToolsArchiveFiles = []string{
"COPYING", "COPYING",
executablePath("abigen"), executablePath("abigen"),
executablePath("bootnode"),
executablePath("evm"), executablePath("evm"),
executablePath("geth"), executablePath("geth"),
executablePath("rlpdump"), executablePath("rlpdump"),
@ -87,10 +86,6 @@ var (
BinaryName: "abigen", BinaryName: "abigen",
Description: "Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages.", Description: "Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages.",
}, },
{
BinaryName: "bootnode",
Description: "Ethereum bootnode.",
},
{ {
BinaryName: "evm", BinaryName: "evm",
Description: "Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode.", Description: "Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode.",

View file

@ -1,209 +0,0 @@
// Copyright 2015 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/>.
// bootnode runs a bootstrap node for the Ethereum Discovery Protocol.
package main
import (
"crypto/ecdsa"
"flag"
"fmt"
"net"
"os"
"time"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/p2p/netutil"
)
func main() {
var (
listenAddr = flag.String("addr", ":30301", "listen address")
genKey = flag.String("genkey", "", "generate a node key")
writeAddr = flag.Bool("writeaddress", false, "write out the node's public key and quit")
nodeKeyFile = flag.String("nodekey", "", "private key filename")
nodeKeyHex = flag.String("nodekeyhex", "", "private key as hex (for testing)")
natdesc = flag.String("nat", "none", "port mapping mechanism (any|none|upnp|pmp|pmp:<IP>|extip:<IP>)")
netrestrict = flag.String("netrestrict", "", "restrict network communication to the given IP networks (CIDR masks)")
runv5 = flag.Bool("v5", false, "run a v5 topic discovery bootnode")
verbosity = flag.Int("verbosity", 3, "log verbosity (0-5)")
vmodule = flag.String("vmodule", "", "log verbosity pattern")
nodeKey *ecdsa.PrivateKey
err error
)
flag.Parse()
glogger := log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false))
slogVerbosity := log.FromLegacyLevel(*verbosity)
glogger.Verbosity(slogVerbosity)
glogger.Vmodule(*vmodule)
log.SetDefault(log.NewLogger(glogger))
natm, err := nat.Parse(*natdesc)
if err != nil {
utils.Fatalf("-nat: %v", err)
}
switch {
case *genKey != "":
nodeKey, err = crypto.GenerateKey()
if err != nil {
utils.Fatalf("could not generate key: %v", err)
}
if err = crypto.SaveECDSA(*genKey, nodeKey); err != nil {
utils.Fatalf("%v", err)
}
if !*writeAddr {
return
}
case *nodeKeyFile == "" && *nodeKeyHex == "":
utils.Fatalf("Use -nodekey or -nodekeyhex to specify a private key")
case *nodeKeyFile != "" && *nodeKeyHex != "":
utils.Fatalf("Options -nodekey and -nodekeyhex are mutually exclusive")
case *nodeKeyFile != "":
if nodeKey, err = crypto.LoadECDSA(*nodeKeyFile); err != nil {
utils.Fatalf("-nodekey: %v", err)
}
case *nodeKeyHex != "":
if nodeKey, err = crypto.HexToECDSA(*nodeKeyHex); err != nil {
utils.Fatalf("-nodekeyhex: %v", err)
}
}
if *writeAddr {
fmt.Printf("%x\n", crypto.FromECDSAPub(&nodeKey.PublicKey)[1:])
os.Exit(0)
}
var restrictList *netutil.Netlist
if *netrestrict != "" {
restrictList, err = netutil.ParseNetlist(*netrestrict)
if err != nil {
utils.Fatalf("-netrestrict: %v", err)
}
}
addr, err := net.ResolveUDPAddr("udp", *listenAddr)
if err != nil {
utils.Fatalf("-ResolveUDPAddr: %v", err)
}
conn, err := net.ListenUDP("udp", addr)
if err != nil {
utils.Fatalf("-ListenUDP: %v", err)
}
defer conn.Close()
db, _ := enode.OpenDB("")
ln := enode.NewLocalNode(db, nodeKey)
listenerAddr := conn.LocalAddr().(*net.UDPAddr)
if natm != nil && !listenerAddr.IP.IsLoopback() {
natAddr := doPortMapping(natm, ln, listenerAddr)
if natAddr != nil {
listenerAddr = natAddr
}
}
printNotice(&nodeKey.PublicKey, *listenerAddr)
cfg := discover.Config{
PrivateKey: nodeKey,
NetRestrict: restrictList,
}
if *runv5 {
if _, err := discover.ListenV5(conn, ln, cfg); err != nil {
utils.Fatalf("%v", err)
}
} else {
if _, err := discover.ListenUDP(conn, ln, cfg); err != nil {
utils.Fatalf("%v", err)
}
}
select {}
}
func printNotice(nodeKey *ecdsa.PublicKey, addr net.UDPAddr) {
if addr.IP.IsUnspecified() {
addr.IP = net.IP{127, 0, 0, 1}
}
n := enode.NewV4(nodeKey, addr.IP, 0, addr.Port)
fmt.Println(n.URLv4())
fmt.Println("Note: you're using cmd/bootnode, a developer tool.")
fmt.Println("We recommend using a regular node as bootstrap node for production deployments.")
}
func doPortMapping(natm nat.Interface, ln *enode.LocalNode, addr *net.UDPAddr) *net.UDPAddr {
const (
protocol = "udp"
name = "ethereum discovery"
)
newLogger := func(external int, internal int) log.Logger {
return log.New("proto", protocol, "extport", external, "intport", internal, "interface", natm)
}
var (
intport = addr.Port
extaddr = &net.UDPAddr{IP: addr.IP, Port: addr.Port}
mapTimeout = nat.DefaultMapTimeout
log = newLogger(addr.Port, intport)
)
addMapping := func() {
// Get the external address.
var err error
extaddr.IP, err = natm.ExternalIP()
if err != nil {
log.Debug("Couldn't get external IP", "err", err)
return
}
// Create the mapping.
p, err := natm.AddMapping(protocol, extaddr.Port, intport, name, mapTimeout)
if err != nil {
log.Debug("Couldn't add port mapping", "err", err)
return
}
if p != uint16(extaddr.Port) {
extaddr.Port = int(p)
log = newLogger(extaddr.Port, intport)
log.Info("NAT mapped alternative port")
} else {
log.Info("NAT mapped port")
}
// Update IP/port information of the local node.
ln.SetStaticIP(extaddr.IP)
ln.SetFallbackUDP(extaddr.Port)
}
// Perform mapping once, synchronously.
log.Info("Attempting port mapping")
addMapping()
// Refresh the mapping periodically.
go func() {
refresh := time.NewTimer(mapTimeout)
defer refresh.Stop()
for range refresh.C {
addMapping()
refresh.Reset(mapTimeout)
}
}()
return extaddr
}

View file

@ -22,79 +22,84 @@ import (
"fmt" "fmt"
"os" "os"
"regexp" "regexp"
"sort" "slices"
"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/tracing"
"github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/ethereum/go-ethereum/tests" "github.com/ethereum/go-ethereum/tests"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"golang.org/x/exp/maps"
) )
var RunFlag = &cli.StringFlag{
Name: "run",
Value: ".*",
Usage: "Run only those tests matching the regular expression.",
}
var blockTestCommand = &cli.Command{ var blockTestCommand = &cli.Command{
Action: blockTestCmd, Action: blockTestCmd,
Name: "blocktest", Name: "blocktest",
Usage: "Executes the given blockchain tests", Usage: "Executes the given blockchain tests",
ArgsUsage: "<file>", ArgsUsage: "<path>",
Flags: []cli.Flag{RunFlag}, Flags: slices.Concat([]cli.Flag{
DumpFlag,
HumanReadableFlag,
RunFlag,
WitnessCrossCheckFlag,
}, traceFlags),
} }
func blockTestCmd(ctx *cli.Context) error { func blockTestCmd(ctx *cli.Context) error {
if len(ctx.Args().First()) == 0 { path := ctx.Args().First()
return errors.New("path-to-test argument required") if len(path) == 0 {
return errors.New("path argument required")
} }
var (
var tracer *tracing.Hooks collected = collectJSONFiles(path)
// Configure the EVM logger results []testResult
if ctx.Bool(MachineFlag.Name) { )
tracer = logger.NewJSONLogger(&logger.Config{ for _, fname := range collected {
EnableMemory: !ctx.Bool(DisableMemoryFlag.Name), r, err := runBlockTest(ctx, fname)
DisableStack: ctx.Bool(DisableStackFlag.Name),
DisableStorage: ctx.Bool(DisableStorageFlag.Name),
EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name),
}, os.Stderr)
}
// Load the test content from the input file
src, err := os.ReadFile(ctx.Args().First())
if err != nil { if err != nil {
return err return err
} }
var tests map[string]tests.BlockTest results = append(results, r...)
}
report(ctx, results)
return nil
}
func runBlockTest(ctx *cli.Context, fname string) ([]testResult, error) {
src, err := os.ReadFile(fname)
if err != nil {
return nil, err
}
var tests map[string]*tests.BlockTest
if err = json.Unmarshal(src, &tests); err != nil { if err = json.Unmarshal(src, &tests); err != nil {
return err return nil, err
} }
re, err := regexp.Compile(ctx.String(RunFlag.Name)) re, err := regexp.Compile(ctx.String(RunFlag.Name))
if err != nil { if err != nil {
return fmt.Errorf("invalid regex -%s: %v", RunFlag.Name, err) return nil, fmt.Errorf("invalid regex -%s: %v", RunFlag.Name, err)
} }
tracer := tracerFromFlags(ctx)
// Run them in order // Pull out keys to sort and ensure tests are run in order.
var keys []string keys := maps.Keys(tests)
for key := range tests { slices.Sort(keys)
keys = append(keys, key)
} // Run all the tests.
sort.Strings(keys) var results []testResult
for _, name := range keys { for _, name := range keys {
if !re.MatchString(name) { if !re.MatchString(name) {
continue continue
} }
test := tests[name] result := &testResult{Name: name, Pass: true}
if err := test.Run(false, rawdb.HashScheme, false, tracer, func(res error, chain *core.BlockChain) { if err := tests[name].Run(false, rawdb.HashScheme, ctx.Bool(WitnessCrossCheckFlag.Name), tracer, func(res error, chain *core.BlockChain) {
if ctx.Bool(DumpFlag.Name) { if ctx.Bool(DumpFlag.Name) {
if state, _ := chain.State(); state != nil { if s, _ := chain.State(); s != nil {
fmt.Println(string(state.Dump(nil))) result.State = dump(s)
} }
} }
}); err != nil { }); err != nil {
return fmt.Errorf("test %v: %w", name, err) result.Pass, result.Error = false, err.Error()
} }
results = append(results, *result)
} }
return nil return results, nil
} }

View file

@ -1,55 +0,0 @@
// Copyright 2017 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 main
import (
"errors"
"fmt"
"os"
"github.com/ethereum/go-ethereum/cmd/evm/internal/compiler"
"github.com/urfave/cli/v2"
)
var compileCommand = &cli.Command{
Action: compileCmd,
Name: "compile",
Usage: "Compiles easm source to evm binary",
ArgsUsage: "<file>",
}
func compileCmd(ctx *cli.Context) error {
debug := ctx.Bool(DebugFlag.Name)
if len(ctx.Args().First()) == 0 {
return errors.New("filename required")
}
fn := ctx.Args().First()
src, err := os.ReadFile(fn)
if err != nil {
return err
}
bin, err := compiler.Compile(fn, src, debug)
if err != nil {
return err
}
fmt.Println(bin)
return nil
}

View file

@ -1,55 +0,0 @@
// Copyright 2017 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 main
import (
"errors"
"fmt"
"os"
"strings"
"github.com/ethereum/go-ethereum/core/asm"
"github.com/urfave/cli/v2"
)
var disasmCommand = &cli.Command{
Action: disasmCmd,
Name: "disasm",
Usage: "Disassembles evm binary",
ArgsUsage: "<file>",
}
func disasmCmd(ctx *cli.Context) error {
var in string
switch {
case len(ctx.Args().First()) > 0:
fn := ctx.Args().First()
input, err := os.ReadFile(fn)
if err != nil {
return err
}
in = string(input)
case ctx.IsSet(InputFlag.Name):
in = ctx.String(InputFlag.Name)
default:
return errors.New("missing filename or --input value")
}
code := strings.TrimSpace(in)
fmt.Printf("%v\n", code)
return asm.PrintDisassembled(code)
}

49
cmd/evm/eest.go Normal file
View file

@ -0,0 +1,49 @@
// Copyright 2024 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 main
import "regexp"
// testMetadata provides more granular access to the test information encoded
// within its filename by the execution spec test (EEST).
type testMetadata struct {
fork string
module string // which python module gnerated the test, e.g. eip7702
file string // exact file the test came from, e.g. test_gas.py
function string // func that created the test, e.g. test_valid_mcopy_operations
parameters string // the name of the parameters which were used to fill the test, e.g. zero_inputs
}
// parseTestMetadata reads a test name and parses out more specific information
// about the test.
func parseTestMetadata(s string) *testMetadata {
var (
pattern = `tests\/([^\/]+)\/([^\/]+)\/([^:]+)::([^[]+)\[fork_([^-\]]+)-[^-]+-(.+)\]`
re = regexp.MustCompile(pattern)
)
match := re.FindStringSubmatch(s)
if len(match) == 0 {
return nil
}
return &testMetadata{
fork: match[5],
module: match[2],
file: match[3],
function: match[4],
parameters: match[6],
}
}

View file

@ -31,13 +31,41 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
var jt vm.JumpTable
const initcode = "INITCODE"
func init() { func init() {
jt = vm.NewPragueEOFInstructionSetForTesting() jt = vm.NewPragueEOFInstructionSetForTesting()
} }
var ( var (
jt vm.JumpTable hexFlag = &cli.StringFlag{
initcode = "INITCODE" Name: "hex",
Usage: "Single container data parse and validation",
}
refTestFlag = &cli.StringFlag{
Name: "test",
Usage: "Path to EOF validation reference test.",
}
eofParseCommand = &cli.Command{
Name: "eofparse",
Aliases: []string{"eof"},
Usage: "Parses hex eof container and returns validation errors (if any)",
Action: eofParseAction,
Flags: []cli.Flag{
hexFlag,
refTestFlag,
},
}
eofDumpCommand = &cli.Command{
Name: "eofdump",
Usage: "Parses hex eof container and prints out human-readable representation of the container.",
Action: eofDumpAction,
Flags: []cli.Flag{
hexFlag,
},
}
) )
func eofParseAction(ctx *cli.Context) error { func eofParseAction(ctx *cli.Context) error {

View file

@ -1,39 +0,0 @@
// Copyright 2017 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 compiler
import (
"errors"
"fmt"
"github.com/ethereum/go-ethereum/core/asm"
)
func Compile(fn string, src []byte, debug bool) (string, error) {
compiler := asm.NewCompiler(debug)
compiler.Feed(asm.Lex(src, debug))
bin, compileErrors := compiler.Compile()
if len(compileErrors) > 0 {
// report errors
for _, err := range compileErrors {
fmt.Printf("%s:%v\n", fn, err)
}
return "", errors.New("compiling failed")
}
return bin, nil
}

View file

@ -253,7 +253,6 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
statedb.SetTxContext(tx.Hash(), txIndex) statedb.SetTxContext(tx.Hash(), txIndex)
var ( var (
txContext = core.NewEVMTxContext(msg)
snapshot = statedb.Snapshot() snapshot = statedb.Snapshot()
prevGas = gaspool.Gas() prevGas = gaspool.Gas()
) )
@ -261,8 +260,6 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
} }
// (ret []byte, usedGas uint64, failed bool, err error) // (ret []byte, usedGas uint64, failed bool, err error)
evm.SetTxContext(txContext)
msgResult, err := core.ApplyMessage(evm, msg, gaspool) msgResult, err := core.ApplyMessage(evm, msg, gaspool)
if err != nil { if err != nil {
statedb.RevertToSnapshot(snapshot) statedb.RevertToSnapshot(snapshot)
@ -366,21 +363,19 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
// Gather the execution-layer triggered requests. // Gather the execution-layer triggered requests.
var requests [][]byte var requests [][]byte
if chainConfig.IsPrague(vmContext.BlockNumber, vmContext.Time) { if chainConfig.IsPrague(vmContext.BlockNumber, vmContext.Time) {
// EIP-6110 deposits requests = [][]byte{}
// EIP-6110
var allLogs []*types.Log var allLogs []*types.Log
for _, receipt := range receipts { for _, receipt := range receipts {
allLogs = append(allLogs, receipt.Logs...) allLogs = append(allLogs, receipt.Logs...)
} }
depositRequests, err := core.ParseDepositLogs(allLogs, chainConfig) if err := core.ParseDepositLogs(&requests, allLogs, chainConfig); err != nil {
if err != nil {
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not parse requests logs: %v", err)) return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not parse requests logs: %v", err))
} }
requests = append(requests, depositRequests) // EIP-7002
core.ProcessWithdrawalQueue(&requests, evm)
// EIP-7002 withdrawals // EIP-7251
requests = append(requests, core.ProcessWithdrawalQueue(evm)) core.ProcessConsolidationQueue(&requests, evm)
// EIP-7251 consolidations
requests = append(requests, core.ProcessConsolidationQueue(evm))
} }
// Commit block // Commit block

View file

@ -19,11 +19,14 @@ package main
import ( import (
"fmt" "fmt"
"math/big" "io/fs"
"os" "os"
"slices" "path/filepath"
"github.com/ethereum/go-ethereum/cmd/evm/internal/t8ntool" "github.com/ethereum/go-ethereum/cmd/evm/internal/t8ntool"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/internal/flags"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
@ -33,122 +36,100 @@ import (
_ "github.com/ethereum/go-ethereum/eth/tracers/native" _ "github.com/ethereum/go-ethereum/eth/tracers/native"
) )
// Some other nice-to-haves:
// * accumulate traces into an object to bundle with test
// * write tx identifier for trace before hand (blocktest only)
// * combine blocktest and statetest runner logic using unified test interface
const traceCategory = "TRACING"
var ( var (
DebugFlag = &cli.BoolFlag{ // Test running flags.
Name: "debug", RunFlag = &cli.StringFlag{
Usage: "output full trace logs", Name: "run",
Category: flags.VMCategory, Value: ".*",
} Usage: "Run only those tests matching the regular expression.",
StatDumpFlag = &cli.BoolFlag{
Name: "statdump",
Usage: "displays stack and heap memory information",
Category: flags.VMCategory,
}
CodeFlag = &cli.StringFlag{
Name: "code",
Usage: "EVM code",
Category: flags.VMCategory,
}
CodeFileFlag = &cli.StringFlag{
Name: "codefile",
Usage: "File containing EVM code. If '-' is specified, code is read from stdin ",
Category: flags.VMCategory,
}
GasFlag = &cli.Uint64Flag{
Name: "gas",
Usage: "gas limit for the evm",
Value: 10000000000,
Category: flags.VMCategory,
}
PriceFlag = &flags.BigFlag{
Name: "price",
Usage: "price set for the evm",
Value: new(big.Int),
Category: flags.VMCategory,
}
ValueFlag = &flags.BigFlag{
Name: "value",
Usage: "value set for the evm",
Value: new(big.Int),
Category: flags.VMCategory,
}
DumpFlag = &cli.BoolFlag{
Name: "dump",
Usage: "dumps the state after the run",
Category: flags.VMCategory,
}
InputFlag = &cli.StringFlag{
Name: "input",
Usage: "input for the EVM",
Category: flags.VMCategory,
}
InputFileFlag = &cli.StringFlag{
Name: "inputfile",
Usage: "file containing input for the EVM",
Category: flags.VMCategory,
} }
BenchFlag = &cli.BoolFlag{ BenchFlag = &cli.BoolFlag{
Name: "bench", Name: "bench",
Usage: "benchmark the execution", Usage: "benchmark the execution",
Category: flags.VMCategory, Category: flags.VMCategory,
} }
CreateFlag = &cli.BoolFlag{ WitnessCrossCheckFlag = &cli.BoolFlag{
Name: "create", Name: "cross-check",
Usage: "indicates the action should be create rather than call", Aliases: []string{"xc"},
Category: flags.VMCategory, Usage: "Cross-check stateful execution against stateless, verifying the witness generation.",
} }
GenesisFlag = &cli.StringFlag{
Name: "prestate", // Debugging flags.
Usage: "JSON file with prestate (genesis) config", DumpFlag = &cli.BoolFlag{
Category: flags.VMCategory, Name: "dump",
Usage: "dumps the state after the run",
}
HumanReadableFlag = &cli.BoolFlag{
Name: "human",
Usage: "\"Human-readable\" output",
}
StatDumpFlag = &cli.BoolFlag{
Name: "statdump",
Usage: "displays stack and heap memory information",
}
// Tracing flags.
TraceFlag = &cli.BoolFlag{
Name: "trace",
Usage: "Enable tracing and output trace log.",
Category: traceCategory,
}
TraceFormatFlag = &cli.StringFlag{
Name: "trace.format",
Usage: "Trace output format to use (struct|json)",
Value: "struct",
Category: traceCategory,
}
TraceDisableMemoryFlag = &cli.BoolFlag{
Name: "trace.nomemory",
Aliases: []string{"nomemory"},
Value: true,
Usage: "disable memory output",
Category: traceCategory,
}
TraceDisableStackFlag = &cli.BoolFlag{
Name: "trace.nostack",
Aliases: []string{"nostack"},
Usage: "disable stack output",
Category: traceCategory,
}
TraceDisableStorageFlag = &cli.BoolFlag{
Name: "trace.nostorage",
Aliases: []string{"nostorage"},
Usage: "disable storage output",
Category: traceCategory,
}
TraceDisableReturnDataFlag = &cli.BoolFlag{
Name: "trace.noreturndata",
Aliases: []string{"noreturndata"},
Value: true,
Usage: "enable return data output",
Category: traceCategory,
}
// Deprecated flags.
DebugFlag = &cli.BoolFlag{
Name: "debug",
Usage: "output full trace logs (deprecated)",
Hidden: true,
Category: traceCategory,
} }
MachineFlag = &cli.BoolFlag{ MachineFlag = &cli.BoolFlag{
Name: "json", Name: "json",
Usage: "output trace logs in machine readable format (json)", Usage: "output trace logs in machine readable format, json (deprecated)",
Category: flags.VMCategory, Hidden: true,
} Category: traceCategory,
SenderFlag = &cli.StringFlag{
Name: "sender",
Usage: "The transaction origin",
Category: flags.VMCategory,
}
ReceiverFlag = &cli.StringFlag{
Name: "receiver",
Usage: "The transaction receiver (execution context)",
Category: flags.VMCategory,
}
DisableMemoryFlag = &cli.BoolFlag{
Name: "nomemory",
Value: true,
Usage: "disable memory output",
Category: flags.VMCategory,
}
DisableStackFlag = &cli.BoolFlag{
Name: "nostack",
Usage: "disable stack output",
Category: flags.VMCategory,
}
DisableStorageFlag = &cli.BoolFlag{
Name: "nostorage",
Usage: "disable storage output",
Category: flags.VMCategory,
}
DisableReturnDataFlag = &cli.BoolFlag{
Name: "noreturndata",
Value: true,
Usage: "enable return data output",
Category: flags.VMCategory,
}
refTestFlag = &cli.StringFlag{
Name: "test",
Usage: "Path to EOF validation reference test.",
}
hexFlag = &cli.StringFlag{
Name: "hex",
Usage: "single container data parse and validation",
} }
) )
// Command definitions.
var ( var (
stateTransitionCommand = &cli.Command{ stateTransitionCommand = &cli.Command{
Name: "transition", Name: "transition",
@ -175,7 +156,6 @@ var (
t8ntool.RewardFlag, t8ntool.RewardFlag,
}, },
} }
transactionCommand = &cli.Command{ transactionCommand = &cli.Command{
Name: "transaction", Name: "transaction",
Aliases: []string{"t9n"}, Aliases: []string{"t9n"},
@ -203,62 +183,27 @@ var (
t8ntool.SealCliqueFlag, t8ntool.SealCliqueFlag,
}, },
} }
eofParseCommand = &cli.Command{
Name: "eofparse",
Aliases: []string{"eof"},
Usage: "Parses hex eof container and returns validation errors (if any)",
Action: eofParseAction,
Flags: []cli.Flag{
hexFlag,
refTestFlag,
},
}
eofDumpCommand = &cli.Command{
Name: "eofdump",
Usage: "Parses hex eof container and prints out human-readable representation of the container.",
Action: eofDumpAction,
Flags: []cli.Flag{
hexFlag,
},
}
) )
// vmFlags contains flags related to running the EVM.
var vmFlags = []cli.Flag{
CodeFlag,
CodeFileFlag,
CreateFlag,
GasFlag,
PriceFlag,
ValueFlag,
InputFlag,
InputFileFlag,
GenesisFlag,
SenderFlag,
ReceiverFlag,
}
// traceFlags contains flags that configure tracing output. // traceFlags contains flags that configure tracing output.
var traceFlags = []cli.Flag{ var traceFlags = []cli.Flag{
BenchFlag, TraceFlag,
TraceFormatFlag,
TraceDisableStackFlag,
TraceDisableMemoryFlag,
TraceDisableStorageFlag,
TraceDisableReturnDataFlag,
// deprecated
DebugFlag, DebugFlag,
DumpFlag,
MachineFlag, MachineFlag,
StatDumpFlag,
DisableMemoryFlag,
DisableStackFlag,
DisableStorageFlag,
DisableReturnDataFlag,
} }
var app = flags.NewApp("the evm command line interface") var app = flags.NewApp("the evm command line interface")
func init() { func init() {
app.Flags = slices.Concat(vmFlags, traceFlags, debug.Flags) app.Flags = debug.Flags
app.Commands = []*cli.Command{ app.Commands = []*cli.Command{
compileCommand,
disasmCommand,
runCommand, runCommand,
blockTestCommand, blockTestCommand,
stateTestCommand, stateTestCommand,
@ -280,11 +225,56 @@ func init() {
func main() { func main() {
if err := app.Run(os.Args); err != nil { if err := app.Run(os.Args); err != nil {
code := 1
if ec, ok := err.(*t8ntool.NumberedError); ok {
code = ec.ExitCode()
}
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
os.Exit(code) os.Exit(1)
} }
} }
// tracerFromFlags parses the cli flags and returns the specified tracer.
func tracerFromFlags(ctx *cli.Context) *tracing.Hooks {
config := &logger.Config{
EnableMemory: !ctx.Bool(TraceDisableMemoryFlag.Name),
DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
DisableStorage: ctx.Bool(TraceDisableStorageFlag.Name),
EnableReturnData: !ctx.Bool(TraceDisableReturnDataFlag.Name),
}
switch {
case ctx.Bool(TraceFlag.Name) && ctx.String(TraceFormatFlag.Name) == "struct":
return logger.NewStreamingStructLogger(config, os.Stderr).Hooks()
case ctx.Bool(TraceFlag.Name) && ctx.String(TraceFormatFlag.Name) == "json":
return logger.NewJSONLogger(config, os.Stderr)
case ctx.Bool(MachineFlag.Name):
return logger.NewJSONLogger(config, os.Stderr)
case ctx.Bool(DebugFlag.Name):
return logger.NewStreamingStructLogger(config, os.Stderr).Hooks()
default:
return nil
}
}
// collectJSONFiles walks the given path and accumulates all files with json
// extension.
func collectJSONFiles(path string) []string {
var out []string
err := filepath.Walk(path, func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && filepath.Ext(info.Name()) == ".json" {
out = append(out, path)
}
return nil
})
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
return out
}
// dump returns a state dump for the most current trie.
func dump(s *state.StateDB) *state.Dump {
root := s.IntermediateRoot(false)
cpy, _ := state.New(root, s.Database())
dump := cpy.RawDump(nil)
return &dump
}

87
cmd/evm/reporter.go Normal file
View file

@ -0,0 +1,87 @@
// Copyright 2024 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 main
import (
"encoding/json"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/urfave/cli/v2"
)
const (
PASS = "\033[32mPASS\033[0m"
FAIL = "\033[31mFAIL\033[0m"
)
// testResult contains the execution status after running a state test, any
// error that might have occurred and a dump of the final state if requested.
type testResult struct {
Name string `json:"name"`
Pass bool `json:"pass"`
Root *common.Hash `json:"stateRoot,omitempty"`
Fork string `json:"fork"`
Error string `json:"error,omitempty"`
State *state.Dump `json:"state,omitempty"`
Stats *execStats `json:"benchStats,omitempty"`
}
func (r testResult) String() string {
var status string
if r.Pass {
status = fmt.Sprintf("[%s]", PASS)
} else {
status = fmt.Sprintf("[%s]", FAIL)
}
info := r.Name
m := parseTestMetadata(r.Name)
if m != nil {
info = fmt.Sprintf("%s %s, param=%s", m.module, m.function, m.parameters)
}
var extra string
if !r.Pass {
extra = fmt.Sprintf(", err=%v, fork=%s", r.Error, r.Fork)
}
out := fmt.Sprintf("%s %s%s", status, info, extra)
if r.State != nil {
state, _ := json.MarshalIndent(r.State, "", " ")
out += "\n" + string(state)
}
return out
}
// report prints the after-test summary.
func report(ctx *cli.Context, results []testResult) {
if ctx.Bool(HumanReadableFlag.Name) {
pass := 0
for _, r := range results {
if r.Pass {
pass++
}
}
for _, r := range results {
fmt.Println(r)
}
fmt.Println("--")
fmt.Printf("%d tests passed, %d tests failed.\n", pass, len(results)-pass)
return
}
out, _ := json.MarshalIndent(results, "", " ")
fmt.Println(string(out))
}

View file

@ -18,6 +18,7 @@ package main
import ( import (
"bytes" "bytes"
"encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
@ -25,16 +26,17 @@ import (
"os" "os"
goruntime "runtime" goruntime "runtime"
"slices" "slices"
"strings"
"testing" "testing"
"time" "time"
"github.com/ethereum/go-ethereum/cmd/evm/internal/compiler"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"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/tracing" "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/core/vm"
"github.com/ethereum/go-ethereum/core/vm/runtime" "github.com/ethereum/go-ethereum/core/vm/runtime"
"github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/eth/tracers/logger"
@ -51,14 +53,82 @@ var runCommand = &cli.Command{
Usage: "Run arbitrary evm binary", Usage: "Run arbitrary evm binary",
ArgsUsage: "<code>", ArgsUsage: "<code>",
Description: `The run command runs arbitrary EVM code.`, Description: `The run command runs arbitrary EVM code.`,
Flags: slices.Concat(vmFlags, traceFlags), Flags: slices.Concat([]cli.Flag{
BenchFlag,
CodeFileFlag,
CreateFlag,
GasFlag,
GenesisFlag,
InputFlag,
InputFileFlag,
PriceFlag,
ReceiverFlag,
SenderFlag,
ValueFlag,
StatDumpFlag,
}, traceFlags),
} }
var (
CodeFileFlag = &cli.StringFlag{
Name: "codefile",
Usage: "File containing EVM code. If '-' is specified, code is read from stdin ",
Category: flags.VMCategory,
}
CreateFlag = &cli.BoolFlag{
Name: "create",
Usage: "Indicates the action should be create rather than call",
Category: flags.VMCategory,
}
GasFlag = &cli.Uint64Flag{
Name: "gas",
Usage: "Gas limit for the evm",
Value: 10000000000,
Category: flags.VMCategory,
}
GenesisFlag = &cli.StringFlag{
Name: "prestate",
Usage: "JSON file with prestate (genesis) config",
Category: flags.VMCategory,
}
InputFlag = &cli.StringFlag{
Name: "input",
Usage: "Input for the EVM",
Category: flags.VMCategory,
}
InputFileFlag = &cli.StringFlag{
Name: "inputfile",
Usage: "File containing input for the EVM",
Category: flags.VMCategory,
}
PriceFlag = &flags.BigFlag{
Name: "price",
Usage: "Price set for the evm",
Value: new(big.Int),
Category: flags.VMCategory,
}
ReceiverFlag = &cli.StringFlag{
Name: "receiver",
Usage: "The transaction receiver (execution context)",
Category: flags.VMCategory,
}
SenderFlag = &cli.StringFlag{
Name: "sender",
Usage: "The transaction origin",
Category: flags.VMCategory,
}
ValueFlag = &flags.BigFlag{
Name: "value",
Usage: "Value set for the evm",
Value: new(big.Int),
Category: flags.VMCategory,
}
)
// readGenesis will read the given JSON format genesis file and return // readGenesis will read the given JSON format genesis file and return
// the initialized Genesis structure // the initialized Genesis structure
func readGenesis(genesisPath string) *core.Genesis { func readGenesis(genesisPath string) *core.Genesis {
// Make sure we have a valid genesis JSON // Make sure we have a valid genesis JSON
//genesisPath := ctx.Args().First()
if len(genesisPath) == 0 { if len(genesisPath) == 0 {
utils.Fatalf("Must supply path to genesis JSON file") utils.Fatalf("Must supply path to genesis JSON file")
} }
@ -128,16 +198,15 @@ func timedExec(bench bool, execFunc func() ([]byte, uint64, error)) ([]byte, exe
func runCmd(ctx *cli.Context) error { func runCmd(ctx *cli.Context) error {
logconfig := &logger.Config{ logconfig := &logger.Config{
EnableMemory: !ctx.Bool(DisableMemoryFlag.Name), EnableMemory: !ctx.Bool(TraceDisableMemoryFlag.Name),
DisableStack: ctx.Bool(DisableStackFlag.Name), DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
DisableStorage: ctx.Bool(DisableStorageFlag.Name), DisableStorage: ctx.Bool(TraceDisableStorageFlag.Name),
EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name), EnableReturnData: !ctx.Bool(TraceDisableReturnDataFlag.Name),
Debug: ctx.Bool(DebugFlag.Name), Debug: ctx.Bool(DebugFlag.Name),
} }
var ( var (
tracer *tracing.Hooks tracer *tracing.Hooks
debugLogger *logger.StructLogger
prestate *state.StateDB prestate *state.StateDB
chainConfig *params.ChainConfig chainConfig *params.ChainConfig
sender = common.BytesToAddress([]byte("sender")) sender = common.BytesToAddress([]byte("sender"))
@ -149,10 +218,7 @@ func runCmd(ctx *cli.Context) error {
if ctx.Bool(MachineFlag.Name) { if ctx.Bool(MachineFlag.Name) {
tracer = logger.NewJSONLogger(logconfig, os.Stdout) tracer = logger.NewJSONLogger(logconfig, os.Stdout)
} else if ctx.Bool(DebugFlag.Name) { } else if ctx.Bool(DebugFlag.Name) {
debugLogger = logger.NewStructLogger(logconfig) tracer = logger.NewStreamingStructLogger(logconfig, os.Stderr).Hooks()
tracer = debugLogger.Hooks()
} else {
debugLogger = logger.NewStructLogger(logconfig)
} }
initialGas := ctx.Uint64(GasFlag.Name) initialGas := ctx.Uint64(GasFlag.Name)
@ -188,48 +254,35 @@ func runCmd(ctx *cli.Context) error {
var code []byte var code []byte
codeFileFlag := ctx.String(CodeFileFlag.Name) codeFileFlag := ctx.String(CodeFileFlag.Name)
codeFlag := ctx.String(CodeFlag.Name) hexcode := ctx.Args().First()
// The '--code' or '--codefile' flag overrides code in state // The '--codefile' flag overrides code in state
if codeFileFlag != "" || codeFlag != "" {
var hexcode []byte
if codeFileFlag != "" {
var err error
// If - is specified, it means that code comes from stdin
if codeFileFlag == "-" { if codeFileFlag == "-" {
// If - is specified, it means that code comes from stdin
// Try reading from stdin // Try reading from stdin
if hexcode, err = io.ReadAll(os.Stdin); err != nil { input, err := io.ReadAll(os.Stdin)
if err != nil {
fmt.Printf("Could not load code from stdin: %v\n", err) fmt.Printf("Could not load code from stdin: %v\n", err)
os.Exit(1) os.Exit(1)
} }
} else { hexcode = string(input)
} else if codeFileFlag != "" {
// Codefile with hex assembly // Codefile with hex assembly
if hexcode, err = os.ReadFile(codeFileFlag); err != nil { input, err := os.ReadFile(codeFileFlag)
if err != nil {
fmt.Printf("Could not load code from file: %v\n", err) fmt.Printf("Could not load code from file: %v\n", err)
os.Exit(1) os.Exit(1)
} }
hexcode = string(input)
} }
} else {
hexcode = []byte(codeFlag) hexcode = strings.TrimSpace(hexcode)
}
hexcode = bytes.TrimSpace(hexcode)
if len(hexcode)%2 != 0 { if len(hexcode)%2 != 0 {
fmt.Printf("Invalid input length for hex data (%d)\n", len(hexcode)) fmt.Printf("Invalid input length for hex data (%d)\n", len(hexcode))
os.Exit(1) os.Exit(1)
} }
code = common.FromHex(string(hexcode)) code = common.FromHex(hexcode)
} else if fn := ctx.Args().First(); len(fn) > 0 {
// EASM-file to compile
src, err := os.ReadFile(fn)
if err != nil {
return err
}
bin, err := compiler.Compile(fn, src, false)
if err != nil {
return err
}
code = common.Hex2Bytes(bin)
}
runtimeConfig := runtime.Config{ runtimeConfig := runtime.Config{
Origin: sender, Origin: sender,
State: prestate, State: prestate,
@ -310,12 +363,10 @@ func runCmd(ctx *cli.Context) error {
} }
if ctx.Bool(DebugFlag.Name) { if ctx.Bool(DebugFlag.Name) {
if debugLogger != nil { if logs := runtimeConfig.State.Logs(); len(logs) > 0 {
fmt.Fprintln(os.Stderr, "#### TRACE ####") fmt.Fprintln(os.Stderr, "### LOGS")
logger.WriteTrace(os.Stderr, debugLogger.StructLogs()) writeLogs(os.Stderr, logs)
} }
fmt.Fprintln(os.Stderr, "#### LOGS ####")
logger.WriteLogs(os.Stderr, runtimeConfig.State.Logs())
} }
if bench || ctx.Bool(StatDumpFlag.Name) { if bench || ctx.Bool(StatDumpFlag.Name) {
@ -334,3 +385,16 @@ allocated bytes: %d
return nil return nil
} }
// writeLogs writes vm logs in a readable format to the given writer
func writeLogs(writer io.Writer, logs []*types.Log) {
for _, log := range logs {
fmt.Fprintf(writer, "LOG%d: %x bn=%d txi=%x\n", len(log.Topics), log.Address, log.BlockNumber, log.TxIndex)
for i, topic := range log.Topics {
fmt.Fprintf(writer, "%08d %x\n", i, topic)
}
fmt.Fprint(writer, hex.Dump(log.Data))
fmt.Fprintln(writer)
}
}

View file

@ -21,12 +21,12 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "os"
"regexp"
"slices"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"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/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/internal/flags"
"github.com/ethereum/go-ethereum/tests" "github.com/ethereum/go-ethereum/tests"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
@ -35,157 +35,124 @@ import (
var ( var (
forkFlag = &cli.StringFlag{ forkFlag = &cli.StringFlag{
Name: "statetest.fork", Name: "statetest.fork",
Usage: "The hard-fork to run the test against", Usage: "Only run tests for the specified fork.",
Category: flags.VMCategory, Category: flags.VMCategory,
} }
idxFlag = &cli.IntFlag{ idxFlag = &cli.IntFlag{
Name: "statetest.index", Name: "statetest.index",
Usage: "The index of the subtest to run", Usage: "The index of the subtest to run.",
Category: flags.VMCategory, Category: flags.VMCategory,
Value: -1, // default to select all subtest indices Value: -1, // default to select all subtest indices
} }
testNameFlag = &cli.StringFlag{
Name: "statetest.name",
Usage: "The name of the state test to run",
Category: flags.VMCategory,
}
) )
var stateTestCommand = &cli.Command{ var stateTestCommand = &cli.Command{
Action: stateTestCmd, Action: stateTestCmd,
Name: "statetest", Name: "statetest",
Usage: "Executes the given state tests. Filenames can be fed via standard input (batch mode) or as an argument (one-off execution).", Usage: "Executes the given state tests. Filenames can be fed via standard input (batch mode) or as an argument (one-off execution).",
ArgsUsage: "<file>", ArgsUsage: "<file>",
Flags: []cli.Flag{ Flags: slices.Concat([]cli.Flag{
forkFlag, DumpFlag,
idxFlag, HumanReadableFlag,
testNameFlag, RunFlag,
}, }, traceFlags),
}
// StatetestResult contains the execution status after running a state test, any
// error that might have occurred and a dump of the final state if requested.
type StatetestResult struct {
Name string `json:"name"`
Pass bool `json:"pass"`
Root *common.Hash `json:"stateRoot,omitempty"`
Fork string `json:"fork"`
Error string `json:"error,omitempty"`
State *state.Dump `json:"state,omitempty"`
BenchStats *execStats `json:"benchStats,omitempty"`
} }
func stateTestCmd(ctx *cli.Context) error { func stateTestCmd(ctx *cli.Context) error {
// Configure the EVM logger path := ctx.Args().First()
config := &logger.Config{
EnableMemory: !ctx.Bool(DisableMemoryFlag.Name),
DisableStack: ctx.Bool(DisableStackFlag.Name),
DisableStorage: ctx.Bool(DisableStorageFlag.Name),
EnableReturnData: !ctx.Bool(DisableReturnDataFlag.Name),
}
var cfg vm.Config
switch {
case ctx.Bool(MachineFlag.Name):
cfg.Tracer = logger.NewJSONLogger(config, os.Stderr)
case ctx.Bool(DebugFlag.Name): // If path is provided, run the tests at that path.
cfg.Tracer = logger.NewStructLogger(config).Hooks() if len(path) != 0 {
var (
collected = collectJSONFiles(path)
results []testResult
)
for _, fname := range collected {
r, err := runStateTest(ctx, fname)
if err != nil {
return err
} }
// Load the test content from the input file results = append(results, r...)
if len(ctx.Args().First()) != 0 {
return runStateTest(ctx, ctx.Args().First(), cfg, ctx.Bool(DumpFlag.Name), ctx.Bool(BenchFlag.Name))
} }
// Read filenames from stdin and execute back-to-back report(ctx, results)
return nil
}
// Otherwise, read filenames from stdin and execute back-to-back.
scanner := bufio.NewScanner(os.Stdin) scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() { for scanner.Scan() {
fname := scanner.Text() fname := scanner.Text()
if len(fname) == 0 { if len(fname) == 0 {
return nil return nil
} }
if err := runStateTest(ctx, fname, cfg, ctx.Bool(DumpFlag.Name), ctx.Bool(BenchFlag.Name)); err != nil { results, err := runStateTest(ctx, fname)
if err != nil {
return err return err
} }
report(ctx, results)
} }
return nil return nil
} }
type stateTestCase struct {
name string
test tests.StateTest
st tests.StateSubtest
}
// collectMatchedSubtests returns test cases which match against provided filtering CLI parameters
func collectMatchedSubtests(ctx *cli.Context, testsByName map[string]tests.StateTest) []stateTestCase {
var res []stateTestCase
subtestName := ctx.String(testNameFlag.Name)
if subtestName != "" {
if subtest, ok := testsByName[subtestName]; ok {
testsByName := make(map[string]tests.StateTest)
testsByName[subtestName] = subtest
}
}
idx := ctx.Int(idxFlag.Name)
fork := ctx.String(forkFlag.Name)
for key, test := range testsByName {
for _, st := range test.Subtests() {
if idx != -1 && st.Index != idx {
continue
}
if fork != "" && st.Fork != fork {
continue
}
res = append(res, stateTestCase{name: key, st: st, test: test})
}
}
return res
}
// runStateTest loads the state-test given by fname, and executes the test. // runStateTest loads the state-test given by fname, and executes the test.
func runStateTest(ctx *cli.Context, fname string, cfg vm.Config, dump bool, bench bool) error { func runStateTest(ctx *cli.Context, fname string) ([]testResult, error) {
src, err := os.ReadFile(fname) src, err := os.ReadFile(fname)
if err != nil { if err != nil {
return err return nil, err
} }
var testsByName map[string]tests.StateTest var testsByName map[string]tests.StateTest
if err := json.Unmarshal(src, &testsByName); err != nil { if err := json.Unmarshal(src, &testsByName); err != nil {
return err return nil, fmt.Errorf("unable to read test file %s: %w", fname, err)
} }
matchingTests := collectMatchedSubtests(ctx, testsByName) cfg := vm.Config{Tracer: tracerFromFlags(ctx)}
re, err := regexp.Compile(ctx.String(RunFlag.Name))
if err != nil {
return nil, fmt.Errorf("invalid regex -%s: %v", RunFlag.Name, err)
}
// Iterate over all the tests, run them and aggregate the results // Iterate over all the tests, run them and aggregate the results
var results []StatetestResult results := make([]testResult, 0, len(testsByName))
for _, test := range matchingTests { for key, test := range testsByName {
if !re.MatchString(key) {
continue
}
for i, st := range test.Subtests() {
if idx := ctx.Int(idxFlag.Name); idx != -1 && idx != i {
// If specific index requested, skip all tests that do not match.
continue
}
if fork := ctx.String(forkFlag.Name); fork != "" && st.Fork != fork {
// If specific fork requested, skip all tests that do not match.
continue
}
// Run the test and aggregate the result // Run the test and aggregate the result
result := &StatetestResult{Name: test.name, Fork: test.st.Fork, Pass: true} result := &testResult{Name: key, Fork: st.Fork, Pass: true}
test.test.Run(test.st, cfg, false, rawdb.HashScheme, func(err error, tstate *tests.StateTestState) { test.Run(st, cfg, false, rawdb.HashScheme, func(err error, state *tests.StateTestState) {
var root common.Hash var root common.Hash
if tstate.StateDB != nil { if state.StateDB != nil {
root = tstate.StateDB.IntermediateRoot(false) root = state.StateDB.IntermediateRoot(false)
result.Root = &root result.Root = &root
fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root) fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root)
if dump { // Dump any state to aid debugging // Dump any state to aid debugging.
cpy, _ := state.New(root, tstate.StateDB.Database()) if ctx.Bool(DumpFlag.Name) {
dump := cpy.RawDump(nil) result.State = dump(state.StateDB)
result.State = &dump
} }
} }
if err != nil { // Collect bench stats if requested.
// Test failed, mark as so if ctx.Bool(BenchFlag.Name) {
result.Pass, result.Error = false, err.Error()
}
})
if bench {
_, stats, _ := timedExec(true, func() ([]byte, uint64, error) { _, stats, _ := timedExec(true, func() ([]byte, uint64, error) {
_, _, gasUsed, _ := test.test.RunNoVerify(test.st, cfg, false, rawdb.HashScheme) _, _, gasUsed, _ := test.RunNoVerify(st, cfg, false, rawdb.HashScheme)
return nil, gasUsed, nil return nil, gasUsed, nil
}) })
result.BenchStats = &stats result.Stats = &stats
} }
if err != nil {
// Test failed, mark as so.
result.Pass, result.Error = false, err.Error()
return
}
})
results = append(results, *result) results = append(results, *result)
} }
out, _ := json.MarshalIndent(results, "", " ") }
fmt.Println(string(out)) return results, nil
return nil
} }

View file

@ -50,7 +50,6 @@ import (
"github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/catalyst" "github.com/ethereum/go-ethereum/eth/catalyst"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/gasprice"
@ -1687,7 +1686,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
godebug.SetGCPercent(int(gogc)) godebug.SetGCPercent(int(gogc))
if ctx.IsSet(SyncTargetFlag.Name) { if ctx.IsSet(SyncTargetFlag.Name) {
cfg.SyncMode = downloader.FullSync // dev sync target forces full sync cfg.SyncMode = ethconfig.FullSync // dev sync target forces full sync
} else if ctx.IsSet(SyncModeFlag.Name) { } else if ctx.IsSet(SyncModeFlag.Name) {
if err = cfg.SyncMode.UnmarshalText([]byte(ctx.String(SyncModeFlag.Name))); err != nil { if err = cfg.SyncMode.UnmarshalText([]byte(ctx.String(SyncModeFlag.Name))); err != nil {
Fatalf("invalid --syncmode flag: %v", err) Fatalf("invalid --syncmode flag: %v", err)
@ -1758,7 +1757,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
} }
if !ctx.Bool(SnapshotFlag.Name) || cfg.SnapshotCache == 0 { if !ctx.Bool(SnapshotFlag.Name) || cfg.SnapshotCache == 0 {
// If snap-sync is requested, this flag is also required // If snap-sync is requested, this flag is also required
if cfg.SyncMode == downloader.SnapSync { if cfg.SyncMode == ethconfig.SnapSync {
if !ctx.Bool(SnapshotFlag.Name) { if !ctx.Bool(SnapshotFlag.Name) {
log.Warn("Snap sync requested, enabling --snapshot") log.Warn("Snap sync requested, enabling --snapshot")
} }
@ -1824,7 +1823,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
if !ctx.IsSet(NetworkIdFlag.Name) { if !ctx.IsSet(NetworkIdFlag.Name) {
cfg.NetworkId = 1337 cfg.NetworkId = 1337
} }
cfg.SyncMode = downloader.FullSync cfg.SyncMode = ethconfig.FullSync
// Create new developer account or reuse existing one // Create new developer account or reuse existing one
var ( var (
developer accounts.Account developer accounts.Account

View file

@ -93,7 +93,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
} }
// The individual checks for blob validity (version-check + not empty) // The individual checks for blob validity (version-check + not empty)
// happens in StateTransition. // happens in state transition.
} }
// Check blob gas usage. // Check blob gas usage.

View file

@ -1926,7 +1926,7 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
task := types.NewBlockWithHeader(context).WithBody(*block.Body()) task := types.NewBlockWithHeader(context).WithBody(*block.Body())
// Run the stateless self-cross-validation // Run the stateless self-cross-validation
crossStateRoot, crossReceiptRoot, err := ExecuteStateless(bc.chainConfig, task, witness) crossStateRoot, crossReceiptRoot, err := ExecuteStateless(bc.chainConfig, bc.vmConfig, task, witness)
if err != nil { if err != nil {
return nil, fmt.Errorf("stateless self-validation failed: %v", err) return nil, fmt.Errorf("stateless self-validation failed: %v", err)
} }

View file

@ -344,10 +344,7 @@ func (bc *BlockChain) stateRecoverable(root common.Hash) bool {
// ContractCodeWithPrefix retrieves a blob of data associated with a contract // ContractCodeWithPrefix retrieves a blob of data associated with a contract
// hash either from ephemeral in-memory cache, or from persistent storage. // hash either from ephemeral in-memory cache, or from persistent storage.
// func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) []byte {
// If the code doesn't exist in the in-memory cache, check the storage with
// new code scheme.
func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) ([]byte, error) {
// TODO(rjl493456442) The associated account address is also required // TODO(rjl493456442) The associated account address is also required
// in Verkle scheme. Fix it once snap-sync is supported for Verkle. // in Verkle scheme. Fix it once snap-sync is supported for Verkle.
return bc.statedb.ContractCodeWithPrefix(common.Address{}, hash) return bc.statedb.ContractCodeWithPrefix(common.Address{}, hash)

View file

@ -349,25 +349,22 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
var requests [][]byte var requests [][]byte
if config.IsPrague(b.header.Number, b.header.Time) { if config.IsPrague(b.header.Number, b.header.Time) {
requests = [][]byte{}
// EIP-6110 deposits // EIP-6110 deposits
var blockLogs []*types.Log var blockLogs []*types.Log
for _, r := range b.receipts { for _, r := range b.receipts {
blockLogs = append(blockLogs, r.Logs...) blockLogs = append(blockLogs, r.Logs...)
} }
depositRequests, err := ParseDepositLogs(blockLogs, config) if err := ParseDepositLogs(&requests, blockLogs, config); err != nil {
if err != nil {
panic(fmt.Sprintf("failed to parse deposit log: %v", err)) panic(fmt.Sprintf("failed to parse deposit log: %v", err))
} }
requests = append(requests, depositRequests)
// create EVM for system calls // create EVM for system calls
blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase) blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase)
evm := vm.NewEVM(blockContext, statedb, cm.config, vm.Config{}) evm := vm.NewEVM(blockContext, statedb, cm.config, vm.Config{})
// EIP-7002 withdrawals // EIP-7002
withdrawalRequests := ProcessWithdrawalQueue(evm) ProcessWithdrawalQueue(&requests, evm)
requests = append(requests, withdrawalRequests) // EIP-7251
// EIP-7251 consolidations ProcessConsolidationQueue(&requests, evm)
consolidationRequests := ProcessConsolidationQueue(evm)
requests = append(requests, consolidationRequests)
} }
if requests != nil { if requests != nil {
reqHash := types.CalcRequestsHash(requests) reqHash := types.CalcRequestsHash(requests)

View file

@ -472,9 +472,7 @@ func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block {
} }
} }
if conf.IsPrague(num, g.Timestamp) { if conf.IsPrague(num, g.Timestamp) {
emptyRequests := [][]byte{{0x00}, {0x01}, {0x02}} head.RequestsHash = &types.EmptyRequestsHash
rhash := types.CalcRequestsHash(emptyRequests)
head.RequestsHash = &rhash
} }
} }
return types.NewBlock(head, &types.Body{Withdrawals: withdrawals}, nil, trie.NewStackTrie(nil)) return types.NewBlock(head, &types.Body{Withdrawals: withdrawals}, nil, trie.NewStackTrie(nil))

View file

@ -293,7 +293,7 @@ func TestVerkleGenesisCommit(t *testing.T) {
}, },
} }
expected := common.FromHex("4a83dc39eb688dbcfaf581d60e82de18f875e38786ebce5833342011d6fef37b") expected := common.FromHex("018d20eebb130b5e2b796465fe36aafab650650729a92435aec071bf2386f080")
got := genesis.ToBlock().Root().Bytes() got := genesis.ToBlock().Root().Bytes()
if !bytes.Equal(got, expected) { if !bytes.Equal(got, expected) {
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got) t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)

View file

@ -17,7 +17,6 @@
package state package state
import ( import (
"errors"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -55,12 +54,6 @@ type Database interface {
// OpenStorageTrie opens the storage trie of an account. // OpenStorageTrie opens the storage trie of an account.
OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, trie Trie) (Trie, error) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, trie Trie) (Trie, error)
// ContractCode retrieves a particular contract's code.
ContractCode(addr common.Address, codeHash common.Hash) ([]byte, error)
// ContractCodeSize retrieves a particular contracts code's size.
ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error)
// PointCache returns the cache holding points used in verkle tree key computation // PointCache returns the cache holding points used in verkle tree key computation
PointCache() *utils.PointCache PointCache() *utils.PointCache
@ -180,7 +173,7 @@ func NewDatabaseForTesting() *CachingDB {
// Reader returns a state reader associated with the specified state root. // Reader returns a state reader associated with the specified state root.
func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) { func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
var readers []Reader var readers []StateReader
// Set up the state snapshot reader if available. This feature // Set up the state snapshot reader if available. This feature
// is optional and may be partially useful if it's not fully // is optional and may be partially useful if it's not fully
@ -188,7 +181,7 @@ func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
if db.snap != nil { if db.snap != nil {
snap := db.snap.Snapshot(stateRoot) snap := db.snap.Snapshot(stateRoot)
if snap != nil { if snap != nil {
readers = append(readers, newStateReader(snap)) // snap reader is optional readers = append(readers, newFlatReader(snap))
} }
} }
// Set up the trie reader, which is expected to always be available // Set up the trie reader, which is expected to always be available
@ -199,7 +192,11 @@ func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
} }
readers = append(readers, tr) readers = append(readers, tr)
return newMultiReader(readers...) combined, err := newMultiStateReader(readers...)
if err != nil {
return nil, err
}
return newReader(newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache), combined), nil
} }
// OpenTrie opens the main account trie at a specific root hash. // OpenTrie opens the main account trie at a specific root hash.
@ -229,45 +226,20 @@ func (db *CachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Addre
return tr, nil return tr, nil
} }
// ContractCode retrieves a particular contract's code.
func (db *CachingDB) ContractCode(address common.Address, codeHash common.Hash) ([]byte, error) {
code, _ := db.codeCache.Get(codeHash)
if len(code) > 0 {
return code, nil
}
code = rawdb.ReadCode(db.disk, codeHash)
if len(code) > 0 {
db.codeCache.Add(codeHash, code)
db.codeSizeCache.Add(codeHash, len(code))
return code, nil
}
return nil, errors.New("not found")
}
// ContractCodeWithPrefix retrieves a particular contract's code. If the // ContractCodeWithPrefix retrieves a particular contract's code. If the
// code can't be found in the cache, then check the existence with **new** // code can't be found in the cache, then check the existence with **new**
// db scheme. // db scheme.
func (db *CachingDB) ContractCodeWithPrefix(address common.Address, codeHash common.Hash) ([]byte, error) { func (db *CachingDB) ContractCodeWithPrefix(address common.Address, codeHash common.Hash) []byte {
code, _ := db.codeCache.Get(codeHash) code, _ := db.codeCache.Get(codeHash)
if len(code) > 0 { if len(code) > 0 {
return code, nil return code
} }
code = rawdb.ReadCodeWithPrefix(db.disk, codeHash) code = rawdb.ReadCodeWithPrefix(db.disk, codeHash)
if len(code) > 0 { if len(code) > 0 {
db.codeCache.Add(codeHash, code) db.codeCache.Add(codeHash, code)
db.codeSizeCache.Add(codeHash, len(code)) db.codeSizeCache.Add(codeHash, len(code))
return code, nil
} }
return nil, errors.New("not found") return code
}
// ContractCodeSize retrieves a particular contracts code's size.
func (db *CachingDB) ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error) {
if cached, ok := db.codeSizeCache.Get(codeHash); ok {
return cached, nil
}
code, err := db.ContractCode(addr, codeHash)
return len(code), err
} }
// TrieDB retrieves any intermediate trie-node caching layer. // TrieDB retrieves any intermediate trie-node caching layer.

View file

@ -136,10 +136,13 @@ func (it *nodeIterator) step() error {
} }
if !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) { if !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) {
it.codeHash = common.BytesToHash(account.CodeHash) it.codeHash = common.BytesToHash(account.CodeHash)
it.code, err = it.state.db.ContractCode(address, common.BytesToHash(account.CodeHash)) it.code, err = it.state.reader.Code(address, common.BytesToHash(account.CodeHash))
if err != nil { if err != nil {
return fmt.Errorf("code %x: %v", account.CodeHash, err) return fmt.Errorf("code %x: %v", account.CodeHash, err)
} }
if len(it.code) == 0 {
return fmt.Errorf("code is not found: %x", account.CodeHash)
}
} }
it.accountHash = it.stateIt.Parent() it.accountHash = it.stateIt.Parent()
return nil return nil

View file

@ -18,11 +18,13 @@ package state
import ( import (
"errors" "errors"
"maps"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/utils" "github.com/ethereum/go-ethereum/trie/utils"
@ -30,9 +32,26 @@ import (
"github.com/ethereum/go-ethereum/triedb/database" "github.com/ethereum/go-ethereum/triedb/database"
) )
// Reader defines the interface for accessing accounts and storage slots // ContractCodeReader defines the interface for accessing contract code.
type ContractCodeReader interface {
// Code retrieves a particular contract's code.
//
// - Returns nil code along with nil error if the requested contract code
// doesn't exist
// - Returns an error only if an unexpected issue occurs
Code(addr common.Address, codeHash common.Hash) ([]byte, error)
// CodeSize retrieves a particular contracts code's size.
//
// - Returns zero code size along with nil error if the requested contract code
// doesn't exist
// - Returns an error only if an unexpected issue occurs
CodeSize(addr common.Address, codeHash common.Hash) (int, error)
}
// StateReader defines the interface for accessing accounts and storage slots
// associated with a specific state. // associated with a specific state.
type Reader interface { type StateReader interface {
// Account retrieves the account associated with a particular address. // Account retrieves the account associated with a particular address.
// //
// - Returns a nil account if it does not exist // - Returns a nil account if it does not exist
@ -47,32 +66,84 @@ type Reader interface {
// - Returns an error only if an unexpected issue occurs // - Returns an error only if an unexpected issue occurs
// - The returned storage slot is safe to modify after the call // - The returned storage slot is safe to modify after the call
Storage(addr common.Address, slot common.Hash) (common.Hash, error) Storage(addr common.Address, slot common.Hash) (common.Hash, error)
// Copy returns a deep-copied state reader.
Copy() Reader
} }
// stateReader wraps a database state reader. // Reader defines the interface for accessing accounts, storage slots and contract
type stateReader struct { // code associated with a specific state.
type Reader interface {
ContractCodeReader
StateReader
}
// cachingCodeReader implements ContractCodeReader, accessing contract code either in
// local key-value store or the shared code cache.
type cachingCodeReader struct {
db ethdb.KeyValueReader
// These caches could be shared by multiple code reader instances,
// they are natively thread-safe.
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
codeSizeCache *lru.Cache[common.Hash, int]
}
// newCachingCodeReader constructs the code reader.
func newCachingCodeReader(db ethdb.KeyValueReader, codeCache *lru.SizeConstrainedCache[common.Hash, []byte], codeSizeCache *lru.Cache[common.Hash, int]) *cachingCodeReader {
return &cachingCodeReader{
db: db,
codeCache: codeCache,
codeSizeCache: codeSizeCache,
}
}
// Code implements ContractCodeReader, retrieving a particular contract's code.
// If the contract code doesn't exist, no error will be returned.
func (r *cachingCodeReader) Code(addr common.Address, codeHash common.Hash) ([]byte, error) {
code, _ := r.codeCache.Get(codeHash)
if len(code) > 0 {
return code, nil
}
code = rawdb.ReadCode(r.db, codeHash)
if len(code) > 0 {
r.codeCache.Add(codeHash, code)
r.codeSizeCache.Add(codeHash, len(code))
}
return code, nil
}
// CodeSize implements ContractCodeReader, retrieving a particular contracts code's size.
// If the contract code doesn't exist, no error will be returned.
func (r *cachingCodeReader) CodeSize(addr common.Address, codeHash common.Hash) (int, error) {
if cached, ok := r.codeSizeCache.Get(codeHash); ok {
return cached, nil
}
code, err := r.Code(addr, codeHash)
if err != nil {
return 0, err
}
return len(code), nil
}
// flatReader wraps a database state reader.
type flatReader struct {
reader database.StateReader reader database.StateReader
buff crypto.KeccakState buff crypto.KeccakState
} }
// newStateReader constructs a state reader with on the given state root. // newFlatReader constructs a state reader with on the given state root.
func newStateReader(reader database.StateReader) *stateReader { func newFlatReader(reader database.StateReader) *flatReader {
return &stateReader{ return &flatReader{
reader: reader, reader: reader,
buff: crypto.NewKeccakState(), buff: crypto.NewKeccakState(),
} }
} }
// Account implements Reader, retrieving the account specified by the address. // Account implements StateReader, retrieving the account specified by the address.
// //
// An error will be returned if the associated snapshot is already stale or // An error will be returned if the associated snapshot is already stale or
// the requested account is not yet covered by the snapshot. // the requested account is not yet covered by the snapshot.
// //
// The returned account might be nil if it's not existent. // The returned account might be nil if it's not existent.
func (r *stateReader) Account(addr common.Address) (*types.StateAccount, error) { func (r *flatReader) Account(addr common.Address) (*types.StateAccount, error) {
account, err := r.reader.Account(crypto.HashData(r.buff, addr.Bytes())) account, err := r.reader.Account(crypto.HashData(r.buff, addr.Bytes()))
if err != nil { if err != nil {
return nil, err return nil, err
@ -95,14 +166,14 @@ func (r *stateReader) Account(addr common.Address) (*types.StateAccount, error)
return acct, nil return acct, nil
} }
// Storage implements Reader, retrieving the storage slot specified by the // Storage implements StateReader, retrieving the storage slot specified by the
// address and slot key. // address and slot key.
// //
// An error will be returned if the associated snapshot is already stale or // An error will be returned if the associated snapshot is already stale or
// the requested storage slot is not yet covered by the snapshot. // the requested storage slot is not yet covered by the snapshot.
// //
// The returned storage slot might be empty if it's not existent. // The returned storage slot might be empty if it's not existent.
func (r *stateReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) { func (r *flatReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) {
addrHash := crypto.HashData(r.buff, addr.Bytes()) addrHash := crypto.HashData(r.buff, addr.Bytes())
slotHash := crypto.HashData(r.buff, key.Bytes()) slotHash := crypto.HashData(r.buff, key.Bytes())
ret, err := r.reader.Storage(addrHash, slotHash) ret, err := r.reader.Storage(addrHash, slotHash)
@ -123,15 +194,7 @@ func (r *stateReader) Storage(addr common.Address, key common.Hash) (common.Hash
return value, nil return value, nil
} }
// Copy implements Reader, returning a deep-copied snap reader. // trieReader implements the StateReader interface, providing functions to access
func (r *stateReader) Copy() Reader {
return &stateReader{
reader: r.reader,
buff: crypto.NewKeccakState(),
}
}
// trieReader implements the Reader interface, providing functions to access
// state from the referenced trie. // state from the referenced trie.
type trieReader struct { type trieReader struct {
root common.Hash // State root which uniquely represent a state root common.Hash // State root which uniquely represent a state
@ -167,7 +230,7 @@ func newTrieReader(root common.Hash, db *triedb.Database, cache *utils.PointCach
}, nil }, nil
} }
// Account implements Reader, retrieving the account specified by the address. // Account implements StateReader, retrieving the account specified by the address.
// //
// An error will be returned if the trie state is corrupted. An nil account // An error will be returned if the trie state is corrupted. An nil account
// will be returned if it's not existent in the trie. // will be returned if it's not existent in the trie.
@ -184,7 +247,7 @@ func (r *trieReader) Account(addr common.Address) (*types.StateAccount, error) {
return account, nil return account, nil
} }
// Storage implements Reader, retrieving the storage slot specified by the // Storage implements StateReader, retrieving the storage slot specified by the
// address and slot key. // address and slot key.
// //
// An error will be returned if the trie state is corrupted. An empty storage // An error will be returned if the trie state is corrupted. An empty storage
@ -227,48 +290,32 @@ func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash,
return value, nil return value, nil
} }
// Copy implements Reader, returning a deep-copied trie reader. // multiStateReader is the aggregation of a list of StateReader interface,
func (r *trieReader) Copy() Reader { // providing state access by leveraging all readers. The checking priority
tries := make(map[common.Address]Trie) // is determined by the position in the reader list.
for addr, tr := range r.subTries { type multiStateReader struct {
tries[addr] = mustCopyTrie(tr) readers []StateReader // List of state readers, sorted by checking priority
}
return &trieReader{
root: r.root,
db: r.db,
buff: crypto.NewKeccakState(),
mainTrie: mustCopyTrie(r.mainTrie),
subRoots: maps.Clone(r.subRoots),
subTries: tries,
}
} }
// multiReader is the aggregation of a list of Reader interface, providing state // newMultiStateReader constructs a multiStateReader instance with the given
// access by leveraging all readers. The checking priority is determined by the // readers. The priority among readers is assumed to be sorted. Note, it must
// position in the reader list. // contain at least one reader for constructing a multiStateReader.
type multiReader struct { func newMultiStateReader(readers ...StateReader) (*multiStateReader, error) {
readers []Reader // List of readers, sorted by checking priority
}
// newMultiReader constructs a multiReader instance with the given readers. The
// priority among readers is assumed to be sorted. Note, it must contain at least
// one reader for constructing a multiReader.
func newMultiReader(readers ...Reader) (*multiReader, error) {
if len(readers) == 0 { if len(readers) == 0 {
return nil, errors.New("empty reader set") return nil, errors.New("empty reader set")
} }
return &multiReader{ return &multiStateReader{
readers: readers, readers: readers,
}, nil }, nil
} }
// Account implementing Reader interface, retrieving the account associated with // Account implementing StateReader interface, retrieving the account associated
// a particular address. // with a particular address.
// //
// - Returns a nil account if it does not exist // - Returns a nil account if it does not exist
// - Returns an error only if an unexpected issue occurs // - Returns an error only if an unexpected issue occurs
// - The returned account is safe to modify after the call // - The returned account is safe to modify after the call
func (r *multiReader) Account(addr common.Address) (*types.StateAccount, error) { func (r *multiStateReader) Account(addr common.Address) (*types.StateAccount, error) {
var errs []error var errs []error
for _, reader := range r.readers { for _, reader := range r.readers {
acct, err := reader.Account(addr) acct, err := reader.Account(addr)
@ -280,13 +327,13 @@ func (r *multiReader) Account(addr common.Address) (*types.StateAccount, error)
return nil, errors.Join(errs...) return nil, errors.Join(errs...)
} }
// Storage implementing Reader interface, retrieving the storage slot associated // Storage implementing StateReader interface, retrieving the storage slot
// with a particular account address and slot key. // associated with a particular account address and slot key.
// //
// - Returns an empty slot if it does not exist // - Returns an empty slot if it does not exist
// - Returns an error only if an unexpected issue occurs // - Returns an error only if an unexpected issue occurs
// - The returned storage slot is safe to modify after the call // - The returned storage slot is safe to modify after the call
func (r *multiReader) Storage(addr common.Address, slot common.Hash) (common.Hash, error) { func (r *multiStateReader) Storage(addr common.Address, slot common.Hash) (common.Hash, error) {
var errs []error var errs []error
for _, reader := range r.readers { for _, reader := range r.readers {
slot, err := reader.Storage(addr, slot) slot, err := reader.Storage(addr, slot)
@ -298,11 +345,16 @@ func (r *multiReader) Storage(addr common.Address, slot common.Hash) (common.Has
return common.Hash{}, errors.Join(errs...) return common.Hash{}, errors.Join(errs...)
} }
// Copy implementing Reader interface, returning a deep-copied state reader. // reader is the wrapper of ContractCodeReader and StateReader interface.
func (r *multiReader) Copy() Reader { type reader struct {
var readers []Reader ContractCodeReader
for _, reader := range r.readers { StateReader
readers = append(readers, reader.Copy()) }
// newReader constructs a reader with the supplied code reader and state reader.
func newReader(codeReader ContractCodeReader, stateReader StateReader) *reader {
return &reader{
ContractCodeReader: codeReader,
StateReader: stateReader,
} }
return &multiReader{readers: readers}
} }

View file

@ -33,9 +33,11 @@ import (
"github.com/ethereum/go-ethereum/triedb" "github.com/ethereum/go-ethereum/triedb"
) )
// 0: initial version const (
// 1: destruct flag in diff layer is removed journalV0 uint64 = 0 // initial version
const journalVersion uint64 = 1 journalV1 uint64 = 1 // current version, with destruct flag (in diff layers) removed
journalCurrentVersion = journalV1
)
// journalGenerator is a disk layer entry containing the generator progress marker. // journalGenerator is a disk layer entry containing the generator progress marker.
type journalGenerator struct { type journalGenerator struct {
@ -50,6 +52,11 @@ type journalGenerator struct {
Storage uint64 Storage uint64
} }
// journalDestruct is an account deletion entry in a diffLayer's disk journal.
type journalDestruct struct {
Hash common.Hash
}
// journalAccount is an account entry in a diffLayer's disk journal. // journalAccount is an account entry in a diffLayer's disk journal.
type journalAccount struct { type journalAccount struct {
Hash common.Hash Hash common.Hash
@ -285,8 +292,8 @@ func iterateJournal(db ethdb.KeyValueReader, callback journalCallback) error {
log.Warn("Failed to resolve the journal version", "error", err) log.Warn("Failed to resolve the journal version", "error", err)
return errors.New("failed to resolve journal version") return errors.New("failed to resolve journal version")
} }
if version != journalVersion { if version != journalV0 && version != journalCurrentVersion {
log.Warn("Discarded the snapshot journal with wrong version", "required", journalVersion, "got", version) log.Warn("Discarded journal with wrong version", "required", journalCurrentVersion, "got", version)
return errors.New("wrong journal version") return errors.New("wrong journal version")
} }
// Secondly, resolve the disk layer root, ensure it's continuous // Secondly, resolve the disk layer root, ensure it's continuous
@ -316,6 +323,36 @@ func iterateJournal(db ethdb.KeyValueReader, callback journalCallback) error {
} }
return fmt.Errorf("load diff root: %v", err) return fmt.Errorf("load diff root: %v", err)
} }
// If a legacy journal is detected, decode the destruct set from the stream.
// The destruct set has been deprecated. If the journal contains non-empty
// destruct set, then it is deemed incompatible.
//
// Since self-destruction has been deprecated following the cancun fork,
// the destruct set is expected to be nil for layers above the fork block.
// However, an exception occurs during contract deployment: pre-funded accounts
// may self-destruct, causing accounts with non-zero balances to be removed
// from the state. For example,
// https://etherscan.io/tx/0xa087333d83f0cd63b96bdafb686462e1622ce25f40bd499e03efb1051f31fe49).
//
// For nodes with a fully synced state, the legacy journal is likely compatible
// with the updated definition, eliminating the need for regeneration. Unfortunately,
// nodes performing a full sync of historical chain segments or encountering
// pre-funded account deletions may face incompatibilities, leading to automatic
// snapshot regeneration.
//
// This approach minimizes snapshot regeneration for Geth nodes upgrading from a
// legacy version that are already synced. The workaround can be safely removed
// after the next hard fork.
if version == journalV0 {
var destructs []journalDestruct
if err := r.Decode(&destructs); err != nil {
return fmt.Errorf("load diff destructs: %v", err)
}
if len(destructs) > 0 {
log.Warn("Incompatible legacy journal detected", "version", journalV0)
return fmt.Errorf("incompatible legacy journal detected")
}
}
if err := r.Decode(&accounts); err != nil { if err := r.Decode(&accounts); err != nil {
return fmt.Errorf("load diff accounts: %v", err) return fmt.Errorf("load diff accounts: %v", err)
} }

View file

@ -664,7 +664,7 @@ func (t *Tree) Journal(root common.Hash) (common.Hash, error) {
// Firstly write out the metadata of journal // Firstly write out the metadata of journal
journal := new(bytes.Buffer) journal := new(bytes.Buffer)
if err := rlp.Encode(journal, journalVersion); err != nil { if err := rlp.Encode(journal, journalCurrentVersion); err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
diskroot := t.diskRoot() diskroot := t.diskRoot()

View file

@ -510,10 +510,13 @@ func (s *stateObject) Code() []byte {
if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) { if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) {
return nil return nil
} }
code, err := s.db.db.ContractCode(s.address, common.BytesToHash(s.CodeHash())) code, err := s.db.reader.Code(s.address, common.BytesToHash(s.CodeHash()))
if err != nil { if err != nil {
s.db.setError(fmt.Errorf("can't load code hash %x: %v", s.CodeHash(), err)) s.db.setError(fmt.Errorf("can't load code hash %x: %v", s.CodeHash(), err))
} }
if len(code) == 0 {
s.db.setError(fmt.Errorf("code is not found %x", s.CodeHash()))
}
s.code = code s.code = code
return code return code
} }
@ -528,10 +531,13 @@ func (s *stateObject) CodeSize() int {
if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) { if bytes.Equal(s.CodeHash(), types.EmptyCodeHash.Bytes()) {
return 0 return 0
} }
size, err := s.db.db.ContractCodeSize(s.address, common.BytesToHash(s.CodeHash())) size, err := s.db.reader.CodeSize(s.address, common.BytesToHash(s.CodeHash()))
if err != nil { if err != nil {
s.db.setError(fmt.Errorf("can't load code size %x: %v", s.CodeHash(), err)) s.db.setError(fmt.Errorf("can't load code size %x: %v", s.CodeHash(), err))
} }
if size == 0 {
s.db.setError(fmt.Errorf("code is not found %x", s.CodeHash()))
}
return size return size
} }

View file

@ -650,10 +650,11 @@ func (s *StateDB) CreateContract(addr common.Address) {
// Snapshots of the copied state cannot be applied to the copy. // Snapshots of the copied state cannot be applied to the copy.
func (s *StateDB) Copy() *StateDB { func (s *StateDB) Copy() *StateDB {
// Copy all the basic fields, initialize the memory ones // Copy all the basic fields, initialize the memory ones
reader, _ := s.db.Reader(s.originalRoot) // impossible to fail
state := &StateDB{ state := &StateDB{
db: s.db, db: s.db,
trie: mustCopyTrie(s.trie), trie: mustCopyTrie(s.trie),
reader: s.reader.Copy(), reader: reader,
originalRoot: s.originalRoot, originalRoot: s.originalRoot,
stateObjects: make(map[common.Address]*stateObject, len(s.stateObjects)), stateObjects: make(map[common.Address]*stateObject, len(s.stateObjects)),
stateObjectsDestruct: make(map[common.Address]*stateObject, len(s.stateObjectsDestruct)), stateObjectsDestruct: make(map[common.Address]*stateObject, len(s.stateObjectsDestruct)),

View file

@ -210,14 +210,18 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool, s
if err != nil { if err != nil {
t.Fatalf("state is not existent, %#x", srcRoot) t.Fatalf("state is not existent, %#x", srcRoot)
} }
cReader, err := srcDb.Reader(srcRoot)
if err != nil {
t.Fatalf("state is not existent, %#x", srcRoot)
}
for len(nodeElements)+len(codeElements) > 0 { for len(nodeElements)+len(codeElements) > 0 {
var ( var (
nodeResults = make([]trie.NodeSyncResult, len(nodeElements)) nodeResults = make([]trie.NodeSyncResult, len(nodeElements))
codeResults = make([]trie.CodeSyncResult, len(codeElements)) codeResults = make([]trie.CodeSyncResult, len(codeElements))
) )
for i, element := range codeElements { for i, element := range codeElements {
data, err := srcDb.ContractCode(common.Address{}, element.code) data, err := cReader.Code(common.Address{}, element.code)
if err != nil { if err != nil || len(data) == 0 {
t.Fatalf("failed to retrieve contract bytecode for hash %x", element.code) t.Fatalf("failed to retrieve contract bytecode for hash %x", element.code)
} }
codeResults[i] = trie.CodeSyncResult{Hash: element.code, Data: data} codeResults[i] = trie.CodeSyncResult{Hash: element.code, Data: data}
@ -329,6 +333,10 @@ func testIterativeDelayedStateSync(t *testing.T, scheme string) {
if err != nil { if err != nil {
t.Fatalf("state is not existent, %#x", srcRoot) t.Fatalf("state is not existent, %#x", srcRoot)
} }
cReader, err := srcDb.Reader(srcRoot)
if err != nil {
t.Fatalf("state is not existent, %#x", srcRoot)
}
for len(nodeElements)+len(codeElements) > 0 { for len(nodeElements)+len(codeElements) > 0 {
// Sync only half of the scheduled nodes // Sync only half of the scheduled nodes
var nodeProcessed int var nodeProcessed int
@ -336,8 +344,8 @@ func testIterativeDelayedStateSync(t *testing.T, scheme string) {
if len(codeElements) > 0 { if len(codeElements) > 0 {
codeResults := make([]trie.CodeSyncResult, len(codeElements)/2+1) codeResults := make([]trie.CodeSyncResult, len(codeElements)/2+1)
for i, element := range codeElements[:len(codeResults)] { for i, element := range codeElements[:len(codeResults)] {
data, err := srcDb.ContractCode(common.Address{}, element.code) data, err := cReader.Code(common.Address{}, element.code)
if err != nil { if err != nil || len(data) == 0 {
t.Fatalf("failed to retrieve contract bytecode for %x", element.code) t.Fatalf("failed to retrieve contract bytecode for %x", element.code)
} }
codeResults[i] = trie.CodeSyncResult{Hash: element.code, Data: data} codeResults[i] = trie.CodeSyncResult{Hash: element.code, Data: data}
@ -433,13 +441,17 @@ func testIterativeRandomStateSync(t *testing.T, count int, scheme string) {
if err != nil { if err != nil {
t.Fatalf("state is not existent, %#x", srcRoot) t.Fatalf("state is not existent, %#x", srcRoot)
} }
cReader, err := srcDb.Reader(srcRoot)
if err != nil {
t.Fatalf("state is not existent, %#x", srcRoot)
}
for len(nodeQueue)+len(codeQueue) > 0 { for len(nodeQueue)+len(codeQueue) > 0 {
// Fetch all the queued nodes in a random order // Fetch all the queued nodes in a random order
if len(codeQueue) > 0 { if len(codeQueue) > 0 {
results := make([]trie.CodeSyncResult, 0, len(codeQueue)) results := make([]trie.CodeSyncResult, 0, len(codeQueue))
for hash := range codeQueue { for hash := range codeQueue {
data, err := srcDb.ContractCode(common.Address{}, hash) data, err := cReader.Code(common.Address{}, hash)
if err != nil { if err != nil || len(data) == 0 {
t.Fatalf("failed to retrieve node data for %x", hash) t.Fatalf("failed to retrieve node data for %x", hash)
} }
results = append(results, trie.CodeSyncResult{Hash: hash, Data: data}) results = append(results, trie.CodeSyncResult{Hash: hash, Data: data})
@ -526,6 +538,10 @@ func testIterativeRandomDelayedStateSync(t *testing.T, scheme string) {
if err != nil { if err != nil {
t.Fatalf("state is not existent, %#x", srcRoot) t.Fatalf("state is not existent, %#x", srcRoot)
} }
cReader, err := srcDb.Reader(srcRoot)
if err != nil {
t.Fatalf("state is not existent, %#x", srcRoot)
}
for len(nodeQueue)+len(codeQueue) > 0 { for len(nodeQueue)+len(codeQueue) > 0 {
// Sync only half of the scheduled nodes, even those in random order // Sync only half of the scheduled nodes, even those in random order
if len(codeQueue) > 0 { if len(codeQueue) > 0 {
@ -533,8 +549,8 @@ func testIterativeRandomDelayedStateSync(t *testing.T, scheme string) {
for hash := range codeQueue { for hash := range codeQueue {
delete(codeQueue, hash) delete(codeQueue, hash)
data, err := srcDb.ContractCode(common.Address{}, hash) data, err := cReader.Code(common.Address{}, hash)
if err != nil { if err != nil || len(data) == 0 {
t.Fatalf("failed to retrieve node data for %x", hash) t.Fatalf("failed to retrieve node data for %x", hash)
} }
results = append(results, trie.CodeSyncResult{Hash: hash, Data: data}) results = append(results, trie.CodeSyncResult{Hash: hash, Data: data})
@ -631,6 +647,10 @@ func testIncompleteStateSync(t *testing.T, scheme string) {
if err != nil { if err != nil {
t.Fatalf("state is not available %x", srcRoot) t.Fatalf("state is not available %x", srcRoot)
} }
cReader, err := srcDb.Reader(srcRoot)
if err != nil {
t.Fatalf("state is not existent, %#x", srcRoot)
}
nodeQueue := make(map[string]stateElement) nodeQueue := make(map[string]stateElement)
codeQueue := make(map[common.Hash]struct{}) codeQueue := make(map[common.Hash]struct{})
paths, nodes, codes := sched.Missing(1) paths, nodes, codes := sched.Missing(1)
@ -649,8 +669,8 @@ func testIncompleteStateSync(t *testing.T, scheme string) {
if len(codeQueue) > 0 { if len(codeQueue) > 0 {
results := make([]trie.CodeSyncResult, 0, len(codeQueue)) results := make([]trie.CodeSyncResult, 0, len(codeQueue))
for hash := range codeQueue { for hash := range codeQueue {
data, err := srcDb.ContractCode(common.Address{}, hash) data, err := cReader.Code(common.Address{}, hash)
if err != nil { if err != nil || len(data) == 0 {
t.Fatalf("failed to retrieve node data for %x", hash) t.Fatalf("failed to retrieve node data for %x", hash)
} }
results = append(results, trie.CodeSyncResult{Hash: hash, Data: data}) results = append(results, trie.CodeSyncResult{Hash: hash, Data: data})
@ -713,6 +733,11 @@ func testIncompleteStateSync(t *testing.T, scheme string) {
// Sanity check that removing any node from the database is detected // Sanity check that removing any node from the database is detected
for _, node := range addedCodes { for _, node := range addedCodes {
val := rawdb.ReadCode(dstDb, node) val := rawdb.ReadCode(dstDb, node)
if len(val) == 0 {
t.Logf("no code: %v", node)
} else {
t.Logf("has code: %v", node)
}
rawdb.DeleteCode(dstDb, node) rawdb.DeleteCode(dstDb, node)
if err := checkStateConsistency(dstDb, ndb.Scheme(), srcRoot); err == nil { if err := checkStateConsistency(dstDb, ndb.Scheme(), srcRoot); err == nil {
t.Errorf("trie inconsistency not caught, missing: %x", node) t.Errorf("trie inconsistency not caught, missing: %x", node)

View file

@ -65,7 +65,10 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
return // Also invalid block, bail out return // Also invalid block, bail out
} }
statedb.SetTxContext(tx.Hash(), i) statedb.SetTxContext(tx.Hash(), i)
if err := precacheTransaction(msg, gaspool, evm); err != nil {
// We attempt to apply a transaction. The goal is not to execute
// the transaction successfully, rather to warm up touched data slots.
if _, err := ApplyMessage(evm, msg, gaspool); err != nil {
return // Ugh, something went horribly wrong, bail out return // Ugh, something went horribly wrong, bail out
} }
// If we're pre-byzantium, pre-load trie nodes for the intermediate root // If we're pre-byzantium, pre-load trie nodes for the intermediate root
@ -78,14 +81,3 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
statedb.IntermediateRoot(true) statedb.IntermediateRoot(true)
} }
} }
// precacheTransaction attempts to apply a transaction to the given state database
// and uses the input parameters for its environment. The goal is not to execute
// the transaction successfully, rather to warm up touched data slots.
func precacheTransaction(msg *Message, gaspool *GasPool, evm *vm.EVM) error {
// Update the evm with the new transaction context.
evm.SetTxContext(NewEVMTxContext(msg))
// Add addresses to access list if applicable
_, err := ApplyMessage(evm, msg, gaspool)
return err
}

View file

@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/core/state" "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/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -106,18 +107,15 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
// Read requests if Prague is enabled. // Read requests if Prague is enabled.
var requests [][]byte var requests [][]byte
if p.config.IsPrague(block.Number(), block.Time()) { if p.config.IsPrague(block.Number(), block.Time()) {
// EIP-6110 deposits requests = [][]byte{}
depositRequests, err := ParseDepositLogs(allLogs, p.config) // EIP-6110
if err != nil { if err := ParseDepositLogs(&requests, allLogs, p.config); err != nil {
return nil, err return nil, err
} }
requests = append(requests, depositRequests) // EIP-7002
// EIP-7002 withdrawals ProcessWithdrawalQueue(&requests, evm)
withdrawalRequests := ProcessWithdrawalQueue(evm) // EIP-7251
requests = append(requests, withdrawalRequests) ProcessConsolidationQueue(&requests, evm)
// EIP-7251 consolidations
consolidationRequests := ProcessConsolidationQueue(evm)
requests = append(requests, consolidationRequests)
} }
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards) // Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
@ -143,17 +141,11 @@ func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB,
defer func() { hooks.OnTxEnd(receipt, err) }() defer func() { hooks.OnTxEnd(receipt, err) }()
} }
} }
// Create a new context to be used in the EVM environment.
txContext := NewEVMTxContext(msg)
evm.SetTxContext(txContext)
// Apply the transaction to the current state (included in the env). // Apply the transaction to the current state (included in the env).
result, err := ApplyMessage(evm, msg, gp) result, err := ApplyMessage(evm, msg, gp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Update the state with pending changes. // Update the state with pending changes.
var root []byte var root []byte
if evm.ChainConfig().IsByzantium(blockNumber) { if evm.ChainConfig().IsByzantium(blockNumber) {
@ -221,9 +213,7 @@ func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *
// contract. This method is exported to be used in tests. // contract. This method is exported to be used in tests.
func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) { func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) {
if tracer := evm.Config.Tracer; tracer != nil { if tracer := evm.Config.Tracer; tracer != nil {
if tracer.OnSystemCallStart != nil { onSystemCallStart(tracer, evm.GetVMContext())
tracer.OnSystemCallStart()
}
if tracer.OnSystemCallEnd != nil { if tracer.OnSystemCallEnd != nil {
defer tracer.OnSystemCallEnd() defer tracer.OnSystemCallEnd()
} }
@ -247,9 +237,7 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) {
// as per EIP-2935. // as per EIP-2935.
func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) { func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) {
if tracer := evm.Config.Tracer; tracer != nil { if tracer := evm.Config.Tracer; tracer != nil {
if tracer.OnSystemCallStart != nil { onSystemCallStart(tracer, evm.GetVMContext())
tracer.OnSystemCallStart()
}
if tracer.OnSystemCallEnd != nil { if tracer.OnSystemCallEnd != nil {
defer tracer.OnSystemCallEnd() defer tracer.OnSystemCallEnd()
} }
@ -271,21 +259,19 @@ func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) {
// ProcessWithdrawalQueue calls the EIP-7002 withdrawal queue contract. // ProcessWithdrawalQueue calls the EIP-7002 withdrawal queue contract.
// It returns the opaque request data returned by the contract. // It returns the opaque request data returned by the contract.
func ProcessWithdrawalQueue(evm *vm.EVM) []byte { func ProcessWithdrawalQueue(requests *[][]byte, evm *vm.EVM) {
return processRequestsSystemCall(evm, 0x01, params.WithdrawalQueueAddress) processRequestsSystemCall(requests, evm, 0x01, params.WithdrawalQueueAddress)
} }
// ProcessConsolidationQueue calls the EIP-7251 consolidation queue contract. // ProcessConsolidationQueue calls the EIP-7251 consolidation queue contract.
// It returns the opaque request data returned by the contract. // It returns the opaque request data returned by the contract.
func ProcessConsolidationQueue(evm *vm.EVM) []byte { func ProcessConsolidationQueue(requests *[][]byte, evm *vm.EVM) {
return processRequestsSystemCall(evm, 0x02, params.ConsolidationQueueAddress) processRequestsSystemCall(requests, evm, 0x02, params.ConsolidationQueueAddress)
} }
func processRequestsSystemCall(evm *vm.EVM, requestType byte, addr common.Address) []byte { func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte, addr common.Address) {
if tracer := evm.Config.Tracer; tracer != nil { if tracer := evm.Config.Tracer; tracer != nil {
if tracer.OnSystemCallStart != nil { onSystemCallStart(tracer, evm.GetVMContext())
tracer.OnSystemCallStart()
}
if tracer.OnSystemCallEnd != nil { if tracer.OnSystemCallEnd != nil {
defer tracer.OnSystemCallEnd() defer tracer.OnSystemCallEnd()
} }
@ -302,26 +288,40 @@ func processRequestsSystemCall(evm *vm.EVM, requestType byte, addr common.Addres
evm.StateDB.AddAddressToAccessList(addr) evm.StateDB.AddAddressToAccessList(addr)
ret, _, _ := evm.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560) ret, _, _ := evm.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560)
evm.StateDB.Finalise(true) evm.StateDB.Finalise(true)
if len(ret) == 0 {
return // skip empty output
}
// Create withdrawals requestsData with prefix 0x01 // Append prefixed requestsData to the requests list.
requestsData := make([]byte, len(ret)+1) requestsData := make([]byte, len(ret)+1)
requestsData[0] = requestType requestsData[0] = requestType
copy(requestsData[1:], ret) copy(requestsData[1:], ret)
return requestsData *requests = append(*requests, requestsData)
} }
// ParseDepositLogs extracts the EIP-6110 deposit values from logs emitted by // ParseDepositLogs extracts the EIP-6110 deposit values from logs emitted by
// BeaconDepositContract. // BeaconDepositContract.
func ParseDepositLogs(logs []*types.Log, config *params.ChainConfig) ([]byte, error) { func ParseDepositLogs(requests *[][]byte, logs []*types.Log, config *params.ChainConfig) error {
deposits := make([]byte, 1) // note: first byte is 0x00 (== deposit request type) deposits := make([]byte, 1) // note: first byte is 0x00 (== deposit request type)
for _, log := range logs { for _, log := range logs {
if log.Address == config.DepositContractAddress { if log.Address == config.DepositContractAddress {
request, err := types.DepositLogToRequest(log.Data) request, err := types.DepositLogToRequest(log.Data)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to parse deposit data: %v", err) return fmt.Errorf("unable to parse deposit data: %v", err)
} }
deposits = append(deposits, request...) deposits = append(deposits, request...)
} }
} }
return deposits, nil if len(deposits) > 1 {
*requests = append(*requests, deposits)
}
return nil
}
func onSystemCallStart(tracer *tracing.Hooks, ctx *tracing.VMContext) {
if tracer.OnSystemCallStartV2 != nil {
tracer.OnSystemCallStartV2(ctx)
} else if tracer.OnSystemCallStart != nil {
tracer.OnSystemCallStart()
}
} }

View file

@ -187,10 +187,11 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
// indicates a core error meaning that the message would always fail for that particular // indicates a core error meaning that the message would always fail for that particular
// state and would never be accepted within a block. // state and would never be accepted within a block.
func ApplyMessage(evm *vm.EVM, msg *Message, gp *GasPool) (*ExecutionResult, error) { func ApplyMessage(evm *vm.EVM, msg *Message, gp *GasPool) (*ExecutionResult, error) {
return NewStateTransition(evm, msg, gp).TransitionDb() evm.SetTxContext(NewEVMTxContext(msg))
return newStateTransition(evm, msg, gp).execute()
} }
// StateTransition represents a state transition. // stateTransition represents a state transition.
// //
// == The State Transitioning Model // == The State Transitioning Model
// //
@ -212,7 +213,7 @@ func ApplyMessage(evm *vm.EVM, msg *Message, gp *GasPool) (*ExecutionResult, err
// //
// 5. Run Script section // 5. Run Script section
// 6. Derive new state root // 6. Derive new state root
type StateTransition struct { type stateTransition struct {
gp *GasPool gp *GasPool
msg *Message msg *Message
gasRemaining uint64 gasRemaining uint64
@ -221,9 +222,9 @@ type StateTransition struct {
evm *vm.EVM evm *vm.EVM
} }
// NewStateTransition initialises and returns a new state transition object. // newStateTransition initialises and returns a new state transition object.
func NewStateTransition(evm *vm.EVM, msg *Message, gp *GasPool) *StateTransition { func newStateTransition(evm *vm.EVM, msg *Message, gp *GasPool) *stateTransition {
return &StateTransition{ return &stateTransition{
gp: gp, gp: gp,
evm: evm, evm: evm,
msg: msg, msg: msg,
@ -232,14 +233,14 @@ func NewStateTransition(evm *vm.EVM, msg *Message, gp *GasPool) *StateTransition
} }
// to returns the recipient of the message. // to returns the recipient of the message.
func (st *StateTransition) to() common.Address { func (st *stateTransition) to() common.Address {
if st.msg == nil || st.msg.To == nil /* contract creation */ { if st.msg == nil || st.msg.To == nil /* contract creation */ {
return common.Address{} return common.Address{}
} }
return *st.msg.To return *st.msg.To
} }
func (st *StateTransition) buyGas() error { func (st *stateTransition) buyGas() error {
mgval := new(big.Int).SetUint64(st.msg.GasLimit) mgval := new(big.Int).SetUint64(st.msg.GasLimit)
mgval.Mul(mgval, st.msg.GasPrice) mgval.Mul(mgval, st.msg.GasPrice)
balanceCheck := new(big.Int).Set(mgval) balanceCheck := new(big.Int).Set(mgval)
@ -283,7 +284,7 @@ func (st *StateTransition) buyGas() error {
return nil return nil
} }
func (st *StateTransition) preCheck() error { func (st *stateTransition) preCheck() error {
// Only check transactions that are not fake // Only check transactions that are not fake
msg := st.msg msg := st.msg
if !msg.SkipNonceChecks { if !msg.SkipNonceChecks {
@ -368,7 +369,7 @@ func (st *StateTransition) preCheck() error {
return st.buyGas() return st.buyGas()
} }
// TransitionDb will transition the state by applying the current message and // execute will transition the state by applying the current message and
// returning the evm execution result with following fields. // returning the evm execution result with following fields.
// //
// - used gas: total gas used (including gas being refunded) // - used gas: total gas used (including gas being refunded)
@ -378,7 +379,7 @@ func (st *StateTransition) preCheck() error {
// //
// However if any consensus issue encountered, return the error directly with // However if any consensus issue encountered, return the error directly with
// nil evm execution result. // nil evm execution result.
func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { func (st *stateTransition) execute() (*ExecutionResult, error) {
// First check this message satisfies all consensus rules before // First check this message satisfies all consensus rules before
// applying the message. The rules include these clauses // applying the message. The rules include these clauses
// //
@ -493,7 +494,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
}, nil }, nil
} }
func (st *StateTransition) refundGas(refundQuotient uint64) uint64 { func (st *stateTransition) refundGas(refundQuotient uint64) uint64 {
// Apply refund counter, capped to a refund quotient // Apply refund counter, capped to a refund quotient
refund := st.gasUsed() / refundQuotient refund := st.gasUsed() / refundQuotient
if refund > st.state.GetRefund() { if refund > st.state.GetRefund() {
@ -523,11 +524,11 @@ func (st *StateTransition) refundGas(refundQuotient uint64) uint64 {
} }
// gasUsed returns the amount of gas used up by the state transition. // gasUsed returns the amount of gas used up by the state transition.
func (st *StateTransition) gasUsed() uint64 { func (st *stateTransition) gasUsed() uint64 {
return st.initialGas - st.gasRemaining return st.initialGas - st.gasRemaining
} }
// blobGasUsed returns the amount of blob gas used by the message. // blobGasUsed returns the amount of blob gas used by the message.
func (st *StateTransition) blobGasUsed() uint64 { func (st *stateTransition) blobGasUsed() uint64 {
return uint64(len(st.msg.BlobHashes) * params.BlobTxBlobGasPerBlob) return uint64(len(st.msg.BlobHashes) * params.BlobTxBlobGasPerBlob)
} }

View file

@ -40,7 +40,7 @@ import (
// - It cannot be placed outside of core, because it needs to construct a dud headerchain // - It cannot be placed outside of core, because it needs to construct a dud headerchain
// //
// TODO(karalabe): Would be nice to resolve both issues above somehow and move it. // TODO(karalabe): Would be nice to resolve both issues above somehow and move it.
func ExecuteStateless(config *params.ChainConfig, block *types.Block, witness *stateless.Witness) (common.Hash, common.Hash, error) { func ExecuteStateless(config *params.ChainConfig, vmconfig vm.Config, block *types.Block, witness *stateless.Witness) (common.Hash, common.Hash, error) {
// Sanity check if the supplied block accidentally contains a set root or // Sanity check if the supplied block accidentally contains a set root or
// receipt hash. If so, be very loud, but still continue. // receipt hash. If so, be very loud, but still continue.
if block.Root() != (common.Hash{}) { if block.Root() != (common.Hash{}) {
@ -66,7 +66,7 @@ func ExecuteStateless(config *params.ChainConfig, block *types.Block, witness *s
validator := NewBlockValidator(config, nil) // No chain, we only validate the state, not the block validator := NewBlockValidator(config, nil) // No chain, we only validate the state, not the block
// Run the stateless blocks processing and self-validate certain fields // Run the stateless blocks processing and self-validate certain fields
res, err := processor.Process(block, db, vm.Config{}) res, err := processor.Process(block, db, vmconfig)
if err != nil { if err != nil {
return common.Hash{}, common.Hash{}, err return common.Hash{}, common.Hash{}, err
} }

View file

@ -55,8 +55,7 @@ type VMContext struct {
BlockNumber *big.Int BlockNumber *big.Int
Time uint64 Time uint64
Random *common.Hash Random *common.Hash
// Effective tx gas price BaseFee *big.Int
GasPrice *big.Int
StateDB StateDB StateDB StateDB
} }
@ -146,6 +145,10 @@ type (
// will not be invoked. // will not be invoked.
OnSystemCallStartHook = func() OnSystemCallStartHook = func()
// OnSystemCallStartHookV2 is called when a system call is about to be executed. Refer
// to `OnSystemCallStartHook` for more information.
OnSystemCallStartHookV2 = func(vm *VMContext)
// OnSystemCallEndHook is called when a system call has finished executing. Today, // OnSystemCallEndHook is called when a system call has finished executing. Today,
// this hook is invoked when the EIP-4788 system call is about to be executed to set the // this hook is invoked when the EIP-4788 system call is about to be executed to set the
// beacon block root. // beacon block root.
@ -188,6 +191,7 @@ type Hooks struct {
OnSkippedBlock SkippedBlockHook OnSkippedBlock SkippedBlockHook
OnGenesisBlock GenesisBlockHook OnGenesisBlock GenesisBlockHook
OnSystemCallStart OnSystemCallStartHook OnSystemCallStart OnSystemCallStartHook
OnSystemCallStartV2 OnSystemCallStartHookV2
OnSystemCallEnd OnSystemCallEndHook OnSystemCallEnd OnSystemCallEndHook
// State events // State events
OnBalanceChange BalanceChangeHook OnBalanceChange BalanceChangeHook

View file

@ -463,10 +463,12 @@ func CalcRequestsHash(requests [][]byte) common.Hash {
h1, h2 := sha256.New(), sha256.New() h1, h2 := sha256.New(), sha256.New()
var buf common.Hash var buf common.Hash
for _, item := range requests { for _, item := range requests {
if len(item) > 1 { // skip items with only requestType and no data.
h1.Reset() h1.Reset()
h1.Write(item) h1.Write(item)
h2.Write(h1.Sum(buf[:0])) h2.Write(h1.Sum(buf[:0]))
} }
}
h2.Sum(buf[:0]) h2.Sum(buf[:0])
return buf return buf
} }

View file

@ -41,6 +41,9 @@ var (
// EmptyWithdrawalsHash is the known hash of the empty withdrawal set. // EmptyWithdrawalsHash is the known hash of the empty withdrawal set.
EmptyWithdrawalsHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") EmptyWithdrawalsHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
// EmptyRequestsHash is the known hash of an empty request set, sha256("").
EmptyRequestsHash = common.HexToHash("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
// EmptyVerkleHash is the known hash of an empty verkle trie. // EmptyVerkleHash is the known hash of an empty verkle trie.
EmptyVerkleHash = common.Hash{} EmptyVerkleHash = common.Hash{}
) )

View file

@ -605,7 +605,7 @@ func (evm *EVM) GetVMContext() *tracing.VMContext {
BlockNumber: evm.Context.BlockNumber, BlockNumber: evm.Context.BlockNumber,
Time: evm.Context.Time, Time: evm.Context.Time,
Random: evm.Context.Random, Random: evm.Context.Random,
GasPrice: evm.TxContext.GasPrice, BaseFee: evm.Context.BaseFee,
StateDB: evm.StateDB, StateDB: evm.StateDB,
} }
} }

View file

@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/asm" "github.com/ethereum/go-ethereum/core/asm"
"github.com/ethereum/go-ethereum/core/state" "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/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/core/vm/program" "github.com/ethereum/go-ethereum/core/vm/program"
@ -670,17 +671,23 @@ func TestColdAccountAccessCost(t *testing.T) {
want: 7600, want: 7600,
}, },
} { } {
tracer := logger.NewStructLogger(nil) var step = 0
var have = uint64(0)
Execute(tc.code, nil, &Config{ Execute(tc.code, nil, &Config{
EVMConfig: vm.Config{ EVMConfig: vm.Config{
Tracer: tracer.Hooks(), Tracer: &tracing.Hooks{
OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
// Uncomment to investigate failures:
//t.Logf("%d: %v %d", step, vm.OpCode(op).String(), cost)
if step == tc.step {
have = cost
}
step++
},
},
}, },
}) })
have := tracer.StructLogs()[tc.step].GasCost
if want := tc.want; have != want { if want := tc.want; have != want {
for ii, op := range tracer.StructLogs() {
t.Logf("%d: %v %d", ii, op.OpName(), op.GasCost)
}
t.Fatalf("testcase %d, gas report wrong, step %d, have %d want %d", i, tc.step, have, want) t.Fatalf("testcase %d, gas report wrong, step %d, have %d want %d", i, tc.step, have, want)
} }
} }

View file

@ -424,17 +424,17 @@ func (s *Ethereum) Stop() error {
// SyncMode retrieves the current sync mode, either explicitly set, or derived // SyncMode retrieves the current sync mode, either explicitly set, or derived
// from the chain status. // from the chain status.
func (s *Ethereum) SyncMode() downloader.SyncMode { func (s *Ethereum) SyncMode() ethconfig.SyncMode {
// If we're in snap sync mode, return that directly // If we're in snap sync mode, return that directly
if s.handler.snapSync.Load() { if s.handler.snapSync.Load() {
return downloader.SnapSync return ethconfig.SnapSync
} }
// We are probably in full sync, but we might have rewound to before the // We are probably in full sync, but we might have rewound to before the
// snap sync pivot, check if we should re-enable snap sync. // snap sync pivot, check if we should re-enable snap sync.
head := s.blockchain.CurrentBlock() head := s.blockchain.CurrentBlock()
if pivot := rawdb.ReadLastPivotNumber(s.chainDb); pivot != nil { if pivot := rawdb.ReadLastPivotNumber(s.chainDb); pivot != nil {
if head.Number.Uint64() < *pivot { if head.Number.Uint64() < *pivot {
return downloader.SnapSync return ethconfig.SnapSync
} }
} }
// We are in a full sync, but the associated head state is missing. To complete // We are in a full sync, but the associated head state is missing. To complete
@ -442,8 +442,8 @@ func (s *Ethereum) SyncMode() downloader.SyncMode {
// persistent state is corrupted, just mismatch with the head block. // persistent state is corrupted, just mismatch with the head block.
if !s.blockchain.HasState(head.Root) { if !s.blockchain.HasState(head.Root) {
log.Info("Reenabled snap sync as chain is stateless") log.Info("Reenabled snap sync as chain is stateless")
return downloader.SnapSync return ethconfig.SnapSync
} }
// Nope, we're really full syncing // Nope, we're really full syncing
return downloader.FullSync return ethconfig.FullSync
} }

View file

@ -31,8 +31,9 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/stateless" "github.com/ethereum/go-ethereum/core/stateless"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/internal/version" "github.com/ethereum/go-ethereum/internal/version"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/miner"
@ -917,7 +918,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
// tries to make it import a block. That should be denied as pushing something // tries to make it import a block. That should be denied as pushing something
// into the database directly will conflict with the assumptions of snap sync // into the database directly will conflict with the assumptions of snap sync
// that it has an empty db that it can fill itself. // that it has an empty db that it can fill itself.
if api.eth.SyncMode() != downloader.FullSync { if api.eth.SyncMode() != ethconfig.FullSync {
return api.delayPayloadImport(block), nil return api.delayPayloadImport(block), nil
} }
if !api.eth.BlockChain().HasBlockAndState(block.ParentHash(), block.NumberU64()-1) { if !api.eth.BlockChain().HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
@ -995,7 +996,7 @@ func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, v
api.lastNewPayloadLock.Unlock() api.lastNewPayloadLock.Unlock()
log.Trace("Executing block statelessly", "number", block.Number(), "hash", params.BlockHash) log.Trace("Executing block statelessly", "number", block.Number(), "hash", params.BlockHash)
stateRoot, receiptRoot, err := core.ExecuteStateless(api.eth.BlockChain().Config(), block, witness) stateRoot, receiptRoot, err := core.ExecuteStateless(api.eth.BlockChain().Config(), vm.Config{}, block, witness)
if err != nil { if err != nil {
log.Warn("ExecuteStatelessPayload: execution failed", "err", err) log.Warn("ExecuteStatelessPayload: execution failed", "err", err)
errorMsg := err.Error() errorMsg := err.Error()
@ -1030,7 +1031,7 @@ func (api *ConsensusAPI) delayPayloadImport(block *types.Block) engine.PayloadSt
// payload as non-integratable on top of the existing sync. We'll just // payload as non-integratable on top of the existing sync. We'll just
// have to rely on the beacon client to forcefully update the head with // have to rely on the beacon client to forcefully update the head with
// a forkchoice update request. // a forkchoice update request.
if api.eth.SyncMode() == downloader.FullSync { if api.eth.SyncMode() == ethconfig.FullSync {
// In full sync mode, failure to import a well-formed block can only mean // In full sync mode, failure to import a well-formed block can only mean
// that the parent state is missing and the syncer rejected extending the // that the parent state is missing and the syncer rejected extending the
// current cycle with the new payload. // current cycle with the new payload.

View file

@ -40,7 +40,6 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/internal/version" "github.com/ethereum/go-ethereum/internal/version"
"github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/miner"
@ -452,7 +451,7 @@ func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block)
} }
mcfg := miner.DefaultConfig mcfg := miner.DefaultConfig
ethcfg := &ethconfig.Config{Genesis: genesis, SyncMode: downloader.FullSync, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256, Miner: mcfg} ethcfg := &ethconfig.Config{Genesis: genesis, SyncMode: ethconfig.FullSync, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256, Miner: mcfg}
ethservice, err := eth.New(n, ethcfg) ethservice, err := eth.New(n, ethcfg)
if err != nil { if err != nil {
t.Fatal("can't create eth service:", err) t.Fatal("can't create eth service:", err)

View file

@ -27,7 +27,6 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
@ -49,7 +48,7 @@ func startSimulatedBeaconEthService(t *testing.T, genesis *core.Genesis, period
t.Fatal("can't create node:", err) t.Fatal("can't create node:", err)
} }
ethcfg := &ethconfig.Config{Genesis: genesis, SyncMode: downloader.FullSync, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256, Miner: miner.DefaultConfig} ethcfg := &ethconfig.Config{Genesis: genesis, SyncMode: ethconfig.FullSync, TrieTimeout: time.Minute, TrieDirtyCache: 256, TrieCleanCache: 256, Miner: miner.DefaultConfig}
ethservice, err := eth.New(n, ethcfg) ethservice, err := eth.New(n, ethcfg)
if err != nil { if err != nil {
t.Fatal("can't create eth service:", err) t.Fatal("can't create eth service:", err)

View file

@ -22,7 +22,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
) )
@ -62,7 +62,7 @@ func (tester *FullSyncTester) Start() error {
// Trigger beacon sync with the provided block hash as trusted // Trigger beacon sync with the provided block hash as trusted
// chain head. // chain head.
err := tester.backend.Downloader().BeaconDevSync(downloader.FullSync, tester.target, tester.closed) err := tester.backend.Downloader().BeaconDevSync(ethconfig.FullSync, tester.target, tester.closed)
if err != nil { if err != nil {
log.Info("Failed to trigger beacon sync", "err", err) log.Info("Failed to trigger beacon sync", "err", err)
} }

View file

@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
@ -198,9 +199,9 @@ func (d *Downloader) findBeaconAncestor() (uint64, error) {
var chainHead *types.Header var chainHead *types.Header
switch d.getMode() { switch d.getMode() {
case FullSync: case ethconfig.FullSync:
chainHead = d.blockchain.CurrentBlock() chainHead = d.blockchain.CurrentBlock()
case SnapSync: case ethconfig.SnapSync:
chainHead = d.blockchain.CurrentSnapBlock() chainHead = d.blockchain.CurrentSnapBlock()
default: default:
panic("unknown sync mode") panic("unknown sync mode")
@ -218,9 +219,9 @@ func (d *Downloader) findBeaconAncestor() (uint64, error) {
} }
var linked bool var linked bool
switch d.getMode() { switch d.getMode() {
case FullSync: case ethconfig.FullSync:
linked = d.blockchain.HasBlock(beaconTail.ParentHash, beaconTail.Number.Uint64()-1) linked = d.blockchain.HasBlock(beaconTail.ParentHash, beaconTail.Number.Uint64()-1)
case SnapSync: case ethconfig.SnapSync:
linked = d.blockchain.HasFastBlock(beaconTail.ParentHash, beaconTail.Number.Uint64()-1) linked = d.blockchain.HasFastBlock(beaconTail.ParentHash, beaconTail.Number.Uint64()-1)
default: default:
panic("unknown sync mode") panic("unknown sync mode")
@ -253,9 +254,9 @@ func (d *Downloader) findBeaconAncestor() (uint64, error) {
var known bool var known bool
switch d.getMode() { switch d.getMode() {
case FullSync: case ethconfig.FullSync:
known = d.blockchain.HasBlock(h.Hash(), n) known = d.blockchain.HasBlock(h.Hash(), n)
case SnapSync: case ethconfig.SnapSync:
known = d.blockchain.HasFastBlock(h.Hash(), n) known = d.blockchain.HasFastBlock(h.Hash(), n)
default: default:
panic("unknown sync mode") panic("unknown sync mode")

View file

@ -30,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state/snapshot" "github.com/ethereum/go-ethereum/core/state/snapshot"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/eth/protocols/snap" "github.com/ethereum/go-ethereum/eth/protocols/snap"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
@ -69,6 +70,17 @@ var (
errNoPivotHeader = errors.New("pivot header is not found") errNoPivotHeader = errors.New("pivot header is not found")
) )
// SyncMode defines the sync method of the downloader.
// Deprecated: use ethconfig.SyncMode instead
type SyncMode = ethconfig.SyncMode
const (
// Deprecated: use ethconfig.FullSync
FullSync = ethconfig.FullSync
// Deprecated: use ethconfig.SnapSync
SnapSync = ethconfig.SnapSync
)
// peerDropFn is a callback type for dropping a peer detected as malicious. // peerDropFn is a callback type for dropping a peer detected as malicious.
type peerDropFn func(id string) type peerDropFn func(id string)
@ -230,9 +242,9 @@ func (d *Downloader) Progress() ethereum.SyncProgress {
current := uint64(0) current := uint64(0)
mode := d.getMode() mode := d.getMode()
switch mode { switch mode {
case FullSync: case ethconfig.FullSync:
current = d.blockchain.CurrentBlock().Number.Uint64() current = d.blockchain.CurrentBlock().Number.Uint64()
case SnapSync: case ethconfig.SnapSync:
current = d.blockchain.CurrentSnapBlock().Number.Uint64() current = d.blockchain.CurrentSnapBlock().Number.Uint64()
default: default:
log.Error("Unknown downloader mode", "mode", mode) log.Error("Unknown downloader mode", "mode", mode)
@ -326,7 +338,7 @@ func (d *Downloader) synchronise(mode SyncMode, beaconPing chan struct{}) error
if d.notified.CompareAndSwap(false, true) { if d.notified.CompareAndSwap(false, true) {
log.Info("Block synchronisation started") log.Info("Block synchronisation started")
} }
if mode == SnapSync { if mode == ethconfig.SnapSync {
// Snap sync will directly modify the persistent state, making the entire // Snap sync will directly modify the persistent state, making the entire
// trie database unusable until the state is fully synced. To prevent any // trie database unusable until the state is fully synced. To prevent any
// subsequent state reads, explicitly disable the trie database and state // subsequent state reads, explicitly disable the trie database and state
@ -434,7 +446,7 @@ func (d *Downloader) syncToHead() (err error) {
// threshold (i.e. new chain). In that case we won't really snap sync // threshold (i.e. new chain). In that case we won't really snap sync
// anyway, but still need a valid pivot block to avoid some code hitting // anyway, but still need a valid pivot block to avoid some code hitting
// nil panics on access. // nil panics on access.
if mode == SnapSync && pivot == nil { if mode == ethconfig.SnapSync && pivot == nil {
pivot = d.blockchain.CurrentBlock() pivot = d.blockchain.CurrentBlock()
} }
height := latest.Number.Uint64() height := latest.Number.Uint64()
@ -452,7 +464,7 @@ func (d *Downloader) syncToHead() (err error) {
d.syncStatsLock.Unlock() d.syncStatsLock.Unlock()
// Ensure our origin point is below any snap sync pivot point // Ensure our origin point is below any snap sync pivot point
if mode == SnapSync { if mode == ethconfig.SnapSync {
if height <= uint64(fsMinFullBlocks) { if height <= uint64(fsMinFullBlocks) {
origin = 0 origin = 0
} else { } else {
@ -466,10 +478,10 @@ func (d *Downloader) syncToHead() (err error) {
} }
} }
d.committed.Store(true) d.committed.Store(true)
if mode == SnapSync && pivot.Number.Uint64() != 0 { if mode == ethconfig.SnapSync && pivot.Number.Uint64() != 0 {
d.committed.Store(false) d.committed.Store(false)
} }
if mode == SnapSync { if mode == ethconfig.SnapSync {
// Set the ancient data limitation. If we are running snap sync, all block // Set the ancient data limitation. If we are running snap sync, all block
// data older than ancientLimit will be written to the ancient store. More // data older than ancientLimit will be written to the ancient store. More
// recent data will be written to the active database and will wait for the // recent data will be written to the active database and will wait for the
@ -523,13 +535,13 @@ func (d *Downloader) syncToHead() (err error) {
func() error { return d.fetchReceipts(origin + 1) }, // Receipts are retrieved during snap sync func() error { return d.fetchReceipts(origin + 1) }, // Receipts are retrieved during snap sync
func() error { return d.processHeaders(origin + 1) }, func() error { return d.processHeaders(origin + 1) },
} }
if mode == SnapSync { if mode == ethconfig.SnapSync {
d.pivotLock.Lock() d.pivotLock.Lock()
d.pivotHeader = pivot d.pivotHeader = pivot
d.pivotLock.Unlock() d.pivotLock.Unlock()
fetchers = append(fetchers, func() error { return d.processSnapSyncContent() }) fetchers = append(fetchers, func() error { return d.processSnapSyncContent() })
} else if mode == FullSync { } else if mode == ethconfig.FullSync {
fetchers = append(fetchers, func() error { return d.processFullSyncContent() }) fetchers = append(fetchers, func() error { return d.processFullSyncContent() })
} }
return d.spawnSync(fetchers) return d.spawnSync(fetchers)
@ -676,7 +688,7 @@ func (d *Downloader) processHeaders(origin uint64) error {
chunkHashes := hashes[:limit] chunkHashes := hashes[:limit]
// In case of header only syncing, validate the chunk immediately // In case of header only syncing, validate the chunk immediately
if mode == SnapSync { if mode == ethconfig.SnapSync {
// Although the received headers might be all valid, a legacy // Although the received headers might be all valid, a legacy
// PoW/PoA sync must not accept post-merge headers. Make sure // PoW/PoA sync must not accept post-merge headers. Make sure
// that any transition is rejected at this point. // that any transition is rejected at this point.

View file

@ -30,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/common/prque" "github.com/ethereum/go-ethereum/common/prque"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
@ -180,7 +181,7 @@ func (q *queue) Reset(blockCacheLimit int, thresholdInitialSize int) {
defer q.lock.Unlock() defer q.lock.Unlock()
q.closed = false q.closed = false
q.mode = FullSync q.mode = ethconfig.FullSync
q.headerHead = common.Hash{} q.headerHead = common.Hash{}
q.headerPendPool = make(map[string]*fetchRequest) q.headerPendPool = make(map[string]*fetchRequest)
@ -328,7 +329,7 @@ func (q *queue) Schedule(headers []*types.Header, hashes []common.Hash, from uin
q.blockTaskQueue.Push(header, -int64(header.Number.Uint64())) q.blockTaskQueue.Push(header, -int64(header.Number.Uint64()))
} }
// Queue for receipt retrieval // Queue for receipt retrieval
if q.mode == SnapSync && !header.EmptyReceipts() { if q.mode == ethconfig.SnapSync && !header.EmptyReceipts() {
if _, ok := q.receiptTaskPool[hash]; ok { if _, ok := q.receiptTaskPool[hash]; ok {
log.Warn("Header already scheduled for receipt fetch", "number", header.Number, "hash", hash) log.Warn("Header already scheduled for receipt fetch", "number", header.Number, "hash", hash)
} else { } else {
@ -523,7 +524,7 @@ func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common
// we can ask the resultcache if this header is within the // we can ask the resultcache if this header is within the
// "prioritized" segment of blocks. If it is not, we need to throttle // "prioritized" segment of blocks. If it is not, we need to throttle
stale, throttle, item, err := q.resultCache.AddFetch(header, q.mode == SnapSync) stale, throttle, item, err := q.resultCache.AddFetch(header, q.mode == ethconfig.SnapSync)
if stale { if stale {
// Don't put back in the task queue, this item has already been // Don't put back in the task queue, this item has already been
// delivered upstream // delivered upstream

View file

@ -29,7 +29,6 @@ import (
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/txpool/blobpool" "github.com/ethereum/go-ethereum/core/txpool/blobpool"
"github.com/ethereum/go-ethereum/core/txpool/legacypool" "github.com/ethereum/go-ethereum/core/txpool/legacypool"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
@ -49,7 +48,7 @@ var FullNodeGPO = gasprice.Config{
// Defaults contains default settings for use on the Ethereum main net. // Defaults contains default settings for use on the Ethereum main net.
var Defaults = Config{ var Defaults = Config{
SyncMode: downloader.SnapSync, SyncMode: SnapSync,
NetworkId: 0, // enable auto configuration of networkID == chainID NetworkId: 0, // enable auto configuration of networkID == chainID
TxLookupLimit: 2350000, TxLookupLimit: 2350000,
TransactionHistory: 2350000, TransactionHistory: 2350000,
@ -80,7 +79,7 @@ type Config struct {
// Network ID separates blockchains on the peer-to-peer networking level. When left // Network ID separates blockchains on the peer-to-peer networking level. When left
// zero, the chain ID is used as network ID. // zero, the chain ID is used as network ID.
NetworkId uint64 NetworkId uint64
SyncMode downloader.SyncMode SyncMode SyncMode
// This can be set to list of enrtree:// URLs which will be queried for // This can be set to list of enrtree:// URLs which will be queried for
// nodes to connect to. // nodes to connect to.

View file

@ -9,7 +9,6 @@ import (
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/txpool/blobpool" "github.com/ethereum/go-ethereum/core/txpool/blobpool"
"github.com/ethereum/go-ethereum/core/txpool/legacypool" "github.com/ethereum/go-ethereum/core/txpool/legacypool"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/miner"
) )
@ -19,7 +18,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
type Config struct { type Config struct {
Genesis *core.Genesis `toml:",omitempty"` Genesis *core.Genesis `toml:",omitempty"`
NetworkId uint64 NetworkId uint64
SyncMode downloader.SyncMode SyncMode SyncMode
EthDiscoveryURLs []string EthDiscoveryURLs []string
SnapDiscoveryURLs []string SnapDiscoveryURLs []string
NoPruning bool NoPruning bool
@ -95,7 +94,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
type Config struct { type Config struct {
Genesis *core.Genesis `toml:",omitempty"` Genesis *core.Genesis `toml:",omitempty"`
NetworkId *uint64 NetworkId *uint64
SyncMode *downloader.SyncMode SyncMode *SyncMode
EthDiscoveryURLs []string EthDiscoveryURLs []string
SnapDiscoveryURLs []string SnapDiscoveryURLs []string
NoPruning *bool NoPruning *bool

View file

@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License // You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package downloader package ethconfig
import "fmt" import "fmt"

View file

@ -217,21 +217,19 @@ func execute(ctx context.Context, call *core.Message, opts *Options, gasLimit ui
func run(ctx context.Context, call *core.Message, opts *Options) (*core.ExecutionResult, error) { func run(ctx context.Context, call *core.Message, opts *Options) (*core.ExecutionResult, error) {
// Assemble the call and the call context // Assemble the call and the call context
var ( var (
msgContext = core.NewEVMTxContext(call)
evmContext = core.NewEVMBlockContext(opts.Header, opts.Chain, nil) evmContext = core.NewEVMBlockContext(opts.Header, opts.Chain, nil)
dirtyState = opts.State.Copy() dirtyState = opts.State.Copy()
) )
// Lower the basefee to 0 to avoid breaking EVM // Lower the basefee to 0 to avoid breaking EVM
// invariants (basefee < feecap). // invariants (basefee < feecap).
if msgContext.GasPrice.Sign() == 0 { if call.GasPrice.Sign() == 0 {
evmContext.BaseFee = new(big.Int) evmContext.BaseFee = new(big.Int)
} }
if msgContext.BlobFeeCap != nil && msgContext.BlobFeeCap.BitLen() == 0 { if call.BlobGasFeeCap != nil && call.BlobGasFeeCap.BitLen() == 0 {
evmContext.BlobBaseFee = new(big.Int) evmContext.BlobBaseFee = new(big.Int)
} }
evm := vm.NewEVM(evmContext, dirtyState, opts.Config, vm.Config{NoBaseFee: true}) evm := vm.NewEVM(evmContext, dirtyState, opts.Config, vm.Config{NoBaseFee: true})
evm.SetTxContext(msgContext)
// Monitor the outer context and interrupt the EVM upon cancellation. To avoid // Monitor the outer context and interrupt the EVM upon cancellation. To avoid
// a dangling goroutine until the outer estimation finishes, create an internal // a dangling goroutine until the outer estimation finishes, create an internal
// context for the lifetime of this method call. // context for the lifetime of this method call.

View file

@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/eth/fetcher" "github.com/ethereum/go-ethereum/eth/fetcher"
"github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/eth/protocols/eth"
"github.com/ethereum/go-ethereum/eth/protocols/snap" "github.com/ethereum/go-ethereum/eth/protocols/snap"
@ -87,7 +88,7 @@ type handlerConfig struct {
Chain *core.BlockChain // Blockchain to serve data from Chain *core.BlockChain // Blockchain to serve data from
TxPool txPool // Transaction pool to propagate from TxPool txPool // Transaction pool to propagate from
Network uint64 // Network identifier to advertise Network uint64 // Network identifier to advertise
Sync downloader.SyncMode // Whether to snap or full sync Sync ethconfig.SyncMode // Whether to snap or full sync
BloomCache uint64 // Megabytes to alloc for snap sync bloom BloomCache uint64 // Megabytes to alloc for snap sync bloom
EventMux *event.TypeMux // Legacy event mux, deprecate for `feed` EventMux *event.TypeMux // Legacy event mux, deprecate for `feed`
RequiredBlocks map[uint64]common.Hash // Hard coded map of required block hashes for sync challenges RequiredBlocks map[uint64]common.Hash // Hard coded map of required block hashes for sync challenges
@ -145,7 +146,7 @@ func newHandler(config *handlerConfig) (*handler, error) {
handlerDoneCh: make(chan struct{}), handlerDoneCh: make(chan struct{}),
handlerStartCh: make(chan struct{}), handlerStartCh: make(chan struct{}),
} }
if config.Sync == downloader.FullSync { if config.Sync == ethconfig.FullSync {
// The database seems empty as the current block is the genesis. Yet the snap // The database seems empty as the current block is the genesis. Yet the snap
// block is ahead, so snap sync was enabled for this node at a certain point. // block is ahead, so snap sync was enabled for this node at a certain point.
// The scenarios where this can happen is // The scenarios where this can happen is

View file

@ -29,7 +29,7 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/eth/protocols/eth"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
@ -109,7 +109,7 @@ func testForkIDSplit(t *testing.T, protocol uint) {
Chain: chainNoFork, Chain: chainNoFork,
TxPool: newTestTxPool(), TxPool: newTestTxPool(),
Network: 1, Network: 1,
Sync: downloader.FullSync, Sync: ethconfig.FullSync,
BloomCache: 1, BloomCache: 1,
}) })
ethProFork, _ = newHandler(&handlerConfig{ ethProFork, _ = newHandler(&handlerConfig{
@ -117,7 +117,7 @@ func testForkIDSplit(t *testing.T, protocol uint) {
Chain: chainProFork, Chain: chainProFork,
TxPool: newTestTxPool(), TxPool: newTestTxPool(),
Network: 1, Network: 1,
Sync: downloader.FullSync, Sync: ethconfig.FullSync,
BloomCache: 1, BloomCache: 1,
}) })
) )

View file

@ -29,7 +29,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
@ -164,7 +164,7 @@ func newTestHandlerWithBlocks(blocks int) *testHandler {
Chain: chain, Chain: chain,
TxPool: txpool, TxPool: txpool,
Network: 1, Network: 1,
Sync: downloader.SnapSync, Sync: ethconfig.SnapSync,
BloomCache: 1, BloomCache: 1,
}) })
handler.Start(1000) handler.Start(1000)

View file

@ -454,7 +454,7 @@ func ServiceGetByteCodesQuery(chain *core.BlockChain, req *GetByteCodesPacket) [
// Peers should not request the empty code, but if they do, at // Peers should not request the empty code, but if they do, at
// least sent them back a correct response without db lookups // least sent them back a correct response without db lookups
codes = append(codes, []byte{}) codes = append(codes, []byte{})
} else if blob, err := chain.ContractCodeWithPrefix(hash); err == nil { } else if blob := chain.ContractCodeWithPrefix(hash); len(blob) > 0 {
codes = append(codes, blob) codes = append(codes, blob)
bytes += uint64(len(blob)) bytes += uint64(len(blob))
} }

View file

@ -255,8 +255,6 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block,
} }
// Assemble the transaction call message and return if the requested offset // Assemble the transaction call message and return if the requested offset
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
txContext := core.NewEVMTxContext(msg)
evm.SetTxContext(txContext)
// Not yet the searched for transaction, execute on top of the current state // Not yet the searched for transaction, execute on top of the current state
statedb.SetTxContext(tx.Hash(), idx) statedb.SetTxContext(tx.Hash(), idx)

View file

@ -20,7 +20,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/eth/protocols/eth"
"github.com/ethereum/go-ethereum/eth/protocols/snap" "github.com/ethereum/go-ethereum/eth/protocols/snap"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
@ -85,7 +85,7 @@ func testSnapSyncDisabling(t *testing.T, ethVer uint, snapVer uint) {
time.Sleep(250 * time.Millisecond) time.Sleep(250 * time.Millisecond)
// Check that snap sync was disabled // Check that snap sync was disabled
if err := empty.handler.downloader.BeaconSync(downloader.SnapSync, full.chain.CurrentBlock(), nil); err != nil { if err := empty.handler.downloader.BeaconSync(ethconfig.SnapSync, full.chain.CurrentBlock(), nil); err != nil {
t.Fatal("sync failed:", err) t.Fatal("sync failed:", err)
} }
empty.handler.enableSyncedFeatures() empty.handler.enableSyncedFeatures()

View file

@ -546,11 +546,7 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config
if err := ctx.Err(); err != nil { if err := ctx.Err(); err != nil {
return nil, err return nil, err
} }
var ( msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
msg, _ = core.TransactionToMessage(tx, signer, block.BaseFee())
txContext = core.NewEVMTxContext(msg)
)
evm.SetTxContext(txContext)
statedb.SetTxContext(tx.Hash(), i) statedb.SetTxContext(tx.Hash(), i)
if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil { if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil {
log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err) log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err)
@ -708,7 +704,6 @@ txloop:
// Generate the next state snapshot fast without tracing // Generate the next state snapshot fast without tracing
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
statedb.SetTxContext(tx.Hash(), i) statedb.SetTxContext(tx.Hash(), i)
evm.SetTxContext(core.NewEVMTxContext(msg))
if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil { if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil {
failed = err failed = err
break txloop break txloop
@ -793,7 +788,6 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
// Prepare the transaction for un-traced execution // Prepare the transaction for un-traced execution
var ( var (
msg, _ = core.TransactionToMessage(tx, signer, block.BaseFee()) msg, _ = core.TransactionToMessage(tx, signer, block.BaseFee())
txContext = core.NewEVMTxContext(msg)
vmConf vm.Config vmConf vm.Config
dump *os.File dump *os.File
writer *bufio.Writer writer *bufio.Writer
@ -820,7 +814,6 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
} }
} }
// Execute the transaction and flush any traces to disk // Execute the transaction and flush any traces to disk
evm.SetTxContext(txContext)
statedb.SetTxContext(tx.Hash(), i) statedb.SetTxContext(tx.Hash(), i)
if vmConf.Tracer.OnTxStart != nil { if vmConf.Tracer.OnTxStart != nil {
vmConf.Tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) vmConf.Tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
@ -1014,9 +1007,8 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor
return nil, err return nil, err
} }
} }
// The actual TxContext will be created as part of ApplyTransactionWithEVM. tracingStateDB := state.NewHookedState(statedb, tracer.Hooks)
evm := vm.NewEVM(vmctx, statedb, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true}) evm := vm.NewEVM(vmctx, tracingStateDB, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true})
evm.SetTxContext(vm.TxContext{GasPrice: message.GasPrice, BlobFeeCap: message.BlobGasFeeCap})
// Define a meaningful timeout of a single transaction trace // Define a meaningful timeout of a single transaction trace
if config.Timeout != nil { if config.Timeout != nil {

View file

@ -37,6 +37,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/tracing"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -177,8 +178,6 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block
return tx, context, statedb, release, nil return tx, context, statedb, release, nil
} }
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
txContext := core.NewEVMTxContext(msg)
evm.SetTxContext(txContext)
if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
} }
@ -187,6 +186,94 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block
return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
} }
type stateTracer struct {
Balance map[common.Address]*hexutil.Big
Nonce map[common.Address]hexutil.Uint64
Storage map[common.Address]map[common.Hash]common.Hash
}
func newStateTracer(ctx *Context, cfg json.RawMessage, chainCfg *params.ChainConfig) (*Tracer, error) {
t := &stateTracer{
Balance: make(map[common.Address]*hexutil.Big),
Nonce: make(map[common.Address]hexutil.Uint64),
Storage: make(map[common.Address]map[common.Hash]common.Hash),
}
return &Tracer{
GetResult: func() (json.RawMessage, error) {
return json.Marshal(t)
},
Hooks: &tracing.Hooks{
OnBalanceChange: func(addr common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) {
t.Balance[addr] = (*hexutil.Big)(new)
},
OnNonceChange: func(addr common.Address, prev, new uint64) {
t.Nonce[addr] = hexutil.Uint64(new)
},
OnStorageChange: func(addr common.Address, slot common.Hash, prev, new common.Hash) {
if t.Storage[addr] == nil {
t.Storage[addr] = make(map[common.Hash]common.Hash)
}
t.Storage[addr][slot] = new
},
},
}, nil
}
func TestStateHooks(t *testing.T) {
t.Parallel()
// Initialize test accounts
var (
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
from = crypto.PubkeyToAddress(key.PublicKey)
to = common.HexToAddress("0x00000000000000000000000000000000deadbeef")
genesis = &core.Genesis{
Config: params.TestChainConfig,
Alloc: types.GenesisAlloc{
from: {Balance: big.NewInt(params.Ether)},
to: {
Code: []byte{
byte(vm.PUSH1), 0x2a, // stack: [42]
byte(vm.PUSH1), 0x0, // stack: [0, 42]
byte(vm.SSTORE), // stack: []
byte(vm.STOP),
},
},
},
}
genBlocks = 2
signer = types.HomesteadSigner{}
nonce = uint64(0)
backend = newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) {
// Transfer from account[0] to account[1]
// value: 1000 wei
// fee: 0 wei
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
Nonce: nonce,
To: &to,
Value: big.NewInt(1000),
Gas: params.TxGas,
GasPrice: b.BaseFee(),
Data: nil}),
signer, key)
b.AddTx(tx)
nonce++
})
)
defer backend.teardown()
DefaultDirectory.Register("stateTracer", newStateTracer, false)
api := NewAPI(backend)
tracer := "stateTracer"
res, err := api.TraceCall(context.Background(), ethapi.TransactionArgs{From: &from, To: &to, Value: (*hexutil.Big)(big.NewInt(1000))}, rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber), &TraceCallConfig{TraceConfig: TraceConfig{Tracer: &tracer}})
if err != nil {
t.Fatalf("failed to trace call: %v", err)
}
expected := `{"Balance":{"0x00000000000000000000000000000000deadbeef":"0x3e8","0x71562b71999873db5b286df957af199ec94617f7":"0xde0975924ed6f90"},"Nonce":{"0x71562b71999873db5b286df957af199ec94617f7":"0x3"},"Storage":{"0x00000000000000000000000000000000deadbeef":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x000000000000000000000000000000000000000000000000000000000000002a"}}}`
if expected != fmt.Sprintf("%s", res) {
t.Fatalf("unexpected trace result: have %s want %s", res, expected)
}
}
func TestTraceCall(t *testing.T) { func TestTraceCall(t *testing.T) {
t.Parallel() t.Parallel()
@ -448,7 +535,7 @@ func TestTraceTransaction(t *testing.T) {
Gas: params.TxGas, Gas: params.TxGas,
Failed: false, Failed: false,
ReturnValue: "", ReturnValue: "",
StructLogs: []logger.StructLogRes{}, StructLogs: []json.RawMessage{},
}) { }) {
t.Error("Transaction tracing result is different") t.Error("Transaction tracing result is different")
} }

View file

@ -133,7 +133,6 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
t.Fatalf("failed to prepare transaction for tracing: %v", err) t.Fatalf("failed to prepare transaction for tracing: %v", err)
} }
evm := vm.NewEVM(context, logState, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) evm := vm.NewEVM(context, logState, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks})
evm.SetTxContext(core.NewEVMTxContext(msg))
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil { if err != nil {
@ -206,11 +205,6 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
b.Fatalf("failed to parse testcase input: %v", err) b.Fatalf("failed to parse testcase input: %v", err)
} }
signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time))
origin, _ := signer.Sender(tx)
txContext := vm.TxContext{
Origin: origin,
GasPrice: tx.GasPrice(),
}
context := test.Context.toBlockContext(test.Genesis) context := test.Context.toBlockContext(test.Genesis)
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
if err != nil { if err != nil {
@ -222,19 +216,25 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{})
for i := 0; i < b.N; i++ {
snap := state.StateDB.Snapshot()
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), nil, test.Genesis.Config) tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), nil, test.Genesis.Config)
if err != nil { if err != nil {
b.Fatalf("failed to create call tracer: %v", err) b.Fatalf("failed to create call tracer: %v", err)
} }
evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) evm.Config.Tracer = tracer.Hooks
evm.SetTxContext(txContext) if tracer.OnTxStart != nil {
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
for i := 0; i < b.N; i++ { }
snap := state.StateDB.Snapshot() _, err = core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil {
if _, err = st.TransitionDb(); err != nil {
b.Fatalf("failed to execute transaction: %v", err) b.Fatalf("failed to execute transaction: %v", err)
} }
if tracer.OnTxEnd != nil {
tracer.OnTxEnd(&types.Receipt{GasUsed: tx.Gas()}, nil)
}
if _, err = tracer.GetResult(); err != nil { if _, err = tracer.GetResult(); err != nil {
b.Fatal(err) b.Fatal(err)
} }
@ -372,12 +372,7 @@ func TestInternals(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("test %v: failed to sign transaction: %v", tc.name, err) t.Fatalf("test %v: failed to sign transaction: %v", tc.name, err)
} }
txContext := vm.TxContext{
Origin: origin,
GasPrice: tx.GasPrice(),
}
evm := vm.NewEVM(context, logState, config, vm.Config{Tracer: tc.tracer.Hooks}) evm := vm.NewEVM(context, logState, config, vm.Config{Tracer: tc.tracer.Hooks})
evm.SetTxContext(txContext)
msg, err := core.TransactionToMessage(tx, signer, big.NewInt(0)) msg, err := core.TransactionToMessage(tx, signer, big.NewInt(0))
if err != nil { if err != nil {
t.Fatalf("test %v: failed to create message: %v", tc.name, err) t.Fatalf("test %v: failed to create message: %v", tc.name, err)

View file

@ -99,7 +99,6 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string
return fmt.Errorf("failed to prepare transaction for tracing: %v", err) return fmt.Errorf("failed to prepare transaction for tracing: %v", err)
} }
evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks})
evm.SetTxContext(core.NewEVMTxContext(msg))
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil { if err != nil {

View file

@ -107,7 +107,6 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) {
t.Fatalf("failed to prepare transaction for tracing: %v", err) t.Fatalf("failed to prepare transaction for tracing: %v", err)
} }
evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks})
evm.SetTxContext(core.NewEVMTxContext(msg))
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil { if err != nil {

View file

@ -86,7 +86,7 @@ func TestSupplyOmittedFields(t *testing.T) {
expected := supplyInfo{ expected := supplyInfo{
Number: 0, Number: 0,
Hash: common.HexToHash("0xc02ee8ee5b54a40e43f0fa827d431e1bd4f217e941790dda10b2521d1925a20b"), Hash: common.HexToHash("0x3055fc27d6b4a08eb07033a0d1ee755a4b2988086f28a6189eac1b507525eeb1"),
ParentHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), ParentHash: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"),
} }
actual := out[expected.Number] actual := out[expected.Number]

View file

@ -260,7 +260,7 @@ func (t *jsTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from
t.activePrecompiles = vm.ActivePrecompiles(rules) t.activePrecompiles = vm.ActivePrecompiles(rules)
t.ctx["block"] = t.vm.ToValue(t.env.BlockNumber.Uint64()) t.ctx["block"] = t.vm.ToValue(t.env.BlockNumber.Uint64())
t.ctx["gas"] = t.vm.ToValue(tx.Gas()) t.ctx["gas"] = t.vm.ToValue(tx.Gas())
gasPriceBig, err := t.toBig(t.vm, env.GasPrice.String()) gasPriceBig, err := t.toBig(t.vm, tx.EffectiveGasTipValue(env.BaseFee).String())
if err != nil { if err != nil {
t.err = err t.err = err
return return

View file

@ -59,7 +59,7 @@ type vmContext struct {
} }
func testCtx() *vmContext { func testCtx() *vmContext {
return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}} return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1), BaseFee: big.NewInt(0)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}}
} }
func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) { func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) {
@ -76,7 +76,7 @@ func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCo
contract.Code = contractCode contract.Code = contractCode
} }
tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit}), contract.Caller()) tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit, GasPrice: vmctx.txCtx.GasPrice}), contract.Caller())
tracer.OnEnter(0, byte(vm.CALL), contract.Caller(), contract.Address(), []byte{}, startGas, value.ToBig()) tracer.OnEnter(0, byte(vm.CALL), contract.Caller(), contract.Address(), []byte{}, startGas, value.ToBig())
ret, err := evm.Interpreter().Run(contract, []byte{}, false) ret, err := evm.Interpreter().Run(contract, []byte{}, false)
tracer.OnExit(0, ret, startGas-contract.Gas, err, true) tracer.OnExit(0, ret, startGas-contract.Gas, err, true)

View file

@ -21,6 +21,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"maps"
"math/big" "math/big"
"strings" "strings"
"sync/atomic" "sync/atomic"
@ -38,15 +39,6 @@ import (
// Storage represents a contract's storage. // Storage represents a contract's storage.
type Storage map[common.Hash]common.Hash type Storage map[common.Hash]common.Hash
// Copy duplicates the current storage.
func (s Storage) Copy() Storage {
cpy := make(Storage, len(s))
for key, value := range s {
cpy[key] = value
}
return cpy
}
// Config are the configuration options for structured logger the EVM // Config are the configuration options for structured logger the EVM
type Config struct { type Config struct {
EnableMemory bool // enable memory capture EnableMemory bool // enable memory capture
@ -54,15 +46,16 @@ type Config struct {
DisableStorage bool // disable storage capture DisableStorage bool // disable storage capture
EnableReturnData bool // enable return data capture EnableReturnData bool // enable return data capture
Debug bool // print output during capture end Debug bool // print output during capture end
Limit int // maximum length of output, but zero means unlimited Limit int // maximum size of output, but zero means unlimited
// Chain overrides, can be used to execute a trace using future fork rules // Chain overrides, can be used to execute a trace using future fork rules
Overrides *params.ChainConfig `json:"overrides,omitempty"` Overrides *params.ChainConfig `json:"overrides,omitempty"`
} }
//go:generate go run github.com/fjl/gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go //go:generate go run github.com/fjl/gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go
// StructLog is emitted to the EVM each cycle and lists information about the current internal state // StructLog is emitted to the EVM each cycle and lists information about the
// prior to the execution of the statement. // current internal state prior to the execution of the statement.
type StructLog struct { type StructLog struct {
Pc uint64 `json:"pc"` Pc uint64 `json:"pc"`
Op vm.OpCode `json:"op"` Op vm.OpCode `json:"op"`
@ -102,29 +95,144 @@ func (s *StructLog) ErrorString() string {
return "" return ""
} }
// WriteTo writes the human-readable log data into the supplied writer.
func (s *StructLog) WriteTo(writer io.Writer) {
fmt.Fprintf(writer, "%-16spc=%08d gas=%v cost=%v", s.Op, s.Pc, s.Gas, s.GasCost)
if s.Err != nil {
fmt.Fprintf(writer, " ERROR: %v", s.Err)
}
fmt.Fprintln(writer)
if len(s.Stack) > 0 {
fmt.Fprintln(writer, "Stack:")
for i := len(s.Stack) - 1; i >= 0; i-- {
fmt.Fprintf(writer, "%08d %s\n", len(s.Stack)-i-1, s.Stack[i].Hex())
}
}
if len(s.Memory) > 0 {
fmt.Fprintln(writer, "Memory:")
fmt.Fprint(writer, hex.Dump(s.Memory))
}
if len(s.Storage) > 0 {
fmt.Fprintln(writer, "Storage:")
for h, item := range s.Storage {
fmt.Fprintf(writer, "%x: %x\n", h, item)
}
}
if len(s.ReturnData) > 0 {
fmt.Fprintln(writer, "ReturnData:")
fmt.Fprint(writer, hex.Dump(s.ReturnData))
}
fmt.Fprintln(writer)
}
// structLogLegacy stores a structured log emitted by the EVM while replaying a
// transaction in debug mode. It's the legacy format used in tracer. The differences
// between the structLog json and the 'legacy' json are:
//
// op:
// Legacy uses string (e.g. "SSTORE"), non-legacy uses a byte.
// non-legacy has an 'opName' field containing the op name.
//
// gas, gasCost:
// Legacy uses integers, non-legacy hex-strings
//
// memory:
// Legacy uses a list of 64-char strings, each representing 32-byte chunks
// of evm memory. Non-legacy just uses a string of hexdata, no chunking.
//
// storage:
// Legacy has a storage field while non-legacy doesn't.
type structLogLegacy struct {
Pc uint64 `json:"pc"`
Op string `json:"op"`
Gas uint64 `json:"gas"`
GasCost uint64 `json:"gasCost"`
Depth int `json:"depth"`
Error string `json:"error,omitempty"`
Stack *[]string `json:"stack,omitempty"`
ReturnData string `json:"returnData,omitempty"`
Memory *[]string `json:"memory,omitempty"`
Storage *map[string]string `json:"storage,omitempty"`
RefundCounter uint64 `json:"refund,omitempty"`
}
// toLegacyJSON converts the structLog to legacy json-encoded legacy form.
func (s *StructLog) toLegacyJSON() json.RawMessage {
msg := structLogLegacy{
Pc: s.Pc,
Op: s.Op.String(),
Gas: s.Gas,
GasCost: s.GasCost,
Depth: s.Depth,
Error: s.ErrorString(),
RefundCounter: s.RefundCounter,
}
if s.Stack != nil {
stack := make([]string, len(s.Stack))
for i, stackValue := range s.Stack {
stack[i] = stackValue.Hex()
}
msg.Stack = &stack
}
if len(s.ReturnData) > 0 {
msg.ReturnData = hexutil.Bytes(s.ReturnData).String()
}
if s.Memory != nil {
memory := make([]string, 0, (len(s.Memory)+31)/32)
for i := 0; i+32 <= len(s.Memory); i += 32 {
memory = append(memory, fmt.Sprintf("%x", s.Memory[i:i+32]))
}
msg.Memory = &memory
}
if s.Storage != nil {
storage := make(map[string]string)
for i, storageValue := range s.Storage {
storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue)
}
msg.Storage = &storage
}
element, _ := json.Marshal(msg)
return element
}
// StructLogger is an EVM state logger and implements EVMLogger. // StructLogger is an EVM state logger and implements EVMLogger.
// //
// StructLogger can capture state based on the given Log configuration and also keeps // StructLogger can capture state based on the given Log configuration and also keeps
// a track record of modified storage which is used in reporting snapshots of the // a track record of modified storage which is used in reporting snapshots of the
// contract their storage. // contract their storage.
//
// A StructLogger can either yield it's output immediately (streaming) or store for
// later output.
type StructLogger struct { type StructLogger struct {
cfg Config cfg Config
env *tracing.VMContext env *tracing.VMContext
storage map[common.Address]Storage storage map[common.Address]Storage
logs []StructLog
output []byte output []byte
err error err error
usedGas uint64 usedGas uint64
writer io.Writer // If set, the logger will stream instead of store logs
logs []json.RawMessage // buffer of json-encoded logs
resultSize int
interrupt atomic.Bool // Atomic flag to signal execution interruption interrupt atomic.Bool // Atomic flag to signal execution interruption
reason error // Textual reason for the interruption reason error // Textual reason for the interruption
} }
// NewStructLogger returns a new logger // NewStreamingStructLogger returns a new streaming logger.
func NewStreamingStructLogger(cfg *Config, writer io.Writer) *StructLogger {
l := NewStructLogger(cfg)
l.writer = writer
return l
}
// NewStructLogger construct a new (non-streaming) struct logger.
func NewStructLogger(cfg *Config) *StructLogger { func NewStructLogger(cfg *Config) *StructLogger {
logger := &StructLogger{ logger := &StructLogger{
storage: make(map[common.Address]Storage), storage: make(map[common.Address]Storage),
logs: make([]json.RawMessage, 0),
} }
if cfg != nil { if cfg != nil {
logger.cfg = *cfg logger.cfg = *cfg
@ -141,44 +249,36 @@ func (l *StructLogger) Hooks() *tracing.Hooks {
} }
} }
// Reset clears the data held by the logger.
func (l *StructLogger) Reset() {
l.storage = make(map[common.Address]Storage)
l.output = make([]byte, 0)
l.logs = l.logs[:0]
l.err = nil
}
// OnOpcode logs a new structured log message and pushes it out to the environment // OnOpcode logs a new structured log message and pushes it out to the environment
// //
// OnOpcode also tracks SLOAD/SSTORE ops to track storage change. // OnOpcode also tracks SLOAD/SSTORE ops to track storage change.
func (l *StructLogger) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { func (l *StructLogger) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
// If tracing was interrupted, set the error and stop // If tracing was interrupted, exit
if l.interrupt.Load() { if l.interrupt.Load() {
return return
} }
// check if already accumulated the specified number of logs // check if already accumulated the size of the response.
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) { if l.cfg.Limit != 0 && l.resultSize > l.cfg.Limit {
return return
} }
var (
op := vm.OpCode(opcode) op = vm.OpCode(opcode)
memory := scope.MemoryData() memory = scope.MemoryData()
stack := scope.StackData() contractAddr = scope.Address()
// Copy a snapshot of the current memory state to a new buffer stack = scope.StackData()
var mem []byte stackLen = len(stack)
)
log := StructLog{pc, op, gas, cost, nil, len(memory), nil, nil, nil, depth, l.env.StateDB.GetRefund(), err}
if l.cfg.EnableMemory { if l.cfg.EnableMemory {
mem = make([]byte, len(memory)) log.Memory = memory
copy(mem, memory)
} }
// Copy a snapshot of the current stack state to a new buffer
var stck []uint256.Int
if !l.cfg.DisableStack { if !l.cfg.DisableStack {
stck = make([]uint256.Int, len(stack)) log.Stack = scope.StackData()
copy(stck, stack)
} }
contractAddr := scope.Address() if l.cfg.EnableReturnData {
stackLen := len(stack) log.ReturnData = rData
}
// Copy a snapshot of the current storage to a new container // Copy a snapshot of the current storage to a new container
var storage Storage var storage Storage
if !l.cfg.DisableStorage && (op == vm.SLOAD || op == vm.SSTORE) { if !l.cfg.DisableStorage && (op == vm.SLOAD || op == vm.SSTORE) {
@ -194,7 +294,7 @@ func (l *StructLogger) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope
value = l.env.StateDB.GetState(contractAddr, address) value = l.env.StateDB.GetState(contractAddr, address)
) )
l.storage[contractAddr][address] = value l.storage[contractAddr][address] = value
storage = l.storage[contractAddr].Copy() storage = maps.Clone(l.storage[contractAddr])
} else if op == vm.SSTORE && stackLen >= 2 { } else if op == vm.SSTORE && stackLen >= 2 {
// capture SSTORE opcodes and record the written entry in the local storage. // capture SSTORE opcodes and record the written entry in the local storage.
var ( var (
@ -202,17 +302,19 @@ func (l *StructLogger) OnOpcode(pc uint64, opcode byte, gas, cost uint64, scope
address = common.Hash(stack[stackLen-1].Bytes32()) address = common.Hash(stack[stackLen-1].Bytes32())
) )
l.storage[contractAddr][address] = value l.storage[contractAddr][address] = value
storage = l.storage[contractAddr].Copy() storage = maps.Clone(l.storage[contractAddr])
} }
} }
var rdata []byte log.Storage = storage
if l.cfg.EnableReturnData {
rdata = make([]byte, len(rData)) // create a log
copy(rdata, rData) if l.writer == nil {
entry := log.toLegacyJSON()
l.resultSize += len(entry)
l.logs = append(l.logs, entry)
return
} }
// create a new snapshot of the EVM. log.WriteTo(l.writer)
log := StructLog{pc, op, gas, cost, mem, len(memory), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), err}
l.logs = append(l.logs, log)
} }
// OnExit is called a call frame finishes processing. // OnExit is called a call frame finishes processing.
@ -246,7 +348,7 @@ func (l *StructLogger) GetResult() (json.RawMessage, error) {
Gas: l.usedGas, Gas: l.usedGas,
Failed: failed, Failed: failed,
ReturnValue: returnVal, ReturnValue: returnVal,
StructLogs: formatLogs(l.StructLogs()), StructLogs: l.logs,
}) })
} }
@ -273,9 +375,6 @@ func (l *StructLogger) OnTxEnd(receipt *types.Receipt, err error) {
} }
} }
// StructLogs returns the captured log entries.
func (l *StructLogger) StructLogs() []StructLog { return l.logs }
// Error returns the VM error captured by the trace. // Error returns the VM error captured by the trace.
func (l *StructLogger) Error() error { return l.err } func (l *StructLogger) Error() error { return l.err }
@ -283,49 +382,10 @@ func (l *StructLogger) Error() error { return l.err }
func (l *StructLogger) Output() []byte { return l.output } func (l *StructLogger) Output() []byte { return l.output }
// WriteTrace writes a formatted trace to the given writer // WriteTrace writes a formatted trace to the given writer
// @deprecated
func WriteTrace(writer io.Writer, logs []StructLog) { func WriteTrace(writer io.Writer, logs []StructLog) {
for _, log := range logs { for _, log := range logs {
fmt.Fprintf(writer, "%-16spc=%08d gas=%v cost=%v", log.Op, log.Pc, log.Gas, log.GasCost) log.WriteTo(writer)
if log.Err != nil {
fmt.Fprintf(writer, " ERROR: %v", log.Err)
}
fmt.Fprintln(writer)
if len(log.Stack) > 0 {
fmt.Fprintln(writer, "Stack:")
for i := len(log.Stack) - 1; i >= 0; i-- {
fmt.Fprintf(writer, "%08d %s\n", len(log.Stack)-i-1, log.Stack[i].Hex())
}
}
if len(log.Memory) > 0 {
fmt.Fprintln(writer, "Memory:")
fmt.Fprint(writer, hex.Dump(log.Memory))
}
if len(log.Storage) > 0 {
fmt.Fprintln(writer, "Storage:")
for h, item := range log.Storage {
fmt.Fprintf(writer, "%x: %x\n", h, item)
}
}
if len(log.ReturnData) > 0 {
fmt.Fprintln(writer, "ReturnData:")
fmt.Fprint(writer, hex.Dump(log.ReturnData))
}
fmt.Fprintln(writer)
}
}
// WriteLogs writes vm logs in a readable format to the given writer
func WriteLogs(writer io.Writer, logs []*types.Log) {
for _, log := range logs {
fmt.Fprintf(writer, "LOG%d: %x bn=%d txi=%x\n", len(log.Topics), log.Address, log.BlockNumber, log.TxIndex)
for i, topic := range log.Topics {
fmt.Fprintf(writer, "%08d %x\n", i, topic)
}
fmt.Fprint(writer, hex.Dump(log.Data))
fmt.Fprintln(writer)
} }
} }
@ -428,62 +488,5 @@ type ExecutionResult struct {
Gas uint64 `json:"gas"` Gas uint64 `json:"gas"`
Failed bool `json:"failed"` Failed bool `json:"failed"`
ReturnValue string `json:"returnValue"` ReturnValue string `json:"returnValue"`
StructLogs []StructLogRes `json:"structLogs"` StructLogs []json.RawMessage `json:"structLogs"`
}
// StructLogRes stores a structured log emitted by the EVM while replaying a
// transaction in debug mode
type StructLogRes struct {
Pc uint64 `json:"pc"`
Op string `json:"op"`
Gas uint64 `json:"gas"`
GasCost uint64 `json:"gasCost"`
Depth int `json:"depth"`
Error string `json:"error,omitempty"`
Stack *[]string `json:"stack,omitempty"`
ReturnData string `json:"returnData,omitempty"`
Memory *[]string `json:"memory,omitempty"`
Storage *map[string]string `json:"storage,omitempty"`
RefundCounter uint64 `json:"refund,omitempty"`
}
// formatLogs formats EVM returned structured logs for json output
func formatLogs(logs []StructLog) []StructLogRes {
formatted := make([]StructLogRes, len(logs))
for index, trace := range logs {
formatted[index] = StructLogRes{
Pc: trace.Pc,
Op: trace.Op.String(),
Gas: trace.Gas,
GasCost: trace.GasCost,
Depth: trace.Depth,
Error: trace.ErrorString(),
RefundCounter: trace.RefundCounter,
}
if trace.Stack != nil {
stack := make([]string, len(trace.Stack))
for i, stackValue := range trace.Stack {
stack[i] = stackValue.Hex()
}
formatted[index].Stack = &stack
}
if len(trace.ReturnData) > 0 {
formatted[index].ReturnData = hexutil.Bytes(trace.ReturnData).String()
}
if trace.Memory != nil {
memory := make([]string, 0, (len(trace.Memory)+31)/32)
for i := 0; i+32 <= len(trace.Memory); i += 32 {
memory = append(memory, fmt.Sprintf("%x", trace.Memory[i:i+32]))
}
formatted[index].Memory = &memory
}
if trace.Storage != nil {
storage := make(map[string]string)
for i, storageValue := range trace.Storage {
storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue)
}
formatted[index].Storage = &storage
}
}
return formatted
} }

View file

@ -31,7 +31,7 @@ import (
"github.com/ethereum/go-ethereum/tests" "github.com/ethereum/go-ethereum/tests"
) )
func BenchmarkTransactionTrace(b *testing.B) { func BenchmarkTransactionTraceV2(b *testing.B) {
key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
from := crypto.PubkeyToAddress(key.PublicKey) from := crypto.PubkeyToAddress(key.PublicKey)
gas := uint64(1000000) // 1M gas gas := uint64(1000000) // 1M gas
@ -47,10 +47,6 @@ func BenchmarkTransactionTrace(b *testing.B) {
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
txContext := vm.TxContext{
Origin: from,
GasPrice: tx.GasPrice(),
}
context := vm.BlockContext{ context := vm.BlockContext{
CanTransfer: core.CanTransfer, CanTransfer: core.CanTransfer,
Transfer: core.Transfer, Transfer: core.Transfer,
@ -82,15 +78,8 @@ func BenchmarkTransactionTrace(b *testing.B) {
state := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false, rawdb.HashScheme) state := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false, rawdb.HashScheme)
defer state.Close() defer state.Close()
// Create the tracer, the EVM environment and run it evm := vm.NewEVM(context, state.StateDB, params.AllEthashProtocolChanges, vm.Config{})
tracer := logger.NewStructLogger(&logger.Config{
Debug: false,
//DisableStorage: true,
//EnableMemory: false,
//EnableReturnData: false,
})
evm := vm.NewEVM(context, state.StateDB, params.AllEthashProtocolChanges, vm.Config{Tracer: tracer.Hooks()})
evm.SetTxContext(txContext)
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
if err != nil { if err != nil {
b.Fatalf("failed to prepare transaction for tracing: %v", err) b.Fatalf("failed to prepare transaction for tracing: %v", err)
@ -99,18 +88,15 @@ func BenchmarkTransactionTrace(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
snap := state.StateDB.Snapshot() tracer := logger.NewStructLogger(&logger.Config{Debug: false}).Hooks()
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) evm.Config.Tracer = tracer
res, err := st.TransitionDb()
snap := state.StateDB.Snapshot()
_, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
tracer.OnTxEnd(&types.Receipt{GasUsed: res.UsedGas}, nil)
state.StateDB.RevertToSnapshot(snap) state.StateDB.RevertToSnapshot(snap)
if have, want := len(tracer.StructLogs()), 244752; have != want {
b.Fatalf("trace wrong, want %d steps, have %d", want, have)
}
tracer.Reset()
} }
} }

View file

@ -26,7 +26,6 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/catalyst" "github.com/ethereum/go-ethereum/eth/catalyst"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
@ -85,7 +84,7 @@ func NewBackend(alloc types.GenesisAlloc, options ...func(nodeConf *node.Config,
GasLimit: ethconfig.Defaults.Miner.GasCeil, GasLimit: ethconfig.Defaults.Miner.GasCeil,
Alloc: alloc, Alloc: alloc,
} }
ethConf.SyncMode = downloader.FullSync ethConf.SyncMode = ethconfig.FullSync
ethConf.TxPool.NoLocals = true ethConf.TxPool.NoLocals = true
for _, option := range options { for _, option := range options {

26
go.mod
View file

@ -15,16 +15,16 @@ require (
github.com/cespare/cp v0.1.0 github.com/cespare/cp v0.1.0
github.com/cloudflare/cloudflare-go v0.79.0 github.com/cloudflare/cloudflare-go v0.79.0
github.com/cockroachdb/pebble v1.1.2 github.com/cockroachdb/pebble v1.1.2
github.com/consensys/gnark-crypto v0.12.1 github.com/consensys/gnark-crypto v0.14.0
github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a
github.com/crate-crypto/go-kzg-4844 v1.0.0 github.com/crate-crypto/go-kzg-4844 v1.1.0
github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew v1.1.1
github.com/deckarep/golang-set/v2 v2.6.0 github.com/deckarep/golang-set/v2 v2.6.0
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1
github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0 github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0
github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3 github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3
github.com/ethereum/c-kzg-4844 v1.0.0 github.com/ethereum/c-kzg-4844 v1.0.0
github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 github.com/ethereum/go-verkle v0.2.2
github.com/fatih/color v1.16.0 github.com/fatih/color v1.16.0
github.com/ferranbt/fastssz v0.1.2 github.com/ferranbt/fastssz v0.1.2
github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e
@ -70,13 +70,13 @@ require (
github.com/urfave/cli/v2 v2.25.7 github.com/urfave/cli/v2 v2.25.7
go.uber.org/automaxprocs v1.5.2 go.uber.org/automaxprocs v1.5.2
go.uber.org/zap v1.27.0 go.uber.org/zap v1.27.0
golang.org/x/crypto v0.22.0 golang.org/x/crypto v0.26.0
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
golang.org/x/sync v0.7.0 golang.org/x/sync v0.10.0
golang.org/x/sys v0.26.0 golang.org/x/sys v0.28.0
golang.org/x/text v0.14.0 golang.org/x/text v0.17.0
golang.org/x/time v0.5.0 golang.org/x/time v0.5.0
golang.org/x/tools v0.20.0 golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d
google.golang.org/protobuf v1.34.2 google.golang.org/protobuf v1.34.2
gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
@ -98,14 +98,14 @@ require (
github.com/aws/aws-sdk-go-v2/service/sts v1.23.2 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.23.2 // indirect
github.com/aws/smithy-go v1.15.0 // indirect github.com/aws/smithy-go v1.15.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.13.0 // indirect github.com/bits-and-blooms/bitset v1.17.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cockroachdb/errors v1.11.3 // indirect github.com/cockroachdb/errors v1.11.3 // indirect
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
github.com/cockroachdb/redact v1.1.5 // indirect github.com/cockroachdb/redact v1.1.5 // indirect
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/bavard v0.1.22 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/deepmap/oapi-codegen v1.6.0 // indirect github.com/deepmap/oapi-codegen v1.6.0 // indirect
github.com/dlclark/regexp2 v1.7.0 // indirect github.com/dlclark/regexp2 v1.7.0 // indirect
@ -145,7 +145,7 @@ require (
github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect github.com/prometheus/procfs v0.7.3 // indirect
github.com/rivo/uniseg v0.2.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect github.com/tklauser/numcpus v0.6.1 // indirect
@ -153,7 +153,7 @@ require (
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
golang.org/x/mod v0.17.0 // indirect golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.24.0 // indirect golang.org/x/net v0.25.0 // indirect
rsc.io/tmplfunc v0.0.3 // indirect rsc.io/tmplfunc v0.0.3 // indirect
) )

59
go.sum
View file

@ -90,8 +90,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= github.com/bits-and-blooms/bitset v1.17.0 h1:1X2TS7aHz1ELcC0yU1y2stUs/0ig5oMU6STFZGrhvHI=
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bits-and-blooms/bitset v1.17.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
@ -124,16 +124,16 @@ github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwP
github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo=
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= github.com/consensys/bavard v0.1.22 h1:Uw2CGvbXSZWhqK59X0VG/zOjpTFuOMcPLStrp1ihI0A=
github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/consensys/bavard v0.1.22/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs=
github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= github.com/consensys/gnark-crypto v0.14.0 h1:DDBdl4HaBtdQsq/wfMwJvZNE80sHidrK3Nfrefatm0E=
github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/consensys/gnark-crypto v0.14.0/go.mod h1:CU4UijNPsHawiVGNxe9co07FkzCeWHHrb1li/n1XoU0=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c h1:uQYC5Z1mdLRPrZhHjHxufI8+2UG/i25QG92j0Er9p6I= github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg=
github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM=
github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI= github.com/crate-crypto/go-kzg-4844 v1.1.0 h1:EN/u9k2TF6OWSHrCCDBBU6GLNMq88OspHHlMnHfoyU4=
github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/crate-crypto/go-kzg-4844 v1.1.0/go.mod h1:JolLjpSff1tCCJKaJx4psrlEdlXuJEC996PL3tTAFks=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -166,8 +166,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA=
github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 h1:8NfxH2iXvJ60YRB8ChToFTUzl8awsc3cJ8CbLjGIl/A= github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8=
github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/ferranbt/fastssz v0.1.2 h1:Dky6dXlngF6Qjc+EfDipAkE83N5I5DE68bY6O0VLNPk= github.com/ferranbt/fastssz v0.1.2 h1:Dky6dXlngF6Qjc+EfDipAkE83N5I5DE68bY6O0VLNPk=
@ -261,8 +261,8 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@ -363,8 +363,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
@ -483,8 +483,9 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
@ -558,8 +559,8 @@ golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -634,8 +635,8 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -655,8 +656,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -720,8 +721,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -738,8 +739,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -791,8 +792,8 @@ golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View file

@ -136,8 +136,8 @@ var (
Category: flags.LoggingCategory, Category: flags.LoggingCategory,
} }
traceFlag = &cli.StringFlag{ traceFlag = &cli.StringFlag{
Name: "trace", Name: "go-execution-trace",
Usage: "Write execution trace to the given file", Usage: "Write Go execution trace to the given file",
Category: flags.LoggingCategory, Category: flags.LoggingCategory,
} }
) )

View file

@ -867,7 +867,6 @@ func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *s
if precompiles != nil { if precompiles != nil {
evm.SetPrecompiles(precompiles) evm.SetPrecompiles(precompiles)
} }
evm.SetTxContext(core.NewEVMTxContext(msg))
res, err := applyMessageWithEVM(ctx, evm, msg, timeout, gp) res, err := applyMessageWithEVM(ctx, evm, msg, timeout, gp)
// If an internal state error occurred, let that have precedence. Otherwise, // If an internal state error occurred, let that have precedence. Otherwise,
// a "trie root missing" type of error will masquerade as e.g. "insufficient gas" // a "trie root missing" type of error will masquerade as e.g. "insufficient gas"
@ -1331,17 +1330,17 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
// Apply the transaction with the access list tracer // Apply the transaction with the access list tracer
tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles) tracer := logger.NewAccessListTracer(accessList, args.from(), to, precompiles)
config := vm.Config{Tracer: tracer.Hooks(), NoBaseFee: true} config := vm.Config{Tracer: tracer.Hooks(), NoBaseFee: true}
vmenv := b.GetEVM(ctx, statedb, header, &config, nil) evm := b.GetEVM(ctx, statedb, header, &config, nil)
// Lower the basefee to 0 to avoid breaking EVM // Lower the basefee to 0 to avoid breaking EVM
// invariants (basefee < feecap). // invariants (basefee < feecap).
if msg.GasPrice.Sign() == 0 { if msg.GasPrice.Sign() == 0 {
vmenv.Context.BaseFee = new(big.Int) evm.Context.BaseFee = new(big.Int)
} }
if msg.BlobGasFeeCap != nil && msg.BlobGasFeeCap.BitLen() == 0 { if msg.BlobGasFeeCap != nil && msg.BlobGasFeeCap.BitLen() == 0 {
vmenv.Context.BlobBaseFee = new(big.Int) evm.Context.BlobBaseFee = new(big.Int)
} }
vmenv.SetTxContext(core.NewEVMTxContext(msg)) res, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit))
res, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit))
if err != nil { if err != nil {
return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.ToTransaction(types.LegacyTxType).Hash(), err) return nil, 0, nil, fmt.Errorf("failed to apply transaction: %v err: %v", args.ToTransaction(types.LegacyTxType).Hash(), err)
} }

View file

@ -207,7 +207,6 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
tracer.reset(tx.Hash(), uint(i)) tracer.reset(tx.Hash(), uint(i))
// EoA check is always skipped, even in validation mode. // EoA check is always skipped, even in validation mode.
msg := call.ToMessage(header.BaseFee, !sim.validate, true) msg := call.ToMessage(header.BaseFee, !sim.validate, true)
evm.SetTxContext(core.NewEVMTxContext(msg))
result, err := applyMessageWithEVM(ctx, evm, msg, timeout, sim.gp) result, err := applyMessageWithEVM(ctx, evm, msg, timeout, sim.gp)
if err != nil { if err != nil {
txErr := txValidationError(err) txErr := txValidationError(err)

View file

@ -2,7 +2,7 @@
{ {
"blobGasPrice": "0x1", "blobGasPrice": "0x1",
"blobGasUsed": "0x20000", "blobGasUsed": "0x20000",
"blockHash": "0x11e6318d77a45c01f89f76b56d36c6936c5250f4e2bd238cb7b09df73cf0cb7d", "blockHash": "0x17124e31fb075a301b1d7d4135683b0a09fe4e6d453c54e2e734d5ee00744a49",
"blockNumber": "0x6", "blockNumber": "0x6",
"contractAddress": null, "contractAddress": null,
"cumulativeGasUsed": "0x5208", "cumulativeGasUsed": "0x5208",

View file

@ -1,6 +1,6 @@
[ [
{ {
"blockHash": "0x5526cd89bc188f20fd5e9bb50d8054dc5a51a81a74ed07eacf36a4a8b10de4b1", "blockHash": "0xb3e447c77374fd285964cba692e96b1673a88a959726826b5b6e2dca15472b0a",
"blockNumber": "0x2", "blockNumber": "0x2",
"contractAddress": "0xae9bea628c4ce503dcfd7e305cab4e29e7476592", "contractAddress": "0xae9bea628c4ce503dcfd7e305cab4e29e7476592",
"cumulativeGasUsed": "0xcf50", "cumulativeGasUsed": "0xcf50",

View file

@ -1,6 +1,6 @@
[ [
{ {
"blockHash": "0x3e946aa9e252873af511b257d9d89a1bcafa54ce7c6a6442f8407ecdf81e288d", "blockHash": "0x102e50de30318ee99a03a09db74387e79cad3165bf6840cc84249806a2a302f3",
"blockNumber": "0x4", "blockNumber": "0x4",
"contractAddress": null, "contractAddress": null,
"cumulativeGasUsed": "0x538d", "cumulativeGasUsed": "0x538d",

View file

@ -1,6 +1,6 @@
[ [
{ {
"blockHash": "0xc281d4299fc4e8ce5bba7ecb8deb50f5403d604c806b36aa887dfe2ff84c064f", "blockHash": "0xcc6225bf39327429a3d869af71182d619a354155187d0b5a8ecd6a9309cffcaa",
"blockNumber": "0x3", "blockNumber": "0x3",
"contractAddress": null, "contractAddress": null,
"cumulativeGasUsed": "0x5e28", "cumulativeGasUsed": "0x5e28",
@ -19,7 +19,7 @@
"blockNumber": "0x3", "blockNumber": "0x3",
"transactionHash": "0xeaf3921cbf03ba45bad4e6ab807b196ce3b2a0b5bacc355b6272fa96b11b4287", "transactionHash": "0xeaf3921cbf03ba45bad4e6ab807b196ce3b2a0b5bacc355b6272fa96b11b4287",
"transactionIndex": "0x0", "transactionIndex": "0x0",
"blockHash": "0xc281d4299fc4e8ce5bba7ecb8deb50f5403d604c806b36aa887dfe2ff84c064f", "blockHash": "0xcc6225bf39327429a3d869af71182d619a354155187d0b5a8ecd6a9309cffcaa",
"logIndex": "0x0", "logIndex": "0x0",
"removed": false "removed": false
} }

View file

@ -1,6 +1,6 @@
[ [
{ {
"blockHash": "0xda50d57d8802553b00bb8e4d777bd5c4114086941119ca04edb15429f4818ed9", "blockHash": "0xe9bd1d8c303b1af5c704b9d78e62c54a34af47e0db04ac1389a5ef74a619b9da",
"blockNumber": "0x1", "blockNumber": "0x1",
"contractAddress": null, "contractAddress": null,
"cumulativeGasUsed": "0x5208", "cumulativeGasUsed": "0x5208",

View file

@ -2,7 +2,7 @@
{ {
"blobGasPrice": "0x1", "blobGasPrice": "0x1",
"blobGasUsed": "0x20000", "blobGasUsed": "0x20000",
"blockHash": "0x11e6318d77a45c01f89f76b56d36c6936c5250f4e2bd238cb7b09df73cf0cb7d", "blockHash": "0x17124e31fb075a301b1d7d4135683b0a09fe4e6d453c54e2e734d5ee00744a49",
"blockNumber": "0x6", "blockNumber": "0x6",
"contractAddress": null, "contractAddress": null,
"cumulativeGasUsed": "0x5208", "cumulativeGasUsed": "0x5208",

View file

@ -1,7 +1,7 @@
{ {
"blobGasPrice": "0x1", "blobGasPrice": "0x1",
"blobGasUsed": "0x20000", "blobGasUsed": "0x20000",
"blockHash": "0x11e6318d77a45c01f89f76b56d36c6936c5250f4e2bd238cb7b09df73cf0cb7d", "blockHash": "0x17124e31fb075a301b1d7d4135683b0a09fe4e6d453c54e2e734d5ee00744a49",
"blockNumber": "0x6", "blockNumber": "0x6",
"contractAddress": null, "contractAddress": null,
"cumulativeGasUsed": "0x5208", "cumulativeGasUsed": "0x5208",

View file

@ -1,5 +1,5 @@
{ {
"blockHash": "0x5526cd89bc188f20fd5e9bb50d8054dc5a51a81a74ed07eacf36a4a8b10de4b1", "blockHash": "0xb3e447c77374fd285964cba692e96b1673a88a959726826b5b6e2dca15472b0a",
"blockNumber": "0x2", "blockNumber": "0x2",
"contractAddress": "0xae9bea628c4ce503dcfd7e305cab4e29e7476592", "contractAddress": "0xae9bea628c4ce503dcfd7e305cab4e29e7476592",
"cumulativeGasUsed": "0xcf50", "cumulativeGasUsed": "0xcf50",

View file

@ -1,5 +1,5 @@
{ {
"blockHash": "0xa04ad6be58c45fe483991b89416572bc50426b0de44b769757e95c704250f874", "blockHash": "0x53bffe54375c0a31fe7bc0db7455db7d48278234c2400efa4d40d1c57cbe868d",
"blockNumber": "0x5", "blockNumber": "0x5",
"contractAddress": "0xfdaa97661a584d977b4d3abb5370766ff5b86a18", "contractAddress": "0xfdaa97661a584d977b4d3abb5370766ff5b86a18",
"cumulativeGasUsed": "0xe01c", "cumulativeGasUsed": "0xe01c",

View file

@ -1,5 +1,5 @@
{ {
"blockHash": "0x3e946aa9e252873af511b257d9d89a1bcafa54ce7c6a6442f8407ecdf81e288d", "blockHash": "0x102e50de30318ee99a03a09db74387e79cad3165bf6840cc84249806a2a302f3",
"blockNumber": "0x4", "blockNumber": "0x4",
"contractAddress": null, "contractAddress": null,
"cumulativeGasUsed": "0x538d", "cumulativeGasUsed": "0x538d",

View file

@ -1,5 +1,5 @@
{ {
"blockHash": "0xda50d57d8802553b00bb8e4d777bd5c4114086941119ca04edb15429f4818ed9", "blockHash": "0xe9bd1d8c303b1af5c704b9d78e62c54a34af47e0db04ac1389a5ef74a619b9da",
"blockNumber": "0x1", "blockNumber": "0x1",
"contractAddress": null, "contractAddress": null,
"cumulativeGasUsed": "0x5208", "cumulativeGasUsed": "0x5208",

View file

@ -1,5 +1,5 @@
{ {
"blockHash": "0xc281d4299fc4e8ce5bba7ecb8deb50f5403d604c806b36aa887dfe2ff84c064f", "blockHash": "0xcc6225bf39327429a3d869af71182d619a354155187d0b5a8ecd6a9309cffcaa",
"blockNumber": "0x3", "blockNumber": "0x3",
"contractAddress": null, "contractAddress": null,
"cumulativeGasUsed": "0x5e28", "cumulativeGasUsed": "0x5e28",
@ -18,7 +18,7 @@
"blockNumber": "0x3", "blockNumber": "0x3",
"transactionHash": "0xeaf3921cbf03ba45bad4e6ab807b196ce3b2a0b5bacc355b6272fa96b11b4287", "transactionHash": "0xeaf3921cbf03ba45bad4e6ab807b196ce3b2a0b5bacc355b6272fa96b11b4287",
"transactionIndex": "0x0", "transactionIndex": "0x0",
"blockHash": "0xc281d4299fc4e8ce5bba7ecb8deb50f5403d604c806b36aa887dfe2ff84c064f", "blockHash": "0xcc6225bf39327429a3d869af71182d619a354155187d0b5a8ecd6a9309cffcaa",
"logIndex": "0x0", "logIndex": "0x0",
"removed": false "removed": false
} }

View file

@ -121,18 +121,15 @@ func (miner *Miner) generateWork(params *generateParams, witness bool) *newPaylo
// Collect consensus-layer requests if Prague is enabled. // Collect consensus-layer requests if Prague is enabled.
var requests [][]byte var requests [][]byte
if miner.chainConfig.IsPrague(work.header.Number, work.header.Time) { if miner.chainConfig.IsPrague(work.header.Number, work.header.Time) {
requests = [][]byte{}
// EIP-6110 deposits // EIP-6110 deposits
depositRequests, err := core.ParseDepositLogs(allLogs, miner.chainConfig) if err := core.ParseDepositLogs(&requests, allLogs, miner.chainConfig); err != nil {
if err != nil {
return &newPayloadResult{err: err} return &newPayloadResult{err: err}
} }
requests = append(requests, depositRequests) // EIP-7002
// EIP-7002 withdrawals core.ProcessWithdrawalQueue(&requests, work.evm)
withdrawalRequests := core.ProcessWithdrawalQueue(work.evm)
requests = append(requests, withdrawalRequests)
// EIP-7251 consolidations // EIP-7251 consolidations
consolidationRequests := core.ProcessConsolidationQueue(work.evm) core.ProcessConsolidationQueue(&requests, work.evm)
requests = append(requests, consolidationRequests)
} }
if requests != nil { if requests != nil {
reqHash := types.CalcRequestsHash(requests) reqHash := types.CalcRequestsHash(requests)

View file

@ -234,10 +234,6 @@ compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/secp256k1 \
compile_fuzzer github.com/ethereum/go-ethereum/eth/protocols/eth \ compile_fuzzer github.com/ethereum/go-ethereum/eth/protocols/eth \
FuzzEthProtocolHandlers fuzz_eth_protocol_handlers \ FuzzEthProtocolHandlers fuzz_eth_protocol_handlers \
$repo/eth/protocols/eth/handler_test.go $repo/eth/protocols/eth/handler_test.go,$repo/eth/protocols/eth/peer_test.go
#compile_fuzzer tests/fuzzers/vflux FuzzClientPool fuzzClientPool
#compile_fuzzer tests/fuzzers/difficulty Fuzz fuzzDifficulty
#compile_fuzzer tests/fuzzers/les Fuzz fuzzLes

View file

@ -277,7 +277,6 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
} }
// Prepare the EVM. // Prepare the EVM.
txContext := core.NewEVMTxContext(msg)
context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase) context := core.NewEVMBlockContext(block.Header(), nil, &t.json.Env.Coinbase)
context.GetHash = vmTestBlockHash context.GetHash = vmTestBlockHash
context.BaseFee = baseFee context.BaseFee = baseFee
@ -294,7 +293,6 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
context.BlobBaseFee = eip4844.CalcBlobFee(*t.json.Env.ExcessBlobGas) context.BlobBaseFee = eip4844.CalcBlobFee(*t.json.Env.ExcessBlobGas)
} }
evm := vm.NewEVM(context, st.StateDB, config, vmconfig) evm := vm.NewEVM(context, st.StateDB, config, vmconfig)
evm.SetTxContext(txContext)
if tracer := vmconfig.Tracer; tracer != nil && tracer.OnTxStart != nil { if tracer := vmconfig.Tracer; tracer != nil && tracer.OnTxStart != nil {
tracer.OnTxStart(evm.GetVMContext(), nil, msg.From) tracer.OnTxStart(evm.GetVMContext(), nil, msg.From)

View file

@ -486,13 +486,11 @@ func VerifyRangeProof(rootHash common.Hash, firstKey []byte, keys [][]byte, valu
return false, fmt.Errorf("inconsistent proof data, keys: %d, values: %d", len(keys), len(values)) return false, fmt.Errorf("inconsistent proof data, keys: %d, values: %d", len(keys), len(values))
} }
// Ensure the received batch is monotonic increasing and contains no deletions // Ensure the received batch is monotonic increasing and contains no deletions
for i := 0; i < len(keys)-1; i++ { for i := 0; i < len(keys); i++ {
if bytes.Compare(keys[i], keys[i+1]) >= 0 { if i < len(keys)-1 && bytes.Compare(keys[i], keys[i+1]) >= 0 {
return false, errors.New("range is not monotonically increasing") return false, errors.New("range is not monotonically increasing")
} }
} if len(values[i]) == 0 {
for _, value := range values {
if len(value) == 0 {
return false, errors.New("range contains deletion") return false, errors.New("range contains deletion")
} }
} }

View file

@ -41,10 +41,10 @@ var (
zero = uint256.NewInt(0) zero = uint256.NewInt(0)
verkleNodeWidthLog2 = 8 verkleNodeWidthLog2 = 8
headerStorageOffset = uint256.NewInt(64) headerStorageOffset = uint256.NewInt(64)
mainStorageOffsetLshVerkleNodeWidth = new(uint256.Int).Lsh(uint256.NewInt(256), 31-uint(verkleNodeWidthLog2))
codeOffset = uint256.NewInt(128) codeOffset = uint256.NewInt(128)
verkleNodeWidth = uint256.NewInt(256) verkleNodeWidth = uint256.NewInt(256)
codeStorageDelta = uint256.NewInt(0).Sub(codeOffset, headerStorageOffset) codeStorageDelta = uint256.NewInt(0).Sub(codeOffset, headerStorageOffset)
mainStorageOffsetLshVerkleNodeWidth = new(uint256.Int).Lsh(uint256.NewInt(1), 248-uint(verkleNodeWidthLog2))
index0Point *verkle.Point // pre-computed commitment of polynomial [2+256*64] index0Point *verkle.Point // pre-computed commitment of polynomial [2+256*64]
@ -273,17 +273,9 @@ func StorageSlotKeyWithEvaluatedAddress(evaluated *verkle.Point, storageKey []by
} }
func pointToHash(evaluated *verkle.Point, suffix byte) []byte { func pointToHash(evaluated *verkle.Point, suffix byte) []byte {
// The output of Byte() is big endian for banderwagon. This retb := verkle.HashPointToBytes(evaluated)
// introduces an imbalance in the tree, because hashes are retb[31] = suffix
// elements of a 253-bit field. This means more than half the return retb[:]
// tree would be empty. To avoid this problem, use a little
// endian commitment and chop the MSB.
bytes := evaluated.Bytes()
for i := 0; i < 16; i++ {
bytes[31-i], bytes[i] = bytes[i], bytes[31-i]
}
bytes[31] = suffix
return bytes[:]
} }
func evaluateAddressPoint(address []byte) *verkle.Point { func evaluateAddressPoint(address []byte) *verkle.Point {

View file

@ -136,8 +136,8 @@ func TestVerkleRollBack(t *testing.T) {
} }
} }
// ensure there is some code in the 2nd group // ensure there is some code in the 2nd group of the 1st account
keyOf2ndGroup := []byte{141, 124, 185, 236, 50, 22, 185, 39, 244, 47, 97, 209, 96, 235, 22, 13, 205, 38, 18, 201, 128, 223, 0, 59, 146, 199, 222, 119, 133, 13, 91, 0} keyOf2ndGroup := utils.CodeChunkKeyWithEvaluatedAddress(tr.cache.Get(common.Address{1}.Bytes()), uint256.NewInt(128))
chunk, err := tr.root.Get(keyOf2ndGroup, nil) chunk, err := tr.root.Get(keyOf2ndGroup, nil)
if err != nil { if err != nil {
t.Fatalf("Failed to get account, %v", err) t.Fatalf("Failed to get account, %v", err)

View file

@ -60,6 +60,10 @@ type backend interface {
// An error will be returned if the specified state is not available. // An error will be returned if the specified state is not available.
NodeReader(root common.Hash) (database.NodeReader, error) NodeReader(root common.Hash) (database.NodeReader, error)
// StateReader returns a reader for accessing flat states within the specified
// state. An error will be returned if the specified state is not available.
StateReader(root common.Hash) (database.StateReader, error)
// Initialized returns an indicator if the state data is already initialized // Initialized returns an indicator if the state data is already initialized
// according to the state scheme. // according to the state scheme.
Initialized(genesisRoot common.Hash) bool Initialized(genesisRoot common.Hash) bool
@ -122,6 +126,13 @@ func (db *Database) NodeReader(blockRoot common.Hash) (database.NodeReader, erro
return db.backend.NodeReader(blockRoot) return db.backend.NodeReader(blockRoot)
} }
// StateReader returns a reader that allows access to the state data associated
// with the specified state. An error will be returned if the specified state is
// not available.
func (db *Database) StateReader(blockRoot common.Hash) (database.StateReader, error) {
return db.backend.StateReader(blockRoot)
}
// Update performs a state transition by committing dirty nodes contained in the // Update performs a state transition by committing dirty nodes contained in the
// given set in order to update state from the specified parent to the specified // given set in order to update state from the specified parent to the specified
// root. The held pre-images accumulated up to this point will be flushed in case // root. The held pre-images accumulated up to this point will be flushed in case

View file

@ -635,3 +635,9 @@ func (reader *reader) Node(owner common.Hash, path []byte, hash common.Hash) ([]
blob, _ := reader.db.node(hash) blob, _ := reader.db.node(hash)
return blob, nil return blob, nil
} }
// StateReader returns a reader that allows access to the state data associated
// with the specified state.
func (db *Database) StateReader(root common.Hash) (database.StateReader, error) {
return nil, errors.New("not implemented")
}

View file

@ -36,37 +36,53 @@ type buffer struct {
layers uint64 // The number of diff layers aggregated inside layers uint64 // The number of diff layers aggregated inside
limit uint64 // The maximum memory allowance in bytes limit uint64 // The maximum memory allowance in bytes
nodes *nodeSet // Aggregated trie node set nodes *nodeSet // Aggregated trie node set
states *stateSet // Aggregated state set
} }
// newBuffer initializes the buffer with the provided states and trie nodes. // newBuffer initializes the buffer with the provided states and trie nodes.
func newBuffer(limit int, nodes *nodeSet, layers uint64) *buffer { func newBuffer(limit int, nodes *nodeSet, states *stateSet, layers uint64) *buffer {
// Don't panic for lazy users if any provided set is nil // Don't panic for lazy users if any provided set is nil
if nodes == nil { if nodes == nil {
nodes = newNodeSet(nil) nodes = newNodeSet(nil)
} }
if states == nil {
states = newStates(nil, nil)
}
return &buffer{ return &buffer{
layers: layers, layers: layers,
limit: uint64(limit), limit: uint64(limit),
nodes: nodes, nodes: nodes,
states: states,
} }
} }
// account retrieves the account blob with account address hash.
func (b *buffer) account(hash common.Hash) ([]byte, bool) {
return b.states.account(hash)
}
// storage retrieves the storage slot with account address hash and slot key.
func (b *buffer) storage(addrHash common.Hash, storageHash common.Hash) ([]byte, bool) {
return b.states.storage(addrHash, storageHash)
}
// node retrieves the trie node with node path and its trie identifier. // node retrieves the trie node with node path and its trie identifier.
func (b *buffer) node(owner common.Hash, path []byte) (*trienode.Node, bool) { func (b *buffer) node(owner common.Hash, path []byte) (*trienode.Node, bool) {
return b.nodes.node(owner, path) return b.nodes.node(owner, path)
} }
// commit merges the provided states and trie nodes into the buffer. // commit merges the provided states and trie nodes into the buffer.
func (b *buffer) commit(nodes *nodeSet) *buffer { func (b *buffer) commit(nodes *nodeSet, states *stateSet) *buffer {
b.layers++ b.layers++
b.nodes.merge(nodes) b.nodes.merge(nodes)
b.states.merge(states)
return b return b
} }
// revert is the reverse operation of commit. It also merges the provided states // revertTo is the reverse operation of commit. It also merges the provided states
// and trie nodes into the buffer. The key difference is that the provided state // and trie nodes into the buffer. The key difference is that the provided state
// set should reverse the changes made by the most recent state transition. // set should reverse the changes made by the most recent state transition.
func (b *buffer) revert(db ethdb.KeyValueReader, nodes map[common.Hash]map[string]*trienode.Node) error { func (b *buffer) revertTo(db ethdb.KeyValueReader, nodes map[common.Hash]map[string]*trienode.Node, accounts map[common.Hash][]byte, storages map[common.Hash]map[common.Hash][]byte) error {
// Short circuit if no embedded state transition to revert // Short circuit if no embedded state transition to revert
if b.layers == 0 { if b.layers == 0 {
return errStateUnrecoverable return errStateUnrecoverable
@ -78,7 +94,8 @@ func (b *buffer) revert(db ethdb.KeyValueReader, nodes map[common.Hash]map[strin
b.reset() b.reset()
return nil return nil
} }
b.nodes.revert(db, nodes) b.nodes.revertTo(db, nodes)
b.states.revertTo(accounts, storages)
return nil return nil
} }
@ -86,6 +103,7 @@ func (b *buffer) revert(db ethdb.KeyValueReader, nodes map[common.Hash]map[strin
func (b *buffer) reset() { func (b *buffer) reset() {
b.layers = 0 b.layers = 0
b.nodes.reset() b.nodes.reset()
b.states.reset()
} }
// empty returns an indicator if buffer is empty. // empty returns an indicator if buffer is empty.
@ -101,7 +119,7 @@ func (b *buffer) full() bool {
// size returns the approximate memory size of the held content. // size returns the approximate memory size of the held content.
func (b *buffer) size() uint64 { func (b *buffer) size() uint64 {
return b.nodes.size return b.states.size + b.nodes.size
} }
// flush persists the in-memory dirty trie node into the disk if the configured // flush persists the in-memory dirty trie node into the disk if the configured

View file

@ -68,6 +68,24 @@ type layer interface {
// - no error will be returned if the requested node is not found in database. // - no error will be returned if the requested node is not found in database.
node(owner common.Hash, path []byte, depth int) ([]byte, common.Hash, *nodeLoc, error) node(owner common.Hash, path []byte, depth int) ([]byte, common.Hash, *nodeLoc, error)
// account directly retrieves the account RLP associated with a particular
// hash in the slim data format. An error will be returned if the read
// operation exits abnormally. Specifically, if the layer is already stale.
//
// Note:
// - the returned account is not a copy, please don't modify it.
// - no error will be returned if the requested account is not found in database.
account(hash common.Hash, depth int) ([]byte, error)
// storage directly retrieves the storage data associated with a particular hash,
// within a particular account. An error will be returned if the read operation
// exits abnormally. Specifically, if the layer is already stale.
//
// Note:
// - the returned storage data is not a copy, please don't modify it.
// - no error will be returned if the requested slot is not found in database.
storage(accountHash, storageHash common.Hash, depth int) ([]byte, error)
// rootHash returns the root hash for which this layer was made. // rootHash returns the root hash for which this layer was made.
rootHash() common.Hash rootHash() common.Hash
@ -130,17 +148,18 @@ var Defaults = &Config{
// ReadOnly is the config in order to open database in read only mode. // ReadOnly is the config in order to open database in read only mode.
var ReadOnly = &Config{ReadOnly: true} var ReadOnly = &Config{ReadOnly: true}
// Database is a multiple-layered structure for maintaining in-memory trie nodes. // Database is a multiple-layered structure for maintaining in-memory states
// It consists of one persistent base layer backed by a key-value store, on top // along with its dirty trie nodes. It consists of one persistent base layer
// of which arbitrarily many in-memory diff layers are stacked. The memory diffs // backed by a key-value store, on top of which arbitrarily many in-memory diff
// can form a tree with branching, but the disk layer is singleton and common to // layers are stacked. The memory diffs can form a tree with branching, but the
// all. If a reorg goes deeper than the disk layer, a batch of reverse diffs can // disk layer is singleton and common to all. If a reorg goes deeper than the
// be applied to rollback. The deepest reorg that can be handled depends on the // disk layer, a batch of reverse diffs can be applied to rollback. The deepest
// amount of state histories tracked in the disk. // reorg that can be handled depends on the amount of state histories tracked
// in the disk.
// //
// At most one readable and writable database can be opened at the same time in // At most one readable and writable database can be opened at the same time in
// the whole system which ensures that only one database writer can operate disk // the whole system which ensures that only one database writer can operate the
// state. Unexpected open operations can cause the system to panic. // persistent state. Unexpected open operations can cause the system to panic.
type Database struct { type Database struct {
// readOnly is the flag whether the mutation is allowed to be applied. // readOnly is the flag whether the mutation is allowed to be applied.
// It will be set automatically when the database is journaled during // It will be set automatically when the database is journaled during
@ -358,7 +377,7 @@ func (db *Database) Enable(root common.Hash) error {
} }
// Re-construct a new disk layer backed by persistent state // Re-construct a new disk layer backed by persistent state
// with **empty clean cache and node buffer**. // with **empty clean cache and node buffer**.
db.tree.reset(newDiskLayer(root, 0, db, nil, newBuffer(db.config.WriteBufferSize, nil, 0))) db.tree.reset(newDiskLayer(root, 0, db, nil, newBuffer(db.config.WriteBufferSize, nil, nil, 0)))
// Re-enable the database as the final step. // Re-enable the database as the final step.
db.waitSync = false db.waitSync = false

View file

@ -309,7 +309,7 @@ func (t *tester) generate(parent common.Hash) (common.Hash, *trienode.MergedNode
delete(t.storages, addrHash) delete(t.storages, addrHash)
} }
} }
return root, ctx.nodes, NewStateSetWithOrigin(ctx.accountOrigin, ctx.storageOrigin) return root, ctx.nodes, NewStateSetWithOrigin(ctx.accounts, ctx.storages, ctx.accountOrigin, ctx.storageOrigin)
} }
// lastHash returns the latest root hash, or empty if nothing is cached. // lastHash returns the latest root hash, or empty if nothing is cached.

View file

@ -52,6 +52,7 @@ func newDiffLayer(parent layer, root common.Hash, id uint64, block uint64, nodes
states: states, states: states,
} }
dirtyNodeWriteMeter.Mark(int64(nodes.size)) dirtyNodeWriteMeter.Mark(int64(nodes.size))
dirtyStateWriteMeter.Mark(int64(states.size))
log.Debug("Created new diff layer", "id", id, "block", block, "nodesize", common.StorageSize(nodes.size), "statesize", common.StorageSize(states.size)) log.Debug("Created new diff layer", "id", id, "block", block, "nodesize", common.StorageSize(nodes.size), "statesize", common.StorageSize(states.size))
return dl return dl
} }
@ -96,6 +97,58 @@ func (dl *diffLayer) node(owner common.Hash, path []byte, depth int) ([]byte, co
return dl.parent.node(owner, path, depth+1) return dl.parent.node(owner, path, depth+1)
} }
// account directly retrieves the account RLP associated with a particular
// hash in the slim data format.
//
// Note the returned account is not a copy, please don't modify it.
func (dl *diffLayer) account(hash common.Hash, depth int) ([]byte, error) {
// Hold the lock, ensure the parent won't be changed during the
// state accessing.
dl.lock.RLock()
defer dl.lock.RUnlock()
if blob, found := dl.states.account(hash); found {
dirtyStateHitMeter.Mark(1)
dirtyStateHitDepthHist.Update(int64(depth))
dirtyStateReadMeter.Mark(int64(len(blob)))
if len(blob) == 0 {
stateAccountInexMeter.Mark(1)
} else {
stateAccountExistMeter.Mark(1)
}
return blob, nil
}
// Account is unknown to this layer, resolve from parent
return dl.parent.account(hash, depth+1)
}
// storage directly retrieves the storage data associated with a particular hash,
// within a particular account.
//
// Note the returned storage slot is not a copy, please don't modify it.
func (dl *diffLayer) storage(accountHash, storageHash common.Hash, depth int) ([]byte, error) {
// Hold the lock, ensure the parent won't be changed during the
// state accessing.
dl.lock.RLock()
defer dl.lock.RUnlock()
if blob, found := dl.states.storage(accountHash, storageHash); found {
dirtyStateHitMeter.Mark(1)
dirtyStateHitDepthHist.Update(int64(depth))
dirtyStateReadMeter.Mark(int64(len(blob)))
if len(blob) == 0 {
stateStorageInexMeter.Mark(1)
} else {
stateStorageExistMeter.Mark(1)
}
return blob, nil
}
// storage slot is unknown to this layer, resolve from parent
return dl.parent.storage(accountHash, storageHash, depth+1)
}
// update implements the layer interface, creating a new layer on top of the // update implements the layer interface, creating a new layer on top of the
// existing layer tree with the specified data items. // existing layer tree with the specified data items.
func (dl *diffLayer) update(root common.Hash, id uint64, block uint64, nodes *nodeSet, states *StateSetWithOrigin) *diffLayer { func (dl *diffLayer) update(root common.Hash, id uint64, block uint64, nodes *nodeSet, states *StateSetWithOrigin) *diffLayer {

View file

@ -30,7 +30,7 @@ import (
func emptyLayer() *diskLayer { func emptyLayer() *diskLayer {
return &diskLayer{ return &diskLayer{
db: New(rawdb.NewMemoryDatabase(), nil, false), db: New(rawdb.NewMemoryDatabase(), nil, false),
buffer: newBuffer(defaultBufferSize, nil, 0), buffer: newBuffer(defaultBufferSize, nil, nil, 0),
} }
} }
@ -76,7 +76,7 @@ func benchmarkSearch(b *testing.B, depth int, total int) {
nblob = common.CopyBytes(blob) nblob = common.CopyBytes(blob)
} }
} }
return newDiffLayer(parent, common.Hash{}, 0, 0, newNodeSet(nodes), NewStateSetWithOrigin(nil, nil)) return newDiffLayer(parent, common.Hash{}, 0, 0, newNodeSet(nodes), NewStateSetWithOrigin(nil, nil, nil, nil))
} }
var layer layer var layer layer
layer = emptyLayer() layer = emptyLayer()
@ -118,7 +118,7 @@ func BenchmarkPersist(b *testing.B) {
) )
nodes[common.Hash{}][string(path)] = node nodes[common.Hash{}][string(path)] = node
} }
return newDiffLayer(parent, common.Hash{}, 0, 0, newNodeSet(nodes), NewStateSetWithOrigin(nil, nil)) return newDiffLayer(parent, common.Hash{}, 0, 0, newNodeSet(nodes), NewStateSetWithOrigin(nil, nil, nil, nil))
} }
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
b.StopTimer() b.StopTimer()
@ -156,7 +156,7 @@ func BenchmarkJournal(b *testing.B) {
) )
nodes[common.Hash{}][string(path)] = node nodes[common.Hash{}][string(path)] = node
} }
return newDiffLayer(parent, common.Hash{}, 0, 0, newNodeSet(nodes), new(StateSetWithOrigin)) return newDiffLayer(parent, common.Hash{}, 0, 0, newNodeSet(nodes), NewStateSetWithOrigin(nil, nil, nil, nil))
} }
var layer layer var layer layer
layer = emptyLayer() layer = emptyLayer()

View file

@ -17,6 +17,7 @@
package pathdb package pathdb
import ( import (
"errors"
"fmt" "fmt"
"sync" "sync"
@ -33,7 +34,7 @@ type diskLayer struct {
id uint64 // Immutable, corresponding state id id uint64 // Immutable, corresponding state id
db *Database // Path-based trie database db *Database // Path-based trie database
nodes *fastcache.Cache // GC friendly memory cache of clean nodes nodes *fastcache.Cache // GC friendly memory cache of clean nodes
buffer *buffer // Dirty buffer to aggregate writes of nodes buffer *buffer // Dirty buffer to aggregate writes of nodes and states
stale bool // Signals that the layer became stale (state progressed) stale bool // Signals that the layer became stale (state progressed)
lock sync.RWMutex // Lock used to protect stale flag lock sync.RWMutex // Lock used to protect stale flag
} }
@ -140,6 +141,75 @@ func (dl *diskLayer) node(owner common.Hash, path []byte, depth int) ([]byte, co
return blob, h.hash(blob), &nodeLoc{loc: locDiskLayer, depth: depth}, nil return blob, h.hash(blob), &nodeLoc{loc: locDiskLayer, depth: depth}, nil
} }
// account directly retrieves the account RLP associated with a particular
// hash in the slim data format.
//
// Note the returned account is not a copy, please don't modify it.
func (dl *diskLayer) account(hash common.Hash, depth int) ([]byte, error) {
dl.lock.RLock()
defer dl.lock.RUnlock()
if dl.stale {
return nil, errSnapshotStale
}
// Try to retrieve the account from the not-yet-written
// node buffer first. Note the buffer is lock free since
// it's impossible to mutate the buffer before tagging the
// layer as stale.
blob, found := dl.buffer.account(hash)
if found {
dirtyStateHitMeter.Mark(1)
dirtyStateReadMeter.Mark(int64(len(blob)))
dirtyStateHitDepthHist.Update(int64(depth))
if len(blob) == 0 {
stateAccountInexMeter.Mark(1)
} else {
stateAccountExistMeter.Mark(1)
}
return blob, nil
}
dirtyStateMissMeter.Mark(1)
// TODO(rjl493456442) support persistent state retrieval
return nil, errors.New("not supported")
}
// storage directly retrieves the storage data associated with a particular hash,
// within a particular account.
//
// Note the returned account is not a copy, please don't modify it.
func (dl *diskLayer) storage(accountHash, storageHash common.Hash, depth int) ([]byte, error) {
// Hold the lock, ensure the parent won't be changed during the
// state accessing.
dl.lock.RLock()
defer dl.lock.RUnlock()
if dl.stale {
return nil, errSnapshotStale
}
// Try to retrieve the storage slot from the not-yet-written
// node buffer first. Note the buffer is lock free since
// it's impossible to mutate the buffer before tagging the
// layer as stale.
if blob, found := dl.buffer.storage(accountHash, storageHash); found {
dirtyStateHitMeter.Mark(1)
dirtyStateReadMeter.Mark(int64(len(blob)))
dirtyStateHitDepthHist.Update(int64(depth))
if len(blob) == 0 {
stateStorageInexMeter.Mark(1)
} else {
stateStorageExistMeter.Mark(1)
}
return blob, nil
}
dirtyStateMissMeter.Mark(1)
// TODO(rjl493456442) support persistent state retrieval
return nil, errors.New("not supported")
}
// update implements the layer interface, returning a new diff layer on top // update implements the layer interface, returning a new diff layer on top
// with the given state set. // with the given state set.
func (dl *diskLayer) update(root common.Hash, id uint64, block uint64, nodes *nodeSet, states *StateSetWithOrigin) *diffLayer { func (dl *diskLayer) update(root common.Hash, id uint64, block uint64, nodes *nodeSet, states *StateSetWithOrigin) *diffLayer {
@ -190,14 +260,14 @@ func (dl *diskLayer) commit(bottom *diffLayer, force bool) (*diskLayer, error) {
// In a unique scenario where the ID of the oldest history object (after tail // In a unique scenario where the ID of the oldest history object (after tail
// truncation) surpasses the persisted state ID, we take the necessary action // truncation) surpasses the persisted state ID, we take the necessary action
// of forcibly committing the cached dirty nodes to ensure that the persisted // of forcibly committing the cached dirty states to ensure that the persisted
// state ID remains higher. // state ID remains higher.
if !force && rawdb.ReadPersistentStateID(dl.db.diskdb) < oldest { if !force && rawdb.ReadPersistentStateID(dl.db.diskdb) < oldest {
force = true force = true
} }
// Merge the trie nodes of the bottom-most diff layer into the buffer as the // Merge the trie nodes and flat states of the bottom-most diff layer into the
// combined layer. // buffer as the combined layer.
combined := dl.buffer.commit(bottom.nodes) combined := dl.buffer.commit(bottom.nodes, bottom.states.stateSet)
if combined.full() || force { if combined.full() || force {
if err := combined.flush(dl.db.diskdb, dl.db.freezer, dl.nodes, bottom.stateID()); err != nil { if err := combined.flush(dl.db.diskdb, dl.db.freezer, dl.nodes, bottom.stateID()); err != nil {
return nil, err return nil, err
@ -225,6 +295,24 @@ func (dl *diskLayer) revert(h *history) (*diskLayer, error) {
if dl.id == 0 { if dl.id == 0 {
return nil, fmt.Errorf("%w: zero state id", errStateUnrecoverable) return nil, fmt.Errorf("%w: zero state id", errStateUnrecoverable)
} }
var (
buff = crypto.NewKeccakState()
hashes = make(map[common.Address]common.Hash)
accounts = make(map[common.Hash][]byte)
storages = make(map[common.Hash]map[common.Hash][]byte)
)
for addr, blob := range h.accounts {
hash := crypto.HashData(buff, addr.Bytes())
hashes[addr] = hash
accounts[hash] = blob
}
for addr, storage := range h.storages {
hash, ok := hashes[addr]
if !ok {
panic(fmt.Errorf("storage history with no account %x", addr))
}
storages[hash] = storage
}
// Apply the reverse state changes upon the current state. This must // Apply the reverse state changes upon the current state. This must
// be done before holding the lock in order to access state in "this" // be done before holding the lock in order to access state in "this"
// layer. // layer.
@ -244,7 +332,7 @@ func (dl *diskLayer) revert(h *history) (*diskLayer, error) {
// needs to be reverted is not yet flushed and cached in node // needs to be reverted is not yet flushed and cached in node
// buffer, otherwise, manipulate persistent state directly. // buffer, otherwise, manipulate persistent state directly.
if !dl.buffer.empty() { if !dl.buffer.empty() {
err := dl.buffer.revert(dl.db.diskdb, nodes) err := dl.buffer.revertTo(dl.db.diskdb, nodes, accounts, storages)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -45,7 +45,8 @@ var (
// //
// - Version 0: initial version // - Version 0: initial version
// - Version 1: storage.Incomplete field is removed // - Version 1: storage.Incomplete field is removed
const journalVersion uint64 = 1 // - Version 2: add post-modification state values
const journalVersion uint64 = 2
// loadJournal tries to parse the layer journal from the disk. // loadJournal tries to parse the layer journal from the disk.
func (db *Database) loadJournal(diskRoot common.Hash) (layer, error) { func (db *Database) loadJournal(diskRoot common.Hash) (layer, error) {
@ -108,7 +109,7 @@ func (db *Database) loadLayers() layer {
log.Info("Failed to load journal, discard it", "err", err) log.Info("Failed to load journal, discard it", "err", err)
} }
// Return single layer with persistent state. // Return single layer with persistent state.
return newDiskLayer(root, rawdb.ReadPersistentStateID(db.diskdb), db, nil, newBuffer(db.config.WriteBufferSize, nil, 0)) return newDiskLayer(root, rawdb.ReadPersistentStateID(db.diskdb), db, nil, newBuffer(db.config.WriteBufferSize, nil, nil, 0))
} }
// loadDiskLayer reads the binary blob from the layer journal, reconstructing // loadDiskLayer reads the binary blob from the layer journal, reconstructing
@ -135,7 +136,12 @@ func (db *Database) loadDiskLayer(r *rlp.Stream) (layer, error) {
if err := nodes.decode(r); err != nil { if err := nodes.decode(r); err != nil {
return nil, err return nil, err
} }
return newDiskLayer(root, id, db, nil, newBuffer(db.config.WriteBufferSize, &nodes, id-stored)), nil // Resolve flat state sets in aggregated buffer
var states stateSet
if err := states.decode(r); err != nil {
return nil, err
}
return newDiskLayer(root, id, db, nil, newBuffer(db.config.WriteBufferSize, &nodes, &states, id-stored)), nil
} }
// loadDiffLayer reads the next sections of a layer journal, reconstructing a new // loadDiffLayer reads the next sections of a layer journal, reconstructing a new
@ -189,6 +195,10 @@ func (dl *diskLayer) journal(w io.Writer) error {
if err := dl.buffer.nodes.encode(w); err != nil { if err := dl.buffer.nodes.encode(w); err != nil {
return err return err
} }
// Step four, write the accumulated flat states into the journal
if err := dl.buffer.states.encode(w); err != nil {
return err
}
log.Debug("Journaled pathdb disk layer", "root", dl.root) log.Debug("Journaled pathdb disk layer", "root", dl.root)
return nil return nil
} }

Some files were not shown because too many files have changed in this diff Show more