From 3af5e435c756fb246d7290d61bbf241eafe4dea2 Mon Sep 17 00:00:00 2001 From: marcello33 Date: Thu, 15 Jun 2023 12:27:20 +0200 Subject: [PATCH] dev: fix: some wsl lint issues --- accounts/abi/bind/backends/simulated.go | 4 + accounts/abi/bind/backends/simulated_test.go | 3 + accounts/abi/bind/base_test.go | 8 ++ cmd/abigen/main.go | 4 + cmd/abigen/namefilter.go | 3 + cmd/checkpoint-admin/common.go | 1 + cmd/clef/main.go | 45 ++++++ cmd/clef/run_test.go | 4 + cmd/devp2p/crawl.go | 9 ++ cmd/devp2p/discv4cmd.go | 9 ++ cmd/devp2p/dns_cloudflare.go | 4 + cmd/devp2p/dns_route53.go | 2 + cmd/devp2p/dnscmd.go | 2 + cmd/devp2p/internal/ethtest/chain.go | 1 + cmd/devp2p/internal/ethtest/helpers.go | 15 ++ cmd/devp2p/internal/ethtest/snap.go | 1 + cmd/devp2p/internal/ethtest/suite.go | 18 +++ cmd/devp2p/internal/ethtest/suite_test.go | 3 + cmd/devp2p/internal/ethtest/transaction.go | 5 + cmd/devp2p/internal/ethtest/types.go | 9 ++ cmd/devp2p/keycmd.go | 11 ++ cmd/devp2p/main.go | 4 + cmd/devp2p/nodeset.go | 1 + cmd/devp2p/rlpxcmd.go | 3 + cmd/ethkey/message.go | 2 + cmd/evm/blockrunner.go | 4 + cmd/evm/internal/t8ntool/block.go | 4 + cmd/evm/internal/t8ntool/execution.go | 3 + cmd/evm/internal/t8ntool/transition.go | 3 + cmd/evm/runner.go | 10 ++ cmd/evm/staterunner.go | 2 + cmd/evm/t8n_test.go | 1 + cmd/faucet/faucet.go | 1 + cmd/geth/accountcmd.go | 7 + cmd/geth/accountcmd_test.go | 6 + cmd/geth/attach_test.go | 14 +- cmd/geth/chaincmd.go | 12 ++ cmd/geth/config.go | 15 ++ cmd/geth/consolecmd.go | 2 + cmd/geth/dbcmd.go | 27 ++++ cmd/geth/exportcmd_test.go | 4 + cmd/geth/genesis_test.go | 6 + cmd/geth/main.go | 4 + cmd/geth/misccmd.go | 2 + cmd/geth/snapshot.go | 21 +++ cmd/geth/verkle.go | 23 +++- cmd/utils/cmd.go | 1 + cmd/utils/flags.go | 117 ++++++++++++++++ cmd/utils/flags_legacy.go | 1 + console/console.go | 3 + core/bench_test.go | 3 + core/block_validator.go | 1 + core/block_validator_test.go | 4 + core/blockchain.go | 63 ++++++++- core/blockchain_reader.go | 1 + core/blockchain_test.go | 101 +++++++++++++- core/chain_makers.go | 15 ++ core/chain_makers_test.go | 4 + core/dao_test.go | 12 ++ core/forkchoice.go | 1 + core/genesis.go | 31 +++++ core/genesis_test.go | 3 + core/headerchain.go | 5 + core/sender_cacher.go | 1 + core/state_prefetcher.go | 1 + core/state_processor.go | 3 + core/state_processor_test.go | 1 + core/state_transition.go | 22 +++ core/tests/blockchain_repair_test.go | 12 ++ core/tests/blockchain_sethead_test.go | 5 + core/tests/blockchain_snapshot_test.go | 5 + core/txpool/txpool_test.go | 3 + core/vm/runtime/runtime_test.go | 1 + eth/api.go | 10 ++ eth/api_backend.go | 17 +++ eth/api_test.go | 1 + eth/backend.go | 7 + eth/catalyst/api.go | 45 +++++- eth/catalyst/api_test.go | 128 ++++++++++++++++++ eth/catalyst/queue.go | 2 + eth/catalyst/tester.go | 4 + eth/downloader/beaconsync.go | 6 + eth/downloader/downloader.go | 24 +++- eth/downloader/downloader_test.go | 8 ++ eth/downloader/fetchers_concurrent.go | 1 + eth/downloader/queue.go | 2 + eth/downloader/queue_test.go | 1 + eth/downloader/skeleton.go | 15 +- eth/downloader/skeleton_test.go | 17 +++ eth/handler.go | 7 +- eth/handler_eth_test.go | 3 + eth/protocols/eth/handler.go | 1 + eth/protocols/eth/handler_test.go | 6 + eth/protocols/eth/handlers.go | 7 + eth/protocols/eth/protocol.go | 1 + eth/protocols/snap/sync_test.go | 13 ++ eth/state_accessor.go | 4 + eth/sync.go | 1 + eth/tracers/api.go | 38 ++++++ eth/tracers/api_bor.go | 1 + eth/tracers/api_test.go | 21 +++ .../internal/tracetest/calltrace_test.go | 6 + .../internal/tracetest/flat_calltrace_test.go | 14 ++ .../internal/tracetest/prestate_test.go | 2 + eth/tracers/internal/tracetest/util.go | 1 + eth/tracers/js/goja.go | 95 +++++++++++++ eth/tracers/js/tracer_test.go | 11 ++ eth/tracers/native/4byte.go | 1 + eth/tracers/native/call.go | 10 ++ eth/tracers/native/call_flat.go | 17 +++ eth/tracers/native/mux.go | 8 ++ eth/tracers/native/prestate.go | 8 ++ eth/tracers/tracers.go | 8 +- eth/tracers/tracers_test.go | 5 + eth/tracers/tracker.go | 8 ++ eth/tracers/tracker_test.go | 5 + ethclient/gethclient/gethclient.go | 5 + ethclient/gethclient/gethclient_test.go | 8 ++ graphql/graphql.go | 33 +++++ graphql/graphql_test.go | 14 ++ graphql/service.go | 2 + internal/cli/server/server_test.go | 1 + les/catalyst/api.go | 6 + les/catalyst/api_test.go | 3 + les/client.go | 7 + les/downloader/peer.go | 4 + les/downloader/queue.go | 1 + les/downloader/statesync.go | 9 ++ les/fetcher.go | 2 + les/fetcher_test.go | 1 + les/handler_test.go | 4 + les/odr.go | 1 + les/odr_test.go | 2 + les/server_handler.go | 2 + les/server_requests.go | 3 + les/state_accessor.go | 3 + les/test_helper.go | 2 + les/ulc_test.go | 1 + miner/stress/beacon/main.go | 7 + signer/core/signed_data_test.go | 15 ++ tests/block_test_util.go | 3 + tests/fuzzers/les/debug/main.go | 1 + tests/fuzzers/snap/debug/main.go | 1 + tests/fuzzers/snap/fuzz_handler.go | 1 + tests/state_test_util.go | 9 ++ 145 files changed, 1488 insertions(+), 15 deletions(-) diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index b8f93b9594..0a968df984 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -124,6 +124,7 @@ func (b *SimulatedBackend) Commit() common.Hash { if _, err := b.blockchain.InsertChain([]*types.Block{b.pendingBlock}); err != nil { panic(err) // This cannot happen unless the simulator is wrong, fail in that case } + blockHash := b.pendingBlock.Hash() // Using the last inserted block here makes it possible to build on a side @@ -669,6 +670,8 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM evmContext := core.NewEVMBlockContext(header, b.blockchain, nil) vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{NoBaseFee: true}) gasPool := new(core.GasPool).AddGas(math.MaxUint64) + + //nolint:contextcheck return core.ApplyMessage(vmEnv, msg, gasPool, context.Background()) } @@ -873,6 +876,7 @@ func (fb *filterBackend) GetBody(ctx context.Context, hash common.Hash, number r if body := fb.bc.GetBody(hash); body != nil { return body, nil } + return nil, errors.New("block body not found") } diff --git a/accounts/abi/bind/backends/simulated_test.go b/accounts/abi/bind/backends/simulated_test.go index d8e700f986..2a6fec1461 100644 --- a/accounts/abi/bind/backends/simulated_test.go +++ b/accounts/abi/bind/backends/simulated_test.go @@ -1349,6 +1349,7 @@ func TestCommitReturnValue(t *testing.T) { testAddr := crypto.PubkeyToAddress(testKey.PublicKey) sim := simTestBackend(testAddr) + defer sim.Close() startBlockHeight := sim.blockchain.CurrentBlock().Number.Uint64() @@ -1380,6 +1381,7 @@ func TestCommitReturnValue(t *testing.T) { if h2 == h2fork { t.Error("The block in the fork and the original block are the same block!") } + if sim.blockchain.GetHeader(h2fork, startBlockHeight+2) == nil { t.Error("Could not retrieve the just created block (side-chain)") } @@ -1392,6 +1394,7 @@ func TestAdjustTimeAfterFork(t *testing.T) { testAddr := crypto.PubkeyToAddress(testKey.PublicKey) sim := simTestBackend(testAddr) + defer sim.Close() sim.Commit() // h1 diff --git a/accounts/abi/bind/base_test.go b/accounts/abi/bind/base_test.go index 0576b268c0..8bb388b044 100644 --- a/accounts/abi/bind/base_test.go +++ b/accounts/abi/bind/base_test.go @@ -199,9 +199,11 @@ func TestUnpackAnonymousLogIntoMap(t *testing.T) { var received map[string]interface{} err := bc.UnpackLogIntoMap(received, "received", mockLog) + if err == nil { t.Error("unpacking anonymous event is not supported") } + if err.Error() != "no event signature" { t.Errorf("expected error 'no event signature', got '%s'", err) } @@ -384,6 +386,7 @@ func TestCall(t *testing.T) { t.Parallel() var method, methodWithArg = "something", "somethingArrrrg" + tests := []struct { name, method string opts *bind.CallOpts @@ -482,6 +485,7 @@ func TestCall(t *testing.T) { results: &[]interface{}{0}, wantErr: true, }} + for _, test := range tests { bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{ Methods: map[string]abi.Method{ @@ -496,15 +500,19 @@ func TestCall(t *testing.T) { }, }, test.mc, nil, nil) err := bc.Call(test.opts, test.results, test.method) + if test.wantErr || test.wantErrExact != nil { if err == nil { t.Fatalf("%q expected error", test.name) } + if test.wantErrExact != nil && !errors.Is(err, test.wantErrExact) { t.Fatalf("%q expected error %q but got %q", test.name, test.wantErrExact, err) } + continue } + if err != nil { t.Fatalf("%q unexpected error: %v", test.name, err) } diff --git a/cmd/abigen/main.go b/cmd/abigen/main.go index 221f45c078..896cc58009 100644 --- a/cmd/abigen/main.go +++ b/cmd/abigen/main.go @@ -99,6 +99,7 @@ func abigen(c *cli.Context) error { utils.Fatalf("No destination package specified (--pkg)") } var lang bind.Lang + switch c.String(langFlag.Name) { case "go": lang = bind.LangGo @@ -120,6 +121,7 @@ func abigen(c *cli.Context) error { abi []byte err error ) + input := c.String(abiFlag.Name) if input == "-" { abi, err = io.ReadAll(os.Stdin) @@ -132,6 +134,7 @@ func abigen(c *cli.Context) error { abis = append(abis, string(abi)) var bin []byte + if binFile := c.String(binFlag.Name); binFile != "" { if bin, err = os.ReadFile(binFile); err != nil { utils.Fatalf("Failed to read input bytecode: %v", err) @@ -225,6 +228,7 @@ func abigen(c *cli.Context) error { fmt.Printf("%s\n", code) return nil } + if err := os.WriteFile(c.String(outFlag.Name), []byte(code), 0600); err != nil { utils.Fatalf("Failed to write ABI binding: %v", err) } diff --git a/cmd/abigen/namefilter.go b/cmd/abigen/namefilter.go index eea5c643c4..e5fffb2851 100644 --- a/cmd/abigen/namefilter.go +++ b/cmd/abigen/namefilter.go @@ -22,6 +22,7 @@ func newNameFilter(patterns ...string) (*nameFilter, error) { return nil, err } } + return f, nil } @@ -40,7 +41,9 @@ func (f *nameFilter) add(pattern string) error { f.files[file] = true return nil } + f.fulls[pattern] = true + return nil } diff --git a/cmd/checkpoint-admin/common.go b/cmd/checkpoint-admin/common.go index f86ac24f06..acbe3f19b9 100644 --- a/cmd/checkpoint-admin/common.go +++ b/cmd/checkpoint-admin/common.go @@ -66,6 +66,7 @@ func getCheckpoint(ctx *cli.Context, client *rpc.Client) *params.TrustedCheckpoi if ctx.IsSet(indexFlag.Name) { var result [3]string + index := uint64(ctx.Int64(indexFlag.Name)) if err := client.Call(&result, "les_getCheckpoint", index); err != nil { utils.Fatalf("Failed to get local checkpoint %v, please ensure the les API is exposed", err) diff --git a/cmd/clef/main.go b/cmd/clef/main.go index d8052effc4..58530be186 100644 --- a/cmd/clef/main.go +++ b/cmd/clef/main.go @@ -329,6 +329,7 @@ func initializeSecrets(c *cli.Context) error { return fmt.Errorf("failed to read enough random") } n, p := keystore.StandardScryptN, keystore.StandardScryptP + if c.Bool(utils.LightKDFFlag.Name) { n, p = keystore.LightScryptN, keystore.LightScryptP } @@ -385,6 +386,7 @@ func attestFile(ctx *cli.Context) error { if err != nil { utils.Fatalf(err.Error()) } + configDir := ctx.String(configdirFlag.Name) vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10])) confKey := crypto.Keccak256([]byte("config"), stretchedKey) @@ -401,15 +403,18 @@ func initInternalApi(c *cli.Context) (*core.UIServerAPI, core.UIClientAPI, error if err := initialize(c); err != nil { return nil, nil, err } + var ( ui = core.NewCommandlineUI() pwStorage storage.Storage = &storage.NoStorage{} ksLoc = c.String(keystoreFlag.Name) lightKdf = c.Bool(utils.LightKDFFlag.Name) ) + am := core.StartClefAccountManager(ksLoc, true, lightKdf, "") api := core.NewSignerAPI(am, 0, true, ui, nil, false, pwStorage) internalApi := core.NewUIServerAPI(api) + return internalApi, ui, nil } @@ -432,6 +437,7 @@ func setCredential(ctx *cli.Context) error { if err != nil { utils.Fatalf(err.Error()) } + configDir := ctx.String(configdirFlag.Name) vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10])) pwkey := crypto.Keccak256([]byte("credentials"), stretchedKey) @@ -460,6 +466,7 @@ func removeCredential(ctx *cli.Context) error { if err != nil { utils.Fatalf(err.Error()) } + configDir := ctx.String(configdirFlag.Name) vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10])) pwkey := crypto.Keccak256([]byte("credentials"), stretchedKey) @@ -501,10 +508,13 @@ func newAccount(c *cli.Context) error { if err != nil { return err } + addr, err := internalApi.New(context.Background()) + if err == nil { fmt.Printf("Generated account %v\n", addr.String()) } + return err } @@ -513,17 +523,23 @@ func listAccounts(c *cli.Context) error { if err != nil { return err } + accs, err := internalApi.ListAccounts(context.Background()) + if err != nil { return err } + if len(accs) == 0 { fmt.Println("\nThe keystore is empty.") } + fmt.Println() + for _, account := range accs { fmt.Printf("%v (%v)\n", account.Address, account.URL) } + return err } @@ -532,18 +548,25 @@ func listWallets(c *cli.Context) error { if err != nil { return err } + wallets := internalApi.ListWallets() + if len(wallets) == 0 { fmt.Println("\nThere are no wallets.") } + fmt.Println() + for i, wallet := range wallets { fmt.Printf("- Wallet %d at %v (%v %v)\n", i, wallet.URL, wallet.Status, wallet.Failure) + for j, acc := range wallet.Accounts { fmt.Printf(" -Account %d: %v (%v)\n", j, acc.Address, acc.URL) } + fmt.Println() } + return nil } @@ -552,14 +575,19 @@ func accountImport(c *cli.Context) error { if c.Args().Len() != 1 { return errors.New(" must be given as first argument.") } + internalApi, ui, err := initInternalApi(c) + if err != nil { return err } + pKey, err := crypto.LoadECDSA(c.Args().First()) + if err != nil { return err } + readPw := func(prompt string) (string, error) { resp, err := ui.OnInputRequired(core.UserInputRequest{ Title: "Password", @@ -569,23 +597,31 @@ func accountImport(c *cli.Context) error { if err != nil { return "", err } + return resp.Text, nil } first, err := readPw("Please enter a password for the imported account") + if err != nil { return err } + second, err := readPw("Please repeat the password you just entered") + if err != nil { return err } + if first != second { return errors.New("Passwords do not match") } + acc, err := internalApi.ImportRawKey(hex.EncodeToString(crypto.FromECDSA(pKey)), first) + if err != nil { return err } + ui.ShowInfo(fmt.Sprintf(`Key imported: Address %v Keystore file: %v @@ -595,6 +631,7 @@ access to the key and all associated funds! Make sure to backup keystore and passwords in a safe location.`, acc.Address, acc.URL.Path)) + return nil } @@ -630,6 +667,7 @@ func signer(c *cli.Context) error { var ( ui core.UIClientAPI ) + if c.Bool(stdiouiFlag.Name) { log.Info("Using stdin/stdout as UI-channel") ui = core.NewStdIOUI() @@ -650,6 +688,7 @@ func signer(c *cli.Context) error { api core.ExternalAPI pwStorage storage.Storage = &storage.NoStorage{} ) + configDir := c.String(configdirFlag.Name) if stretchedKey, err := readMasterKey(c, ui); err != nil { log.Warn("Failed to open master, rules disabled", "err", err) @@ -727,6 +766,7 @@ func signer(c *cli.Context) error { Service: api, }, } + if c.Bool(utils.HTTPEnabledFlag.Name) { vhosts := utils.SplitAndTrim(c.String(utils.HTTPVirtualHostsFlag.Name)) cors := utils.SplitAndTrim(c.String(utils.HTTPCORSDomainFlag.Name)) @@ -756,6 +796,7 @@ func signer(c *cli.Context) error { log.Info("HTTP endpoint closed", "url", extapiURL) }() } + if !c.Bool(utils.IPCDisabledFlag.Name) { givenPath := c.String(utils.IPCPathFlag.Name) ipcapiURL = ipcEndpoint(filepath.Join(givenPath, "clef.ipc"), configDir) @@ -769,6 +810,7 @@ func signer(c *cli.Context) error { log.Info("IPC endpoint closed", "url", ipcapiURL) }() } + if c.Bool(testFlag.Name) { log.Info("Performing UI test") go testExternalUI(apiImpl) @@ -816,6 +858,7 @@ func readMasterKey(ctx *cli.Context, ui core.UIClientAPI) ([]byte, error) { file string configDir = ctx.String(configdirFlag.Name) ) + if ctx.IsSet(signerSecretFlag.Name) { file = ctx.String(signerSecretFlag.Name) } else { @@ -824,6 +867,7 @@ func readMasterKey(ctx *cli.Context, ui core.UIClientAPI) ([]byte, error) { if err := checkFile(file); err != nil { return nil, err } + cipherKey, err := os.ReadFile(file) if err != nil { return nil, err @@ -1215,5 +1259,6 @@ These data types are defined in the channel between clef and the UI`) for _, elem := range output { fmt.Println(elem) } + return nil } diff --git a/cmd/clef/run_test.go b/cmd/clef/run_test.go index cd7cd0e6f1..ecf0d576a3 100644 --- a/cmd/clef/run_test.go +++ b/cmd/clef/run_test.go @@ -50,6 +50,7 @@ func TestMain(m *testing.M) { if reexec.Init() { return } + os.Exit(m.Run()) } @@ -63,9 +64,11 @@ func runClef(t *testing.T, args ...string) *testproc { if err != nil { return nil } + t.Cleanup(func() { os.RemoveAll(ddir) }) + return runWithKeystore(t, ddir, args...) } @@ -81,6 +84,7 @@ func runWithKeystore(t *testing.T, keystore string, args ...string) *testproc { // Boot "clef". This actually runs the test binary but the TestMain // function will prevent any tests from running. tt.Run(registeredName, args...) + return tt } diff --git a/cmd/devp2p/crawl.go b/cmd/devp2p/crawl.go index 1b964164d8..b2ed37fa2d 100644 --- a/cmd/devp2p/crawl.go +++ b/cmd/devp2p/crawl.go @@ -78,6 +78,7 @@ func (c *crawler) run(timeout time.Duration, nthreads int) nodeSet { doneCh = make(chan enode.Iterator, len(c.iters)) liveIters = len(c.iters) ) + if nthreads < 1 { nthreads = 1 } @@ -86,6 +87,7 @@ func (c *crawler) run(timeout time.Duration, nthreads int) nodeSet { for _, it := range c.iters { go c.runIterator(doneCh, it) } + var ( added uint64 updated uint64 @@ -94,10 +96,13 @@ func (c *crawler) run(timeout time.Duration, nthreads int) nodeSet { removed uint64 wg sync.WaitGroup ) + wg.Add(nthreads) + for i := 0; i < nthreads; i++ { go func() { defer wg.Done() + for { select { case n := <-c.ch: @@ -183,6 +188,7 @@ func (c *crawler) updateNode(n *enode.Node) int { // Request the node record. status := nodeUpdated node.LastCheck = truncNow() + if nn, err := c.disc.RequestENR(n); err != nil { if node.Score == 0 { // Node doesn't implement EIP-868. @@ -206,10 +212,13 @@ func (c *crawler) updateNode(n *enode.Node) int { if node.Score <= 0 { log.Debug("Removing node", "id", n.ID()) delete(c.output, n.ID()) + return nodeRemoved } + log.Debug("Updating node", "id", n.ID(), "seq", n.Seq(), "score", node.Score) c.output[n.ID()] = node + return status } diff --git a/cmd/devp2p/discv4cmd.go b/cmd/devp2p/discv4cmd.go index 6312263478..f7b76777e6 100644 --- a/cmd/devp2p/discv4cmd.go +++ b/cmd/devp2p/discv4cmd.go @@ -282,18 +282,25 @@ func parseExtAddr(spec string) (ip net.IP, port int, ok bool) { if ip != nil { return ip, 0, true } + host, portstr, err := net.SplitHostPort(spec) + if err != nil { return nil, 0, false } + ip = net.ParseIP(host) + if ip == nil { return nil, 0, false } + port, err = strconv.Atoi(portstr) + if err != nil { return nil, 0, false } + return ip, port, true } @@ -326,7 +333,9 @@ func listen(ctx *cli.Context, ln *enode.LocalNode) *net.UDPConn { if !ok { exit(fmt.Errorf("-%s: invalid external address %q", extAddrFlag.Name, extAddr)) } + ln.SetStaticIP(ip) + if port != 0 { ln.SetFallbackUDP(port) } diff --git a/cmd/devp2p/dns_cloudflare.go b/cmd/devp2p/dns_cloudflare.go index 798fdc7332..7f6749cf0b 100644 --- a/cmd/devp2p/dns_cloudflare.go +++ b/cmd/devp2p/dns_cloudflare.go @@ -127,6 +127,7 @@ func (c *cloudflareClient) uploadRecords(name string, records map[string]string) // Iterate over the new records and inject anything missing. log.Info("Updating DNS entries") + created := 0 updated := 0 skipped := 0 @@ -156,9 +157,11 @@ func (c *cloudflareClient) uploadRecords(name string, records map[string]string) return fmt.Errorf("failed to publish %s: %v", path, err) } } + log.Info("Updated DNS entries", "new", created, "updated", updated, "untouched", skipped) // Iterate over the old records and delete anything stale. deleted := 0 + log.Info("Deleting stale DNS entries") for path, entry := range existing { if _, ok := records[path]; ok { @@ -171,6 +174,7 @@ func (c *cloudflareClient) uploadRecords(name string, records map[string]string) return fmt.Errorf("failed to delete %s: %v", path, err) } } + log.Info("Deleted stale DNS entries", "count", deleted) return nil } diff --git a/cmd/devp2p/dns_route53.go b/cmd/devp2p/dns_route53.go index 400ab1b1cf..c98a4cd4fe 100644 --- a/cmd/devp2p/dns_route53.go +++ b/cmd/devp2p/dns_route53.go @@ -279,6 +279,7 @@ func makeDeletionChanges(records map[string]recordSet, keep map[string]string) [ if _, ok := keep[path]; ok { continue } + log.Debug(fmt.Sprintf("Deleting %s = %s", path, strings.Join(set.values, ""))) changes = append(changes, newTXTChange("DELETE", path, set.ttl, set.values...)) } @@ -345,6 +346,7 @@ func (c *route53Client) collectRecords(name string) (map[string]recordSet, error var req route53.ListResourceRecordSetsInput req.HostedZoneId = &c.zoneID existing := make(map[string]recordSet) + log.Info("Loading existing TXT records", "name", name, "zone", c.zoneID) for page := 0; ; page++ { log.Debug("Loading existing TXT records", "name", name, "zone", c.zoneID, "page", page) diff --git a/cmd/devp2p/dnscmd.go b/cmd/devp2p/dnscmd.go index 58eb6e8db1..88baed51a9 100644 --- a/cmd/devp2p/dnscmd.go +++ b/cmd/devp2p/dnscmd.go @@ -381,6 +381,7 @@ func writeTreeMetadata(directory string, def *dnsDefinition) { exit(err) } metaFile, _ := treeDefinitionFiles(directory) + if err := os.WriteFile(metaFile, metaJSON, 0644); err != nil { exit(err) } @@ -410,6 +411,7 @@ func writeTXTJSON(file string, txt map[string]string) { fmt.Println() return } + if err := os.WriteFile(file, txtJSON, 0644); err != nil { exit(err) } diff --git a/cmd/devp2p/internal/ethtest/chain.go b/cmd/devp2p/internal/ethtest/chain.go index f4db05db8e..dec46c572d 100644 --- a/cmd/devp2p/internal/ethtest/chain.go +++ b/cmd/devp2p/internal/ethtest/chain.go @@ -139,6 +139,7 @@ func loadChain(chainfile string, genesis string) (*Chain, error) { if err != nil { return nil, err } + gblock := gen.ToBlock() blocks, err := blocksFromFile(chainfile, gblock) diff --git a/cmd/devp2p/internal/ethtest/helpers.go b/cmd/devp2p/internal/ethtest/helpers.go index 70ed2d2106..ef0e7e0731 100644 --- a/cmd/devp2p/internal/ethtest/helpers.go +++ b/cmd/devp2p/internal/ethtest/helpers.go @@ -208,14 +208,18 @@ loop: // node, and one for receiving messages from the node. func (s *Suite) createSendAndRecvConns() (*Conn, *Conn, error) { sendConn, err := s.dial() + if err != nil { return nil, nil, fmt.Errorf("dial failed: %v", err) } + recvConn, err := s.dial() + if err != nil { sendConn.Close() return nil, nil, fmt.Errorf("dial failed: %v", err) } + return sendConn, recvConn, nil } @@ -235,10 +239,12 @@ func (c *Conn) readAndServe(chain *Chain, timeout time.Duration) Message { if err != nil { return errorf("could not get headers for inbound header request: %v", err) } + resp := &BlockHeaders{ RequestId: msg.ReqID(), BlockHeadersPacket: eth.BlockHeadersPacket(headers), } + if err := c.Write(resp); err != nil { return errorf("could not write to connection: %v", err) } @@ -246,6 +252,7 @@ func (c *Conn) readAndServe(chain *Chain, timeout time.Duration) Message { return msg } } + return errorf("no message received within %v", timeout) } @@ -263,10 +270,13 @@ func (c *Conn) headersRequest(request *GetBlockHeaders, chain *Chain, reqID uint // wait for response msg := c.waitForResponse(chain, timeout, request.RequestId) resp, ok := msg.(*BlockHeaders) + if !ok { return nil, fmt.Errorf("unexpected message received: %s", pretty.Sdump(msg)) } + headers := []*types.Header(resp.BlockHeadersPacket) + return headers, nil } @@ -591,21 +601,26 @@ func (s *Suite) hashAnnounce() error { // Announcement sent, now wait for a header request msg := sendConn.Read() blockHeaderReq, ok := msg.(*GetBlockHeaders) + if !ok { return fmt.Errorf("unexpected %s", pretty.Sdump(msg)) } + if blockHeaderReq.Amount != 1 { return fmt.Errorf("unexpected number of block headers requested: %v", blockHeaderReq.Amount) } + if blockHeaderReq.Origin.Hash != announcement.Hash { return fmt.Errorf("unexpected block header requested. Announced:\n %v\n Remote request:\n%v", pretty.Sdump(announcement), pretty.Sdump(blockHeaderReq)) } + err = sendConn.Write(&BlockHeaders{ RequestId: blockHeaderReq.ReqID(), BlockHeadersPacket: eth.BlockHeadersPacket{nextBlock.Header()}, }) + if err != nil { return fmt.Errorf("failed to write to connection: %v", err) } diff --git a/cmd/devp2p/internal/ethtest/snap.go b/cmd/devp2p/internal/ethtest/snap.go index f947e4bc9b..ad80e268d4 100644 --- a/cmd/devp2p/internal/ethtest/snap.go +++ b/cmd/devp2p/internal/ethtest/snap.go @@ -357,6 +357,7 @@ func (s *Suite) TestSnapTrieNodes(t *utesting.T) { for i := 1; i <= 65; i++ { accPaths = append(accPaths, pathTo(i)) } + empty := types.EmptyCodeHash for i, tc := range []trieNodesTest{ { diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index 815353be72..c76b78e155 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -197,13 +197,16 @@ func (s *Suite) TestSimultaneousRequests(t *utesting.T) { if err != nil { t.Fatalf("failed to get expected headers for request 1: %v", err) } + expected2, err := s.chain.GetHeaders(req2) if err != nil { t.Fatalf("failed to get expected headers for request 2: %v", err) } + if !headersMatch(expected1, headers1.BlockHeadersPacket) { t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected1, headers1) } + if !headersMatch(expected2, headers2.BlockHeadersPacket) { t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected2, headers2) } @@ -245,6 +248,7 @@ func (s *Suite) TestSameRequestID(t *utesting.T) { if err = conn.Write(request1); err != nil { t.Fatalf("failed to write to connection: %v", err) } + if err = conn.Write(request2); err != nil { t.Fatalf("failed to write to connection: %v", err) } @@ -266,13 +270,16 @@ func (s *Suite) TestSameRequestID(t *utesting.T) { if err != nil { t.Fatalf("failed to get expected block headers: %v", err) } + expected2, err := s.chain.GetHeaders(request2) if err != nil { t.Fatalf("failed to get expected block headers: %v", err) } + if !headersMatch(expected1, headers1.BlockHeadersPacket) { t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected1, headers1) } + if !headersMatch(expected2, headers2.BlockHeadersPacket) { t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected2, headers2) } @@ -299,6 +306,7 @@ func (s *Suite) TestZeroRequestID(t *utesting.T) { if err != nil { t.Fatalf("failed to get block headers: %v", err) } + expected, err := s.chain.GetHeaders(req) if err != nil { t.Fatalf("failed to get expected block headers: %v", err) @@ -336,8 +344,10 @@ func (s *Suite) TestGetBlockBodies(t *utesting.T) { if !ok { t.Fatalf("unexpected: %s", pretty.Sdump(msg)) } + bodies := resp.BlockBodiesPacket t.Logf("received %d block bodies", len(bodies)) + if len(bodies) != len(req.GetBlockBodiesPacket) { t.Fatalf("wrong bodies in response: expected %d bodies, "+ "got %d", len(req.GetBlockBodiesPacket), len(bodies)) @@ -372,6 +382,7 @@ func (s *Suite) TestLargeAnnounce(t *utesting.T) { for i, blockAnnouncement := range blocks[0:3] { t.Logf("Testing malicious announcement: %v\n", i) + conn, err := s.dial() if err != nil { t.Fatalf("dial failed: %v", err) @@ -480,10 +491,12 @@ func (s *Suite) TestLargeTxRequest(t *utesting.T) { for _, hash := range hashMap { hashes = append(hashes, hash) } + getTxReq := &GetPooledTransactions{ RequestId: 1234, GetPooledTransactionsPacket: hashes, } + if err = conn.Write(getTxReq); err != nil { t.Fatalf("could not write to conn: %v", err) } @@ -514,9 +527,11 @@ func (s *Suite) TestNewPooledTxs(t *utesting.T) { if err != nil { t.Fatalf("failed to generate transactions: %v", err) } + hashes := make([]common.Hash, len(txs)) types := make([]byte, len(txs)) sizes := make([]uint32, len(txs)) + for i, tx := range txs { hashes[i] = tx.Hash() types[i] = tx.Type() @@ -534,10 +549,13 @@ func (s *Suite) TestNewPooledTxs(t *utesting.T) { } var ann Message = NewPooledTransactionHashes{Types: types, Sizes: sizes, Hashes: hashes} + if conn.negotiatedProtoVersion < eth.ETH68 { ann = NewPooledTransactionHashes66(hashes) } + err = conn.Write(ann) + if err != nil { t.Fatalf("failed to write to connection: %v", err) } diff --git a/cmd/devp2p/internal/ethtest/suite_test.go b/cmd/devp2p/internal/ethtest/suite_test.go index 8a2b132fa3..66cabbef19 100644 --- a/cmd/devp2p/internal/ethtest/suite_test.go +++ b/cmd/devp2p/internal/ethtest/suite_test.go @@ -45,8 +45,11 @@ func TestEthSuite(t *testing.T) { if err != nil { t.Fatalf("could not create new test suite: %v", err) } + for _, test := range suite.EthTests() { + test := test t.Run(test.Name, func(t *testing.T) { + t.Parallel() result := utesting.RunTAP([]utesting.Test{{Name: test.Name, Fn: test.Fn}}, os.Stdout) if result[0].Failed { t.Fatal() diff --git a/cmd/devp2p/internal/ethtest/transaction.go b/cmd/devp2p/internal/ethtest/transaction.go index bf3a4b7f06..56c33e16f5 100644 --- a/cmd/devp2p/internal/ethtest/transaction.go +++ b/cmd/devp2p/internal/ethtest/transaction.go @@ -115,6 +115,7 @@ func sendSuccessfulTx(s *Suite, tx *types.Transaction, prevTx *types.Transaction if len(txHashes) != len(msg.Sizes) { return fmt.Errorf("invalid msg size lengths: hashes: %v sizes: %v", len(txHashes), len(msg.Sizes)) } + if len(txHashes) != len(msg.Types) { return fmt.Errorf("invalid msg type lengths: hashes: %v types: %v", len(txHashes), len(msg.Types)) } @@ -124,11 +125,13 @@ func sendSuccessfulTx(s *Suite, tx *types.Transaction, prevTx *types.Transaction continue } } + for index, gotHash := range txHashes { if gotHash == tx.Hash() { if msg.Sizes[index] != uint32(tx.Size()) { return fmt.Errorf("invalid tx size: got %v want %v", msg.Sizes[index], tx.Size()) } + if msg.Types[index] != tx.Type() { return fmt.Errorf("invalid tx type: got %v want %v", msg.Types[index], tx.Type()) } @@ -136,6 +139,7 @@ func sendSuccessfulTx(s *Suite, tx *types.Transaction, prevTx *types.Transaction return nil } } + return fmt.Errorf("missing transaction announcement: got %v missing %v", txHashes, tx.Hash()) default: @@ -165,6 +169,7 @@ func (s *Suite) sendMaliciousTxs(t *utesting.T) error { for i, tx := range badTxs { t.Logf("Testing malicious tx propagation: %v\n", i) + if err = sendMaliciousTx(s, tx); err != nil { return fmt.Errorf("malicious tx test failed:\ntx: %v\nerror: %v", tx, err) } diff --git a/cmd/devp2p/internal/ethtest/types.go b/cmd/devp2p/internal/ethtest/types.go index 3c7b6dbcf1..6067fc9cd9 100644 --- a/cmd/devp2p/internal/ethtest/types.go +++ b/cmd/devp2p/internal/ethtest/types.go @@ -183,24 +183,28 @@ func (c *Conn) Read() Message { if err := rlp.DecodeBytes(rawData, ethMsg); err != nil { return errorf("could not rlp decode message: %v", err) } + return (*GetBlockHeaders)(ethMsg) case (BlockHeaders{}).Code(): ethMsg := new(eth.BlockHeadersPacket66) if err := rlp.DecodeBytes(rawData, ethMsg); err != nil { return errorf("could not rlp decode message: %v", err) } + return (*BlockHeaders)(ethMsg) case (GetBlockBodies{}).Code(): ethMsg := new(eth.GetBlockBodiesPacket66) if err := rlp.DecodeBytes(rawData, ethMsg); err != nil { return errorf("could not rlp decode message: %v", err) } + return (*GetBlockBodies)(ethMsg) case (BlockBodies{}).Code(): ethMsg := new(eth.BlockBodiesPacket66) if err := rlp.DecodeBytes(rawData, ethMsg); err != nil { return errorf("could not rlp decode message: %v", err) } + return (*BlockBodies)(ethMsg) case (NewBlock{}).Code(): msg = new(NewBlock) @@ -214,18 +218,21 @@ func (c *Conn) Read() Message { if err := rlp.DecodeBytes(rawData, ethMsg); err == nil { return ethMsg } + msg = new(NewPooledTransactionHashes66) case (GetPooledTransactions{}.Code()): ethMsg := new(eth.GetPooledTransactionsPacket66) if err := rlp.DecodeBytes(rawData, ethMsg); err != nil { return errorf("could not rlp decode message: %v", err) } + return (*GetPooledTransactions)(ethMsg) case (PooledTransactions{}.Code()): ethMsg := new(eth.PooledTransactionsPacket66) if err := rlp.DecodeBytes(rawData, ethMsg); err != nil { return errorf("could not rlp decode message: %v", err) } + return (*PooledTransactions)(ethMsg) default: msg = errorf("invalid message code: %d", code) @@ -235,8 +242,10 @@ func (c *Conn) Read() Message { if err := rlp.DecodeBytes(rawData, msg); err != nil { return errorf("could not rlp decode message: %v", err) } + return msg } + return errorf("invalid message: %s", string(rawData)) } diff --git a/cmd/devp2p/keycmd.go b/cmd/devp2p/keycmd.go index f409057fe0..c2de9ad185 100644 --- a/cmd/devp2p/keycmd.go +++ b/cmd/devp2p/keycmd.go @@ -102,7 +102,9 @@ func keyToID(ctx *cli.Context) error { if err != nil { return err } + fmt.Println(n.ID()) + return nil } @@ -111,7 +113,9 @@ func keyToURL(ctx *cli.Context) error { if err != nil { return err } + fmt.Println(n.URLv4()) + return nil } @@ -120,7 +124,9 @@ func keyToRecord(ctx *cli.Context) error { if err != nil { return err } + fmt.Println(n.String()) + return nil } @@ -141,16 +147,20 @@ func makeRecord(ctx *cli.Context) (*enode.Node, error) { } var r enr.Record + if host != "" { ip := net.ParseIP(host) if ip == nil { return nil, fmt.Errorf("invalid IP address %q", host) } + r.Set(enr.IP(ip)) } + if udp != 0 { r.Set(enr.UDP(udp)) } + if tcp != 0 { r.Set(enr.TCP(tcp)) } @@ -158,5 +168,6 @@ func makeRecord(ctx *cli.Context) (*enode.Node, error) { if err := enode.SignV4(&r, key); err != nil { return nil, err } + return enode.New(enode.ValidSchemes, &r) } diff --git a/cmd/devp2p/main.go b/cmd/devp2p/main.go index 8461a8b9b5..2bb7506cce 100644 --- a/cmd/devp2p/main.go +++ b/cmd/devp2p/main.go @@ -63,14 +63,17 @@ func main() { func commandHasFlag(ctx *cli.Context, flag cli.Flag) bool { names := flag.Names() set := make(map[string]struct{}, len(names)) + for _, name := range names { set[name] = struct{}{} } + for _, fn := range ctx.FlagNames() { if _, ok := set[fn]; ok { return true } } + return false } @@ -79,6 +82,7 @@ func getNodeArg(ctx *cli.Context) *enode.Node { if ctx.NArg() < 1 { exit("missing node as command-line argument") } + n, err := parseNode(ctx.Args().First()) if err != nil { exit(err) diff --git a/cmd/devp2p/nodeset.go b/cmd/devp2p/nodeset.go index 33c39f4b9e..6af6baa7ea 100644 --- a/cmd/devp2p/nodeset.go +++ b/cmd/devp2p/nodeset.go @@ -65,6 +65,7 @@ func writeNodesJSON(file string, nodes nodeSet) { os.Stdout.Write(nodesJSON) return } + if err := os.WriteFile(file, nodesJSON, 0644); err != nil { exit(err) } diff --git a/cmd/devp2p/rlpxcmd.go b/cmd/devp2p/rlpxcmd.go index 42b38120c4..a1d6fe2f95 100644 --- a/cmd/devp2p/rlpxcmd.go +++ b/cmd/devp2p/rlpxcmd.go @@ -105,10 +105,12 @@ func rlpxEthTest(ctx *cli.Context) error { if ctx.NArg() < 3 { exit("missing path to chain.rlp as command-line argument") } + suite, err := ethtest.NewSuite(getNodeArg(ctx), ctx.Args().Get(1), ctx.Args().Get(2)) if err != nil { exit(err) } + return runTests(ctx, suite.EthTests()) } @@ -117,6 +119,7 @@ func rlpxSnapTest(ctx *cli.Context) error { if ctx.NArg() < 3 { exit("missing path to chain.rlp as command-line argument") } + suite, err := ethtest.NewSuite(getNodeArg(ctx), ctx.Args().Get(1), ctx.Args().Get(2)) if err != nil { exit(err) diff --git a/cmd/ethkey/message.go b/cmd/ethkey/message.go index 6b8dec03cd..0f12f7b7bf 100644 --- a/cmd/ethkey/message.go +++ b/cmd/ethkey/message.go @@ -147,6 +147,7 @@ func getMessage(ctx *cli.Context, msgarg int) []byte { if ctx.NArg() > msgarg { utils.Fatalf("Can't use --msgfile and message argument at the same time.") } + msg, err := os.ReadFile(file) if err != nil { utils.Fatalf("Can't read message file: %v", err) @@ -155,6 +156,7 @@ func getMessage(ctx *cli.Context, msgarg int) []byte { } else if ctx.NArg() == msgarg+1 { return []byte(ctx.Args().Get(msgarg)) } + utils.Fatalf("Invalid number of arguments: want %d, got %d", msgarg+1, ctx.NArg()) return nil } diff --git a/cmd/evm/blockrunner.go b/cmd/evm/blockrunner.go index ffd76b8fb0..a50e7d9bca 100644 --- a/cmd/evm/blockrunner.go +++ b/cmd/evm/blockrunner.go @@ -48,14 +48,18 @@ func blockTestCmd(ctx *cli.Context) error { if err != nil { return err } + var tests map[string]tests.BlockTest + if err = json.Unmarshal(src, &tests); err != nil { return err } + for i, test := range tests { if err := test.Run(false); err != nil { return fmt.Errorf("test %v: %w", i, err) } } + return nil } diff --git a/cmd/evm/internal/t8ntool/block.go b/cmd/evm/internal/t8ntool/block.go index 73b04f420d..75f04bc4a0 100644 --- a/cmd/evm/internal/t8ntool/block.go +++ b/cmd/evm/internal/t8ntool/block.go @@ -157,6 +157,7 @@ func (i *bbInput) ToBlock() *types.Block { if header.Difficulty != nil { header.Difficulty = i.Header.Difficulty } + return types.NewBlockWithHeader(header).WithBody(i.Txs, i.Ommers).WithWithdrawals(i.Withdrawals) } @@ -317,11 +318,13 @@ func readInput(ctx *cli.Context) (*bbInput, error) { } inputData.OmmersRlp = ommers } + if withdrawalsStr != stdinSelector && withdrawalsStr != "" { var withdrawals []*types.Withdrawal if err := readFile(withdrawalsStr, "withdrawals", &withdrawals); err != nil { return nil, err } + inputData.Withdrawals = withdrawals } if txsStr != stdinSelector { @@ -367,6 +370,7 @@ func dispatchBlock(ctx *cli.Context, baseDir string, block *types.Block) error { Rlp hexutil.Bytes `json:"rlp"` Hash common.Hash `json:"hash"` } + enc := blockInfo{ Rlp: raw, Hash: block.Hash(), diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 6dcc3b846c..853143ae4e 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -175,6 +175,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, return nil, nil, err } vmConfig.Tracer = tracer + statedb.SetTxContext(tx.Hash(), txIndex) var ( @@ -190,6 +191,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, statedb.RevertToSnapshot(snapshot) log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err) rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()}) + gaspool.SetGas(prevGas) continue } @@ -285,6 +287,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, GasUsed: (math.HexOrDecimal64)(gasUsed), BaseFee: (*math.HexOrDecimal256)(vmContext.BaseFee), } + if pre.Env.Withdrawals != nil { h := types.DeriveSha(types.Withdrawals(pre.Env.Withdrawals), trie.NewStackTrie(nil)) execRs.WithdrawalsRoot = &h diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index c34eb76a08..a5dc03d6dc 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -265,8 +265,10 @@ func Transition(ctx *cli.Context) error { if chainConfig.IsShanghai(prestate.Env.Number) && prestate.Env.Withdrawals == nil { return NewError(ErrorConfig, errors.New("Shanghai config but missing 'withdrawals' in env section")) } + isMerged := chainConfig.TerminalTotalDifficulty != nil && chainConfig.TerminalTotalDifficulty.BitLen() == 0 env := prestate.Env + if isMerged { // post-merge: // - random must be supplied @@ -277,6 +279,7 @@ func Transition(ctx *cli.Context) error { case env.Difficulty != nil && env.Difficulty.BitLen() != 0: return NewError(ErrorConfig, errors.New("post-merge difficulty must be zero (or omitted) in env")) } + prestate.Env.Difficulty = nil } else if env.Difficulty == nil { // pre-merge: diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index 18ec3330c4..870fee5368 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -128,6 +128,7 @@ func runCmd(ctx *cli.Context) error { genesisConfig *core.Genesis preimages = ctx.Bool(DumpFlag.Name) ) + if ctx.Bool(MachineFlag.Name) { tracer = logger.NewJSONLogger(logconfig, os.Stdout) } else if ctx.Bool(DebugFlag.Name) { @@ -136,6 +137,7 @@ func runCmd(ctx *cli.Context) error { } else { debugLogger = logger.NewStructLogger(logconfig) } + if ctx.String(GenesisFlag.Name) != "" { gen := readGenesis(ctx.String(GenesisFlag.Name)) genesisConfig = gen @@ -149,6 +151,7 @@ func runCmd(ctx *cli.Context) error { statedb, _ = state.New(common.Hash{}, sdb, nil) genesisConfig = new(core.Genesis) } + if ctx.String(SenderFlag.Name) != "" { sender = common.HexToAddress(ctx.String(SenderFlag.Name)) } @@ -159,6 +162,7 @@ func runCmd(ctx *cli.Context) error { } var code []byte + codeFileFlag := ctx.String(CodeFileFlag.Name) codeFlag := ctx.String(CodeFlag.Name) @@ -202,6 +206,7 @@ func runCmd(ctx *cli.Context) error { } code = common.Hex2Bytes(bin) } + initialGas := ctx.Uint64(GasFlag.Name) if genesisConfig.GasLimit != 0 { initialGas = genesisConfig.GasLimit @@ -241,6 +246,7 @@ func runCmd(ctx *cli.Context) error { } var hexInput []byte + if inputFileFlag := ctx.String(InputFileFlag.Name); inputFileFlag != "" { var err error if hexInput, err = os.ReadFile(inputFileFlag); err != nil { @@ -250,14 +256,18 @@ func runCmd(ctx *cli.Context) error { } else { hexInput = []byte(ctx.String(InputFlag.Name)) } + hexInput = bytes.TrimSpace(hexInput) + if len(hexInput)%2 != 0 { fmt.Println("input length must be even") os.Exit(1) } + input := common.FromHex(string(hexInput)) var execFunc func() ([]byte, uint64, error) + if ctx.Bool(CreateFlag.Name) { input = append(code, input...) execFunc = func() ([]byte, uint64, error) { diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go index e9229eaeca..462530f7f8 100644 --- a/cmd/evm/staterunner.go +++ b/cmd/evm/staterunner.go @@ -103,6 +103,7 @@ func stateTestCmd(ctx *cli.Context) error { if s != nil { root := s.IntermediateRoot(false) result.Root = &root + if ctx.Bool(MachineFlag.Name) { fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root) } @@ -110,6 +111,7 @@ func stateTestCmd(ctx *cli.Context) error { if err != nil { // Test failed, mark as so and dump any state to aid debugging result.Pass, result.Error = false, err.Error() + if ctx.Bool(DumpFlag.Name) && s != nil { dump := s.RawDump(nil) result.State = &dump diff --git a/cmd/evm/t8n_test.go b/cmd/evm/t8n_test.go index de4ad11d7d..37eaa8c19c 100644 --- a/cmd/evm/t8n_test.go +++ b/cmd/evm/t8n_test.go @@ -420,6 +420,7 @@ func (args *b11rInput) get(base string) []string { out = append(out, "--input.ommers") out = append(out, fmt.Sprintf("%v/%v", base, opt)) } + if opt := args.inWithdrawals; opt != "" { out = append(out, "--input.withdrawals") out = append(out, fmt.Sprintf("%v/%v", base, opt)) diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go index 143166574b..735b0dfe1b 100644 --- a/cmd/faucet/faucet.go +++ b/cmd/faucet/faucet.go @@ -168,6 +168,7 @@ func main() { pass := strings.TrimSuffix(string(blob), "\n") ks := keystore.NewKeyStore(filepath.Join(os.Getenv("HOME"), ".faucet", "keys"), keystore.StandardScryptN, keystore.StandardScryptP) + if blob, err = os.ReadFile(*accJSONFlag); err != nil { log.Crit("Failed to read account key contents", "file", *accJSONFlag, "err", err) } diff --git a/cmd/geth/accountcmd.go b/cmd/geth/accountcmd.go index a36da7d55f..eb4de5d930 100644 --- a/cmd/geth/accountcmd.go +++ b/cmd/geth/accountcmd.go @@ -236,6 +236,7 @@ func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrErr } fmt.Println("Testing your password against all of them...") var match *accounts.Account + for i, a := range err.Matches { if e := ks.Unlock(a, auth); e == nil { match = &err.Matches[i] @@ -302,9 +303,11 @@ func accountUpdate(ctx *cli.Context) error { } stack, _ := makeConfigNode(ctx) backends := stack.AccountManager().Backends(keystore.KeyStoreType) + if len(backends) == 0 { utils.Fatalf("Keystore is not available") } + ks := backends[0].(*keystore.KeyStore) for _, addr := range ctx.Args().Slice() { @@ -321,6 +324,7 @@ func importWallet(ctx *cli.Context) error { if ctx.Args().Len() != 1 { utils.Fatalf("keyfile must be given as the only argument") } + keyfile := ctx.Args().First() keyJSON, err := os.ReadFile(keyfile) if err != nil { @@ -334,6 +338,7 @@ func importWallet(ctx *cli.Context) error { if len(backends) == 0 { utils.Fatalf("Keystore is not available") } + ks := backends[0].(*keystore.KeyStore) acct, err := ks.ImportPreSaleKey(keyJSON, passphrase) if err != nil { @@ -347,6 +352,7 @@ func accountImport(ctx *cli.Context) error { if ctx.Args().Len() != 1 { utils.Fatalf("keyfile must be given as the only argument") } + keyfile := ctx.Args().First() key, err := crypto.LoadECDSA(keyfile) if err != nil { @@ -359,6 +365,7 @@ func accountImport(ctx *cli.Context) error { if len(backends) == 0 { utils.Fatalf("Keystore is not available") } + ks := backends[0].(*keystore.KeyStore) acct, err := ks.ImportECDSA(key, passphrase) if err != nil { diff --git a/cmd/geth/accountcmd_test.go b/cmd/geth/accountcmd_test.go index 3aec802d02..26512974ba 100644 --- a/cmd/geth/accountcmd_test.go +++ b/cmd/geth/accountcmd_test.go @@ -49,6 +49,7 @@ func TestAccountListEmpty(t *testing.T) { func TestAccountList(t *testing.T) { datadir := tmpDatadirWithKeystore(t) + var want = ` Account #0: {7ef5a6135f1fd6a02593eedc869c6d41d934aef8} keystore://{{.Datadir}}/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8 Account #1: {f466859ead1932d743d622cb74fc058882e8648a} keystore://{{.Datadir}}/keystore/aaa @@ -122,12 +123,14 @@ func TestAccountHelp(t *testing.T) { geth := runGeth(t, "account", "-h") geth.WaitExit() + if have, want := geth.ExitStatus(), 0; have != want { t.Errorf("exit error, have %d want %d", have, want) } geth = runGeth(t, "account", "import", "-h") geth.WaitExit() + if have, want := geth.ExitStatus(), 0; have != want { t.Errorf("exit error, have %d want %d", have, want) } @@ -136,13 +139,16 @@ func TestAccountHelp(t *testing.T) { func importAccountWithExpect(t *testing.T, key string, expected string) { dir := t.TempDir() keyfile := filepath.Join(dir, "key.prv") + if err := os.WriteFile(keyfile, []byte(key), 0600); err != nil { t.Error(err) } passwordFile := filepath.Join(dir, "password.txt") + if err := os.WriteFile(passwordFile, []byte("foobar"), 0600); err != nil { t.Error(err) } + geth := runGeth(t, "--lightkdf", "account", "import", "-password", passwordFile, keyfile) defer geth.ExpectExit() geth.Expect(expected) diff --git a/cmd/geth/attach_test.go b/cmd/geth/attach_test.go index 7dd8bcd50d..396497e840 100644 --- a/cmd/geth/attach_test.go +++ b/cmd/geth/attach_test.go @@ -36,26 +36,31 @@ func (t *testHandler) ServeHTTP(out http.ResponseWriter, in *http.Request) { // that custom headers are forwarded to the target. func TestAttachWithHeaders(t *testing.T) { t.Parallel() + ln, err := net.Listen("tcp", "localhost:0") + if err != nil { t.Fatal(err) } - port := ln.Addr().(*net.TCPAddr).Port - testReceiveHeaders(t, ln, "attach", "-H", "first: one", "-H", "second: two", fmt.Sprintf("http://localhost:%d", port)) + // This way to do it fails due to flag ordering: - // // testReceiveHeaders(t, ln, "-H", "first: one", "-H", "second: two", "attach", fmt.Sprintf("http://localhost:%d", port)) // This is fixed in a follow-up PR. + port := ln.Addr().(*net.TCPAddr).Port + testReceiveHeaders(t, ln, "attach", "-H", "first: one", "-H", "second: two", fmt.Sprintf("http://localhost:%d", port)) } // TestAttachWithHeaders tests that 'geth db --remotedb' with custom headers works, i.e // that custom headers are forwarded to the target. func TestRemoteDbWithHeaders(t *testing.T) { t.Parallel() + ln, err := net.Listen("tcp", "localhost:0") + if err != nil { t.Fatal(err) } + port := ln.Addr().(*net.TCPAddr).Port testReceiveHeaders(t, ln, "db", "metadata", "--remotedb", fmt.Sprintf("http://localhost:%d", port), "-H", "first: one", "-H", "second: two") } @@ -64,6 +69,7 @@ func testReceiveHeaders(t *testing.T, ln net.Listener, gethArgs ...string) { t.Helper() var ok uint32 + server := &http.Server{ Addr: "localhost:0", Handler: &testHandler{func(w http.ResponseWriter, r *http.Request) { @@ -77,8 +83,10 @@ func testReceiveHeaders(t *testing.T, ln net.Listener, gethArgs ...string) { atomic.StoreUint32(&ok, 1) }}} go server.Serve(ln) + defer server.Close() runGeth(t, gethArgs...).WaitExit() + if atomic.LoadUint32(&ok) != 1 { t.Fatal("Test fail, expected invocation to succeed") } diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go index 96999075a3..a0f38f2eb2 100644 --- a/cmd/geth/chaincmd.go +++ b/cmd/geth/chaincmd.go @@ -195,6 +195,7 @@ func initGenesis(ctx *cli.Context) error { if err != nil { utils.Fatalf("Failed to open database: %v", err) } + triedb := trie.NewDatabaseWithConfig(chaindb, &trie.Config{ Preimages: ctx.Bool(utils.CachePreimagesFlag.Name), }) @@ -215,32 +216,41 @@ func dumpGenesis(ctx *cli.Context) error { if err := json.NewEncoder(os.Stdout).Encode(genesis); err != nil { utils.Fatalf("could not encode genesis: %s", err) } + return nil } // dump whatever already exists in the datadir stack, _ := makeConfigNode(ctx) for _, name := range []string{"chaindata", "lightchaindata"} { db, err := stack.OpenDatabase(name, 0, 0, "", true) + if err != nil { if !os.IsNotExist(err) { return err } + continue } + genesis, err := core.ReadGenesis(db) + if err != nil { utils.Fatalf("failed to read genesis: %s", err) } + db.Close() if err := json.NewEncoder(os.Stdout).Encode(*genesis); err != nil { utils.Fatalf("could not encode stored genesis: %s", err) } + return nil } + if ctx.IsSet(utils.DataDirFlag.Name) { utils.Fatalf("no existing datadir at %s", stack.Config().DataDir) } + utils.Fatalf("no network preset provided. no exisiting genesis in the default datadir") return nil } @@ -337,6 +347,7 @@ func exportChain(ctx *cli.Context) error { var err error fp := ctx.Args().First() + if ctx.Args().Len() < 3 { err = utils.ExportChain(chain, fp) } else { @@ -465,6 +476,7 @@ func dump(ctx *cli.Context) error { if err != nil { return err } + config := &trie.Config{ Preimages: true, // always enable preimage lookup } diff --git a/cmd/geth/config.go b/cmd/geth/config.go index c9dfc59f8b..08e24e4fb2 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -129,6 +129,7 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) { } utils.SetEthConfig(ctx, stack, &cfg.Eth) + if ctx.IsSet(utils.EthStatsURLFlag.Name) { cfg.Ethstats.URL = ctx.String(utils.EthStatsURLFlag.Name) } @@ -188,45 +189,59 @@ func dumpConfig(ctx *cli.Context) error { } func applyMetricConfig(ctx *cli.Context, cfg *gethConfig) { + if ctx.IsSet(utils.MetricsEnabledFlag.Name) { cfg.Metrics.Enabled = ctx.Bool(utils.MetricsEnabledFlag.Name) } + if ctx.IsSet(utils.MetricsEnabledExpensiveFlag.Name) { cfg.Metrics.EnabledExpensive = ctx.Bool(utils.MetricsEnabledExpensiveFlag.Name) } + if ctx.IsSet(utils.MetricsHTTPFlag.Name) { cfg.Metrics.HTTP = ctx.String(utils.MetricsHTTPFlag.Name) } + if ctx.IsSet(utils.MetricsPortFlag.Name) { cfg.Metrics.Port = ctx.Int(utils.MetricsPortFlag.Name) } + if ctx.IsSet(utils.MetricsEnableInfluxDBFlag.Name) { cfg.Metrics.EnableInfluxDB = ctx.Bool(utils.MetricsEnableInfluxDBFlag.Name) } + if ctx.IsSet(utils.MetricsInfluxDBEndpointFlag.Name) { cfg.Metrics.InfluxDBEndpoint = ctx.String(utils.MetricsInfluxDBEndpointFlag.Name) } + if ctx.IsSet(utils.MetricsInfluxDBDatabaseFlag.Name) { cfg.Metrics.InfluxDBDatabase = ctx.String(utils.MetricsInfluxDBDatabaseFlag.Name) } + if ctx.IsSet(utils.MetricsInfluxDBUsernameFlag.Name) { cfg.Metrics.InfluxDBUsername = ctx.String(utils.MetricsInfluxDBUsernameFlag.Name) } + if ctx.IsSet(utils.MetricsInfluxDBPasswordFlag.Name) { cfg.Metrics.InfluxDBPassword = ctx.String(utils.MetricsInfluxDBPasswordFlag.Name) } + if ctx.IsSet(utils.MetricsInfluxDBTagsFlag.Name) { cfg.Metrics.InfluxDBTags = ctx.String(utils.MetricsInfluxDBTagsFlag.Name) } + if ctx.IsSet(utils.MetricsEnableInfluxDBV2Flag.Name) { cfg.Metrics.EnableInfluxDBV2 = ctx.Bool(utils.MetricsEnableInfluxDBV2Flag.Name) } + if ctx.IsSet(utils.MetricsInfluxDBTokenFlag.Name) { cfg.Metrics.InfluxDBToken = ctx.String(utils.MetricsInfluxDBTokenFlag.Name) } + if ctx.IsSet(utils.MetricsInfluxDBBucketFlag.Name) { cfg.Metrics.InfluxDBBucket = ctx.String(utils.MetricsInfluxDBBucketFlag.Name) } + if ctx.IsSet(utils.MetricsInfluxDBOrganizationFlag.Name) { cfg.Metrics.InfluxDBOrganization = ctx.String(utils.MetricsInfluxDBOrganizationFlag.Name) } diff --git a/cmd/geth/consolecmd.go b/cmd/geth/consolecmd.go index 9464f974b0..4eb1c2bc33 100644 --- a/cmd/geth/consolecmd.go +++ b/cmd/geth/consolecmd.go @@ -138,6 +138,7 @@ func remoteConsole(ctx *cli.Context) error { } endpoint = fmt.Sprintf("%s/bor.ipc", path) } + client, err := utils.DialRPCWithHeaders(endpoint, ctx.StringSlice(utils.HttpHeaderFlag.Name)) if err != nil { utils.Fatalf("Unable to attach to remote geth: %v", err) @@ -173,6 +174,7 @@ func ephemeralConsole(ctx *cli.Context) error { for _, file := range ctx.Args().Slice() { b.Write([]byte(fmt.Sprintf("loadScript('%s');", file))) } + utils.Fatalf(`The "js" command is deprecated. Please use the following instead: geth --exec "%s" console`, b.String()) return nil diff --git a/cmd/geth/dbcmd.go b/cmd/geth/dbcmd.go index b409b19260..6ef4acc174 100644 --- a/cmd/geth/dbcmd.go +++ b/cmd/geth/dbcmd.go @@ -291,9 +291,11 @@ func checkStateContent(ctx *cli.Context) error { prefix []byte start []byte ) + if ctx.NArg() > 1 { return fmt.Errorf("max 1 argument: %v", ctx.Command.ArgsUsage) } + if ctx.NArg() > 0 { if d, err := hexutil.Decode(ctx.Args().First()); err != nil { return fmt.Errorf("failed to hex-decode 'start': %v", err) @@ -301,11 +303,13 @@ func checkStateContent(ctx *cli.Context) error { start = d } } + stack, _ := makeConfigNode(ctx) defer stack.Close() db := utils.MakeChainDatabase(ctx, stack, true) defer db.Close() + var ( it = rawdb.NewKeyLengthIterator(db.NewIterator(prefix, start), 32) hasher = crypto.NewKeccakState() @@ -315,28 +319,37 @@ func checkStateContent(ctx *cli.Context) error { startTime = time.Now() lastLog = time.Now() ) + for it.Next() { count++ + k := it.Key() v := it.Value() + hasher.Reset() hasher.Write(v) hasher.Read(got) + if !bytes.Equal(k, got) { errs++ + fmt.Printf("Error at %#x\n", k) fmt.Printf(" Hash: %#x\n", got) fmt.Printf(" Data: %#x\n", v) } + if time.Since(lastLog) > 8*time.Second { log.Info("Iterating the database", "at", fmt.Sprintf("%#x", k), "elapsed", common.PrettyDuration(time.Since(startTime))) lastLog = time.Now() } } + if err := it.Error(); err != nil { return err } + log.Info("Iterated the state content", "errors", errs, "items", count) + return nil } @@ -454,6 +467,7 @@ func dbPut(ctx *cli.Context) error { data []byte err error ) + key, err = common.ParseHexOrString(ctx.Args().Get(0)) if err != nil { log.Info("Could not decode the key", "error", err) @@ -490,30 +504,36 @@ func dbDumpTrie(ctx *cli.Context) error { max = int64(-1) err error ) + if state, err = hexutil.Decode(ctx.Args().Get(0)); err != nil { log.Info("Could not decode the state root", "error", err) return err } + if account, err = hexutil.Decode(ctx.Args().Get(1)); err != nil { log.Info("Could not decode the account hash", "error", err) return err } + if storage, err = hexutil.Decode(ctx.Args().Get(2)); err != nil { log.Info("Could not decode the storage trie root", "error", err) return err } + if ctx.NArg() > 3 { if start, err = hexutil.Decode(ctx.Args().Get(3)); err != nil { log.Info("Could not decode the seek position", "error", err) return err } } + if ctx.NArg() > 4 { if max, err = strconv.ParseInt(ctx.Args().Get(4), 10, 64); err != nil { log.Info("Could not decode the max count", "error", err) return err } } + id := trie.StorageTrieID(common.BytesToHash(state), common.BytesToHash(account), common.BytesToHash(storage)) theTrie, err := trie.New(id, trie.NewDatabase(db)) if err != nil { @@ -536,16 +556,21 @@ func freezerInspect(ctx *cli.Context) error { if ctx.NArg() < 4 { return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage) } + var ( freezer = ctx.Args().Get(0) table = ctx.Args().Get(1) ) + start, err := strconv.ParseInt(ctx.Args().Get(2), 10, 64) + if err != nil { log.Info("Could not read start-param", "err", err) return err } + end, err := strconv.ParseInt(ctx.Args().Get(3), 10, 64) + if err != nil { log.Info("Could not read count param", "err", err) return err @@ -553,6 +578,7 @@ func freezerInspect(ctx *cli.Context) error { stack, _ := makeConfigNode(ctx) ancient := stack.ResolveAncient("chaindata", ctx.String(utils.AncientFlag.Name)) stack.Close() + return rawdb.InspectFreezerTable(ancient, freezer, table, start, end) } @@ -694,6 +720,7 @@ func showMetaData(ctx *cli.Context) error { if err != nil { fmt.Fprintf(os.Stderr, "Error accessing ancients: %v", err) } + data := rawdb.ReadChainMetadata(db) data = append(data, []string{"frozen", fmt.Sprintf("%d items", ancients)}) data = append(data, []string{"snapshotGenerator", snapshot.ParseGeneratorStatus(rawdb.ReadSnapshotGenerator(db))}) diff --git a/cmd/geth/exportcmd_test.go b/cmd/geth/exportcmd_test.go index c280adcd61..8047e089fa 100644 --- a/cmd/geth/exportcmd_test.go +++ b/cmd/geth/exportcmd_test.go @@ -33,13 +33,17 @@ func TestExport(t *testing.T) { defer os.Remove(outfile) geth := runGeth(t, "--datadir", initGeth(t), "export", outfile) geth.WaitExit() + if have, want := geth.ExitStatus(), 0; have != want { t.Errorf("exit error, have %d want %d", have, want) } + have, err := os.ReadFile(outfile) + if err != nil { t.Fatal(err) } + want := common.FromHex("0xf9026bf90266a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a08758259b018f7bce3d2be2ddb62f325eaeea0a0c188cf96623eab468a4413e03a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000180837a12008080b875000000000000000000000000000000000000000000000000000000000000000002f0d131f1f97aef08aec6e3291b957d9efe71050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0") if !bytes.Equal(have, want) { t.Fatalf("wrong content exported") diff --git a/cmd/geth/genesis_test.go b/cmd/geth/genesis_test.go index 8e379b867a..80468e8e9a 100644 --- a/cmd/geth/genesis_test.go +++ b/cmd/geth/genesis_test.go @@ -101,6 +101,7 @@ func TestCustomBackend(t *testing.T) { if strconv.IntSize != 64 { t.Skip("Custom backends are only available on 64-bit platform") } + genesis := `{ "alloc" : {}, "coinbase" : "0x0000000000000000000000000000000000000000", @@ -113,13 +114,16 @@ func TestCustomBackend(t *testing.T) { "timestamp" : "0x00", "config" : {} }` + type backendTest struct { initArgs []string initExpect string execArgs []string execExpect string } + testfunc := func(t *testing.T, tt backendTest) error { + t.Helper() // Create a temporary data directory to use and inspect later datadir := t.TempDir() @@ -143,8 +147,10 @@ func TestCustomBackend(t *testing.T) { geth.ExpectRegexp(tt.execExpect) geth.ExpectExit() } + return nil } + for i, tt := range []backendTest{ { // When not specified, it should default to leveldb execArgs: []string{"--db.engine", "leveldb"}, diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 5d7a0600f1..f2d11b51ac 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -473,6 +473,7 @@ func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend, isCon // unlockAccounts unlocks any account specifically requested. func unlockAccounts(ctx *cli.Context, stack *node.Node) { var unlocks []string + inputs := strings.Split(ctx.String(utils.UnlockedAccountFlag.Name), ",") for _, input := range inputs { if trimmed := strings.TrimSpace(input); trimmed != "" { @@ -488,11 +489,14 @@ func unlockAccounts(ctx *cli.Context, stack *node.Node) { if !stack.Config().InsecureUnlockAllowed && stack.Config().ExtRPCEnabled() { utils.Fatalf("Account unlock with HTTP access is forbidden!") } + backends := stack.AccountManager().Backends(keystore.KeyStoreType) + if len(backends) == 0 { log.Warn("Failed to unlock accounts, keystore is not available") return } + ks := backends[0].(*keystore.KeyStore) passwords := utils.MakePasswordList(ctx) for i, account := range unlocks { diff --git a/cmd/geth/misccmd.go b/cmd/geth/misccmd.go index d8a523c632..76baa9a8ed 100644 --- a/cmd/geth/misccmd.go +++ b/cmd/geth/misccmd.go @@ -131,9 +131,11 @@ func printVersion(ctx *cli.Context) error { fmt.Println(strings.Title(clientIdentifier)) fmt.Println("Version:", params.VersionWithMeta) + if git.Commit != "" { fmt.Println("Git Commit:", git.Commit) } + if git.Date != "" { fmt.Println("Git Commit Date:", git.Date) } diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index 36519a9300..ac2f3d1045 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -207,6 +207,7 @@ func verifyState(ctx *cli.Context) error { log.Error("Failed to load head block") return errors.New("no head block") } + snapconfig := snapshot.Config{ CacheSize: 256, Recovery: false, @@ -235,6 +236,7 @@ func verifyState(ctx *cli.Context) error { return err } log.Info("Verified the state", "root", root) + return snapshot.CheckDanglingStorage(chaindb) } @@ -300,6 +302,7 @@ func traverseState(ctx *cli.Context) error { log.Error("Invalid account encountered during traversal", "err", err) return err } + if acc.Root != types.EmptyRootHash { id := trie.StorageTrieID(root, common.BytesToHash(accIter.Key), acc.Root) storageTrie, err := trie.NewStateTrie(id, triedb) @@ -316,6 +319,7 @@ func traverseState(ctx *cli.Context) error { return storageIter.Err } } + if !bytes.Equal(acc.CodeHash, types.EmptyCodeHash.Bytes()) { if !rawdb.HasCode(chaindb, common.BytesToHash(acc.CodeHash)) { log.Error("Code is missing", "hash", common.BytesToHash(acc.CodeHash)) @@ -398,9 +402,11 @@ func traverseRawState(ctx *cli.Context) error { log.Error("Missing trie node(account)", "hash", node) return errors.New("missing account") } + hasher.Reset() hasher.Write(blob) hasher.Read(got) + if !bytes.Equal(got, node.Bytes()) { log.Error("Invalid trie node(account)", "hash", node.Hex(), "value", blob) return errors.New("invalid account node") @@ -415,6 +421,7 @@ func traverseRawState(ctx *cli.Context) error { log.Error("Invalid account encountered during traversal", "err", err) return errors.New("invalid account") } + if acc.Root != types.EmptyRootHash { id := trie.StorageTrieID(root, common.BytesToHash(accIter.LeafKey()), acc.Root) storageTrie, err := trie.NewStateTrie(id, triedb) @@ -435,9 +442,11 @@ func traverseRawState(ctx *cli.Context) error { log.Error("Missing trie node(storage)", "hash", node) return errors.New("missing storage") } + hasher.Reset() hasher.Write(blob) hasher.Read(got) + if !bytes.Equal(got, node.Bytes()) { log.Error("Invalid trie node(storage)", "hash", node.Hex(), "value", blob) return errors.New("invalid storage node") @@ -453,6 +462,7 @@ func traverseRawState(ctx *cli.Context) error { return storageIter.Error() } } + if !bytes.Equal(acc.CodeHash, types.EmptyCodeHash.Bytes()) { if !rawdb.HasCode(chaindb, common.BytesToHash(acc.CodeHash)) { log.Error("Code is missing", "account", common.BytesToHash(accIter.LeafKey())) @@ -490,6 +500,7 @@ func dumpState(ctx *cli.Context) error { if err != nil { return err } + snapConfig := snapshot.Config{ CacheSize: 256, Recovery: false, @@ -528,6 +539,7 @@ func dumpState(ctx *cli.Context) error { CodeHash: account.CodeHash, SecureKey: accIt.Hash().Bytes(), } + if !conf.SkipCode && !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) { da.Code = rawdb.ReadCode(db, common.BytesToHash(account.CodeHash)) } @@ -564,10 +576,12 @@ func checkAccount(ctx *cli.Context) error { if ctx.NArg() != 1 { return errors.New("need arg") } + var ( hash common.Hash addr common.Address ) + switch arg := ctx.Args().First(); len(arg) { case 40, 42: addr = common.HexToAddress(arg) @@ -577,15 +591,22 @@ func checkAccount(ctx *cli.Context) error { default: return errors.New("malformed address or hash") } + stack, _ := makeConfigNode(ctx) defer stack.Close() + chaindb := utils.MakeChainDatabase(ctx, stack, true) defer chaindb.Close() + start := time.Now() + log.Info("Checking difflayer journal", "address", addr, "hash", hash) + if err := snapshot.CheckJournalAccount(chaindb, hash); err != nil { return err } + log.Info("Checked the snapshot journalled storage", "time", common.PrettyDuration(time.Since(start))) + return nil } diff --git a/cmd/geth/verkle.go b/cmd/geth/verkle.go index a5756ceab0..0c6299b444 100644 --- a/cmd/geth/verkle.go +++ b/cmd/geth/verkle.go @@ -77,9 +77,11 @@ func checkChildren(root verkle.VerkleNode, resolver verkle.NodeResolverFn) error childC := child.ComputeCommitment().Bytes() childS, err := resolver(childC[:]) + if bytes.Equal(childC[:], zero[:]) { continue } + if err != nil { return fmt.Errorf("could not find child %x in db: %w", childC, err) } @@ -88,18 +90,19 @@ func checkChildren(root verkle.VerkleNode, resolver verkle.NodeResolverFn) error if err != nil { return fmt.Errorf("decode error child %x in db: %w", child.ComputeCommitment().Bytes(), err) } + if err := checkChildren(childN, resolver); err != nil { return fmt.Errorf("%x%w", i, err) // write the path to the erroring node } } case *verkle.LeafNode: // sanity check: ensure at least one value is non-zero - for i := 0; i < verkle.NodeWidth; i++ { if len(node.Value(i)) != 0 { return nil } } + return fmt.Errorf("Both balance and nonce are 0") case verkle.Empty: // nothing to do @@ -116,24 +119,29 @@ func verifyVerkle(ctx *cli.Context) error { chaindb := utils.MakeChainDatabase(ctx, stack, true) headBlock := rawdb.ReadHeadBlock(chaindb) + if headBlock == nil { log.Error("Failed to load head block") return errors.New("no head block") } + if ctx.NArg() > 1 { log.Error("Too many arguments given") return errors.New("too many arguments") } + var ( rootC common.Hash err error ) + if ctx.NArg() == 1 { rootC, err = parseRoot(ctx.Args().First()) if err != nil { log.Error("Failed to resolve state root", "error", err) return err } + log.Info("Rebuilding the tree", "root", rootC) } else { rootC = headBlock.Root() @@ -144,7 +152,9 @@ func verifyVerkle(ctx *cli.Context) error { if err != nil { return err } + root, err := verkle.ParseNode(serializedRoot, 0, rootC[:]) + if err != nil { return err } @@ -155,6 +165,7 @@ func verifyVerkle(ctx *cli.Context) error { } log.Info("Tree was rebuilt from the database") + return nil } @@ -163,27 +174,34 @@ func expandVerkle(ctx *cli.Context) error { defer stack.Close() chaindb := utils.MakeChainDatabase(ctx, stack, true) + var ( rootC common.Hash keylist [][]byte err error ) + if ctx.NArg() >= 2 { rootC, err = parseRoot(ctx.Args().First()) if err != nil { log.Error("Failed to resolve state root", "error", err) return err } + keylist = make([][]byte, 0, ctx.Args().Len()-1) + args := ctx.Args().Slice() for i := range args[1:] { key, err := hex.DecodeString(args[i+1]) log.Info("decoded key", "arg", args[i+1], "key", key) + if err != nil { return fmt.Errorf("error decoding key #%d: %w", i+1, err) } + keylist = append(keylist, key) } + log.Info("Rebuilding the tree", "root", rootC) } else { return fmt.Errorf("usage: %s root key1 [key 2...]", ctx.App.Name) @@ -193,7 +211,9 @@ func expandVerkle(ctx *cli.Context) error { if err != nil { return err } + root, err := verkle.ParseNode(serializedRoot, 0, rootC[:]) + if err != nil { return err } @@ -208,5 +228,6 @@ func expandVerkle(ctx *cli.Context) error { } else { log.Info("Tree was dumped to file", "file", "dump.dot") } + return nil } diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go index e1bafc53c3..5fbd8a15b8 100644 --- a/cmd/utils/cmd.go +++ b/cmd/utils/cmd.go @@ -131,6 +131,7 @@ func monitorFreeDiskSpace(sigc chan os.Signal, path string, freeDiskSpaceCritica } else if freeSpace < 2*freeDiskSpaceCritical { log.Warn("Disk space is running low. Geth will shutdown if disk space runs below critical level.", "available", common.StorageSize(freeSpace), "critical_level", common.StorageSize(freeDiskSpaceCritical), "path", path) } + time.Sleep(30 * time.Second) } } diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index dfb5158824..34899d00d4 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1037,6 +1037,7 @@ func MakeDataDir(ctx *cli.Context) string { if ctx.Bool(GoerliFlag.Name) { return filepath.Join(path, "goerli") } + if ctx.Bool(SepoliaFlag.Name) { return filepath.Join(path, "sepolia") } @@ -1140,6 +1141,7 @@ func setListenAddress(ctx *cli.Context, cfg *p2p.Config) { if ctx.IsSet(ListenPortFlag.Name) { cfg.ListenAddr = fmt.Sprintf(":%d", ctx.Int(ListenPortFlag.Name)) } + if ctx.IsSet(DiscoveryPortFlag.Name) { cfg.DiscAddr = fmt.Sprintf(":%d", ctx.Int(DiscoveryPortFlag.Name)) } @@ -1209,6 +1211,7 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) { if ctx.IsSet(HTTPPathPrefixFlag.Name) { cfg.HTTPPathPrefix = ctx.String(HTTPPathPrefixFlag.Name) } + if ctx.IsSet(AllowUnprotectedTxs.Name) { cfg.AllowUnprotectedTxs = ctx.Bool(AllowUnprotectedTxs.Name) } @@ -1220,6 +1223,7 @@ func setGraphQL(ctx *cli.Context, cfg *node.Config) { if ctx.IsSet(GraphQLCORSDomainFlag.Name) { cfg.GraphQLCors = SplitAndTrim(ctx.String(GraphQLCORSDomainFlag.Name)) } + if ctx.IsSet(GraphQLVirtualHostsFlag.Name) { cfg.GraphQLVirtualHosts = SplitAndTrim(ctx.String(GraphQLVirtualHostsFlag.Name)) } @@ -1234,6 +1238,7 @@ func setWS(ctx *cli.Context, cfg *node.Config) { cfg.WSHost = ctx.String(WSListenAddrFlag.Name) } } + if ctx.IsSet(WSPortFlag.Name) { cfg.WSPort = ctx.Int(WSPortFlag.Name) } @@ -1268,31 +1273,40 @@ func setLes(ctx *cli.Context, cfg *ethconfig.Config) { if ctx.IsSet(LightServeFlag.Name) { cfg.LightServ = ctx.Int(LightServeFlag.Name) } + if ctx.IsSet(LightIngressFlag.Name) { cfg.LightIngress = ctx.Int(LightIngressFlag.Name) } + if ctx.IsSet(LightEgressFlag.Name) { cfg.LightEgress = ctx.Int(LightEgressFlag.Name) } + if ctx.IsSet(LightMaxPeersFlag.Name) { cfg.LightPeers = ctx.Int(LightMaxPeersFlag.Name) } + if ctx.IsSet(UltraLightServersFlag.Name) { cfg.UltraLightServers = strings.Split(ctx.String(UltraLightServersFlag.Name), ",") } + if ctx.IsSet(UltraLightFractionFlag.Name) { cfg.UltraLightFraction = ctx.Int(UltraLightFractionFlag.Name) } + if cfg.UltraLightFraction <= 0 && cfg.UltraLightFraction > 100 { log.Error("Ultra light fraction is invalid", "had", cfg.UltraLightFraction, "updated", ethconfig.Defaults.UltraLightFraction) cfg.UltraLightFraction = ethconfig.Defaults.UltraLightFraction } + if ctx.IsSet(UltraLightOnlyAnnounceFlag.Name) { cfg.UltraLightOnlyAnnounce = ctx.Bool(UltraLightOnlyAnnounceFlag.Name) } + if ctx.IsSet(LightNoPruneFlag.Name) { cfg.LightNoPrune = ctx.Bool(LightNoPruneFlag.Name) } + if ctx.IsSet(LightNoSyncServeFlag.Name) { cfg.LightNoSyncServe = ctx.Bool(LightNoSyncServeFlag.Name) } @@ -1355,15 +1369,20 @@ func setEtherbase(ctx *cli.Context, cfg *ethconfig.Config) { if !ctx.IsSet(MinerEtherbaseFlag.Name) { return } + addr := ctx.String(MinerEtherbaseFlag.Name) + if strings.HasPrefix(addr, "0x") || strings.HasPrefix(addr, "0X") { addr = addr[2:] } + b, err := hex.DecodeString(addr) + if err != nil || len(b) != common.AddressLength { Fatalf("-%s: invalid etherbase address %q", MinerEtherbaseFlag.Name, addr) return } + cfg.Miner.Etherbase = common.BytesToAddress(b) } @@ -1373,6 +1392,7 @@ func MakePasswordList(ctx *cli.Context) []string { if path == "" { return nil } + text, err := os.ReadFile(path) if err != nil { Fatalf("Failed to read password file: %v", err) @@ -1426,6 +1446,7 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) { if ctx.IsSet(MaxPendingPeersFlag.Name) { cfg.MaxPendingPeers = ctx.Int(MaxPendingPeersFlag.Name) } + if ctx.IsSet(NoDiscoverFlag.Name) || lightClient { cfg.NoDiscovery = true } @@ -1434,6 +1455,7 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) { // unless it is explicitly disabled with --nodiscover note that explicitly specifying // --v5disc overrides --nodiscover, in which case the later only disables v4 discovery forceV5Discovery := (lightClient || lightServer) && !ctx.Bool(NoDiscoverFlag.Name) + if ctx.IsSet(DiscoveryV5Flag.Name) { cfg.DiscoveryV5 = ctx.Bool(DiscoveryV5Flag.Name) } else if forceV5Discovery { @@ -1484,26 +1506,33 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) { if ctx.IsSet(KeyStoreDirFlag.Name) { cfg.KeyStoreDir = ctx.String(KeyStoreDirFlag.Name) } + if ctx.IsSet(DeveloperFlag.Name) { cfg.UseLightweightKDF = true } + if ctx.IsSet(LightKDFFlag.Name) { cfg.UseLightweightKDF = ctx.Bool(LightKDFFlag.Name) } + if ctx.IsSet(NoUSBFlag.Name) || cfg.NoUSB { log.Warn("Option nousb is deprecated and USB is deactivated by default. Use --usb to enable") } + if ctx.IsSet(USBFlag.Name) { cfg.USB = ctx.Bool(USBFlag.Name) } + if ctx.IsSet(InsecureUnlockAllowedFlag.Name) { cfg.InsecureUnlockAllowed = ctx.Bool(InsecureUnlockAllowedFlag.Name) } + if ctx.IsSet(DBEngineFlag.Name) { dbEngine := ctx.String(DBEngineFlag.Name) if dbEngine != "leveldb" && dbEngine != "pebble" { Fatalf("Invalid choice for db.engine '%s', allowed 'leveldb' or 'pebble'", dbEngine) } + log.Info(fmt.Sprintf("Using %s as db engine", dbEngine)) cfg.DBEngine = dbEngine } @@ -1517,10 +1546,12 @@ func setSmartCard(ctx *cli.Context, cfg *node.Config) { } // Sanity check that the smartcard path is valid fi, err := os.Stat(path) + if err != nil { log.Info("Smartcard socket not found, disabling", "err", err) return } + if fi.Mode()&os.ModeType != os.ModeSocket { log.Error("Invalid smartcard daemon path", "path", path, "type", fi.Mode().String()) return @@ -1548,15 +1579,19 @@ func setGPO(ctx *cli.Context, cfg *gasprice.Config, light bool) { if light { *cfg = ethconfig.LightClientGPO } + if ctx.IsSet(GpoBlocksFlag.Name) { cfg.Blocks = ctx.Int(GpoBlocksFlag.Name) } + if ctx.IsSet(GpoPercentileFlag.Name) { cfg.Percentile = ctx.Int(GpoPercentileFlag.Name) } + if ctx.IsSet(GpoMaxGasPriceFlag.Name) { cfg.MaxPrice = big.NewInt(ctx.Int64(GpoMaxGasPriceFlag.Name)) } + if ctx.IsSet(GpoIgnoreGasPriceFlag.Name) { cfg.IgnorePrice = big.NewInt(ctx.Int64(GpoIgnoreGasPriceFlag.Name)) } @@ -1573,33 +1608,43 @@ func setTxPool(ctx *cli.Context, cfg *txpool.Config) { } } } + if ctx.IsSet(TxPoolNoLocalsFlag.Name) { cfg.NoLocals = ctx.Bool(TxPoolNoLocalsFlag.Name) } + if ctx.IsSet(TxPoolJournalFlag.Name) { cfg.Journal = ctx.String(TxPoolJournalFlag.Name) } + if ctx.IsSet(TxPoolRejournalFlag.Name) { cfg.Rejournal = ctx.Duration(TxPoolRejournalFlag.Name) } + if ctx.IsSet(TxPoolPriceLimitFlag.Name) { cfg.PriceLimit = ctx.Uint64(TxPoolPriceLimitFlag.Name) } + if ctx.IsSet(TxPoolPriceBumpFlag.Name) { cfg.PriceBump = ctx.Uint64(TxPoolPriceBumpFlag.Name) } + if ctx.IsSet(TxPoolAccountSlotsFlag.Name) { cfg.AccountSlots = ctx.Uint64(TxPoolAccountSlotsFlag.Name) } + if ctx.IsSet(TxPoolGlobalSlotsFlag.Name) { cfg.GlobalSlots = ctx.Uint64(TxPoolGlobalSlotsFlag.Name) } + if ctx.IsSet(TxPoolAccountQueueFlag.Name) { cfg.AccountQueue = ctx.Uint64(TxPoolAccountQueueFlag.Name) } + if ctx.IsSet(TxPoolGlobalQueueFlag.Name) { cfg.GlobalQueue = ctx.Uint64(TxPoolGlobalQueueFlag.Name) } + if ctx.IsSet(TxPoolLifetimeFlag.Name) { cfg.Lifetime = ctx.Duration(TxPoolLifetimeFlag.Name) } @@ -1609,24 +1654,31 @@ func setEthash(ctx *cli.Context, cfg *ethconfig.Config) { if ctx.IsSet(EthashCacheDirFlag.Name) { cfg.Ethash.CacheDir = ctx.String(EthashCacheDirFlag.Name) } + if ctx.IsSet(EthashDatasetDirFlag.Name) { cfg.Ethash.DatasetDir = ctx.String(EthashDatasetDirFlag.Name) } + if ctx.IsSet(EthashCachesInMemoryFlag.Name) { cfg.Ethash.CachesInMem = ctx.Int(EthashCachesInMemoryFlag.Name) } + if ctx.IsSet(EthashCachesOnDiskFlag.Name) { cfg.Ethash.CachesOnDisk = ctx.Int(EthashCachesOnDiskFlag.Name) } + if ctx.IsSet(EthashCachesLockMmapFlag.Name) { cfg.Ethash.CachesLockMmap = ctx.Bool(EthashCachesLockMmapFlag.Name) } + if ctx.IsSet(EthashDatasetsInMemoryFlag.Name) { cfg.Ethash.DatasetsInMem = ctx.Int(EthashDatasetsInMemoryFlag.Name) } + if ctx.IsSet(EthashDatasetsOnDiskFlag.Name) { cfg.Ethash.DatasetsOnDisk = ctx.Int(EthashDatasetsOnDiskFlag.Name) } + if ctx.IsSet(EthashDatasetsLockMmapFlag.Name) { cfg.Ethash.DatasetsLockMmap = ctx.Bool(EthashDatasetsLockMmapFlag.Name) } @@ -1636,22 +1688,29 @@ func setMiner(ctx *cli.Context, cfg *miner.Config) { if ctx.IsSet(MinerNotifyFlag.Name) { cfg.Notify = strings.Split(ctx.String(MinerNotifyFlag.Name), ",") } + cfg.NotifyFull = ctx.Bool(MinerNotifyFullFlag.Name) + if ctx.IsSet(MinerExtraDataFlag.Name) { cfg.ExtraData = []byte(ctx.String(MinerExtraDataFlag.Name)) } + if ctx.IsSet(MinerGasLimitFlag.Name) { cfg.GasCeil = ctx.Uint64(MinerGasLimitFlag.Name) } + if ctx.IsSet(MinerGasPriceFlag.Name) { cfg.GasPrice = flags.GlobalBig(ctx, MinerGasPriceFlag.Name) } + if ctx.IsSet(MinerRecommitIntervalFlag.Name) { cfg.Recommit = ctx.Duration(MinerRecommitIntervalFlag.Name) } + if ctx.IsSet(MinerNoVerifyFlag.Name) { cfg.Noverify = ctx.Bool(MinerNoVerifyFlag.Name) } + if ctx.IsSet(MinerNewPayloadTimeout.Name) { cfg.NewPayloadTimeout = ctx.Duration(MinerNewPayloadTimeout.Name) } @@ -1662,12 +1721,15 @@ func setRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) { if requiredBlocks == "" { if ctx.IsSet(LegacyWhitelistFlag.Name) { log.Warn("The flag --whitelist is deprecated and will be removed, please use --eth.requiredblocks") + requiredBlocks = ctx.String(LegacyWhitelistFlag.Name) } else { return } } + cfg.RequiredBlocks = make(map[uint64]common.Hash) + for _, entry := range strings.Split(requiredBlocks, ",") { parts := strings.Split(entry, "=") if len(parts) != 2 { @@ -1681,6 +1743,7 @@ func setRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) { if err = hash.UnmarshalText([]byte(parts[1])); err != nil { Fatalf("Invalid required block hash %s: %v", parts[1], err) } + cfg.RequiredBlocks[number] = hash } } @@ -1736,9 +1799,11 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { ctx.Set(TxLookupLimitFlag.Name, "0") log.Warn("Disable transaction unindexing for archive node") } + if ctx.IsSet(LightServeFlag.Name) && ctx.Uint64(TxLookupLimitFlag.Name) != 0 { log.Warn("LES server cannot serve old transaction status and cannot connect below les/4 protocol version if transaction lookup index is limited") } + setEtherbase(ctx, cfg) setGPO(ctx, &cfg.GPO, ctx.String(SyncModeFlag.Name) == "light") setTxPool(ctx, &cfg.TxPool) @@ -1755,6 +1820,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { mem.Total = 2 * 1024 * 1024 * 1024 } allowance := int(mem.Total / 1024 / 1024 / 3) + if cache := ctx.Int(CacheFlag.Name); cache > allowance { log.Warn("Sanitizing cache to Go's GC limits", "provided", cache, "updated", allowance) ctx.Set(CacheFlag.Name, strconv.Itoa(allowance)) @@ -1770,13 +1836,17 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { if ctx.IsSet(SyncModeFlag.Name) { cfg.SyncMode = *flags.GlobalTextMarshaler(ctx, SyncModeFlag.Name).(*downloader.SyncMode) } + if ctx.IsSet(NetworkIdFlag.Name) { cfg.NetworkId = ctx.Uint64(NetworkIdFlag.Name) } + if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheDatabaseFlag.Name) { cfg.DatabaseCache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheDatabaseFlag.Name) / 100 } + cfg.DatabaseHandles = MakeDatabaseHandles(ctx.Int(FDLimitFlag.Name)) + if ctx.IsSet(AncientFlag.Name) { cfg.DatabaseFreezer = ctx.String(AncientFlag.Name) } @@ -1784,39 +1854,50 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { if gcmode := ctx.String(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" { Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name) } + if ctx.IsSet(GCModeFlag.Name) { cfg.NoPruning = ctx.String(GCModeFlag.Name) == "archive" } + if ctx.IsSet(CacheNoPrefetchFlag.Name) { cfg.NoPrefetch = ctx.Bool(CacheNoPrefetchFlag.Name) } // Read the value from the flag no matter if it's set or not. cfg.Preimages = ctx.Bool(CachePreimagesFlag.Name) + if cfg.NoPruning && !cfg.Preimages { cfg.Preimages = true log.Info("Enabling recording of key preimages since archive mode is used") } + if ctx.IsSet(TxLookupLimitFlag.Name) { cfg.TxLookupLimit = ctx.Uint64(TxLookupLimitFlag.Name) } + if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheTrieFlag.Name) { cfg.TrieCleanCache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheTrieFlag.Name) / 100 } + if ctx.IsSet(CacheTrieJournalFlag.Name) { cfg.TrieCleanCacheJournal = ctx.String(CacheTrieJournalFlag.Name) } + if ctx.IsSet(CacheTrieRejournalFlag.Name) { cfg.TrieCleanCacheRejournal = ctx.Duration(CacheTrieRejournalFlag.Name) } + if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheGCFlag.Name) { cfg.TrieDirtyCache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheGCFlag.Name) / 100 } + if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheSnapshotFlag.Name) { cfg.SnapshotCache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheSnapshotFlag.Name) / 100 } + if ctx.IsSet(CacheLogSizeFlag.Name) { cfg.FilterLogCacheSize = ctx.Int(CacheLogSizeFlag.Name) } + if !ctx.Bool(SnapshotFlag.Name) { // If snap-sync is requested, this flag is also required if cfg.SyncMode == downloader.SnapSync { @@ -1826,9 +1907,11 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { cfg.SnapshotCache = 0 // Disabled } } + if ctx.IsSet(DocRootFlag.Name) { cfg.DocRoot = ctx.String(DocRootFlag.Name) } + if ctx.IsSet(VMEnableDebugFlag.Name) { // TODO(fjl): force-enable this in --dev mode cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name) @@ -1842,12 +1925,15 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { } else { log.Info("Global gas cap disabled") } + if ctx.IsSet(RPCGlobalEVMTimeoutFlag.Name) { cfg.RPCEVMTimeout = ctx.Duration(RPCGlobalEVMTimeoutFlag.Name) } + if ctx.IsSet(RPCGlobalTxFeeCapFlag.Name) { cfg.RPCTxFeeCap = ctx.Float64(RPCGlobalTxFeeCapFlag.Name) } + if ctx.IsSet(NoDiscoverFlag.Name) { cfg.EthDiscoveryURLs, cfg.SnapDiscoveryURLs = []string{}, []string{} } else if ctx.IsSet(DNSDiscoveryFlag.Name) { @@ -1902,6 +1988,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { if keystores := stack.AccountManager().Backends(keystore.KeyStoreType); len(keystores) > 0 { ks = keystores[0].(*keystore.KeyStore) } + if ks == nil { Fatalf("Keystore is not available") } @@ -1929,6 +2016,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { // Create a new developer genesis block or reuse existing one cfg.Genesis = core.DeveloperGenesisBlock(uint64(ctx.Int(DeveloperPeriodFlag.Name)), ctx.Uint64(DeveloperGasLimitFlag.Name), developer.Address) + if ctx.IsSet(DataDirFlag.Name) { // If datadir doesn't exist we need to open db in write-mode // so leveldb can create files. @@ -1944,6 +2032,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { } chaindb.Close() } + if !ctx.IsSet(MinerGasPriceFlag.Name) { cfg.Miner.GasPrice = big.NewInt(1) } @@ -1980,6 +2069,7 @@ func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) (ethapi.Backend Fatalf("Failed to register the Ethereum service: %v", err) } stack.RegisterAPIs(tracers.APIs(backend.ApiBackend)) + if err := lescatalyst.Register(stack, backend); err != nil { Fatalf("Failed to register the Engine API service: %v", err) } @@ -1995,6 +2085,7 @@ func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) (ethapi.Backend Fatalf("Failed to create the LES server: %v", err) } } + if err := ethcatalyst.Register(stack, backend); err != nil { Fatalf("Failed to register the Engine API service: %v", err) } @@ -2023,10 +2114,12 @@ func RegisterFilterAPI(stack *node.Node, backend ethapi.Backend, ethcfg *ethconf filterSystem := filters.NewFilterSystem(backend, filters.Config{ LogCacheSize: ethcfg.FilterLogCacheSize, }) + stack.RegisterAPIs([]rpc.API{{ Namespace: "eth", Service: filters.NewFilterAPI(filterSystem, isLightClient, ethconfig.Defaults.BorLogs), }}) + return filterSystem } @@ -2036,11 +2129,15 @@ func RegisterFullSyncTester(stack *node.Node, eth *eth.Ethereum, path string) { if err != nil { Fatalf("Failed to read block file: %v", err) } + rlpBlob, err := hexutil.Decode(string(bytes.TrimRight(blob, "\r\n"))) + if err != nil { Fatalf("Failed to decode block blob: %v", err) } + var block types.Block + if err := rlp.DecodeBytes(rlpBlob, &block); err != nil { Fatalf("Failed to decode block: %v", err) } @@ -2135,13 +2232,16 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node, readonly bool) ethdb. err error chainDb ethdb.Database ) + switch { case ctx.IsSet(RemoteDBFlag.Name): log.Info("Using remote db", "url", ctx.String(RemoteDBFlag.Name), "headers", len(ctx.StringSlice(HttpHeaderFlag.Name))) client, err := DialRPCWithHeaders(ctx.String(RemoteDBFlag.Name), ctx.StringSlice(HttpHeaderFlag.Name)) + if err != nil { break } + chainDb = remotedb.New(client) case ctx.String(SyncModeFlag.Name) == "light": chainDb, err = stack.OpenDatabase("lightchaindata", cache, handles, "", readonly) @@ -2161,6 +2261,7 @@ func IsNetworkPreset(ctx *cli.Context) bool { return true } } + return false } @@ -2168,23 +2269,30 @@ func DialRPCWithHeaders(endpoint string, headers []string) (*rpc.Client, error) if endpoint == "" { return nil, errors.New("endpoint must be specified") } + if strings.HasPrefix(endpoint, "rpc:") || strings.HasPrefix(endpoint, "ipc:") { // Backwards compatibility with geth < 1.5 which required // these prefixes. endpoint = endpoint[4:] } + var opts []rpc.ClientOption + if len(headers) > 0 { var customHeaders = make(http.Header) + for _, h := range headers { kv := strings.Split(h, ":") if len(kv) != 2 { return nil, fmt.Errorf("invalid http header directive: %q", h) } + customHeaders.Add(kv[0], kv[1]) } + opts = append(opts, rpc.WithHeaders(customHeaders)) } + return rpc.DialOptions(context.Background(), endpoint, opts...) } @@ -2209,18 +2317,23 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh gspec = MakeGenesis(ctx) chainDb = MakeChainDatabase(ctx, stack, readonly) ) + config, _, err := core.SetupGenesisBlock(chainDb, trie.NewDatabase(chainDb), gspec) if err != nil { Fatalf("%v", err) } + cliqueConfig, err := core.LoadCliqueConfig(chainDb, gspec) if err != nil { Fatalf("%v", err) } + ethashConfig := ethconfig.Defaults.Ethash + if ctx.Bool(FakePoWFlag.Name) { ethashConfig.PowMode = ethash.ModeFake } + configs := ðconfig.Config{ Genesis: gspec, HeimdallURL: ctx.String(HeimdallURLFlag.Name), @@ -2232,6 +2345,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh } _ = CreateBorEthereum(configs) engine := ethconfig.CreateConsensusEngine(stack, config, configs, ðashConfig, cliqueConfig, nil, false, chainDb, nil) + if gcmode := ctx.String(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" { Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name) } @@ -2248,6 +2362,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh cache.Preimages = true log.Info("Enabling recording of key preimages since archive mode is used") } + if !ctx.Bool(SnapshotFlag.Name) { cache.SnapshotLimit = 0 // Disabled } @@ -2259,9 +2374,11 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheTrieFlag.Name) { cache.TrieCleanLimit = ctx.Int(CacheFlag.Name) * ctx.Int(CacheTrieFlag.Name) / 100 } + if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheGCFlag.Name) { cache.TrieDirtyLimit = ctx.Int(CacheFlag.Name) * ctx.Int(CacheGCFlag.Name) / 100 } + vmcfg := vm.Config{EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name)} // Disable transaction indexing/unindexing by default. diff --git a/cmd/utils/flags_legacy.go b/cmd/utils/flags_legacy.go index 930b68fb91..973bd0c1a9 100644 --- a/cmd/utils/flags_legacy.go +++ b/cmd/utils/flags_legacy.go @@ -54,5 +54,6 @@ func showDeprecated(*cli.Context) error { fmt.Println(flag.String()) } fmt.Println() + return nil } diff --git a/console/console.go b/console/console.go index 8190921210..80950bd60a 100644 --- a/console/console.go +++ b/console/console.go @@ -182,6 +182,7 @@ func (c *Console) initWeb3(bridge *bridge) error { if err := c.jsre.Compile("bignumber.js", deps.BigNumberJS); err != nil { return fmt.Errorf("bignumber.js: %v", err) } + if err := c.jsre.Compile("web3.js", deps.Web3JS); err != nil { return fmt.Errorf("web3.js: %v", err) } @@ -208,6 +209,7 @@ func (c *Console) initExtensions() error { if err != nil { if rpcErr, ok := err.(rpc.Error); ok && rpcErr.ErrorCode() == methodNotFound { log.Warn("Server does not support method rpc_modules, using default API list.") + apis = defaultAPIs } else { return err @@ -260,6 +262,7 @@ func (c *Console) initPersonal(vm *goja.Runtime, bridge *bridge) { if personal == nil || c.prompter == nil { return } + log.Warn("Enabling deprecated personal namespace") jeth := vm.NewObject() vm.Set("jeth", jeth) diff --git a/core/bench_test.go b/core/bench_test.go index 41f92bd758..7b6b56222b 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -173,6 +173,7 @@ func genUncles(i int, gen *BlockGen) { func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) { // Create the database in memory or in a temporary directory. var db ethdb.Database + var err error if !disk { db = rawdb.NewMemoryDatabase() @@ -264,6 +265,7 @@ func makeChainForBench(db ethdb.Database, full bool, count uint64) { if n == 0 { rawdb.WriteChainConfig(db, hash, params.AllEthashProtocolChanges) } + rawdb.WriteHeadHeaderHash(db, hash) if full || n == 0 { @@ -307,6 +309,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) { if err != nil { b.Fatalf("error opening database at %v: %v", dir, err) } + chain, err := NewBlockChain(db, &cacheConfig, nil, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil) if err != nil { b.Fatalf("error creating chain: %v", err) diff --git a/core/block_validator.go b/core/block_validator.go index bcb228830d..bc5fc0b6c5 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -73,6 +73,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { if block.Withdrawals() == nil { return fmt.Errorf("missing withdrawals in block body") } + if hash := types.DeriveSha(block.Withdrawals(), trie.NewStackTrie(nil)); hash != *header.WithdrawalsHash { return fmt.Errorf("withdrawals root hash mismatch (header value %x, calculated %x)", *header.WithdrawalsHash, hash) } diff --git a/core/block_validator_test.go b/core/block_validator_test.go index 3245a1e886..0da8bc1966 100644 --- a/core/block_validator_test.go +++ b/core/block_validator_test.go @@ -98,6 +98,7 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) { addr = crypto.PubkeyToAddress(key.PublicKey) config = *params.AllCliqueProtocolChanges ) + engine = beacon.New(clique.New(params.AllCliqueProtocolChanges.Clique, rawdb.NewMemoryDatabase())) gspec = &Genesis{ Config: &config, @@ -112,6 +113,7 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) { td := 0 genDb, blocks, _ := GenerateChainWithGenesis(gspec, engine, 8, nil) + for i, block := range blocks { header := block.Header() if i > 0 { @@ -127,6 +129,7 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) { // calculate td td += int(block.Difficulty().Uint64()) } + preBlocks = blocks gspec.Config.TerminalTotalDifficulty = big.NewInt(int64(td)) postBlocks, _ = GenerateChain(gspec.Config, preBlocks[len(preBlocks)-1], engine, genDb, 8, nil) @@ -221,6 +224,7 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) { headers = append(headers, block.Header()) seals = append(seals, true) } + _, results := engine.VerifyHeaders(chain, headers, seals) for i := 0; i < len(headers); i++ { select { diff --git a/core/blockchain.go b/core/blockchain.go index f7d09bbc82..07003e4753 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -274,11 +274,14 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok { return nil, genesisErr } + log.Info("") log.Info(strings.Repeat("-", 153)) + for _, line := range strings.Split(chainConfig.Description(), "\n") { log.Info(line) } + log.Info(strings.Repeat("-", 153)) log.Info("") @@ -381,6 +384,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis snapBlock := bc.CurrentSnapBlock() if snapBlock != nil && snapBlock.Number.Uint64() < frozen-1 { needRewind = true + if snapBlock.Number.Uint64() < low || low == 0 { low = snapBlock.Number.Uint64() } @@ -426,6 +430,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis log.Warn("Enabling snapshot recovery", "chainhead", head.Number, "diskbase", *layer) recover = true } + snapconfig := snapshot.Config{ CacheSize: bc.cacheConfig.SnapshotLimit, Recovery: recover, @@ -454,11 +459,13 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis // Rewind the chain in case of an incompatible config upgrade. if compat, ok := genesisErr.(*params.ConfigCompatError); ok { log.Warn("Rewinding chain to upgrade configuration", "err", compat) + if compat.RewindToTime > 0 { bc.SetHeadWithTimestamp(compat.RewindToTime) } else { bc.SetHead(compat.RewindToBlock) } + rawdb.WriteChainConfig(db, genesisHash, chainConfig) } // Start tx indexer/unindexer if required. @@ -466,8 +473,10 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis bc.txLookupLimit = *txLookupLimit bc.wg.Add(1) + go bc.maintainTxIndex() } + return bc, nil } @@ -486,6 +495,7 @@ func NewParallelBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis Preimages: cacheConfig.Preimages, }) chainConfig, _, genesisErr := SetupGenesisBlockWithOverride(db, triedb, genesis, overrides) + if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok { return nil, genesisErr } @@ -611,6 +621,7 @@ func (bc *BlockChain) loadLastState() error { headHeader = header } } + bc.hc.SetCurrentHeader(headHeader) // Restore the last known head fast block @@ -643,14 +654,18 @@ func (bc *BlockChain) loadLastState() error { headerTd = bc.GetTd(headHeader.Hash(), headHeader.Number.Uint64()) blockTd = bc.GetTd(headBlock.Hash(), headBlock.NumberU64()) ) + if headHeader.Hash() != headBlock.Hash() { log.Info("Loaded most recent local header", "number", headHeader.Number, "hash", headHeader.Hash(), "td", headerTd, "age", common.PrettyAge(time.Unix(int64(headHeader.Time), 0))) } + log.Info("Loaded most recent local block", "number", headBlock.Number(), "hash", headBlock.Hash(), "td", blockTd, "age", common.PrettyAge(time.Unix(int64(headBlock.Time()), 0))) + if headBlock.Hash() != currentSnapBlock.Hash() { fastTd := bc.GetTd(currentSnapBlock.Hash(), currentSnapBlock.Number.Uint64()) log.Info("Loaded most recent local snap block", "number", currentSnapBlock.Number, "hash", currentSnapBlock.Hash(), "td", fastTd, "age", common.PrettyAge(time.Unix(int64(currentSnapBlock.Time), 0))) } + if currentFinalBlock != nil { finalTd := bc.GetTd(currentFinalBlock.Hash(), currentFinalBlock.Number.Uint64()) log.Info("Loaded most recent local finalized block", "number", currentFinalBlock.Number, "hash", currentFinalBlock.Hash(), "td", finalTd, "age", common.PrettyAge(time.Unix(int64(currentFinalBlock.Time), 0))) @@ -671,6 +686,7 @@ func (bc *BlockChain) SetHead(head uint64) error { // Send chain head event to update the transaction pool header := bc.CurrentBlock() block := bc.GetBlock(header.Hash(), header.Number.Uint64()) + if block == nil { // This should never happen. In practice, previsouly currentBlock // contained the entire block whereas now only a "marker", so there @@ -678,7 +694,9 @@ func (bc *BlockChain) SetHead(head uint64) error { log.Error("Current block not found in database", "block", header.Number, "hash", header.Hash()) return fmt.Errorf("current block missing: #%d [%x..]", header.Number, header.Hash().Bytes()[:4]) } + bc.chainHeadFeed.Send(ChainHeadEvent{Block: block}) + return nil } @@ -693,6 +711,7 @@ func (bc *BlockChain) SetHeadWithTimestamp(timestamp uint64) error { // Send chain head event to update the transaction pool header := bc.CurrentBlock() block := bc.GetBlock(header.Hash(), header.Number.Uint64()) + if block == nil { // This should never happen. In practice, previsouly currentBlock // contained the entire block whereas now only a "marker", so there @@ -700,13 +719,16 @@ func (bc *BlockChain) SetHeadWithTimestamp(timestamp uint64) error { log.Error("Current block not found in database", "block", header.Number, "hash", header.Hash()) return fmt.Errorf("current block missing: #%d [%x..]", header.Number, header.Hash().Bytes()[:4]) } + bc.chainHeadFeed.Send(ChainHeadEvent{Block: block}) + return nil } // SetFinalized sets the finalized block. func (bc *BlockChain) SetFinalized(header *types.Header) { bc.currentFinalBlock.Store(header) + if header != nil { rawdb.WriteFinalizedBlockHash(bc.db, header.Hash()) headFinalizedBlockGauge.Update(int64(header.Number.Uint64())) @@ -719,6 +741,7 @@ func (bc *BlockChain) SetFinalized(header *types.Header) { // SetSafe sets the safe block. func (bc *BlockChain) SetSafe(header *types.Header) { bc.currentSafeBlock.Store(header) + if header != nil { headSafeBlockGauge.Update(int64(header.Number.Uint64())) } else { @@ -824,15 +847,17 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha if newHeadSnapBlock == nil { newHeadSnapBlock = bc.genesisBlock } + rawdb.WriteHeadFastBlockHash(db, newHeadSnapBlock.Hash()) // Degrade the chain markers if they are explicitly reverted. - // In theory we should update all in-memory markers in the + // In theory, we should update all in-memory markers in the // last step, however the direction of SetHead is from high // to low, so it's safe the update in-memory markers directly. bc.currentSnapBlock.Store(newHeadSnapBlock.Header()) headFastBlockGauge.Update(int64(newHeadSnapBlock.NumberU64())) } + var ( headHeader = bc.CurrentBlock() headNumber = headHeader.Number.Uint64() @@ -844,6 +869,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha if headNumber+1 < frozen { wipe = pivot == nil || headNumber >= *pivot } + return headHeader, wipe // Only force wipe if full synced } // Rewind the header chain, deleting all block bodies until then @@ -900,6 +926,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha log.Warn("SetHead invalidated safe block") bc.SetSafe(nil) } + if finalized := bc.CurrentFinalBlock(); finalized != nil && head < finalized.Number.Uint64() { log.Error("SetHead invalidated finalized block") bc.SetFinalized(nil) @@ -915,7 +942,9 @@ func (bc *BlockChain) SnapSyncCommitHead(hash common.Hash) error { if block == nil { return fmt.Errorf("non existent block [%x..]", hash[:4]) } + root := block.Root() + if !bc.HasState(root) { return fmt.Errorf("non existent state [%x..]", root[:4]) } @@ -923,6 +952,7 @@ func (bc *BlockChain) SnapSyncCommitHead(hash common.Hash) error { if !bc.chainmu.TryLock() { return errChainStopped } + bc.currentBlock.Store(block.Header()) headBlockGauge.Update(int64(block.NumberU64())) bc.chainmu.Unlock() @@ -995,9 +1025,11 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { if block == nil { return fmt.Errorf("export failed on #%d: not found", nr) } + if nr > first && block.ParentHash() != parentHash { return fmt.Errorf("export failed: chain reorg during export") } + parentHash = block.Hash() if err := block.EncodeRLP(w); err != nil { return err @@ -1094,6 +1126,7 @@ func (bc *BlockChain) Stop() { recent := bc.GetBlockByNumber(number - offset) log.Info("Writing cached state to disk", "block", recent.Number(), "hash", recent.Hash(), "root", recent.Root()) + if err := triedb.Commit(recent.Root(), true); err != nil { log.Error("Failed to commit recent state trie", "err", err) } @@ -1101,6 +1134,7 @@ func (bc *BlockChain) Stop() { } if snapBase != (common.Hash{}) { log.Info("Writing snapshot state to disk", "root", snapBase) + if err := triedb.Commit(snapBase, true); err != nil { log.Error("Failed to commit recent state trie", "err", err) } @@ -1450,6 +1484,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ if stats.ignored > 0 { context = append(context, []interface{}{"ignored", stats.ignored}...) } + log.Debug("Imported new block receipts", context...) return 0, nil @@ -1565,6 +1600,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. nodes, imgs = bc.triedb.Size() limit = common.StorageSize(bc.cacheConfig.TrieDirtyLimit) * 1024 * 1024 ) + if nodes > limit || imgs > 4*1024*1024 { bc.triedb.Cap(limit - ethdb.IdealBatchSize) } @@ -1597,6 +1633,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. bc.triegc.Push(root, number) break } + bc.triedb.Dereference(root) } return stateSyncLogs, nil @@ -2026,6 +2063,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool) bc.prefetcher.Prefetch(followup, throwaway, bc.vmConfig, &followupInterrupt) blockPrefetchExecuteTimer.Update(time.Since(start)) + if followupInterrupt.Load() { blockPrefetchInterruptMeter.Mark(1) } @@ -2056,6 +2094,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool) followupInterrupt.Store(true) return it.index, err } + vtime := time.Since(vstart) proctime := time.Since(start) // processing + validation @@ -2086,6 +2125,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool) } else { status, err = bc.writeBlockAndSetHead(context.Background(), block, receipts, logs, statedb, false) } + followupInterrupt.Store(true) if err != nil { return it.index, err @@ -2353,6 +2393,7 @@ func (bc *BlockChain) recoverAncestors(block *types.Block) (common.Hash, error) return b.ParentHash(), err } } + return block.Hash(), nil } @@ -2396,10 +2437,13 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Block) error { deletedTxs []common.Hash addedTxs []common.Hash ) + oldBlock := bc.GetBlock(oldHead.Hash(), oldHead.Number.Uint64()) + if oldBlock == nil { return errors.New("current head block missing") } + newBlock := newHead // Reduce the longer chain to the same number as the shorter one @@ -2407,6 +2451,7 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Block) error { // Old chain is longer, gather all transactions and logs as deleted ones for ; oldBlock != nil && oldBlock.NumberU64() != newBlock.NumberU64(); oldBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1) { oldChain = append(oldChain, oldBlock) + for _, tx := range oldBlock.Transactions() { deletedTxs = append(deletedTxs, tx.Hash()) } @@ -2433,6 +2478,7 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Block) error { } // Remove an old block as well as stash away a new block oldChain = append(oldChain, oldBlock) + for _, tx := range oldBlock.Transactions() { deletedTxs = append(deletedTxs, tx.Hash()) } @@ -2551,6 +2597,7 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Block) error { // Deleted logs + blocks: var deletedLogs []*types.Log + for i := len(oldChain) - 1; i >= 0; i-- { // Also send event for blocks removed from the canon chain. bc.chainSideFeed.Send(ChainSideEvent{Block: oldChain[i]}) @@ -2559,26 +2606,31 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Block) error { if logs := bc.collectLogs(oldChain[i], true); len(logs) > 0 { deletedLogs = append(deletedLogs, logs...) } + if len(deletedLogs) > 512 { bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs}) deletedLogs = nil } } + if len(deletedLogs) > 0 { bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs}) } // New logs: var rebirthLogs []*types.Log + for i := len(newChain) - 1; i >= 1; i-- { if logs := bc.collectLogs(newChain[i], false); len(logs) > 0 { rebirthLogs = append(rebirthLogs, logs...) } + if len(rebirthLogs) > 512 { bc.logsFeed.Send(rebirthLogs) rebirthLogs = nil } } + if len(rebirthLogs) > 0 { bc.logsFeed.Send(rebirthLogs) } @@ -2652,6 +2704,7 @@ func (bc *BlockChain) SetCanonical(head *types.Block) (common.Hash, error) { if latestValidHash, err := bc.recoverAncestors(head); err != nil { return latestValidHash, err } + log.Info("Recovered head state", "number", head.Number(), "hash", head.Hash()) } // Run the reorg if necessary and set the given block as new head. @@ -2681,6 +2734,7 @@ func (bc *BlockChain) SetCanonical(head *types.Block) (common.Hash, error) { context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...) } log.Info("Chain head was updated", context...) + return head.Hash(), nil } @@ -2750,7 +2804,9 @@ func (bc *BlockChain) indexBlocks(tail *uint64, head uint64, done chan struct{}) if bc.txLookupLimit != 0 && head >= bc.txLookupLimit { from = head - bc.txLookupLimit + 1 } + rawdb.IndexTransactions(bc.db, from, head+1, bc.quit) + return } // The tail flag is existent, but the whole chain is required to be indexed. @@ -2763,8 +2819,10 @@ func (bc *BlockChain) indexBlocks(tail *uint64, head uint64, done chan struct{}) if end > head+1 { end = head + 1 } + rawdb.IndexTransactions(bc.db, 0, end, bc.quit) } + return } // Update the transaction index to the new chain state @@ -2835,11 +2893,14 @@ func summarizeBadBlock(block *types.Block, receipts []*types.Receipt, config *pa i, receipt.CumulativeGasUsed, receipt.GasUsed, receipt.ContractAddress.Hex(), receipt.Status, receipt.TxHash.Hex(), receipt.Logs, receipt.Bloom, receipt.PostState) } + version, vcs := version.Info() platform := fmt.Sprintf("%s %s %s %s", version, runtime.Version(), runtime.GOARCH, runtime.GOOS) + if vcs != "" { vcs = fmt.Sprintf("\nVCS: %s", vcs) } + return fmt.Sprintf(` ########## BAD BLOCK ######### Block: %v (%#x) diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index af4b431acf..6601f915ae 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -138,6 +138,7 @@ func (bc *BlockChain) HasBlock(hash common.Hash, number uint64) bool { if bc.blockCache.Contains(hash) { return true } + if !bc.HasHeader(hash, number) { return false } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 5f068c9195..b45379df10 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -69,11 +69,13 @@ func newCanonical(engine consensus.Engine, n int, full bool) (ethdb.Database, *G // Full block-chain requested genDb, blocks := makeBlockChainWithGenesis(genesis, n, engine, canonicalSeed) _, err := blockchain.InsertChain(blocks) + return genDb, genesis, blockchain, err } // Header-only chain requested genDb, headers := makeHeaderChainWithGenesis(genesis, n, engine, canonicalSeed) _, err := blockchain.InsertHeaderChain(headers, 1) + return genDb, genesis, blockchain, err } @@ -261,6 +263,7 @@ func testInsertAfterMerge(t *testing.T, blockchain *BlockChain, i, n int, full b if _, err := blockchain2.InsertChain(blockChainB); err != nil { t.Fatalf("failed to insert forking chain: %v", err) } + if blockchain2.CurrentBlock().Number.Uint64() != blockChainB[len(blockChainB)-1].NumberU64() { t.Fatalf("failed to reorg to the given chain") } @@ -767,6 +770,7 @@ func TestFastVsFullChains(t *testing.T) { } signer = types.LatestSigner(gspec.Config) ) + _, blocks, receipts := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 1024, func(i int, block *BlockGen) { block.SetCoinbase(common.Address{0x00}) @@ -902,6 +906,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) { if err != nil { t.Fatalf("failed to create temp freezer db: %v", err) } + return db } // Configure a subchain to roll back @@ -914,6 +919,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) { if num := chain.CurrentBlock().Number.Uint64(); num != block { t.Errorf("%s head block mismatch: have #%v, want #%v", kind, num, block) } + if num := chain.CurrentSnapBlock().Number.Uint64(); num != fast { t.Errorf("%s head snap-block mismatch: have #%v, want #%v", kind, num, fast) } @@ -1122,6 +1128,7 @@ func TestLogReorgs(t *testing.T) { rmLogsCh := make(chan RemovedLogsEvent) blockchain.SubscribeRemovedLogsEvent(rmLogsCh) + _, chain, _ := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 2, func(i int, gen *BlockGen) { if i == 1 { tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, gen.header.BaseFee, code), signer, key1) @@ -1198,6 +1205,7 @@ func TestLogRebirth(t *testing.T) { if _, err := blockchain.InsertChain(chain); err != nil { t.Fatalf("failed to insert chain: %v", err) } + checkLogEvents(t, newLogCh, rmLogsCh, 10, 0) // Generate long reorg chain containing more logs. Inserting the @@ -1224,6 +1232,7 @@ func TestLogRebirth(t *testing.T) { if _, err := blockchain.InsertChain(forkChain); err != nil { t.Fatalf("failed to insert forked chain: %v", err) } + checkLogEvents(t, newLogCh, rmLogsCh, 10, 10) // This chain segment is rooted in the original chain, but doesn't contain any logs. @@ -1233,6 +1242,7 @@ func TestLogRebirth(t *testing.T) { if _, err := blockchain.InsertChain(newBlocks); err != nil { t.Fatalf("failed to insert forked chain: %v", err) } + checkLogEvents(t, newLogCh, rmLogsCh, 10, 10) } @@ -1288,6 +1298,7 @@ func TestSideLogRebirth(t *testing.T) { func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan RemovedLogsEvent, wantNew, wantRemoved int) { t.Helper() + var ( countNew int countRm int @@ -1297,25 +1308,31 @@ func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan Re for len(logsCh) > 0 { x := <-logsCh countNew += len(x) + for _, log := range x { // We expect added logs to be in ascending order: 0:0, 0:1, 1:0 ... have := 100*int(log.BlockNumber) + int(log.TxIndex) if have < prev { t.Fatalf("Expected new logs to arrive in ascending order (%d < %d)", have, prev) } + prev = have } } + prev = 0 + for len(rmLogsCh) > 0 { x := <-rmLogsCh countRm += len(x.Logs) + for _, log := range x.Logs { // We expect removed logs to be in ascending order: 0:0, 0:1, 1:0 ... have := 100*int(log.BlockNumber) + int(log.TxIndex) if have < prev { t.Fatalf("Expected removed logs to arrive in ascending order (%d < %d)", have, prev) } + prev = have } } @@ -1323,6 +1340,7 @@ func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan Re if countNew != wantNew { t.Fatalf("wrong number of log events: got %d, want %d", countNew, wantNew) } + if countRm != wantRemoved { t.Fatalf("wrong number of removed log events: got %d, want %d", countRm, wantRemoved) } @@ -1338,6 +1356,7 @@ func TestReorgSideEvent(t *testing.T) { } signer = types.LatestSigner(gspec.Config) ) + blockchain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil) defer blockchain.Stop() @@ -1471,6 +1490,7 @@ func TestEIP155Transition(t *testing.T) { Alloc: GenesisAlloc{address: {Balance: funds}, deleteAddr: {Balance: new(big.Int)}}, } ) + genDb, blocks, _ := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 4, func(i int, block *BlockGen) { var ( tx *types.Transaction @@ -1582,6 +1602,7 @@ func TestEIP161AccountRemoval(t *testing.T) { Alloc: GenesisAlloc{address: {Balance: funds}}, } ) + _, blocks, _ := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 3, func(i int, block *BlockGen) { var ( tx *types.Transaction @@ -1650,6 +1671,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) { if i > 0 { parent = blocks[i-1] } + fork, _ := GenerateChain(genesis.Config, parent, engine, genDb, 1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) }) forks[i] = fork[0] } @@ -1659,6 +1681,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() for i := 0; i < len(blocks); i++ { @@ -1695,6 +1718,7 @@ func TestTrieForkGC(t *testing.T) { if i > 0 { parent = blocks[i-1] } + fork, _ := GenerateChain(genesis.Config, parent, engine, genDb, 1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) }) forks[i] = fork[0] } @@ -1703,6 +1727,7 @@ func TestTrieForkGC(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() for i := 0; i < len(blocks); i++ { @@ -1741,6 +1766,7 @@ func TestLargeReorgTrieGC(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() if _, err := chain.InsertChain(shared); err != nil { @@ -1814,9 +1840,11 @@ func TestBlockchainRecovery(t *testing.T) { // Reopen broken blockchain again ancient, _ = NewBlockChain(ancientDb, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil) defer ancient.Stop() + if num := ancient.CurrentBlock().Number.Uint64(); num != 0 { t.Errorf("head block mismatch: have #%v, want #%v", num, 0) } + if num := ancient.CurrentSnapBlock().Number.Uint64(); num != midBlock.NumberU64() { t.Errorf("head snap-block mismatch: have #%v, want #%v", num, midBlock.NumberU64()) } @@ -1837,6 +1865,7 @@ func TestInsertReceiptChainRollback(t *testing.T) { if _, err := tmpChain.InsertChain(sideblocks); err != nil { t.Fatal("processing side chain failed:", err) } + t.Log("sidechain head:", tmpChain.CurrentBlock().Number, tmpChain.CurrentBlock().Hash()) sidechainReceipts := make([]types.Receipts, len(sideblocks)) for i, block := range sideblocks { @@ -1846,6 +1875,7 @@ func TestInsertReceiptChainRollback(t *testing.T) { if _, err := tmpChain.InsertChain(canonblocks); err != nil { t.Fatal("processing canon chain failed:", err) } + t.Log("canon head:", tmpChain.CurrentBlock().Number, tmpChain.CurrentBlock().Hash()) canonReceipts := make([]types.Receipts, len(canonblocks)) for i, block := range canonblocks { @@ -1876,6 +1906,7 @@ func TestInsertReceiptChainRollback(t *testing.T) { if err == nil { t.Fatal("expected error from InsertReceiptChain.") } + if ancientChain.CurrentSnapBlock().Number.Uint64() != 0 { t.Fatalf("failed to rollback ancient data, want %d, have %d", 0, ancientChain.CurrentSnapBlock().Number) } @@ -1888,6 +1919,7 @@ func TestInsertReceiptChainRollback(t *testing.T) { if err != nil { t.Fatalf("can't import canon chain receipts: %v", err) } + if ancientChain.CurrentSnapBlock().Number.Uint64() != canonblocks[len(canonblocks)-1].NumberU64() { t.Fatalf("failed to insert ancient recept chain after rollback") } @@ -1922,6 +1954,7 @@ func TestLowDiffLongChain(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.stopWithoutSaving() if n, err := chain.InsertChain(blocks); err != nil { @@ -1984,6 +2017,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() // Activate the transition since genesis if required @@ -1995,6 +2029,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon // Set the terminal total difficulty in the config gspec.Config.TerminalTotalDifficulty = big.NewInt(0) } + genDb, blocks, _ := GenerateChainWithGenesis(gspec, engine, 2*int(DefaultCacheConfig.TriesInMemory), func(i int, gen *BlockGen) { tx, err := types.SignTx(types.NewTransaction(nonce, common.HexToAddress("deadbeef"), big.NewInt(100), 21000, big.NewInt(int64(i+1)*params.GWei), nil), signer, key) if err != nil { @@ -2133,6 +2168,7 @@ func testInsertKnownChainData(t *testing.T, typ string) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() var ( @@ -2304,6 +2340,7 @@ func testInsertKnownChainDataWithMerging(t *testing.T, typ string, mergeHeight i if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() var ( @@ -2316,7 +2353,9 @@ func testInsertKnownChainDataWithMerging(t *testing.T, typ string, mergeHeight i for _, block := range blocks { headers = append(headers, block.Header()) } + i, err := chain.InsertHeaderChain(headers, 1) + if err != nil { return fmt.Errorf("index %d, number %d: %w", i, headers[i].Number, err) } @@ -2425,6 +2464,7 @@ func getLongAndShortChains() (*BlockChain, []*types.Block, []*types.Block, *Gene b.SetCoinbase(common.Address{2}) b.OffsetTime(-9) }) + var heavyChain []*types.Block heavyChain = append(heavyChain, longChain[:parentIndex+1]...) heavyChain = append(heavyChain, heavyChainExt...) @@ -2451,6 +2491,7 @@ func getLongAndShortChains() (*BlockChain, []*types.Block, []*types.Block, *Gene if shorterNum >= longerNum { return nil, nil, nil, nil, fmt.Errorf("test is moot, heavyChain num (%v) must be lower than canon num (%v)", shorterNum, longerNum) } + return chain, longChain, heavyChain, genesis, nil } @@ -2464,11 +2505,13 @@ func TestReorgToShorterRemovesCanonMapping(t *testing.T) { if err != nil { t.Fatal(err) } + defer chain.Stop() if n, err := chain.InsertChain(canonblocks); err != nil { t.Fatalf("block %d: failed to insert into chain: %v", n, err) } + canonNum := chain.CurrentBlock().Number.Uint64() canonHash := chain.CurrentBlock().Hash() _, err = chain.InsertChain(sideblocks) @@ -2502,6 +2545,7 @@ func TestReorgToShorterRemovesCanonMappingHeaderChain(t *testing.T) { if err != nil { t.Fatal(err) } + defer chain.Stop() // Convert into headers @@ -2553,6 +2597,7 @@ func TestTransactionIndices(t *testing.T) { } signer = types.LatestSigner(gspec.Config) ) + _, blocks, receipts := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 128, func(i int, block *BlockGen) { tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, block.header.BaseFee, nil), signer, key) if err != nil { @@ -2606,6 +2651,7 @@ func TestTransactionIndices(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + chain.indexBlocks(rawdb.ReadTxIndexTail(ancientDb), 128, make(chan struct{})) var tail uint64 @@ -2624,16 +2670,20 @@ func TestTransactionIndices(t *testing.T) { rawdb.WriteAncientBlocks(ancientDb, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...), []types.Receipts{nil}, big.NewInt(0)) limit = []uint64{0, 64 /* drop stale */, 32 /* shorten history */, 64 /* extend history */, 0 /* restore all */} + for _, l := range limit { l := l chain, err := NewBlockChain(ancientDb, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, &l, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + var tail uint64 + if l != 0 { tail = uint64(128) - l + 1 } + chain.indexBlocks(rawdb.ReadTxIndexTail(ancientDb), 128, make(chan struct{})) check(&tail, chain) chain.Stop() @@ -2649,6 +2699,7 @@ func TestSkipStaleTxIndicesInSnapSync(t *testing.T) { gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}} signer = types.LatestSigner(gspec.Config) ) + _, blocks, receipts := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 128, func(i int, block *BlockGen) { tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, block.header.BaseFee, nil), signer, key) if err != nil { @@ -2703,6 +2754,7 @@ func TestSkipStaleTxIndicesInSnapSync(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() headers := make([]*types.Header, len(blocks)) @@ -2769,7 +2821,9 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in b.Fatalf("failed to insert shared chain: %v", err) } b.StopTimer() + block := chain.GetBlockByHash(chain.CurrentBlock().Hash()) + if got := block.Transactions().Len(); got != numTxs*numBlocks { b.Fatalf("Transactions were not included, expected %d, got %d", numTxs*numBlocks, got) } @@ -2846,6 +2900,7 @@ func TestSideImportPrunedBlocks(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() if n, err := chain.InsertChain(blocks); err != nil { @@ -2937,6 +2992,7 @@ func TestDeleteCreateRevert(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() if n, err := chain.InsertChain(blocks); err != nil { @@ -3047,6 +3103,7 @@ func TestDeleteRecreateSlots(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() if n, err := chain.InsertChain(blocks); err != nil { @@ -3124,6 +3181,7 @@ func TestDeleteRecreateAccount(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() if n, err := chain.InsertChain(blocks); err != nil { @@ -3295,6 +3353,7 @@ func TestDeleteRecreateSlotsAcrossManyBlocks(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() var asHash = func(num int) common.Hash { @@ -3426,6 +3485,7 @@ func TestInitThenFailCreateContract(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() statedb, _ := chain.State() @@ -3508,6 +3568,7 @@ func TestEIP2718Transition(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() if n, err := chain.InsertChain(blocks); err != nil { @@ -3596,6 +3657,7 @@ func TestEIP1559Transition(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() if n, err := chain.InsertChain(blocks); err != nil { @@ -3674,8 +3736,7 @@ func TestEIP1559Transition(t *testing.T) { // Tests the scenario the chain is requested to another point with the missing state. // It expects the state is recovered and all relevant chain markers are set correctly. func TestSetCanonical(t *testing.T) { - //log.Root().SetHandler(log.LvlFilterHandler(log.LvlDebug, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) - + t.Parallel() var ( key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") address = crypto.PubkeyToAddress(key.PublicKey) @@ -3688,6 +3749,9 @@ func TestSetCanonical(t *testing.T) { signer = types.LatestSigner(gspec.Config) engine = ethash.NewFaker() ) + + //log.Root().SetHandler(log.LvlFilterHandler(log.LvlDebug, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) + // Generate and import the canonical chain _, canon, _ := GenerateChainWithGenesis(gspec, engine, 2*int(DefaultCacheConfig.TriesInMemory), func(i int, gen *BlockGen) { tx, err := types.SignTx(types.NewTransaction(gen.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key) @@ -3696,10 +3760,12 @@ func TestSetCanonical(t *testing.T) { } gen.AddTx(tx) }) + chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil, nil, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + defer chain.Stop() if n, err := chain.InsertChain(canon); err != nil { @@ -3714,12 +3780,14 @@ func TestSetCanonical(t *testing.T) { } gen.AddTx(tx) }) + for _, block := range side { err := chain.InsertBlockWithoutSetHead(block) if err != nil { t.Fatalf("Failed to insert into chain: %v", err) } } + for _, block := range side { got := chain.GetBlockByHash(block.Hash()) if got == nil { @@ -3732,12 +3800,15 @@ func TestSetCanonical(t *testing.T) { if chain.CurrentBlock().Hash() != head.Hash() { t.Fatalf("Unexpected block hash, want %x, got %x", head.Hash(), chain.CurrentBlock().Hash()) } + if chain.CurrentSnapBlock().Hash() != head.Hash() { t.Fatalf("Unexpected fast block hash, want %x, got %x", head.Hash(), chain.CurrentSnapBlock().Hash()) } + if chain.CurrentHeader().Hash() != head.Hash() { t.Fatalf("Unexpected head header, want %x, got %x", head.Hash(), chain.CurrentHeader().Hash()) } + if !chain.HasState(head.Root()) { t.Fatalf("Lost block state %v %x", head.Number(), head.Hash()) } @@ -3787,6 +3858,7 @@ func TestCanonicalHashMarker(t *testing.T) { // markers [1, 11] should be updated {10, 11}, } + for _, c := range cases { var ( gspec = &Genesis{ @@ -3796,6 +3868,7 @@ func TestCanonicalHashMarker(t *testing.T) { } engine = ethash.NewFaker() ) + _, forkA, _ := GenerateChainWithGenesis(gspec, engine, c.forkA, func(i int, gen *BlockGen) {}) _, forkB, _ := GenerateChainWithGenesis(gspec, engine, c.forkB, func(i int, gen *BlockGen) {}) @@ -3804,10 +3877,12 @@ func TestCanonicalHashMarker(t *testing.T) { if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + // Insert forkA and forkB, the canonical should on forkA still if n, err := chain.InsertChain(forkA); err != nil { t.Fatalf("block %d: failed to insert into chain: %v", n, err) } + if n, err := chain.InsertChain(forkB); err != nil { t.Fatalf("block %d: failed to insert into chain: %v", n, err) } @@ -3816,12 +3891,15 @@ func TestCanonicalHashMarker(t *testing.T) { if chain.CurrentBlock().Hash() != head.Hash() { t.Fatalf("Unexpected block hash, want %x, got %x", head.Hash(), chain.CurrentBlock().Hash()) } + if chain.CurrentSnapBlock().Hash() != head.Hash() { t.Fatalf("Unexpected fast block hash, want %x, got %x", head.Hash(), chain.CurrentSnapBlock().Hash()) } + if chain.CurrentHeader().Hash() != head.Hash() { t.Fatalf("Unexpected head header, want %x, got %x", head.Hash(), chain.CurrentHeader().Hash()) } + if !chain.HasState(head.Root()) { t.Fatalf("Lost block state %v %x", head.Number(), head.Hash()) } @@ -3840,10 +3918,12 @@ func TestCanonicalHashMarker(t *testing.T) { for i := 0; i < len(forkB); i++ { block := forkB[i] hash := chain.GetCanonicalHash(block.NumberU64()) + if hash != block.Hash() { t.Fatalf("Unexpected canonical hash %d", block.NumberU64()) } } + if c.forkA > c.forkB { for i := uint64(c.forkB) + 1; i <= uint64(c.forkA); i++ { hash := chain.GetCanonicalHash(i) @@ -3852,6 +3932,7 @@ func TestCanonicalHashMarker(t *testing.T) { } } } + chain.Stop() } } @@ -3873,6 +3954,7 @@ func TestTxIndexer(t *testing.T) { engine = ethash.NewFaker() nonce = uint64(0) ) + _, blocks, receipts := GenerateChainWithGenesis(gspec, engine, 128, func(i int, gen *BlockGen) { tx, _ := types.SignTx(types.NewTransaction(nonce, common.HexToAddress("0xdeadbeef"), big.NewInt(1000), params.TxGas, big.NewInt(10*params.InitialBaseFee), nil), types.HomesteadSigner{}, testBankKey) gen.AddTx(tx) @@ -3885,12 +3967,15 @@ func TestTxIndexer(t *testing.T) { if number == 0 { return } + block := blocks[number-1] + for _, tx := range block.Transactions() { lookup := rawdb.ReadTxLookupEntry(db, tx.Hash()) if exist && lookup == nil { t.Fatalf("missing %d %x", number, tx.Hash().Hex()) } + if !exist && lookup != nil { t.Fatalf("unexpected %d %x", number, tx.Hash().Hex()) } @@ -3907,12 +3992,15 @@ func TestTxIndexer(t *testing.T) { if tail == nil { t.Fatal("Failed to write tx index tail") } + if *tail != expTail { t.Fatalf("Unexpected tx index tail, want %v, got %d", expTail, *tail) } + if *tail != 0 { verifyRange(db, 0, *tail-1, false) } + verifyRange(db, *tail, 128, true) } @@ -4035,6 +4123,7 @@ func TestTxIndexer(t *testing.T) { tailC: 65, }, } + for _, c := range cases { frdir := t.TempDir() db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false) @@ -4177,6 +4266,7 @@ func TestTransientStorageReset(t *testing.T) { ExtraEips: []int{1153}, // Enable transient storage EIP } ) + code := append([]byte{ // TLoad value with location 1 byte(vm.PUSH1), 0x1, @@ -4251,8 +4341,10 @@ func TestTransientStorageReset(t *testing.T) { if err != nil { t.Fatalf("Failed to load state %v", err) } + loc := common.BytesToHash([]byte{1}) slot := state.GetState(destAddress, loc) + if slot != (common.Hash{}) { t.Fatalf("Unexpected dirty storage slot") } @@ -4335,9 +4427,11 @@ func TestEIP3651(t *testing.T) { b.AddTx(tx) }) chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr)}, nil, nil, nil) + if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + if n, err := chain.InsertChain(blocks); err != nil { t.Fatalf("block %d: failed to insert into chain: %v", n, err) } @@ -4347,6 +4441,7 @@ func TestEIP3651(t *testing.T) { // 1+2: Ensure EIP-1559 access lists are accounted for via gas usage. innerGas := vm.GasQuickStep*2 + params.ColdSloadCostEIP2929*2 expectedGas := params.TxGas + 5*vm.GasFastestStep + vm.GasQuickStep + 100 + innerGas // 100 because 0xaaaa is in access list + if block.GasUsed() != expectedGas { t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expectedGas, block.GasUsed()) } @@ -4356,6 +4451,7 @@ func TestEIP3651(t *testing.T) { // 3: Ensure that miner received only the tx's tip. actual := state.GetBalance(block.Coinbase()) expected := new(big.Int).SetUint64(block.GasUsed() * block.Transactions()[0].GasTipCap().Uint64()) + if actual.Cmp(expected) != 0 { t.Fatalf("miner balance incorrect: expected %d, got %d", expected, actual) } @@ -4363,6 +4459,7 @@ func TestEIP3651(t *testing.T) { // 4: Ensure the tx sender paid for the gasUsed * (tip + block baseFee). actual = new(big.Int).Sub(funds, state.GetBalance(addr1)) expected = new(big.Int).SetUint64(block.GasUsed() * (block.Transactions()[0].GasTipCap().Uint64() + block.BaseFee().Uint64())) + if actual.Cmp(expected) != 0 { t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual) } diff --git a/core/chain_makers.go b/core/chain_makers.go index 85fe8648e9..277d79d404 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -99,11 +99,14 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti if b.gasPool == nil { b.SetCoinbase(common.Address{}) } + b.statedb.SetTxContext(tx.Hash(), len(b.txs)) receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vmConfig, nil) + if err != nil { panic(err) } + b.txs = append(b.txs, tx) b.receipts = append(b.receipts, receipt) } @@ -226,6 +229,7 @@ func (b *BlockGen) AddWithdrawal(w *types.Withdrawal) uint64 { cpy := *w cpy.Index = b.nextWithdrawalIndex() b.withdrawals = append(b.withdrawals, &cpy) + return cpy.Index } @@ -234,10 +238,12 @@ func (b *BlockGen) nextWithdrawalIndex() uint64 { if len(b.withdrawals) != 0 { return b.withdrawals[len(b.withdrawals)-1].Index + 1 } + for i := b.i - 1; i >= 0; i-- { if wd := b.chain[i].Withdrawals(); len(wd) != 0 { return wd[len(wd)-1].Index + 1 } + if i == 0 { // Correctly set the index if no parent had withdrawals. if wd := b.parent.Withdrawals(); len(wd) != 0 { @@ -245,6 +251,7 @@ func (b *BlockGen) nextWithdrawalIndex() uint64 { } } } + return 0 } @@ -334,6 +341,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse if err != nil { panic(fmt.Sprintf("state write error: %v", err)) } + if err := statedb.Database().TrieDB().Commit(root, false); err != nil { panic(fmt.Sprintf("trie write error: %v", err)) } @@ -360,10 +368,13 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int, gen func(int, *BlockGen)) (ethdb.Database, []*types.Block, []types.Receipts) { db := rawdb.NewMemoryDatabase() _, err := genesis.Commit(db, trie.NewDatabase(db)) + if err != nil { panic(err) } + blocks, receipts := GenerateChain(genesis.Config, genesis.ToBlock(), engine, db, n, gen) + return db, blocks, receipts } @@ -412,9 +423,11 @@ func makeHeaderChain(chainConfig *params.ChainConfig, parent *types.Header, n in func makeHeaderChainWithGenesis(genesis *Genesis, n int, engine consensus.Engine, seed int) (ethdb.Database, []*types.Header) { db, blocks := makeBlockChainWithGenesis(genesis, n, engine, seed) headers := make([]*types.Header, len(blocks)) + for i, block := range blocks { headers[i] = block.Header() } + return db, headers } @@ -423,6 +436,7 @@ func makeBlockChain(chainConfig *params.ChainConfig, parent *types.Block, n int, blocks, _ := GenerateChain(chainConfig, parent, engine, db, n, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)}) }) + return blocks } @@ -431,6 +445,7 @@ func makeBlockChainWithGenesis(genesis *Genesis, n int, engine consensus.Engine, db, blocks, _ := GenerateChainWithGenesis(genesis, engine, n, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)}) }) + return db, blocks } diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go index 9b94895b71..98a8cb4ec4 100644 --- a/core/chain_makers_test.go +++ b/core/chain_makers_test.go @@ -122,18 +122,22 @@ func TestGenerateWithdrawalChain(t *testing.T) { withdrawalIndex uint64 head = blockchain.CurrentBlock().Number.Uint64() ) + for i := 0; i < int(head); i++ { block := blockchain.GetBlockByNumber(uint64(i)) if block == nil { t.Fatalf("block %d not found", i) } + if len(block.Withdrawals()) == 0 { continue } + for j := 0; j < len(block.Withdrawals()); j++ { if block.Withdrawals()[j].Index != withdrawalIndex { t.Fatalf("withdrawal index %d does not equal expected index %d", block.Withdrawals()[j].Index, withdrawalIndex) } + withdrawalIndex += 1 } } diff --git a/core/dao_test.go b/core/dao_test.go index a4bf092a75..e15742eb91 100644 --- a/core/dao_test.go +++ b/core/dao_test.go @@ -83,10 +83,13 @@ func TestDAOForkRangeExtradata(t *testing.T) { if _, err := bc.InsertChain(blocks); err != nil { t.Fatalf("failed to import contra-fork chain for expansion: %v", err) } + if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, false); err != nil { t.Fatalf("failed to commit contra-fork head for expansion: %v", err) } + bc.Stop() + blocks, _ = GenerateChain(&proConf, conBc.GetBlockByHash(conBc.CurrentBlock().Hash()), ethash.NewFaker(), genDb, 1, func(i int, gen *BlockGen) {}) if _, err := conBc.InsertChain(blocks); err == nil { t.Fatalf("contra-fork chain accepted pro-fork block: %v", blocks[0]) @@ -106,10 +109,13 @@ func TestDAOForkRangeExtradata(t *testing.T) { if _, err := bc.InsertChain(blocks); err != nil { t.Fatalf("failed to import pro-fork chain for expansion: %v", err) } + if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, false); err != nil { t.Fatalf("failed to commit pro-fork head for expansion: %v", err) } + bc.Stop() + blocks, _ = GenerateChain(&conConf, proBc.GetBlockByHash(proBc.CurrentBlock().Hash()), ethash.NewFaker(), genDb, 1, func(i int, gen *BlockGen) {}) if _, err := proBc.InsertChain(blocks); err == nil { t.Fatalf("pro-fork chain accepted contra-fork block: %v", blocks[0]) @@ -131,9 +137,11 @@ func TestDAOForkRangeExtradata(t *testing.T) { if _, err := bc.InsertChain(blocks); err != nil { t.Fatalf("failed to import contra-fork chain for expansion: %v", err) } + if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, false); err != nil { t.Fatalf("failed to commit contra-fork head for expansion: %v", err) } + blocks, _ = GenerateChain(&proConf, conBc.GetBlockByHash(conBc.CurrentBlock().Hash()), ethash.NewFaker(), genDb, 1, func(i int, gen *BlockGen) {}) if _, err := conBc.InsertChain(blocks); err != nil { t.Fatalf("contra-fork chain didn't accept pro-fork block post-fork: %v", err) @@ -143,15 +151,19 @@ func TestDAOForkRangeExtradata(t *testing.T) { defer bc.Stop() blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().Number.Uint64())) + for j := 0; j < len(blocks)/2; j++ { blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j] } + if _, err := bc.InsertChain(blocks); err != nil { t.Fatalf("failed to import pro-fork chain for expansion: %v", err) } + if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, false); err != nil { t.Fatalf("failed to commit pro-fork head for expansion: %v", err) } + blocks, _ = GenerateChain(&conConf, proBc.GetBlockByHash(proBc.CurrentBlock().Hash()), ethash.NewFaker(), genDb, 1, func(i int, gen *BlockGen) {}) if _, err := proBc.InsertChain(blocks); err != nil { t.Fatalf("pro-fork chain didn't accept contra-fork block post-fork: %v", err) diff --git a/core/forkchoice.go b/core/forkchoice.go index 3105a9c207..c9c38fb95f 100644 --- a/core/forkchoice.go +++ b/core/forkchoice.go @@ -103,6 +103,7 @@ func (f *ForkChoice) ReorgNeeded(current *types.Header, extern *types.Header) (b // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf reorg := false externNum, localNum := extern.Number.Uint64(), current.Number.Uint64() + if externNum < localNum { reorg = true } else if externNum == localNum { diff --git a/core/genesis.go b/core/genesis.go index 4ef8030d68..5780d82954 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -71,27 +71,37 @@ type Genesis struct { func ReadGenesis(db ethdb.Database) (*Genesis, error) { var genesis Genesis + stored := rawdb.ReadCanonicalHash(db, 0) + if (stored == common.Hash{}) { return nil, fmt.Errorf("invalid genesis hash in database: %x", stored) } + blob := rawdb.ReadGenesisStateSpec(db, stored) + if blob == nil { return nil, fmt.Errorf("genesis state missing from db") } + if len(blob) != 0 { if err := genesis.Alloc.UnmarshalJSON(blob); err != nil { return nil, fmt.Errorf("could not unmarshal genesis state json: %s", err) } } + genesis.Config = rawdb.ReadChainConfig(db, stored) + if genesis.Config == nil { return nil, fmt.Errorf("genesis config missing from db") } + genesisBlock := rawdb.ReadBlock(db, stored, 0) + if genesisBlock == nil { return nil, fmt.Errorf("genesis block missing from db") } + genesisHeader := genesisBlock.Header() genesis.Nonce = genesisHeader.Nonce.Uint64() genesis.Timestamp = genesisHeader.Time @@ -136,6 +146,7 @@ func (ga *GenesisAlloc) deriveHash() (common.Hash, error) { statedb.SetState(addr, key, value) } } + return statedb.Commit(false) } @@ -147,14 +158,17 @@ func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhas if err != nil { return err } + for addr, account := range *ga { statedb.AddBalance(addr, account.Balance) statedb.SetCode(addr, account.Code) statedb.SetNonce(addr, account.Nonce) + for key, value := range account.Storage { statedb.SetState(addr, key, value) } } + root, err := statedb.Commit(false) if err != nil { return err @@ -170,6 +184,7 @@ func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhas if err != nil { return err } + rawdb.WriteGenesisStateSpec(db, blockhash, blob) return nil } @@ -178,6 +193,7 @@ func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhas // hash and commits it into the provided trie database. func CommitGenesisState(db ethdb.Database, triedb *trie.Database, blockhash common.Hash) error { var alloc GenesisAlloc + blob := rawdb.ReadGenesisStateSpec(db, blockhash) if len(blob) != 0 { if err := alloc.UnmarshalJSON(blob); err != nil { @@ -206,6 +222,7 @@ func CommitGenesisState(db ethdb.Database, triedb *trie.Database, blockhash comm return errors.New("not found") } } + return alloc.flush(db, triedb, blockhash) } @@ -295,6 +312,7 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen if genesis != nil && genesis.Config == nil { return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig } + applyOverrides := func(config *params.ChainConfig) { if config != nil { // TODO marcello double check @@ -312,10 +330,12 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen } else { log.Info("Writing custom genesis block") } + block, err := genesis.Commit(db, triedb) if err != nil { return genesis.Config, common.Hash{}, err } + applyOverrides(genesis.Config) return genesis.Config, block.Hash(), nil } @@ -331,10 +351,12 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen if hash != stored { return genesis.Config, hash, &GenesisMismatchError{stored, hash} } + block, err := genesis.Commit(db, triedb) if err != nil { return genesis.Config, hash, err } + applyOverrides(genesis.Config) return genesis.Config, block.Hash(), nil } @@ -357,6 +379,8 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen rawdb.WriteChainConfig(db, stored, newcfg) return newcfg, stored, nil } + + // nolint:errchkjson storedData, _ := json.Marshal(storedcfg) // Special case: if a private network is being used (no genesis and also no // mainnet hash in the database), we must not apply the `configOrDefault` @@ -373,7 +397,9 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen if head == nil { return newcfg, stored, fmt.Errorf("missing head header") } + compatErr := storedcfg.CheckCompatible(newcfg, head.Number.Uint64(), head.Time) + if compatErr != nil && ((head.Number.Uint64() != 0 && compatErr.RewindToBlock != 0) || (head.Time != 0 && compatErr.RewindToTime != 0)) { return newcfg, stored, compatErr } @@ -413,6 +439,7 @@ func LoadCliqueConfig(db ethdb.Database, genesis *Genesis) (*params.CliqueConfig if stored != (common.Hash{}) && genesis.ToBlock().Hash() != stored { return nil, &GenesisMismatchError{stored, genesis.ToBlock().Hash()} } + return genesis.Config.Clique, nil } // There is no stored chain config and no new config provided, @@ -478,11 +505,14 @@ func (g *Genesis) ToBlock() *types.Block { head.BaseFee = new(big.Int).SetUint64(params.InitialBaseFee) } } + var withdrawals []*types.Withdrawal + if g.Config != nil && g.Config.IsShanghai(g.Timestamp) { head.WithdrawalsHash = &types.EmptyWithdrawalsHash withdrawals = make([]*types.Withdrawal, 0) } + return types.NewBlock(head, nil, nil, nil, trie.NewStackTrie(nil)).WithWithdrawals(withdrawals) } @@ -500,6 +530,7 @@ func (g *Genesis) Commit(db ethdb.Database, triedb *trie.Database) (*types.Block if err := config.CheckConfigForkOrder(); err != nil { return nil, err } + if config.Clique != nil && len(block.Extra()) < 32+crypto.SignatureLength { return nil, errors.New("can't start clique chain without signers") } diff --git a/core/genesis_test.go b/core/genesis_test.go index ef3f854649..b0f4525112 100644 --- a/core/genesis_test.go +++ b/core/genesis_test.go @@ -36,6 +36,7 @@ func TestInvalidCliqueConfig(t *testing.T) { block := DefaultGoerliGenesisBlock() block.ExtraData = []byte{} db := rawdb.NewMemoryDatabase() + if _, err := block.Commit(db, trie.NewDatabase(db)); err == nil { t.Fatal("Expected error on invalid clique config") } @@ -222,6 +223,8 @@ func TestReadWriteGenesisAlloc(t *testing.T) { } hash, _ = alloc.deriveHash() ) + + // nolint : errchkjson blob, _ := json.Marshal(alloc) rawdb.WriteGenesisStateSpec(db, hash, blob) diff --git a/core/headerchain.go b/core/headerchain.go index a1296ce1dd..92e805795c 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -400,6 +400,7 @@ func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, start time.Time, if res.ignored > 0 { context = append(context, []interface{}{"ignored", res.ignored}...) } + log.Debug("Imported new block headers", context...) return res.status, err } @@ -531,6 +532,7 @@ func (hc *HeaderChain) GetHeadersFrom(number, count uint64) []rlp.RawValue { if !ok { break } + rlpData, _ := rlp.EncodeToBytes(header) headers = append(headers, rlpData) hash = header.ParentHash @@ -607,12 +609,15 @@ func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn Updat batch = hc.chainDb.NewBatch() origin = true ) + done := func(header *types.Header) bool { if headTime > 0 { return header.Time <= headTime } + return header.Number.Uint64() <= headBlock } + for hdr := hc.CurrentHeader(); hdr != nil && !done(hdr); hdr = hc.CurrentHeader() { num := hdr.Number.Uint64() diff --git a/core/sender_cacher.go b/core/sender_cacher.go index 4be53619eb..8909db1598 100644 --- a/core/sender_cacher.go +++ b/core/sender_cacher.go @@ -101,5 +101,6 @@ func (cacher *txSenderCacher) RecoverFromBlocks(signer types.Signer, blocks []*t for _, block := range blocks { txs = append(txs, block.Transactions()...) } + cacher.Recover(signer, txs) } diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index e7106c9015..cbbefa53a1 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -68,6 +68,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c if err != nil { return // Also invalid block, bail out } + statedb.SetTxContext(tx.Hash(), i) if err := precacheTransaction(msg, p.config, gaspool, statedb, header, evm); err != nil { return // Ugh, something went horribly wrong, bail out diff --git a/core/state_processor.go b/core/state_processor.go index af45ea1e27..1aba120edd 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -82,10 +82,12 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg default: } } + msg, err := TransactionToMessage(tx, types.MakeSigner(p.config, header.Number), header.BaseFee) if err != nil { return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } + statedb.SetTxContext(tx.Hash(), i) receipt, err := applyTransaction(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv, interruptCtx) if err != nil { @@ -207,5 +209,6 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo // Create a new context to be used in the EVM environment blockContext := NewEVMBlockContext(header, bc, author) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg) + return applyTransaction(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv, interruptCtx) } diff --git a/core/state_processor_test.go b/core/state_processor_test.go index ae9802893b..0a368f8c36 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -80,6 +80,7 @@ func TestStateProcessorErrors(t *testing.T) { }), signer, key1) return tx } + var mkDynamicCreationTx = func(nonce uint64, gasLimit uint64, gasTipCap, gasFeeCap *big.Int, data []byte) *types.Transaction { tx, _ := types.SignTx(types.NewTx(&types.DynamicFeeTx{ Nonce: nonce, diff --git a/core/state_transition.go b/core/state_transition.go index 9e272a44ae..b26259e37a 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -77,6 +77,7 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b } else { gas = params.TxGas } + dataLen := uint64(len(data)) // Bump the required gas by the amount of transactional data if dataLen > 0 { @@ -108,6 +109,7 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b if (math.MaxUint64-gas)/params.InitCodeWordGas < lenWords { return 0, ErrGasUintOverflow } + gas += lenWords * params.InitCodeWordGas } } @@ -165,8 +167,10 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In if baseFee != nil { msg.GasPrice = cmath.BigMin(msg.GasPrice.Add(msg.GasTipCap, baseFee), msg.GasFeeCap) } + var err error msg.From, err = types.Sender(s, tx) + return msg, err } @@ -239,6 +243,7 @@ func (st *StateTransition) to() common.Address { if st.msg == nil || st.msg.To == nil /* contract creation */ { return common.Address{} } + return *st.msg.To } @@ -246,17 +251,21 @@ func (st *StateTransition) buyGas() error { mgval := new(big.Int).SetUint64(st.msg.GasLimit) mgval = mgval.Mul(mgval, st.msg.GasPrice) balanceCheck := mgval + if st.msg.GasFeeCap != nil { balanceCheck = new(big.Int).SetUint64(st.msg.GasLimit) balanceCheck = balanceCheck.Mul(balanceCheck, st.msg.GasFeeCap) balanceCheck.Add(balanceCheck, st.msg.Value) } + if have, want := st.state.GetBalance(st.msg.From), balanceCheck; have.Cmp(want) < 0 { return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From.Hex(), have, want) } + if err := st.gp.SubGas(st.msg.GasLimit); err != nil { return err } + st.gasRemaining += st.msg.GasLimit st.initialGas = st.msg.GasLimit @@ -296,10 +305,12 @@ func (st *StateTransition) preCheck() error { return fmt.Errorf("%w: address %v, maxFeePerGas bit length: %d", ErrFeeCapVeryHigh, msg.From.Hex(), l) } + if l := msg.GasTipCap.BitLen(); l > 256 { return fmt.Errorf("%w: address %v, maxPriorityFeePerGas bit length: %d", ErrTipVeryHigh, msg.From.Hex(), l) } + if msg.GasFeeCap.Cmp(msg.GasTipCap) < 0 { return fmt.Errorf("%w: address %v, maxPriorityFeePerGas: %s, maxFeePerGas: %s", ErrTipAboveFeeCap, msg.From.Hex(), msg.GasTipCap, msg.GasFeeCap) @@ -327,7 +338,9 @@ func (st *StateTransition) preCheck() error { // nil evm execution result. func (st *StateTransition) TransitionDb(interruptCtx context.Context) (*ExecutionResult, error) { input1 := st.state.GetBalance(st.msg.From) + var input2 *big.Int + if !st.noFeeBurnAndTip { input2 = st.state.GetBalance(st.evm.Context.Coinbase) } @@ -348,6 +361,7 @@ func (st *StateTransition) TransitionDb(interruptCtx context.Context) (*Executio if tracer := st.evm.Config.Tracer; tracer != nil { tracer.CaptureTxStart(st.initialGas) + defer func() { tracer.CaptureTxEnd(st.gasRemaining) }() @@ -365,9 +379,11 @@ func (st *StateTransition) TransitionDb(interruptCtx context.Context) (*Executio if err != nil { return nil, err } + if st.gasRemaining < gas { return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining, gas) } + st.gasRemaining -= gas // Check clause 6 @@ -406,7 +422,9 @@ func (st *StateTransition) TransitionDb(interruptCtx context.Context) (*Executio // After EIP-3529: refunds are capped to gasUsed / 5 st.refundGas(params.RefundQuotientEIP3529) } + effectiveTip := msg.GasPrice + if rules.IsLondon { effectiveTip = cmath.BigMin(msg.GasTipCap, new(big.Int).Sub(msg.GasFeeCap, st.evm.Context.BaseFee)) } @@ -430,12 +448,15 @@ func (st *StateTransition) TransitionDb(interruptCtx context.Context) (*Executio if rules.IsLondon { burntContractAddress := common.HexToAddress(st.evm.ChainConfig().Bor.CalculateBurntContract(st.evm.Context.BlockNumber.Uint64())) burnAmount := new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.evm.Context.BaseFee) + if !st.noFeeBurnAndTip { st.state.AddBalance(burntContractAddress, burnAmount) } } + if !st.noFeeBurnAndTip { st.state.AddBalance(st.evm.Context.Coinbase, amount) + output1 := new(big.Int).SetBytes(input1.Bytes()) output2 := new(big.Int).SetBytes(input2.Bytes()) @@ -472,6 +493,7 @@ func (st *StateTransition) refundGas(refundQuotient uint64) { if refund > st.state.GetRefund() { refund = st.state.GetRefund() } + st.gasRemaining += refund // Return ETH for remaining gas, exchanged at the original rate. diff --git a/core/tests/blockchain_repair_test.go b/core/tests/blockchain_repair_test.go index e30be7a7ce..ad776fd56c 100644 --- a/core/tests/blockchain_repair_test.go +++ b/core/tests/blockchain_repair_test.go @@ -1984,10 +1984,12 @@ func TestIssue23496(t *testing.T) { SnapshotWait: true, } ) + chain, err := core.NewBlockChain(db, config, gspec, nil, engine, vm.Config{}, nil, nil, nil) if err != nil { t.Fatalf("Failed to create chain: %v", err) } + _, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, 4, func(i int, b *core.BlockGen) { b.SetCoinbase(common.Address{0x02}) b.SetDifficulty(big.NewInt(1000000)) @@ -1997,14 +1999,17 @@ func TestIssue23496(t *testing.T) { if _, err := chain.InsertChain(blocks[:1]); err != nil { t.Fatalf("Failed to import canonical chain start: %v", err) } + err = chain.StateCache().TrieDB().Commit(blocks[0].Root(), true) if err != nil { t.Fatal("on trieDB.Commit", err) } + // Insert block B2 and commit the snapshot into disk if _, err := chain.InsertChain(blocks[1:2]); err != nil { t.Fatalf("Failed to import canonical chain start: %v", err) } + if err := chain.Snaps().Cap(blocks[1].Root(), 0); err != nil { t.Fatalf("Failed to flatten snapshots: %v", err) } @@ -2035,20 +2040,24 @@ func TestIssue23496(t *testing.T) { if err != nil { t.Fatalf("Failed to reopen persistent database: %v", err) } + defer db.Close() chain, err = core.NewBlockChain(db, nil, gspec, nil, engine, vm.Config{}, nil, nil, nil) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } + defer chain.Stop() if head := chain.CurrentHeader(); head.Number.Uint64() != uint64(4) { t.Errorf("Head header mismatch: have %d, want %d", head.Number, 4) } + if head := chain.CurrentSnapBlock(); head.Number.Uint64() != uint64(4) { t.Errorf("Head fast block mismatch: have %d, want %d", head.Number, uint64(4)) } + if head := chain.CurrentBlock(); head.Number.Uint64() != uint64(1) { t.Errorf("Head block mismatch: have %d, want %d", head.Number, uint64(1)) } @@ -2057,12 +2066,15 @@ func TestIssue23496(t *testing.T) { if _, err := chain.InsertChain(blocks[1:]); err != nil { t.Fatalf("Failed to import canonical chain tail: %v", err) } + if head := chain.CurrentHeader(); head.Number.Uint64() != uint64(4) { t.Errorf("Head header mismatch: have %d, want %d", head.Number, 4) } + if head := chain.CurrentSnapBlock(); head.Number.Uint64() != uint64(4) { t.Errorf("Head fast block mismatch: have %d, want %d", head.Number, uint64(4)) } + if head := chain.CurrentBlock(); head.Number.Uint64() != uint64(4) { t.Errorf("Head block mismatch: have %d, want %d", head.Number, uint64(4)) } diff --git a/core/tests/blockchain_sethead_test.go b/core/tests/blockchain_sethead_test.go index 932d0d9240..6ac8b9b3c2 100644 --- a/core/tests/blockchain_sethead_test.go +++ b/core/tests/blockchain_sethead_test.go @@ -1984,10 +1984,12 @@ func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) { config.SnapshotLimit = 256 config.SnapshotWait = true } + chain, err := core.NewBlockChain(db, config, gspec, nil, engine, vm.Config{}, nil, nil, nil) if err != nil { t.Fatalf("Failed to create chain: %v", err) } + defer chain.Stop() // If sidechain blocks are needed, make a light chain and import it @@ -2000,6 +2002,7 @@ func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) { t.Fatalf("Failed to import side chain: %v", err) } } + canonblocks, _ := core.GenerateChain(gspec.Config, gspec.ToBlock(), engine, rawdb.NewMemoryDatabase(), tt.canonicalBlocks, func(i int, b *core.BlockGen) { b.SetCoinbase(common.Address{0x02}) b.SetDifficulty(big.NewInt(1000000)) @@ -2052,9 +2055,11 @@ func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) { if head := chain.CurrentHeader(); head.Number.Uint64() != tt.expHeadHeader { t.Errorf("Head header mismatch: have %d, want %d", head.Number, tt.expHeadHeader) } + if head := chain.CurrentSnapBlock(); head.Number.Uint64() != tt.expHeadFastBlock { t.Errorf("Head fast block mismatch: have %d, want %d", head.Number, tt.expHeadFastBlock) } + if head := chain.CurrentBlock(); head.Number.Uint64() != tt.expHeadBlock { t.Errorf("Head block mismatch: have %d, want %d", head.Number, tt.expHeadBlock) } diff --git a/core/tests/blockchain_snapshot_test.go b/core/tests/blockchain_snapshot_test.go index 7d63f1143b..fe87b7e227 100644 --- a/core/tests/blockchain_snapshot_test.go +++ b/core/tests/blockchain_snapshot_test.go @@ -84,10 +84,12 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*core.BlockChain, []*type // will happen during the block insertion. cacheConfig = core.DefaultCacheConfig ) + chain, err := core.NewBlockChain(db, cacheConfig, gspec, nil, engine, vm.Config{}, nil, nil, nil) if err != nil { t.Fatalf("Failed to create chain: %v", err) } + genDb, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, basic.chainBlocks, func(i int, b *core.BlockGen) {}) // Insert the blocks with configured settings. @@ -326,6 +328,7 @@ func (snaptest *gappedSnapshotTest) test(t *testing.T) { TrieTimeLimit: 5 * time.Minute, SnapshotLimit: 0, } + newchain, err := core.NewBlockChain(snaptest.db, cacheConfig, snaptest.gspec, nil, snaptest.engine, vm.Config{}, nil, nil, nil) if err != nil { t.Fatalf("Failed to recreate chain: %v", err) @@ -401,6 +404,7 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) { if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } + newBlocks, _ := core.GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], snaptest.engine, snaptest.genDb, snaptest.newBlocks, func(i int, b *core.BlockGen) {}) newchain.InsertChain(newBlocks) newchain.Stop() @@ -422,6 +426,7 @@ func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) { if err != nil { t.Fatalf("Failed to recreate chain: %v", err) } + defer newchain.Stop() snaptest.verify(t, newchain, blocks) } diff --git a/core/txpool/txpool_test.go b/core/txpool/txpool_test.go index e7d739f198..f356ce5407 100644 --- a/core/txpool/txpool_test.go +++ b/core/txpool/txpool_test.go @@ -4705,15 +4705,18 @@ func BenchmarkMultiAccountBatchInsert(b *testing.B) { defer pool.Stop() b.ReportAllocs() batches := make(types.Transactions, b.N) + for i := 0; i < b.N; i++ { key, _ := crypto.GenerateKey() account := crypto.PubkeyToAddress(key.PublicKey) pool.currentState.AddBalance(account, big.NewInt(1000000)) + tx := transaction(uint64(0), 100000, key) batches[i] = tx } // Benchmark importing the transactions into the queue b.ResetTimer() + for _, tx := range batches { pool.AddRemotesSync([]*types.Transaction{tx}) } diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index f80f1011e9..9f401523bc 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -508,6 +508,7 @@ func TestEip2929Cases(t *testing.T) { ops := strings.Join(instrs, ", ") fmt.Printf("### Case %d\n\n", id) id++ + fmt.Printf("%v\n\nBytecode: \n```\n%#x\n```\nOperations: \n```\n%v\n```\n\n", comment, code, ops) diff --git a/eth/api.go b/eth/api.go index 0bf7f86f58..905b0165a9 100644 --- a/eth/api.go +++ b/eth/api.go @@ -267,6 +267,7 @@ func (api *DebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error) { _, stateDb := api.eth.miner.Pending() return stateDb.RawDump(opts), nil } + var header *types.Header if blockNr == rpc.LatestBlockNumber { header = api.eth.blockchain.CurrentBlock() @@ -281,9 +282,11 @@ func (api *DebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error) { } header = block.Header() } + if header == nil { return state.Dump{}, fmt.Errorf("block #%d not found", blockNr) } + stateDb, err := api.eth.BlockChain().StateAt(header.Root) if err != nil { return state.Dump{}, err @@ -419,10 +422,13 @@ func (api *DebugAPI) StorageRangeAt(ctx context.Context, blockHash common.Hash, if block == nil { return StorageRangeResult{}, fmt.Errorf("block %#x not found", blockHash) } + _, _, statedb, release, err := api.eth.stateAtTransaction(ctx, block, txIndex, 0) + if err != nil { return StorageRangeResult{}, err } + defer release() st, err := statedb.StorageTrie(contractAddress) @@ -523,6 +529,7 @@ func (api *DebugAPI) getModifiedAccounts(startBlock, endBlock *types.Block) ([]c if err != nil { return nil, err } + newTrie, err := trie.NewStateTrie(trie.StateTrieID(endBlock.Root()), triedb) if err != nil { return nil, err @@ -599,6 +606,7 @@ func (api *DebugAPI) GetAccessibleState(from, to rpc.BlockNumber) (uint64, error return uint64(i), nil } } + return 0, errors.New("no state found") } @@ -609,6 +617,8 @@ func (api *DebugAPI) SetTrieFlushInterval(interval string) error { if err != nil { return err } + api.eth.blockchain.SetTrieFlushInterval(t) + return nil } diff --git a/eth/api_backend.go b/eth/api_backend.go index 1b04fbc402..d0eef55dc4 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -74,24 +74,32 @@ func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumb if number == rpc.LatestBlockNumber { return b.eth.blockchain.CurrentBlock(), nil } + if number == rpc.FinalizedBlockNumber { if !b.eth.Merger().TDDReached() { return nil, errors.New("'finalized' tag not supported on pre-merge network") } + block := b.eth.blockchain.CurrentFinalBlock() + if block != nil { return block, nil } + return nil, errors.New("finalized block not found") } + if number == rpc.SafeBlockNumber { if !b.eth.Merger().TDDReached() { return nil, errors.New("'safe' tag not supported on pre-merge network") } + block := b.eth.blockchain.CurrentSafeBlock() + if block != nil { return block, nil } + return nil, errors.New("safe block not found") } return b.eth.blockchain.GetHeaderByNumber(uint64(number)), nil @@ -129,18 +137,24 @@ func (b *EthAPIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumbe header := b.eth.blockchain.CurrentBlock() return b.eth.blockchain.GetBlock(header.Hash(), header.Number.Uint64()), nil } + if number == rpc.FinalizedBlockNumber { if !b.eth.Merger().TDDReached() { return nil, errors.New("'finalized' tag not supported on pre-merge network") } + header := b.eth.blockchain.CurrentFinalBlock() + return b.eth.blockchain.GetBlock(header.Hash(), header.Number.Uint64()), nil } + if number == rpc.SafeBlockNumber { if !b.eth.Merger().TDDReached() { return nil, errors.New("'safe' tag not supported on pre-merge network") } + header := b.eth.blockchain.CurrentSafeBlock() + return b.eth.blockchain.GetBlock(header.Hash(), header.Number.Uint64()), nil } return b.eth.blockchain.GetBlockByNumber(uint64(number)), nil @@ -155,9 +169,11 @@ func (b *EthAPIBackend) GetBody(ctx context.Context, hash common.Hash, number rp if number < 0 || hash == (common.Hash{}) { return nil, errors.New("invalid arguments; expect hash and no special block numbers") } + if body := b.eth.blockchain.GetBody(hash); body != nil { return body, nil } + return nil, errors.New("block body not found") } @@ -246,6 +262,7 @@ func (b *EthAPIBackend) GetEVM(ctx context.Context, msg *core.Message, state *st } txContext := core.NewEVMTxContext(msg) context := core.NewEVMBlockContext(header, b.eth.BlockChain(), nil) + return vm.NewEVM(context, txContext, state, b.eth.blockchain.Config(), *vmConfig), state.Error, nil } diff --git a/eth/api_test.go b/eth/api_test.go index fca17f1217..316c0899d9 100644 --- a/eth/api_test.go +++ b/eth/api_test.go @@ -213,6 +213,7 @@ func TestStorageRangeAt(t *testing.T) { if err != nil { t.Error(err) } + result, err := storageRangeAt(tr, test.start, test.limit) if err != nil { t.Error(err) diff --git a/eth/backend.go b/eth/backend.go index 6014f8f79e..746472b070 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -140,6 +140,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if err != nil { return nil, err } + if err := pruner.RecoverPruning(stack.ResolvePath(""), chainDb, stack.ResolvePath(config.TrieCleanCacheJournal)); err != nil { log.Error("Failed to recover state", "error", err) } @@ -147,6 +148,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { ethashConfig := config.Ethash ethashConfig.NotifyFull = config.Miner.NotifyFull cliqueConfig, err := core.LoadCliqueConfig(chainDb, config.Genesis) + if err != nil { return nil, err } @@ -180,6 +182,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if gpoParams.Default == nil { gpoParams.Default = config.Miner.GasPrice } + ethereum.APIBackend.gpo = gasprice.NewOracle(ethereum.APIBackend, gpoParams) // Override the chain config with provided settings. @@ -199,6 +202,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { // END: Bor changes bcVersion := rawdb.ReadDatabaseVersion(chainDb) + var dbVer = "" if bcVersion != nil { dbVer = fmt.Sprintf("%d", *bcVersion) @@ -259,6 +263,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if config.TxPool.Journal != "" { config.TxPool.Journal = stack.ResolvePath(config.TxPool.Journal) } + ethereum.txPool = txpool.NewTxPool(config.TxPool, ethereum.blockchain.Config(), ethereum.blockchain) // Permit the downloader to use the trie cache allowance during fast sync @@ -267,6 +272,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if checkpoint == nil { checkpoint = params.TrustedCheckpoints[ethereum.blockchain.Genesis().Hash()] } + if ethereum.handler, err = newHandler(&handlerConfig{ Database: chainDb, Chain: ethereum.blockchain, @@ -294,6 +300,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if err != nil { return nil, err } + ethereum.snapDialCandidates, err = dnsclient.NewIterator(ethereum.config.SnapDiscoveryURLs...) if err != nil { return nil, err diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 6f83a0cbd5..9f51f6695b 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -135,6 +135,7 @@ func NewConsensusAPI(eth *eth.Ethereum) *ConsensusAPI { if eth.BlockChain().Config().TerminalTotalDifficulty == nil { log.Warn("Engine API started but chain not configured for merge yet") } + api := &ConsensusAPI{ eth: eth, remoteBlocks: newHeaderQueue(), @@ -143,6 +144,7 @@ func NewConsensusAPI(eth *eth.Ethereum) *ConsensusAPI { invalidTipsets: make(map[common.Hash]*types.Header), } eth.Downloader().SetBadBlockCallback(api.setInvalidAncestor) + go api.heartbeat() return api @@ -167,10 +169,12 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update engine.ForkchoiceStateV1, pa if payloadAttributes.Withdrawals != nil { return engine.STATUS_INVALID, engine.InvalidParams.With(fmt.Errorf("withdrawals not supported in V1")) } + if api.eth.BlockChain().Config().IsShanghai(payloadAttributes.Timestamp) { return engine.STATUS_INVALID, engine.InvalidParams.With(fmt.Errorf("forkChoiceUpdateV1 called post-shanghai")) } } + return api.forkchoiceUpdated(update, payloadAttributes) } @@ -181,6 +185,7 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV2(update engine.ForkchoiceStateV1, pa return engine.STATUS_INVALID, engine.InvalidParams.With(err) } } + return api.forkchoiceUpdated(update, payloadAttributes) } @@ -196,6 +201,7 @@ func (api *ConsensusAPI) verifyPayloadAttributes(attr *engine.PayloadAttributes) return errors.New("missing withdrawals list") } } + return nil } @@ -242,7 +248,9 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl merger.ReachTTD() api.eth.Downloader().Cancel() } + context := []interface{}{"number", header.Number, "hash", header.Hash()} + if update.FinalizedBlockHash != (common.Hash{}) { if finalized == nil { context = append(context, []interface{}{"finalized", "unknown"}...) @@ -250,10 +258,13 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl context = append(context, []interface{}{"finalized", finalized.Number}...) } } + log.Info("Forkchoice requested sync to new head", context...) + if err := api.eth.Downloader().BeaconSync(api.eth.SyncMode(), header, finalized); err != nil { return engine.STATUS_SYNCING, err } + return engine.STATUS_SYNCING, nil } // Block is known locally, just sanity check that the beacon client does not @@ -268,15 +279,18 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl log.Error("TDs unavailable for TTD check", "number", block.NumberU64(), "hash", update.HeadBlockHash, "td", td, "parent", block.ParentHash(), "ptd", ptd) return engine.STATUS_INVALID, errors.New("TDs unavailable for TDD check") } + if td.Cmp(ttd) < 0 { log.Error("Refusing beacon update to pre-merge", "number", block.NumberU64(), "hash", update.HeadBlockHash, "diff", block.Difficulty(), "age", common.PrettyAge(time.Unix(int64(block.Time()), 0))) return engine.ForkChoiceResponse{PayloadStatus: engine.INVALID_TERMINAL_BLOCK, PayloadID: nil}, nil } + if block.NumberU64() > 0 && ptd.Cmp(ttd) >= 0 { log.Error("Parent block is already post-ttd", "number", block.NumberU64(), "hash", update.HeadBlockHash, "diff", block.Difficulty(), "age", common.PrettyAge(time.Unix(int64(block.Time()), 0))) return engine.ForkChoiceResponse{PayloadStatus: engine.INVALID_TERMINAL_BLOCK, PayloadID: nil}, nil } } + valid := func(id *engine.PayloadID) engine.ForkChoiceResponse { return engine.ForkChoiceResponse{ PayloadStatus: engine.PayloadStatusV1{Status: engine.VALID, LatestValidHash: &update.HeadBlockHash}, @@ -349,16 +363,20 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl if api.localBlocks.has(id) { return valid(&id), nil } + payload, err := api.eth.Miner().BuildPayload(args) if err != nil { log.Error("Failed to build payload", "err", err) return valid(nil), engine.InvalidPayloadAttributes.With(err) } + api.localBlocks.put(id, payload) + return valid(&id), nil } + return valid(nil), nil - } +} // ExchangeTransitionConfigurationV1 checks the given configuration against // the configuration of the node. @@ -387,6 +405,7 @@ func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config engine.Transit } return nil, fmt.Errorf("invalid terminal block hash") } + return &engine.TransitionConfigurationV1{TerminalTotalDifficulty: (*hexutil.Big)(ttd)}, nil } @@ -396,6 +415,7 @@ func (api *ConsensusAPI) GetPayloadV1(payloadID engine.PayloadID) (*engine.Execu if err != nil { return nil, err } + return data.ExecutionPayload, nil } @@ -418,6 +438,7 @@ func (api *ConsensusAPI) NewPayloadV1(params engine.ExecutableData) (engine.Payl if params.Withdrawals != nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(fmt.Errorf("withdrawals not supported in V1")) } + return api.newPayload(params) } @@ -430,6 +451,7 @@ func (api *ConsensusAPI) NewPayloadV2(params engine.ExecutableData) (engine.Payl } else if params.Withdrawals != nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(fmt.Errorf("non-nil withdrawals pre-shanghai")) } + return api.newPayload(params) } @@ -466,6 +488,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData) (engine.Payloa if block := api.eth.BlockChain().GetBlockByHash(params.BlockHash); block != nil { log.Warn("Ignoring already known beacon payload", "number", params.Number, "hash", params.BlockHash, "age", common.PrettyAge(time.Unix(int64(block.Time()), 0))) hash := block.Hash() + return engine.PayloadStatusV1{Status: engine.VALID, LatestValidHash: &hash}, nil } // If this block was rejected previously, keep rejecting it @@ -489,10 +512,12 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData) (engine.Payloa ttd = api.eth.BlockChain().Config().TerminalTotalDifficulty gptd = api.eth.BlockChain().GetTd(parent.ParentHash(), parent.NumberU64()-1) ) + if ptd.Cmp(ttd) < 0 { log.Warn("Ignoring pre-merge payload", "number", params.Number, "hash", params.BlockHash, "td", ptd, "ttd", ttd) return engine.INVALID_TERMINAL_BLOCK, nil } + if parent.Difficulty().BitLen() > 0 && gptd != nil && gptd.Cmp(ttd) >= 0 { log.Error("Ignoring pre-merge parent block", "number", params.Number, "hash", params.BlockHash, "td", ptd, "ttd", ttd) return engine.INVALID_TERMINAL_BLOCK, nil @@ -511,6 +536,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData) (engine.Payloa if !api.eth.BlockChain().HasBlockAndState(block.ParentHash(), block.NumberU64()-1) { api.remoteBlocks.put(block.Hash(), block.Header()) log.Warn("State not available, ignoring new payload") + return engine.PayloadStatusV1{Status: engine.ACCEPTED}, nil } log.Trace("Inserting block without sethead", "hash", block.Hash(), "number", block.Number) @@ -532,6 +558,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData) (engine.Payloa api.eth.Downloader().Cancel() } hash := block.Hash() + return engine.PayloadStatusV1{Status: engine.VALID, LatestValidHash: &hash}, nil } @@ -571,6 +598,7 @@ func (api *ConsensusAPI) delayPayloadImport(block *types.Block) (engine.PayloadS // and cannot afford concurrent out-if-band modifications via imports. log.Warn("Ignoring payload while snap syncing", "number", block.NumberU64(), "hash", block.Hash()) } + return engine.PayloadStatusV1{Status: engine.SYNCING}, nil } @@ -609,17 +637,20 @@ func (api *ConsensusAPI) checkInvalidAncestor(check common.Hash, head common.Has delete(api.invalidTipsets, descendant) } } + return nil } // Not too many failures yet, mark the head of the invalid chain as invalid if check != head { log.Warn("Marked new chain head as invalid", "hash", head, "badnumber", invalid.Number, "badhash", badHash) + for len(api.invalidTipsets) >= invalidTipsetsCap { for key := range api.invalidTipsets { delete(api.invalidTipsets, key) break } } + api.invalidTipsets[head] = invalid } // If the last valid hash is the terminal pow block, return 0x0 for latest valid hash @@ -627,7 +658,9 @@ func (api *ConsensusAPI) checkInvalidAncestor(check common.Hash, head common.Has if header := api.eth.BlockChain().GetHeader(invalid.ParentHash, invalid.Number.Uint64()-1); header != nil && header.Difficulty.Sign() != 0 { lastValid = &common.Hash{} } + failure := "links to previously rejected block" + return &engine.PayloadStatusV1{ Status: engine.INVALID, LatestValidHash: lastValid, @@ -648,6 +681,7 @@ func (api *ConsensusAPI) invalid(err error, latestValid *types.Header) engine.Pa } } errorMsg := err.Error() + return engine.PayloadStatusV1{Status: engine.INVALID, LatestValidHash: ¤tHash, ValidationError: &errorMsg} } @@ -702,8 +736,10 @@ func (api *ConsensusAPI) heartbeat() { } else { log.Warn("Beacon client online, but no consensus updates received in a while. Please fix your beacon client to follow the chain!") } + offlineLogged = time.Now() } + continue } } @@ -718,10 +754,12 @@ func (api *ConsensusAPI) ExchangeCapabilities([]string) []string { // of block bodies by the engine api. func (api *ConsensusAPI) GetPayloadBodiesByHashV1(hashes []common.Hash) []*engine.ExecutionPayloadBodyV1 { var bodies = make([]*engine.ExecutionPayloadBodyV1, len(hashes)) + for i, hash := range hashes { block := api.eth.BlockChain().GetBlockByHash(hash) bodies[i] = getBody(block) } + return bodies } @@ -731,20 +769,25 @@ func (api *ConsensusAPI) GetPayloadBodiesByRangeV1(start, count hexutil.Uint64) if start == 0 || count == 0 { return nil, engine.InvalidParams.With(fmt.Errorf("invalid start or count, start: %v count: %v", start, count)) } + if count > 1024 { return nil, engine.TooLargeRequest.With(fmt.Errorf("requested count too large: %v", count)) } // limit count up until current current := api.eth.BlockChain().CurrentBlock().Number.Uint64() last := uint64(start) + uint64(count) - 1 + if last > current { last = current } + bodies := make([]*engine.ExecutionPayloadBodyV1, 0, uint64(count)) + for i := uint64(start); i <= last; i++ { block := api.eth.BlockChain().GetBlockByNumber(i) bodies = append(bodies, getBody(block)) } + return bodies, nil } diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 8a94722b1f..d46abce09c 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -61,6 +61,7 @@ var ( func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block) { config := *params.AllEthashProtocolChanges engine := consensus.Engine(beaconConsensus.New(ethash.NewFaker())) + if merged { config.TerminalTotalDifficulty = common.Big0 config.TerminalTotalDifficultyPassed = true @@ -78,6 +79,7 @@ func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block) { generate := func(i int, g *core.BlockGen) { g.OffsetTime(5) g.SetExtra([]byte("test")) + tx, _ := types.SignTx(types.NewTransaction(testNonce, common.HexToAddress("0x9a9070028361F7AAbeB3f2F2Dc07F82C4a98A02a"), big.NewInt(1), params.TxGas, big.NewInt(params.InitialBaseFee*2), nil), types.LatestSigner(&config), testKey) g.AddTx(tx) testNonce++ @@ -89,6 +91,7 @@ func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block) { for _, b := range blocks { totalDifficulty.Add(totalDifficulty, b.Difficulty()) } + config.TerminalTotalDifficulty = totalDifficulty } @@ -109,6 +112,7 @@ func TestEth2AssembleBlock(t *testing.T) { t.Fatalf("error signing transaction, err=%v", err) } ethservice.TxPool().AddLocal(tx) + blockParams := engine.PayloadAttributes{ Timestamp: blocks[9].Time() + 5, } @@ -127,12 +131,15 @@ func assembleWithTransactions(api *ConsensusAPI, parentHash common.Hash, params if err != nil { return nil, err } + if have, want := len(execData.Transactions), want; have != want { err = fmt.Errorf("invalid number of transactions, have %d want %d", have, want) continue } + return execData, nil } + return nil, err } @@ -147,6 +154,7 @@ func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) { // Put the 10th block's tx in the pool and produce a new block api.eth.TxPool().AddRemotesSync(blocks[9].Transactions()) + blockParams := engine.PayloadAttributes{ Timestamp: blocks[8].Time() + 5, } @@ -180,12 +188,14 @@ func TestEth2PrepareAndGetPayload(t *testing.T) { // We need to properly set the terminal total difficulty genesis.Config.TerminalTotalDifficulty.Sub(genesis.Config.TerminalTotalDifficulty, blocks[9].Difficulty()) n, ethservice := startEthService(t, genesis, blocks[:9]) + defer n.Close() api := NewConsensusAPI(ethservice) // Put the 10th block's tx in the pool and produce a new block ethservice.TxPool().AddLocals(blocks[9].Transactions()) + blockParams := engine.PayloadAttributes{ Timestamp: blocks[8].Time() + 5, } @@ -195,11 +205,13 @@ func TestEth2PrepareAndGetPayload(t *testing.T) { FinalizedBlockHash: common.Hash{}, } _, err := api.ForkchoiceUpdatedV1(fcState, &blockParams) + if err != nil { t.Fatalf("error preparing payload, err=%v", err) } // give the payload some time to be built time.Sleep(100 * time.Millisecond) + payloadID := (&miner.BuildPayloadArgs{ Parent: fcState.HeadBlockHash, Timestamp: blockParams.Timestamp, @@ -207,17 +219,21 @@ func TestEth2PrepareAndGetPayload(t *testing.T) { Random: blockParams.Random, }).Id() execData, err := api.GetPayloadV1(payloadID) + if err != nil { t.Fatalf("error getting payload, err=%v", err) } + if len(execData.Transactions) != blocks[9].Transactions().Len() { t.Fatalf("invalid number of transactions %d != 1", len(execData.Transactions)) } // Test invalid payloadID var invPayload engine.PayloadID + copy(invPayload[:], payloadID[:]) invPayload[0] = ^invPayload[0] _, err = api.GetPayloadV1(invPayload) + if err == nil { t.Fatal("expected error retrieving invalid payload") } @@ -287,6 +303,7 @@ func TestInvalidPayloadTimestamp(t *testing.T) { func TestEth2NewBlock(t *testing.T) { t.Skip("ETH2 in Bor") + genesis, preMergeBlocks := generateMergeChain(10, false) n, ethservice := startEthService(t, genesis, preMergeBlocks) defer n.Close() @@ -316,11 +333,13 @@ func TestEth2NewBlock(t *testing.T) { if err != nil { t.Fatalf("Failed to create the executable data %v", err) } + block, err := engine.ExecutableDataToBlock(*execData) if err != nil { t.Fatalf("Failed to convert executable data to block %v", err) } newResp, err := api.NewPayloadV1(*execData) + switch { case err != nil: t.Fatalf("Failed to insert block: %v", err) @@ -330,14 +349,17 @@ func TestEth2NewBlock(t *testing.T) { t.Fatalf("Chain head shouldn't be updated") } checkLogEvents(t, newLogCh, rmLogsCh, 0, 0) + fcState := engine.ForkchoiceStateV1{ HeadBlockHash: block.Hash(), SafeBlockHash: block.Hash(), FinalizedBlockHash: block.Hash(), } + if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { t.Fatalf("Failed to insert block: %v", err) } + if have, want := ethservice.BlockChain().CurrentBlock().Number.Uint64(), block.NumberU64(); have != want { t.Fatalf("Chain head should be updated, have %d want %d", have, want) } @@ -350,22 +372,28 @@ func TestEth2NewBlock(t *testing.T) { var ( head = ethservice.BlockChain().CurrentBlock().Number.Uint64() ) + parent = preMergeBlocks[len(preMergeBlocks)-1] for i := 0; i < 10; i++ { execData, err := assembleBlock(api, parent.Hash(), &engine.PayloadAttributes{ Timestamp: parent.Time() + 6, }) + if err != nil { t.Fatalf("Failed to create the executable data %v", err) } + block, err := engine.ExecutableDataToBlock(*execData) + if err != nil { t.Fatalf("Failed to convert executable data to block %v", err) } + newResp, err := api.NewPayloadV1(*execData) if err != nil || newResp.Status != "VALID" { t.Fatalf("Failed to insert block: %v", err) } + if ethservice.BlockChain().CurrentBlock().Number.Uint64() != head { t.Fatalf("Chain head shouldn't be updated") } @@ -375,9 +403,11 @@ func TestEth2NewBlock(t *testing.T) { SafeBlockHash: block.Hash(), FinalizedBlockHash: block.Hash(), } + if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { t.Fatalf("Failed to insert block: %v", err) } + if ethservice.BlockChain().CurrentBlock().Number.Uint64() != block.NumberU64() { t.Fatalf("Chain head should be updated") } @@ -487,38 +517,48 @@ func TestFullAPI(t *testing.T) { func setupBlocks(t *testing.T, ethservice *eth.Ethereum, n int, parent *types.Header, callback func(parent *types.Header), withdrawals [][]*types.Withdrawal) []*types.Header { api := NewConsensusAPI(ethservice) var blocks []*types.Header + for i := 0; i < n; i++ { callback(parent) + var w []*types.Withdrawal + if withdrawals != nil { w = withdrawals[i] } payload := getNewPayload(t, api, parent, w) execResp, err := api.NewPayloadV2(*payload) + if err != nil { t.Fatalf("can't execute payload: %v", err) } + if execResp.Status != engine.VALID { t.Fatalf("invalid status: %v", execResp.Status) } + fcState := engine.ForkchoiceStateV1{ HeadBlockHash: payload.BlockHash, SafeBlockHash: payload.ParentHash, FinalizedBlockHash: payload.ParentHash, } + if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { t.Fatalf("Failed to insert block: %v", err) } + if ethservice.BlockChain().CurrentBlock().Number.Uint64() != payload.Number { t.Fatal("Chain head should be updated") } + if ethservice.BlockChain().CurrentFinalBlock().Number.Uint64() != payload.Number-1 { t.Fatal("Finalized block should be updated") } parent = ethservice.BlockChain().CurrentBlock() blocks = append(blocks, parent) } + return blocks } @@ -588,6 +628,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) { genesis, preMergeBlocks := generateMergeChain(10, false) n, ethservice := startEthService(t, genesis, preMergeBlocks) + defer n.Close() var ( @@ -597,6 +638,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) { // This EVM code generates a log when the contract is created. logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") ) + for i := 0; i < 10; i++ { statedb, _ := ethservice.BlockChain().StateAt(parent.Root) tx := types.MustSignNewTx(testKey, signer, &types.LegacyTx{ @@ -607,6 +649,7 @@ func TestNewPayloadOnInvalidChain(t *testing.T) { Data: logCode, }) ethservice.TxPool().AddRemotesSync([]*types.Transaction{tx}) + var ( params = engine.PayloadAttributes{ Timestamp: parent.Time + 1, @@ -622,45 +665,59 @@ func TestNewPayloadOnInvalidChain(t *testing.T) { resp engine.ForkChoiceResponse err error ) + for i := 0; ; i++ { if resp, err = api.ForkchoiceUpdatedV1(fcState, ¶ms); err != nil { t.Fatalf("error preparing payload, err=%v", err) } + if resp.PayloadStatus.Status != engine.VALID { t.Fatalf("error preparing payload, invalid status: %v", resp.PayloadStatus.Status) } + // give the payload some time to be built time.Sleep(50 * time.Millisecond) + if payload, err = api.GetPayloadV1(*resp.PayloadID); err != nil { t.Fatalf("can't get payload: %v", err) } + if len(payload.Transactions) > 0 { break } + // No luck this time we need to update the params and try again. params.Timestamp = params.Timestamp + 1 + if i > 10 { t.Fatalf("payload should not be empty") } } + execResp, err := api.NewPayloadV1(*payload) + if err != nil { t.Fatalf("can't execute payload: %v", err) } + if execResp.Status != engine.VALID { t.Fatalf("invalid status: %v", execResp.Status) } + fcState = engine.ForkchoiceStateV1{ HeadBlockHash: payload.BlockHash, SafeBlockHash: payload.ParentHash, FinalizedBlockHash: payload.ParentHash, } + if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { t.Fatalf("Failed to insert block: %v", err) } + if ethservice.BlockChain().CurrentBlock().Number.Uint64() != payload.Number { t.Fatalf("Chain head should be updated") } + parent = ethservice.BlockChain().CurrentBlock() } } @@ -674,9 +731,11 @@ func assembleBlock(api *ConsensusAPI, parentHash common.Hash, params *engine.Pay Withdrawals: params.Withdrawals, } payload, err := api.eth.Miner().BuildPayload(args) + if err != nil { return nil, err } + return payload.ResolveFull().ExecutionPayload, nil } @@ -685,6 +744,7 @@ func TestEmptyBlocks(t *testing.T) { genesis, preMergeBlocks := generateMergeChain(10, false) n, ethservice := startEthService(t, genesis, preMergeBlocks) + defer n.Close() commonAncestor := ethservice.BlockChain().CurrentBlock() @@ -700,9 +760,11 @@ func TestEmptyBlocks(t *testing.T) { if err != nil { t.Fatal(err) } + if status.Status != engine.VALID { t.Errorf("invalid status: expected VALID got: %v", status.Status) } + if !bytes.Equal(status.LatestValidHash[:], payload.BlockHash[:]) { t.Fatalf("invalid LVH: got %v want %v", status.LatestValidHash, payload.BlockHash) } @@ -716,6 +778,7 @@ func TestEmptyBlocks(t *testing.T) { if err != nil { t.Fatal(err) } + if status.Status != engine.INVALID { t.Errorf("invalid status: expected INVALID got: %v", status.Status) } @@ -734,9 +797,11 @@ func TestEmptyBlocks(t *testing.T) { if err != nil { t.Fatal(err) } + if status.Status != engine.SYNCING { t.Errorf("invalid status: expected SYNCING got: %v", status.Status) } + if status.LatestValidHash != nil { t.Fatalf("invalid LVH: got %v wanted nil", status.LatestValidHash) } @@ -756,6 +821,7 @@ func getNewPayload(t *testing.T, api *ConsensusAPI, parent *types.Header, withdr if err != nil { t.Fatal(err) } + return payload } @@ -765,6 +831,7 @@ func setBlockhash(data *engine.ExecutableData) *engine.ExecutableData { txs, _ := decodeTransactions(data.Transactions) number := big.NewInt(0) number.SetUint64(data.Number) + header := &types.Header{ ParentHash: data.ParentHash, UncleHash: types.EmptyUncleHash, @@ -784,18 +851,22 @@ func setBlockhash(data *engine.ExecutableData) *engine.ExecutableData { } block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */) data.BlockHash = block.Hash() + return data } func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { var txs = make([]*types.Transaction, len(enc)) + for i, encTx := range enc { var tx types.Transaction if err := tx.UnmarshalBinary(encTx); err != nil { return nil, fmt.Errorf("invalid transaction %d: %v", i, err) } + txs[i] = &tx } + return txs, nil } @@ -806,13 +877,16 @@ func TestTrickRemoteBlockCache(t *testing.T) { genesis, preMergeBlocks := generateMergeChain(10, false) nodeA, ethserviceA := startEthService(t, genesis, preMergeBlocks) nodeB, ethserviceB := startEthService(t, genesis, preMergeBlocks) + defer nodeA.Close() defer nodeB.Close() + for nodeB.Server().NodeInfo().Ports.Listener == 0 { time.Sleep(250 * time.Millisecond) } nodeA.Server().AddPeer(nodeB.Server().Self()) nodeB.Server().AddPeer(nodeA.Server().Self()) + apiA := NewConsensusAPI(ethserviceA) apiB := NewConsensusAPI(ethserviceB) @@ -850,6 +924,7 @@ func TestTrickRemoteBlockCache(t *testing.T) { if err != nil { panic(err) } + if status.Status == engine.VALID { t.Error("invalid status: VALID on an invalid chain") } @@ -858,9 +933,11 @@ func TestTrickRemoteBlockCache(t *testing.T) { if err != nil { t.Fatal(err) } + if resp.PayloadStatus.Status == engine.VALID { t.Error("invalid status: VALID on an invalid chain") } + time.Sleep(100 * time.Millisecond) } } @@ -871,6 +948,7 @@ func TestInvalidBloom(t *testing.T) { genesis, preMergeBlocks := generateMergeChain(10, false) n, ethservice := startEthService(t, genesis, preMergeBlocks) ethservice.Merger().ReachTTD() + defer n.Close() commonAncestor := ethservice.BlockChain().CurrentBlock() @@ -883,9 +961,11 @@ func TestInvalidBloom(t *testing.T) { payload := getNewPayload(t, api, commonAncestor, nil) payload.LogsBloom = append(payload.LogsBloom, byte(1)) status, err := api.NewPayloadV1(*payload) + if err != nil { t.Fatal(err) } + if status.Status != engine.INVALID { t.Errorf("invalid status: expected INVALID got: %v", status.Status) } @@ -896,7 +976,9 @@ func TestNewPayloadOnInvalidTerminalBlock(t *testing.T) { genesis, preMergeBlocks := generateMergeChain(100, false) n, ethservice := startEthService(t, genesis, preMergeBlocks) + defer n.Close() + api := NewConsensusAPI(ethservice) // Test parent already post TTD in FCU @@ -907,9 +989,11 @@ func TestNewPayloadOnInvalidTerminalBlock(t *testing.T) { FinalizedBlockHash: common.Hash{}, } resp, err := api.ForkchoiceUpdatedV1(fcState, nil) + if err != nil { t.Fatalf("error sending forkchoice, err=%v", err) } + if resp.PayloadStatus != engine.INVALID_TERMINAL_BLOCK { t.Fatalf("error sending invalid forkchoice, invalid status: %v", resp.PayloadStatus.Status) } @@ -922,9 +1006,11 @@ func TestNewPayloadOnInvalidTerminalBlock(t *testing.T) { FeeRecipient: parent.Coinbase(), } payload, err := api.eth.Miner().BuildPayload(args) + if err != nil { t.Fatalf("error preparing payload, err=%v", err) } + data := *payload.Resolve().ExecutionPayload // We need to recompute the blockhash, since the miner computes a wrong (correct) blockhash txs, _ := decodeTransactions(data.Transactions) @@ -952,6 +1038,7 @@ func TestNewPayloadOnInvalidTerminalBlock(t *testing.T) { if err != nil { t.Fatalf("error sending NewPayload, err=%v", err) } + if resp2 != engine.INVALID_TERMINAL_BLOCK { t.Fatalf("error sending invalid forkchoice, invalid status: %v", resp.PayloadStatus.Status) } @@ -965,12 +1052,14 @@ func TestSimultaneousNewBlock(t *testing.T) { genesis, preMergeBlocks := generateMergeChain(10, false) n, ethservice := startEthService(t, genesis, preMergeBlocks) + defer n.Close() var ( api = NewConsensusAPI(ethservice) parent = preMergeBlocks[len(preMergeBlocks)-1] ) + for i := 0; i < 10; i++ { execData, err := assembleBlock(api, parent.Hash(), &engine.PayloadAttributes{ Timestamp: parent.Time() + 5, @@ -1005,13 +1094,17 @@ func TestSimultaneousNewBlock(t *testing.T) { t.Fatal(testErr) } } + block, err := engine.ExecutableDataToBlock(*execData) + if err != nil { t.Fatalf("Failed to convert executable data to block %v", err) } + if ethservice.BlockChain().CurrentBlock().Number.Uint64() != block.NumberU64()-1 { t.Fatalf("Chain head shouldn't be updated") } + fcState := engine.ForkchoiceStateV1{ HeadBlockHash: block.Hash(), SafeBlockHash: block.Hash(), @@ -1040,9 +1133,11 @@ func TestSimultaneousNewBlock(t *testing.T) { t.Fatal(testErr) } } + if have, want := ethservice.BlockChain().CurrentBlock().Number.Uint64(), block.NumberU64(); have != want { t.Fatalf("Chain head should be updated, have %d want %d", have, want) } + parent = block } } @@ -1059,6 +1154,7 @@ func TestWithdrawals(t *testing.T) { n, ethservice := startEthService(t, genesis, blocks) ethservice.Merger().ReachTTD() + defer n.Close() api := NewConsensusAPI(ethservice) @@ -1073,9 +1169,11 @@ func TestWithdrawals(t *testing.T) { HeadBlockHash: parent.Hash(), } resp, err := api.ForkchoiceUpdatedV2(fcState, &blockParams) + if err != nil { t.Fatalf("error preparing payload, err=%v", err) } + if resp.PayloadStatus.Status != engine.VALID { t.Fatalf("unexpected status (got: %s, want: %s)", resp.PayloadStatus.Status, engine.VALID) } @@ -1089,9 +1187,11 @@ func TestWithdrawals(t *testing.T) { Withdrawals: blockParams.Withdrawals, }).Id() execData, err := api.GetPayloadV2(payloadID) + if err != nil { t.Fatalf("error getting payload, err=%v", err) } + if execData.ExecutionPayload.StateRoot != parent.Root { t.Fatalf("mismatch state roots (got: %s, want: %s)", execData.ExecutionPayload.StateRoot, blocks[8].Root()) } @@ -1123,6 +1223,7 @@ func TestWithdrawals(t *testing.T) { } fcState.HeadBlockHash = execData.ExecutionPayload.BlockHash _, err = api.ForkchoiceUpdatedV2(fcState, &blockParams) + if err != nil { t.Fatalf("error preparing payload, err=%v", err) } @@ -1136,9 +1237,11 @@ func TestWithdrawals(t *testing.T) { Withdrawals: blockParams.Withdrawals, }).Id() execData, err = api.GetPayloadV2(payloadID) + if err != nil { t.Fatalf("error getting payload, err=%v", err) } + if status, err := api.NewPayloadV2(*execData.ExecutionPayload); err != nil { t.Fatalf("error validating payload: %v", err) } else if status.Status != engine.VALID { @@ -1148,6 +1251,7 @@ func TestWithdrawals(t *testing.T) { // 11: set block as head. fcState.HeadBlockHash = execData.ExecutionPayload.BlockHash _, err = api.ForkchoiceUpdatedV2(fcState, nil) + if err != nil { t.Fatalf("error preparing payload, err=%v", err) } @@ -1157,6 +1261,7 @@ func TestWithdrawals(t *testing.T) { if err != nil { t.Fatalf("unable to load db: %v", err) } + for i, w := range blockParams.Withdrawals { // w.Amount is in gwei, balance in wei if db.GetBalance(w.Address).Uint64() != w.Amount*params.GWei { @@ -1175,6 +1280,7 @@ func TestNilWithdrawals(t *testing.T) { n, ethservice := startEthService(t, genesis, blocks) ethservice.Merger().ReachTTD() + defer n.Close() api := NewConsensusAPI(ethservice) @@ -1185,6 +1291,7 @@ func TestNilWithdrawals(t *testing.T) { blockParams engine.PayloadAttributes wantErr bool } + tests := []test{ // Before Shanghai { @@ -1254,8 +1361,10 @@ func TestNilWithdrawals(t *testing.T) { if err == nil { t.Fatal("wanted error on fcuv2 with invalid withdrawals") } + continue } + if err != nil { t.Fatalf("error preparing payload, err=%v", err) } @@ -1268,9 +1377,11 @@ func TestNilWithdrawals(t *testing.T) { Random: test.blockParams.Random, }).Id() execData, err := api.GetPayloadV2(payloadID) + if err != nil { t.Fatalf("error getting payload, err=%v", err) } + if status, err := api.NewPayloadV2(*execData.ExecutionPayload); err != nil { t.Fatalf("error validating payload: %v", err) } else if status.Status != engine.VALID { @@ -1304,9 +1415,12 @@ func setupBodies(t *testing.T) (*node.Node, *eth.Ethereum, []*types.Block) { withdrawals := make([][]*types.Withdrawal, 10) withdrawals[0] = nil // should be filtered out by miner withdrawals[1] = make([]*types.Withdrawal, 0) + for i := 2; i < len(withdrawals); i++ { addr := make([]byte, 20) + crand.Read(addr) + withdrawals[i] = []*types.Withdrawal{ {Index: rand.Uint64(), Validator: rand.Uint64(), Amount: rand.Uint64(), Address: common.BytesToAddress(addr)}, } @@ -1314,9 +1428,11 @@ func setupBodies(t *testing.T) (*node.Node, *eth.Ethereum, []*types.Block) { postShanghaiHeaders := setupBlocks(t, ethservice, 10, parent, callback, withdrawals) postShanghaiBlocks := make([]*types.Block, len(postShanghaiHeaders)) + for i, header := range postShanghaiHeaders { postShanghaiBlocks[i] = ethservice.BlockChain().GetBlock(header.Hash(), header.Number.Uint64()) } + return n, ethservice, append(blocks, postShanghaiBlocks...) } @@ -1325,6 +1441,7 @@ func allHashes(blocks []*types.Block) []common.Hash { for _, b := range blocks { hashes = append(hashes, b.Hash()) } + return hashes } func allBodies(blocks []*types.Block) []*types.Body { @@ -1332,6 +1449,7 @@ func allBodies(blocks []*types.Block) []*types.Body { for _, b := range blocks { bodies = append(bodies, b.Body()) } + return bodies } @@ -1340,6 +1458,7 @@ func TestGetBlockBodiesByHash(t *testing.T) { node, eth, blocks := setupBodies(t) api := NewConsensusAPI(eth) + defer node.Close() tests := []struct { @@ -1398,6 +1517,7 @@ func TestGetBlockBodiesByRange(t *testing.T) { node, eth, blocks := setupBodies(t) api := NewConsensusAPI(eth) + defer node.Close() tests := []struct { @@ -1464,6 +1584,7 @@ func TestGetBlockBodiesByRange(t *testing.T) { if err != nil { t.Fatal(err) } + if len(result) == len(test.results) { for i, r := range result { if !equalBody(test.results[i], r) { @@ -1481,7 +1602,9 @@ func TestGetBlockBodiesByRangeInvalidParams(t *testing.T) { node, eth, _ := setupBodies(t) api := NewConsensusAPI(eth) + defer node.Close() + tests := []struct { start hexutil.Uint64 count hexutil.Uint64 @@ -1512,11 +1635,13 @@ func TestGetBlockBodiesByRangeInvalidParams(t *testing.T) { want: engine.TooLargeRequest, }, } + for i, tc := range tests { result, err := api.GetPayloadBodiesByRangeV1(tc.start, tc.count) if err == nil { t.Fatalf("test %d: expected error, got %v", i, result) } + if have, want := err.Error(), tc.want.Error(); have != want { t.Fatalf("test %d: have %s, want %s", i, have, want) } @@ -1529,14 +1654,17 @@ func equalBody(a *types.Body, b *engine.ExecutionPayloadBodyV1) bool { } else if a == nil || b == nil { return false } + if len(a.Transactions) != len(b.TransactionData) { return false } + for i, tx := range a.Transactions { data, _ := tx.MarshalBinary() if !bytes.Equal(data, b.TransactionData[i]) { return false } } + return reflect.DeepEqual(a.Withdrawals, b.Withdrawals) } diff --git a/eth/catalyst/queue.go b/eth/catalyst/queue.go index e8037aacad..07f20b4cc3 100644 --- a/eth/catalyst/queue.go +++ b/eth/catalyst/queue.go @@ -97,10 +97,12 @@ func (q *payloadQueue) has(id engine.PayloadID) bool { if item == nil { return false } + if item.id == id { return true } } + return false } diff --git a/eth/catalyst/tester.go b/eth/catalyst/tester.go index c4eafd30d9..55f0ba6ba4 100644 --- a/eth/catalyst/tester.go +++ b/eth/catalyst/tester.go @@ -47,12 +47,14 @@ func RegisterFullSyncTester(stack *node.Node, backend *eth.Ethereum, block *type closed: make(chan struct{}), } stack.RegisterLifecycle(cl) + return cl, nil } // Start launches the beacon sync with provided sync target. func (tester *FullSyncTester) Start() error { tester.wg.Add(1) + go func() { defer tester.wg.Done() @@ -85,6 +87,7 @@ func (tester *FullSyncTester) Start() error { } } }() + return nil } @@ -93,5 +96,6 @@ func (tester *FullSyncTester) Start() error { func (tester *FullSyncTester) Stop() error { close(tester.closed) tester.wg.Wait() + return nil } diff --git a/eth/downloader/beaconsync.go b/eth/downloader/beaconsync.go index df8af68bc7..6c01733b32 100644 --- a/eth/downloader/beaconsync.go +++ b/eth/downloader/beaconsync.go @@ -271,6 +271,7 @@ func (d *Downloader) findBeaconAncestor() (uint64, error) { // until sync errors or is finished. func (d *Downloader) fetchBeaconHeaders(from uint64) error { var head *types.Header + _, tail, _, err := d.skeleton.Bounds() if err != nil { return err @@ -280,11 +281,13 @@ func (d *Downloader) fetchBeaconHeaders(from uint64) error { // and it should only happen when there are less than 64 post-merge // blocks in the network. var localHeaders []*types.Header + if from < tail.Number.Uint64() { count := tail.Number.Uint64() - from if count > uint64(fsMinFullBlocks) { return fmt.Errorf("invalid origin (%d) of beacon sync (%d)", from, tail.Number) } + localHeaders = d.readHeaderRange(tail, int(count)) log.Warn("Retrieved beacon headers from local", "from", from, "count", count) } @@ -305,6 +308,7 @@ func (d *Downloader) fetchBeaconHeaders(from uint64) error { number := head.Number.Uint64() - uint64(fsMinFullBlocks) log.Warn("Pivot seemingly stale, moving", "old", d.pivotHeader.Number, "new", number) + if d.pivotHeader = d.skeleton.Header(number); d.pivotHeader == nil { if number < tail.Number.Uint64() { dist := tail.Number.Uint64() - number @@ -320,6 +324,7 @@ func (d *Downloader) fetchBeaconHeaders(from uint64) error { if d.pivotHeader == nil { log.Error("Pivot header is not found", "number", number) d.pivotLock.Unlock() + return errNoPivotHeader } // Write out the pivot into the database so a rollback beyond @@ -350,6 +355,7 @@ func (d *Downloader) fetchBeaconHeaders(from uint64) error { if header == nil { return fmt.Errorf("missing beacon header %d", from) } + headers = append(headers, header) hashes = append(hashes, headers[i].Hash()) from++ diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 9b6aac597e..1e926ddac1 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -371,6 +371,7 @@ func (d *Downloader) LegacySync(id string, head common.Hash, td, ttd *big.Int, m if errors.Is(err, ErrMergeTransition) { return err // This is an expected fault, don't keep printing it in a spin-loop } + log.Warn("Synchronisation failed, retrying", "err", err) return err } @@ -524,9 +525,9 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td, ttd * } } } - // If no pivot block was returned, the head is below the min full block - // threshold (i.e. new chain). In that case we won't really fast sync - // anyway, but still need a valid pivot block to avoid some code hitting + // If no pivot block was returned, the head is below the min full block + // threshold (i.e. new chain). In that case we won't really fast sync + // anyway, but still need a valid pivot block to avoid some code hitting // nil panics on access. if mode == SnapSync && pivot == nil { pivot = d.blockchain.CurrentBlock() @@ -568,6 +569,7 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td, ttd * rawdb.WriteLastPivotNumber(d.stateDB, pivotNumber) } } + d.committed.Store(true) if mode == SnapSync && pivot.Number.Uint64() != 0 { d.committed.Store(false) @@ -1677,6 +1679,7 @@ func (d *Downloader) processSnapSyncContent() error { if d.chainInsertHook != nil { d.chainInsertHook(results) } + d.reportSnapSyncProgress(false) // If we haven't downloaded the pivot block yet, check pivot staleness @@ -1821,6 +1824,7 @@ func (d *Downloader) commitPivotBlock(result *fetchResult) error { if err := d.blockchain.SnapSyncCommitHead(block.Hash()); err != nil { return err } + d.committed.Store(true) return nil } @@ -1859,17 +1863,22 @@ func (d *Downloader) readHeaderRange(last *types.Header, count int) []*types.Hea current = last headers []*types.Header ) + for { parent := d.lightchain.GetHeaderByHash(current.ParentHash) if parent == nil { break // The chain is not continuous, or the chain is exhausted } + headers = append(headers, parent) + if len(headers) >= count { break } + current = parent } + return headers } @@ -1889,15 +1898,20 @@ func (d *Downloader) reportSnapSyncProgress(force bool) { bodyBytes, _ = d.stateDB.AncientSize(rawdb.ChainFreezerBodiesTable) receiptBytes, _ = d.stateDB.AncientSize(rawdb.ChainFreezerReceiptTable) ) + syncedBytes := common.StorageSize(headerBytes + bodyBytes + receiptBytes) + if syncedBytes == 0 { return } + var ( header = d.blockchain.CurrentHeader() block = d.blockchain.CurrentSnapBlock() ) + syncedBlocks := block.Number.Uint64() - d.syncStartBlock + if syncedBlocks == 0 { return } @@ -1907,12 +1921,14 @@ func (d *Downloader) reportSnapSyncProgress(force bool) { // We're going to cheat for non-merged networks, but that's fine latest = d.pivotHeader } + if latest == nil { // This should really never happen, but add some defensive code for now. // TODO(karalabe): Remove it eventually if we don't see it blow. log.Error("Nil latest block in sync progress report") return } + var ( left = latest.Number.Uint64() - block.Number.Uint64() eta = time.Since(d.syncStartTime) / time.Duration(syncedBlocks) * time.Duration(left) @@ -1922,6 +1938,8 @@ func (d *Downloader) reportSnapSyncProgress(force bool) { bodies = fmt.Sprintf("%v@%v", log.FormatLogfmtUint64(block.Number.Uint64()), common.StorageSize(bodyBytes).TerminalString()) receipts = fmt.Sprintf("%v@%v", log.FormatLogfmtUint64(block.Number.Uint64()), common.StorageSize(receiptBytes).TerminalString()) ) + log.Info("Syncing: chain download in progress", "synced", progress, "chain", syncedBytes, "headers", headers, "bodies", bodies, "receipts", receipts, "eta", common.PrettyDuration(eta)) + d.syncLogTime = time.Now() } diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index b9eedde5b6..128bedcce9 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -69,9 +69,11 @@ func newTesterWithNotification(t *testing.T, success func()) *downloadTester { if err != nil { panic(err) } + t.Cleanup(func() { db.Close() }) + gspec := &core.Genesis{ Config: params.TestChainConfig, Alloc: core.GenesisAlloc{testAddress: {Balance: big.NewInt(1000000000000000)}}, @@ -440,9 +442,11 @@ func assertOwnChain(t *testing.T, tester *downloadTester, length int) { if hs := int(tester.chain.CurrentHeader().Number.Uint64()) + 1; hs != headers { t.Fatalf("synchronised headers mismatch: have %v, want %v", hs, headers) } + if bs := int(tester.chain.CurrentBlock().Number.Uint64()) + 1; bs != blocks { t.Fatalf("synchronised blocks mismatch: have %v, want %v", bs, blocks) } + if rs := int(tester.chain.CurrentSnapBlock().Number.Uint64()) + 1; rs != receipts { t.Fatalf("synchronised receipts mismatch: have %v, want %v", rs, receipts) } @@ -517,6 +521,7 @@ func testThrottling(t *testing.T, protocol uint, mode SyncMode) { // Wrap the importer to allow stepping var blocked atomic.Uint32 + proceed := make(chan struct{}) tester.downloader.chainInsertHook = func(results []*fetchResult) { blocked.Store(uint32(len(results))) @@ -984,9 +989,11 @@ func testEmptyShortCircuit(t *testing.T, protocol uint, mode SyncMode) { receiptsNeeded++ } } + if int(bodiesHave.Load()) != bodiesNeeded { t.Errorf("body retrieval count mismatch: have %v, want %v", bodiesHave.Load(), bodiesNeeded) } + if int(receiptsHave.Load()) != receiptsNeeded { t.Errorf("receipt retrieval count mismatch: have %v, want %v", receiptsHave.Load(), receiptsNeeded) } @@ -1790,6 +1797,7 @@ func testBeaconSync(t *testing.T, protocol uint, mode SyncMode) { {name: "Beacon sync with long local chain", local: blockCacheMaxItems - 15 - fsMinFullBlocks/2}, {name: "Beacon sync with full local chain", local: blockCacheMaxItems - 15 - 1}, } + for _, c := range cases { t.Run(c.name, func(t *testing.T) { t.Parallel() diff --git a/eth/downloader/fetchers_concurrent.go b/eth/downloader/fetchers_concurrent.go index 649aa27615..6c94cad838 100644 --- a/eth/downloader/fetchers_concurrent.go +++ b/eth/downloader/fetchers_concurrent.go @@ -286,6 +286,7 @@ func (d *Downloader) concurrentFetch(queue typedQueue, beaconMode bool) error { _, exp := timeouts.Peek() timeout.Reset(time.Until(time.Unix(0, -exp))) } + delete(ordering, req) // New timeout potentially set if there are more requests pending, diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index e9907297a0..c39a20851c 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -436,6 +436,7 @@ func (q *queue) ReserveHeaders(p *peerConnection, count int) *fetchRequest { continue } } + send = from } // Merge all the skipped batches back @@ -783,6 +784,7 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListH if uncleListHashes[index] != header.UncleHash { return errInvalidBody } + if header.WithdrawalsHash == nil { // nil hash means that withdrawals should not be present in body if withdrawalLists[index] != nil { diff --git a/eth/downloader/queue_test.go b/eth/downloader/queue_test.go index e03067bbd4..8b1ada199a 100644 --- a/eth/downloader/queue_test.go +++ b/eth/downloader/queue_test.go @@ -345,6 +345,7 @@ func XTestDelivery(t *testing.T) { uncleHashes[i] = types.CalcUncleHash(uncles) } time.Sleep(100 * time.Millisecond) + _, err := q.DeliverBodies(peer.id, txset, txsHashes, uncleset, uncleHashes, nil, nil) if err != nil { fmt.Printf("delivered %d bodies %v\n", len(txset), err) diff --git a/eth/downloader/skeleton.go b/eth/downloader/skeleton.go index 12eb5700f8..cc6c299286 100644 --- a/eth/downloader/skeleton.go +++ b/eth/downloader/skeleton.go @@ -366,6 +366,7 @@ func (s *skeleton) sync(head *types.Header) (*types.Header, error) { if linked { s.filler.resume() } + defer func() { if filled := s.filler.suspend(); filled != nil { // If something was filled, try to delete stale sync helpers. If @@ -1013,14 +1014,16 @@ func (s *skeleton) processResponse(res *headerResponse) (linked bool, merged boo context = append(context, fmt.Sprintf("stale_next_%d", i+1)) context = append(context, s.progress.Subchains[i+1].Next) } + log.Error("Cleaning spurious beacon sync leftovers", context...) - s.progress.Subchains = s.progress.Subchains[:1] // Note, here we didn't actually delete the headers at all, // just the metadata. We could implement a cleanup mechanism, // but further modifying corrupted state is kind of asking // for it. Unless there's a good enough reason to risk it, // better to live with the small database junk. + + s.progress.Subchains = s.progress.Subchains[:1] } } break @@ -1116,6 +1119,7 @@ func (s *skeleton) cleanStales(filled *types.Header) error { end = number // delete until the requested threshold batch = s.db.NewBatch() ) + s.progress.Subchains[0].Tail = number s.progress.Subchains[0].Next = filled.ParentHash @@ -1132,6 +1136,7 @@ func (s *skeleton) cleanStales(filled *types.Header) error { } // Execute the trimming and the potential rewiring of the progress s.saveSyncStatus(batch) + for n := start; n < end; n++ { // If the batch grew too big, flush it and continue with a new batch. // The catch is that the sync metadata needs to reflect the actually @@ -1148,17 +1153,21 @@ func (s *skeleton) cleanStales(filled *types.Header) error { if err := batch.Write(); err != nil { log.Crit("Failed to write beacon trim data", "err", err) } + batch.Reset() s.progress.Subchains[0].Tail = tmpTail s.progress.Subchains[0].Next = tmpNext s.saveSyncStatus(batch) } + rawdb.DeleteSkeletonHeader(batch, n) } + if err := batch.Write(); err != nil { log.Crit("Failed to write beacon trim data", "err", err) } + return nil } @@ -1187,19 +1196,23 @@ func (s *skeleton) Bounds() (head *types.Header, tail *types.Header, final *type return nil, nil, nil, err } head = rawdb.ReadSkeletonHeader(s.db, progress.Subchains[0].Head) + if head == nil { return nil, nil, nil, fmt.Errorf("head skeleton header %d is missing", progress.Subchains[0].Head) } tail = rawdb.ReadSkeletonHeader(s.db, progress.Subchains[0].Tail) + if tail == nil { return nil, nil, nil, fmt.Errorf("tail skeleton header %d is missing", progress.Subchains[0].Tail) } + if progress.Finalized != nil && tail.Number.Uint64() <= *progress.Finalized && *progress.Finalized <= head.Number.Uint64() { final = rawdb.ReadSkeletonHeader(s.db, *progress.Finalized) if final == nil { return nil, nil, nil, fmt.Errorf("finalized skeleton header %d is missing", *progress.Finalized) } } + return head, tail, final, nil } diff --git a/eth/downloader/skeleton_test.go b/eth/downloader/skeleton_test.go index 6a76d78ac8..6b82bc8e4b 100644 --- a/eth/downloader/skeleton_test.go +++ b/eth/downloader/skeleton_test.go @@ -58,6 +58,7 @@ func (hf *hookedBackfiller) suspend() *types.Header { if hf.suspendHook != nil { return hf.suspendHook() } + return nil // we don't really care about header cleanups for now } @@ -161,6 +162,7 @@ func (p *skeletonTestPeer) RequestHeadersByNumber(origin uint64, amount int, ski } } } + p.served.Add(uint64(len(headers))) hashes := make([]common.Hash, len(headers)) @@ -487,6 +489,7 @@ func TestSkeletonSyncExtend(t *testing.T) { skeleton.Sync(tt.head, nil, true) <-wait + if err := skeleton.Sync(tt.extend, nil, false); err != tt.err { t.Errorf("test %d: extension failure mismatch: have %v, want %v", i, err, tt.err) } @@ -530,6 +533,7 @@ func TestSkeletonSyncRetrievals(t *testing.T) { for i := 0; i < len(chain)/2; i++ { // Fork at block #5000 sidechain = append(sidechain, chain[i]) } + for i := len(chain) / 2; i < len(chain); i++ { sidechain = append(sidechain, &types.Header{ ParentHash: sidechain[i-1].Hash(), @@ -824,6 +828,7 @@ func TestSkeletonSyncRetrievals(t *testing.T) { } // Create a backfiller if we need to run more advanced tests filler := newHookedBackfiller() + if tt.fill { var filled *types.Header @@ -892,18 +897,23 @@ func TestSkeletonSyncRetrievals(t *testing.T) { t.Error(err) continue } + if !tt.unpredictable { var served uint64 for _, peer := range tt.peers { served += peer.served.Load() } + if served != tt.midserve { t.Errorf("test %d, mid state: served headers mismatch: have %d, want %d", i, served, tt.midserve) } + var drops uint64 + for _, peer := range tt.peers { drops += peer.dropped.Load() } + if drops != tt.middrop { t.Errorf("test %d, mid state: dropped peers mismatch: have %d, want %d", i, drops, tt.middrop) } @@ -934,6 +944,7 @@ func TestSkeletonSyncRetrievals(t *testing.T) { return nil } waitStart = time.Now() + for waitTime := 20 * time.Millisecond; time.Since(waitStart) < 2*time.Second; waitTime = waitTime * 2 { time.Sleep(waitTime) // Check the post-init end state if it matches the required results @@ -952,19 +963,25 @@ func TestSkeletonSyncRetrievals(t *testing.T) { for _, peer := range tt.peers { served += peer.served.Load() } + if tt.newPeer != nil { served += tt.newPeer.served.Load() } + if served != tt.endserve { t.Errorf("test %d, end state: served headers mismatch: have %d, want %d", i, served, tt.endserve) } + drops := uint64(0) + for _, peer := range tt.peers { drops += peer.dropped.Load() } + if tt.newPeer != nil { drops += tt.newPeer.dropped.Load() } + if drops != tt.enddrop { t.Errorf("test %d, end state: dropped peers mismatch: have %d, want %d", i, drops, tt.middrop) } diff --git a/eth/handler.go b/eth/handler.go index 9eb3518cb2..c40e3afe48 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -163,7 +163,10 @@ func newHandler(config *handlerConfig) (*handler, error) { // time. But we don't have any recent state for full sync. // In these cases however it's safe to reenable snap sync. fullBlock, snapBlock := h.chain.CurrentBlock(), h.chain.CurrentSnapBlock() + if fullBlock.Number.Uint64() == 0 && snapBlock.Number.Uint64() > 0 { + log.Warn("Preventing switching sync mode from full sync to snap sync") + // Note: Ideally this should never happen with bor, but to be extra // preventive we won't allow it to roll over to snap sync until // we have it working @@ -171,8 +174,6 @@ func newHandler(config *handlerConfig) (*handler, error) { // TODO(snap): Uncomment when we have snap sync working // h.snapSync = uint32(1) // log.Warn("Switch sync mode from full sync to snap sync") - - log.Warn("Preventing switching sync mode from full sync to snap sync") } } else { if h.chain.CurrentBlock().Number.Uint64() > 0 { @@ -348,6 +349,7 @@ func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error { number = head.Number.Uint64() td = h.chain.GetTd(hash, number) ) + forkID := forkid.NewID(h.chain.Config(), genesis.Hash(), number, head.Time) if err := peer.Handshake(h.networkID, td, hash, genesis.Hash(), forkID, h.forkFilter); err != nil { peer.Log().Debug("Ethereum handshake failed", "err", err) @@ -464,6 +466,7 @@ func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error { if err != nil { return err } + go func(number uint64, hash common.Hash, req *eth.Request) { // Ensure the request gets cancelled in case of error/drop defer req.Close() diff --git a/eth/handler_eth_test.go b/eth/handler_eth_test.go index 2e7ba1183a..c1bdeb8ddb 100644 --- a/eth/handler_eth_test.go +++ b/eth/handler_eth_test.go @@ -697,6 +697,7 @@ func testBroadcastBlock(t *testing.T, peers, bcasts int) { } // Initiate a block propagation across the peers time.Sleep(100 * time.Millisecond) + header := source.chain.CurrentBlock() source.handler.BroadcastBlock(source.chain.GetBlock(header.Hash(), header.Number.Uint64()), true) @@ -784,8 +785,10 @@ func testBroadcastMalformedBlock(t *testing.T, protocol uint) { malformedUncles := head malformedUncles.UncleHash[0]++ + malformedTransactions := head malformedTransactions.TxHash[0]++ + malformedEverything := head malformedEverything.UncleHash[0]++ malformedEverything.TxHash[0]++ diff --git a/eth/protocols/eth/handler.go b/eth/protocols/eth/handler.go index 2f2dd1cf6a..55dc77fab8 100644 --- a/eth/protocols/eth/handler.go +++ b/eth/protocols/eth/handler.go @@ -230,6 +230,7 @@ func handleMessage(backend Backend, peer *Peer) error { if peer.Version() == ETH67 { handlers = eth67 } + if peer.Version() >= ETH68 { handlers = eth68 } diff --git a/eth/protocols/eth/handler_test.go b/eth/protocols/eth/handler_test.go index cb26327b7c..d1453fbdef 100644 --- a/eth/protocols/eth/handler_test.go +++ b/eth/protocols/eth/handler_test.go @@ -111,9 +111,11 @@ func newTestBackendWithGenerator(blocks int, shanghai bool, generator func(int, if _, err := chain.InsertChain(bs); err != nil { panic(err) } + for _, block := range bs { chain.StateCache().TrieDB().Commit(block.Root(), false) } + txconfig := txpool.DefaultConfig txconfig.Journal = "" // Don't litter the disk with test journals @@ -430,6 +432,7 @@ func testGetBlockBodies(t *testing.T, protocol uint) { hashes = append(hashes, hash) if tt.available[j] && len(bodies) < tt.expected { block := backend.chain.GetBlockByHash(hash) + bodies = append(bodies, &BlockBody{Transactions: block.Transactions(), Uncles: block.Uncles(), Withdrawals: block.Withdrawals()}) } } @@ -524,6 +527,7 @@ func testGetNodeData(t *testing.T, protocol uint, drop bool) { GetNodeDataPacket: hashes, }) msg, err := peer.app.ReadMsg() + if !drop { if err != nil { t.Fatalf("failed to read node data response: %v", err) @@ -558,6 +562,7 @@ func testGetNodeData(t *testing.T, protocol uint, drop bool) { // Sanity check whether all state matches. accounts := []common.Address{testAddr, acc1Addr, acc2Addr} + for i := uint64(0); i <= backend.chain.CurrentBlock().Number.Uint64(); i++ { root := backend.chain.GetBlockByNumber(i).Root() reconstructed, _ := state.New(root, state.NewDatabase(reconstructDB), nil) @@ -640,6 +645,7 @@ func testGetBlockReceipts(t *testing.T, protocol uint) { hashes []common.Hash receipts [][]*types.Receipt ) + for i := uint64(0); i <= backend.chain.CurrentBlock().Number.Uint64(); i++ { block := backend.chain.GetBlockByNumber(i) diff --git a/eth/protocols/eth/handlers.go b/eth/protocols/eth/handlers.go index 74e514b863..c15ec8bd47 100644 --- a/eth/protocols/eth/handlers.go +++ b/eth/protocols/eth/handlers.go @@ -387,10 +387,12 @@ func handleBlockBodies66(backend Backend, msg Decoder, peer *Peer) error { for i, body := range res.BlockBodiesPacket { txsHashes[i] = types.DeriveSha(types.Transactions(body.Transactions), hasher) uncleHashes[i] = types.CalcUncleHash(body.Uncles) + if body.Withdrawals != nil { withdrawalHashes[i] = types.DeriveSha(types.Withdrawals(body.Withdrawals), hasher) } } + return [][]common.Hash{txsHashes, uncleHashes, withdrawalHashes} } return peer.dispatchResponse(&Response{ @@ -440,6 +442,7 @@ func handleNewPooledTransactionHashes66(backend Backend, msg Decoder, peer *Peer if !backend.AcceptTxs() { return nil } + ann := new(NewPooledTransactionHashesPacket66) if err := msg.Decode(ann); err != nil { return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) @@ -457,10 +460,13 @@ func handleNewPooledTransactionHashes68(backend Backend, msg Decoder, peer *Peer if !backend.AcceptTxs() { return nil } + ann := new(NewPooledTransactionHashesPacket68) + if err := msg.Decode(ann); err != nil { return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) } + if len(ann.Hashes) != len(ann.Types) || len(ann.Hashes) != len(ann.Sizes) { return fmt.Errorf("%w: message %v: invalid len of fields: %v %v %v", errDecode, msg, len(ann.Hashes), len(ann.Types), len(ann.Sizes)) } @@ -468,6 +474,7 @@ func handleNewPooledTransactionHashes68(backend Backend, msg Decoder, peer *Peer for _, hash := range ann.Hashes { peer.markTransaction(hash) } + return backend.Handle(peer, ann) } diff --git a/eth/protocols/eth/protocol.go b/eth/protocols/eth/protocol.go index 0d4b368988..68937e7080 100644 --- a/eth/protocols/eth/protocol.go +++ b/eth/protocols/eth/protocol.go @@ -254,6 +254,7 @@ func (p *BlockBodiesPacket) Unpack() ([][]*types.Transaction, [][]*types.Header, for i, body := range *p { txset[i], uncleset[i], withdrawalset[i] = body.Transactions, body.Uncles, body.Withdrawals } + return txset, uncleset, withdrawalset } diff --git a/eth/protocols/snap/sync_test.go b/eth/protocols/snap/sync_test.go index 6a3b482d5d..22902c764b 100644 --- a/eth/protocols/snap/sync_test.go +++ b/eth/protocols/snap/sync_test.go @@ -1510,6 +1510,7 @@ func makeAccountTrieWithStorageWithUniqueStorage(accounts, slots int, code bool) trie, _ := trie.New(id, db) storageTries[common.BytesToHash(key)] = trie } + return db.Scheme(), accTrie, entries, storageTries, storageEntries } @@ -1537,6 +1538,7 @@ func makeAccountTrieWithStorage(accounts, slots int, code, boundary bool) (strin stNodes *trie.NodeSet stEntries entrySlice ) + if boundary { stRoot, stNodes, stEntries = makeBoundaryStorageTrie(common.BytesToHash(key), slots, db) } else { @@ -1572,15 +1574,19 @@ func makeAccountTrieWithStorage(accounts, slots int, code, boundary bool) (strin if err != nil { panic(err) } + for i := uint64(1); i <= uint64(accounts); i++ { key := key32(i) id := trie.StorageTrieID(root, common.BytesToHash(key), storageRoots[common.BytesToHash(key)]) + trie, err := trie.New(id, db) if err != nil { panic(err) } + storageTries[common.BytesToHash(key)] = trie } + return db.Scheme(), accTrie, entries, storageTries, storageEntries } @@ -1603,7 +1609,9 @@ func makeStorageTrieWithSeed(owner common.Hash, n, seed uint64, db *trie.Databas entries = append(entries, elem) } sort.Sort(entries) + root, nodes := trie.Commit(false) + return root, nodes, entries } @@ -1654,12 +1662,15 @@ func makeBoundaryStorageTrie(owner common.Hash, n int, db *trie.Database) (commo entries = append(entries, elem) } sort.Sort(entries) + root, nodes := trie.Commit(false) + return root, nodes, entries } func verifyTrie(db ethdb.KeyValueStore, root common.Hash, t *testing.T) { t.Helper() + triedb := trie.NewDatabase(rawdb.NewDatabase(db)) accTrie, err := trie.New(trie.StateTrieID(root), triedb) if err != nil { @@ -1678,6 +1689,7 @@ func verifyTrie(db ethdb.KeyValueStore, root common.Hash, t *testing.T) { log.Crit("Invalid account encountered during snapshot creation", "err", err) } accounts++ + if acc.Root != types.EmptyRootHash { id := trie.StorageTrieID(root, common.BytesToHash(accIt.Key), acc.Root) storeTrie, err := trie.NewStateTrie(id, triedb) @@ -1716,6 +1728,7 @@ func TestSyncAccountPerformance(t *testing.T) { }) } ) + nodeScheme, sourceAccountTrie, elems := makeAccountTrieNoStorage(100) mkSource := func(name string) *testPeer { diff --git a/eth/state_accessor.go b/eth/state_accessor.go index c4db017607..99701faaeb 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -72,6 +72,7 @@ func (eth *Ethereum) StateAtBlock(ctx context.Context, block *types.Block, reexe // function to deref it. if statedb, err = eth.blockchain.StateAt(block.Root()); err == nil { statedb.Database().TrieDB().Reference(block.Root(), common.Hash{}) + return statedb, func() { statedb.Database().TrieDB().Dereference(block.Root()) }, nil @@ -188,6 +189,7 @@ func (eth *Ethereum) StateAtBlock(ctx context.Context, block *types.Block, reexe nodes, imgs := database.TrieDB().Size() log.Info("Historical state regenerated", "block", current.NumberU64(), "elapsed", time.Since(start), "nodes", nodes, "preimages", imgs) } + return statedb, func() { database.TrieDB().Dereference(block.Root()) }, nil } @@ -224,6 +226,7 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, // Not yet the searched for transaction, execute on top of the current state vmenv := vm.NewEVM(context, txContext, statedb, eth.blockchain.Config(), vm.Config{}) statedb.SetTxContext(tx.Hash(), idx) + // nolint : contextcheck if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), nil); err != nil { return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) } @@ -231,5 +234,6 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) } + return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) } diff --git a/eth/sync.go b/eth/sync.go index 957690d0ce..db73d60dad 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -286,6 +286,7 @@ func (h *handler) doSync(op *chainSyncOp) error { atomic.StoreUint32(&h.acceptTxs, 1) } } + if head.Number.Uint64() > 0 { // We've completed a sync cycle, notify all peers of new state. This path is // essential in star-topology networks where a gateway node needs to notify diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 193060b95e..fe9418246d 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -288,15 +288,18 @@ func (api *API) TraceChain(ctx context.Context, start, end rpc.BlockNumber, conf if !supported { return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported } + sub := notifier.CreateSubscription() // nolint : contextcheck resCh := api.traceChain(from, to, config, notifier.Closed()) + go func() { for result := range resCh { notifier.Notify(sub.ID, result) } }() + return sub, nil } @@ -350,6 +353,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed txs = txs[:len(txs)-1] stateSyncPresent = false } + for i, tx := range task.block.Transactions() { msg, _ := core.TransactionToMessage(tx, signer, task.block.BaseFee()) txctx := &Context{ @@ -421,6 +425,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed default: log.Info("Chain tracing finished", "start", start.NumberU64(), "end", end.NumberU64(), "transactions", traced, "elapsed", time.Since(begin)) } + close(resCh) }() // Feed all the blocks both into the tracer, as well as fast process concurrently @@ -442,6 +447,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed failed = err break } + next, err := api.blockByNumber(ctx, rpc.BlockNumber(number+1)) if err != nil { failed = err @@ -460,10 +466,12 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed // limit, the trie database will be reconstructed from scratch only // if the relevant state is available in disk. var preferDisk bool + if statedb != nil { s1, s2 := statedb.Database().TrieDB().Size() preferDisk = s1+s2 > defaultTracechainMemLimit } + statedb, release, err = api.backend.StateAtBlock(ctx, block, reexec, statedb, false, preferDisk) if err != nil { failed = err @@ -495,6 +503,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed next = start.NumberU64() + 1 done = make(map[uint64]*blockTraceResult) ) + for res := range resCh { // Queue up next received result result := &blockTraceResult{ @@ -518,6 +527,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed } } }() + return retCh } @@ -636,10 +646,12 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config if config != nil && config.Reexec != nil { reexec = *config.Reexec } + statedb, release, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false) if err != nil { return nil, err } + defer release() var ( @@ -660,6 +672,7 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config txContext = core.NewEVMTxContext(msg) vmenv = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vm.Config{}) ) + statedb.SetTxContext(tx.Hash(), i) //nolint: nestif if stateSyncPresent && i == len(txs)-1 { @@ -758,6 +771,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac if err != nil { return nil, err } + defer release() // create and add empty mvHashMap in statedb as StateAtBlock does not have mvHashmap in it. @@ -778,6 +792,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac if threads > len(txs) { threads = len(txs) } + jobs := make(chan *txTraceTask, threads) for th := 0; th < threads; th++ { pend.Add(1) @@ -982,10 +997,12 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block if config != nil && config.Reexec != nil { reexec = *config.Reexec } + statedb, release, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false) if err != nil { return nil, err } + defer release() // Retrieve the tracing configurations, or use default values @@ -1040,6 +1057,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block if !canon { prefix = fmt.Sprintf("%valt-", prefix) } + dump, err = os.CreateTemp(os.TempDir(), prefix) if err != nil { return nil, err @@ -1143,10 +1161,12 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config * if err != nil { return nil, err } + msg, vmctx, statedb, release, err := api.backend.StateAtTransaction(ctx, block, int(index), reexec) if err != nil { return nil, err } + defer release() txctx := &Context{ @@ -1190,18 +1210,22 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc if config != nil && config.Reexec != nil { reexec = *config.Reexec } + statedb, release, err := api.backend.StateAtBlock(ctx, block, reexec, nil, true, false) if err != nil { return nil, err } + defer release() vmctx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + // Apply the customization rules if required. if config != nil { if err := config.StateOverrides.Apply(statedb); err != nil { return nil, err } + config.BlockOverrides.Apply(&vmctx) } // Execute the trace @@ -1238,6 +1262,7 @@ func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *Conte timeout = defaultTraceTimeout txContext = core.NewEVMTxContext(message) ) + if config == nil { config = &TraceConfig{} } @@ -1249,6 +1274,7 @@ func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *Conte return nil, err } } + vmenv := vm.NewEVM(vmctx, txContext, statedb, api.backend.ChainConfig(), vm.Config{Tracer: tracer, NoBaseFee: true}) // Define a meaningful timeout of a single transaction trace @@ -1257,15 +1283,19 @@ func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *Conte return nil, err } } + deadlineCtx, cancel := context.WithTimeout(ctx, timeout) + go func() { <-deadlineCtx.Done() + if errors.Is(deadlineCtx.Err(), context.DeadlineExceeded) { tracer.Stop(errors.New("execution timeout")) // Stop evm execution. Note cancellation is not necessarily immediate. vmenv.Cancel() } }() + defer cancel() // Call Prepare to clear out the statedb access list @@ -1287,6 +1317,7 @@ func (api *API) traceTx(ctx context.Context, message *core.Message, txctx *Conte return nil, fmt.Errorf("tracing failed: %w", err) } } + return tracer.GetResult() } @@ -1314,30 +1345,37 @@ func overrideConfig(original *params.ChainConfig, override *params.ChainConfig) chainConfigCopy.BerlinBlock = block canon = false } + if block := override.LondonBlock; block != nil { chainConfigCopy.LondonBlock = block canon = false } + if block := override.ArrowGlacierBlock; block != nil { chainConfigCopy.ArrowGlacierBlock = block canon = false } + if block := override.GrayGlacierBlock; block != nil { chainConfigCopy.GrayGlacierBlock = block canon = false } + if block := override.MergeNetsplitBlock; block != nil { chainConfigCopy.MergeNetsplitBlock = block canon = false } + if timestamp := override.ShanghaiTime; timestamp != nil { chainConfigCopy.ShanghaiTime = timestamp canon = false } + if timestamp := override.CancunTime; timestamp != nil { chainConfigCopy.CancunTime = timestamp canon = false } + if timestamp := override.PragueTime; timestamp != nil { chainConfigCopy.PragueTime = timestamp canon = false diff --git a/eth/tracers/api_bor.go b/eth/tracers/api_bor.go index ffebd2e7d9..6bc2849c64 100644 --- a/eth/tracers/api_bor.go +++ b/eth/tracers/api_bor.go @@ -66,6 +66,7 @@ func (api *API) traceBorBlock(ctx context.Context, block *types.Block, config *T if err != nil { return nil, err } + defer release() // Execute all the transaction contained within the block concurrently diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index e0891e803a..26826349f3 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -149,14 +149,17 @@ func (b *testBackend) StateAtBlock(ctx context.Context, block *types.Block, reex if err != nil { return nil, nil, errStateNotFound } + if b.refHook != nil { b.refHook() } + release := func() { if b.relHook != nil { b.relHook() } } + return statedb, release, nil } @@ -165,6 +168,7 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block if parent == nil { return nil, vm.BlockContext{}, nil, nil, errBlockNotFound } + statedb, release, err := b.StateAtBlock(ctx, parent, reexec, nil, true, false) if err != nil { return nil, vm.BlockContext{}, nil, nil, errStateNotFound @@ -181,6 +185,7 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block if idx == txIndex { return msg, blockContext, statedb, release, nil } + vmenv := vm.NewEVM(blockContext, txContext, statedb, b.chainConfig, vm.Config{}) // nolint : contextcheck if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), context.Background()); err != nil { @@ -188,6 +193,7 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block } statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) } + return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) } @@ -218,6 +224,7 @@ func TestTraceCall(t *testing.T) { tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) b.AddTx(tx) }) + defer backend.teardown() api := NewAPI(backend) var testSuite = []struct { @@ -301,6 +308,7 @@ func TestTraceCall(t *testing.T) { {"pc":1,"op":"STOP","gas":24946982,"gasCost":0,"depth":1,"stack":["0x1337"]}]}`, }, } + for i, testspec := range testSuite { result, err := api.TraceCall(context.Background(), testspec.call, rpc.BlockNumberOrHash{BlockNumber: &testspec.blockNumber}, testspec.config) if testspec.expectErr != nil { @@ -353,16 +361,20 @@ func TestTraceTransaction(t *testing.T) { b.AddTx(tx) target = tx.Hash() }) + defer backend.chain.Stop() api := NewAPI(backend) result, err := api.TraceTransaction(context.Background(), target, nil) if err != nil { t.Errorf("Failed to trace transaction %v", err) } + var have *logger.ExecutionResult + if err := json.Unmarshal(result.(json.RawMessage), &have); err != nil { t.Errorf("failed to unmarshal result %v", err) } + if !reflect.DeepEqual(have, &logger.ExecutionResult{ Gas: params.TxGas, Failed: false, @@ -401,6 +413,7 @@ func TestTraceBlock(t *testing.T) { tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) b.AddTx(tx) }) + defer backend.chain.Stop() api := NewAPI(backend) @@ -569,6 +582,7 @@ func TestTracingWithOverrides(t *testing.T) { tx, _ := types.SignTx(types.NewTransaction(uint64(i), accounts[1].addr, big.NewInt(1000), params.TxGas, b.BaseFee(), nil), signer, accounts[0].key) b.AddTx(tx) }) + defer backend.chain.Stop() api := NewAPI(backend) randomAccounts := newAccounts(3) @@ -930,6 +944,7 @@ func TestTraceChain(t *testing.T) { rel atomic.Uint32 // total rels has made nonce uint64 ) + backend := newTestBackend(t, genBlocks, genesis, func(i int, b *core.BlockGen) { // Transfer from account[0] to account[1] // value: 1000 wei @@ -945,6 +960,7 @@ func TestTraceChain(t *testing.T) { api := NewAPI(backend) single := `{"result":{"gas":21000,"failed":false,"returnValue":"","structLogs":[]}}` + var cases = []struct { start uint64 end uint64 @@ -953,6 +969,7 @@ func TestTraceChain(t *testing.T) { {0, 50, nil}, // the entire chain range, blocks [1, 50] {10, 20, nil}, // the middle chain range, blocks [11, 20] } + for _, c := range cases { ref.Store(0) rel.Store(0) @@ -966,9 +983,11 @@ func TestTraceChain(t *testing.T) { if next != uint64(result.Block) { t.Error("Unexpected tracing block") } + if len(result.Traces) != int(next) { t.Error("Unexpected tracing result") } + for _, trace := range result.Traces { // nolint : errchkjson blob, _ := json.Marshal(trace) @@ -976,8 +995,10 @@ func TestTraceChain(t *testing.T) { t.Error("Unexpected tracing result") } } + next += 1 } + if next != c.end+1 { t.Error("Missing tracing block") } diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index db220e566c..fc127809a8 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -248,9 +248,11 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { if err != nil { b.Fatalf("failed to create call tracer: %v", err) } + evm := vm.NewEVM(blockContext, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer}) snap := statedb.Snapshot() st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) + if _, err = st.TransitionDb(context.Background()); err != nil { b.Fatalf("failed to execute transaction: %v", err) } @@ -281,11 +283,13 @@ func TestInternals(t *testing.T) { GasLimit: uint64(6000000), } ) + mkTracer := func(name string, cfg json.RawMessage) tracers.Tracer { tr, err := tracers.DefaultDirectory.New(name, nil, cfg) if err != nil { t.Fatalf("failed to create call tracer: %v", err) } + return tr } @@ -368,6 +372,7 @@ func TestInternals(t *testing.T) { SkipAccountChecks: false, } st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)) + if _, err := st.TransitionDb(context.Background()); err != nil { t.Fatalf("test %v: failed to execute transaction: %v", tc.name, err) } @@ -376,6 +381,7 @@ func TestInternals(t *testing.T) { if err != nil { t.Fatalf("test %v: failed to retrieve trace result: %v", tc.name, err) } + if string(res) != tc.want { t.Fatalf("test %v: trace mismatch\n have: %v\n want: %v\n", tc.name, string(res), tc.want) } diff --git a/eth/tracers/internal/tracetest/flat_calltrace_test.go b/eth/tracers/internal/tracetest/flat_calltrace_test.go index 5f5a222495..e9c702c517 100644 --- a/eth/tracers/internal/tracetest/flat_calltrace_test.go +++ b/eth/tracers/internal/tracetest/flat_calltrace_test.go @@ -79,7 +79,9 @@ func flatCallTracerTestRunner(tb testing.TB, tracerName string, filename string, if err != nil { return fmt.Errorf("failed to read testcase: %v", err) } + test := new(flatCallTracerTest) + if err := json.Unmarshal(blob, test); err != nil { return fmt.Errorf("failed to parse testcase: %v", err) } @@ -88,6 +90,7 @@ func flatCallTracerTestRunner(tb testing.TB, tracerName string, filename string, if err := rlp.DecodeBytes(common.FromHex(test.Input), tx); err != nil { return fmt.Errorf("failed to parse testcase input: %v", err) } + signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number))) origin, _ := signer.Sender(tx) txContext := vm.TxContext{ @@ -110,12 +113,14 @@ func flatCallTracerTestRunner(tb testing.TB, tracerName string, filename string, if err != nil { return fmt.Errorf("failed to create call tracer: %v", err) } + evm := vm.NewEVM(blockContext, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer}) msg, err := core.TransactionToMessage(tx, signer, nil) if err != nil { return fmt.Errorf("failed to prepare transaction for tracing: %v", err) } + st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if _, err = st.TransitionDb(context.Background()); err != nil { @@ -127,10 +132,13 @@ func flatCallTracerTestRunner(tb testing.TB, tracerName string, filename string, if err != nil { return fmt.Errorf("failed to retrieve trace result: %v", err) } + ret := make([]flatCallTrace, 0) + if err := json.Unmarshal(res, &ret); err != nil { return fmt.Errorf("failed to unmarshal trace result: %v", err) } + if !jsonEqualFlat(ret, test.Result) { tb.Logf("tracer name: %s", tracerName) @@ -148,6 +156,7 @@ func flatCallTracerTestRunner(tb testing.TB, tracerName string, filename string, tb.Fatalf("trace mismatch: \nhave %+v\nwant %+v", ret, test.Result) } + return nil } @@ -165,10 +174,12 @@ func testFlatCallTracer(t *testing.T, tracerName string, dirPath string) { if err != nil { t.Fatalf("failed to retrieve tracer test suite: %v", err) } + for _, file := range files { if !strings.HasSuffix(file.Name(), ".json") { continue } + file := file // capture range variable t.Run(camel(strings.TrimSuffix(file.Name(), ".json")), func(t *testing.T) { t.Parallel() @@ -186,16 +197,19 @@ func testFlatCallTracer(t *testing.T, tracerName string, dirPath string) { func jsonEqualFlat(x, y interface{}) bool { xTrace := new([]flatCallTrace) yTrace := new([]flatCallTrace) + if xj, err := json.Marshal(x); err == nil { json.Unmarshal(xj, xTrace) } else { return false } + if yj, err := json.Marshal(y); err == nil { json.Unmarshal(yj, yTrace) } else { return false } + return reflect.DeepEqual(xTrace, yTrace) } diff --git a/eth/tracers/internal/tracetest/prestate_test.go b/eth/tracers/internal/tracetest/prestate_test.go index 3edc60cfa9..6793dd9588 100644 --- a/eth/tracers/internal/tracetest/prestate_test.go +++ b/eth/tracers/internal/tracetest/prestate_test.go @@ -73,10 +73,12 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { if err != nil { t.Fatalf("failed to retrieve tracer test suite: %v", err) } + for _, file := range files { if !strings.HasSuffix(file.Name(), ".json") { continue } + file := file // capture range variable t.Run(camel(strings.TrimSuffix(file.Name(), ".json")), func(t *testing.T) { t.Parallel() diff --git a/eth/tracers/internal/tracetest/util.go b/eth/tracers/internal/tracetest/util.go index 95d292c924..73a8ec3ffa 100644 --- a/eth/tracers/internal/tracetest/util.go +++ b/eth/tracers/internal/tracetest/util.go @@ -68,5 +68,6 @@ func camel(str string) string { for i := 1; i < len(pieces); i++ { pieces[i] = string(unicode.ToUpper(rune(pieces[i][0]))) + pieces[i][1:] } + return strings.Join(pieces, "") } diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go index 2a2789e93d..461c3ad12e 100644 --- a/eth/tracers/js/goja.go +++ b/eth/tracers/js/goja.go @@ -38,18 +38,23 @@ var assetTracers = make(map[string]string) func init() { var err error assetTracers, err = jsassets.Load() + if err != nil { panic(err) } + type ctorFn = func(*tracers.Context, json.RawMessage) (tracers.Tracer, error) + lookup := func(code string) ctorFn { return func(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { return newJsTracer(code, ctx, cfg) } } + for name, code := range assetTracers { tracers.DefaultDirectory.Register(name, lookup(code), true) } + tracers.DefaultDirectory.RegisterJSEval(newJsTracer) } @@ -73,6 +78,7 @@ func fromBuf(vm *goja.Runtime, bufType goja.Value, buf goja.Value, allowString b if !allowString { break } + return common.FromHex(obj.String()), nil case "Array": @@ -80,15 +86,19 @@ func fromBuf(vm *goja.Runtime, bufType goja.Value, buf goja.Value, allowString b if err := vm.ExportTo(buf, &b); err != nil { return nil, err } + return b, nil case "Object": if !obj.Get("constructor").SameAs(bufType) { break } + b := obj.Get("buffer").Export().(goja.ArrayBuffer).Bytes() + return b, nil } + return nil, fmt.Errorf("invalid buffer type") } @@ -138,13 +148,16 @@ func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracer vm := goja.New() // By default field names are exported to JS as is, i.e. capitalized. vm.SetFieldNameMapper(goja.UncapFieldNameMapper()) + t := &jsTracer{ vm: vm, ctx: make(map[string]goja.Value), } + if ctx == nil { ctx = new(tracers.Context) } + if ctx.BlockHash != (common.Hash{}) { t.ctx["blockHash"] = vm.ToValue(ctx.BlockHash.Bytes()) if ctx.TxHash != (common.Hash{}) { @@ -155,27 +168,35 @@ func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracer t.setTypeConverters() t.setBuiltinFunctions() + ret, err := vm.RunString("(" + code + ")") + if err != nil { return nil, err } // Check tracer's interface for required and optional methods. obj := ret.ToObject(vm) result, ok := goja.AssertFunction(obj.Get("result")) + if !ok { return nil, errors.New("trace object must expose a function result()") } + fault, ok := goja.AssertFunction(obj.Get("fault")) + if !ok { return nil, errors.New("trace object must expose a function fault()") } + step, ok := goja.AssertFunction(obj.Get("step")) t.traceStep = ok enter, hasEnter := goja.AssertFunction(obj.Get("enter")) exit, hasExit := goja.AssertFunction(obj.Get("exit")) + if hasEnter != hasExit { return nil, errors.New("trace object must expose either both or none of enter() and exit()") } + t.traceFrame = hasEnter t.obj = obj t.step = step @@ -190,6 +211,7 @@ func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracer if cfg != nil { cfgStr = string(cfg) } + if _, err := setup(obj, vm.ToValue(cfgStr)); err != nil { return nil, err } @@ -207,6 +229,7 @@ func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracer t.frameValue = t.frame.setupObject() t.frameResultValue = t.frameResult.setupObject() t.logValue = t.log.setupObject() + return t, nil } @@ -227,21 +250,25 @@ func (t *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Addr t.env = env db := &dbObj{db: env.StateDB, vm: t.vm, toBig: t.toBig, toBuf: t.toBuf, fromBuf: t.fromBuf} t.dbValue = db.setupObject() + if create { t.ctx["type"] = t.vm.ToValue("CREATE") } else { t.ctx["type"] = t.vm.ToValue("CALL") } + t.ctx["from"] = t.vm.ToValue(from.Bytes()) t.ctx["to"] = t.vm.ToValue(to.Bytes()) t.ctx["input"] = t.vm.ToValue(input) t.ctx["gas"] = t.vm.ToValue(t.gasLimit) t.ctx["gasPrice"] = t.vm.ToValue(env.TxContext.GasPrice) valueBig, err := t.toBig(t.vm, value.String()) + if err != nil { t.err = err return } + t.ctx["value"] = valueBig t.ctx["block"] = t.vm.ToValue(env.Context.BlockNumber.Uint64()) // Update list of precompiles based on current block @@ -254,6 +281,7 @@ func (t *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope if !t.traceStep { return } + if t.err != nil { return } @@ -269,6 +297,7 @@ func (t *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope log.refund = t.env.StateDB.GetRefund() log.depth = depth log.err = err + if _, err := t.step(t.obj, t.logValue, t.dbValue); err != nil { t.onError("step", err) } @@ -299,6 +328,7 @@ func (t *jsTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Ad if !t.traceFrame { return } + if t.err != nil { return } @@ -309,6 +339,7 @@ func (t *jsTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Ad t.frame.input = common.CopyBytes(input) t.frame.gas = uint(gas) t.frame.value = nil + if value != nil { t.frame.value = new(big.Int).SetBytes(value.Bytes()) } @@ -338,13 +369,17 @@ func (t *jsTracer) CaptureExit(output []byte, gasUsed uint64, err error) { func (t *jsTracer) GetResult() (json.RawMessage, error) { ctx := t.vm.ToValue(t.ctx) res, err := t.result(t.obj, ctx, t.dbValue) + if err != nil { return nil, wrapError("result", err) } + encoded, err := json.Marshal(res) + if err != nil { return nil, err } + return json.RawMessage(encoded), t.err } @@ -493,6 +528,7 @@ func (t *jsTracer) setTypeConverters() error { if !ok { return errors.New("failed to bind bigInt func") } + toBigWrapper := func(vm *goja.Runtime, val string) (goja.Value, error) { return toBigFn(goja.Undefined(), vm.ToValue(val)) } @@ -510,6 +546,7 @@ func (t *jsTracer) setTypeConverters() error { return fromBuf(vm, uint8ArrayType, buf, allowString) } t.fromBuf = fromBufWrapper + return nil } @@ -535,6 +572,7 @@ func (o *opObj) setupObject() *goja.Object { obj.Set("toNumber", o.vm.ToValue(o.ToNumber)) obj.Set("toString", o.vm.ToValue(o.ToString)) obj.Set("isPush", o.vm.ToValue(o.IsPush)) + return obj } @@ -551,11 +589,14 @@ func (mo *memoryObj) Slice(begin, end int64) goja.Value { mo.vm.Interrupt(err) return nil } + res, err := mo.toBuf(mo.vm, b) + if err != nil { mo.vm.Interrupt(err) return nil } + return res } @@ -564,13 +605,17 @@ func (mo *memoryObj) slice(begin, end int64) ([]byte, error) { if end == begin { return []byte{}, nil } + if end < begin || begin < 0 { return nil, fmt.Errorf("tracer accessed out of bound memory: offset %d, end %d", begin, end) } + slice, err := tracers.GetMemoryCopyPadded(mo.memory, begin, end-begin) + if err != nil { return nil, err } + return slice, nil } @@ -580,11 +625,14 @@ func (mo *memoryObj) GetUint(addr int64) goja.Value { mo.vm.Interrupt(err) return nil } + res, err := mo.toBig(mo.vm, value.String()) + if err != nil { mo.vm.Interrupt(err) return nil } + return res } @@ -593,6 +641,7 @@ func (mo *memoryObj) getUint(addr int64) (*big.Int, error) { if mo.memory.Len() < int(addr)+32 || addr < 0 { return nil, fmt.Errorf("tracer accessed out of bound memory: available %d, offset %d, size %d", mo.memory.Len(), addr, 32) } + return new(big.Int).SetBytes(mo.memory.GetPtr(addr, 32)), nil } @@ -605,6 +654,7 @@ func (m *memoryObj) setupObject() *goja.Object { o.Set("slice", m.vm.ToValue(m.Slice)) o.Set("getUint", m.vm.ToValue(m.GetUint)) o.Set("length", m.vm.ToValue(m.Length)) + return o } @@ -620,11 +670,14 @@ func (s *stackObj) Peek(idx int) goja.Value { s.vm.Interrupt(err) return nil } + res, err := s.toBig(s.vm, value.String()) + if err != nil { s.vm.Interrupt(err) return nil } + return res } @@ -633,6 +686,7 @@ func (s *stackObj) peek(idx int) (*big.Int, error) { if len(s.stack.Data()) <= idx || idx < 0 { return nil, fmt.Errorf("tracer accessed out of bound stack: size %d, index %d", len(s.stack.Data()), idx) } + return s.stack.Back(idx).ToBig(), nil } @@ -644,6 +698,7 @@ func (s *stackObj) setupObject() *goja.Object { o := s.vm.NewObject() o.Set("peek", s.vm.ToValue(s.Peek)) o.Set("length", s.vm.ToValue(s.Length)) + return o } @@ -661,13 +716,16 @@ func (do *dbObj) GetBalance(addrSlice goja.Value) goja.Value { do.vm.Interrupt(err) return nil } + addr := common.BytesToAddress(a) value := do.db.GetBalance(addr) res, err := do.toBig(do.vm, value.String()) + if err != nil { do.vm.Interrupt(err) return nil } + return res } @@ -677,7 +735,9 @@ func (do *dbObj) GetNonce(addrSlice goja.Value) uint64 { do.vm.Interrupt(err) return 0 } + addr := common.BytesToAddress(a) + return do.db.GetNonce(addr) } @@ -687,13 +747,16 @@ func (do *dbObj) GetCode(addrSlice goja.Value) goja.Value { do.vm.Interrupt(err) return nil } + addr := common.BytesToAddress(a) code := do.db.GetCode(addr) res, err := do.toBuf(do.vm, code) + if err != nil { do.vm.Interrupt(err) return nil } + return res } @@ -703,19 +766,24 @@ func (do *dbObj) GetState(addrSlice goja.Value, hashSlice goja.Value) goja.Value do.vm.Interrupt(err) return nil } + addr := common.BytesToAddress(a) h, err := do.fromBuf(do.vm, hashSlice, false) + if err != nil { do.vm.Interrupt(err) return nil } + hash := common.BytesToHash(h) state := do.db.GetState(addr, hash).Bytes() res, err := do.toBuf(do.vm, state) + if err != nil { do.vm.Interrupt(err) return nil } + return res } @@ -725,7 +793,9 @@ func (do *dbObj) Exists(addrSlice goja.Value) bool { do.vm.Interrupt(err) return false } + addr := common.BytesToAddress(a) + return do.db.Exist(addr) } @@ -736,6 +806,7 @@ func (do *dbObj) setupObject() *goja.Object { o.Set("getCode", do.vm.ToValue(do.GetCode)) o.Set("getState", do.vm.ToValue(do.GetState)) o.Set("exists", do.vm.ToValue(do.Exists)) + return o } @@ -749,40 +820,48 @@ type contractObj struct { func (co *contractObj) GetCaller() goja.Value { caller := co.contract.Caller().Bytes() res, err := co.toBuf(co.vm, caller) + if err != nil { co.vm.Interrupt(err) return nil } + return res } func (co *contractObj) GetAddress() goja.Value { addr := co.contract.Address().Bytes() res, err := co.toBuf(co.vm, addr) + if err != nil { co.vm.Interrupt(err) return nil } + return res } func (co *contractObj) GetValue() goja.Value { value := co.contract.Value() res, err := co.toBig(co.vm, value.String()) + if err != nil { co.vm.Interrupt(err) return nil } + return res } func (co *contractObj) GetInput() goja.Value { input := common.CopyBytes(co.contract.Input) res, err := co.toBuf(co.vm, input) + if err != nil { co.vm.Interrupt(err) return nil } + return res } @@ -792,6 +871,7 @@ func (c *contractObj) setupObject() *goja.Object { o.Set("getAddress", c.vm.ToValue(c.GetAddress)) o.Set("getValue", c.vm.ToValue(c.GetValue)) o.Set("getInput", c.vm.ToValue(c.GetInput)) + return o } @@ -815,30 +895,36 @@ func (f *callframe) GetType() string { func (f *callframe) GetFrom() goja.Value { from := f.from.Bytes() res, err := f.toBuf(f.vm, from) + if err != nil { f.vm.Interrupt(err) return nil } + return res } func (f *callframe) GetTo() goja.Value { to := f.to.Bytes() res, err := f.toBuf(f.vm, to) + if err != nil { f.vm.Interrupt(err) return nil } + return res } func (f *callframe) GetInput() goja.Value { input := f.input res, err := f.toBuf(f.vm, input) + if err != nil { f.vm.Interrupt(err) return nil } + return res } @@ -850,11 +936,14 @@ func (f *callframe) GetValue() goja.Value { if f.value == nil { return goja.Undefined() } + res, err := f.toBig(f.vm, f.value.String()) + if err != nil { f.vm.Interrupt(err) return nil } + return res } @@ -866,6 +955,7 @@ func (f *callframe) setupObject() *goja.Object { o.Set("getInput", f.vm.ToValue(f.GetInput)) o.Set("getGas", f.vm.ToValue(f.GetGas)) o.Set("getValue", f.vm.ToValue(f.GetValue)) + return o } @@ -888,6 +978,7 @@ func (r *callframeResult) GetOutput() goja.Value { r.vm.Interrupt(err) return nil } + return res } @@ -895,6 +986,7 @@ func (r *callframeResult) GetError() goja.Value { if r.err != nil { return r.vm.ToValue(r.err.Error()) } + return goja.Undefined() } @@ -903,6 +995,7 @@ func (r *callframeResult) setupObject() *goja.Object { o.Set("getGasUsed", r.vm.ToValue(r.GetGasUsed)) o.Set("getOutput", r.vm.ToValue(r.GetOutput)) o.Set("getError", r.vm.ToValue(r.GetError)) + return o } @@ -932,6 +1025,7 @@ func (l *steplog) GetError() goja.Value { if l.err != nil { return l.vm.ToValue(l.err.Error()) } + return goja.Undefined() } @@ -949,5 +1043,6 @@ func (l *steplog) setupObject() *goja.Object { o.Set("stack", l.stack.setupObject()) o.Set("memory", l.memory.setupObject()) o.Set("contract", l.contract.setupObject()) + return o } diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go index 75f8d54acd..99b689d3a5 100644 --- a/eth/tracers/js/tracer_test.go +++ b/eth/tracers/js/tracer_test.go @@ -69,6 +69,7 @@ func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCon contract = vm.NewContract(account{}, account{}, value, startGas) ) contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} + if contractCode != nil { contract.Code = contractCode } @@ -88,10 +89,12 @@ func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCon func TestTracer(t *testing.T) { execTracer := func(code string, contract []byte) ([]byte, string) { t.Helper() + tracer, err := newJsTracer(code, nil, nil) if err != nil { t.Fatal(err) } + ret, err := runTrace(tracer, testCtx(), params.TestChainConfig, contract) if err != nil { return nil, err.Error() // Stringify to allow comparison without nil checks @@ -170,6 +173,7 @@ func TestHalt(t *testing.T) { time.Sleep(1 * time.Second) tracer.Stop(timeout) }() + if _, err = runTrace(tracer, testCtx(), params.TestChainConfig, nil); !strings.Contains(err.Error(), "stahp") { t.Errorf("Expected timeout error, got %v", err) } @@ -180,6 +184,7 @@ func TestHaltBetweenSteps(t *testing.T) { if err != nil { t.Fatal(err) } + env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer}) scope := &vm.ScopeContext{ Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0), @@ -200,10 +205,12 @@ func TestHaltBetweenSteps(t *testing.T) { func TestNoStepExec(t *testing.T) { execTracer := func(code string) []byte { t.Helper() + tracer, err := newJsTracer(code, nil, nil) if err != nil { t.Fatal(err) } + env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(100)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer}) tracer.CaptureStart(env, common.Address{}, common.Address{}, false, []byte{}, 1000, big.NewInt(0)) tracer.CaptureEnd(nil, 0, nil) @@ -264,6 +271,7 @@ func TestEnterExit(t *testing.T) { if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(tracers.Context), nil); err == nil { t.Fatal("tracer creation should've failed without exit() definition") } + if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(tracers.Context), nil); err != nil { t.Fatal(err) } @@ -311,10 +319,13 @@ func TestSetup(t *testing.T) { if err != nil { t.Fatal(err) } + have, err := tracer.GetResult() + if err != nil { t.Fatal(err) } + if string(have) != `"bar"` { t.Errorf("tracer returned wrong result. have: %s, want: \"bar\"\n", string(have)) } diff --git a/eth/tracers/native/4byte.go b/eth/tracers/native/4byte.go index 5a2c4f9111..eb73c37363 100644 --- a/eth/tracers/native/4byte.go +++ b/eth/tracers/native/4byte.go @@ -59,6 +59,7 @@ func newFourByteTracer(ctx *tracers.Context, _ json.RawMessage) (tracers.Tracer, t := &fourByteTracer{ ids: make(map[string]int), } + return t, nil } diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index 4ac03e512f..cc608bf631 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -72,17 +72,23 @@ func (f *callFrame) processOutput(output []byte, err error) { f.Output = output return } + f.Error = err.Error() + if f.Type == vm.CREATE || f.Type == vm.CREATE2 { f.To = nil } + if !errors.Is(err, vm.ErrExecutionReverted) || len(output) == 0 { return } + f.Output = output + if len(output) < 4 { return } + if unpacked, err := abi.UnpackRevert(output); err == nil { f.RevertReason = unpacked } @@ -164,6 +170,8 @@ func (t *callTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, sco if t.interrupt.Load() { return } + + // nolint : exhaustive switch op { case vm.LOG0, vm.LOG1, vm.LOG2, vm.LOG3, vm.LOG4: size := int(op - vm.LOG0) @@ -175,6 +183,7 @@ func (t *callTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, sco mStart := stackData[len(stackData)-1] mSize := stackData[len(stackData)-2] topics := make([]common.Hash, size) + for i := 0; i < size; i++ { topic := stackData[len(stackData)-2-(i+1)] topics[i] = common.Hash(topic.Bytes32()) @@ -273,6 +282,7 @@ func clearFailedLogs(cf *callFrame, parentFailed bool) { if failed { cf.Logs = nil } + for i := range cf.Calls { clearFailedLogs(&cf.Calls[i], failed) } diff --git a/eth/tracers/native/call_flat.go b/eth/tracers/native/call_flat.go index 5da6040506..6ddd32b151 100644 --- a/eth/tracers/native/call_flat.go +++ b/eth/tracers/native/call_flat.go @@ -133,7 +133,9 @@ func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Trace if err != nil { return nil, err } + t, ok := tracer.(*callTracer) + if !ok { return nil, errors.New("internal error: embedded tracer has wrong type") } @@ -185,6 +187,7 @@ func (t *flatCallTracer) CaptureExit(output []byte, gasUsed uint64, err error) { if t.config.IncludePrecompiles { return } + var ( // call has been nested in parent parent = t.tracer.callstack[len(t.tracer.callstack)-1] @@ -192,6 +195,7 @@ func (t *flatCallTracer) CaptureExit(output []byte, gasUsed uint64, err error) { typ = call.Type to = call.To ) + if typ == vm.CALL || typ == vm.STATICCALL { if t.isPrecompiled(*to) { t.tracer.callstack[len(t.tracer.callstack)-1].Calls = parent.Calls[:len(parent.Calls)-1] @@ -222,6 +226,7 @@ func (t *flatCallTracer) GetResult() (json.RawMessage, error) { if err != nil { return nil, err } + return res, t.reason } @@ -237,11 +242,14 @@ func (t *flatCallTracer) isPrecompiled(addr common.Address) bool { return true } } + return false } func flatFromNested(input *callFrame, traceAddress []int, convertErrs bool, ctx *tracers.Context) (output []flatCallFrame, err error) { var frame *flatCallFrame + + // nolint:exhaustive switch input.Type { case vm.CREATE, vm.CREATE2: frame = newFlatCreate(input) @@ -257,6 +265,7 @@ func flatFromNested(input *callFrame, traceAddress []int, convertErrs bool, ctx frame.Error = input.Error frame.Subtraces = len(input.Calls) fillCallFrameFromContext(frame, ctx) + if convertErrs { convertErrorToParity(frame) } @@ -268,14 +277,17 @@ func flatFromNested(input *callFrame, traceAddress []int, convertErrs bool, ctx } output = append(output, *frame) + if len(input.Calls) > 0 { for i, childCall := range input.Calls { childAddr := childTraceAddress(traceAddress, i) childCallCopy := childCall flat, err := flatFromNested(&childCallCopy, childAddr, convertErrs, ctx) + if err != nil { return nil, err } + output = append(output, flat...) } } @@ -343,15 +355,19 @@ func fillCallFrameFromContext(callFrame *flatCallFrame, ctx *tracers.Context) { if ctx == nil { return } + if ctx.BlockHash != (common.Hash{}) { callFrame.BlockHash = &ctx.BlockHash } + if ctx.BlockNumber != nil { callFrame.BlockNumber = ctx.BlockNumber.Uint64() } + if ctx.TxHash != (common.Hash{}) { callFrame.TransactionHash = &ctx.TxHash } + callFrame.TransactionPosition = uint64(ctx.TxIndex) } @@ -375,5 +391,6 @@ func childTraceAddress(a []int, i int) []int { child := make([]int, 0, len(a)+1) child = append(child, a...) child = append(child, i) + return child } diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go index db8ddd6438..df19bef9f3 100644 --- a/eth/tracers/native/mux.go +++ b/eth/tracers/native/mux.go @@ -44,13 +44,16 @@ func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, er return nil, err } } + objects := make([]tracers.Tracer, 0, len(config)) names := make([]string, 0, len(config)) + for k, v := range config { t, err := tracers.DefaultDirectory.New(k, ctx, v) if err != nil { return nil, err } + objects = append(objects, t) names = append(names, k) } @@ -116,17 +119,22 @@ func (t *muxTracer) CaptureTxEnd(restGas uint64) { // GetResult returns an empty json object. func (t *muxTracer) GetResult() (json.RawMessage, error) { resObject := make(map[string]json.RawMessage) + for i, tt := range t.tracers { r, err := tt.GetResult() if err != nil { return nil, err } + resObject[t.names[i]] = r } + res, err := json.Marshal(resObject) + if err != nil { return nil, err } + return res, nil } diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index b71d5d6215..41da5ef66a 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -79,6 +79,7 @@ func newPrestateTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Trace return nil, err } } + return &prestateTracer{ pre: state{}, post: state{}, @@ -151,6 +152,7 @@ func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, case stackLen >= 1 && (op == vm.EXTCODECOPY || op == vm.EXTCODEHASH || op == vm.EXTCODESIZE || op == vm.BALANCE || op == vm.SELFDESTRUCT): addr := common.Address(stackData[stackLen-1].Bytes20()) t.lookupAccount(addr) + if op == vm.SELFDESTRUCT { t.deleted[caller] = true } @@ -188,6 +190,7 @@ func (t *prestateTracer) CaptureTxEnd(restGas uint64) { if _, ok := t.deleted[addr]; ok { continue } + modified := false postAccount := &account{Storage: make(map[common.Hash]common.Hash)} newBalance := t.env.StateDB.GetBalance(addr) @@ -198,10 +201,12 @@ func (t *prestateTracer) CaptureTxEnd(restGas uint64) { modified = true postAccount.Balance = newBalance } + if newNonce != t.pre[addr].Nonce { modified = true postAccount.Nonce = newNonce } + if !bytes.Equal(newCode, t.pre[addr].Code) { modified = true postAccount.Code = newCode @@ -245,7 +250,9 @@ func (t *prestateTracer) CaptureTxEnd(restGas uint64) { // error arising from the encoding or forceful termination (via `Stop`). func (t *prestateTracer) GetResult() (json.RawMessage, error) { var res []byte + var err error + if t.config.DiffMode { res, err = json.Marshal(struct { Post state `json:"post"` @@ -288,5 +295,6 @@ func (t *prestateTracer) lookupStorage(addr common.Address, key common.Hash) { if _, ok := t.pre[addr].Storage[key]; ok { return } + t.pre[addr].Storage[key] = t.env.StateDB.GetState(addr, key) } diff --git a/eth/tracers/tracers.go b/eth/tracers/tracers.go index d8ab05f2cd..869e1e01e6 100644 --- a/eth/tracers/tracers.go +++ b/eth/tracers/tracers.go @@ -73,7 +73,7 @@ func (d *directory) Register(name string, f ctorFn, isJS bool) { // dynamic user-provided JS code. func (d *directory) RegisterJSEval(f jsCtorFn) { d.jsEval = f - } +} // New returns a new instance of a tracer, by iterating through the // registered lookups. Name is either name of an existing tracer @@ -107,16 +107,22 @@ func GetMemoryCopyPadded(m *vm.Memory, offset, size int64) ([]byte, error) { if offset < 0 || size < 0 { return nil, fmt.Errorf("offset or size must not be negative") } + if int(offset+size) < m.Len() { // slice fully inside memory return m.GetCopy(offset, size), nil } + paddingNeeded := int(offset+size) - m.Len() + if paddingNeeded > memoryPadLimit { return nil, fmt.Errorf("reached limit for padding memory slice: %d", paddingNeeded) } + cpy := make([]byte, size) + if overlap := int64(m.Len()) - offset; overlap > 0 { copy(cpy, m.GetPtr(offset, overlap)) } + return cpy, nil } diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index 72e406ffd9..61f7961e1f 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -133,18 +133,23 @@ func TestMemCopying(t *testing.T) { mem := vm.NewMemory() mem.Resize(uint64(tc.memsize)) cpy, err := GetMemoryCopyPadded(mem, tc.offset, tc.size) + if want := tc.wantErr; want != "" { if err == nil { t.Fatalf("test %d: want '%v' have no error", i, want) } + if have := err.Error(); want != have { t.Fatalf("test %d: want '%v' have '%v'", i, want, have) } + continue } + if err != nil { t.Fatalf("test %d: unexpected error: %v", i, err) } + if want, have := tc.wantSize, len(cpy); have != want { t.Fatalf("test %d: want %v have %v", i, want, have) } diff --git a/eth/tracers/tracker.go b/eth/tracers/tracker.go index 136be37f5c..649814e426 100644 --- a/eth/tracers/tracker.go +++ b/eth/tracers/tracker.go @@ -37,6 +37,7 @@ type stateTracker struct { // the number of the first state that will be used. func newStateTracker(limit int, oldest uint64) *stateTracker { lock := new(sync.RWMutex) + return &stateTracker{ limit: limit, oldest: oldest, @@ -61,12 +62,15 @@ func (t *stateTracker) releaseState(number uint64, release StateReleaseFunc) { // it to the next state which is not used up. if number == t.oldest { var count int + for _, used := range t.used { if !used { break } + count += 1 } + t.oldest += uint64(count) copy(t.used, t.used[count:]) @@ -77,6 +81,7 @@ func (t *stateTracker) releaseState(number uint64, release StateReleaseFunc) { // Fire the signal to all waiters that oldest marker is updated. t.cond.Broadcast() } + t.releases = append(t.releases, release) } @@ -88,6 +93,7 @@ func (t *stateTracker) callReleases() { for _, release := range t.releases { release() } + t.releases = t.releases[:0] } @@ -100,10 +106,12 @@ func (t *stateTracker) wait(number uint64) error { if number < t.oldest { return fmt.Errorf("invalid state number %d head %d", number, t.oldest) } + if number < t.oldest+uint64(t.limit) { // number is now within limit, wait over return nil } + t.cond.Wait() } } diff --git a/eth/tracers/tracker_test.go b/eth/tracers/tracker_test.go index f17b9a08a3..9adf25682a 100644 --- a/eth/tracers/tracker_test.go +++ b/eth/tracers/tracker_test.go @@ -54,11 +54,13 @@ func TestTracker(t *testing.T) { expHead: 6, }, } + for _, c := range cases { tracker := newStateTracker(c.limit, 0) for _, call := range c.calls { tracker.releaseState(call, func() {}) } + tracker.lock.RLock() head := tracker.oldest tracker.lock.RUnlock() @@ -105,13 +107,16 @@ func TestTracker(t *testing.T) { expHead: 5, }, } + tracker := newStateTracker(5, 0) // limit = 5, oldest = 0 + for _, call := range calls { tracker.releaseState(call.number, nil) tracker.lock.RLock() if !reflect.DeepEqual(tracker.used, call.expUsed) { t.Fatalf("Unexpected used array") } + if tracker.oldest != call.expHead { t.Fatalf("Unexpected head") } diff --git a/ethclient/gethclient/gethclient.go b/ethclient/gethclient/gethclient.go index 7f8152a4d7..b705d767a3 100644 --- a/ethclient/gethclient/gethclient.go +++ b/ethclient/gethclient/gethclient.go @@ -190,10 +190,12 @@ func toBlockNumArg(number *big.Int) string { if number.Cmp(pending) == 0 { return "pending" } + finalized := big.NewInt(int64(rpc.FinalizedBlockNumber)) if number.Cmp(finalized) == 0 { return "finalized" } + safe := big.NewInt(int64(rpc.SafeBlockNumber)) if number.Cmp(safe) == 0 { return "safe" @@ -258,11 +260,14 @@ func (a OverrideAccount) MarshalJSON() ([]byte, error) { Balance: (*hexutil.Big)(a.Balance), StateDiff: a.StateDiff, } + if a.Code != nil { output.Code = hexutil.Encode(a.Code) } + if a.State != nil { output.State = a.State } + return json.Marshal(output) } diff --git a/ethclient/gethclient/gethclient_test.go b/ethclient/gethclient/gethclient_test.go index 30812ed10e..b1889f7935 100644 --- a/ethclient/gethclient/gethclient_test.go +++ b/ethclient/gethclient/gethclient_test.go @@ -61,7 +61,9 @@ func newTestBackend(t *testing.T) (*node.Node, []*types.Block) { if err != nil { t.Fatalf("can't create new ethereum service: %v", err) } + filterSystem := filters.NewFilterSystem(ethservice.APIBackend, filters.Config{}) + n.RegisterAPIs([]rpc.API{{ Namespace: "eth", Service: filters.NewFilterAPI(filterSystem, false, config.BorLogs), @@ -237,11 +239,14 @@ func testGetProof(t *testing.T, client *rpc.Client) { if len(result.StorageProof) != 1 { t.Fatalf("invalid storage proof, want 1 proof, got %v proof(s)", len(result.StorageProof)) } + proof := result.StorageProof[0] slotValue, _ := ethcl.StorageAt(context.Background(), testAddr, testSlot, nil) + if !bytes.Equal(slotValue, proof.Value.Bytes()) { t.Fatalf("invalid storage proof value, want: %v, got: %v", slotValue, proof.Value.Bytes()) } + if proof.Key != testSlot.String() { t.Fatalf("invalid storage proof key, want: %v, got: %v", testSlot.String(), proof.Key) } @@ -337,10 +342,13 @@ func testSubscribeFullPendingTransactions(t *testing.T, client *rpc.Client) { tx := types.NewTransaction(1, common.Address{1}, big.NewInt(1), 22000, big.NewInt(1), nil) signer := types.LatestSignerForChainID(chainID) signature, err := crypto.Sign(signer.Hash(tx).Bytes(), testKey) + if err != nil { t.Fatal(err) } + signedTx, err := tx.WithSignature(signer, signature) + if err != nil { t.Fatal(err) } diff --git a/graphql/graphql.go b/graphql/graphql.go index 1dce7b7111..f1a9a9440d 100644 --- a/graphql/graphql.go +++ b/graphql/graphql.go @@ -199,6 +199,7 @@ type Transaction struct { func (t *Transaction) resolve(ctx context.Context) (*types.Transaction, *Block, error) { t.mu.Lock() defer t.mu.Unlock() + if t.tx != nil { return t.tx, t.block, nil } @@ -213,10 +214,12 @@ func (t *Transaction) resolve(ctx context.Context) (*types.Transaction, *Block, hash: blockHash, } t.index = index + return t.tx, t.block, nil } // No finalized transaction, try to retrieve it from the pool t.tx = t.r.backend.GetPoolTransaction(t.hash) + return t.tx, nil, nil } @@ -270,6 +273,7 @@ func (t *Transaction) EffectiveGasPrice(ctx context.Context) (*hexutil.Big, erro if block == nil { return nil, nil } + header, err := block.resolveHeader(ctx) if err != nil || header == nil { return nil, err @@ -319,6 +323,7 @@ func (t *Transaction) EffectiveTip(ctx context.Context) (*hexutil.Big, error) { if block == nil { return nil, nil } + header, err := block.resolveHeader(ctx) if err != nil || header == nil { return nil, err @@ -374,6 +379,7 @@ func (t *Transaction) From(ctx context.Context, args BlockNumberArgs) (*Account, if err != nil || tx == nil { return nil, err } + signer := types.LatestSigner(t.r.backend.ChainConfig()) from, _ := types.Sender(signer, tx) return &Account{ @@ -388,6 +394,7 @@ func (t *Transaction) Block(ctx context.Context) (*Block, error) { if err != nil { return nil, err } + return block, nil } @@ -414,6 +421,7 @@ func (t *Transaction) getReceipt(ctx context.Context) (*types.Receipt, error) { if block == nil { return nil, nil } + receipts, err := block.resolveReceipts(ctx) if err != nil { return nil, err @@ -468,15 +476,19 @@ func (t *Transaction) Logs(ctx context.Context) (*[]*Log, error) { if err != nil { return nil, err } + // Pending tx if block == nil { //nolint:nilnil return nil, nil } + h, err := block.Hash(ctx) + if err != nil { return nil, err } + return t.getLogs(ctx, h) } @@ -487,6 +499,7 @@ func (t *Transaction) getLogs(ctx context.Context, hash common.Hash) (*[]*Log, e filter = t.r.filterSystem.NewBlockFilter(hash, nil, nil) logs, err = filter.Logs(ctx) ) + if err != nil { return nil, err } @@ -561,6 +574,7 @@ func (t *Transaction) Raw(ctx context.Context) (hexutil.Bytes, error) { if err != nil || tx == nil { return hexutil.Bytes{}, err } + return tx.MarshalBinary() } @@ -569,6 +583,7 @@ func (t *Transaction) RawReceipt(ctx context.Context) (hexutil.Bytes, error) { if err != nil || receipt == nil { return hexutil.Bytes{}, err } + return receipt.MarshalBinary() } @@ -602,6 +617,7 @@ func (b *Block) resolve(ctx context.Context) (*types.Block, error) { } var err error b.block, err = b.r.backend.BlockByNumberOrHash(ctx, *b.numberOrHash) + if b.block != nil { b.hash = b.block.Hash() if b.header == nil { @@ -617,6 +633,7 @@ func (b *Block) resolve(ctx context.Context) (*types.Block, error) { func (b *Block) resolveHeader(ctx context.Context) (*types.Header, error) { b.mu.Lock() defer b.mu.Unlock() + if b.header != nil { return b.header, nil } @@ -625,12 +642,15 @@ func (b *Block) resolveHeader(ctx context.Context) (*types.Header, error) { } var err error b.header, err = b.r.backend.HeaderByNumberOrHash(ctx, *b.numberOrHash) + if err != nil { return nil, err } + if b.hash == (common.Hash{}) { b.hash = b.header.Hash() } + return b.header, nil } @@ -639,14 +659,19 @@ func (b *Block) resolveHeader(ctx context.Context) (*types.Header, error) { func (b *Block) resolveReceipts(ctx context.Context) ([]*types.Receipt, error) { b.mu.Lock() defer b.mu.Unlock() + if b.receipts != nil { return b.receipts, nil } + receipts, err := b.r.backend.GetReceipts(ctx, b.hash) + if err != nil { return nil, err } + b.receipts = receipts + return receipts, nil } @@ -697,6 +722,7 @@ func (b *Block) NextBaseFeePerGas(ctx context.Context) (*hexutil.Big, error) { if err != nil { return nil, err } + chaincfg := b.r.backend.ChainConfig() if header.BaseFee == nil { // Make sure next block doesn't enable EIP-1559 @@ -715,6 +741,7 @@ func (b *Block) Parent(ctx context.Context) (*Block, error) { if b.header == nil || b.header.Number.Uint64() < 1 { return nil, nil } + var ( num = rpc.BlockNumber(b.header.Number.Uint64() - 1) hash = b.header.ParentHash @@ -842,6 +869,7 @@ func (b *Block) TotalDifficulty(ctx context.Context) (hexutil.Big, error) { if err != nil { return hexutil.Big{}, err } + td := b.r.backend.GetTd(ctx, hash) if td == nil { return hexutil.Big{}, fmt.Errorf("total difficulty not found %x", hash) @@ -854,6 +882,7 @@ func (b *Block) RawHeader(ctx context.Context) (hexutil.Bytes, error) { if err != nil { return hexutil.Bytes{}, err } + return rlp.EncodeToBytes(header) } @@ -862,6 +891,7 @@ func (b *Block) Raw(ctx context.Context) (hexutil.Bytes, error) { if err != nil { return hexutil.Bytes{}, err } + return rlp.EncodeToBytes(block) } @@ -1017,6 +1047,7 @@ func (b *Block) Logs(ctx context.Context, args struct{ Filter BlockFilterCriteri if err != nil { return nil, err } + filter := b.r.filterSystem.NewBlockFilter(hash, addresses, topics) // Run the filter and return all the logs @@ -1178,6 +1209,7 @@ func (r *Resolver) Block(ctx context.Context, args struct { } else { numberOrHash = rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) } + block := &Block{ r: r, numberOrHash: &numberOrHash, @@ -1299,6 +1331,7 @@ func (r *Resolver) Logs(ctx context.Context, args struct{ Filter FilterCriteria } // Construct the range filter filter := r.filterSystem.NewRangeFilter(begin, end, addresses, topics) + return runFilter(ctx, r, filter) } diff --git a/graphql/graphql_test.go b/graphql/graphql_test.go index 5fc5fcee83..37a741de90 100644 --- a/graphql/graphql_test.go +++ b/graphql/graphql_test.go @@ -63,6 +63,7 @@ func TestBuildSchema(t *testing.T) { func TestGraphQLBlockSerialization(t *testing.T) { stack := createNode(t) defer stack.Close() + genesis := &core.Genesis{ Config: params.AllEthashProtocolChanges, GasLimit: 11500000, @@ -156,6 +157,7 @@ func TestGraphQLBlockSerialization(t *testing.T) { if err != nil { t.Fatalf("could not post: %v", err) } + bodyBytes, err := io.ReadAll(resp.Body) resp.Body.Close() if err != nil { @@ -178,8 +180,10 @@ func TestGraphQLBlockSerializationEIP2718(t *testing.T) { funds = big.NewInt(1000000000000000) dad = common.HexToAddress("0x0000000000000000000000000000000000000dad") ) + stack := createNode(t) defer stack.Close() + genesis := &core.Genesis{ Config: params.AllEthashProtocolChanges, GasLimit: 11500000, @@ -240,6 +244,7 @@ func TestGraphQLBlockSerializationEIP2718(t *testing.T) { if err != nil { t.Fatalf("could not post: %v", err) } + bodyBytes, err := io.ReadAll(resp.Body) resp.Body.Close() if err != nil { @@ -266,6 +271,7 @@ func TestGraphQLHTTPOnSamePort_GQLRequest_Unsuccessful(t *testing.T) { if err != nil { t.Fatalf("could not post: %v", err) } + resp.Body.Close() // make sure the request is not handled successfully assert.Equal(t, http.StatusNotFound, resp.StatusCode) @@ -296,9 +302,11 @@ func TestGraphQLConcurrentResolvers(t *testing.T) { signer = types.LatestSigner(genesis.Config) stack = createNode(t) ) + defer stack.Close() var tx *types.Transaction + handler, chain := newGQLService(t, stack, genesis, 1, func(i int, gen *core.BlockGen) { tx, _ = types.SignNewTx(key, signer, &types.LegacyTx{To: &dad, Gas: 100000, GasPrice: big.NewInt(params.InitialBaseFee)}) gen.AddTx(tx) @@ -354,13 +362,17 @@ func TestGraphQLConcurrentResolvers(t *testing.T) { }, } { res := handler.Schema.Exec(context.Background(), tt.body, "", map[string]interface{}{}) + if res.Errors != nil { t.Fatalf("failed to execute query for testcase #%d: %v", i, res.Errors) } + have, err := json.Marshal(res.Data) + if err != nil { t.Fatalf("failed to encode graphql response for testcase #%d: %s", i, err) } + if string(have) != tt.want { t.Errorf("response unmatch for testcase #%d.\nExpected:\n%s\nGot:\n%s\n", i, tt.want, have) } @@ -380,6 +392,7 @@ func createNode(t *testing.T) *node.Node { if err != nil { t.Fatalf("could not create node: %v", err) } + return stack } @@ -416,5 +429,6 @@ func newGQLService(t *testing.T, stack *node.Node, gspec *core.Genesis, genBlock if err != nil { t.Fatalf("could not create graphql service: %v", err) } + return handler, chain } diff --git a/graphql/service.go b/graphql/service.go index 4392dd83e6..d9053a9507 100644 --- a/graphql/service.go +++ b/graphql/service.go @@ -53,6 +53,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { timer *time.Timer cancel context.CancelFunc ) + ctx, cancel = context.WithCancel(ctx) defer cancel() @@ -88,6 +89,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } response := h.Schema.Exec(ctx, params.Query, params.OperationName, params.Variables) + timer.Stop() responded.Do(func() { responseJSON, err := json.Marshal(response) diff --git a/internal/cli/server/server_test.go b/internal/cli/server/server_test.go index 16447af31e..5cea044df4 100644 --- a/internal/cli/server/server_test.go +++ b/internal/cli/server/server_test.go @@ -53,6 +53,7 @@ func TestServer_DeveloperMode(t *testing.T) { for i = 0; i < 3; i++ { // We expect the node to mine blocks every `config.Developer.Period` time period time.Sleep(time.Duration(config.Developer.Period) * time.Second) + currBlock := server.backend.BlockChain().CurrentBlock().Number.Int64() expected := blockNumber + i + 1 if res := assert.Equal(t, currBlock, expected); res == false { diff --git a/les/catalyst/api.go b/les/catalyst/api.go index c9db514fcc..e21c58c5fe 100644 --- a/les/catalyst/api.go +++ b/les/catalyst/api.go @@ -80,6 +80,7 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(heads engine.ForkchoiceStateV1, pay // TODO (MariusVanDerWijden) trigger sync return engine.STATUS_SYNCING, nil } + return engine.STATUS_INVALID, err } // If the finalized block is set, check if it is in our blockchain @@ -136,11 +137,13 @@ func (api *ConsensusAPI) ExecutePayloadV1(params engine.ExecutableData) (engine. merger.ReachTTD() } hash := block.Hash() + return engine.PayloadStatusV1{Status: engine.VALID, LatestValidHash: &hash}, nil } func (api *ConsensusAPI) validForkChoiceResponse() engine.ForkChoiceResponse { currentHash := api.les.BlockChain().CurrentHeader().Hash() + return engine.ForkChoiceResponse{ PayloadStatus: engine.PayloadStatusV1{Status: engine.VALID, LatestValidHash: ¤tHash}, } @@ -181,6 +184,7 @@ func (api *ConsensusAPI) setCanonical(newHead common.Hash) error { if newHeadHeader == nil { return errors.New("unknown header") } + if err := api.les.BlockChain().SetCanonical(newHeadHeader); err != nil { return err } @@ -195,6 +199,7 @@ func (api *ConsensusAPI) setCanonical(newHead common.Hash) error { // the configuration of the node. func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config engine.TransitionConfigurationV1) (*engine.TransitionConfigurationV1, error) { log.Trace("Engine API request received", "method", "ExchangeTransitionConfiguration", "ttd", config.TerminalTotalDifficulty) + if config.TerminalTotalDifficulty == nil { return nil, errors.New("invalid terminal total difficulty") } @@ -213,6 +218,7 @@ func (api *ConsensusAPI) ExchangeTransitionConfigurationV1(config engine.Transit TerminalBlockNumber: config.TerminalBlockNumber, }, nil } + return nil, fmt.Errorf("invalid terminal block hash") } diff --git a/les/catalyst/api_test.go b/les/catalyst/api_test.go index 54757f61da..fe3c798cd7 100644 --- a/les/catalyst/api_test.go +++ b/les/catalyst/api_test.go @@ -58,6 +58,7 @@ func generatePreMergeChain(pre, post int) (*core.Genesis, []*types.Header, []*ty totalDifficulty := new(big.Int).Set(params.GenesisDifficulty) var preHeaders []*types.Header + for _, b := range preBLocks { totalDifficulty.Add(totalDifficulty, b.Difficulty()) preHeaders = append(preHeaders, b.Header()) @@ -109,6 +110,7 @@ func TestExecutePayloadV1(t *testing.T) { if _, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil { t.Errorf("Failed to update head %v", err) } + block := postBlocks[0] fakeBlock := types.NewBlock(&types.Header{ @@ -153,6 +155,7 @@ func TestExecutePayloadV1(t *testing.T) { if headHeader.Number.Uint64() != fakeBlock.NumberU64()-1 { t.Fatal("Unexpected chain head update") } + fcState = engine.ForkchoiceStateV1{ HeadBlockHash: fakeBlock.Hash(), SafeBlockHash: common.Hash{}, diff --git a/les/client.go b/les/client.go index 88fbfc8160..348a20547a 100644 --- a/les/client.go +++ b/les/client.go @@ -94,19 +94,25 @@ func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) { if err != nil { return nil, err } + var overrides core.ChainOverrides + if config.OverrideShanghai != nil { overrides.OverrideShanghai = config.OverrideShanghai } + chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, trie.NewDatabase(chainDb), config.Genesis, &overrides) if _, isCompat := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !isCompat { return nil, genesisErr } + log.Info("") log.Info(strings.Repeat("-", 153)) + for _, line := range strings.Split(chainConfig.Description(), "\n") { log.Info(line) } + log.Info(strings.Repeat("-", 153)) log.Info("") @@ -177,6 +183,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) { // Rewind the chain in case of an incompatible config upgrade. if compat, ok := genesisErr.(*params.ConfigCompatError); ok { log.Warn("Rewinding chain to upgrade configuration", "err", compat) + if compat.RewindToTime > 0 { leth.blockchain.SetHeadWithTimestamp(compat.RewindToTime) } else { diff --git a/les/downloader/peer.go b/les/downloader/peer.go index c2161e2dae..92930894ac 100644 --- a/les/downloader/peer.go +++ b/les/downloader/peer.go @@ -414,6 +414,7 @@ func (ps *peerSet) HeaderIdlePeers() ([]*peerConnection, int) { throughput := func(p *peerConnection) int { return p.rates.Capacity(eth.BlockHeadersMsg, time.Second) } + return ps.idlePeers(eth.ETH66, eth.ETH67, idle, throughput) } @@ -426,6 +427,7 @@ func (ps *peerSet) BodyIdlePeers() ([]*peerConnection, int) { throughput := func(p *peerConnection) int { return p.rates.Capacity(eth.BlockBodiesMsg, time.Second) } + return ps.idlePeers(eth.ETH66, eth.ETH67, idle, throughput) } @@ -438,6 +440,7 @@ func (ps *peerSet) ReceiptIdlePeers() ([]*peerConnection, int) { throughput := func(p *peerConnection) int { return p.rates.Capacity(eth.ReceiptsMsg, time.Second) } + return ps.idlePeers(eth.ETH66, eth.ETH67, idle, throughput) } @@ -450,6 +453,7 @@ func (ps *peerSet) NodeDataIdlePeers() ([]*peerConnection, int) { throughput := func(p *peerConnection) int { return p.rates.Capacity(eth.NodeDataMsg, time.Second) } + return ps.idlePeers(eth.ETH66, eth.ETH67, idle, throughput) } diff --git a/les/downloader/queue.go b/les/downloader/queue.go index 6896b09b38..836826a043 100644 --- a/les/downloader/queue.go +++ b/les/downloader/queue.go @@ -429,6 +429,7 @@ func (q *queue) ReserveHeaders(p *peerConnection, count int) *fetchRequest { continue } } + send = from } // Merge all the skipped batches back diff --git a/les/downloader/statesync.go b/les/downloader/statesync.go index 4dacade3fa..b85e2a4e2c 100644 --- a/les/downloader/statesync.go +++ b/les/downloader/statesync.go @@ -497,6 +497,7 @@ func (s *stateSync) fillTasks(n int, req *stateReq) (nodes []common.Hash, paths req.codeTasks[hash] = t delete(s.codeTasks, hash) } + for path, t := range s.trieTasks { // Stop when we've gathered enough requests if len(nodes)+len(codes) == n { @@ -513,6 +514,7 @@ func (s *stateSync) fillTasks(n int, req *stateReq) (nodes []common.Hash, paths paths = append(paths, t.path) req.trieTasks[path] = t + delete(s.trieTasks, path) } req.nItems = uint16(len(nodes) + len(codes)) @@ -551,6 +553,7 @@ func (s *stateSync) process(req *stateReq) (int, error) { } // Put unfulfilled tasks back into the retry queue npeers := s.d.peers.Len() + for path, task := range req.trieTasks { // If the node did deliver something, missing items may be due to a protocol // limit or a previous timeout + delayed delivery. Both cases should permit @@ -602,19 +605,25 @@ func (s *stateSync) processNodeData(nodeTasks map[string]*trieTask, codeTasks ma Hash: hash, Data: blob, }) + delete(codeTasks, hash) + return hash, err } + for path, task := range nodeTasks { if task.hash == hash { err := s.sched.ProcessNode(trie.NodeSyncResult{ Path: path, Data: blob, }) + delete(nodeTasks, path) + return hash, err } } + return common.Hash{}, trie.ErrNotRequested } diff --git a/les/fetcher.go b/les/fetcher.go index c7a55b193d..200d58fa98 100644 --- a/les/fetcher.go +++ b/les/fetcher.go @@ -272,6 +272,7 @@ func (f *lightFetcher) mainloop() { localHead = f.chain.CurrentHeader() localTd = f.chain.GetTd(localHead.Hash(), localHead.Number.Uint64()) ) + defer requestTimer.Stop() sub := f.chain.SubscribeChainHeadEvent(headCh) defer sub.Unsubscribe() @@ -460,6 +461,7 @@ func (f *lightFetcher) mainloop() { } untrusted = append(untrusted, hash) head = f.chain.GetHeader(head.ParentHash, number-1) + if head == nil { break // all the synced headers will be dropped } diff --git a/les/fetcher_test.go b/les/fetcher_test.go index 2f3a80aa5b..d5a50eeef0 100644 --- a/les/fetcher_test.go +++ b/les/fetcher_test.go @@ -173,6 +173,7 @@ func testTrustedAnnouncement(t *testing.T, protocol int) { } } ) + for i := 0; i < 4; i++ { s, n, teardown := newTestServerPeer(t, int(2*config.ChtSize+config.ChtConfirms), protocol, waitIndexers) diff --git a/les/handler_test.go b/les/handler_test.go index 2511b134d1..aaf257376d 100644 --- a/les/handler_test.go +++ b/les/handler_test.go @@ -292,6 +292,7 @@ func testGetCode(t *testing.T, protocol int) { var codereqs []*CodeReq var codes [][]byte + for i := uint64(0); i <= bc.CurrentBlock().Number.Uint64(); i++ { header := bc.GetHeaderByNumber(i) req := &CodeReq{ @@ -367,6 +368,7 @@ func testGetReceipt(t *testing.T, protocol int) { // Collect the hashes to request, and the response to expect var receipts []types.Receipts var hashes []common.Hash + for i := uint64(0); i <= bc.CurrentBlock().Number.Uint64(); i++ { block := bc.GetBlockByNumber(i) @@ -404,6 +406,7 @@ func testGetProofs(t *testing.T, protocol int) { proofsV2 := light.NewNodeSet() accounts := []common.Address{bankAddr, userAddr1, userAddr2, signerAddr, {}} + for i := uint64(0); i <= bc.CurrentBlock().Number.Uint64(); i++ { header := bc.GetHeaderByNumber(i) trie, _ := trie.New(trie.StateTrieID(header.Root), trie.NewDatabase(server.db)) @@ -664,6 +667,7 @@ func testTransactionStatus(t *testing.T, protocol int) { // check if their status is included now block1hash := rawdb.ReadCanonicalHash(server.db, 1) + test(tx1, false, light.TxStatus{Status: txpool.TxStatusIncluded, Lookup: &rawdb.LegacyTxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 0}}) test(tx2, false, light.TxStatus{Status: txpool.TxStatusIncluded, Lookup: &rawdb.LegacyTxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 1}}) diff --git a/les/odr.go b/les/odr.go index 943b05fdfc..558cf4f8d0 100644 --- a/les/odr.go +++ b/les/odr.go @@ -180,6 +180,7 @@ func (odr *LesOdr) RetrieveTxStatus(ctx context.Context, req *light.TxStatusRequ if result[index].Status != txpool.TxStatusUnknown { continue } + if status.Status == txpool.TxStatusUnknown { continue } diff --git a/les/odr_test.go b/les/odr_test.go index e6e5fe523d..d68b6641d9 100644 --- a/les/odr_test.go +++ b/les/odr_test.go @@ -297,6 +297,7 @@ func testGetTxStatusFromUnindexedPeers(t *testing.T, protocol int) { blockHashes = make(map[common.Hash]common.Hash) // Transaction hash to block hash mappings intraIndex = make(map[common.Hash]uint64) // Transaction intra-index in block ) + for number := uint64(1); number < server.backend.Blockchain().CurrentBlock().Number.Uint64(); number++ { block := server.backend.Blockchain().GetBlockByNumber(number) if block == nil { @@ -344,6 +345,7 @@ func testGetTxStatusFromUnindexedPeers(t *testing.T, protocol int) { if txLookup != txIndexUnlimited && (txLookup == txIndexDisabled || number < min) { continue // Filter out unindexed transactions } + stats[i].Status = txpool.TxStatusIncluded stats[i].Lookup = &rawdb.LegacyTxLookupEntry{ BlockHash: blockHashes[hash], diff --git a/les/server_handler.go b/les/server_handler.go index 39c7ace1c9..dc545e4b6b 100644 --- a/les/server_handler.go +++ b/les/server_handler.go @@ -364,6 +364,7 @@ func getAccount(triedb *trie.Database, root, hash common.Hash) (types.StateAccou if err != nil { return types.StateAccount{}, err } + blob, err := trie.Get(hash[:]) if err != nil { return types.StateAccount{}, err @@ -392,6 +393,7 @@ func (h *serverHandler) GetHelperTrie(typ uint, index uint64) *trie.Trie { if root == (common.Hash{}) { return nil } + trie, _ := trie.New(trie.TrieID(root), trie.NewDatabase(rawdb.NewTable(h.chainDb, prefix))) return trie } diff --git a/les/server_requests.go b/les/server_requests.go index ef3fdd913e..f8c070bc37 100644 --- a/les/server_requests.go +++ b/les/server_requests.go @@ -429,6 +429,7 @@ func handleGetProofs(msg Decoder) (serveRequestFn, uint64, uint64, error) { p.bumpInvalid() continue } + trie, err = statedb.OpenStorageTrie(root, common.BytesToHash(request.AccKey), account.Root) if trie == nil || err != nil { p.Log().Warn("Failed to open storage trie for proof", "block", header.Number, "hash", header.Hash(), "account", common.BytesToHash(request.AccKey), "root", account.Root, "err", err) @@ -517,12 +518,14 @@ func handleSendTx(msg Decoder) (serveRequestFn, uint64, uint64, error) { } hash := tx.Hash() stats[i] = txStatus(backend, hash) + if stats[i].Status == txpool.TxStatusUnknown { addFn := backend.TxPool().AddRemotes // Add txs synchronously for testing purpose if backend.AddTxsSync() { addFn = backend.TxPool().AddRemotesSync } + if errs := addFn([]*types.Transaction{tx}); errs[0] != nil { stats[i].Error = errs[0].Error() continue diff --git a/les/state_accessor.go b/les/state_accessor.go index 1c6d2bf8bf..1cd4017e7a 100644 --- a/les/state_accessor.go +++ b/les/state_accessor.go @@ -49,6 +49,7 @@ func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types. if err != nil { return nil, vm.BlockContext{}, nil, nil, err } + statedb, release, err := leth.stateAtBlock(ctx, parent, reexec) if err != nil { return nil, vm.BlockContext{}, nil, nil, err @@ -63,6 +64,7 @@ func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types. msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) txContext := core.NewEVMTxContext(msg) blockContext := core.NewEVMBlockContext(block.Header(), leth.blockchain, nil) + statedb.SetTxContext(tx.Hash(), idx) if idx == txIndex { return msg, blockContext, statedb, release, nil @@ -77,5 +79,6 @@ func (leth *LightEthereum) stateAtTransaction(ctx context.Context, block *types. // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number())) } + return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash()) } diff --git a/les/test_helper.go b/les/test_helper.go index 3dcc5b4a6e..206ed59c30 100644 --- a/les/test_helper.go +++ b/les/test_helper.go @@ -489,6 +489,7 @@ func (client *testClient) newRawPeer(t *testing.T, name string, version int, rec head = client.handler.backend.blockchain.CurrentHeader() td = client.handler.backend.blockchain.GetTd(head.Hash(), head.Number.Uint64()) ) + forkID := forkid.NewID(client.handler.backend.blockchain.Config(), genesis.Hash(), head.Number.Uint64(), head.Time) tp.handshakeWithClient(t, td, head.Hash(), head.Number.Uint64(), genesis.Hash(), forkID, testCostList(0), recentTxLookup) // disable flow control by default @@ -553,6 +554,7 @@ func (server *testServer) newRawPeer(t *testing.T, name string, version int) (*t head = server.handler.blockchain.CurrentHeader() td = server.handler.blockchain.GetTd(head.Hash(), head.Number.Uint64()) ) + forkID := forkid.NewID(server.handler.blockchain.Config(), genesis.Hash(), head.Number.Uint64(), head.Time) tp.handshakeWithServer(t, td, head.Hash(), head.Number.Uint64(), genesis.Hash(), forkID) diff --git a/les/ulc_test.go b/les/ulc_test.go index d304674fb1..c5923d6f8b 100644 --- a/les/ulc_test.go +++ b/les/ulc_test.go @@ -47,6 +47,7 @@ func testULCAnnounceThreshold(t *testing.T, protocol int) { nopruning: true, } _, c, teardown := newClientServerEnv(t, netconfig) + return c, teardown } diff --git a/miner/stress/beacon/main.go b/miner/stress/beacon/main.go index 65318f1a0e..61f894c847 100644 --- a/miner/stress/beacon/main.go +++ b/miner/stress/beacon/main.go @@ -128,9 +128,11 @@ func newNode(typ nodetype, genesis *core.Genesis, enodes []*enode.Node) *ethNode // Inject the signer key and start sealing with it stack.AccountManager().AddBackend(keystore.NewPlaintextKeyStore("beacon-stress")) ks := stack.AccountManager().Backends(keystore.KeyStoreType) + if len(ks) == 0 { panic("Keystore is not available") } + store := ks[0].(*keystore.KeyStore) if _, err := store.NewAccount(""); err != nil { panic(err) @@ -154,6 +156,7 @@ func (n *ethNode) assembleBlock(parentHash common.Hash, parentTimestamp uint64) if timestamp <= parentTimestamp { timestamp = parentTimestamp + 1 } + payloadAttribute := engine.PayloadAttributes{ Timestamp: timestamp, Random: common.Hash{}, @@ -168,6 +171,7 @@ func (n *ethNode) assembleBlock(parentHash common.Hash, parentTimestamp uint64) if err != nil { return nil, err } + time.Sleep(time.Second * 5) // give enough time for block creation return n.api.GetPayloadV1(*payload.PayloadID) } @@ -205,10 +209,12 @@ func (n *ethNode) insertBlockAndSetHead(parent *types.Header, ed engine.Executab if err := n.insertBlock(ed); err != nil { return err } + block, err := engine.ExecutableDataToBlock(ed) if err != nil { return err } + fcState := engine.ForkchoiceStateV1{ HeadBlockHash: block.ParentHash(), SafeBlockHash: common.Hash{}, @@ -366,6 +372,7 @@ func (mgr *nodeManager) run() { log.Error("Failed to assemble the block", "err", err) continue } + block, _ := engine.ExecutableDataToBlock(*ed) nodes := mgr.getNodes(eth2MiningNode) diff --git a/signer/core/signed_data_test.go b/signer/core/signed_data_test.go index 69ca198420..f0653df580 100644 --- a/signer/core/signed_data_test.go +++ b/signer/core/signed_data_test.go @@ -224,7 +224,9 @@ func TestSignData(t *testing.T) { // data/typed via SignTypeData control.approveCh <- "Y" control.inputCh <- "a_long_password" + var want []byte + if signature, err = api.SignTypedData(context.Background(), a, typedData); err != nil { t.Fatal(err) } else if signature == nil || len(signature) != 65 { @@ -236,6 +238,7 @@ func TestSignData(t *testing.T) { // data/typed via SignData / mimetype typed data control.approveCh <- "Y" control.inputCh <- "a_long_password" + if typedDataJson, err := json.Marshal(typedData); err != nil { t.Fatal(err) } else if signature, err = api.SignData(context.Background(), apitypes.DataTyped.Mime, a, hexutil.Encode(typedDataJson)); err != nil { @@ -833,19 +836,26 @@ func TestGnosisSafe(t *testing.T) { // json missing chain id js := "{\n \"safe\": \"0x899FcB1437DE65DC6315f5a69C017dd3F2837557\",\n \"to\": \"0x899FcB1437DE65DC6315f5a69C017dd3F2837557\",\n \"value\": \"0\",\n \"data\": \"0x0d582f13000000000000000000000000d3ed2b8756b942c98c851722f3bd507a17b4745f0000000000000000000000000000000000000000000000000000000000000005\",\n \"operation\": 0,\n \"gasToken\": \"0x0000000000000000000000000000000000000000\",\n \"safeTxGas\": 0,\n \"baseGas\": 0,\n \"gasPrice\": \"0\",\n \"refundReceiver\": \"0x0000000000000000000000000000000000000000\",\n \"nonce\": 0,\n \"executionDate\": null,\n \"submissionDate\": \"2022-02-23T14:09:00.018475Z\",\n \"modified\": \"2022-12-01T15:52:21.214357Z\",\n \"blockNumber\": null,\n \"transactionHash\": null,\n \"safeTxHash\": \"0x6f0f5cffee69087c9d2471e477a63cab2ae171cf433e754315d558d8836274f4\",\n \"executor\": null,\n \"isExecuted\": false,\n \"isSuccessful\": null,\n \"ethGasPrice\": null,\n \"maxFeePerGas\": null,\n \"maxPriorityFeePerGas\": null,\n \"gasUsed\": null,\n \"fee\": null,\n \"origin\": \"https://gnosis-safe.io\",\n \"dataDecoded\": {\n \"method\": \"addOwnerWithThreshold\",\n \"parameters\": [\n {\n \"name\": \"owner\",\n \"type\": \"address\",\n \"value\": \"0xD3Ed2b8756b942c98c851722F3bd507a17B4745F\"\n },\n {\n \"name\": \"_threshold\",\n \"type\": \"uint256\",\n \"value\": \"5\"\n }\n ]\n },\n \"confirmationsRequired\": 4,\n \"confirmations\": [\n {\n \"owner\": \"0x30B714E065B879F5c042A75Bb40a220A0BE27966\",\n \"submissionDate\": \"2022-03-01T14:56:22Z\",\n \"transactionHash\": \"0x6d0a9c83ac7578ef3be1f2afce089fb83b619583dfa779b82f4422fd64ff3ee9\",\n \"signature\": \"0x00000000000000000000000030b714e065b879f5c042a75bb40a220a0be27966000000000000000000000000000000000000000000000000000000000000000001\",\n \"signatureType\": \"APPROVED_HASH\"\n },\n {\n \"owner\": \"0x8300dFEa25Da0eb744fC0D98c23283F86AB8c10C\",\n \"submissionDate\": \"2022-12-01T15:52:21.214357Z\",\n \"transactionHash\": null,\n \"signature\": \"0xbce73de4cc6ee208e933a93c794dcb8ba1810f9848d1eec416b7be4dae9854c07dbf1720e60bbd310d2159197a380c941cfdb55b3ce58f9dd69efd395d7bef881b\",\n \"signatureType\": \"EOA\"\n }\n ],\n \"trusted\": true,\n \"signatures\": null\n}\n" + var gnosisTx core.GnosisSafeTx + if err := json.Unmarshal([]byte(js), &gnosisTx); err != nil { t.Fatal(err) } + sighash, _, err := apitypes.TypedDataAndHash(gnosisTx.ToTypedData()) + if err != nil { t.Fatal(err) } + if bytes.Equal(sighash, gnosisTx.InputExpHash.Bytes()) { t.Fatal("expected inequality") } + gnosisTx.ChainId = (*math.HexOrDecimal256)(big.NewInt(1)) sighash, _, _ = apitypes.TypedDataAndHash(gnosisTx.ToTypedData()) + if !bytes.Equal(sighash, gnosisTx.InputExpHash.Bytes()) { t.Fatal("expected equality") } @@ -990,14 +1000,19 @@ func TestComplexTypedDataWithLowercaseReftype(t *testing.T) { var td apitypes.TypedData err := json.Unmarshal([]byte(complexTypedDataLCRefType), &td) + if err != nil { t.Fatalf("unmarshalling failed '%v'", err) } + _, sighash, err := sign(td) + if err != nil { t.Fatal(err) } + expSigHash := common.FromHex("0x49191f910874f0148597204d9076af128d4694a7c4b714f1ccff330b87207bff") + if !bytes.Equal(expSigHash, sighash) { t.Fatalf("Error, got %x, wanted %x", sighash, expSigHash) } diff --git a/tests/block_test_util.go b/tests/block_test_util.go index 161b0efb77..dfca3cf277 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -131,6 +131,7 @@ func (t *BlockTest) Run(snapshotter bool) error { cache.SnapshotLimit = 1 cache.SnapshotWait = true } + chain, err := core.NewBlockChain(db, cache, gspec, nil, engine, vm.Config{}, nil, nil, nil) if err != nil { return err @@ -277,9 +278,11 @@ func validateHeader(h *btHeader, h2 *types.Header) error { if h.Timestamp != h2.Time { return fmt.Errorf("timestamp: want: %v have: %v", h.Timestamp, h2.Time) } + if !reflect.DeepEqual(h.BaseFeePerGas, h2.BaseFee) { return fmt.Errorf("baseFeePerGas: want: %v have: %v", h.BaseFeePerGas, h2.BaseFee) } + if !reflect.DeepEqual(h.WithdrawalsRoot, h2.WithdrawalsHash) { return fmt.Errorf("withdrawalsRoot: want: %v have: %v", h.WithdrawalsRoot, h2.WithdrawalsHash) } diff --git a/tests/fuzzers/les/debug/main.go b/tests/fuzzers/les/debug/main.go index 77a6127030..3ddba2f0da 100644 --- a/tests/fuzzers/les/debug/main.go +++ b/tests/fuzzers/les/debug/main.go @@ -32,6 +32,7 @@ func main() { } crasher := os.Args[1] data, err := os.ReadFile(crasher) + if err != nil { fmt.Fprintf(os.Stderr, "error loading crasher %v: %v", crasher, err) os.Exit(1) diff --git a/tests/fuzzers/snap/debug/main.go b/tests/fuzzers/snap/debug/main.go index df46bb1e22..b417fcd8ac 100644 --- a/tests/fuzzers/snap/debug/main.go +++ b/tests/fuzzers/snap/debug/main.go @@ -30,6 +30,7 @@ func main() { } crasher := os.Args[1] data, err := os.ReadFile(crasher) + if err != nil { fmt.Fprintf(os.Stderr, "error loading crasher %v: %v", crasher, err) os.Exit(1) diff --git a/tests/fuzzers/snap/fuzz_handler.go b/tests/fuzzers/snap/fuzz_handler.go index fbe404c45b..31458906c3 100644 --- a/tests/fuzzers/snap/fuzz_handler.go +++ b/tests/fuzzers/snap/fuzz_handler.go @@ -61,6 +61,7 @@ func getChain() *core.BlockChain { } ga[common.BytesToAddress(a)] = acc } + gspec := &core.Genesis{ Config: params.TestChainConfig, Alloc: ga, diff --git a/tests/state_test_util.go b/tests/state_test_util.go index d99940e2eb..e772b47f13 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -170,16 +170,20 @@ func (t *StateTest) checkError(subtest StateSubtest, err error) error { if err == nil && expectedError == "" { return nil } + if err == nil && expectedError != "" { return fmt.Errorf("expected error %q, got no error", expectedError) } + if err != nil && expectedError == "" { return fmt.Errorf("unexpected error: %w", err) } + if err != nil && expectedError != "" { // Ignore expected errors (TODO MariusVanDerWijden check error string) return nil } + return nil } @@ -252,20 +256,24 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh context.GetHash = vmTestBlockHash context.BaseFee = baseFee context.Random = nil + if t.json.Env.Difficulty != nil { context.Difficulty = new(big.Int).Set(t.json.Env.Difficulty) } + if config.IsLondon(new(big.Int)) && t.json.Env.Random != nil { rnd := common.BigToHash(t.json.Env.Random) context.Random = &rnd context.Difficulty = big.NewInt(0) } + evm := vm.NewEVM(context, txContext, statedb, config, vmconfig) // Execute the message. snapshot := statedb.Snapshot() gaspool := new(core.GasPool) gaspool.AddGas(block.GasLimit()) _, err = core.ApplyMessage(evm, msg, gaspool, nil) + if err != nil { statedb.RevertToSnapshot(snapshot) } @@ -279,6 +287,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh statedb.Commit(config.IsEIP158(block.Number())) // And _now_ get the state root root := statedb.IntermediateRoot(config.IsEIP158(block.Number())) + return snaps, statedb, root, err }