diff --git a/.golangci.yml b/.golangci.yml index 2132f5403a..75452472d0 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -64,10 +64,6 @@ issues: text: 'SA1019: "golang.org/x/crypto/openpgp" is deprecated: this package is unmaintained except for security fixes.' - path: core/vm/contracts.go text: 'SA1019: "golang.org/x/crypto/ripemd160" is deprecated: RIPEMD-160 is a legacy hash and should not be used for new applications.' - - path: accounts/usbwallet/trezor.go - text: 'SA1019: "github.com/golang/protobuf/proto" is deprecated: Use the "google.golang.org/protobuf/proto" package instead.' - - path: accounts/usbwallet/trezor/ - text: 'SA1019: "github.com/golang/protobuf/proto" is deprecated: Use the "google.golang.org/protobuf/proto" package instead.' exclude: - 'SA1019: event.TypeMux is deprecated: use Feed' - 'SA1019: strings.Title is deprecated' diff --git a/Makefile b/Makefile index 857cb8c978..f4932165a4 100644 --- a/Makefile +++ b/Makefile @@ -42,7 +42,7 @@ clean: devtools: env GOBIN= go install golang.org/x/tools/cmd/stringer@latest env GOBIN= go install github.com/fjl/gencodec@latest - env GOBIN= go install github.com/golang/protobuf/protoc-gen-go@latest + env GOBIN= go install google.golang.org/protobuf/cmd/protoc-gen-go@latest env GOBIN= go install ./cmd/abigen @type "solc" 2> /dev/null || echo 'Please install solc' @type "protoc" 2> /dev/null || echo 'Please install protoc' diff --git a/SECURITY.md b/SECURITY.md index 41b900d5e9..1c99ab5950 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -171,5 +171,5 @@ i4O1UeWKs9owWttan9+PI47ozBSKOTxmMqLSQ0f56Np9FJsV0ilGxRKfjhzJ4KniOMUBA7mP epy6lH7HmxjjOR7eo0DaSxQGQpThAtFGwkWkFh8yki8j3E42kkrxvEyyYZDXn2YcI3bpqhJx PtwCMZUJ3kc/skOrs6bOI19iBNaEoNX5Dllm7UHjOgWNDQkcCuOCxucKano= =arte ------END PGP PUBLIC KEY BLOCK------ +-----END PGP PUBLIC KEY BLOCK----- ``` diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go index c8972a9dff..0504089c71 100644 --- a/accounts/abi/bind/base.go +++ b/accounts/abi/bind/base.go @@ -59,11 +59,12 @@ type TransactOpts struct { Nonce *big.Int // Nonce to use for the transaction execution (nil = use pending state) Signer SignerFn // Method to use for signing the transaction (mandatory) - Value *big.Int // Funds to transfer along the transaction (nil = 0 = no funds) - GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle) - GasFeeCap *big.Int // Gas fee cap to use for the 1559 transaction execution (nil = gas price oracle) - GasTipCap *big.Int // Gas priority fee cap to use for the 1559 transaction execution (nil = gas price oracle) - GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate) + Value *big.Int // Funds to transfer along the transaction (nil = 0 = no funds) + GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle) + GasFeeCap *big.Int // Gas fee cap to use for the 1559 transaction execution (nil = gas price oracle) + GasTipCap *big.Int // Gas priority fee cap to use for the 1559 transaction execution (nil = gas price oracle) + GasLimit uint64 // Gas limit to set for the transaction execution (0 = estimate) + AccessList types.AccessList // Access list to set for the transaction execution (nil = no access list) Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) @@ -300,20 +301,21 @@ func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Add return nil, err } baseTx := &types.DynamicFeeTx{ - To: contract, - Nonce: nonce, - GasFeeCap: gasFeeCap, - GasTipCap: gasTipCap, - Gas: gasLimit, - Value: value, - Data: input, + To: contract, + Nonce: nonce, + GasFeeCap: gasFeeCap, + GasTipCap: gasTipCap, + Gas: gasLimit, + Value: value, + Data: input, + AccessList: opts.AccessList, } return types.NewTx(baseTx), nil } func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) { - if opts.GasFeeCap != nil || opts.GasTipCap != nil { - return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet") + if opts.GasFeeCap != nil || opts.GasTipCap != nil || opts.AccessList != nil { + return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas or accessList specified but london is not active yet") } // Normalize value value := opts.Value diff --git a/accounts/keystore/account_cache_test.go b/accounts/keystore/account_cache_test.go index 41a3002248..c9a8cdfcef 100644 --- a/accounts/keystore/account_cache_test.go +++ b/accounts/keystore/account_cache_test.go @@ -114,7 +114,7 @@ func TestWatchNewFile(t *testing.T) { func TestWatchNoDir(t *testing.T) { t.Parallel() // Create ks but not the directory that it watches. - dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watchnodir-test-%d-%d", os.Getpid(), rand.Int())) + dir := filepath.Join(t.TempDir(), fmt.Sprintf("eth-keystore-watchnodir-test-%d-%d", os.Getpid(), rand.Int())) ks := NewKeyStore(dir, LightScryptN, LightScryptP) list := ks.Accounts() if len(list) > 0 { @@ -126,7 +126,6 @@ func TestWatchNoDir(t *testing.T) { } // Create the directory and copy a key file into it. os.MkdirAll(dir, 0700) - defer os.RemoveAll(dir) file := filepath.Join(dir, "aaa") if err := cp.CopyFile(file, cachetestAccounts[0].URL.Path); err != nil { t.Fatal(err) diff --git a/accounts/usbwallet/trezor.go b/accounts/usbwallet/trezor.go index 9644dc4e02..1c4270d255 100644 --- a/accounts/usbwallet/trezor.go +++ b/accounts/usbwallet/trezor.go @@ -33,7 +33,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" - "github.com/golang/protobuf/proto" + "google.golang.org/protobuf/proto" ) // ErrTrezorPINNeeded is returned if opening the trezor requires a PIN code. In diff --git a/accounts/usbwallet/trezor/trezor.go b/accounts/usbwallet/trezor/trezor.go index 4324258314..93aee3c289 100644 --- a/accounts/usbwallet/trezor/trezor.go +++ b/accounts/usbwallet/trezor/trezor.go @@ -39,8 +39,8 @@ // - Download the latest protoc https://github.com/protocolbuffers/protobuf/releases // - Build with the usual `./configure && make` and ensure it's on your $PATH // - Delete all the .proto and .pb.go files, pull in fresh ones from Trezor -// - Grab the latest Go plugin `go get -u github.com/golang/protobuf/protoc-gen-go` -// - Vendor in the latest Go plugin `govendor fetch github.com/golang/protobuf/...` +// - Grab the latest Go plugin `go get -u google.golang.org/protobuf/cmd/protoc-gen-go` +// - Vendor in the latest Go plugin `govendor fetch google.golang.org/protobuf/...` //go:generate protoc -I/usr/local/include:. --go_out=paths=source_relative:. messages.proto messages-common.proto messages-management.proto messages-ethereum.proto @@ -50,7 +50,7 @@ package trezor import ( "reflect" - "github.com/golang/protobuf/proto" + "google.golang.org/protobuf/proto" ) // Type returns the protocol buffer type number of a specific message. If the diff --git a/beacon/types/beacon_block.go b/beacon/types/beacon_block.go index 370152114a..e4cd1340e5 100644 --- a/beacon/types/beacon_block.go +++ b/beacon/types/beacon_block.go @@ -48,7 +48,7 @@ func BlockFromJSON(forkName string, data []byte) (*BeaconBlock, error) { case "capella": obj = new(capella.BeaconBlock) default: - return nil, fmt.Errorf("unsupported fork: " + forkName) + return nil, fmt.Errorf("unsupported fork: %s", forkName) } if err := json.Unmarshal(data, obj); err != nil { return nil, err diff --git a/beacon/types/exec_header.go b/beacon/types/exec_header.go index dce101ba20..b5f90bae25 100644 --- a/beacon/types/exec_header.go +++ b/beacon/types/exec_header.go @@ -46,7 +46,7 @@ func ExecutionHeaderFromJSON(forkName string, data []byte) (*ExecutionHeader, er case "deneb": obj = new(deneb.ExecutionPayloadHeader) default: - return nil, fmt.Errorf("unsupported fork: " + forkName) + return nil, fmt.Errorf("unsupported fork: %s", forkName) } if err := json.Unmarshal(data, obj); err != nil { return nil, err diff --git a/build/tools/tools.go b/build/tools/tools.go index 506e26eeff..e9e2241d2f 100644 --- a/build/tools/tools.go +++ b/build/tools/tools.go @@ -22,6 +22,6 @@ package tools import ( // Tool imports for go:generate. _ "github.com/fjl/gencodec" - _ "github.com/golang/protobuf/protoc-gen-go" _ "golang.org/x/tools/cmd/stringer" + _ "google.golang.org/protobuf/cmd/protoc-gen-go" ) diff --git a/cmd/clef/consolecmd_test.go b/cmd/clef/consolecmd_test.go index c8b37f5b92..a5b324c53f 100644 --- a/cmd/clef/consolecmd_test.go +++ b/cmd/clef/consolecmd_test.go @@ -27,9 +27,8 @@ import ( // TestImportRaw tests clef --importraw func TestImportRaw(t *testing.T) { t.Parallel() - keyPath := filepath.Join(os.TempDir(), fmt.Sprintf("%v-tempkey.test", t.Name())) + keyPath := filepath.Join(t.TempDir(), fmt.Sprintf("%v-tempkey.test", t.Name())) os.WriteFile(keyPath, []byte("0102030405060708090a0102030405060708090a0102030405060708090a0102"), 0777) - t.Cleanup(func() { os.Remove(keyPath) }) t.Run("happy-path", func(t *testing.T) { t.Parallel() @@ -68,9 +67,8 @@ func TestImportRaw(t *testing.T) { // TestListAccounts tests clef --list-accounts func TestListAccounts(t *testing.T) { t.Parallel() - keyPath := filepath.Join(os.TempDir(), fmt.Sprintf("%v-tempkey.test", t.Name())) + keyPath := filepath.Join(t.TempDir(), fmt.Sprintf("%v-tempkey.test", t.Name())) os.WriteFile(keyPath, []byte("0102030405060708090a0102030405060708090a0102030405060708090a0102"), 0777) - t.Cleanup(func() { os.Remove(keyPath) }) t.Run("no-accounts", func(t *testing.T) { t.Parallel() @@ -97,9 +95,8 @@ func TestListAccounts(t *testing.T) { // TestListWallets tests clef --list-wallets func TestListWallets(t *testing.T) { t.Parallel() - keyPath := filepath.Join(os.TempDir(), fmt.Sprintf("%v-tempkey.test", t.Name())) + keyPath := filepath.Join(t.TempDir(), fmt.Sprintf("%v-tempkey.test", t.Name())) os.WriteFile(keyPath, []byte("0102030405060708090a0102030405060708090a0102030405060708090a0102"), 0777) - t.Cleanup(func() { os.Remove(keyPath) }) t.Run("no-accounts", func(t *testing.T) { t.Parallel() diff --git a/cmd/devp2p/internal/ethtest/suite_test.go b/cmd/devp2p/internal/ethtest/suite_test.go index d70adda51f..a6fca0e524 100644 --- a/cmd/devp2p/internal/ethtest/suite_test.go +++ b/cmd/devp2p/internal/ethtest/suite_test.go @@ -34,12 +34,12 @@ import ( "github.com/ethereum/go-ethereum/p2p" ) -func makeJWTSecret() (string, [32]byte, error) { +func makeJWTSecret(t *testing.T) (string, [32]byte, error) { var secret [32]byte if _, err := crand.Read(secret[:]); err != nil { return "", secret, fmt.Errorf("failed to create jwt secret: %v", err) } - jwtPath := filepath.Join(os.TempDir(), "jwt_secret") + jwtPath := filepath.Join(t.TempDir(), "jwt_secret") if err := os.WriteFile(jwtPath, []byte(hexutil.Encode(secret[:])), 0600); err != nil { return "", secret, fmt.Errorf("failed to prepare jwt secret file: %v", err) } @@ -47,7 +47,7 @@ func makeJWTSecret() (string, [32]byte, error) { } func TestEthSuite(t *testing.T) { - jwtPath, secret, err := makeJWTSecret() + jwtPath, secret, err := makeJWTSecret(t) if err != nil { t.Fatalf("could not make jwt secret: %v", err) } @@ -75,7 +75,7 @@ func TestEthSuite(t *testing.T) { } func TestSnapSuite(t *testing.T) { - jwtPath, secret, err := makeJWTSecret() + jwtPath, secret, err := makeJWTSecret(t) if err != nil { t.Fatalf("could not make jwt secret: %v", err) } diff --git a/cmd/devp2p/rlpxcmd.go b/cmd/devp2p/rlpxcmd.go index 77f09e6b85..118731fd6c 100644 --- a/cmd/devp2p/rlpxcmd.go +++ b/cmd/devp2p/rlpxcmd.go @@ -79,7 +79,7 @@ func rlpxPing(ctx *cli.Context) error { n := getNodeArg(ctx) tcpEndpoint, ok := n.TCPEndpoint() if !ok { - return fmt.Errorf("node has no TCP endpoint") + return errors.New("node has no TCP endpoint") } fd, err := net.Dial("tcp", tcpEndpoint.String()) if err != nil { diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index f179e733e6..d06f85ed59 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -162,7 +162,6 @@ func runCmd(ctx *cli.Context) error { if ctx.String(SenderFlag.Name) != "" { sender = common.HexToAddress(ctx.String(SenderFlag.Name)) } - statedb.CreateAccount(sender) if ctx.String(ReceiverFlag.Name) != "" { receiver = common.HexToAddress(ctx.String(ReceiverFlag.Name)) diff --git a/cmd/geth/dbcmd.go b/cmd/geth/dbcmd.go index 4b683569a4..052ae0eab2 100644 --- a/cmd/geth/dbcmd.go +++ b/cmd/geth/dbcmd.go @@ -248,7 +248,8 @@ func removeDB(ctx *cli.Context) error { // Delete state data statePaths := []string{ rootDir, - filepath.Join(ancientDir, rawdb.StateFreezerName), + filepath.Join(ancientDir, rawdb.MerkleStateFreezerName), + filepath.Join(ancientDir, rawdb.VerkleStateFreezerName), } confirmAndRemoveDB(statePaths, "state data", ctx, removeStateDataFlag.Name) diff --git a/cmd/geth/exportcmd_test.go b/cmd/geth/exportcmd_test.go index 9570b1ffd2..d08c890734 100644 --- a/cmd/geth/exportcmd_test.go +++ b/cmd/geth/exportcmd_test.go @@ -28,8 +28,7 @@ import ( // TestExport does a basic test of "geth export", exporting the test-genesis. func TestExport(t *testing.T) { t.Parallel() - outfile := fmt.Sprintf("%v/testExport.out", os.TempDir()) - defer os.Remove(outfile) + outfile := fmt.Sprintf("%v/testExport.out", t.TempDir()) geth := runGeth(t, "--datadir", initGeth(t), "export", outfile) geth.WaitExit() if have, want := geth.ExitStatus(), 0; have != want { diff --git a/cmd/geth/logging_test.go b/cmd/geth/logging_test.go index f426b138bb..4293a860ec 100644 --- a/cmd/geth/logging_test.go +++ b/cmd/geth/logging_test.go @@ -201,9 +201,8 @@ func TestFileOut(t *testing.T) { var ( have, want []byte err error - path = fmt.Sprintf("%s/test_file_out-%d", os.TempDir(), rand.Int63()) + path = fmt.Sprintf("%s/test_file_out-%d", t.TempDir(), rand.Int63()) ) - t.Cleanup(func() { os.Remove(path) }) if want, err = runSelf(fmt.Sprintf("--log.file=%s", path), "logtest"); err != nil { t.Fatal(err) } @@ -222,9 +221,8 @@ func TestRotatingFileOut(t *testing.T) { var ( have, want []byte err error - path = fmt.Sprintf("%s/test_file_out-%d", os.TempDir(), rand.Int63()) + path = fmt.Sprintf("%s/test_file_out-%d", t.TempDir(), rand.Int63()) ) - t.Cleanup(func() { os.Remove(path) }) if want, err = runSelf(fmt.Sprintf("--log.file=%s", path), "--log.rotate", "logtest"); err != nil { t.Fatal(err) } diff --git a/cmd/geth/main.go b/cmd/geth/main.go index f6bb09ee54..a37bb5f987 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -360,8 +360,6 @@ func geth(ctx *cli.Context) error { // it unlocks any requested accounts, and starts the RPC/IPC interfaces and the // miner. func startNode(ctx *cli.Context, stack *node.Node, isConsole bool) { - debug.Memsize.Add("node", stack) - // Start up the node itself utils.StartNode(ctx, stack, isConsole) diff --git a/cmd/utils/export_test.go b/cmd/utils/export_test.go index c22aad64b8..b70d2451c6 100644 --- a/cmd/utils/export_test.go +++ b/cmd/utils/export_test.go @@ -29,18 +29,12 @@ import ( // TestExport does basic sanity checks on the export/import functionality func TestExport(t *testing.T) { - f := fmt.Sprintf("%v/tempdump", os.TempDir()) - defer func() { - os.Remove(f) - }() + f := fmt.Sprintf("%v/tempdump", t.TempDir()) testExport(t, f) } func TestExportGzip(t *testing.T) { - f := fmt.Sprintf("%v/tempdump.gz", os.TempDir()) - defer func() { - os.Remove(f) - }() + f := fmt.Sprintf("%v/tempdump.gz", t.TempDir()) testExport(t, f) } @@ -99,20 +93,14 @@ func testExport(t *testing.T, f string) { // TestDeletionExport tests if the deletion markers can be exported/imported correctly func TestDeletionExport(t *testing.T) { - f := fmt.Sprintf("%v/tempdump", os.TempDir()) - defer func() { - os.Remove(f) - }() + f := fmt.Sprintf("%v/tempdump", t.TempDir()) testDeletion(t, f) } // TestDeletionExportGzip tests if the deletion markers can be exported/imported // correctly with gz compression. func TestDeletionExportGzip(t *testing.T) { - f := fmt.Sprintf("%v/tempdump.gz", os.TempDir()) - defer func() { - os.Remove(f) - }() + f := fmt.Sprintf("%v/tempdump.gz", t.TempDir()) testDeletion(t, f) } @@ -171,10 +159,7 @@ func testDeletion(t *testing.T, f string) { // TestImportFutureFormat tests that we reject unsupported future versions. func TestImportFutureFormat(t *testing.T) { t.Parallel() - f := fmt.Sprintf("%v/tempdump-future", os.TempDir()) - defer func() { - os.Remove(f) - }() + f := fmt.Sprintf("%v/tempdump-future", t.TempDir()) fh, err := os.OpenFile(f, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) if err != nil { t.Fatal(err) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 46d380b984..b9fa3b8b4e 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -42,6 +42,7 @@ import ( "github.com/ethereum/go-ethereum/common/fdlimit" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/txpool/blobpool" "github.com/ethereum/go-ethereum/core/txpool/legacypool" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" @@ -291,7 +292,7 @@ var ( } BeaconApiHeaderFlag = &cli.StringSliceFlag{ Name: "beacon.api.header", - Usage: "Pass custom HTTP header fields to the emote beacon node API in \"key:value\" format. This flag can be given multiple times.", + Usage: "Pass custom HTTP header fields to the remote beacon node API in \"key:value\" format. This flag can be given multiple times.", Category: flags.BeaconCategory, } BeaconThresholdFlag = &cli.IntFlag{ @@ -1550,6 +1551,18 @@ func setTxPool(ctx *cli.Context, cfg *legacypool.Config) { } } +func setBlobPool(ctx *cli.Context, cfg *blobpool.Config) { + if ctx.IsSet(BlobPoolDataDirFlag.Name) { + cfg.Datadir = ctx.String(BlobPoolDataDirFlag.Name) + } + if ctx.IsSet(BlobPoolDataCapFlag.Name) { + cfg.Datacap = ctx.Uint64(BlobPoolDataCapFlag.Name) + } + if ctx.IsSet(BlobPoolPriceBumpFlag.Name) { + cfg.PriceBump = ctx.Uint64(BlobPoolPriceBumpFlag.Name) + } +} + func setMiner(ctx *cli.Context, cfg *miner.Config) { if ctx.Bool(MiningEnabledFlag.Name) { log.Warn("The flag --mine is deprecated and will be removed") @@ -1651,6 +1664,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { setEtherbase(ctx, cfg) setGPO(ctx, &cfg.GPO) setTxPool(ctx, &cfg.TxPool) + setBlobPool(ctx, &cfg.BlobPool) setMiner(ctx, &cfg.Miner) setRequiredBlocks(ctx, cfg) setLes(ctx, cfg) diff --git a/core/genesis_test.go b/core/genesis_test.go index ab408327d4..002e58a961 100644 --- a/core/genesis_test.go +++ b/core/genesis_test.go @@ -311,7 +311,7 @@ func TestVerkleGenesisCommit(t *testing.T) { } db := rawdb.NewMemoryDatabase() - triedb := triedb.NewDatabase(db, &triedb.Config{IsVerkle: true, PathDB: pathdb.Defaults}) + triedb := triedb.NewDatabase(db, triedb.VerkleDefaults) block := genesis.MustCommit(db, triedb) if !bytes.Equal(block.Root().Bytes(), expected) { t.Fatalf("invalid genesis state root, expected %x, got %x", expected, block.Root()) @@ -321,8 +321,8 @@ func TestVerkleGenesisCommit(t *testing.T) { if !triedb.IsVerkle() { t.Fatalf("expected trie to be verkle") } - - if !rawdb.HasAccountTrieNode(db, nil) { + vdb := rawdb.NewTable(db, string(rawdb.VerklePrefix)) + if !rawdb.HasAccountTrieNode(vdb, nil) { t.Fatal("could not find node") } } diff --git a/core/rawdb/accessors_trie.go b/core/rawdb/accessors_trie.go index 44eb715d04..0f856d1804 100644 --- a/core/rawdb/accessors_trie.go +++ b/core/rawdb/accessors_trie.go @@ -245,7 +245,7 @@ func DeleteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, has // ReadStateScheme reads the state scheme of persistent state, or none // if the state is not present in database. -func ReadStateScheme(db ethdb.Reader) string { +func ReadStateScheme(db ethdb.Database) string { // Check if state in path-based scheme is present. if HasAccountTrieNode(db, nil) { return PathScheme @@ -255,6 +255,16 @@ func ReadStateScheme(db ethdb.Reader) string { if id := ReadPersistentStateID(db); id != 0 { return PathScheme } + // Check if verkle state in path-based scheme is present. + vdb := NewTable(db, string(VerklePrefix)) + if HasAccountTrieNode(vdb, nil) { + return PathScheme + } + // The root node of verkle might be deleted during the initial snap sync, + // check the persistent state id then. + if id := ReadPersistentStateID(vdb); id != 0 { + return PathScheme + } // In a hash-based scheme, the genesis state is consistently stored // on the disk. To assess the scheme of the persistent state, it // suffices to inspect the scheme of the genesis state. diff --git a/core/rawdb/ancient_scheme.go b/core/rawdb/ancient_scheme.go index 44867ded04..371fd384ad 100644 --- a/core/rawdb/ancient_scheme.go +++ b/core/rawdb/ancient_scheme.go @@ -72,12 +72,13 @@ var stateFreezerNoSnappy = map[string]bool{ // The list of identifiers of ancient stores. var ( - ChainFreezerName = "chain" // the folder name of chain segment ancient store. - StateFreezerName = "state" // the folder name of reverse diff ancient store. + ChainFreezerName = "chain" // the folder name of chain segment ancient store. + MerkleStateFreezerName = "state" // the folder name of state history ancient store. + VerkleStateFreezerName = "state_verkle" // the folder name of state history ancient store. ) // freezers the collections of all builtin freezers. -var freezers = []string{ChainFreezerName, StateFreezerName} +var freezers = []string{ChainFreezerName, MerkleStateFreezerName, VerkleStateFreezerName} // NewStateFreezer initializes the ancient store for state history. // @@ -85,9 +86,15 @@ var freezers = []string{ChainFreezerName, StateFreezerName} // state freezer (e.g. dev mode). // - if non-empty directory is given, initializes the regular file-based // state freezer. -func NewStateFreezer(ancientDir string, readOnly bool) (ethdb.ResettableAncientStore, error) { +func NewStateFreezer(ancientDir string, verkle bool, readOnly bool) (ethdb.ResettableAncientStore, error) { if ancientDir == "" { return NewMemoryFreezer(readOnly, stateFreezerNoSnappy), nil } - return newResettableFreezer(filepath.Join(ancientDir, StateFreezerName), "eth/db/state", readOnly, stateHistoryTableSize, stateFreezerNoSnappy) + var name string + if verkle { + name = filepath.Join(ancientDir, VerkleStateFreezerName) + } else { + name = filepath.Join(ancientDir, MerkleStateFreezerName) + } + return newResettableFreezer(name, "eth/db/state", readOnly, stateHistoryTableSize, stateFreezerNoSnappy) } diff --git a/core/rawdb/ancient_utils.go b/core/rawdb/ancient_utils.go index 1c69639c9d..6804d7a91a 100644 --- a/core/rawdb/ancient_utils.go +++ b/core/rawdb/ancient_utils.go @@ -88,12 +88,12 @@ func inspectFreezers(db ethdb.Database) ([]freezerInfo, error) { } infos = append(infos, info) - case StateFreezerName: + case MerkleStateFreezerName, VerkleStateFreezerName: datadir, err := db.AncientDatadir() if err != nil { return nil, err } - f, err := NewStateFreezer(datadir, true) + f, err := NewStateFreezer(datadir, freezer == VerkleStateFreezerName, true) if err != nil { continue // might be possible the state freezer is not existent } @@ -124,7 +124,7 @@ func InspectFreezerTable(ancient string, freezerName string, tableName string, s switch freezerName { case ChainFreezerName: path, tables = resolveChainFreezerDir(ancient), chainFreezerNoSnappy - case StateFreezerName: + case MerkleStateFreezerName, VerkleStateFreezerName: path, tables = filepath.Join(ancient, freezerName), stateFreezerNoSnappy default: return fmt.Errorf("unknown freezer, supported ones: %v", freezers) diff --git a/core/rawdb/database.go b/core/rawdb/database.go index 3436958de7..831016da15 100644 --- a/core/rawdb/database.go +++ b/core/rawdb/database.go @@ -481,6 +481,10 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { beaconHeaders stat cliqueSnaps stat + // Verkle statistics + verkleTries stat + verkleStateLookups stat + // Les statistic chtTrieNodes stat bloomTrieNodes stat @@ -550,6 +554,24 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { bytes.HasPrefix(key, BloomTrieIndexPrefix) || bytes.HasPrefix(key, BloomTriePrefix): // Bloomtrie sub bloomTrieNodes.Add(size) + + // Verkle trie data is detected, determine the sub-category + case bytes.HasPrefix(key, VerklePrefix): + remain := key[len(VerklePrefix):] + switch { + case IsAccountTrieNode(remain): + verkleTries.Add(size) + case bytes.HasPrefix(remain, stateIDPrefix) && len(remain) == len(stateIDPrefix)+common.HashLength: + verkleStateLookups.Add(size) + case bytes.Equal(remain, persistentStateIDKey): + metadata.Add(size) + case bytes.Equal(remain, trieJournalKey): + metadata.Add(size) + case bytes.Equal(remain, snapSyncStatusFlagKey): + metadata.Add(size) + default: + unaccounted.Add(size) + } default: var accounted bool for _, meta := range [][]byte{ @@ -590,6 +612,8 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error { {"Key-Value store", "Path trie state lookups", stateLookups.Size(), stateLookups.Count()}, {"Key-Value store", "Path trie account nodes", accountTries.Size(), accountTries.Count()}, {"Key-Value store", "Path trie storage nodes", storageTries.Size(), storageTries.Count()}, + {"Key-Value store", "Verkle trie nodes", verkleTries.Size(), verkleTries.Count()}, + {"Key-Value store", "Verkle trie state lookups", verkleStateLookups.Size(), verkleStateLookups.Count()}, {"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()}, {"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()}, {"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()}, diff --git a/core/rawdb/freezer_meta_test.go b/core/rawdb/freezer_meta_test.go index ba1a95e453..409e811026 100644 --- a/core/rawdb/freezer_meta_test.go +++ b/core/rawdb/freezer_meta_test.go @@ -22,10 +22,11 @@ import ( ) func TestReadWriteFreezerTableMeta(t *testing.T) { - f, err := os.CreateTemp(os.TempDir(), "*") + f, err := os.CreateTemp(t.TempDir(), "*") if err != nil { t.Fatalf("Failed to create file %v", err) } + defer f.Close() err = writeMetadata(f, newMetadata(100)) if err != nil { t.Fatalf("Failed to write metadata %v", err) @@ -43,10 +44,11 @@ func TestReadWriteFreezerTableMeta(t *testing.T) { } func TestInitializeFreezerTableMeta(t *testing.T) { - f, err := os.CreateTemp(os.TempDir(), "*") + f, err := os.CreateTemp(t.TempDir(), "*") if err != nil { t.Fatalf("Failed to create file %v", err) } + defer f.Close() meta, err := loadMetadata(f, uint64(100)) if err != nil { t.Fatalf("Failed to read metadata %v", err) diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index dbf010be0c..04b5d0d6d2 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -117,6 +117,13 @@ var ( TrieNodeStoragePrefix = []byte("O") // TrieNodeStoragePrefix + accountHash + hexPath -> trie node stateIDPrefix = []byte("L") // stateIDPrefix + state root -> state id + // VerklePrefix is the database prefix for Verkle trie data, which includes: + // (a) Trie nodes + // (b) In-memory trie node journal + // (c) Persistent state ID + // (d) State ID lookups, etc. + VerklePrefix = []byte("v") + PreimagePrefix = []byte("secure-key-") // PreimagePrefix + hash -> preimage configPrefix = []byte("ethereum-config-") // config prefix for the db genesisPrefix = []byte("ethereum-genesis-") // genesis state prefix for the db diff --git a/core/rawdb/table.go b/core/rawdb/table.go index 90849fe9d9..0a25c5bcdd 100644 --- a/core/rawdb/table.go +++ b/core/rawdb/table.go @@ -200,13 +200,6 @@ func (t *table) NewBatchWithSize(size int) ethdb.Batch { return &tableBatch{t.db.NewBatchWithSize(size), t.prefix} } -// NewSnapshot creates a database snapshot based on the current state. -// The created snapshot will not be affected by all following mutations -// happened on the database. -func (t *table) NewSnapshot() (ethdb.Snapshot, error) { - return t.db.NewSnapshot() -} - // tableBatch is a wrapper around a database batch that prefixes each key access // with a pre-configured string. type tableBatch struct { diff --git a/core/state/metrics.go b/core/state/metrics.go index 7447e44dfd..e702ef3a81 100644 --- a/core/state/metrics.go +++ b/core/state/metrics.go @@ -27,10 +27,4 @@ var ( storageTriesUpdatedMeter = metrics.NewRegisteredMeter("state/update/storagenodes", nil) accountTrieDeletedMeter = metrics.NewRegisteredMeter("state/delete/accountnodes", nil) storageTriesDeletedMeter = metrics.NewRegisteredMeter("state/delete/storagenodes", nil) - - slotDeletionMaxCount = metrics.NewRegisteredGauge("state/delete/storage/max/slot", nil) - slotDeletionMaxSize = metrics.NewRegisteredGauge("state/delete/storage/max/size", nil) - slotDeletionTimer = metrics.NewRegisteredResettingTimer("state/delete/storage/timer", nil) - slotDeletionCount = metrics.NewRegisteredMeter("state/delete/storage/slot", nil) - slotDeletionSize = metrics.NewRegisteredMeter("state/delete/storage/size", nil) ) diff --git a/core/state/statedb.go b/core/state/statedb.go index ac82a8e3e3..80a53dbb17 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -471,20 +471,28 @@ func (s *StateDB) SetState(addr common.Address, key, value common.Hash) { // storage. This function should only be used for debugging and the mutations // must be discarded afterwards. func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common.Hash) { - // SetStorage needs to wipe existing storage. We achieve this by pretending - // that the account self-destructed earlier in this block, by flagging - // it in stateObjectsDestruct. The effect of doing so is that storage lookups - // will not hit disk, since it is assumed that the disk-data is belonging + // SetStorage needs to wipe the existing storage. We achieve this by marking + // the account as self-destructed in this block. The effect is that storage + // lookups will not hit the disk, as it is assumed that the disk data belongs // to a previous incarnation of the object. // - // TODO(rjl493456442) this function should only be supported by 'unwritable' - // state and all mutations made should all be discarded afterwards. - if _, ok := s.stateObjectsDestruct[addr]; !ok { - s.stateObjectsDestruct[addr] = nil + // TODO (rjl493456442): This function should only be supported by 'unwritable' + // state, and all mutations made should be discarded afterward. + obj := s.getStateObject(addr) + if obj != nil { + if _, ok := s.stateObjectsDestruct[addr]; !ok { + s.stateObjectsDestruct[addr] = obj + } } - stateObject := s.getOrNewStateObject(addr) + newObj := s.createObject(addr) for k, v := range storage { - stateObject.SetState(k, v) + newObj.SetState(k, v) + } + // Inherit the metadata of original object if it was existent + if obj != nil { + newObj.SetCode(common.BytesToHash(obj.CodeHash()), obj.code) + newObj.SetNonce(obj.Nonce()) + newObj.SetBalance(obj.Balance(), tracing.BalanceChangeUnspecified) } } @@ -860,12 +868,16 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { } obj := s.stateObjects[addr] // closure for the task runner below workers.Go(func() error { - obj.updateRoot() + if s.db.TrieDB().IsVerkle() { + obj.updateTrie() + } else { + obj.updateRoot() - // If witness building is enabled and the state object has a trie, - // gather the witnesses for its specific storage trie - if s.witness != nil && obj.trie != nil { - s.witness.AddState(obj.trie.Witness()) + // If witness building is enabled and the state object has a trie, + // gather the witnesses for its specific storage trie + if s.witness != nil && obj.trie != nil { + s.witness.AddState(obj.trie.Witness()) + } } return nil }) @@ -907,9 +919,12 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { // Now we're about to start to write changes to the trie. The trie is so far // _untouched_. We can check with the prefetcher, if it can give us a trie // which has the same root, but also has some content loaded into it. + // + // Don't check prefetcher if verkle trie has been used. In the context of verkle, + // only a single trie is used for state hashing. Replacing a non-nil verkle tree + // here could result in losing uncommitted changes from storage. start = time.Now() - - if s.prefetcher != nil { + if s.prefetcher != nil && (s.trie == nil || !s.trie.IsVerkle()) { if trie := s.prefetcher.trie(common.Hash{}, s.originalRoot); trie == nil { log.Error("Failed to retrieve account pre-fetcher trie") } else { @@ -985,76 +1000,70 @@ func (s *StateDB) clearJournalAndRefund() { // of a specific account. It leverages the associated state snapshot for fast // storage iteration and constructs trie node deletion markers by creating // stack trie with iterated slots. -func (s *StateDB) fastDeleteStorage(addrHash common.Hash, root common.Hash) (common.StorageSize, map[common.Hash][]byte, *trienode.NodeSet, error) { +func (s *StateDB) fastDeleteStorage(addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, *trienode.NodeSet, error) { iter, err := s.snaps.StorageIterator(s.originalRoot, addrHash, common.Hash{}) if err != nil { - return 0, nil, nil, err + return nil, nil, err } defer iter.Release() var ( - size common.StorageSize nodes = trienode.NewNodeSet(addrHash) slots = make(map[common.Hash][]byte) ) stack := trie.NewStackTrie(func(path []byte, hash common.Hash, blob []byte) { nodes.AddNode(path, trienode.NewDeleted()) - size += common.StorageSize(len(path)) }) for iter.Next() { slot := common.CopyBytes(iter.Slot()) if err := iter.Error(); err != nil { // error might occur after Slot function - return 0, nil, nil, err + return nil, nil, err } - size += common.StorageSize(common.HashLength + len(slot)) slots[iter.Hash()] = slot if err := stack.Update(iter.Hash().Bytes(), slot); err != nil { - return 0, nil, nil, err + return nil, nil, err } } if err := iter.Error(); err != nil { // error might occur during iteration - return 0, nil, nil, err + return nil, nil, err } if stack.Hash() != root { - return 0, nil, nil, fmt.Errorf("snapshot is not matched, exp %x, got %x", root, stack.Hash()) + return nil, nil, fmt.Errorf("snapshot is not matched, exp %x, got %x", root, stack.Hash()) } - return size, slots, nodes, nil + return slots, nodes, nil } // slowDeleteStorage serves as a less-efficient alternative to "fastDeleteStorage," // employed when the associated state snapshot is not available. It iterates the // storage slots along with all internal trie nodes via trie directly. -func (s *StateDB) slowDeleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (common.StorageSize, map[common.Hash][]byte, *trienode.NodeSet, error) { +func (s *StateDB) slowDeleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, *trienode.NodeSet, error) { tr, err := s.db.OpenStorageTrie(s.originalRoot, addr, root, s.trie) if err != nil { - return 0, nil, nil, fmt.Errorf("failed to open storage trie, err: %w", err) + return nil, nil, fmt.Errorf("failed to open storage trie, err: %w", err) } it, err := tr.NodeIterator(nil) if err != nil { - return 0, nil, nil, fmt.Errorf("failed to open storage iterator, err: %w", err) + return nil, nil, fmt.Errorf("failed to open storage iterator, err: %w", err) } var ( - size common.StorageSize nodes = trienode.NewNodeSet(addrHash) slots = make(map[common.Hash][]byte) ) for it.Next(true) { if it.Leaf() { slots[common.BytesToHash(it.LeafKey())] = common.CopyBytes(it.LeafBlob()) - size += common.StorageSize(common.HashLength + len(it.LeafBlob())) continue } if it.Hash() == (common.Hash{}) { continue } - size += common.StorageSize(len(it.Path())) nodes.AddNode(it.Path(), trienode.NewDeleted()) } if err := it.Error(); err != nil { - return 0, nil, nil, err + return nil, nil, err } - return size, slots, nodes, nil + return slots, nodes, nil } // deleteStorage is designed to delete the storage trie of a designated account. @@ -1063,9 +1072,7 @@ func (s *StateDB) slowDeleteStorage(addr common.Address, addrHash common.Hash, r // efficient approach. func (s *StateDB) deleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, *trienode.NodeSet, error) { var ( - start = time.Now() err error - size common.StorageSize slots map[common.Hash][]byte nodes *trienode.NodeSet ) @@ -1073,24 +1080,14 @@ func (s *StateDB) deleteStorage(addr common.Address, addrHash common.Hash, root // generated, or it's internally corrupted. Fallback to the slow // one just in case. if s.snap != nil { - size, slots, nodes, err = s.fastDeleteStorage(addrHash, root) + slots, nodes, err = s.fastDeleteStorage(addrHash, root) } if s.snap == nil || err != nil { - size, slots, nodes, err = s.slowDeleteStorage(addr, addrHash, root) + slots, nodes, err = s.slowDeleteStorage(addr, addrHash, root) } if err != nil { return nil, nil, err } - // Report the metrics - n := int64(len(slots)) - - slotDeletionMaxCount.UpdateIfGt(int64(len(slots))) - slotDeletionMaxSize.UpdateIfGt(int64(size)) - - slotDeletionTimer.UpdateSince(start) - slotDeletionCount.Mark(n) - slotDeletionSize.Mark(int64(size)) - return slots, nodes, nil } @@ -1169,6 +1166,10 @@ func (s *StateDB) commit(deleteEmptyObjects bool) (*stateUpdate, error) { // Finalize any pending changes and merge everything into the tries s.IntermediateRoot(deleteEmptyObjects) + // Short circuit if any error occurs within the IntermediateRoot. + if s.dbErr != nil { + return nil, fmt.Errorf("commit aborted due to database error: %v", s.dbErr) + } // Commit objects to the trie, measuring the elapsed time var ( accountTrieNodesUpdated int diff --git a/core/tracing/CHANGELOG.md b/core/tracing/CHANGELOG.md index 93b91cf479..cddc728fc0 100644 --- a/core/tracing/CHANGELOG.md +++ b/core/tracing/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to the tracing interface will be documented in this file. -## [Unreleased] +## [v1.14.3] There have been minor backwards-compatible changes to the tracing interface to explicitly mark the execution of **system** contracts. As of now the only system call updates the parent beacon block root as per [EIP-4788](https://eips.ethereum.org/EIPS/eip-4788). Other system calls are being considered for the future hardfork. @@ -76,4 +76,5 @@ The hooks `CaptureStart` and `CaptureEnd` have been removed. These hooks signale - `CaptureFault` -> `OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error)`. Similar to above. [unreleased]: https://github.com/ethereum/go-ethereum/compare/v1.14.0...master -[v1.14.0]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.0 \ No newline at end of file +[v1.14.0]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.0 +[v1.14.3]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.3 diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go index 2fca5c7b3e..cb266fd10a 100644 --- a/core/txpool/blobpool/blobpool.go +++ b/core/txpool/blobpool/blobpool.go @@ -1116,7 +1116,7 @@ func (p *BlobPool) validateTx(tx *types.Transaction) error { ExistingCost: func(addr common.Address, nonce uint64) *big.Int { next := p.state.GetNonce(addr) if uint64(len(p.index[addr])) > nonce-next { - return p.index[addr][int(tx.Nonce()-next)].costCap.ToBig() + return p.index[addr][int(nonce-next)].costCap.ToBig() } return nil }, @@ -1598,8 +1598,9 @@ func (p *BlobPool) SubscribeTransactions(ch chan<- core.NewTxsEvent, reorgs bool // Nonce returns the next nonce of an account, with all transactions executable // by the pool already applied on top. func (p *BlobPool) Nonce(addr common.Address) uint64 { - p.lock.RLock() - defer p.lock.RUnlock() + // We need a write lock here, since state.GetNonce might write the cache. + p.lock.Lock() + defer p.lock.Unlock() if txs, ok := p.index[addr]; ok { return txs[len(txs)-1].nonce + 1 diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 4e1d26acf4..5506ecc31f 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -38,6 +38,7 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" + "golang.org/x/exp/maps" ) const ( @@ -1717,7 +1718,7 @@ func (a addressesByHeartbeat) Swap(i, j int) { a[i], a[j] = a[j], a[i] } type accountSet struct { accounts map[common.Address]struct{} signer types.Signer - cache *[]common.Address + cache []common.Address } // newAccountSet creates a new address set with an associated signer for sender @@ -1765,20 +1766,14 @@ func (as *accountSet) addTx(tx *types.Transaction) { // reuse. The returned slice should not be changed! func (as *accountSet) flatten() []common.Address { if as.cache == nil { - accounts := make([]common.Address, 0, len(as.accounts)) - for account := range as.accounts { - accounts = append(accounts, account) - } - as.cache = &accounts + as.cache = maps.Keys(as.accounts) } - return *as.cache + return as.cache } // merge adds all addresses from the 'other' set into 'as'. func (as *accountSet) merge(other *accountSet) { - for addr := range other.accounts { - as.accounts[addr] = struct{}{} - } + maps.Copy(as.accounts, other.accounts) as.cache = nil } diff --git a/core/txpool/validation.go b/core/txpool/validation.go index 555b777505..7fd5f8bc79 100644 --- a/core/txpool/validation.go +++ b/core/txpool/validation.go @@ -201,7 +201,7 @@ type ValidationOptionsWithState struct { // rules without duplicating code and running the risk of missed updates. func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, opts *ValidationOptionsWithState) error { // Ensure the transaction adheres to nonce ordering - from, err := signer.Sender(tx) // already validated (and cached), but cleaner to check + from, err := types.Sender(signer, tx) // already validated (and cached), but cleaner to check if err != nil { log.Error("Transaction sender recovery failed", "err", err) return err diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index 339fee6f97..dd25f081f7 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -572,6 +572,6 @@ func deriveChainId(v *big.Int) *big.Int { } return new(big.Int).SetUint64((v - 35) / 2) } - v.Sub(v, big.NewInt(35)) - return v.Rsh(v, 1) + vCopy := new(big.Int).Sub(v, big.NewInt(35)) + return vCopy.Rsh(vCopy, 1) } diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go index 5dbf367073..eed13ee205 100644 --- a/core/types/transaction_test.go +++ b/core/types/transaction_test.go @@ -345,6 +345,41 @@ func TestTransactionCoding(t *testing.T) { } } +func TestLegacyTransaction_ConsistentV_LargeChainIds(t *testing.T) { + chainId := new(big.Int).SetUint64(13317435930671861669) + + txdata := &LegacyTx{ + Nonce: 1, + Gas: 1, + GasPrice: big.NewInt(2), + Data: []byte("abcdef"), + } + + key, err := crypto.GenerateKey() + if err != nil { + t.Fatalf("could not generate key: %v", err) + } + + tx, err := SignNewTx(key, NewEIP2930Signer(chainId), txdata) + if err != nil { + t.Fatalf("could not sign transaction: %v", err) + } + + // Make a copy of the initial V value + preV, _, _ := tx.RawSignatureValues() + preV = new(big.Int).Set(preV) + + if tx.ChainId().Cmp(chainId) != 0 { + t.Fatalf("wrong chain id: %v", tx.ChainId()) + } + + v, _, _ := tx.RawSignatureValues() + + if v.Cmp(preV) != 0 { + t.Fatalf("wrong v value: %v", v) + } +} + func encodeDecodeJSON(tx *Transaction) (*Transaction, error) { data, err := json.Marshal(tx) if err != nil { diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 01109e2851..10451af073 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -583,6 +583,86 @@ func opGas(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte return nil, nil } +func opSwap1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.swap1() + return nil, nil +} + +func opSwap2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.swap2() + return nil, nil +} + +func opSwap3(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.swap3() + return nil, nil +} + +func opSwap4(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.swap4() + return nil, nil +} + +func opSwap5(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.swap5() + return nil, nil +} + +func opSwap6(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.swap6() + return nil, nil +} + +func opSwap7(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.swap7() + return nil, nil +} + +func opSwap8(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.swap8() + return nil, nil +} + +func opSwap9(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.swap9() + return nil, nil +} + +func opSwap10(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.swap10() + return nil, nil +} + +func opSwap11(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.swap11() + return nil, nil +} + +func opSwap12(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.swap12() + return nil, nil +} + +func opSwap13(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.swap13() + return nil, nil +} + +func opSwap14(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.swap14() + return nil, nil +} + +func opSwap15(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.swap15() + return nil, nil +} + +func opSwap16(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + scope.Stack.swap16() + return nil, nil +} + func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { if interpreter.readOnly { return nil, ErrWriteProtection @@ -923,13 +1003,3 @@ func makeDup(size int64) executionFunc { return nil, nil } } - -// make swap instruction function -func makeSwap(size int64) executionFunc { - // switch n + 1 otherwise n would be swapped with n - size++ - return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.swap(int(size)) - return nil, nil - } -} diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 5624f47ba7..6b2950194d 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -892,97 +892,97 @@ func newFrontierInstructionSet() JumpTable { maxStack: maxDupStack(16), }, SWAP1: { - execute: makeSwap(1), + execute: opSwap1, constantGas: GasFastestStep, minStack: minSwapStack(2), maxStack: maxSwapStack(2), }, SWAP2: { - execute: makeSwap(2), + execute: opSwap2, constantGas: GasFastestStep, minStack: minSwapStack(3), maxStack: maxSwapStack(3), }, SWAP3: { - execute: makeSwap(3), + execute: opSwap3, constantGas: GasFastestStep, minStack: minSwapStack(4), maxStack: maxSwapStack(4), }, SWAP4: { - execute: makeSwap(4), + execute: opSwap4, constantGas: GasFastestStep, minStack: minSwapStack(5), maxStack: maxSwapStack(5), }, SWAP5: { - execute: makeSwap(5), + execute: opSwap5, constantGas: GasFastestStep, minStack: minSwapStack(6), maxStack: maxSwapStack(6), }, SWAP6: { - execute: makeSwap(6), + execute: opSwap6, constantGas: GasFastestStep, minStack: minSwapStack(7), maxStack: maxSwapStack(7), }, SWAP7: { - execute: makeSwap(7), + execute: opSwap7, constantGas: GasFastestStep, minStack: minSwapStack(8), maxStack: maxSwapStack(8), }, SWAP8: { - execute: makeSwap(8), + execute: opSwap8, constantGas: GasFastestStep, minStack: minSwapStack(9), maxStack: maxSwapStack(9), }, SWAP9: { - execute: makeSwap(9), + execute: opSwap9, constantGas: GasFastestStep, minStack: minSwapStack(10), maxStack: maxSwapStack(10), }, SWAP10: { - execute: makeSwap(10), + execute: opSwap10, constantGas: GasFastestStep, minStack: minSwapStack(11), maxStack: maxSwapStack(11), }, SWAP11: { - execute: makeSwap(11), + execute: opSwap11, constantGas: GasFastestStep, minStack: minSwapStack(12), maxStack: maxSwapStack(12), }, SWAP12: { - execute: makeSwap(12), + execute: opSwap12, constantGas: GasFastestStep, minStack: minSwapStack(13), maxStack: maxSwapStack(13), }, SWAP13: { - execute: makeSwap(13), + execute: opSwap13, constantGas: GasFastestStep, minStack: minSwapStack(14), maxStack: maxSwapStack(14), }, SWAP14: { - execute: makeSwap(14), + execute: opSwap14, constantGas: GasFastestStep, minStack: minSwapStack(15), maxStack: maxSwapStack(15), }, SWAP15: { - execute: makeSwap(15), + execute: opSwap15, constantGas: GasFastestStep, minStack: minSwapStack(16), maxStack: maxSwapStack(16), }, SWAP16: { - execute: makeSwap(16), + execute: opSwap16, constantGas: GasFastestStep, minStack: minSwapStack(17), maxStack: maxSwapStack(17), diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 0c6df9992c..f56d4a7452 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -40,7 +40,6 @@ import ( // force-load js tracers to trigger registration _ "github.com/ethereum/go-ethereum/eth/tracers/js" - "github.com/holiman/uint256" ) func TestDefaults(t *testing.T) { @@ -215,6 +214,35 @@ func BenchmarkEVM_CREATE2_1200(bench *testing.B) { benchmarkEVM_Create(bench, "5b5862124f80600080f5600152600056") } +func BenchmarkEVM_SWAP1(b *testing.B) { + // returns a contract that does n swaps (SWAP1) + swapContract := func(n uint64) []byte { + contract := []byte{ + byte(vm.PUSH0), // PUSH0 + byte(vm.PUSH0), // PUSH0 + } + for i := uint64(0); i < n; i++ { + contract = append(contract, byte(vm.SWAP1)) + } + return contract + } + + state, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + contractAddr := common.BytesToAddress([]byte("contract")) + + b.Run("10k", func(b *testing.B) { + contractCode := swapContract(10_000) + state.SetCode(contractAddr, contractCode) + + for i := 0; i < b.N; i++ { + _, _, err := Call(contractAddr, []byte{}, &Config{State: state}) + if err != nil { + b.Fatal(err) + } + } + }) +} + func BenchmarkEVM_RETURN(b *testing.B) { // returns a contract that returns a zero-byte slice of len size returnContract := func(size uint64) []byte { @@ -376,11 +404,7 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode Tracer: tracer.Hooks, } } - var ( - destination = common.BytesToAddress([]byte("contract")) - vmenv = NewEnv(cfg) - sender = vm.AccountRef(cfg.Origin) - ) + destination := common.BytesToAddress([]byte("contract")) cfg.State.CreateAccount(destination) eoa := common.HexToAddress("E0") { @@ -400,12 +424,12 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode //cfg.State.CreateAccount(cfg.Origin) // set the receiver's (the executing contract) code for execution. cfg.State.SetCode(destination, code) - vmenv.Call(sender, destination, nil, gas, uint256.MustFromBig(cfg.Value)) + Call(destination, nil, cfg) b.Run(name, func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - vmenv.Call(sender, destination, nil, gas, uint256.MustFromBig(cfg.Value)) + Call(destination, nil, cfg) } }) } diff --git a/core/vm/stack.go b/core/vm/stack.go index e1a957e244..879dc9aa6d 100644 --- a/core/vm/stack.go +++ b/core/vm/stack.go @@ -30,7 +30,7 @@ var stackPool = sync.Pool{ // Stack is an object for basic stack operations. Items popped to the stack are // expected to be changed and modified. stack does not take care of adding newly -// initialised objects. +// initialized objects. type Stack struct { data []uint256.Int } @@ -64,8 +64,53 @@ func (st *Stack) len() int { return len(st.data) } -func (st *Stack) swap(n int) { - st.data[st.len()-n], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-n] +func (st *Stack) swap1() { + st.data[st.len()-2], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-2] +} +func (st *Stack) swap2() { + st.data[st.len()-3], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-3] +} +func (st *Stack) swap3() { + st.data[st.len()-4], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-4] +} +func (st *Stack) swap4() { + st.data[st.len()-5], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-5] +} +func (st *Stack) swap5() { + st.data[st.len()-6], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-6] +} +func (st *Stack) swap6() { + st.data[st.len()-7], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-7] +} +func (st *Stack) swap7() { + st.data[st.len()-8], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-8] +} +func (st *Stack) swap8() { + st.data[st.len()-9], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-9] +} +func (st *Stack) swap9() { + st.data[st.len()-10], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-10] +} +func (st *Stack) swap10() { + st.data[st.len()-11], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-11] +} +func (st *Stack) swap11() { + st.data[st.len()-12], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-12] +} +func (st *Stack) swap12() { + st.data[st.len()-13], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-13] +} +func (st *Stack) swap13() { + st.data[st.len()-14], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-14] +} +func (st *Stack) swap14() { + st.data[st.len()-15], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-15] +} +func (st *Stack) swap15() { + st.data[st.len()-16], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-16] +} +func (st *Stack) swap16() { + st.data[st.len()-17], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-17] } func (st *Stack) dup(n int) { diff --git a/crypto/signature_nocgo.go b/crypto/signature_nocgo.go index 989057442b..5ac3765c71 100644 --- a/crypto/signature_nocgo.go +++ b/crypto/signature_nocgo.go @@ -88,10 +88,7 @@ func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) { return nil, errors.New("invalid private key") } defer priv.Zero() - sig, err := btc_ecdsa.SignCompact(&priv, hash, false) // ref uncompressed pubkey - if err != nil { - return nil, err - } + sig := btc_ecdsa.SignCompact(&priv, hash, false) // ref uncompressed pubkey // Convert to Ethereum signature format with 'recovery id' v at the end. v := sig[0] - 27 copy(sig, sig[1:]) diff --git a/eth/backend.go b/eth/backend.go index 91a07811f0..8679018dab 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -264,11 +264,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if eth.APIBackend.allowUnprotectedTxs { log.Info("Unprotected transactions allowed") } - gpoParams := config.GPO - if gpoParams.Default == nil { - gpoParams.Default = config.Miner.GasPrice - } - eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, gpoParams) + eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, config.GPO, config.Miner.GasPrice) // Setup DNS discovery iterators. dnsclient := dnsdisc.NewClient(dnsdisc.Config{}) diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 00cce259c8..5a001e1ee8 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -546,7 +546,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe bgu = strconv.Itoa(int(*params.BlobGasUsed)) } ebg := "nil" - if params.BlobGasUsed != nil { + if params.ExcessBlobGas != nil { ebg = strconv.Itoa(int(*params.ExcessBlobGas)) } log.Warn("Invalid NewPayload params", diff --git a/eth/catalyst/simulated_beacon.go b/eth/catalyst/simulated_beacon.go index 2d6569e422..8bdf94b80e 100644 --- a/eth/catalyst/simulated_beacon.go +++ b/eth/catalyst/simulated_beacon.go @@ -302,7 +302,7 @@ func (c *SimulatedBeacon) AdjustTime(adjustment time.Duration) error { return errors.New("parent not found") } withdrawals := c.withdrawals.gatherPending(10) - return c.sealBlock(withdrawals, parent.Time+uint64(adjustment)) + return c.sealBlock(withdrawals, parent.Time+uint64(adjustment/time.Second)) } func RegisterSimulatedBeaconAPIs(stack *node.Node, sim *SimulatedBeacon) { diff --git a/eth/downloader/beaconsync.go b/eth/downloader/beaconsync.go index 57c6eee40a..e682536e07 100644 --- a/eth/downloader/beaconsync.go +++ b/eth/downloader/beaconsync.go @@ -123,7 +123,8 @@ func (b *beaconBackfiller) resume() { func (b *beaconBackfiller) setMode(mode SyncMode) { // Update the old sync mode and track if it was changed b.lock.Lock() - updated := b.syncMode != mode + oldMode := b.syncMode + updated := oldMode != mode filling := b.filling b.syncMode = mode b.lock.Unlock() @@ -133,7 +134,7 @@ func (b *beaconBackfiller) setMode(mode SyncMode) { if !updated || !filling { return } - log.Error("Downloader sync mode changed mid-run", "old", mode.String(), "new", mode.String()) + log.Error("Downloader sync mode changed mid-run", "old", oldMode.String(), "new", mode.String()) b.suspend() b.resume() } diff --git a/eth/gasprice/feehistory_test.go b/eth/gasprice/feehistory_test.go index 3d426db46f..241b91b810 100644 --- a/eth/gasprice/feehistory_test.go +++ b/eth/gasprice/feehistory_test.go @@ -59,7 +59,7 @@ func TestFeeHistory(t *testing.T) { MaxBlockHistory: c.maxBlock, } backend := newTestBackend(t, big.NewInt(16), big.NewInt(28), c.pending) - oracle := NewOracle(backend, config) + oracle := NewOracle(backend, config, nil) first, reward, baseFee, ratio, blobBaseFee, blobRatio, err := oracle.FeeHistory(context.Background(), c.count, c.last, c.percent) backend.teardown() diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index c90408e363..19a6c0010a 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -45,7 +45,6 @@ type Config struct { Percentile int MaxHeaderHistory uint64 MaxBlockHistory uint64 - Default *big.Int `toml:",omitempty"` MaxPrice *big.Int `toml:",omitempty"` IgnorePrice *big.Int `toml:",omitempty"` } @@ -79,7 +78,7 @@ type Oracle struct { // NewOracle returns a new gasprice oracle which can recommend suitable // gasprice for newly created transaction. -func NewOracle(backend OracleBackend, params Config) *Oracle { +func NewOracle(backend OracleBackend, params Config, startPrice *big.Int) *Oracle { blocks := params.Blocks if blocks < 1 { blocks = 1 @@ -115,6 +114,9 @@ func NewOracle(backend OracleBackend, params Config) *Oracle { maxBlockHistory = 1 log.Warn("Sanitizing invalid gasprice oracle max block history", "provided", params.MaxBlockHistory, "updated", maxBlockHistory) } + if startPrice == nil { + startPrice = new(big.Int) + } cache := lru.NewCache[cacheKey, processedFees](2048) headEvent := make(chan core.ChainHeadEvent, 1) @@ -131,7 +133,7 @@ func NewOracle(backend OracleBackend, params Config) *Oracle { return &Oracle{ backend: backend, - lastPrice: params.Default, + lastPrice: startPrice, maxPrice: maxPrice, ignorePrice: ignorePrice, checkBlocks: blocks, diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index b22e75666f..39f3c79b98 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -235,7 +235,6 @@ func TestSuggestTipCap(t *testing.T) { config := Config{ Blocks: 3, Percentile: 60, - Default: big.NewInt(params.GWei), } var cases = []struct { fork *big.Int // London fork number @@ -249,7 +248,7 @@ func TestSuggestTipCap(t *testing.T) { } for _, c := range cases { backend := newTestBackend(t, c.fork, nil, false) - oracle := NewOracle(backend, config) + oracle := NewOracle(backend, config, big.NewInt(params.GWei)) // The gas price sampled is: 32G, 31G, 30G, 29G, 28G, 27G got, err := oracle.SuggestTipCap(context.Background()) diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index 6fbb50848d..e717f5352d 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -843,7 +843,7 @@ func TestTracingWithOverrides(t *testing.T) { byte(vm.PUSH1), 00, byte(vm.RETURN), }), - StateDiff: &map[common.Hash]common.Hash{ + StateDiff: map[common.Hash]common.Hash{ common.HexToHash("0x03"): common.HexToHash("0x11"), }, }, @@ -898,9 +898,9 @@ func newAccounts(n int) (accounts []Account) { return accounts } -func newRPCBalance(balance *big.Int) **hexutil.Big { +func newRPCBalance(balance *big.Int) *hexutil.Big { rpcBalance := (*hexutil.Big)(balance) - return &rpcBalance + return rpcBalance } func newRPCBytes(bytes []byte) *hexutil.Bytes { @@ -908,7 +908,7 @@ func newRPCBytes(bytes []byte) *hexutil.Bytes { return &rpcBytes } -func newStates(keys []common.Hash, vals []common.Hash) *map[common.Hash]common.Hash { +func newStates(keys []common.Hash, vals []common.Hash) map[common.Hash]common.Hash { if len(keys) != len(vals) { panic("invalid input") } @@ -916,7 +916,7 @@ func newStates(keys []common.Hash, vals []common.Hash) *map[common.Hash]common.H for i := 0; i < len(keys); i++ { m[keys[i]] = vals[i] } - return &m + return m } func TestTraceChain(t *testing.T) { diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 390f085677..f769ee8475 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -359,7 +359,7 @@ func (ec *Client) NetworkID(ctx context.Context) (*big.Int, error) { if err := ec.c.CallContext(ctx, &ver, "net_version"); err != nil { return nil, err } - if _, ok := version.SetString(ver, 10); !ok { + if _, ok := version.SetString(ver, 0); !ok { return nil, fmt.Errorf("invalid net_version result %q", ver) } return version, nil diff --git a/ethclient/simulated/backend_test.go b/ethclient/simulated/backend_test.go index a8fd7913c3..b70086b385 100644 --- a/ethclient/simulated/backend_test.go +++ b/ethclient/simulated/backend_test.go @@ -106,7 +106,7 @@ func TestAdjustTime(t *testing.T) { block2, _ := client.BlockByNumber(context.Background(), nil) prevTime := block1.Time() newTime := block2.Time() - if newTime-prevTime != uint64(time.Minute) { + if newTime-prevTime != 60 { t.Errorf("adjusted time not equal to 60 seconds. prev: %v, new: %v", prevTime, newTime) } } diff --git a/ethdb/database.go b/ethdb/database.go index 9ef5942a13..89c793d0be 100644 --- a/ethdb/database.go +++ b/ethdb/database.go @@ -64,7 +64,6 @@ type KeyValueStore interface { Batcher Iteratee Compacter - Snapshotter io.Closer } @@ -199,6 +198,5 @@ type Database interface { Iteratee Stater Compacter - Snapshotter io.Closer } diff --git a/ethdb/dbtest/testsuite.go b/ethdb/dbtest/testsuite.go index 29a773ced4..1af55a0e38 100644 --- a/ethdb/dbtest/testsuite.go +++ b/ethdb/dbtest/testsuite.go @@ -318,69 +318,6 @@ func TestDatabaseSuite(t *testing.T, New func() ethdb.KeyValueStore) { } }) - t.Run("Snapshot", func(t *testing.T) { - db := New() - defer db.Close() - - initial := map[string]string{ - "k1": "v1", "k2": "v2", "k3": "", "k4": "", - } - for k, v := range initial { - db.Put([]byte(k), []byte(v)) - } - snapshot, err := db.NewSnapshot() - if err != nil { - t.Fatal(err) - } - for k, v := range initial { - got, err := snapshot.Get([]byte(k)) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(got, []byte(v)) { - t.Fatalf("Unexpected value want: %v, got %v", v, got) - } - } - - // Flush more modifications into the database, ensure the snapshot - // isn't affected. - var ( - update = map[string]string{"k1": "v1-b", "k3": "v3-b"} - insert = map[string]string{"k5": "v5-b"} - delete = map[string]string{"k2": ""} - ) - for k, v := range update { - db.Put([]byte(k), []byte(v)) - } - for k, v := range insert { - db.Put([]byte(k), []byte(v)) - } - for k := range delete { - db.Delete([]byte(k)) - } - for k, v := range initial { - got, err := snapshot.Get([]byte(k)) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(got, []byte(v)) { - t.Fatalf("Unexpected value want: %v, got %v", v, got) - } - } - for k := range insert { - got, err := snapshot.Get([]byte(k)) - if err == nil || len(got) != 0 { - t.Fatal("Unexpected value") - } - } - for k := range delete { - got, err := snapshot.Get([]byte(k)) - if err != nil || len(got) == 0 { - t.Fatal("Unexpected deletion") - } - } - }) - t.Run("OperationsAfterClose", func(t *testing.T) { db := New() db.Put([]byte("key"), []byte("value")) diff --git a/ethdb/leveldb/leveldb.go b/ethdb/leveldb/leveldb.go index 92838ad7ab..24925a4f04 100644 --- a/ethdb/leveldb/leveldb.go +++ b/ethdb/leveldb/leveldb.go @@ -230,19 +230,6 @@ func (db *Database) NewIterator(prefix []byte, start []byte) ethdb.Iterator { return db.db.NewIterator(bytesPrefixRange(prefix, start), nil) } -// NewSnapshot creates a database snapshot based on the current state. -// The created snapshot will not be affected by all following mutations -// happened on the database. -// Note don't forget to release the snapshot once it's used up, otherwise -// the stale data will never be cleaned up by the underlying compactor. -func (db *Database) NewSnapshot() (ethdb.Snapshot, error) { - snap, err := db.db.GetSnapshot() - if err != nil { - return nil, err - } - return &snapshot{db: snap}, nil -} - // Stat returns the statistic data of the database. func (db *Database) Stat() (string, error) { var stats leveldb.DBStats @@ -498,26 +485,3 @@ func bytesPrefixRange(prefix, start []byte) *util.Range { r.Start = append(r.Start, start...) return r } - -// snapshot wraps a leveldb snapshot for implementing the Snapshot interface. -type snapshot struct { - db *leveldb.Snapshot -} - -// Has retrieves if a key is present in the snapshot backing by a key-value -// data store. -func (snap *snapshot) Has(key []byte) (bool, error) { - return snap.db.Has(key, nil) -} - -// Get retrieves the given key if it's present in the snapshot backing by -// key-value data store. -func (snap *snapshot) Get(key []byte) ([]byte, error) { - return snap.db.Get(key, nil) -} - -// Release releases associated resources. Release should always succeed and can -// be called multiple times without causing error. -func (snap *snapshot) Release() { - snap.db.Release() -} diff --git a/ethdb/memorydb/memorydb.go b/ethdb/memorydb/memorydb.go index 9b0872f89a..532e0dfe3f 100644 --- a/ethdb/memorydb/memorydb.go +++ b/ethdb/memorydb/memorydb.go @@ -35,10 +35,6 @@ var ( // errMemorydbNotFound is returned if a key is requested that is not found in // the provided memory database. errMemorydbNotFound = errors.New("not found") - - // errSnapshotReleased is returned if callers want to retrieve data from a - // released snapshot. - errSnapshotReleased = errors.New("snapshot released") ) // Database is an ephemeral key-value store. Apart from basic data storage @@ -175,13 +171,6 @@ func (db *Database) NewIterator(prefix []byte, start []byte) ethdb.Iterator { } } -// NewSnapshot creates a database snapshot based on the current state. -// The created snapshot will not be affected by all following mutations -// happened on the database. -func (db *Database) NewSnapshot() (ethdb.Snapshot, error) { - return newSnapshot(db), nil -} - // Stat returns the statistic data of the database. func (db *Database) Stat() (string, error) { return "", nil @@ -332,59 +321,3 @@ func (it *iterator) Value() []byte { func (it *iterator) Release() { it.index, it.keys, it.values = -1, nil, nil } - -// snapshot wraps a batch of key-value entries deep copied from the in-memory -// database for implementing the Snapshot interface. -type snapshot struct { - db map[string][]byte - lock sync.RWMutex -} - -// newSnapshot initializes the snapshot with the given database instance. -func newSnapshot(db *Database) *snapshot { - db.lock.RLock() - defer db.lock.RUnlock() - - copied := make(map[string][]byte, len(db.db)) - for key, val := range db.db { - copied[key] = common.CopyBytes(val) - } - return &snapshot{db: copied} -} - -// Has retrieves if a key is present in the snapshot backing by a key-value -// data store. -func (snap *snapshot) Has(key []byte) (bool, error) { - snap.lock.RLock() - defer snap.lock.RUnlock() - - if snap.db == nil { - return false, errSnapshotReleased - } - _, ok := snap.db[string(key)] - return ok, nil -} - -// Get retrieves the given key if it's present in the snapshot backing by -// key-value data store. -func (snap *snapshot) Get(key []byte) ([]byte, error) { - snap.lock.RLock() - defer snap.lock.RUnlock() - - if snap.db == nil { - return nil, errSnapshotReleased - } - if entry, ok := snap.db[string(key)]; ok { - return common.CopyBytes(entry), nil - } - return nil, errMemorydbNotFound -} - -// Release releases associated resources. Release should always succeed and can -// be called multiple times without causing error. -func (snap *snapshot) Release() { - snap.lock.Lock() - defer snap.lock.Unlock() - - snap.db = nil -} diff --git a/ethdb/pebble/pebble.go b/ethdb/pebble/pebble.go index 130d6617b5..8203dd136d 100644 --- a/ethdb/pebble/pebble.go +++ b/ethdb/pebble/pebble.go @@ -351,55 +351,6 @@ func (d *Database) NewBatchWithSize(size int) ethdb.Batch { } } -// snapshot wraps a pebble snapshot for implementing the Snapshot interface. -type snapshot struct { - db *pebble.Snapshot -} - -// NewSnapshot creates a database snapshot based on the current state. -// The created snapshot will not be affected by all following mutations -// happened on the database. -// Note don't forget to release the snapshot once it's used up, otherwise -// the stale data will never be cleaned up by the underlying compactor. -func (d *Database) NewSnapshot() (ethdb.Snapshot, error) { - snap := d.db.NewSnapshot() - return &snapshot{db: snap}, nil -} - -// Has retrieves if a key is present in the snapshot backing by a key-value -// data store. -func (snap *snapshot) Has(key []byte) (bool, error) { - _, closer, err := snap.db.Get(key) - if err != nil { - if err != pebble.ErrNotFound { - return false, err - } else { - return false, nil - } - } - closer.Close() - return true, nil -} - -// Get retrieves the given key if it's present in the snapshot backing by -// key-value data store. -func (snap *snapshot) Get(key []byte) ([]byte, error) { - dat, closer, err := snap.db.Get(key) - if err != nil { - return nil, err - } - ret := make([]byte, len(dat)) - copy(ret, dat) - closer.Close() - return ret, nil -} - -// Release releases associated resources. Release should always succeed and can -// be called multiple times without causing error. -func (snap *snapshot) Release() { - snap.db.Close() -} - // upperBound returns the upper bound for the given prefix func upperBound(prefix []byte) (limit []byte) { for i := len(prefix) - 1; i >= 0; i-- { diff --git a/ethdb/remotedb/remotedb.go b/ethdb/remotedb/remotedb.go index bfecde4a32..c8b76eab4a 100644 --- a/ethdb/remotedb/remotedb.go +++ b/ethdb/remotedb/remotedb.go @@ -138,10 +138,6 @@ func (db *Database) Compact(start []byte, limit []byte) error { return nil } -func (db *Database) NewSnapshot() (ethdb.Snapshot, error) { - panic("not supported") -} - func (db *Database) Close() error { db.remote.Close() return nil diff --git a/ethdb/snapshot.go b/ethdb/snapshot.go deleted file mode 100644 index 03b7794a77..0000000000 --- a/ethdb/snapshot.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2022 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package ethdb - -type Snapshot interface { - // Has retrieves if a key is present in the snapshot backing by a key-value - // data store. - Has(key []byte) (bool, error) - - // Get retrieves the given key if it's present in the snapshot backing by - // key-value data store. - Get(key []byte) ([]byte, error) - - // Release releases associated resources. Release should always succeed and can - // be called multiple times without causing error. - Release() -} - -// Snapshotter wraps the Snapshot method of a backing data store. -type Snapshotter interface { - // NewSnapshot creates a database snapshot based on the current state. - // The created snapshot will not be affected by all following mutations - // happened on the database. - // Note don't forget to release the snapshot once it's used up, otherwise - // the stale data will never be cleaned up by the underlying compactor. - NewSnapshot() (Snapshot, error) -} diff --git a/go.mod b/go.mod index a757b6705d..3e9837186d 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/aws/aws-sdk-go-v2/config v1.18.45 github.com/aws/aws-sdk-go-v2/credentials v1.13.43 github.com/aws/aws-sdk-go-v2/service/route53 v1.30.2 - github.com/btcsuite/btcd/btcec/v2 v2.2.0 + github.com/btcsuite/btcd/btcec/v2 v2.3.4 github.com/cespare/cp v0.1.0 github.com/cloudflare/cloudflare-go v0.79.0 github.com/cockroachdb/pebble v1.1.1 @@ -26,12 +26,10 @@ require ( github.com/fatih/color v1.16.0 github.com/ferranbt/fastssz v0.1.2 github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e - github.com/fjl/memsize v0.0.2 github.com/fsnotify/fsnotify v1.6.0 github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff github.com/gofrs/flock v0.8.1 github.com/golang-jwt/jwt/v4 v4.5.0 - github.com/golang/protobuf v1.5.4 github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb github.com/google/gofuzz v1.2.0 github.com/google/uuid v1.3.0 @@ -74,7 +72,7 @@ require ( golang.org/x/text v0.14.0 golang.org/x/time v0.5.0 golang.org/x/tools v0.20.0 - google.golang.org/protobuf v1.33.0 + google.golang.org/protobuf v1.34.2 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v3 v3.0.1 ) @@ -112,6 +110,7 @@ require ( github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect diff --git a/go.sum b/go.sum index 826f0f674f..d8e7a540c7 100644 --- a/go.sum +++ b/go.sum @@ -92,8 +92,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= -github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= +github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= +github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -178,8 +178,6 @@ github.com/ferranbt/fastssz v0.1.2 h1:Dky6dXlngF6Qjc+EfDipAkE83N5I5DE68bY6O0VLNP github.com/ferranbt/fastssz v0.1.2/go.mod h1:X5UPrE2u1UJjxHA8X54u04SBwdAQjG2sFtWs39YxyWs= github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e h1:bBLctRc7kr01YGvaDfgLbTwjFNW5jdp5y5rj8XXBHfY= github.com/fjl/gencodec v0.0.0-20230517082657-f9840df7b83e/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY= -github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA= -github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= @@ -842,6 +840,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/debug/flags.go b/internal/debug/flags.go index 19222c8325..0e05975c7e 100644 --- a/internal/debug/flags.go +++ b/internal/debug/flags.go @@ -31,15 +31,12 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics/exp" - "github.com/fjl/memsize/memsizeui" "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" "github.com/urfave/cli/v2" "gopkg.in/natefinch/lumberjack.v2" ) -var Memsize memsizeui.Handler - var ( verbosityFlag = &cli.IntFlag{ Name: "verbosity", @@ -313,7 +310,6 @@ func StartPProf(address string, withMetrics bool) { if withMetrics { exp.Exp(metrics.DefaultRegistry) } - http.Handle("/memsize/", http.StripPrefix("/memsize", &Memsize)) log.Info("Starting pprof server", "addr", fmt.Sprintf("http://%s/debug/pprof", address)) go func() { if err := http.ListenAndServe(address, nil); err != nil { diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 0ecedf1130..1c3cb4adf9 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -968,11 +968,11 @@ func (api *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rp // if stateDiff is set, all diff will be applied first and then execute the call // message. type OverrideAccount struct { - Nonce *hexutil.Uint64 `json:"nonce"` - Code *hexutil.Bytes `json:"code"` - Balance **hexutil.Big `json:"balance"` - State *map[common.Hash]common.Hash `json:"state"` - StateDiff *map[common.Hash]common.Hash `json:"stateDiff"` + Nonce *hexutil.Uint64 `json:"nonce"` + Code *hexutil.Bytes `json:"code"` + Balance *hexutil.Big `json:"balance"` + State map[common.Hash]common.Hash `json:"state"` + StateDiff map[common.Hash]common.Hash `json:"stateDiff"` } // StateOverride is the collection of overridden accounts. @@ -994,7 +994,7 @@ func (diff *StateOverride) Apply(statedb *state.StateDB) error { } // Override account balance. if account.Balance != nil { - u256Balance, _ := uint256.FromBig((*big.Int)(*account.Balance)) + u256Balance, _ := uint256.FromBig((*big.Int)(account.Balance)) statedb.SetBalance(addr, u256Balance, tracing.BalanceChangeUnspecified) } if account.State != nil && account.StateDiff != nil { @@ -1002,11 +1002,11 @@ func (diff *StateOverride) Apply(statedb *state.StateDB) error { } // Replace entire state if caller requires. if account.State != nil { - statedb.SetStorage(addr, *account.State) + statedb.SetStorage(addr, account.State) } // Apply state diff into specified accounts. if account.StateDiff != nil { - for key, value := range *account.StateDiff { + for key, value := range account.StateDiff { statedb.SetState(addr, key, value) } } diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index cf5160caf7..7465fb5529 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -781,15 +781,24 @@ func TestEstimateGas(t *testing.T) { func TestCall(t *testing.T) { t.Parallel() + // Initialize test accounts var ( accounts = newAccounts(3) + dad = common.HexToAddress("0x0000000000000000000000000000000000000dad") genesis = &core.Genesis{ Config: params.MergedTestChainConfig, Alloc: types.GenesisAlloc{ accounts[0].addr: {Balance: big.NewInt(params.Ether)}, accounts[1].addr: {Balance: big.NewInt(params.Ether)}, accounts[2].addr: {Balance: big.NewInt(params.Ether)}, + dad: { + Balance: big.NewInt(params.Ether), + Nonce: 1, + Storage: map[common.Hash]common.Hash{ + common.Hash{}: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000001"), + }, + }, }, } genBlocks = 10 @@ -904,7 +913,7 @@ func TestCall(t *testing.T) { overrides: StateOverride{ randomAccounts[2].addr: OverrideAccount{ Code: hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c80638381f58a14602d575b600080fd5b60336049565b6040518082815260200191505060405180910390f35b6000548156fea2646970667358221220eab35ffa6ab2adfe380772a48b8ba78e82a1b820a18fcb6f59aa4efb20a5f60064736f6c63430007040033"), - StateDiff: &map[common.Hash]common.Hash{{}: common.BigToHash(big.NewInt(123))}, + StateDiff: map[common.Hash]common.Hash{{}: common.BigToHash(big.NewInt(123))}, }, }, want: "0x000000000000000000000000000000000000000000000000000000000000007b", @@ -949,6 +958,32 @@ func TestCall(t *testing.T) { }, want: "0x0122000000000000000000000000000000000000000000000000000000000000", }, + // Clear the entire storage set + { + blockNumber: rpc.LatestBlockNumber, + call: TransactionArgs{ + From: &accounts[1].addr, + // Yul: + // object "Test" { + // code { + // let dad := 0x0000000000000000000000000000000000000dad + // if eq(balance(dad), 0) { + // revert(0, 0) + // } + // let slot := sload(0) + // mstore(0, slot) + // return(0, 32) + // } + // } + Input: hex2Bytes("610dad6000813103600f57600080fd5b6000548060005260206000f3"), + }, + overrides: StateOverride{ + dad: OverrideAccount{ + State: map[common.Hash]common.Hash{}, + }, + }, + want: "0x0000000000000000000000000000000000000000000000000000000000000000", + }, } for i, tc := range testSuite { result, err := api.Call(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides, &tc.blockOverrides) @@ -1308,9 +1343,9 @@ func newAccounts(n int) (accounts []account) { return accounts } -func newRPCBalance(balance *big.Int) **hexutil.Big { +func newRPCBalance(balance *big.Int) *hexutil.Big { rpcBalance := (*hexutil.Big)(balance) - return &rpcBalance + return rpcBalance } func hex2Bytes(str string) *hexutil.Bytes { diff --git a/miner/worker.go b/miner/worker.go index 5dc3e2056b..9aae6e1609 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -205,8 +205,7 @@ func (miner *Miner) prepareWork(genParams *generateParams) (*environment, error) // makeEnv creates a new environment for the sealing block. func (miner *Miner) makeEnv(parent *types.Header, header *types.Header, coinbase common.Address) (*environment, error) { - // Retrieve the parent state to execute on top and start a prefetcher for - // the miner to speed block sealing up a bit. + // Retrieve the parent state to execute on top. state, err := miner.chain.StateAt(parent.Root) if err != nil { return nil, err diff --git a/p2p/discover/node.go b/p2p/discover/node.go index 042619221b..ac34b7c5b2 100644 --- a/p2p/discover/node.go +++ b/p2p/discover/node.go @@ -17,16 +17,10 @@ package discover import ( - "crypto/ecdsa" - "crypto/elliptic" - "errors" - "math/big" "slices" "sort" "time" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/p2p/enode" ) @@ -48,33 +42,6 @@ type tableNode struct { isValidatedLive bool // true if existence of node is considered validated right now } -type encPubkey [64]byte - -func encodePubkey(key *ecdsa.PublicKey) encPubkey { - var e encPubkey - math.ReadBits(key.X, e[:len(e)/2]) - math.ReadBits(key.Y, e[len(e)/2:]) - return e -} - -func decodePubkey(curve elliptic.Curve, e []byte) (*ecdsa.PublicKey, error) { - if len(e) != len(encPubkey{}) { - return nil, errors.New("wrong size public key data") - } - p := &ecdsa.PublicKey{Curve: curve, X: new(big.Int), Y: new(big.Int)} - half := len(e) / 2 - p.X.SetBytes(e[:half]) - p.Y.SetBytes(e[half:]) - if !p.Curve.IsOnCurve(p.X, p.Y) { - return nil, errors.New("invalid curve point") - } - return p, nil -} - -func (e encPubkey) id() enode.ID { - return enode.ID(crypto.Keccak256Hash(e[:])) -} - func unwrapNodes(ns []*tableNode) []*enode.Node { result := make([]*enode.Node, len(ns)) for i, n := range ns { diff --git a/p2p/discover/table_reval.go b/p2p/discover/table_reval.go index f2ea8b34fa..2465fee906 100644 --- a/p2p/discover/table_reval.go +++ b/p2p/discover/table_reval.go @@ -77,14 +77,18 @@ func (tr *tableRevalidation) nodeEndpointChanged(tab *Table, n *tableNode) { // It returns the next time it should be invoked, which is used in the Table main loop // to schedule a timer. However, run can be called at any time. func (tr *tableRevalidation) run(tab *Table, now mclock.AbsTime) (nextTime mclock.AbsTime) { - if n := tr.fast.get(now, &tab.rand, tr.activeReq); n != nil { - tr.startRequest(tab, n) - tr.fast.schedule(now, &tab.rand) - } - if n := tr.slow.get(now, &tab.rand, tr.activeReq); n != nil { - tr.startRequest(tab, n) - tr.slow.schedule(now, &tab.rand) + reval := func(list *revalidationList) { + if list.nextTime <= now { + if n := list.get(now, &tab.rand, tr.activeReq); n != nil { + tr.startRequest(tab, n) + } + // Update nextTime regardless if any requests were started because + // current value has passed. + list.schedule(now, &tab.rand) + } } + reval(&tr.fast) + reval(&tr.slow) return min(tr.fast.nextTime, tr.slow.nextTime) } @@ -200,7 +204,7 @@ type revalidationList struct { // get returns a random node from the queue. Nodes in the 'exclude' map are not returned. func (list *revalidationList) get(now mclock.AbsTime, rand randomSource, exclude map[enode.ID]struct{}) *tableNode { - if now < list.nextTime || len(list.nodes) == 0 { + if len(list.nodes) == 0 { return nil } for i := 0; i < len(list.nodes)*3; i++ { diff --git a/p2p/discover/table_util_test.go b/p2p/discover/table_util_test.go index 5b2699d460..fe10883fe6 100644 --- a/p2p/discover/table_util_test.go +++ b/p2p/discover/table_util_test.go @@ -30,6 +30,7 @@ import ( "time" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/p2p/discover/v4wire" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enr" ) @@ -284,7 +285,7 @@ func hexEncPrivkey(h string) *ecdsa.PrivateKey { } // hexEncPubkey decodes h as a public key. -func hexEncPubkey(h string) (ret encPubkey) { +func hexEncPubkey(h string) (ret v4wire.Pubkey) { b, err := hex.DecodeString(h) if err != nil { panic(err) diff --git a/p2p/discover/v4_lookup_test.go b/p2p/discover/v4_lookup_test.go index bc9475a8b3..70bd7056fb 100644 --- a/p2p/discover/v4_lookup_test.go +++ b/p2p/discover/v4_lookup_test.go @@ -34,7 +34,7 @@ func TestUDPv4_Lookup(t *testing.T) { test := newUDPTest(t) // Lookup on empty table returns no nodes. - targetKey, _ := decodePubkey(crypto.S256(), lookupTestnet.target[:]) + targetKey, _ := v4wire.DecodePubkey(crypto.S256(), lookupTestnet.target) if results := test.udp.LookupPubkey(targetKey); len(results) > 0 { t.Fatalf("lookup on empty table returned %d results: %#v", len(results), results) } @@ -56,7 +56,7 @@ func TestUDPv4_Lookup(t *testing.T) { results := <-resultC t.Logf("results:") for _, e := range results { - t.Logf(" ld=%d, %x", enode.LogDist(lookupTestnet.target.id(), e.ID()), e.ID().Bytes()) + t.Logf(" ld=%d, %x", enode.LogDist(lookupTestnet.target.ID(), e.ID()), e.ID().Bytes()) } if len(results) != bucketSize { t.Errorf("wrong number of results: got %d, want %d", len(results), bucketSize) @@ -142,7 +142,7 @@ func serveTestnet(test *udpTest, testnet *preminedTestnet) { case *v4wire.Ping: test.packetInFrom(nil, key, to, &v4wire.Pong{Expiration: futureExp, ReplyTok: hash}) case *v4wire.Findnode: - dist := enode.LogDist(n.ID(), testnet.target.id()) + dist := enode.LogDist(n.ID(), testnet.target.ID()) nodes := testnet.nodesAtDistance(dist - 1) test.packetInFrom(nil, key, to, &v4wire.Neighbors{Expiration: futureExp, Nodes: nodes}) } @@ -156,12 +156,12 @@ func checkLookupResults(t *testing.T, tn *preminedTestnet, results []*enode.Node t.Helper() t.Logf("results:") for _, e := range results { - t.Logf(" ld=%d, %x", enode.LogDist(tn.target.id(), e.ID()), e.ID().Bytes()) + t.Logf(" ld=%d, %x", enode.LogDist(tn.target.ID(), e.ID()), e.ID().Bytes()) } if hasDuplicates(results) { t.Errorf("result set contains duplicate entries") } - if !sortedByDistanceTo(tn.target.id(), results) { + if !sortedByDistanceTo(tn.target.ID(), results) { t.Errorf("result set not sorted by distance to target") } wantNodes := tn.closest(len(results)) @@ -231,7 +231,7 @@ var lookupTestnet = &preminedTestnet{ } type preminedTestnet struct { - target encPubkey + target v4wire.Pubkey dists [hashBits + 1][]*ecdsa.PrivateKey } @@ -304,7 +304,7 @@ func (tn *preminedTestnet) closest(n int) (nodes []*enode.Node) { } } slices.SortFunc(nodes, func(a, b *enode.Node) int { - return enode.DistCmp(tn.target.id(), a.ID(), b.ID()) + return enode.DistCmp(tn.target.ID(), a.ID(), b.ID()) }) return nodes[:n] } @@ -319,11 +319,11 @@ func (tn *preminedTestnet) mine() { tn.dists[i] = nil } - targetSha := tn.target.id() + targetSha := tn.target.ID() found, need := 0, 40 for found < need { k := newkey() - ld := enode.LogDist(targetSha, encodePubkey(&k.PublicKey).id()) + ld := enode.LogDist(targetSha, v4wire.EncodePubkey(&k.PublicKey).ID()) if len(tn.dists[ld]) < 8 { tn.dists[ld] = append(tn.dists[ld], k) found++ diff --git a/p2p/discover/v4_udp.go b/p2p/discover/v4_udp.go index cca01bd3ce..321552ddc3 100644 --- a/p2p/discover/v4_udp.go +++ b/p2p/discover/v4_udp.go @@ -271,7 +271,7 @@ func (t *UDPv4) LookupPubkey(key *ecdsa.PublicKey) []*enode.Node { // case and run the bootstrapping logic. <-t.tab.refresh() } - return t.newLookup(t.closeCtx, encodePubkey(key)).run() + return t.newLookup(t.closeCtx, v4wire.EncodePubkey(key)).run() } // RandomNodes is an iterator yielding nodes from a random walk of the DHT. @@ -286,24 +286,24 @@ func (t *UDPv4) lookupRandom() []*enode.Node { // lookupSelf implements transport. func (t *UDPv4) lookupSelf() []*enode.Node { - return t.newLookup(t.closeCtx, encodePubkey(&t.priv.PublicKey)).run() + pubkey := v4wire.EncodePubkey(&t.priv.PublicKey) + return t.newLookup(t.closeCtx, pubkey).run() } func (t *UDPv4) newRandomLookup(ctx context.Context) *lookup { - var target encPubkey + var target v4wire.Pubkey crand.Read(target[:]) return t.newLookup(ctx, target) } -func (t *UDPv4) newLookup(ctx context.Context, targetKey encPubkey) *lookup { +func (t *UDPv4) newLookup(ctx context.Context, targetKey v4wire.Pubkey) *lookup { target := enode.ID(crypto.Keccak256Hash(targetKey[:])) - ekey := v4wire.Pubkey(targetKey) it := newLookup(ctx, t.tab, target, func(n *enode.Node) ([]*enode.Node, error) { addr, ok := n.UDPEndpoint() if !ok { return nil, errNoUDPEndpoint } - return t.findnode(n.ID(), addr, ekey) + return t.findnode(n.ID(), addr, targetKey) }) return it } diff --git a/p2p/discover/v4_udp_test.go b/p2p/discover/v4_udp_test.go index 9d6df08ead..1af31f4f1b 100644 --- a/p2p/discover/v4_udp_test.go +++ b/p2p/discover/v4_udp_test.go @@ -314,7 +314,7 @@ func TestUDPv4_findnodeMultiReply(t *testing.T) { // queue a pending findnode request resultc, errc := make(chan []*enode.Node, 1), make(chan error, 1) go func() { - rid := encodePubkey(&test.remotekey.PublicKey).id() + rid := v4wire.EncodePubkey(&test.remotekey.PublicKey).ID() ns, err := test.udp.findnode(rid, test.remoteaddr, testTarget) if err != nil && len(ns) == 0 { errc <- err @@ -433,7 +433,7 @@ func TestUDPv4_successfulPing(t *testing.T) { // pong packet. select { case n := <-added: - rid := encodePubkey(&test.remotekey.PublicKey).id() + rid := v4wire.EncodePubkey(&test.remotekey.PublicKey).ID() if n.ID() != rid { t.Errorf("node has wrong ID: got %v, want %v", n.ID(), rid) } diff --git a/p2p/discover/v5_udp_test.go b/p2p/discover/v5_udp_test.go index 1f8e972200..8631b918ff 100644 --- a/p2p/discover/v5_udp_test.go +++ b/p2p/discover/v5_udp_test.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/internal/testlog" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/discover/v4wire" "github.com/ethereum/go-ethereum/p2p/discover/v5wire" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enr" @@ -576,7 +577,7 @@ func TestUDPv5_lookup(t *testing.T) { test := newUDPV5Test(t) // Lookup on empty table returns no nodes. - if results := test.udp.Lookup(lookupTestnet.target.id()); len(results) > 0 { + if results := test.udp.Lookup(lookupTestnet.target.ID()); len(results) > 0 { t.Fatalf("lookup on empty table returned %d results: %#v", len(results), results) } @@ -596,7 +597,7 @@ func TestUDPv5_lookup(t *testing.T) { // Start the lookup. resultC := make(chan []*enode.Node, 1) go func() { - resultC <- test.udp.Lookup(lookupTestnet.target.id()) + resultC <- test.udp.Lookup(lookupTestnet.target.ID()) test.close() }() @@ -793,7 +794,7 @@ func (test *udpV5Test) packetInFrom(key *ecdsa.PrivateKey, addr netip.AddrPort, // getNode ensures the test knows about a node at the given endpoint. func (test *udpV5Test) getNode(key *ecdsa.PrivateKey, addr netip.AddrPort) *enode.LocalNode { - id := encodePubkey(&key.PublicKey).id() + id := v4wire.EncodePubkey(&key.PublicKey).ID() ln := test.nodesByID[id] if ln == nil { db, _ := enode.OpenDB("") diff --git a/p2p/enr/entries.go b/p2p/enr/entries.go index 917e1becba..155ec4c023 100644 --- a/p2p/enr/entries.go +++ b/p2p/enr/entries.go @@ -177,7 +177,7 @@ func (v IPv4Addr) ENRKey() string { return "ip" } func (v IPv4Addr) EncodeRLP(w io.Writer) error { addr := netip.Addr(v) if !addr.Is4() { - return fmt.Errorf("address is not IPv4") + return errors.New("address is not IPv4") } enc := rlp.NewEncoderBuffer(w) bytes := addr.As4() @@ -204,7 +204,7 @@ func (v IPv6Addr) ENRKey() string { return "ip6" } func (v IPv6Addr) EncodeRLP(w io.Writer) error { addr := netip.Addr(v) if !addr.Is6() { - return fmt.Errorf("address is not IPv6") + return errors.New("address is not IPv6") } enc := rlp.NewEncoderBuffer(w) bytes := addr.As16() diff --git a/p2p/nat/nat.go b/p2p/nat/nat.go index 2aa1f85585..c656044268 100644 --- a/p2p/nat/nat.go +++ b/p2p/nat/nat.go @@ -138,8 +138,10 @@ func (n ExtIP) String() string { return fmt.Sprintf("ExtIP(%v)", ne // These do nothing. -func (ExtIP) AddMapping(string, int, int, string, time.Duration) (uint16, error) { return 0, nil } -func (ExtIP) DeleteMapping(string, int, int) error { return nil } +func (ExtIP) AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) (uint16, error) { + return uint16(extport), nil +} +func (ExtIP) DeleteMapping(string, int, int) error { return nil } // Any returns a port mapper that tries to discover any supported // mechanism on the local network. diff --git a/p2p/server_nat.go b/p2p/server_nat.go index 299d275490..933993bc1f 100644 --- a/p2p/server_nat.go +++ b/p2p/server_nat.go @@ -125,7 +125,7 @@ func (srv *Server) portMappingLoop() { if err != nil { log.Debug("Couldn't get external IP", "err", err, "interface", srv.NAT) } else if !ip.Equal(lastExtIP) { - log.Debug("External IP changed", "ip", extip, "interface", srv.NAT) + log.Debug("External IP changed", "ip", ip, "interface", srv.NAT) } else { continue } diff --git a/p2p/server_nat_test.go b/p2p/server_nat_test.go index cbb1f37e0a..7e19387210 100644 --- a/p2p/server_nat_test.go +++ b/p2p/server_nat_test.go @@ -36,6 +36,7 @@ func TestServerPortMapping(t *testing.T) { PrivateKey: newkey(), NoDial: true, ListenAddr: ":0", + DiscAddr: ":0", NAT: mockNAT, Logger: testlog.Logger(t, log.LvlTrace), clock: clock, diff --git a/params/network_params.go b/params/network_params.go index 9311b5e2d5..61bd6b2f42 100644 --- a/params/network_params.go +++ b/params/network_params.go @@ -24,44 +24,13 @@ const ( // contains on the server side. BloomBitsBlocks uint64 = 4096 - // BloomBitsBlocksClient is the number of blocks a single bloom bit section vector - // contains on the light client side - BloomBitsBlocksClient uint64 = 32768 - // BloomConfirms is the number of confirmation blocks before a bloom section is // considered probably final and its rotated bits are calculated. BloomConfirms = 256 - // CHTFrequency is the block frequency for creating CHTs - CHTFrequency = 32768 - - // BloomTrieFrequency is the block frequency for creating BloomTrie on both - // server/client sides. - BloomTrieFrequency = 32768 - - // HelperTrieConfirmations is the number of confirmations before a client is expected - // to have the given HelperTrie available. - HelperTrieConfirmations = 2048 - - // HelperTrieProcessConfirmations is the number of confirmations before a HelperTrie - // is generated - HelperTrieProcessConfirmations = 256 - - // CheckpointFrequency is the block frequency for creating checkpoint - CheckpointFrequency = 32768 - - // CheckpointProcessConfirmations is the number before a checkpoint is generated - CheckpointProcessConfirmations = 256 - // FullImmutabilityThreshold is the number of blocks after which a chain segment is // considered immutable (i.e. soft finality). It is used by the downloader as a // hard limit against deep ancestors, by the blockchain against deep reorgs, by // the freezer as the cutoff threshold and by clique as the snapshot trust limit. FullImmutabilityThreshold = 90000 - - // LightImmutabilityThreshold is the number of blocks after which a header chain - // segment is considered immutable for light client(i.e. soft finality). It is used by - // the downloader as a hard limit against deep ancestors, by the blockchain against deep - // reorgs, by the light pruner as the pruning validity guarantee. - LightImmutabilityThreshold = 30000 ) diff --git a/params/version.go b/params/version.go index a3e841750e..852f9fd1e1 100644 --- a/params/version.go +++ b/params/version.go @@ -23,7 +23,7 @@ import ( const ( VersionMajor = 1 // Major version component of the current release VersionMinor = 14 // Minor version component of the current release - VersionPatch = 7 // Patch version component of the current release + VersionPatch = 8 // Patch version component of the current release VersionMeta = "unstable" // Version metadata to append to the version string ) diff --git a/rpc/server.go b/rpc/server.go index 52866004f8..42b59f8f6f 100644 --- a/rpc/server.go +++ b/rpc/server.go @@ -18,7 +18,9 @@ package rpc import ( "context" + "errors" "io" + "net" "sync" "sync/atomic" @@ -151,8 +153,8 @@ func (s *Server) serveSingleRequest(ctx context.Context, codec ServerCodec) { reqs, batch, err := codec.readBatch() if err != nil { - if err != io.EOF { - resp := errorMessage(&invalidMessageError{"parse error"}) + if msg := messageForReadError(err); msg != "" { + resp := errorMessage(&invalidMessageError{msg}) codec.writeJSON(ctx, resp, true) } return @@ -164,6 +166,20 @@ func (s *Server) serveSingleRequest(ctx context.Context, codec ServerCodec) { } } +func messageForReadError(err error) string { + var netErr net.Error + if errors.As(err, &netErr) { + if netErr.Timeout() { + return "read timeout" + } else { + return "read error" + } + } else if err != io.EOF { + return "parse error" + } + return "" +} + // Stop stops reading new requests, waits for stopPendingRequestTimeout to allow pending // requests to finish, then closes all codecs which will cancel pending requests and // subscriptions. diff --git a/rpc/subscription_test.go b/rpc/subscription_test.go index a7dac705c9..ab40ab169f 100644 --- a/rpc/subscription_test.go +++ b/rpc/subscription_test.go @@ -267,13 +267,9 @@ func TestNotify(t *testing.T) { sub: &Subscription{ID: id}, activated: true, } - msg := &types.Header{ - ParentHash: common.HexToHash("0x01"), - Number: big.NewInt(100), - } - notifier.Notify(id, msg) + notifier.Notify(id, "hello") have := strings.TrimSpace(out.String()) - want := `{"jsonrpc":"2.0","method":"_subscription","params":{"subscription":"test","result":{"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000001","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":null,"number":"0x64","gasLimit":"0x0","gasUsed":"0x0","timestamp":"0x0","extraData":"0x","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":null,"withdrawalsRoot":null,"blobGasUsed":null,"excessBlobGas":null,"parentBeaconBlockRoot":null,"hash":"0xe5fb877dde471b45b9742bb4bb4b3d74a761e2fb7cb849a3d2b687eed90fb604"}}}` + want := `{"jsonrpc":"2.0","method":"_subscription","params":{"subscription":"test","result":"hello"}}` if have != want { t.Errorf("have:\n%v\nwant:\n%v\n", have, want) } diff --git a/trie/committer.go b/trie/committer.go index 4e2f7b8bd6..863e7bafdc 100644 --- a/trie/committer.go +++ b/trie/committer.go @@ -154,12 +154,8 @@ func (c *committer) store(path []byte, n node) node { return hash } -// MerkleResolver the children resolver in merkle-patricia-tree. -type MerkleResolver struct{} - -// ForEach implements childResolver, decodes the provided node and -// traverses the children inside. -func (resolver MerkleResolver) ForEach(node []byte, onChild func(common.Hash)) { +// ForGatherChildren decodes the provided node and traverses the children inside. +func ForGatherChildren(node []byte, onChild func(common.Hash)) { forGatherChildren(mustDecodeNodeUnsafe(nil, node), onChild) } diff --git a/trie/trie_test.go b/trie/trie_test.go index 5f706a28be..505b517bc5 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -819,7 +819,6 @@ func (s *spongeDb) Get(key []byte) ([]byte, error) { return nil, error func (s *spongeDb) Delete(key []byte) error { panic("implement me") } func (s *spongeDb) NewBatch() ethdb.Batch { return &spongeBatch{s} } func (s *spongeDb) NewBatchWithSize(size int) ethdb.Batch { return &spongeBatch{s} } -func (s *spongeDb) NewSnapshot() (ethdb.Snapshot, error) { panic("implement me") } func (s *spongeDb) Stat() (string, error) { panic("implement me") } func (s *spongeDb) Compact(start []byte, limit []byte) error { panic("implement me") } func (s *spongeDb) Close() error { return nil } diff --git a/trie/verkle.go b/trie/verkle.go index a457097e95..fb4d81281c 100644 --- a/trie/verkle.go +++ b/trie/verkle.go @@ -199,6 +199,57 @@ func (t *VerkleTrie) DeleteAccount(addr common.Address) error { return nil } +// RollBackAccount removes the account info + code from the tree, unlike DeleteAccount +// that will overwrite it with 0s. The first 64 storage slots are also removed. +func (t *VerkleTrie) RollBackAccount(addr common.Address) error { + var ( + evaluatedAddr = t.cache.Get(addr.Bytes()) + codeSizeKey = utils.CodeSizeKeyWithEvaluatedAddress(evaluatedAddr) + ) + codeSizeBytes, err := t.root.Get(codeSizeKey, t.nodeResolver) + if err != nil { + return fmt.Errorf("rollback: error finding code size: %w", err) + } + if len(codeSizeBytes) == 0 { + return errors.New("rollback: code size is not existent") + } + codeSize := binary.LittleEndian.Uint64(codeSizeBytes) + + // Delete the account header + first 64 slots + first 128 code chunks + key := common.CopyBytes(codeSizeKey) + for i := 0; i < verkle.NodeWidth; i++ { + key[31] = byte(i) + + // this is a workaround to avoid deleting nil leaves, the lib needs to be + // fixed to be able to handle that + v, err := t.root.Get(key, t.nodeResolver) + if err != nil { + return fmt.Errorf("error rolling back account header: %w", err) + } + if len(v) == 0 { + continue + } + _, err = t.root.Delete(key, t.nodeResolver) + if err != nil { + return fmt.Errorf("error rolling back account header: %w", err) + } + } + // Delete all further code + for i, chunknr := uint64(32*128), uint64(128); i < codeSize; i, chunknr = i+32, chunknr+1 { + // evaluate group key at the start of a new group + groupOffset := (chunknr + 128) % 256 + if groupOffset == 0 { + key = utils.CodeChunkKeyWithEvaluatedAddress(evaluatedAddr, uint256.NewInt(chunknr)) + } + key[31] = byte(groupOffset) + _, err = t.root.Delete(key[:], t.nodeResolver) + if err != nil { + return fmt.Errorf("error deleting code chunk (addr=%x) error: %w", addr[:], err) + } + } + return nil +} + // DeleteStorage implements state.Trie, deleting the specified storage slot from // the trie. If the storage slot was not existent in the trie, no error will be // returned. If the trie is corrupted, an error will be returned. diff --git a/trie/verkle_test.go b/trie/verkle_test.go index 0cbe28bf01..55438d45e1 100644 --- a/trie/verkle_test.go +++ b/trie/verkle_test.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/trie/utils" "github.com/holiman/uint256" ) @@ -89,3 +90,84 @@ func TestVerkleTreeReadWrite(t *testing.T) { } } } + +func TestVerkleRollBack(t *testing.T) { + db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.PathScheme) + tr, _ := NewVerkleTrie(types.EmptyVerkleHash, db, utils.NewPointCache(100)) + + for addr, acct := range accounts { + if err := tr.UpdateAccount(addr, acct); err != nil { + t.Fatalf("Failed to update account, %v", err) + } + for key, val := range storages[addr] { + if err := tr.UpdateStorage(addr, key.Bytes(), val); err != nil { + t.Fatalf("Failed to update account, %v", err) + } + } + // create more than 128 chunks of code + code := make([]byte, 129*32) + for i := 0; i < len(code); i += 2 { + code[i] = 0x60 + code[i+1] = byte(i % 256) + } + hash := crypto.Keccak256Hash(code) + if err := tr.UpdateContractCode(addr, hash, code); err != nil { + t.Fatalf("Failed to update contract, %v", err) + } + } + + // Check that things were created + for addr, acct := range accounts { + stored, err := tr.GetAccount(addr) + if err != nil { + t.Fatalf("Failed to get account, %v", err) + } + if !reflect.DeepEqual(stored, acct) { + t.Fatal("account is not matched") + } + for key, val := range storages[addr] { + stored, err := tr.GetStorage(addr, key.Bytes()) + if err != nil { + t.Fatalf("Failed to get storage, %v", err) + } + if !bytes.Equal(stored, val) { + t.Fatal("storage is not matched") + } + } + } + + // ensure there is some code in the 2nd group + keyOf2ndGroup := []byte{141, 124, 185, 236, 50, 22, 185, 39, 244, 47, 97, 209, 96, 235, 22, 13, 205, 38, 18, 201, 128, 223, 0, 59, 146, 199, 222, 119, 133, 13, 91, 0} + chunk, err := tr.root.Get(keyOf2ndGroup, nil) + if err != nil { + t.Fatalf("Failed to get account, %v", err) + } + if len(chunk) == 0 { + t.Fatal("account was not created ") + } + + // Rollback first account and check that it is gone + addr1 := common.Address{1} + err = tr.RollBackAccount(addr1) + if err != nil { + t.Fatalf("error rolling back address 1: %v", err) + } + + // ensure the account is gone + stored, err := tr.GetAccount(addr1) + if err != nil { + t.Fatalf("Failed to get account, %v", err) + } + if stored != nil { + t.Fatal("account was not deleted") + } + + // ensure that the last code chunk is also gone from the tree + chunk, err = tr.root.Get(keyOf2ndGroup, nil) + if err != nil { + t.Fatalf("Failed to get account, %v", err) + } + if len(chunk) != 0 { + t.Fatal("account was not deleted") + } +} diff --git a/triedb/database.go b/triedb/database.go index 91386a9dbc..aecb900f31 100644 --- a/triedb/database.go +++ b/triedb/database.go @@ -23,7 +23,6 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/triestate" "github.com/ethereum/go-ethereum/triedb/database" @@ -43,9 +42,18 @@ type Config struct { // default settings. var HashDefaults = &Config{ Preimages: false, + IsVerkle: false, HashDB: hashdb.Defaults, } +// VerkleDefaults represents a config for holding verkle trie data +// using path-based scheme with default settings. +var VerkleDefaults = &Config{ + Preimages: false, + IsVerkle: true, + PathDB: pathdb.Defaults, +} + // backend defines the methods needed to access/update trie nodes in different // state scheme. type backend interface { @@ -85,7 +93,6 @@ type backend interface { // relevant with trie nodes and node preimages. type Database struct { config *Config // Configuration for trie database - diskdb ethdb.Database // Persistent database to store the snapshot preimages *preimageStore // The store for caching preimages backend backend // The backend for managing trie nodes } @@ -103,7 +110,6 @@ func NewDatabase(diskdb ethdb.Database, config *Config) *Database { } db := &Database{ config: config, - diskdb: diskdb, preimages: preimages, } if config.HashDB != nil && config.PathDB != nil { @@ -112,14 +118,7 @@ func NewDatabase(diskdb ethdb.Database, config *Config) *Database { if config.PathDB != nil { db.backend = pathdb.New(diskdb, config.PathDB, config.IsVerkle) } else { - var resolver hashdb.ChildResolver - if config.IsVerkle { - // TODO define verkle resolver - log.Crit("verkle does not use a hash db") - } else { - resolver = trie.MerkleResolver{} - } - db.backend = hashdb.New(diskdb, config.HashDB, resolver) + db.backend = hashdb.New(diskdb, config.HashDB) } return db } diff --git a/triedb/hashdb/database.go b/triedb/hashdb/database.go index bb0deca9a7..4def10e338 100644 --- a/triedb/hashdb/database.go +++ b/triedb/hashdb/database.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/trienode" "github.com/ethereum/go-ethereum/trie/triestate" "github.com/ethereum/go-ethereum/triedb/database" @@ -60,12 +61,6 @@ var ( memcacheCommitBytesMeter = metrics.NewRegisteredMeter("hashdb/memcache/commit/bytes", nil) ) -// ChildResolver defines the required method to decode the provided -// trie node and iterate the children on top. -type ChildResolver interface { - ForEach(node []byte, onChild func(common.Hash)) -} - // Config contains the settings for database. type Config struct { CleanCacheSize int // Maximum memory allowance (in bytes) for caching clean nodes @@ -84,9 +79,7 @@ var Defaults = &Config{ // the disk database. The aim is to accumulate trie writes in-memory and only // periodically flush a couple tries to disk, garbage collecting the remainder. type Database struct { - diskdb ethdb.Database // Persistent storage for matured trie nodes - resolver ChildResolver // The handler to resolve children of nodes - + diskdb ethdb.Database // Persistent storage for matured trie nodes cleans *fastcache.Cache // GC friendly memory cache of clean node RLPs dirties map[common.Hash]*cachedNode // Data and references relationships of dirty trie nodes oldest common.Hash // Oldest tracked node, flush-list head @@ -124,15 +117,15 @@ var cachedNodeSize = int(reflect.TypeOf(cachedNode{}).Size()) // forChildren invokes the callback for all the tracked children of this node, // both the implicit ones from inside the node as well as the explicit ones // from outside the node. -func (n *cachedNode) forChildren(resolver ChildResolver, onChild func(hash common.Hash)) { +func (n *cachedNode) forChildren(onChild func(hash common.Hash)) { for child := range n.external { onChild(child) } - resolver.ForEach(n.node, onChild) + trie.ForGatherChildren(n.node, onChild) } // New initializes the hash-based node database. -func New(diskdb ethdb.Database, config *Config, resolver ChildResolver) *Database { +func New(diskdb ethdb.Database, config *Config) *Database { if config == nil { config = Defaults } @@ -141,10 +134,9 @@ func New(diskdb ethdb.Database, config *Config, resolver ChildResolver) *Databas cleans = fastcache.New(config.CleanCacheSize) } return &Database{ - diskdb: diskdb, - resolver: resolver, - cleans: cleans, - dirties: make(map[common.Hash]*cachedNode), + diskdb: diskdb, + cleans: cleans, + dirties: make(map[common.Hash]*cachedNode), } } @@ -163,7 +155,7 @@ func (db *Database) insert(hash common.Hash, node []byte) { node: node, flushPrev: db.newest, } - entry.forChildren(db.resolver, func(child common.Hash) { + entry.forChildren(func(child common.Hash) { if c := db.dirties[child]; c != nil { c.parents++ } @@ -316,7 +308,7 @@ func (db *Database) dereference(hash common.Hash) { db.dirties[node.flushNext].flushPrev = node.flushPrev } // Dereference all children and delete the node - node.forChildren(db.resolver, func(child common.Hash) { + node.forChildren(func(child common.Hash) { db.dereference(child) }) delete(db.dirties, hash) @@ -465,7 +457,7 @@ func (db *Database) commit(hash common.Hash, batch ethdb.Batch, uncacher *cleane var err error // Dereference all children and delete the node - node.forChildren(db.resolver, func(child common.Hash) { + node.forChildren(func(child common.Hash) { if err == nil { err = db.commit(child, batch, uncacher) } diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index 450c3a8f4f..31e478117c 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -152,6 +152,14 @@ func New(diskdb ethdb.Database, config *Config, isVerkle bool) *Database { } config = config.sanitize() + // Establish a dedicated database namespace tailored for verkle-specific + // data, ensuring the isolation of both verkle and merkle tree data. It's + // important to note that the introduction of a prefix won't lead to + // substantial storage overhead, as the underlying database will efficiently + // compress the shared key prefix. + if isVerkle { + diskdb = rawdb.NewTable(diskdb, string(rawdb.VerklePrefix)) + } db := &Database{ readOnly: config.ReadOnly, isVerkle: isVerkle, @@ -190,7 +198,7 @@ func (db *Database) repairHistory() error { // all of them. Fix the tests first. return nil } - freezer, err := rawdb.NewStateFreezer(ancient, db.readOnly) + freezer, err := rawdb.NewStateFreezer(ancient, db.isVerkle, db.readOnly) if err != nil { log.Crit("Failed to open state history freezer", "err", err) } diff --git a/triedb/pathdb/history_test.go b/triedb/pathdb/history_test.go index 4114aa1185..586f907fe4 100644 --- a/triedb/pathdb/history_test.go +++ b/triedb/pathdb/history_test.go @@ -129,7 +129,7 @@ func TestTruncateHeadHistory(t *testing.T) { roots []common.Hash hs = makeHistories(10) db = rawdb.NewMemoryDatabase() - freezer, _ = rawdb.NewStateFreezer(t.TempDir(), false) + freezer, _ = rawdb.NewStateFreezer(t.TempDir(), false, false) ) defer freezer.Close() @@ -157,7 +157,7 @@ func TestTruncateTailHistory(t *testing.T) { roots []common.Hash hs = makeHistories(10) db = rawdb.NewMemoryDatabase() - freezer, _ = rawdb.NewStateFreezer(t.TempDir(), false) + freezer, _ = rawdb.NewStateFreezer(t.TempDir(), false, false) ) defer freezer.Close() @@ -200,7 +200,7 @@ func TestTruncateTailHistories(t *testing.T) { roots []common.Hash hs = makeHistories(10) db = rawdb.NewMemoryDatabase() - freezer, _ = rawdb.NewStateFreezer(t.TempDir()+fmt.Sprintf("%d", i), false) + freezer, _ = rawdb.NewStateFreezer(t.TempDir()+fmt.Sprintf("%d", i), false, false) ) defer freezer.Close() @@ -228,7 +228,7 @@ func TestTruncateOutOfRange(t *testing.T) { var ( hs = makeHistories(10) db = rawdb.NewMemoryDatabase() - freezer, _ = rawdb.NewStateFreezer(t.TempDir(), false) + freezer, _ = rawdb.NewStateFreezer(t.TempDir(), false, false) ) defer freezer.Close() diff --git a/triedb/pathdb/reader.go b/triedb/pathdb/reader.go index 54dc98a543..6a58493ba6 100644 --- a/triedb/pathdb/reader.go +++ b/triedb/pathdb/reader.go @@ -78,7 +78,7 @@ func (r *reader) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, if len(blob) > 0 { blobHex = hexutil.Encode(blob) } - log.Error("Unexpected trie node", "location", loc.loc, "owner", owner, "path", path, "expect", hash, "got", got, "blob", blobHex) + log.Error("Unexpected trie node", "location", loc.loc, "owner", owner.Hex(), "path", path, "expect", hash.Hex(), "got", got.Hex(), "blob", blobHex) return nil, fmt.Errorf("unexpected node: (%x %v), %x!=%x, %s, blob: %s", owner, path, hash, got, loc.string(), blobHex) } return blob, nil