diff --git a/cmd/worldland/accountcmd.go b/cmd/worldland/accountcmd.go
new file mode 100644
index 0000000000..e56e914e80
--- /dev/null
+++ b/cmd/worldland/accountcmd.go
@@ -0,0 +1,357 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package main
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/cryptoecc/ETH-ECC/accounts"
+ "github.com/cryptoecc/ETH-ECC/accounts/keystore"
+ "github.com/cryptoecc/ETH-ECC/cmd/utils"
+ "github.com/cryptoecc/ETH-ECC/crypto"
+ "github.com/cryptoecc/ETH-ECC/log"
+ "github.com/urfave/cli/v2"
+)
+
+var (
+ walletCommand = &cli.Command{
+ Name: "wallet",
+ Usage: "Manage Ethereum presale wallets",
+ ArgsUsage: "",
+ Description: `
+ geth wallet import /path/to/my/presale.wallet
+
+will prompt for your password and imports your ether presale account.
+It can be used non-interactively with the --password option taking a
+passwordfile as argument containing the wallet password in plaintext.`,
+ Subcommands: []*cli.Command{
+ {
+
+ Name: "import",
+ Usage: "Import Ethereum presale wallet",
+ ArgsUsage: "",
+ Action: importWallet,
+ Flags: []cli.Flag{
+ utils.DataDirFlag,
+ utils.KeyStoreDirFlag,
+ utils.PasswordFileFlag,
+ utils.LightKDFFlag,
+ },
+ Description: `
+ geth wallet [options] /path/to/my/presale.wallet
+
+will prompt for your password and imports your ether presale account.
+It can be used non-interactively with the --password option taking a
+passwordfile as argument containing the wallet password in plaintext.`,
+ },
+ },
+ }
+
+ accountCommand = &cli.Command{
+ Name: "account",
+ Usage: "Manage accounts",
+ Description: `
+
+Manage accounts, list all existing accounts, import a private key into a new
+account, create a new account or update an existing account.
+
+It supports interactive mode, when you are prompted for password as well as
+non-interactive mode where passwords are supplied via a given password file.
+Non-interactive mode is only meant for scripted use on test networks or known
+safe environments.
+
+Make sure you remember the password you gave when creating a new account (with
+either new or import). Without it you are not able to unlock your account.
+
+Note that exporting your key in unencrypted format is NOT supported.
+
+Keys are stored under /keystore.
+It is safe to transfer the entire directory or the individual keys therein
+between ethereum nodes by simply copying.
+
+Make sure you backup your keys regularly.`,
+ Subcommands: []*cli.Command{
+ {
+ Name: "list",
+ Usage: "Print summary of existing accounts",
+ Action: accountList,
+ Flags: []cli.Flag{
+ utils.DataDirFlag,
+ utils.KeyStoreDirFlag,
+ },
+ Description: `
+Print a short summary of all accounts`,
+ },
+ {
+ Name: "new",
+ Usage: "Create a new account",
+ Action: accountCreate,
+ Flags: []cli.Flag{
+ utils.DataDirFlag,
+ utils.KeyStoreDirFlag,
+ utils.PasswordFileFlag,
+ utils.LightKDFFlag,
+ },
+ Description: `
+ geth account new
+
+Creates a new account and prints the address.
+
+The account is saved in encrypted format, you are prompted for a password.
+
+You must remember this password to unlock your account in the future.
+
+For non-interactive use the password can be specified with the --password flag:
+
+Note, this is meant to be used for testing only, it is a bad idea to save your
+password to file or expose in any other way.
+`,
+ },
+ {
+ Name: "update",
+ Usage: "Update an existing account",
+ Action: accountUpdate,
+ ArgsUsage: "",
+ Flags: []cli.Flag{
+ utils.DataDirFlag,
+ utils.KeyStoreDirFlag,
+ utils.LightKDFFlag,
+ },
+ Description: `
+ geth account update
+
+Update an existing account.
+
+The account is saved in the newest version in encrypted format, you are prompted
+for a password to unlock the account and another to save the updated file.
+
+This same command can therefore be used to migrate an account of a deprecated
+format to the newest format or change the password for an account.
+
+For non-interactive use the password can be specified with the --password flag:
+
+ geth account update [options]
+
+Since only one password can be given, only format update can be performed,
+changing your password is only possible interactively.
+`,
+ },
+ {
+ Name: "import",
+ Usage: "Import a private key into a new account",
+ Action: accountImport,
+ Flags: []cli.Flag{
+ utils.DataDirFlag,
+ utils.KeyStoreDirFlag,
+ utils.PasswordFileFlag,
+ utils.LightKDFFlag,
+ },
+ ArgsUsage: "",
+ Description: `
+ geth account import
+
+Imports an unencrypted private key from and creates a new account.
+Prints the address.
+
+The keyfile is assumed to contain an unencrypted private key in hexadecimal format.
+
+The account is saved in encrypted format, you are prompted for a password.
+
+You must remember this password to unlock your account in the future.
+
+For non-interactive use the password can be specified with the -password flag:
+
+ geth account import [options]
+
+Note:
+As you can directly copy your encrypted accounts to another ethereum instance,
+this import mechanism is not needed when you transfer an account between
+nodes.
+`,
+ },
+ },
+ }
+)
+
+func accountList(ctx *cli.Context) error {
+ stack, _ := makeConfigNode(ctx)
+ var index int
+ for _, wallet := range stack.AccountManager().Wallets() {
+ for _, account := range wallet.Accounts() {
+ fmt.Printf("Account #%d: {%x} %s\n", index, account.Address, &account.URL)
+ index++
+ }
+ }
+ return nil
+}
+
+// tries unlocking the specified account a few times.
+func unlockAccount(ks *keystore.KeyStore, address string, i int, passwords []string) (accounts.Account, string) {
+ account, err := utils.MakeAddress(ks, address)
+ if err != nil {
+ utils.Fatalf("Could not list accounts: %v", err)
+ }
+ for trials := 0; trials < 3; trials++ {
+ prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3)
+ password := utils.GetPassPhraseWithList(prompt, false, i, passwords)
+ err = ks.Unlock(account, password)
+ if err == nil {
+ log.Info("Unlocked account", "address", account.Address.Hex())
+ return account, password
+ }
+ if err, ok := err.(*keystore.AmbiguousAddrError); ok {
+ log.Info("Unlocked account", "address", account.Address.Hex())
+ return ambiguousAddrRecovery(ks, err, password), password
+ }
+ if err != keystore.ErrDecrypt {
+ // No need to prompt again if the error is not decryption-related.
+ break
+ }
+ }
+ // All trials expended to unlock account, bail out
+ utils.Fatalf("Failed to unlock account %s (%v)", address, err)
+
+ return accounts.Account{}, ""
+}
+
+func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrError, auth string) accounts.Account {
+ fmt.Printf("Multiple key files exist for address %x:\n", err.Addr)
+ for _, a := range err.Matches {
+ fmt.Println(" ", a.URL)
+ }
+ fmt.Println("Testing your password against all of them...")
+ var match *accounts.Account
+ for i, a := range err.Matches {
+ if e := ks.Unlock(a, auth); e == nil {
+ match = &err.Matches[i]
+ break
+ }
+ }
+ if match == nil {
+ utils.Fatalf("None of the listed files could be unlocked.")
+ return accounts.Account{}
+ }
+ fmt.Printf("Your password unlocked %s\n", match.URL)
+ fmt.Println("In order to avoid this warning, you need to remove the following duplicate key files:")
+ for _, a := range err.Matches {
+ if a != *match {
+ fmt.Println(" ", a.URL)
+ }
+ }
+ return *match
+}
+
+// accountCreate creates a new account into the keystore defined by the CLI flags.
+func accountCreate(ctx *cli.Context) error {
+ cfg := gethConfig{Node: defaultNodeConfig()}
+ // Load config file.
+ if file := ctx.String(configFileFlag.Name); file != "" {
+ if err := loadConfig(file, &cfg); err != nil {
+ utils.Fatalf("%v", err)
+ }
+ }
+ utils.SetNodeConfig(ctx, &cfg.Node)
+ keydir, err := cfg.Node.KeyDirConfig()
+ if err != nil {
+ utils.Fatalf("Failed to read configuration: %v", err)
+ }
+ scryptN := keystore.StandardScryptN
+ scryptP := keystore.StandardScryptP
+ if cfg.Node.UseLightweightKDF {
+ scryptN = keystore.LightScryptN
+ scryptP = keystore.LightScryptP
+ }
+
+ password := utils.GetPassPhraseWithList("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
+
+ account, err := keystore.StoreKey(keydir, password, scryptN, scryptP)
+
+ if err != nil {
+ utils.Fatalf("Failed to create account: %v", err)
+ }
+ fmt.Printf("\nYour new key was generated\n\n")
+ fmt.Printf("Public address of the key: %s\n", account.Address.Hex())
+ fmt.Printf("Path of the secret key file: %s\n\n", account.URL.Path)
+ fmt.Printf("- You can share your public address with anyone. Others need it to interact with you.\n")
+ fmt.Printf("- You must NEVER share the secret key with anyone! The key controls access to your funds!\n")
+ fmt.Printf("- You must BACKUP your key file! Without the key, it's impossible to access account funds!\n")
+ fmt.Printf("- You must REMEMBER your password! Without the password, it's impossible to decrypt the key!\n\n")
+ return nil
+}
+
+// accountUpdate transitions an account from a previous format to the current
+// one, also providing the possibility to change the pass-phrase.
+func accountUpdate(ctx *cli.Context) error {
+ if ctx.Args().Len() == 0 {
+ utils.Fatalf("No accounts specified to update")
+ }
+ stack, _ := makeConfigNode(ctx)
+ ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+
+ for _, addr := range ctx.Args().Slice() {
+ account, oldPassword := unlockAccount(ks, addr, 0, nil)
+ newPassword := utils.GetPassPhraseWithList("Please give a new password. Do not forget this password.", true, 0, nil)
+ if err := ks.Update(account, oldPassword, newPassword); err != nil {
+ utils.Fatalf("Could not update the account: %v", err)
+ }
+ }
+ return nil
+}
+
+func importWallet(ctx *cli.Context) error {
+ if ctx.Args().Len() != 1 {
+ utils.Fatalf("keyfile must be given as the only argument")
+ }
+ keyfile := ctx.Args().First()
+ keyJSON, err := os.ReadFile(keyfile)
+ if err != nil {
+ utils.Fatalf("Could not read wallet file: %v", err)
+ }
+
+ stack, _ := makeConfigNode(ctx)
+ passphrase := utils.GetPassPhraseWithList("", false, 0, utils.MakePasswordList(ctx))
+
+ ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+ acct, err := ks.ImportPreSaleKey(keyJSON, passphrase)
+ if err != nil {
+ utils.Fatalf("%v", err)
+ }
+ fmt.Printf("Address: {%x}\n", acct.Address)
+ return nil
+}
+
+func accountImport(ctx *cli.Context) error {
+ if ctx.Args().Len() != 1 {
+ utils.Fatalf("keyfile must be given as the only argument")
+ }
+ keyfile := ctx.Args().First()
+ key, err := crypto.LoadECDSA(keyfile)
+ if err != nil {
+ utils.Fatalf("Failed to load the private key: %v", err)
+ }
+ stack, _ := makeConfigNode(ctx)
+ passphrase := utils.GetPassPhraseWithList("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
+
+ ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+ acct, err := ks.ImportECDSA(key, passphrase)
+ if err != nil {
+ utils.Fatalf("Could not create the account: %v", err)
+ }
+ fmt.Printf("Address: {%x}\n", acct.Address)
+ return nil
+}
diff --git a/cmd/worldland/accountcmd_test.go b/cmd/worldland/accountcmd_test.go
new file mode 100644
index 0000000000..8d132a8bf5
--- /dev/null
+++ b/cmd/worldland/accountcmd_test.go
@@ -0,0 +1,362 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package main
+
+import (
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+
+ "github.com/cespare/cp"
+)
+
+// These tests are 'smoke tests' for the account related
+// subcommands and flags.
+//
+// For most tests, the test files from package accounts
+// are copied into a temporary keystore directory.
+
+func tmpDatadirWithKeystore(t *testing.T) string {
+ datadir := t.TempDir()
+ keystore := filepath.Join(datadir, "keystore")
+ source := filepath.Join("..", "..", "accounts", "keystore", "testdata", "keystore")
+ if err := cp.CopyAll(keystore, source); err != nil {
+ t.Fatal(err)
+ }
+ return datadir
+}
+
+func TestAccountListEmpty(t *testing.T) {
+ geth := runGeth(t, "account", "list")
+ geth.ExpectExit()
+}
+
+func TestAccountList(t *testing.T) {
+ datadir := tmpDatadirWithKeystore(t)
+ var want = `
+Account #0: {7ef5a6135f1fd6a02593eedc869c6d41d934aef8} keystore://{{.Datadir}}/keystore/UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
+Account #1: {f466859ead1932d743d622cb74fc058882e8648a} keystore://{{.Datadir}}/keystore/aaa
+Account #2: {289d485d9771714cce91d3393d764e1311907acc} keystore://{{.Datadir}}/keystore/zzz
+`
+ if runtime.GOOS == "windows" {
+ want = `
+Account #0: {7ef5a6135f1fd6a02593eedc869c6d41d934aef8} keystore://{{.Datadir}}\keystore\UTC--2016-03-22T12-57-55.920751759Z--7ef5a6135f1fd6a02593eedc869c6d41d934aef8
+Account #1: {f466859ead1932d743d622cb74fc058882e8648a} keystore://{{.Datadir}}\keystore\aaa
+Account #2: {289d485d9771714cce91d3393d764e1311907acc} keystore://{{.Datadir}}\keystore\zzz
+`
+ }
+ {
+ geth := runGeth(t, "account", "list", "--datadir", datadir)
+ geth.Expect(want)
+ geth.ExpectExit()
+ }
+ {
+ geth := runGeth(t, "--datadir", datadir, "account", "list")
+ geth.Expect(want)
+ geth.ExpectExit()
+ }
+}
+
+func TestAccountNew(t *testing.T) {
+ geth := runGeth(t, "account", "new", "--lightkdf")
+ defer geth.ExpectExit()
+ geth.Expect(`
+Your new account is locked with a password. Please give a password. Do not forget this password.
+!! Unsupported terminal, password will be echoed.
+Password: {{.InputLine "foobar"}}
+Repeat password: {{.InputLine "foobar"}}
+
+Your new key was generated
+`)
+ geth.ExpectRegexp(`
+Public address of the key: 0x[0-9a-fA-F]{40}
+Path of the secret key file: .*UTC--.+--[0-9a-f]{40}
+
+- You can share your public address with anyone. Others need it to interact with you.
+- You must NEVER share the secret key with anyone! The key controls access to your funds!
+- You must BACKUP your key file! Without the key, it's impossible to access account funds!
+- You must REMEMBER your password! Without the password, it's impossible to decrypt the key!
+`)
+}
+
+func TestAccountImport(t *testing.T) {
+ tests := []struct{ name, key, output string }{
+ {
+ name: "correct account",
+ key: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
+ output: "Address: {fcad0b19bb29d4674531d6f115237e16afce377c}\n",
+ },
+ {
+ name: "invalid character",
+ key: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef1",
+ output: "Fatal: Failed to load the private key: invalid character '1' at end of key file\n",
+ },
+ }
+ for _, test := range tests {
+ test := test
+ t.Run(test.name, func(t *testing.T) {
+ t.Parallel()
+ importAccountWithExpect(t, test.key, test.output)
+ })
+ }
+}
+
+func TestAccountHelp(t *testing.T) {
+ geth := runGeth(t, "account", "-h")
+ geth.WaitExit()
+ if have, want := geth.ExitStatus(), 0; have != want {
+ t.Errorf("exit error, have %d want %d", have, want)
+ }
+
+ geth = runGeth(t, "account", "import", "-h")
+ geth.WaitExit()
+ if have, want := geth.ExitStatus(), 0; have != want {
+ t.Errorf("exit error, have %d want %d", have, want)
+ }
+}
+
+func importAccountWithExpect(t *testing.T, key string, expected string) {
+ dir := t.TempDir()
+ keyfile := filepath.Join(dir, "key.prv")
+ if err := os.WriteFile(keyfile, []byte(key), 0600); err != nil {
+ t.Error(err)
+ }
+ passwordFile := filepath.Join(dir, "password.txt")
+ if err := os.WriteFile(passwordFile, []byte("foobar"), 0600); err != nil {
+ t.Error(err)
+ }
+ geth := runGeth(t, "--lightkdf", "account", "import", "-password", passwordFile, keyfile)
+ defer geth.ExpectExit()
+ geth.Expect(expected)
+}
+
+func TestAccountNewBadRepeat(t *testing.T) {
+ geth := runGeth(t, "account", "new", "--lightkdf")
+ defer geth.ExpectExit()
+ geth.Expect(`
+Your new account is locked with a password. Please give a password. Do not forget this password.
+!! Unsupported terminal, password will be echoed.
+Password: {{.InputLine "something"}}
+Repeat password: {{.InputLine "something else"}}
+Fatal: Passwords do not match
+`)
+}
+
+func TestAccountUpdate(t *testing.T) {
+ datadir := tmpDatadirWithKeystore(t)
+ geth := runGeth(t, "account", "update",
+ "--datadir", datadir, "--lightkdf",
+ "f466859ead1932d743d622cb74fc058882e8648a")
+ defer geth.ExpectExit()
+ geth.Expect(`
+Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
+!! Unsupported terminal, password will be echoed.
+Password: {{.InputLine "foobar"}}
+Please give a new password. Do not forget this password.
+Password: {{.InputLine "foobar2"}}
+Repeat password: {{.InputLine "foobar2"}}
+`)
+}
+
+func TestWalletImport(t *testing.T) {
+ geth := runGeth(t, "wallet", "import", "--lightkdf", "testdata/guswallet.json")
+ defer geth.ExpectExit()
+ geth.Expect(`
+!! Unsupported terminal, password will be echoed.
+Password: {{.InputLine "foo"}}
+Address: {d4584b5f6229b7be90727b0fc8c6b91bb427821f}
+`)
+
+ files, err := os.ReadDir(filepath.Join(geth.Datadir, "keystore"))
+ if len(files) != 1 {
+ t.Errorf("expected one key file in keystore directory, found %d files (error: %v)", len(files), err)
+ }
+}
+
+func TestWalletImportBadPassword(t *testing.T) {
+ geth := runGeth(t, "wallet", "import", "--lightkdf", "testdata/guswallet.json")
+ defer geth.ExpectExit()
+ geth.Expect(`
+!! Unsupported terminal, password will be echoed.
+Password: {{.InputLine "wrong"}}
+Fatal: could not decrypt key with given password
+`)
+}
+
+func TestUnlockFlag(t *testing.T) {
+ geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t),
+ "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "console", "--exec", "loadScript('testdata/empty.js')")
+ geth.Expect(`
+Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
+!! Unsupported terminal, password will be echoed.
+Password: {{.InputLine "foobar"}}
+undefined
+`)
+ geth.ExpectExit()
+
+ wantMessages := []string{
+ "Unlocked account",
+ "=0xf466859eAD1932D743d622CB74FC058882E8648A",
+ }
+ for _, m := range wantMessages {
+ if !strings.Contains(geth.StderrText(), m) {
+ t.Errorf("stderr text does not contain %q", m)
+ }
+ }
+}
+
+func TestUnlockFlagWrongPassword(t *testing.T) {
+ geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t),
+ "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "console", "--exec", "loadScript('testdata/empty.js')")
+
+ defer geth.ExpectExit()
+ geth.Expect(`
+Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
+!! Unsupported terminal, password will be echoed.
+Password: {{.InputLine "wrong1"}}
+Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 2/3
+Password: {{.InputLine "wrong2"}}
+Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 3/3
+Password: {{.InputLine "wrong3"}}
+Fatal: Failed to unlock account f466859ead1932d743d622cb74fc058882e8648a (could not decrypt key with given password)
+`)
+}
+
+// https://github.com/cryptoecc/ETH-ECC/issues/1785
+func TestUnlockFlagMultiIndex(t *testing.T) {
+ geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t),
+ "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "--unlock", "0,2", "console", "--exec", "loadScript('testdata/empty.js')")
+
+ geth.Expect(`
+Unlocking account 0 | Attempt 1/3
+!! Unsupported terminal, password will be echoed.
+Password: {{.InputLine "foobar"}}
+Unlocking account 2 | Attempt 1/3
+Password: {{.InputLine "foobar"}}
+undefined
+`)
+ geth.ExpectExit()
+
+ wantMessages := []string{
+ "Unlocked account",
+ "=0x7EF5A6135f1FD6a02593eEdC869c6D41D934aef8",
+ "=0x289d485D9771714CCe91D3393D764E1311907ACc",
+ }
+ for _, m := range wantMessages {
+ if !strings.Contains(geth.StderrText(), m) {
+ t.Errorf("stderr text does not contain %q", m)
+ }
+ }
+}
+
+func TestUnlockFlagPasswordFile(t *testing.T) {
+ geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t),
+ "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "--password", "testdata/passwords.txt", "--unlock", "0,2", "console", "--exec", "loadScript('testdata/empty.js')")
+
+ geth.Expect(`
+undefined
+`)
+ geth.ExpectExit()
+
+ wantMessages := []string{
+ "Unlocked account",
+ "=0x7EF5A6135f1FD6a02593eEdC869c6D41D934aef8",
+ "=0x289d485D9771714CCe91D3393D764E1311907ACc",
+ }
+ for _, m := range wantMessages {
+ if !strings.Contains(geth.StderrText(), m) {
+ t.Errorf("stderr text does not contain %q", m)
+ }
+ }
+}
+
+func TestUnlockFlagPasswordFileWrongPassword(t *testing.T) {
+ geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t),
+ "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "--password",
+ "testdata/wrong-passwords.txt", "--unlock", "0,2")
+ defer geth.ExpectExit()
+ geth.Expect(`
+Fatal: Failed to unlock account 0 (could not decrypt key with given password)
+`)
+}
+
+func TestUnlockFlagAmbiguous(t *testing.T) {
+ store := filepath.Join("..", "..", "accounts", "keystore", "testdata", "dupes")
+ geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t),
+ "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "--keystore",
+ store, "--unlock", "f466859ead1932d743d622cb74fc058882e8648a",
+ "console", "--exec", "loadScript('testdata/empty.js')")
+ defer geth.ExpectExit()
+
+ // Helper for the expect template, returns absolute keystore path.
+ geth.SetTemplateFunc("keypath", func(file string) string {
+ abs, _ := filepath.Abs(filepath.Join(store, file))
+ return abs
+ })
+ geth.Expect(`
+Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
+!! Unsupported terminal, password will be echoed.
+Password: {{.InputLine "foobar"}}
+Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a:
+ keystore://{{keypath "1"}}
+ keystore://{{keypath "2"}}
+Testing your password against all of them...
+Your password unlocked keystore://{{keypath "1"}}
+In order to avoid this warning, you need to remove the following duplicate key files:
+ keystore://{{keypath "2"}}
+undefined
+`)
+ geth.ExpectExit()
+
+ wantMessages := []string{
+ "Unlocked account",
+ "=0xf466859eAD1932D743d622CB74FC058882E8648A",
+ }
+ for _, m := range wantMessages {
+ if !strings.Contains(geth.StderrText(), m) {
+ t.Errorf("stderr text does not contain %q", m)
+ }
+ }
+}
+
+func TestUnlockFlagAmbiguousWrongPassword(t *testing.T) {
+ store := filepath.Join("..", "..", "accounts", "keystore", "testdata", "dupes")
+ geth := runMinimalGeth(t, "--port", "0", "--ipcdisable", "--datadir", tmpDatadirWithKeystore(t),
+ "--unlock", "f466859ead1932d743d622cb74fc058882e8648a", "--keystore",
+ store, "--unlock", "f466859ead1932d743d622cb74fc058882e8648a")
+
+ defer geth.ExpectExit()
+
+ // Helper for the expect template, returns absolute keystore path.
+ geth.SetTemplateFunc("keypath", func(file string) string {
+ abs, _ := filepath.Abs(filepath.Join(store, file))
+ return abs
+ })
+ geth.Expect(`
+Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
+!! Unsupported terminal, password will be echoed.
+Password: {{.InputLine "wrong"}}
+Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a:
+ keystore://{{keypath "1"}}
+ keystore://{{keypath "2"}}
+Testing your password against all of them...
+Fatal: None of the listed files could be unlocked.
+`)
+ geth.ExpectExit()
+}
diff --git a/cmd/worldland/chaincmd.go b/cmd/worldland/chaincmd.go
new file mode 100644
index 0000000000..1ddb19eff6
--- /dev/null
+++ b/cmd/worldland/chaincmd.go
@@ -0,0 +1,458 @@
+// Copyright 2015 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package main
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "os"
+ "runtime"
+ "strconv"
+ "sync/atomic"
+ "time"
+
+ "github.com/cryptoecc/ETH-ECC/cmd/utils"
+ "github.com/cryptoecc/ETH-ECC/common"
+ "github.com/cryptoecc/ETH-ECC/common/hexutil"
+ "github.com/cryptoecc/ETH-ECC/core"
+ "github.com/cryptoecc/ETH-ECC/core/rawdb"
+ "github.com/cryptoecc/ETH-ECC/core/state"
+ "github.com/cryptoecc/ETH-ECC/core/types"
+ "github.com/cryptoecc/ETH-ECC/crypto"
+ "github.com/cryptoecc/ETH-ECC/ethdb"
+ "github.com/cryptoecc/ETH-ECC/internal/flags"
+ "github.com/cryptoecc/ETH-ECC/log"
+ "github.com/cryptoecc/ETH-ECC/metrics"
+ "github.com/cryptoecc/ETH-ECC/node"
+ "github.com/urfave/cli/v2"
+)
+
+var (
+ initCommand = &cli.Command{
+ Action: initGenesis,
+ Name: "init",
+ Usage: "Bootstrap and initialize a new genesis block",
+ ArgsUsage: "",
+ Flags: utils.DatabasePathFlags,
+ Description: `
+The init command initializes a new genesis block and definition for the network.
+This is a destructive action and changes the network in which you will be
+participating.
+
+It expects the genesis file as argument.`,
+ }
+ dumpGenesisCommand = &cli.Command{
+ Action: dumpGenesis,
+ Name: "dumpgenesis",
+ Usage: "Dumps genesis block JSON configuration to stdout",
+ ArgsUsage: "",
+ Flags: utils.NetworkFlags,
+ Description: `
+The dumpgenesis command dumps the genesis block configuration in JSON format to stdout.`,
+ }
+ importCommand = &cli.Command{
+ Action: importChain,
+ Name: "import",
+ Usage: "Import a blockchain file",
+ ArgsUsage: " ( ... ) ",
+ Flags: flags.Merge([]cli.Flag{
+ utils.CacheFlag,
+ utils.SyncModeFlag,
+ utils.GCModeFlag,
+ utils.SnapshotFlag,
+ utils.CacheDatabaseFlag,
+ utils.CacheGCFlag,
+ utils.MetricsEnabledFlag,
+ utils.MetricsEnabledExpensiveFlag,
+ utils.MetricsHTTPFlag,
+ utils.MetricsPortFlag,
+ utils.MetricsEnableInfluxDBFlag,
+ utils.MetricsEnableInfluxDBV2Flag,
+ utils.MetricsInfluxDBEndpointFlag,
+ utils.MetricsInfluxDBDatabaseFlag,
+ utils.MetricsInfluxDBUsernameFlag,
+ utils.MetricsInfluxDBPasswordFlag,
+ utils.MetricsInfluxDBTagsFlag,
+ utils.MetricsInfluxDBTokenFlag,
+ utils.MetricsInfluxDBBucketFlag,
+ utils.MetricsInfluxDBOrganizationFlag,
+ utils.TxLookupLimitFlag,
+ }, utils.DatabasePathFlags),
+ Description: `
+The import command imports blocks from an RLP-encoded form. The form can be one file
+with several RLP-encoded blocks, or several files can be used.
+
+If only one file is used, import error will result in failure. If several files are used,
+processing will proceed even if an individual RLP-file import failure occurs.`,
+ }
+ exportCommand = &cli.Command{
+ Action: exportChain,
+ Name: "export",
+ Usage: "Export blockchain into file",
+ ArgsUsage: " [ ]",
+ Flags: flags.Merge([]cli.Flag{
+ utils.CacheFlag,
+ utils.SyncModeFlag,
+ }, utils.DatabasePathFlags),
+ Description: `
+Requires a first argument of the file to write to.
+Optional second and third arguments control the first and
+last block to write. In this mode, the file will be appended
+if already existing. If the file ends with .gz, the output will
+be gzipped.`,
+ }
+ importPreimagesCommand = &cli.Command{
+ Action: importPreimages,
+ Name: "import-preimages",
+ Usage: "Import the preimage database from an RLP stream",
+ ArgsUsage: "",
+ Flags: flags.Merge([]cli.Flag{
+ utils.CacheFlag,
+ utils.SyncModeFlag,
+ }, utils.DatabasePathFlags),
+ Description: `
+The import-preimages command imports hash preimages from an RLP encoded stream.
+It's deprecated, please use "geth db import" instead.
+`,
+ }
+ exportPreimagesCommand = &cli.Command{
+ Action: exportPreimages,
+ Name: "export-preimages",
+ Usage: "Export the preimage database into an RLP stream",
+ ArgsUsage: "",
+ Flags: flags.Merge([]cli.Flag{
+ utils.CacheFlag,
+ utils.SyncModeFlag,
+ }, utils.DatabasePathFlags),
+ Description: `
+The export-preimages command exports hash preimages to an RLP encoded stream.
+It's deprecated, please use "geth db export" instead.
+`,
+ }
+ dumpCommand = &cli.Command{
+ Action: dump,
+ Name: "dump",
+ Usage: "Dump a specific block from storage",
+ ArgsUsage: "[? | ]",
+ Flags: flags.Merge([]cli.Flag{
+ utils.CacheFlag,
+ utils.IterativeOutputFlag,
+ utils.ExcludeCodeFlag,
+ utils.ExcludeStorageFlag,
+ utils.IncludeIncompletesFlag,
+ utils.StartKeyFlag,
+ utils.DumpLimitFlag,
+ }, utils.DatabasePathFlags),
+ Description: `
+This command dumps out the state for a given block (or latest, if none provided).
+`,
+ }
+)
+
+// initGenesis will initialise the given JSON format genesis file and writes it as
+// the zero'd block (i.e. genesis) or will fail hard if it can't succeed.
+func initGenesis(ctx *cli.Context) error {
+ if ctx.Args().Len() != 1 {
+ utils.Fatalf("need genesis.json file as the only argument")
+ }
+ genesisPath := ctx.Args().First()
+ if len(genesisPath) == 0 {
+ utils.Fatalf("invalid path to genesis file")
+ }
+ file, err := os.Open(genesisPath)
+ if err != nil {
+ utils.Fatalf("Failed to read genesis file: %v", err)
+ }
+ defer file.Close()
+
+ genesis := new(core.Genesis)
+ if err := json.NewDecoder(file).Decode(genesis); err != nil {
+ utils.Fatalf("invalid genesis file: %v", err)
+ }
+ // Open and initialise both full and light databases
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+ for _, name := range []string{"chaindata", "lightchaindata"} {
+ chaindb, err := stack.OpenDatabaseWithFreezer(name, 0, 0, ctx.String(utils.AncientFlag.Name), "", false)
+ if err != nil {
+ utils.Fatalf("Failed to open database: %v", err)
+ }
+ _, hash, err := core.SetupGenesisBlock(chaindb, genesis)
+ if err != nil {
+ utils.Fatalf("Failed to write genesis block: %v", err)
+ }
+ chaindb.Close()
+ log.Info("Successfully wrote genesis state", "database", name, "hash", hash)
+ }
+ return nil
+}
+
+func dumpGenesis(ctx *cli.Context) error {
+ // TODO(rjl493456442) support loading from the custom datadir
+ genesis := utils.MakeGenesis(ctx)
+ if genesis == nil {
+ genesis = core.DefaultGenesisBlock()
+ }
+ if err := json.NewEncoder(os.Stdout).Encode(genesis); err != nil {
+ utils.Fatalf("could not encode genesis")
+ }
+ return nil
+}
+
+func importChain(ctx *cli.Context) error {
+ if ctx.Args().Len() < 1 {
+ utils.Fatalf("This command requires an argument.")
+ }
+ // Start metrics export if enabled
+ utils.SetupMetrics(ctx)
+ // Start system runtime metrics collection
+ go metrics.CollectProcessMetrics(3 * time.Second)
+
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+
+ chain, db := utils.MakeChain(ctx, stack)
+ defer db.Close()
+
+ // Start periodically gathering memory profiles
+ var peakMemAlloc, peakMemSys uint64
+ go func() {
+ stats := new(runtime.MemStats)
+ for {
+ runtime.ReadMemStats(stats)
+ if atomic.LoadUint64(&peakMemAlloc) < stats.Alloc {
+ atomic.StoreUint64(&peakMemAlloc, stats.Alloc)
+ }
+ if atomic.LoadUint64(&peakMemSys) < stats.Sys {
+ atomic.StoreUint64(&peakMemSys, stats.Sys)
+ }
+ time.Sleep(5 * time.Second)
+ }
+ }()
+ // Import the chain
+ start := time.Now()
+
+ var importErr error
+
+ if ctx.Args().Len() == 1 {
+ if err := utils.ImportChain(chain, ctx.Args().First()); err != nil {
+ importErr = err
+ log.Error("Import error", "err", err)
+ }
+ } else {
+ for _, arg := range ctx.Args().Slice() {
+ if err := utils.ImportChain(chain, arg); err != nil {
+ importErr = err
+ log.Error("Import error", "file", arg, "err", err)
+ }
+ }
+ }
+ chain.Stop()
+ fmt.Printf("Import done in %v.\n\n", time.Since(start))
+
+ // Output pre-compaction stats mostly to see the import trashing
+ showLeveldbStats(db)
+
+ // Print the memory statistics used by the importing
+ mem := new(runtime.MemStats)
+ runtime.ReadMemStats(mem)
+
+ fmt.Printf("Object memory: %.3f MB current, %.3f MB peak\n", float64(mem.Alloc)/1024/1024, float64(atomic.LoadUint64(&peakMemAlloc))/1024/1024)
+ fmt.Printf("System memory: %.3f MB current, %.3f MB peak\n", float64(mem.Sys)/1024/1024, float64(atomic.LoadUint64(&peakMemSys))/1024/1024)
+ fmt.Printf("Allocations: %.3f million\n", float64(mem.Mallocs)/1000000)
+ fmt.Printf("GC pause: %v\n\n", time.Duration(mem.PauseTotalNs))
+
+ if ctx.Bool(utils.NoCompactionFlag.Name) {
+ return nil
+ }
+
+ // Compact the entire database to more accurately measure disk io and print the stats
+ start = time.Now()
+ fmt.Println("Compacting entire database...")
+ if err := db.Compact(nil, nil); err != nil {
+ utils.Fatalf("Compaction failed: %v", err)
+ }
+ fmt.Printf("Compaction done in %v.\n\n", time.Since(start))
+
+ showLeveldbStats(db)
+ return importErr
+}
+
+func exportChain(ctx *cli.Context) error {
+ if ctx.Args().Len() < 1 {
+ utils.Fatalf("This command requires an argument.")
+ }
+
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+
+ chain, _ := utils.MakeChain(ctx, stack)
+ start := time.Now()
+
+ var err error
+ fp := ctx.Args().First()
+ if ctx.Args().Len() < 3 {
+ err = utils.ExportChain(chain, fp)
+ } else {
+ // This can be improved to allow for numbers larger than 9223372036854775807
+ first, ferr := strconv.ParseInt(ctx.Args().Get(1), 10, 64)
+ last, lerr := strconv.ParseInt(ctx.Args().Get(2), 10, 64)
+ if ferr != nil || lerr != nil {
+ utils.Fatalf("Export error in parsing parameters: block number not an integer\n")
+ }
+ if first < 0 || last < 0 {
+ utils.Fatalf("Export error: block number must be greater than 0\n")
+ }
+ if head := chain.CurrentFastBlock(); uint64(last) > head.NumberU64() {
+ utils.Fatalf("Export error: block number %d larger than head block %d\n", uint64(last), head.NumberU64())
+ }
+ err = utils.ExportAppendChain(chain, fp, uint64(first), uint64(last))
+ }
+
+ if err != nil {
+ utils.Fatalf("Export error: %v\n", err)
+ }
+ fmt.Printf("Export done in %v\n", time.Since(start))
+ return nil
+}
+
+// importPreimages imports preimage data from the specified file.
+func importPreimages(ctx *cli.Context) error {
+ if ctx.Args().Len() < 1 {
+ utils.Fatalf("This command requires an argument.")
+ }
+
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+
+ db := utils.MakeChainDatabase(ctx, stack, false)
+ start := time.Now()
+
+ if err := utils.ImportPreimages(db, ctx.Args().First()); err != nil {
+ utils.Fatalf("Import error: %v\n", err)
+ }
+ fmt.Printf("Import done in %v\n", time.Since(start))
+ return nil
+}
+
+// exportPreimages dumps the preimage data to specified json file in streaming way.
+func exportPreimages(ctx *cli.Context) error {
+ if ctx.Args().Len() < 1 {
+ utils.Fatalf("This command requires an argument.")
+ }
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+
+ db := utils.MakeChainDatabase(ctx, stack, true)
+ start := time.Now()
+
+ if err := utils.ExportPreimages(db, ctx.Args().First()); err != nil {
+ utils.Fatalf("Export error: %v\n", err)
+ }
+ fmt.Printf("Export done in %v\n", time.Since(start))
+ return nil
+}
+
+func parseDumpConfig(ctx *cli.Context, stack *node.Node) (*state.DumpConfig, ethdb.Database, common.Hash, error) {
+ db := utils.MakeChainDatabase(ctx, stack, true)
+ var header *types.Header
+ if ctx.NArg() > 1 {
+ return nil, nil, common.Hash{}, fmt.Errorf("expected 1 argument (number or hash), got %d", ctx.NArg())
+ }
+ if ctx.NArg() == 1 {
+ arg := ctx.Args().First()
+ if hashish(arg) {
+ hash := common.HexToHash(arg)
+ if number := rawdb.ReadHeaderNumber(db, hash); number != nil {
+ header = rawdb.ReadHeader(db, hash, *number)
+ } else {
+ return nil, nil, common.Hash{}, fmt.Errorf("block %x not found", hash)
+ }
+ } else {
+ number, err := strconv.ParseUint(arg, 10, 64)
+ if err != nil {
+ return nil, nil, common.Hash{}, err
+ }
+ if hash := rawdb.ReadCanonicalHash(db, number); hash != (common.Hash{}) {
+ header = rawdb.ReadHeader(db, hash, number)
+ } else {
+ return nil, nil, common.Hash{}, fmt.Errorf("header for block %d not found", number)
+ }
+ }
+ } else {
+ // Use latest
+ header = rawdb.ReadHeadHeader(db)
+ }
+ if header == nil {
+ return nil, nil, common.Hash{}, errors.New("no head block found")
+ }
+ startArg := common.FromHex(ctx.String(utils.StartKeyFlag.Name))
+ var start common.Hash
+ switch len(startArg) {
+ case 0: // common.Hash
+ case 32:
+ start = common.BytesToHash(startArg)
+ case 20:
+ start = crypto.Keccak256Hash(startArg)
+ log.Info("Converting start-address to hash", "address", common.BytesToAddress(startArg), "hash", start.Hex())
+ default:
+ return nil, nil, common.Hash{}, fmt.Errorf("invalid start argument: %x. 20 or 32 hex-encoded bytes required", startArg)
+ }
+ var conf = &state.DumpConfig{
+ SkipCode: ctx.Bool(utils.ExcludeCodeFlag.Name),
+ SkipStorage: ctx.Bool(utils.ExcludeStorageFlag.Name),
+ OnlyWithAddresses: !ctx.Bool(utils.IncludeIncompletesFlag.Name),
+ Start: start.Bytes(),
+ Max: ctx.Uint64(utils.DumpLimitFlag.Name),
+ }
+ log.Info("State dump configured", "block", header.Number, "hash", header.Hash().Hex(),
+ "skipcode", conf.SkipCode, "skipstorage", conf.SkipStorage,
+ "start", hexutil.Encode(conf.Start), "limit", conf.Max)
+ return conf, db, header.Root, nil
+}
+
+func dump(ctx *cli.Context) error {
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+
+ conf, db, root, err := parseDumpConfig(ctx, stack)
+ if err != nil {
+ return err
+ }
+ state, err := state.New(root, state.NewDatabase(db), nil)
+ if err != nil {
+ return err
+ }
+ if ctx.Bool(utils.IterativeOutputFlag.Name) {
+ state.IterativeDump(conf, json.NewEncoder(os.Stdout))
+ } else {
+ if conf.OnlyWithAddresses {
+ fmt.Fprintf(os.Stderr, "If you want to include accounts with missing preimages, you need iterative output, since"+
+ " otherwise the accounts will overwrite each other in the resulting mapping.")
+ return fmt.Errorf("incompatible options")
+ }
+ fmt.Println(string(state.Dump(conf)))
+ }
+ return nil
+}
+
+// hashish returns true for strings that look like hashes.
+func hashish(x string) bool {
+ _, err := strconv.Atoi(x)
+ return err != nil
+}
diff --git a/cmd/worldland/config.go b/cmd/worldland/config.go
new file mode 100644
index 0000000000..38bef4e0d2
--- /dev/null
+++ b/cmd/worldland/config.go
@@ -0,0 +1,345 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package main
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ "os"
+ "reflect"
+ "unicode"
+
+ "github.com/urfave/cli/v2"
+
+ "github.com/cryptoecc/ETH-ECC/accounts/external"
+ "github.com/cryptoecc/ETH-ECC/accounts/keystore"
+ "github.com/cryptoecc/ETH-ECC/accounts/scwallet"
+ "github.com/cryptoecc/ETH-ECC/accounts/usbwallet"
+ "github.com/cryptoecc/ETH-ECC/cmd/utils"
+ "github.com/cryptoecc/ETH-ECC/core/rawdb"
+ "github.com/cryptoecc/ETH-ECC/eth/ethconfig"
+ "github.com/cryptoecc/ETH-ECC/internal/ethapi"
+ "github.com/cryptoecc/ETH-ECC/internal/flags"
+ "github.com/cryptoecc/ETH-ECC/log"
+ "github.com/cryptoecc/ETH-ECC/metrics"
+ "github.com/cryptoecc/ETH-ECC/node"
+ "github.com/cryptoecc/ETH-ECC/params"
+ "github.com/naoina/toml"
+)
+
+var (
+ dumpConfigCommand = &cli.Command{
+ Action: dumpConfig,
+ Name: "dumpconfig",
+ Usage: "Show configuration values",
+ ArgsUsage: "",
+ Flags: flags.Merge(nodeFlags, rpcFlags),
+ Description: `The dumpconfig command shows configuration values.`,
+ }
+
+ configFileFlag = &cli.StringFlag{
+ Name: "config",
+ Usage: "TOML configuration file",
+ Category: flags.EthCategory,
+ }
+)
+
+// These settings ensure that TOML keys use the same names as Go struct fields.
+var tomlSettings = toml.Config{
+ NormFieldName: func(rt reflect.Type, key string) string {
+ return key
+ },
+ FieldToKey: func(rt reflect.Type, field string) string {
+ return field
+ },
+ MissingField: func(rt reflect.Type, field string) error {
+ id := fmt.Sprintf("%s.%s", rt.String(), field)
+ if deprecated(id) {
+ log.Warn("Config field is deprecated and won't have an effect", "name", id)
+ return nil
+ }
+ var link string
+ if unicode.IsUpper(rune(rt.Name()[0])) && rt.PkgPath() != "main" {
+ link = fmt.Sprintf(", see https://godoc.org/%s#%s for available fields", rt.PkgPath(), rt.Name())
+ }
+ return fmt.Errorf("field '%s' is not defined in %s%s", field, rt.String(), link)
+ },
+}
+
+type ethstatsConfig struct {
+ URL string `toml:",omitempty"`
+}
+
+type gethConfig struct {
+ Eth ethconfig.Config
+ Node node.Config
+ Ethstats ethstatsConfig
+ Metrics metrics.Config
+}
+
+func loadConfig(file string, cfg *gethConfig) error {
+ f, err := os.Open(file)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ err = tomlSettings.NewDecoder(bufio.NewReader(f)).Decode(cfg)
+ // Add file name to errors that have a line number.
+ if _, ok := err.(*toml.LineError); ok {
+ err = errors.New(file + ", " + err.Error())
+ }
+ return err
+}
+
+func defaultNodeConfig() node.Config {
+ cfg := node.DefaultConfig
+ cfg.Name = clientIdentifier
+ cfg.Version = params.VersionWithCommit(gitCommit, gitDate)
+ cfg.HTTPModules = append(cfg.HTTPModules, "eth")
+ cfg.WSModules = append(cfg.WSModules, "eth")
+ cfg.IPCPath = "worldland.ipc"
+ return cfg
+}
+
+// makeConfigNode loads geth configuration and creates a blank node instance.
+func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
+ // Load defaults.
+ cfg := gethConfig{
+ Eth: ethconfig.Defaults,
+ Node: defaultNodeConfig(),
+ Metrics: metrics.DefaultConfig,
+ }
+
+ // Load config file.
+ if file := ctx.String(configFileFlag.Name); file != "" {
+ if err := loadConfig(file, &cfg); err != nil {
+ utils.Fatalf("%v", err)
+ }
+ }
+
+ // Apply flags.
+ utils.SetNodeConfig(ctx, &cfg.Node)
+ stack, err := node.New(&cfg.Node)
+ if err != nil {
+ utils.Fatalf("Failed to create the protocol stack: %v", err)
+ }
+ // Node doesn't by default populate account manager backends
+ if err := setAccountManagerBackends(stack); err != nil {
+ utils.Fatalf("Failed to set account manager backends: %v", err)
+ }
+
+ utils.SetEthConfig(ctx, stack, &cfg.Eth)
+ if ctx.IsSet(utils.EthStatsURLFlag.Name) {
+ cfg.Ethstats.URL = ctx.String(utils.EthStatsURLFlag.Name)
+ }
+ applyMetricConfig(ctx, &cfg)
+
+ return stack, cfg
+}
+
+// makeFullNode loads geth configuration and creates the Ethereum backend.
+func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
+ stack, cfg := makeConfigNode(ctx)
+ if ctx.IsSet(utils.OverrideTerminalTotalDifficulty.Name) {
+ cfg.Eth.OverrideTerminalTotalDifficulty = flags.GlobalBig(ctx, utils.OverrideTerminalTotalDifficulty.Name)
+ }
+ if ctx.IsSet(utils.OverrideTerminalTotalDifficultyPassed.Name) {
+ override := ctx.Bool(utils.OverrideTerminalTotalDifficultyPassed.Name)
+ cfg.Eth.OverrideTerminalTotalDifficultyPassed = &override
+ }
+
+ backend, eth := utils.RegisterEthService(stack, &cfg.Eth)
+
+ // Warn users to migrate if they have a legacy freezer format.
+ if eth != nil && !ctx.IsSet(utils.IgnoreLegacyReceiptsFlag.Name) {
+ firstIdx := uint64(0)
+ // Hack to speed up check for mainnet because we know
+ // the first non-empty block.
+ ghash := rawdb.ReadCanonicalHash(eth.ChainDb(), 0)
+ if cfg.Eth.NetworkId == 1 && ghash == params.MainnetGenesisHash {
+ firstIdx = 46147
+ }
+ isLegacy, firstLegacy, err := dbHasLegacyReceipts(eth.ChainDb(), firstIdx)
+ if err != nil {
+ log.Error("Failed to check db for legacy receipts", "err", err)
+ } else if isLegacy {
+ stack.Close()
+ log.Error("Database has receipts with a legacy format", "firstLegacy", firstLegacy)
+ utils.Fatalf("Aborting. Please run `geth db freezer-migrate`.")
+ }
+ }
+
+ // Configure log filter RPC API.
+ filterSystem := utils.RegisterFilterAPI(stack, backend, &cfg.Eth)
+
+ // Configure GraphQL if requested.
+ if ctx.IsSet(utils.GraphQLEnabledFlag.Name) {
+ utils.RegisterGraphQLService(stack, backend, filterSystem, &cfg.Node)
+ }
+
+ // Add the Ethereum Stats daemon if requested.
+ if cfg.Ethstats.URL != "" {
+ utils.RegisterEthStatsService(stack, backend, cfg.Ethstats.URL)
+ }
+ return stack, backend
+}
+
+// dumpConfig is the dumpconfig command.
+func dumpConfig(ctx *cli.Context) error {
+ _, cfg := makeConfigNode(ctx)
+ comment := ""
+
+ if cfg.Eth.Genesis != nil {
+ cfg.Eth.Genesis = nil
+ comment += "# Note: this config doesn't contain the genesis block.\n\n"
+ }
+
+ out, err := tomlSettings.Marshal(&cfg)
+ if err != nil {
+ return err
+ }
+
+ dump := os.Stdout
+ if ctx.NArg() > 0 {
+ dump, err = os.OpenFile(ctx.Args().Get(0), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
+ if err != nil {
+ return err
+ }
+ defer dump.Close()
+ }
+ dump.WriteString(comment)
+ dump.Write(out)
+
+ return nil
+}
+
+func applyMetricConfig(ctx *cli.Context, cfg *gethConfig) {
+ if ctx.IsSet(utils.MetricsEnabledFlag.Name) {
+ cfg.Metrics.Enabled = ctx.Bool(utils.MetricsEnabledFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsEnabledExpensiveFlag.Name) {
+ cfg.Metrics.EnabledExpensive = ctx.Bool(utils.MetricsEnabledExpensiveFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsHTTPFlag.Name) {
+ cfg.Metrics.HTTP = ctx.String(utils.MetricsHTTPFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsPortFlag.Name) {
+ cfg.Metrics.Port = ctx.Int(utils.MetricsPortFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsEnableInfluxDBFlag.Name) {
+ cfg.Metrics.EnableInfluxDB = ctx.Bool(utils.MetricsEnableInfluxDBFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsInfluxDBEndpointFlag.Name) {
+ cfg.Metrics.InfluxDBEndpoint = ctx.String(utils.MetricsInfluxDBEndpointFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsInfluxDBDatabaseFlag.Name) {
+ cfg.Metrics.InfluxDBDatabase = ctx.String(utils.MetricsInfluxDBDatabaseFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsInfluxDBUsernameFlag.Name) {
+ cfg.Metrics.InfluxDBUsername = ctx.String(utils.MetricsInfluxDBUsernameFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsInfluxDBPasswordFlag.Name) {
+ cfg.Metrics.InfluxDBPassword = ctx.String(utils.MetricsInfluxDBPasswordFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsInfluxDBTagsFlag.Name) {
+ cfg.Metrics.InfluxDBTags = ctx.String(utils.MetricsInfluxDBTagsFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsEnableInfluxDBV2Flag.Name) {
+ cfg.Metrics.EnableInfluxDBV2 = ctx.Bool(utils.MetricsEnableInfluxDBV2Flag.Name)
+ }
+ if ctx.IsSet(utils.MetricsInfluxDBTokenFlag.Name) {
+ cfg.Metrics.InfluxDBToken = ctx.String(utils.MetricsInfluxDBTokenFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsInfluxDBBucketFlag.Name) {
+ cfg.Metrics.InfluxDBBucket = ctx.String(utils.MetricsInfluxDBBucketFlag.Name)
+ }
+ if ctx.IsSet(utils.MetricsInfluxDBOrganizationFlag.Name) {
+ cfg.Metrics.InfluxDBOrganization = ctx.String(utils.MetricsInfluxDBOrganizationFlag.Name)
+ }
+}
+
+func deprecated(field string) bool {
+ switch field {
+ case "ethconfig.Config.EVMInterpreter":
+ return true
+ case "ethconfig.Config.EWASMInterpreter":
+ return true
+ default:
+ return false
+ }
+}
+
+func setAccountManagerBackends(stack *node.Node) error {
+ conf := stack.Config()
+ am := stack.AccountManager()
+ keydir := stack.KeyStoreDir()
+ scryptN := keystore.StandardScryptN
+ scryptP := keystore.StandardScryptP
+ if conf.UseLightweightKDF {
+ scryptN = keystore.LightScryptN
+ scryptP = keystore.LightScryptP
+ }
+
+ // Assemble the supported backends
+ if len(conf.ExternalSigner) > 0 {
+ log.Info("Using external signer", "url", conf.ExternalSigner)
+ if extapi, err := external.NewExternalBackend(conf.ExternalSigner); err == nil {
+ am.AddBackend(extapi)
+ return nil
+ } else {
+ return fmt.Errorf("error connecting to external signer: %v", err)
+ }
+ }
+
+ // For now, we're using EITHER external signer OR local signers.
+ // If/when we implement some form of lockfile for USB and keystore wallets,
+ // we can have both, but it's very confusing for the user to see the same
+ // accounts in both externally and locally, plus very racey.
+ am.AddBackend(keystore.NewKeyStore(keydir, scryptN, scryptP))
+ if conf.USB {
+ // Start a USB hub for Ledger hardware wallets
+ if ledgerhub, err := usbwallet.NewLedgerHub(); err != nil {
+ log.Warn(fmt.Sprintf("Failed to start Ledger hub, disabling: %v", err))
+ } else {
+ am.AddBackend(ledgerhub)
+ }
+ // Start a USB hub for Trezor hardware wallets (HID version)
+ if trezorhub, err := usbwallet.NewTrezorHubWithHID(); err != nil {
+ log.Warn(fmt.Sprintf("Failed to start HID Trezor hub, disabling: %v", err))
+ } else {
+ am.AddBackend(trezorhub)
+ }
+ // Start a USB hub for Trezor hardware wallets (WebUSB version)
+ if trezorhub, err := usbwallet.NewTrezorHubWithWebUSB(); err != nil {
+ log.Warn(fmt.Sprintf("Failed to start WebUSB Trezor hub, disabling: %v", err))
+ } else {
+ am.AddBackend(trezorhub)
+ }
+ }
+ if len(conf.SmartCardDaemonPath) > 0 {
+ // Start a smart card hub
+ if schub, err := scwallet.NewHub(conf.SmartCardDaemonPath, scwallet.Scheme, keydir); err != nil {
+ log.Warn(fmt.Sprintf("Failed to start smart card hub, disabling: %v", err))
+ } else {
+ am.AddBackend(schub)
+ }
+ }
+
+ return nil
+}
diff --git a/cmd/worldland/consolecmd.go b/cmd/worldland/consolecmd.go
new file mode 100644
index 0000000000..e9dd6685b5
--- /dev/null
+++ b/cmd/worldland/consolecmd.go
@@ -0,0 +1,180 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package main
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/cryptoecc/ETH-ECC/cmd/utils"
+ "github.com/cryptoecc/ETH-ECC/console"
+ "github.com/cryptoecc/ETH-ECC/internal/flags"
+ "github.com/cryptoecc/ETH-ECC/node"
+ "github.com/cryptoecc/ETH-ECC/rpc"
+ "github.com/urfave/cli/v2"
+)
+
+var (
+ consoleFlags = []cli.Flag{utils.JSpathFlag, utils.ExecFlag, utils.PreloadJSFlag}
+
+ consoleCommand = &cli.Command{
+ Action: localConsole,
+ Name: "console",
+ Usage: "Start an interactive JavaScript environment",
+ Flags: flags.Merge(nodeFlags, rpcFlags, consoleFlags),
+ Description: `
+The Geth console is an interactive shell for the JavaScript runtime environment
+which exposes a node admin interface as well as the Ðapp JavaScript API.
+See https://geth.ethereum.org/docs/interface/javascript-console.`,
+ }
+
+ attachCommand = &cli.Command{
+ Action: remoteConsole,
+ Name: "attach",
+ Usage: "Start an interactive JavaScript environment (connect to node)",
+ ArgsUsage: "[endpoint]",
+ Flags: flags.Merge([]cli.Flag{utils.DataDirFlag}, consoleFlags),
+ Description: `
+The Geth console is an interactive shell for the JavaScript runtime environment
+which exposes a node admin interface as well as the Ðapp JavaScript API.
+See https://geth.ethereum.org/docs/interface/javascript-console.
+This command allows to open a console on a running geth node.`,
+ }
+
+ javascriptCommand = &cli.Command{
+ Action: ephemeralConsole,
+ Name: "js",
+ Usage: "(DEPRECATED) Execute the specified JavaScript files",
+ ArgsUsage: " [jsfile...]",
+ Flags: flags.Merge(nodeFlags, consoleFlags),
+ Description: `
+The JavaScript VM exposes a node admin interface as well as the Ðapp
+JavaScript API. See https://geth.ethereum.org/docs/interface/javascript-console`,
+ }
+)
+
+// localConsole starts a new geth node, attaching a JavaScript console to it at the
+// same time.
+func localConsole(ctx *cli.Context) error {
+ // Create and start the node based on the CLI flags
+ prepare(ctx)
+ stack, backend := makeFullNode(ctx)
+ startNode(ctx, stack, backend, true)
+ defer stack.Close()
+
+ // Attach to the newly started node and create the JavaScript console.
+ client, err := stack.Attach()
+ if err != nil {
+ return fmt.Errorf("Failed to attach to the inproc geth: %v", err)
+ }
+ config := console.Config{
+ DataDir: utils.MakeDataDir(ctx),
+ DocRoot: ctx.String(utils.JSpathFlag.Name),
+ Client: client,
+ Preload: utils.MakeConsolePreloads(ctx),
+ }
+ console, err := console.New(config)
+ if err != nil {
+ return fmt.Errorf("Failed to start the JavaScript console: %v", err)
+ }
+ defer console.Stop(false)
+
+ // If only a short execution was requested, evaluate and return.
+ if script := ctx.String(utils.ExecFlag.Name); script != "" {
+ console.Evaluate(script)
+ return nil
+ }
+
+ // Track node shutdown and stop the console when it goes down.
+ // This happens when SIGTERM is sent to the process.
+ go func() {
+ stack.Wait()
+ console.StopInteractive()
+ }()
+
+ // Print the welcome screen and enter interactive mode.
+ console.Welcome()
+ console.Interactive()
+ return nil
+}
+
+// remoteConsole will connect to a remote geth instance, attaching a JavaScript
+// console to it.
+func remoteConsole(ctx *cli.Context) error {
+ if ctx.Args().Len() > 1 {
+ utils.Fatalf("invalid command-line: too many arguments")
+ }
+
+ endpoint := ctx.Args().First()
+ if endpoint == "" {
+ cfg := defaultNodeConfig()
+ utils.SetDataDir(ctx, &cfg)
+ endpoint = cfg.IPCEndpoint()
+ }
+ client, err := dialRPC(endpoint)
+ if err != nil {
+ utils.Fatalf("Unable to attach to remote geth: %v", err)
+ }
+ config := console.Config{
+ DataDir: utils.MakeDataDir(ctx),
+ DocRoot: ctx.String(utils.JSpathFlag.Name),
+ Client: client,
+ Preload: utils.MakeConsolePreloads(ctx),
+ }
+ console, err := console.New(config)
+ if err != nil {
+ utils.Fatalf("Failed to start the JavaScript console: %v", err)
+ }
+ defer console.Stop(false)
+
+ if script := ctx.String(utils.ExecFlag.Name); script != "" {
+ console.Evaluate(script)
+ return nil
+ }
+
+ // Otherwise print the welcome screen and enter interactive mode
+ console.Welcome()
+ console.Interactive()
+ return nil
+}
+
+// ephemeralConsole starts a new geth node, attaches an ephemeral JavaScript
+// console to it, executes each of the files specified as arguments and tears
+// everything down.
+func ephemeralConsole(ctx *cli.Context) error {
+ var b strings.Builder
+ for _, file := range ctx.Args().Slice() {
+ b.Write([]byte(fmt.Sprintf("loadScript('%s');", file)))
+ }
+ utils.Fatalf(`The "js" command is deprecated. Please use the following instead:
+geth --exec "%s" console`, b.String())
+ return nil
+}
+
+// dialRPC returns a RPC client which connects to the given endpoint.
+// The check for empty endpoint implements the defaulting logic
+// for "geth attach" with no argument.
+func dialRPC(endpoint string) (*rpc.Client, error) {
+ if endpoint == "" {
+ endpoint = node.DefaultIPCEndpoint(clientIdentifier)
+ } else if strings.HasPrefix(endpoint, "rpc:") || strings.HasPrefix(endpoint, "ipc:") {
+ // Backwards compatibility with geth < 1.5 which required
+ // these prefixes.
+ endpoint = endpoint[4:]
+ }
+ return rpc.Dial(endpoint)
+}
diff --git a/cmd/worldland/consolecmd_test.go b/cmd/worldland/consolecmd_test.go
new file mode 100644
index 0000000000..93c5b7b38d
--- /dev/null
+++ b/cmd/worldland/consolecmd_test.go
@@ -0,0 +1,162 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package main
+
+import (
+ "crypto/rand"
+ "math/big"
+ "path/filepath"
+ "runtime"
+ "strconv"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/cryptoecc/ETH-ECC/params"
+)
+
+const (
+ ipcAPIs = "admin:1.0 debug:1.0 engine:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0"
+ httpAPIs = "eth:1.0 net:1.0 rpc:1.0 web3:1.0"
+)
+
+// spawns geth with the given command line args, using a set of flags to minimise
+// memory and disk IO. If the args don't set --datadir, the
+// child g gets a temporary data directory.
+func runMinimalGeth(t *testing.T, args ...string) *testgeth {
+ // --ropsten to make the 'writing genesis to disk' faster (no accounts)
+ // --networkid=1337 to avoid cache bump
+ // --syncmode=full to avoid allocating fast sync bloom
+ allArgs := []string{"--ropsten", "--networkid", "1337", "--authrpc.port", "0", "--syncmode=full", "--port", "0",
+ "--nat", "none", "--nodiscover", "--maxpeers", "0", "--cache", "64",
+ "--datadir.minfreedisk", "0"}
+ return runGeth(t, append(allArgs, args...)...)
+}
+
+// Tests that a node embedded within a console can be started up properly and
+// then terminated by closing the input stream.
+func TestConsoleWelcome(t *testing.T) {
+ coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
+
+ // Start a geth console, make sure it's cleaned up and terminate the console
+ geth := runMinimalGeth(t, "--miner.etherbase", coinbase, "console")
+
+ // Gather all the infos the welcome message needs to contain
+ geth.SetTemplateFunc("goos", func() string { return runtime.GOOS })
+ geth.SetTemplateFunc("goarch", func() string { return runtime.GOARCH })
+ geth.SetTemplateFunc("gover", runtime.Version)
+ geth.SetTemplateFunc("gethver", func() string { return params.VersionWithCommit("", "") })
+ geth.SetTemplateFunc("niltime", func() string {
+ return time.Unix(0, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)")
+ })
+ geth.SetTemplateFunc("apis", func() string { return ipcAPIs })
+
+ // Verify the actual welcome message to the required template
+ geth.Expect(`
+Welcome to the Worldland JavaScript console!
+
+instance: Worldland/v{{gethver}}/{{goos}}-{{goarch}}/{{gover}}
+coinbase: {{.Etherbase}}
+at block: 0 ({{niltime}})
+ datadir: {{.Datadir}}
+ modules: {{apis}}
+
+To exit, press ctrl-d or type exit
+> {{.InputLine "exit"}}
+`)
+ geth.ExpectExit()
+}
+
+// Tests that a console can be attached to a running node via various means.
+func TestAttachWelcome(t *testing.T) {
+ var (
+ ipc string
+ httpPort string
+ wsPort string
+ )
+ // Configure the instance for IPC attachment
+ if runtime.GOOS == "windows" {
+ ipc = `\\.\pipe\geth` + strconv.Itoa(trulyRandInt(100000, 999999))
+ } else {
+ ipc = filepath.Join(t.TempDir(), "geth.ipc")
+ }
+ // And HTTP + WS attachment
+ p := trulyRandInt(1024, 65533) // Yeah, sometimes this will fail, sorry :P
+ httpPort = strconv.Itoa(p)
+ wsPort = strconv.Itoa(p + 1)
+ geth := runMinimalGeth(t, "--miner.etherbase", "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182",
+ "--ipcpath", ipc,
+ "--http", "--http.port", httpPort,
+ "--ws", "--ws.port", wsPort)
+ t.Run("ipc", func(t *testing.T) {
+ waitForEndpoint(t, ipc, 3*time.Second)
+ testAttachWelcome(t, geth, "ipc:"+ipc, ipcAPIs)
+ })
+ t.Run("http", func(t *testing.T) {
+ endpoint := "http://127.0.0.1:" + httpPort
+ waitForEndpoint(t, endpoint, 3*time.Second)
+ testAttachWelcome(t, geth, endpoint, httpAPIs)
+ })
+ t.Run("ws", func(t *testing.T) {
+ endpoint := "ws://127.0.0.1:" + wsPort
+ waitForEndpoint(t, endpoint, 3*time.Second)
+ testAttachWelcome(t, geth, endpoint, httpAPIs)
+ })
+ geth.ExpectExit()
+}
+
+func testAttachWelcome(t *testing.T, geth *testgeth, endpoint, apis string) {
+ // Attach to a running geth note and terminate immediately
+ attach := runGeth(t, "attach", endpoint)
+ defer attach.ExpectExit()
+ attach.CloseStdin()
+
+ // Gather all the infos the welcome message needs to contain
+ attach.SetTemplateFunc("goos", func() string { return runtime.GOOS })
+ attach.SetTemplateFunc("goarch", func() string { return runtime.GOARCH })
+ attach.SetTemplateFunc("gover", runtime.Version)
+ attach.SetTemplateFunc("gethver", func() string { return params.VersionWithCommit("", "") })
+ attach.SetTemplateFunc("etherbase", func() string { return geth.Etherbase })
+ attach.SetTemplateFunc("niltime", func() string {
+ return time.Unix(0, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)")
+ })
+ attach.SetTemplateFunc("ipc", func() bool { return strings.HasPrefix(endpoint, "ipc") })
+ attach.SetTemplateFunc("datadir", func() string { return geth.Datadir })
+ attach.SetTemplateFunc("apis", func() string { return apis })
+
+ // Verify the actual welcome message to the required template
+ attach.Expect(`
+Welcome to the Worldland JavaScript console!
+
+instance: Worldland/v{{gethver}}/{{goos}}-{{goarch}}/{{gover}}
+coinbase: {{etherbase}}
+at block: 0 ({{niltime}}){{if ipc}}
+ datadir: {{datadir}}{{end}}
+ modules: {{apis}}
+
+To exit, press ctrl-d or type exit
+> {{.InputLine "exit" }}
+`)
+ attach.ExpectExit()
+}
+
+// trulyRandInt generates a crypto random integer used by the console tests to
+// not clash network ports with other tests running concurrently.
+func trulyRandInt(lo, hi int) int {
+ num, _ := rand.Int(rand.Reader, big.NewInt(int64(hi-lo)))
+ return int(num.Int64()) + lo
+}
diff --git a/cmd/worldland/dao_test.go b/cmd/worldland/dao_test.go
new file mode 100644
index 0000000000..3afd044829
--- /dev/null
+++ b/cmd/worldland/dao_test.go
@@ -0,0 +1,152 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package main
+
+import (
+ "math/big"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/cryptoecc/ETH-ECC/common"
+ "github.com/cryptoecc/ETH-ECC/core/rawdb"
+ "github.com/cryptoecc/ETH-ECC/params"
+)
+
+// Genesis block for nodes which don't care about the DAO fork (i.e. not configured)
+var daoOldGenesis = `{
+ "alloc" : {},
+ "coinbase" : "0x0000000000000000000000000000000000000000",
+ "difficulty" : "0x20000",
+ "extraData" : "",
+ "gasLimit" : "0x2fefd8",
+ "nonce" : "0x0000000000000042",
+ "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "timestamp" : "0x00",
+ "config" : {
+ "homesteadBlock" : 0
+ }
+}`
+
+// Genesis block for nodes which actively oppose the DAO fork
+var daoNoForkGenesis = `{
+ "alloc" : {},
+ "coinbase" : "0x0000000000000000000000000000000000000000",
+ "difficulty" : "0x20000",
+ "extraData" : "",
+ "gasLimit" : "0x2fefd8",
+ "nonce" : "0x0000000000000042",
+ "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "timestamp" : "0x00",
+ "config" : {
+ "homesteadBlock" : 0,
+ "daoForkBlock" : 314,
+ "daoForkSupport" : false
+ }
+}`
+
+// Genesis block for nodes which actively support the DAO fork
+var daoProForkGenesis = `{
+ "alloc" : {},
+ "coinbase" : "0x0000000000000000000000000000000000000000",
+ "difficulty" : "0x20000",
+ "extraData" : "",
+ "gasLimit" : "0x2fefd8",
+ "nonce" : "0x0000000000000042",
+ "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "timestamp" : "0x00",
+ "config" : {
+ "homesteadBlock" : 0,
+ "daoForkBlock" : 314,
+ "daoForkSupport" : true
+ }
+}`
+
+var daoGenesisHash = common.HexToHash("5e1fc79cb4ffa4739177b5408045cd5d51c6cf766133f23f7cd72ee1f8d790e0")
+var daoGenesisForkBlock = big.NewInt(314)
+
+// TestDAOForkBlockNewChain tests that the DAO hard-fork number and the nodes support/opposition is correctly
+// set in the database after various initialization procedures and invocations.
+func TestDAOForkBlockNewChain(t *testing.T) {
+ for i, arg := range []struct {
+ genesis string
+ expectBlock *big.Int
+ expectVote bool
+ }{
+ // Test DAO Default Mainnet
+ {"", params.MainnetChainConfig.DAOForkBlock, true},
+ // test DAO Init Old Privnet
+ {daoOldGenesis, nil, false},
+ // test DAO Default No Fork Privnet
+ {daoNoForkGenesis, daoGenesisForkBlock, false},
+ // test DAO Default Pro Fork Privnet
+ {daoProForkGenesis, daoGenesisForkBlock, true},
+ } {
+ testDAOForkBlockNewChain(t, i, arg.genesis, arg.expectBlock, arg.expectVote)
+ }
+}
+
+func testDAOForkBlockNewChain(t *testing.T, test int, genesis string, expectBlock *big.Int, expectVote bool) {
+ // Create a temporary data directory to use and inspect later
+ datadir := t.TempDir()
+
+ // Start a Geth instance with the requested flags set and immediately terminate
+ if genesis != "" {
+ json := filepath.Join(datadir, "genesis.json")
+ if err := os.WriteFile(json, []byte(genesis), 0600); err != nil {
+ t.Fatalf("test %d: failed to write genesis file: %v", test, err)
+ }
+ runGeth(t, "--datadir", datadir, "--networkid", "1337", "init", json).WaitExit()
+ } else {
+ // Force chain initialization
+ args := []string{"--port", "0", "--networkid", "1337", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--ipcdisable", "--datadir", datadir}
+ runGeth(t, append(args, []string{"--exec", "2+2", "console"}...)...).WaitExit()
+ }
+ // Retrieve the DAO config flag from the database
+ path := filepath.Join(datadir, "geth", "chaindata")
+ db, err := rawdb.NewLevelDBDatabase(path, 0, 0, "", false)
+ if err != nil {
+ t.Fatalf("test %d: failed to open test database: %v", test, err)
+ }
+ defer db.Close()
+
+ genesisHash := common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")
+ if genesis != "" {
+ genesisHash = daoGenesisHash
+ }
+ config := rawdb.ReadChainConfig(db, genesisHash)
+ if config == nil {
+ t.Errorf("test %d: failed to retrieve chain config: %v", test, err)
+ return // we want to return here, the other checks can't make it past this point (nil panic).
+ }
+ // Validate the DAO hard-fork block number against the expected value
+ if config.DAOForkBlock == nil {
+ if expectBlock != nil {
+ t.Errorf("test %d: dao hard-fork block mismatch: have nil, want %v", test, expectBlock)
+ }
+ } else if expectBlock == nil {
+ t.Errorf("test %d: dao hard-fork block mismatch: have %v, want nil", test, config.DAOForkBlock)
+ } else if config.DAOForkBlock.Cmp(expectBlock) != 0 {
+ t.Errorf("test %d: dao hard-fork block mismatch: have %v, want %v", test, config.DAOForkBlock, expectBlock)
+ }
+ if config.DAOForkSupport != expectVote {
+ t.Errorf("test %d: dao hard-fork support mismatch: have %v, want %v", test, config.DAOForkSupport, expectVote)
+ }
+}
diff --git a/cmd/worldland/dbcmd.go b/cmd/worldland/dbcmd.go
new file mode 100644
index 0000000000..a09f2a5de3
--- /dev/null
+++ b/cmd/worldland/dbcmd.go
@@ -0,0 +1,836 @@
+// Copyright 2021 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "os"
+ "os/signal"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "syscall"
+ "time"
+
+ "github.com/cryptoecc/ETH-ECC/cmd/utils"
+ "github.com/cryptoecc/ETH-ECC/common"
+ "github.com/cryptoecc/ETH-ECC/common/hexutil"
+ "github.com/cryptoecc/ETH-ECC/console/prompt"
+ "github.com/cryptoecc/ETH-ECC/core/rawdb"
+ "github.com/cryptoecc/ETH-ECC/core/state/snapshot"
+ "github.com/cryptoecc/ETH-ECC/core/types"
+ "github.com/cryptoecc/ETH-ECC/crypto"
+ "github.com/cryptoecc/ETH-ECC/ethdb"
+ "github.com/cryptoecc/ETH-ECC/internal/flags"
+ "github.com/cryptoecc/ETH-ECC/log"
+ "github.com/cryptoecc/ETH-ECC/trie"
+ "github.com/olekukonko/tablewriter"
+ "github.com/urfave/cli/v2"
+)
+
+var (
+ removedbCommand = &cli.Command{
+ Action: removeDB,
+ Name: "removedb",
+ Usage: "Remove blockchain and state databases",
+ ArgsUsage: "",
+ Flags: utils.DatabasePathFlags,
+ Description: `
+Remove blockchain and state databases`,
+ }
+ dbCommand = &cli.Command{
+ Name: "db",
+ Usage: "Low level database operations",
+ ArgsUsage: "",
+ Subcommands: []*cli.Command{
+ dbInspectCmd,
+ dbStatCmd,
+ dbCompactCmd,
+ dbGetCmd,
+ dbDeleteCmd,
+ dbPutCmd,
+ dbGetSlotsCmd,
+ dbDumpFreezerIndex,
+ dbImportCmd,
+ dbExportCmd,
+ dbMetadataCmd,
+ dbMigrateFreezerCmd,
+ dbCheckStateContentCmd,
+ },
+ }
+ dbInspectCmd = &cli.Command{
+ Action: inspect,
+ Name: "inspect",
+ ArgsUsage: " ",
+ Flags: flags.Merge([]cli.Flag{
+ utils.SyncModeFlag,
+ }, utils.NetworkFlags, utils.DatabasePathFlags),
+ Usage: "Inspect the storage size for each type of data in the database",
+ Description: `This commands iterates the entire database. If the optional 'prefix' and 'start' arguments are provided, then the iteration is limited to the given subset of data.`,
+ }
+ dbCheckStateContentCmd = &cli.Command{
+ Action: checkStateContent,
+ Name: "check-state-content",
+ ArgsUsage: "",
+ Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags),
+ Usage: "Verify that state data is cryptographically correct",
+ Description: `This command iterates the entire database for 32-byte keys, looking for rlp-encoded trie nodes.
+For each trie node encountered, it checks that the key corresponds to the keccak256(value). If this is not true, this indicates
+a data corruption.`,
+ }
+ dbStatCmd = &cli.Command{
+ Action: dbStats,
+ Name: "stats",
+ Usage: "Print leveldb statistics",
+ Flags: flags.Merge([]cli.Flag{
+ utils.SyncModeFlag,
+ }, utils.NetworkFlags, utils.DatabasePathFlags),
+ }
+ dbCompactCmd = &cli.Command{
+ Action: dbCompact,
+ Name: "compact",
+ Usage: "Compact leveldb database. WARNING: May take a very long time",
+ Flags: flags.Merge([]cli.Flag{
+ utils.SyncModeFlag,
+ utils.CacheFlag,
+ utils.CacheDatabaseFlag,
+ }, utils.NetworkFlags, utils.DatabasePathFlags),
+ Description: `This command performs a database compaction.
+WARNING: This operation may take a very long time to finish, and may cause database
+corruption if it is aborted during execution'!`,
+ }
+ dbGetCmd = &cli.Command{
+ Action: dbGet,
+ Name: "get",
+ Usage: "Show the value of a database key",
+ ArgsUsage: "",
+ Flags: flags.Merge([]cli.Flag{
+ utils.SyncModeFlag,
+ }, utils.NetworkFlags, utils.DatabasePathFlags),
+ Description: "This command looks up the specified database key from the database.",
+ }
+ dbDeleteCmd = &cli.Command{
+ Action: dbDelete,
+ Name: "delete",
+ Usage: "Delete a database key (WARNING: may corrupt your database)",
+ ArgsUsage: "",
+ Flags: flags.Merge([]cli.Flag{
+ utils.SyncModeFlag,
+ }, utils.NetworkFlags, utils.DatabasePathFlags),
+ Description: `This command deletes the specified database key from the database.
+WARNING: This is a low-level operation which may cause database corruption!`,
+ }
+ dbPutCmd = &cli.Command{
+ Action: dbPut,
+ Name: "put",
+ Usage: "Set the value of a database key (WARNING: may corrupt your database)",
+ ArgsUsage: " ",
+ Flags: flags.Merge([]cli.Flag{
+ utils.SyncModeFlag,
+ }, utils.NetworkFlags, utils.DatabasePathFlags),
+ Description: `This command sets a given database key to the given value.
+WARNING: This is a low-level operation which may cause database corruption!`,
+ }
+ dbGetSlotsCmd = &cli.Command{
+ Action: dbDumpTrie,
+ Name: "dumptrie",
+ Usage: "Show the storage key/values of a given storage trie",
+ ArgsUsage: " ",
+ Flags: flags.Merge([]cli.Flag{
+ utils.SyncModeFlag,
+ }, utils.NetworkFlags, utils.DatabasePathFlags),
+ Description: "This command looks up the specified database key from the database.",
+ }
+ dbDumpFreezerIndex = &cli.Command{
+ Action: freezerInspect,
+ Name: "freezer-index",
+ Usage: "Dump out the index of a specific freezer table",
+ ArgsUsage: " ",
+ Flags: flags.Merge([]cli.Flag{
+ utils.SyncModeFlag,
+ }, utils.NetworkFlags, utils.DatabasePathFlags),
+ Description: "This command displays information about the freezer index.",
+ }
+ dbImportCmd = &cli.Command{
+ Action: importLDBdata,
+ Name: "import",
+ Usage: "Imports leveldb-data from an exported RLP dump.",
+ ArgsUsage: " has .gz suffix, gzip compression will be used.",
+ ArgsUsage: " ",
+ Flags: flags.Merge([]cli.Flag{
+ utils.SyncModeFlag,
+ }, utils.NetworkFlags, utils.DatabasePathFlags),
+ Description: "Exports the specified chain data to an RLP encoded stream, optionally gzip-compressed.",
+ }
+ dbMetadataCmd = &cli.Command{
+ Action: showMetaData,
+ Name: "metadata",
+ Usage: "Shows metadata about the chain status.",
+ Flags: flags.Merge([]cli.Flag{
+ utils.SyncModeFlag,
+ }, utils.NetworkFlags, utils.DatabasePathFlags),
+ Description: "Shows metadata about the chain status.",
+ }
+ dbMigrateFreezerCmd = &cli.Command{
+ Action: freezerMigrate,
+ Name: "freezer-migrate",
+ Usage: "Migrate legacy parts of the freezer. (WARNING: may take a long time)",
+ ArgsUsage: "",
+ Flags: flags.Merge([]cli.Flag{
+ utils.SyncModeFlag,
+ }, utils.NetworkFlags, utils.DatabasePathFlags),
+ Description: `The freezer-migrate command checks your database for receipts in a legacy format and updates those.
+WARNING: please back-up the receipt files in your ancients before running this command.`,
+ }
+)
+
+func removeDB(ctx *cli.Context) error {
+ stack, config := makeConfigNode(ctx)
+
+ // Remove the full node state database
+ path := stack.ResolvePath("chaindata")
+ if common.FileExist(path) {
+ confirmAndRemoveDB(path, "full node state database")
+ } else {
+ log.Info("Full node state database missing", "path", path)
+ }
+ // Remove the full node ancient database
+ path = config.Eth.DatabaseFreezer
+ switch {
+ case path == "":
+ path = filepath.Join(stack.ResolvePath("chaindata"), "ancient")
+ case !filepath.IsAbs(path):
+ path = config.Node.ResolvePath(path)
+ }
+ if common.FileExist(path) {
+ confirmAndRemoveDB(path, "full node ancient database")
+ } else {
+ log.Info("Full node ancient database missing", "path", path)
+ }
+ // Remove the light node database
+ path = stack.ResolvePath("lightchaindata")
+ if common.FileExist(path) {
+ confirmAndRemoveDB(path, "light node database")
+ } else {
+ log.Info("Light node database missing", "path", path)
+ }
+ return nil
+}
+
+// confirmAndRemoveDB prompts the user for a last confirmation and removes the
+// folder if accepted.
+func confirmAndRemoveDB(database string, kind string) {
+ confirm, err := prompt.Stdin.PromptConfirm(fmt.Sprintf("Remove %s (%s)?", kind, database))
+ switch {
+ case err != nil:
+ utils.Fatalf("%v", err)
+ case !confirm:
+ log.Info("Database deletion skipped", "path", database)
+ default:
+ start := time.Now()
+ filepath.Walk(database, func(path string, info os.FileInfo, err error) error {
+ // If we're at the top level folder, recurse into
+ if path == database {
+ return nil
+ }
+ // Delete all the files, but not subfolders
+ if !info.IsDir() {
+ os.Remove(path)
+ return nil
+ }
+ return filepath.SkipDir
+ })
+ log.Info("Database successfully deleted", "path", database, "elapsed", common.PrettyDuration(time.Since(start)))
+ }
+}
+
+func inspect(ctx *cli.Context) error {
+ var (
+ prefix []byte
+ start []byte
+ )
+ if ctx.NArg() > 2 {
+ return fmt.Errorf("max 2 arguments: %v", ctx.Command.ArgsUsage)
+ }
+ if ctx.NArg() >= 1 {
+ if d, err := hexutil.Decode(ctx.Args().Get(0)); err != nil {
+ return fmt.Errorf("failed to hex-decode 'prefix': %v", err)
+ } else {
+ prefix = d
+ }
+ }
+ if ctx.NArg() >= 2 {
+ if d, err := hexutil.Decode(ctx.Args().Get(1)); err != nil {
+ return fmt.Errorf("failed to hex-decode 'start': %v", err)
+ } else {
+ start = d
+ }
+ }
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+
+ db := utils.MakeChainDatabase(ctx, stack, true)
+ defer db.Close()
+
+ return rawdb.InspectDatabase(db, prefix, start)
+}
+
+func checkStateContent(ctx *cli.Context) error {
+ var (
+ prefix []byte
+ start []byte
+ )
+ if ctx.NArg() > 1 {
+ return fmt.Errorf("max 1 argument: %v", ctx.Command.ArgsUsage)
+ }
+ if ctx.NArg() > 0 {
+ if d, err := hexutil.Decode(ctx.Args().First()); err != nil {
+ return fmt.Errorf("failed to hex-decode 'start': %v", err)
+ } else {
+ start = d
+ }
+ }
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+
+ db := utils.MakeChainDatabase(ctx, stack, true)
+ defer db.Close()
+ var (
+ it = rawdb.NewKeyLengthIterator(db.NewIterator(prefix, start), 32)
+ hasher = crypto.NewKeccakState()
+ got = make([]byte, 32)
+ errs int
+ count int
+ startTime = time.Now()
+ lastLog = time.Now()
+ )
+ for it.Next() {
+ count++
+ k := it.Key()
+ v := it.Value()
+ hasher.Reset()
+ hasher.Write(v)
+ hasher.Read(got)
+ if !bytes.Equal(k, got) {
+ errs++
+ fmt.Printf("Error at %#x\n", k)
+ fmt.Printf(" Hash: %#x\n", got)
+ fmt.Printf(" Data: %#x\n", v)
+ }
+ if time.Since(lastLog) > 8*time.Second {
+ log.Info("Iterating the database", "at", fmt.Sprintf("%#x", k), "elapsed", common.PrettyDuration(time.Since(startTime)))
+ lastLog = time.Now()
+ }
+ }
+ if err := it.Error(); err != nil {
+ return err
+ }
+ log.Info("Iterated the state content", "errors", errs, "items", count)
+ return nil
+}
+
+func showLeveldbStats(db ethdb.KeyValueStater) {
+ if stats, err := db.Stat("leveldb.stats"); err != nil {
+ log.Warn("Failed to read database stats", "error", err)
+ } else {
+ fmt.Println(stats)
+ }
+ if ioStats, err := db.Stat("leveldb.iostats"); err != nil {
+ log.Warn("Failed to read database iostats", "error", err)
+ } else {
+ fmt.Println(ioStats)
+ }
+}
+
+func dbStats(ctx *cli.Context) error {
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+
+ db := utils.MakeChainDatabase(ctx, stack, true)
+ defer db.Close()
+
+ showLeveldbStats(db)
+ return nil
+}
+
+func dbCompact(ctx *cli.Context) error {
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+
+ db := utils.MakeChainDatabase(ctx, stack, false)
+ defer db.Close()
+
+ log.Info("Stats before compaction")
+ showLeveldbStats(db)
+
+ log.Info("Triggering compaction")
+ if err := db.Compact(nil, nil); err != nil {
+ log.Info("Compact err", "error", err)
+ return err
+ }
+ log.Info("Stats after compaction")
+ showLeveldbStats(db)
+ return nil
+}
+
+// dbGet shows the value of a given database key
+func dbGet(ctx *cli.Context) error {
+ if ctx.NArg() != 1 {
+ return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
+ }
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+
+ db := utils.MakeChainDatabase(ctx, stack, true)
+ defer db.Close()
+
+ key, err := common.ParseHexOrString(ctx.Args().Get(0))
+ if err != nil {
+ log.Info("Could not decode the key", "error", err)
+ return err
+ }
+
+ data, err := db.Get(key)
+ if err != nil {
+ log.Info("Get operation failed", "key", fmt.Sprintf("%#x", key), "error", err)
+ return err
+ }
+ fmt.Printf("key %#x: %#x\n", key, data)
+ return nil
+}
+
+// dbDelete deletes a key from the database
+func dbDelete(ctx *cli.Context) error {
+ if ctx.NArg() != 1 {
+ return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
+ }
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+
+ db := utils.MakeChainDatabase(ctx, stack, false)
+ defer db.Close()
+
+ key, err := common.ParseHexOrString(ctx.Args().Get(0))
+ if err != nil {
+ log.Info("Could not decode the key", "error", err)
+ return err
+ }
+ data, err := db.Get(key)
+ if err == nil {
+ fmt.Printf("Previous value: %#x\n", data)
+ }
+ if err = db.Delete(key); err != nil {
+ log.Info("Delete operation returned an error", "key", fmt.Sprintf("%#x", key), "error", err)
+ return err
+ }
+ return nil
+}
+
+// dbPut overwrite a value in the database
+func dbPut(ctx *cli.Context) error {
+ if ctx.NArg() != 2 {
+ return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
+ }
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+
+ db := utils.MakeChainDatabase(ctx, stack, false)
+ defer db.Close()
+
+ var (
+ key []byte
+ value []byte
+ data []byte
+ err error
+ )
+ key, err = common.ParseHexOrString(ctx.Args().Get(0))
+ if err != nil {
+ log.Info("Could not decode the key", "error", err)
+ return err
+ }
+ value, err = hexutil.Decode(ctx.Args().Get(1))
+ if err != nil {
+ log.Info("Could not decode the value", "error", err)
+ return err
+ }
+ data, err = db.Get(key)
+ if err == nil {
+ fmt.Printf("Previous value: %#x\n", data)
+ }
+ return db.Put(key, value)
+}
+
+// dbDumpTrie shows the key-value slots of a given storage trie
+func dbDumpTrie(ctx *cli.Context) error {
+ if ctx.NArg() < 1 {
+ return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
+ }
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+
+ db := utils.MakeChainDatabase(ctx, stack, true)
+ defer db.Close()
+ var (
+ root []byte
+ start []byte
+ max = int64(-1)
+ err error
+ )
+ if root, err = hexutil.Decode(ctx.Args().Get(0)); err != nil {
+ log.Info("Could not decode the root", "error", err)
+ return err
+ }
+ stRoot := common.BytesToHash(root)
+ if ctx.NArg() >= 2 {
+ if start, err = hexutil.Decode(ctx.Args().Get(1)); err != nil {
+ log.Info("Could not decode the seek position", "error", err)
+ return err
+ }
+ }
+ if ctx.NArg() >= 3 {
+ if max, err = strconv.ParseInt(ctx.Args().Get(2), 10, 64); err != nil {
+ log.Info("Could not decode the max count", "error", err)
+ return err
+ }
+ }
+ theTrie, err := trie.New(common.Hash{}, stRoot, trie.NewDatabase(db))
+ if err != nil {
+ return err
+ }
+ var count int64
+ it := trie.NewIterator(theTrie.NodeIterator(start))
+ for it.Next() {
+ if max > 0 && count == max {
+ fmt.Printf("Exiting after %d values\n", count)
+ break
+ }
+ fmt.Printf(" %d. key %#x: %#x\n", count, it.Key, it.Value)
+ count++
+ }
+ return it.Err
+}
+
+func freezerInspect(ctx *cli.Context) error {
+ if ctx.NArg() < 4 {
+ return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
+ }
+ var (
+ freezer = ctx.Args().Get(0)
+ table = ctx.Args().Get(1)
+ )
+ start, err := strconv.ParseInt(ctx.Args().Get(2), 10, 64)
+ if err != nil {
+ log.Info("Could not read start-param", "err", err)
+ return err
+ }
+ end, err := strconv.ParseInt(ctx.Args().Get(3), 10, 64)
+ if err != nil {
+ log.Info("Could not read count param", "err", err)
+ return err
+ }
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+
+ db := utils.MakeChainDatabase(ctx, stack, true)
+ defer db.Close()
+
+ ancient, err := db.AncientDatadir()
+ if err != nil {
+ log.Info("Failed to retrieve ancient root", "err", err)
+ return err
+ }
+ return rawdb.InspectFreezerTable(ancient, freezer, table, start, end)
+}
+
+func importLDBdata(ctx *cli.Context) error {
+ start := 0
+ switch ctx.NArg() {
+ case 1:
+ break
+ case 2:
+ s, err := strconv.Atoi(ctx.Args().Get(1))
+ if err != nil {
+ return fmt.Errorf("second arg must be an integer: %v", err)
+ }
+ start = s
+ default:
+ return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
+ }
+ var (
+ fName = ctx.Args().Get(0)
+ stack, _ = makeConfigNode(ctx)
+ interrupt = make(chan os.Signal, 1)
+ stop = make(chan struct{})
+ )
+ defer stack.Close()
+ signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
+ defer signal.Stop(interrupt)
+ defer close(interrupt)
+ go func() {
+ if _, ok := <-interrupt; ok {
+ log.Info("Interrupted during ldb import, stopping at next batch")
+ }
+ close(stop)
+ }()
+ db := utils.MakeChainDatabase(ctx, stack, false)
+ return utils.ImportLDBData(db, fName, int64(start), stop)
+}
+
+type preimageIterator struct {
+ iter ethdb.Iterator
+}
+
+func (iter *preimageIterator) Next() (byte, []byte, []byte, bool) {
+ for iter.iter.Next() {
+ key := iter.iter.Key()
+ if bytes.HasPrefix(key, rawdb.PreimagePrefix) && len(key) == (len(rawdb.PreimagePrefix)+common.HashLength) {
+ return utils.OpBatchAdd, key, iter.iter.Value(), true
+ }
+ }
+ return 0, nil, nil, false
+}
+
+func (iter *preimageIterator) Release() {
+ iter.iter.Release()
+}
+
+type snapshotIterator struct {
+ init bool
+ account ethdb.Iterator
+ storage ethdb.Iterator
+}
+
+func (iter *snapshotIterator) Next() (byte, []byte, []byte, bool) {
+ if !iter.init {
+ iter.init = true
+ return utils.OpBatchDel, rawdb.SnapshotRootKey, nil, true
+ }
+ for iter.account.Next() {
+ key := iter.account.Key()
+ if bytes.HasPrefix(key, rawdb.SnapshotAccountPrefix) && len(key) == (len(rawdb.SnapshotAccountPrefix)+common.HashLength) {
+ return utils.OpBatchAdd, key, iter.account.Value(), true
+ }
+ }
+ for iter.storage.Next() {
+ key := iter.storage.Key()
+ if bytes.HasPrefix(key, rawdb.SnapshotStoragePrefix) && len(key) == (len(rawdb.SnapshotStoragePrefix)+2*common.HashLength) {
+ return utils.OpBatchAdd, key, iter.storage.Value(), true
+ }
+ }
+ return 0, nil, nil, false
+}
+
+func (iter *snapshotIterator) Release() {
+ iter.account.Release()
+ iter.storage.Release()
+}
+
+// chainExporters defines the export scheme for all exportable chain data.
+var chainExporters = map[string]func(db ethdb.Database) utils.ChainDataIterator{
+ "preimage": func(db ethdb.Database) utils.ChainDataIterator {
+ iter := db.NewIterator(rawdb.PreimagePrefix, nil)
+ return &preimageIterator{iter: iter}
+ },
+ "snapshot": func(db ethdb.Database) utils.ChainDataIterator {
+ account := db.NewIterator(rawdb.SnapshotAccountPrefix, nil)
+ storage := db.NewIterator(rawdb.SnapshotStoragePrefix, nil)
+ return &snapshotIterator{account: account, storage: storage}
+ },
+}
+
+func exportChaindata(ctx *cli.Context) error {
+ if ctx.NArg() < 2 {
+ return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
+ }
+ // Parse the required chain data type, make sure it's supported.
+ kind := ctx.Args().Get(0)
+ kind = strings.ToLower(strings.Trim(kind, " "))
+ exporter, ok := chainExporters[kind]
+ if !ok {
+ var kinds []string
+ for kind := range chainExporters {
+ kinds = append(kinds, kind)
+ }
+ return fmt.Errorf("invalid data type %s, supported types: %s", kind, strings.Join(kinds, ", "))
+ }
+ var (
+ stack, _ = makeConfigNode(ctx)
+ interrupt = make(chan os.Signal, 1)
+ stop = make(chan struct{})
+ )
+ defer stack.Close()
+ signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
+ defer signal.Stop(interrupt)
+ defer close(interrupt)
+ go func() {
+ if _, ok := <-interrupt; ok {
+ log.Info("Interrupted during db export, stopping at next batch")
+ }
+ close(stop)
+ }()
+ db := utils.MakeChainDatabase(ctx, stack, true)
+ return utils.ExportChaindata(ctx.Args().Get(1), kind, exporter(db), stop)
+}
+
+func showMetaData(ctx *cli.Context) error {
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+ db := utils.MakeChainDatabase(ctx, stack, true)
+ ancients, err := db.Ancients()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error accessing ancients: %v", err)
+ }
+ pp := func(val *uint64) string {
+ if val == nil {
+ return ""
+ }
+ return fmt.Sprintf("%d (%#x)", *val, *val)
+ }
+ data := [][]string{
+ {"databaseVersion", pp(rawdb.ReadDatabaseVersion(db))},
+ {"headBlockHash", fmt.Sprintf("%v", rawdb.ReadHeadBlockHash(db))},
+ {"headFastBlockHash", fmt.Sprintf("%v", rawdb.ReadHeadFastBlockHash(db))},
+ {"headHeaderHash", fmt.Sprintf("%v", rawdb.ReadHeadHeaderHash(db))}}
+ if b := rawdb.ReadHeadBlock(db); b != nil {
+ data = append(data, []string{"headBlock.Hash", fmt.Sprintf("%v", b.Hash())})
+ data = append(data, []string{"headBlock.Root", fmt.Sprintf("%v", b.Root())})
+ data = append(data, []string{"headBlock.Number", fmt.Sprintf("%d (%#x)", b.Number(), b.Number())})
+ }
+ if b := rawdb.ReadSkeletonSyncStatus(db); b != nil {
+ data = append(data, []string{"SkeletonSyncStatus", string(b)})
+ }
+ if h := rawdb.ReadHeadHeader(db); h != nil {
+ data = append(data, []string{"headHeader.Hash", fmt.Sprintf("%v", h.Hash())})
+ data = append(data, []string{"headHeader.Root", fmt.Sprintf("%v", h.Root)})
+ data = append(data, []string{"headHeader.Number", fmt.Sprintf("%d (%#x)", h.Number, h.Number)})
+ }
+ data = append(data, [][]string{{"frozen", fmt.Sprintf("%d items", ancients)},
+ {"lastPivotNumber", pp(rawdb.ReadLastPivotNumber(db))},
+ {"len(snapshotSyncStatus)", fmt.Sprintf("%d bytes", len(rawdb.ReadSnapshotSyncStatus(db)))},
+ {"snapshotGenerator", snapshot.ParseGeneratorStatus(rawdb.ReadSnapshotGenerator(db))},
+ {"snapshotDisabled", fmt.Sprintf("%v", rawdb.ReadSnapshotDisabled(db))},
+ {"snapshotJournal", fmt.Sprintf("%d bytes", len(rawdb.ReadSnapshotJournal(db)))},
+ {"snapshotRecoveryNumber", pp(rawdb.ReadSnapshotRecoveryNumber(db))},
+ {"snapshotRoot", fmt.Sprintf("%v", rawdb.ReadSnapshotRoot(db))},
+ {"txIndexTail", pp(rawdb.ReadTxIndexTail(db))},
+ {"fastTxLookupLimit", pp(rawdb.ReadFastTxLookupLimit(db))},
+ }...)
+ table := tablewriter.NewWriter(os.Stdout)
+ table.SetHeader([]string{"Field", "Value"})
+ table.AppendBulk(data)
+ table.Render()
+ return nil
+}
+
+func freezerMigrate(ctx *cli.Context) error {
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+
+ db := utils.MakeChainDatabase(ctx, stack, false)
+ defer db.Close()
+
+ // Check first block for legacy receipt format
+ numAncients, err := db.Ancients()
+ if err != nil {
+ return err
+ }
+ if numAncients < 1 {
+ log.Info("No receipts in freezer to migrate")
+ return nil
+ }
+
+ isFirstLegacy, firstIdx, err := dbHasLegacyReceipts(db, 0)
+ if err != nil {
+ return err
+ }
+ if !isFirstLegacy {
+ log.Info("No legacy receipts to migrate")
+ return nil
+ }
+
+ log.Info("Starting migration", "ancients", numAncients, "firstLegacy", firstIdx)
+ start := time.Now()
+ if err := db.MigrateTable("receipts", types.ConvertLegacyStoredReceipts); err != nil {
+ return err
+ }
+ if err := db.Close(); err != nil {
+ return err
+ }
+ log.Info("Migration finished", "duration", time.Since(start))
+
+ return nil
+}
+
+// dbHasLegacyReceipts checks freezer entries for legacy receipts. It stops at the first
+// non-empty receipt and checks its format. The index of this first non-empty element is
+// the second return parameter.
+func dbHasLegacyReceipts(db ethdb.Database, firstIdx uint64) (bool, uint64, error) {
+ // Check first block for legacy receipt format
+ numAncients, err := db.Ancients()
+ if err != nil {
+ return false, 0, err
+ }
+ if numAncients < 1 {
+ return false, 0, nil
+ }
+ if firstIdx >= numAncients {
+ return false, firstIdx, nil
+ }
+ var (
+ legacy bool
+ blob []byte
+ emptyRLPList = []byte{192}
+ )
+ // Find first block with non-empty receipt, only if
+ // the index is not already provided.
+ if firstIdx == 0 {
+ for i := uint64(0); i < numAncients; i++ {
+ blob, err = db.Ancient("receipts", i)
+ if err != nil {
+ return false, 0, err
+ }
+ if len(blob) == 0 {
+ continue
+ }
+ if !bytes.Equal(blob, emptyRLPList) {
+ firstIdx = i
+ break
+ }
+ }
+ }
+ first, err := db.Ancient("receipts", firstIdx)
+ if err != nil {
+ return false, 0, err
+ }
+ // We looped over all receipts and they were all empty
+ if bytes.Equal(first, emptyRLPList) {
+ return false, 0, nil
+ }
+ // Is first non-empty receipt legacy?
+ legacy, err = types.IsLegacyStoredReceipts(first)
+ return legacy, firstIdx, err
+}
diff --git a/cmd/worldland/genesis_test.go b/cmd/worldland/genesis_test.go
new file mode 100644
index 0000000000..7667a85811
--- /dev/null
+++ b/cmd/worldland/genesis_test.go
@@ -0,0 +1,92 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package main
+
+import (
+ "os"
+ "path/filepath"
+ "testing"
+)
+
+var customGenesisTests = []struct {
+ genesis string
+ query string
+ result string
+}{
+ // Genesis file with an empty chain configuration (ensure missing fields work)
+ {
+ genesis: `{
+ "alloc" : {},
+ "coinbase" : "0x0000000000000000000000000000000000000000",
+ "difficulty" : "0x20000",
+ "extraData" : "",
+ "gasLimit" : "0x2fefd8",
+ "nonce" : "0x0000000000001338",
+ "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "timestamp" : "0x00",
+ "config" : {}
+ }`,
+ query: "eth.getBlock(0).nonce",
+ result: "0x0000000000001338",
+ },
+ // Genesis file with specific chain configurations
+ {
+ genesis: `{
+ "alloc" : {},
+ "coinbase" : "0x0000000000000000000000000000000000000000",
+ "difficulty" : "0x20000",
+ "extraData" : "",
+ "gasLimit" : "0x2fefd8",
+ "nonce" : "0x0000000000001339",
+ "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "timestamp" : "0x00",
+ "config" : {
+ "homesteadBlock" : 42,
+ "daoForkBlock" : 141,
+ "daoForkSupport" : true
+ }
+ }`,
+ query: "eth.getBlock(0).nonce",
+ result: "0x0000000000001339",
+ },
+}
+
+// Tests that initializing Geth with a custom genesis block and chain definitions
+// work properly.
+func TestCustomGenesis(t *testing.T) {
+ for i, tt := range customGenesisTests {
+ // Create a temporary data directory to use and inspect later
+ datadir := t.TempDir()
+
+ // Initialize the data directory with the custom genesis block
+ json := filepath.Join(datadir, "genesis.json")
+ if err := os.WriteFile(json, []byte(tt.genesis), 0600); err != nil {
+ t.Fatalf("test %d: failed to write genesis file: %v", i, err)
+ }
+ runGeth(t, "--datadir", datadir, "init", json).WaitExit()
+
+ // Query the custom genesis block
+ geth := runGeth(t, "--networkid", "1337", "--syncmode=full", "--cache", "16",
+ "--datadir", datadir, "--maxpeers", "0", "--port", "0", "--authrpc.port", "0",
+ "--nodiscover", "--nat", "none", "--ipcdisable",
+ "--exec", tt.query, "console")
+ geth.ExpectRegexp(tt.result)
+ geth.ExpectExit()
+ }
+}
diff --git a/cmd/worldland/les_test.go b/cmd/worldland/les_test.go
new file mode 100644
index 0000000000..cd6e7b2bb4
--- /dev/null
+++ b/cmd/worldland/les_test.go
@@ -0,0 +1,205 @@
+// Copyright 2020 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package main
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "sync/atomic"
+ "testing"
+ "time"
+
+ "github.com/cryptoecc/ETH-ECC/p2p"
+ "github.com/cryptoecc/ETH-ECC/rpc"
+)
+
+type gethrpc struct {
+ name string
+ rpc *rpc.Client
+ geth *testgeth
+ nodeInfo *p2p.NodeInfo
+}
+
+func (g *gethrpc) killAndWait() {
+ g.geth.Kill()
+ g.geth.WaitExit()
+}
+
+func (g *gethrpc) callRPC(result interface{}, method string, args ...interface{}) {
+ if err := g.rpc.Call(&result, method, args...); err != nil {
+ g.geth.Fatalf("callRPC %v: %v", method, err)
+ }
+}
+
+func (g *gethrpc) addPeer(peer *gethrpc) {
+ g.geth.Logf("%v.addPeer(%v)", g.name, peer.name)
+ enode := peer.getNodeInfo().Enode
+ peerCh := make(chan *p2p.PeerEvent)
+ sub, err := g.rpc.Subscribe(context.Background(), "admin", peerCh, "peerEvents")
+ if err != nil {
+ g.geth.Fatalf("subscribe %v: %v", g.name, err)
+ }
+ defer sub.Unsubscribe()
+ g.callRPC(nil, "admin_addPeer", enode)
+ dur := 14 * time.Second
+ timeout := time.After(dur)
+ select {
+ case ev := <-peerCh:
+ g.geth.Logf("%v received event: type=%v, peer=%v", g.name, ev.Type, ev.Peer)
+ case err := <-sub.Err():
+ g.geth.Fatalf("%v sub error: %v", g.name, err)
+ case <-timeout:
+ g.geth.Error("timeout adding peer after", dur)
+ }
+}
+
+// Use this function instead of `g.nodeInfo` directly
+func (g *gethrpc) getNodeInfo() *p2p.NodeInfo {
+ if g.nodeInfo != nil {
+ return g.nodeInfo
+ }
+ g.nodeInfo = &p2p.NodeInfo{}
+ g.callRPC(&g.nodeInfo, "admin_nodeInfo")
+ return g.nodeInfo
+}
+
+// ipcEndpoint resolves an IPC endpoint based on a configured value, taking into
+// account the set data folders as well as the designated platform we're currently
+// running on.
+func ipcEndpoint(ipcPath, datadir string) string {
+ // On windows we can only use plain top-level pipes
+ if runtime.GOOS == "windows" {
+ if strings.HasPrefix(ipcPath, `\\.\pipe\`) {
+ return ipcPath
+ }
+ return `\\.\pipe\` + ipcPath
+ }
+ // Resolve names into the data directory full paths otherwise
+ if filepath.Base(ipcPath) == ipcPath {
+ if datadir == "" {
+ return filepath.Join(os.TempDir(), ipcPath)
+ }
+ return filepath.Join(datadir, ipcPath)
+ }
+ return ipcPath
+}
+
+// nextIPC ensures that each ipc pipe gets a unique name.
+// On linux, it works well to use ipc pipes all over the filesystem (in datadirs),
+// but windows require pipes to sit in "\\.\pipe\". Therefore, to run several
+// nodes simultaneously, we need to distinguish between them, which we do by
+// the pipe filename instead of folder.
+var nextIPC = uint32(0)
+
+func startGethWithIpc(t *testing.T, name string, args ...string) *gethrpc {
+ ipcName := fmt.Sprintf("geth-%d.ipc", atomic.AddUint32(&nextIPC, 1))
+ args = append([]string{"--networkid=42", "--port=0", "--authrpc.port", "0", "--ipcpath", ipcName}, args...)
+ t.Logf("Starting %v with rpc: %v", name, args)
+
+ g := &gethrpc{
+ name: name,
+ geth: runGeth(t, args...),
+ }
+ ipcpath := ipcEndpoint(ipcName, g.geth.Datadir)
+ // We can't know exactly how long geth will take to start, so we try 10
+ // times over a 5 second period.
+ var err error
+ for i := 0; i < 10; i++ {
+ time.Sleep(500 * time.Millisecond)
+ if g.rpc, err = rpc.Dial(ipcpath); err == nil {
+ return g
+ }
+ }
+ t.Fatalf("%v rpc connect to %v: %v", name, ipcpath, err)
+ return nil
+}
+
+func initGeth(t *testing.T) string {
+ args := []string{"--networkid=42", "init", "./testdata/clique.json"}
+ t.Logf("Initializing geth: %v ", args)
+ g := runGeth(t, args...)
+ datadir := g.Datadir
+ g.WaitExit()
+ return datadir
+}
+
+func startLightServer(t *testing.T) *gethrpc {
+ datadir := initGeth(t)
+ t.Logf("Importing keys to geth")
+ runGeth(t, "account", "import", "--datadir", datadir, "--password", "./testdata/password.txt", "--lightkdf", "./testdata/key.prv").WaitExit()
+ account := "0x02f0d131f1f97aef08aec6e3291b957d9efe7105"
+ server := startGethWithIpc(t, "lightserver", "--allow-insecure-unlock", "--datadir", datadir, "--password", "./testdata/password.txt", "--unlock", account, "--mine", "--light.serve=100", "--light.maxpeers=1", "--nodiscover", "--nat=extip:127.0.0.1", "--verbosity=4")
+ return server
+}
+
+func startClient(t *testing.T, name string) *gethrpc {
+ datadir := initGeth(t)
+ return startGethWithIpc(t, name, "--datadir", datadir, "--nodiscover", "--syncmode=light", "--nat=extip:127.0.0.1", "--verbosity=4")
+}
+
+func TestPriorityClient(t *testing.T) {
+ lightServer := startLightServer(t)
+ defer lightServer.killAndWait()
+
+ // Start client and add lightServer as peer
+ freeCli := startClient(t, "freeCli")
+ defer freeCli.killAndWait()
+ freeCli.addPeer(lightServer)
+
+ var peers []*p2p.PeerInfo
+ freeCli.callRPC(&peers, "admin_peers")
+ if len(peers) != 1 {
+ t.Errorf("Expected: # of client peers == 1, actual: %v", len(peers))
+ return
+ }
+
+ // Set up priority client, get its nodeID, increase its balance on the lightServer
+ prioCli := startClient(t, "prioCli")
+ defer prioCli.killAndWait()
+ // 3_000_000_000 once we move to Go 1.13
+ tokens := uint64(3000000000)
+ lightServer.callRPC(nil, "les_addBalance", prioCli.getNodeInfo().ID, tokens)
+ prioCli.addPeer(lightServer)
+
+ // Check if priority client is actually syncing and the regular client got kicked out
+ prioCli.callRPC(&peers, "admin_peers")
+ if len(peers) != 1 {
+ t.Errorf("Expected: # of prio peers == 1, actual: %v", len(peers))
+ }
+
+ nodes := map[string]*gethrpc{
+ lightServer.getNodeInfo().ID: lightServer,
+ freeCli.getNodeInfo().ID: freeCli,
+ prioCli.getNodeInfo().ID: prioCli,
+ }
+ time.Sleep(1 * time.Second)
+ lightServer.callRPC(&peers, "admin_peers")
+ peersWithNames := make(map[string]string)
+ for _, p := range peers {
+ peersWithNames[nodes[p.ID].name] = p.ID
+ }
+ if _, freeClientFound := peersWithNames[freeCli.name]; freeClientFound {
+ t.Error("client is still a peer of lightServer", peersWithNames)
+ }
+ if _, prioClientFound := peersWithNames[prioCli.name]; !prioClientFound {
+ t.Error("prio client is not among lightServer peers", peersWithNames)
+ }
+}
diff --git a/cmd/worldland/main.go b/cmd/worldland/main.go
new file mode 100644
index 0000000000..d5329ce3be
--- /dev/null
+++ b/cmd/worldland/main.go
@@ -0,0 +1,494 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+// geth is the official command-line client for Ethereum.
+package main
+
+import (
+ "fmt"
+ "os"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/cryptoecc/ETH-ECC/accounts"
+ "github.com/cryptoecc/ETH-ECC/accounts/keystore"
+ "github.com/cryptoecc/ETH-ECC/cmd/utils"
+ "github.com/cryptoecc/ETH-ECC/common"
+ "github.com/cryptoecc/ETH-ECC/console/prompt"
+ "github.com/cryptoecc/ETH-ECC/eth"
+ "github.com/cryptoecc/ETH-ECC/eth/downloader"
+ "github.com/cryptoecc/ETH-ECC/ethclient"
+ "github.com/cryptoecc/ETH-ECC/internal/debug"
+ "github.com/cryptoecc/ETH-ECC/internal/ethapi"
+ "github.com/cryptoecc/ETH-ECC/internal/flags"
+ "github.com/cryptoecc/ETH-ECC/log"
+ "github.com/cryptoecc/ETH-ECC/metrics"
+ "github.com/cryptoecc/ETH-ECC/node"
+
+ // Force-load the tracer engines to trigger registration
+ _ "github.com/cryptoecc/ETH-ECC/eth/tracers/js"
+ _ "github.com/cryptoecc/ETH-ECC/eth/tracers/native"
+
+ "github.com/urfave/cli/v2"
+)
+
+const (
+ clientIdentifier = "worldland" // Client identifier to advertise over the network
+)
+
+var (
+ // Git SHA1 commit hash of the release (set via linker flags)
+ gitCommit = ""
+ gitDate = ""
+ // The app that holds all commands and flags.
+ app = flags.NewApp(gitCommit, gitDate, "the ETH-ECC command line interface")
+ // flags that configure the node
+ nodeFlags = flags.Merge([]cli.Flag{
+ utils.IdentityFlag,
+ utils.UnlockedAccountFlag,
+ utils.PasswordFileFlag,
+ utils.BootnodesFlag,
+ utils.MinFreeDiskSpaceFlag,
+ utils.KeyStoreDirFlag,
+ utils.ExternalSignerFlag,
+ utils.NoUSBFlag,
+ utils.USBFlag,
+ utils.SmartCardDaemonPathFlag,
+ utils.OverrideTerminalTotalDifficulty,
+ utils.OverrideTerminalTotalDifficultyPassed,
+ utils.EthashCacheDirFlag,
+ utils.EthashCachesInMemoryFlag,
+ utils.EthashCachesOnDiskFlag,
+ utils.EthashCachesLockMmapFlag,
+ utils.EthashDatasetDirFlag,
+ utils.EthashDatasetsInMemoryFlag,
+ utils.EthashDatasetsOnDiskFlag,
+ utils.EthashDatasetsLockMmapFlag,
+ utils.TxPoolLocalsFlag,
+ utils.TxPoolNoLocalsFlag,
+ utils.TxPoolJournalFlag,
+ utils.TxPoolRejournalFlag,
+ utils.TxPoolPriceLimitFlag,
+ utils.TxPoolPriceBumpFlag,
+ utils.TxPoolAccountSlotsFlag,
+ utils.TxPoolGlobalSlotsFlag,
+ utils.TxPoolAccountQueueFlag,
+ utils.TxPoolGlobalQueueFlag,
+ utils.TxPoolLifetimeFlag,
+ utils.SyncModeFlag,
+ utils.ExitWhenSyncedFlag,
+ utils.GCModeFlag,
+ utils.SnapshotFlag,
+ utils.TxLookupLimitFlag,
+ utils.LightServeFlag,
+ utils.LightIngressFlag,
+ utils.LightEgressFlag,
+ utils.LightMaxPeersFlag,
+ utils.LightNoPruneFlag,
+ utils.LightKDFFlag,
+ utils.UltraLightServersFlag,
+ utils.UltraLightFractionFlag,
+ utils.UltraLightOnlyAnnounceFlag,
+ utils.LightNoSyncServeFlag,
+ utils.EthRequiredBlocksFlag,
+ utils.LegacyWhitelistFlag,
+ utils.BloomFilterSizeFlag,
+ utils.CacheFlag,
+ utils.CacheDatabaseFlag,
+ utils.CacheTrieFlag,
+ utils.CacheTrieJournalFlag,
+ utils.CacheTrieRejournalFlag,
+ utils.CacheGCFlag,
+ utils.CacheSnapshotFlag,
+ utils.CacheNoPrefetchFlag,
+ utils.CachePreimagesFlag,
+ utils.CacheLogSizeFlag,
+ utils.FDLimitFlag,
+ utils.ListenPortFlag,
+ utils.DiscoveryPortFlag,
+ utils.MaxPeersFlag,
+ utils.MaxPendingPeersFlag,
+ utils.MiningEnabledFlag,
+ utils.MinerThreadsFlag,
+ utils.MinerNotifyFlag,
+ utils.LegacyMinerGasTargetFlag,
+ utils.MinerGasLimitFlag,
+ utils.MinerGasPriceFlag,
+ utils.MinerEtherbaseFlag,
+ utils.MinerExtraDataFlag,
+ utils.MinerRecommitIntervalFlag,
+ utils.MinerNoVerifyFlag,
+ utils.NATFlag,
+ utils.NoDiscoverFlag,
+ utils.DiscoveryV5Flag,
+ utils.NetrestrictFlag,
+ utils.NodeKeyFileFlag,
+ utils.NodeKeyHexFlag,
+ utils.DNSDiscoveryFlag,
+ utils.DeveloperFlag,
+ utils.DeveloperPeriodFlag,
+ utils.DeveloperGasLimitFlag,
+ utils.VMEnableDebugFlag,
+ utils.NetworkIdFlag,
+ utils.EthStatsURLFlag,
+ utils.FakePoWFlag,
+ utils.NoCompactionFlag,
+ utils.GpoBlocksFlag,
+ utils.GpoPercentileFlag,
+ utils.GpoMaxGasPriceFlag,
+ utils.GpoIgnoreGasPriceFlag,
+ utils.MinerNotifyFullFlag,
+ utils.IgnoreLegacyReceiptsFlag,
+ configFileFlag,
+ }, utils.NetworkFlags, utils.DatabasePathFlags)
+
+ rpcFlags = []cli.Flag{
+ utils.HTTPEnabledFlag,
+ utils.HTTPListenAddrFlag,
+ utils.HTTPPortFlag,
+ utils.HTTPCORSDomainFlag,
+ utils.AuthListenFlag,
+ utils.AuthPortFlag,
+ utils.AuthVirtualHostsFlag,
+ utils.JWTSecretFlag,
+ utils.HTTPVirtualHostsFlag,
+ utils.GraphQLEnabledFlag,
+ utils.GraphQLCORSDomainFlag,
+ utils.GraphQLVirtualHostsFlag,
+ utils.HTTPApiFlag,
+ utils.HTTPPathPrefixFlag,
+ utils.WSEnabledFlag,
+ utils.WSListenAddrFlag,
+ utils.WSPortFlag,
+ utils.WSApiFlag,
+ utils.WSAllowedOriginsFlag,
+ utils.WSPathPrefixFlag,
+ utils.IPCDisabledFlag,
+ utils.IPCPathFlag,
+ utils.InsecureUnlockAllowedFlag,
+ utils.RPCGlobalGasCapFlag,
+ utils.RPCGlobalEVMTimeoutFlag,
+ utils.RPCGlobalTxFeeCapFlag,
+ utils.AllowUnprotectedTxs,
+ }
+
+ metricsFlags = []cli.Flag{
+ utils.MetricsEnabledFlag,
+ utils.MetricsEnabledExpensiveFlag,
+ utils.MetricsHTTPFlag,
+ utils.MetricsPortFlag,
+ utils.MetricsEnableInfluxDBFlag,
+ utils.MetricsInfluxDBEndpointFlag,
+ utils.MetricsInfluxDBDatabaseFlag,
+ utils.MetricsInfluxDBUsernameFlag,
+ utils.MetricsInfluxDBPasswordFlag,
+ utils.MetricsInfluxDBTagsFlag,
+ utils.MetricsEnableInfluxDBV2Flag,
+ utils.MetricsInfluxDBTokenFlag,
+ utils.MetricsInfluxDBBucketFlag,
+ utils.MetricsInfluxDBOrganizationFlag,
+ }
+)
+
+func init() {
+ // Initialize the CLI app and start Geth
+ app.Action = geth
+ app.HideVersion = true // we have a command to print the version
+ app.Copyright = "Copyright 2023 The ETH-ECC and go-ethereum Authors"
+ app.Commands = []*cli.Command{
+ // See chaincmd.go:
+ initCommand,
+ importCommand,
+ exportCommand,
+ importPreimagesCommand,
+ exportPreimagesCommand,
+ removedbCommand,
+ dumpCommand,
+ dumpGenesisCommand,
+ // See accountcmd.go:
+ accountCommand,
+ walletCommand,
+ // See consolecmd.go:
+ consoleCommand,
+ attachCommand,
+ javascriptCommand,
+ // See misccmd.go:
+ makecacheCommand,
+ makedagCommand,
+ versionCommand,
+ versionCheckCommand,
+ licenseCommand,
+ // See config.go
+ dumpConfigCommand,
+ // see dbcmd.go
+ dbCommand,
+ // See cmd/utils/flags_legacy.go
+ utils.ShowDeprecated,
+ // See snapshot.go
+ snapshotCommand,
+ }
+ sort.Sort(cli.CommandsByName(app.Commands))
+
+ app.Flags = flags.Merge(
+ nodeFlags,
+ rpcFlags,
+ consoleFlags,
+ debug.Flags,
+ metricsFlags,
+ )
+
+ app.Before = func(ctx *cli.Context) error {
+ flags.MigrateGlobalFlags(ctx)
+ return debug.Setup(ctx)
+ }
+ app.After = func(ctx *cli.Context) error {
+ debug.Exit()
+ prompt.Stdin.Close() // Resets terminal mode.
+ return nil
+ }
+}
+
+func main() {
+ if err := app.Run(os.Args); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+}
+
+// prepare manipulates memory cache allowance and setups metric system.
+// This function should be called before launching devp2p stack.
+func prepare(ctx *cli.Context) {
+ // If we're running a known preset, log it for convenience.
+ switch {
+ /*case ctx.IsSet(utils.RopstenFlag.Name):
+ log.Info("Starting Geth on Ropsten testnet...")
+
+ case ctx.IsSet(utils.RinkebyFlag.Name):
+ log.Info("Starting Geth on Rinkeby testnet...")
+
+ case ctx.IsSet(utils.GoerliFlag.Name):
+ log.Info("Starting Geth on Görli testnet...")
+
+ case ctx.IsSet(utils.SepoliaFlag.Name):
+ log.Info("Starting Geth on Sepolia testnet...")
+
+ case ctx.IsSet(utils.KilnFlag.Name):
+ log.Info("Starting Geth on Kiln testnet...")*/
+
+ case ctx.IsSet(utils.LveFlag.Name):
+ log.Info("Starting Worldland on Lve ...")
+
+ case ctx.IsSet(utils.SeoulFlag.Name):
+ log.Info("Starting Worldland on Seoul ...")
+
+ case ctx.IsSet(utils.GwangjuFlag.Name):
+ log.Info("Starting Worldland on Gwangju testnet ...")
+
+ case ctx.IsSet(utils.DeveloperFlag.Name):
+ log.Info("Starting Worldland in ephemeral dev mode...")
+ log.Warn(`You are running Worldland in --dev mode. Please note the following:
+
+ 1. This mode is only intended for fast, iterative development without assumptions on
+ security or persistence.
+ 2. The database is created in memory unless specified otherwise. Therefore, shutting down
+ your computer or losing power will wipe your entire block data and chain state for
+ your dev environment.
+ 3. A random, pre-allocated developer account will be available and unlocked as
+ eth.coinbase, which can be used for testing. The random dev account is temporary,
+ stored on a ramdisk, and will be lost if your machine is restarted.
+ 4. Mining is enabled by default. However, the client will only seal blocks if transactions
+ are pending in the mempool. The miner's minimum accepted gas price is 1.
+ 5. Networking is disabled; there is no listen-address, the maximum number of peers is set
+ to 0, and discovery is disabled.
+`)
+
+ case !ctx.IsSet(utils.NetworkIdFlag.Name):
+ log.Info("Starting clinet on Worldland Seoul mainnet...")
+ ctx.Set(utils.SeoulFlag.Name, strconv.FormatBool(true))
+ }
+ // If we're a full node on mainnet without --cache specified, bump default cache allowance
+ if ctx.String(utils.SyncModeFlag.Name) != "light" && !ctx.IsSet(utils.CacheFlag.Name) && !ctx.IsSet(utils.NetworkIdFlag.Name) {
+ // Make sure we're not on any supported preconfigured testnet either
+ if /*!ctx.IsSet(utils.RopstenFlag.Name) &&
+ !ctx.IsSet(utils.SepoliaFlag.Name) &&
+ !ctx.IsSet(utils.RinkebyFlag.Name) &&
+ !ctx.IsSet(utils.GoerliFlag.Name) &&
+ !ctx.IsSet(utils.KilnFlag.Name) &&*/
+ !ctx.IsSet(utils.LveFlag.Name) &&
+ !ctx.IsSet(utils.SeoulFlag.Name) &&
+ !ctx.IsSet(utils.GwangjuFlag.Name) &&
+ !ctx.IsSet(utils.DeveloperFlag.Name) {
+ // Nope, we're really on mainnet. Bump that cache up!
+ log.Info("Bumping default cache on mainnet", "provided", ctx.Int(utils.CacheFlag.Name), "updated", 4096)
+ //ctx.Set(utils.SeoulFlag.Name,"seoul")
+ ctx.Set(utils.CacheFlag.Name, strconv.Itoa(4096))
+ }
+ }
+ // If we're running a light client on any network, drop the cache to some meaningfully low amount
+ if ctx.String(utils.SyncModeFlag.Name) == "light" && !ctx.IsSet(utils.CacheFlag.Name) {
+ log.Info("Dropping default light client cache", "provided", ctx.Int(utils.CacheFlag.Name), "updated", 128)
+ ctx.Set(utils.CacheFlag.Name, strconv.Itoa(128))
+ }
+
+ // Start metrics export if enabled
+ utils.SetupMetrics(ctx)
+
+ // Start system runtime metrics collection
+ go metrics.CollectProcessMetrics(3 * time.Second)
+}
+
+// geth is the main entry point into the system if no special subcommand is ran.
+// It creates a default node based on the command line arguments and runs it in
+// blocking mode, waiting for it to be shut down.
+func geth(ctx *cli.Context) error {
+ if args := ctx.Args().Slice(); len(args) > 0 {
+ return fmt.Errorf("invalid command: %q", args[0])
+ }
+
+ prepare(ctx)
+ stack, backend := makeFullNode(ctx)
+ defer stack.Close()
+ startNode(ctx, stack, backend, false)
+ stack.Wait()
+ return nil
+}
+
+// startNode boots up the system node and all registered protocols, after which
+// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
+// miner.
+func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend, isConsole bool) {
+ debug.Memsize.Add("node", stack)
+ utils.StartNode(ctx, stack, isConsole)
+
+ // Unlock any account specifically requested
+ unlockAccounts(ctx, stack)
+
+ // Register wallet event handlers to open and auto-derive wallets
+ events := make(chan accounts.WalletEvent, 16)
+ stack.AccountManager().Subscribe(events)
+
+ // Create a client to interact with local geth node.
+ rpcClient, err := stack.Attach()
+ if err != nil {
+ utils.Fatalf("Failed to attach to self: %v", err)
+ }
+ ethClient := ethclient.NewClient(rpcClient)
+
+ go func() {
+ // Open any wallets already attached
+ for _, wallet := range stack.AccountManager().Wallets() {
+ if err := wallet.Open(""); err != nil {
+ log.Warn("Failed to open wallet", "url", wallet.URL(), "err", err)
+ }
+ }
+ // Listen for wallet event till termination
+ for event := range events {
+ switch event.Kind {
+ case accounts.WalletArrived:
+ if err := event.Wallet.Open(""); err != nil {
+ log.Warn("New wallet appeared, failed to open", "url", event.Wallet.URL(), "err", err)
+ }
+ case accounts.WalletOpened:
+ status, _ := event.Wallet.Status()
+ log.Info("New wallet appeared", "url", event.Wallet.URL(), "status", status)
+
+ var derivationPaths []accounts.DerivationPath
+ if event.Wallet.URL().Scheme == "ledger" {
+ derivationPaths = append(derivationPaths, accounts.LegacyLedgerBaseDerivationPath)
+ }
+ derivationPaths = append(derivationPaths, accounts.DefaultBaseDerivationPath)
+
+ event.Wallet.SelfDerive(derivationPaths, ethClient)
+
+ case accounts.WalletDropped:
+ log.Info("Old wallet dropped", "url", event.Wallet.URL())
+ event.Wallet.Close()
+ }
+ }
+ }()
+
+ // Spawn a standalone goroutine for status synchronization monitoring,
+ // close the node when synchronization is complete if user required.
+ if ctx.Bool(utils.ExitWhenSyncedFlag.Name) {
+ go func() {
+ sub := stack.EventMux().Subscribe(downloader.DoneEvent{})
+ defer sub.Unsubscribe()
+ for {
+ event := <-sub.Chan()
+ if event == nil {
+ continue
+ }
+ done, ok := event.Data.(downloader.DoneEvent)
+ if !ok {
+ continue
+ }
+ if timestamp := time.Unix(int64(done.Latest.Time), 0); time.Since(timestamp) < 10*time.Minute {
+ log.Info("Synchronisation completed", "latestnum", done.Latest.Number, "latesthash", done.Latest.Hash(),
+ "age", common.PrettyAge(timestamp))
+ stack.Close()
+ }
+ }
+ }()
+ }
+
+ // Start auxiliary services if enabled
+ if ctx.Bool(utils.MiningEnabledFlag.Name) || ctx.Bool(utils.DeveloperFlag.Name) {
+ // Mining only makes sense if a full Ethereum node is running
+ if ctx.String(utils.SyncModeFlag.Name) == "light" {
+ utils.Fatalf("Light clients do not support mining")
+ }
+ ethBackend, ok := backend.(*eth.EthAPIBackend)
+ if !ok {
+ utils.Fatalf("Ethereum service not running")
+ }
+ // Set the gas price to the limits from the CLI and start mining
+ gasprice := flags.GlobalBig(ctx, utils.MinerGasPriceFlag.Name)
+ ethBackend.TxPool().SetGasPrice(gasprice)
+ // start mining
+ threads := ctx.Int(utils.MinerThreadsFlag.Name)
+ if err := ethBackend.StartMining(threads); err != nil {
+ utils.Fatalf("Failed to start mining: %v", err)
+ }
+ }
+
+}
+
+// unlockAccounts unlocks any account specifically requested.
+func unlockAccounts(ctx *cli.Context, stack *node.Node) {
+ var unlocks []string
+ inputs := strings.Split(ctx.String(utils.UnlockedAccountFlag.Name), ",")
+ for _, input := range inputs {
+ if trimmed := strings.TrimSpace(input); trimmed != "" {
+ unlocks = append(unlocks, trimmed)
+ }
+ }
+ // Short circuit if there is no account to unlock.
+ if len(unlocks) == 0 {
+ return
+ }
+ // If insecure account unlocking is not allowed if node's APIs are exposed to external.
+ // Print warning log to user and skip unlocking.
+ if !stack.Config().InsecureUnlockAllowed && stack.Config().ExtRPCEnabled() {
+ utils.Fatalf("Account unlock with HTTP access is forbidden!")
+ }
+ ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+ passwords := utils.MakePasswordList(ctx)
+ for i, account := range unlocks {
+ unlockAccount(ks, account, i, passwords)
+ }
+}
diff --git a/cmd/worldland/misccmd.go b/cmd/worldland/misccmd.go
new file mode 100644
index 0000000000..de311988a9
--- /dev/null
+++ b/cmd/worldland/misccmd.go
@@ -0,0 +1,161 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package main
+
+import (
+ "fmt"
+ "os"
+ "runtime"
+ "strconv"
+ "strings"
+
+ "github.com/cryptoecc/ETH-ECC/cmd/utils"
+ "github.com/cryptoecc/ETH-ECC/consensus/ethash"
+ "github.com/cryptoecc/ETH-ECC/params"
+ "github.com/urfave/cli/v2"
+)
+
+var (
+ VersionCheckUrlFlag = &cli.StringFlag{
+ Name: "check.url",
+ Usage: "URL to use when checking vulnerabilities",
+ Value: "https://geth.ethereum.org/docs/vulnerabilities/vulnerabilities.json",
+ }
+ VersionCheckVersionFlag = &cli.StringFlag{
+ Name: "check.version",
+ Usage: "Version to check",
+ Value: fmt.Sprintf("Geth/v%v/%v-%v/%v",
+ params.VersionWithCommit(gitCommit, gitDate),
+ runtime.GOOS, runtime.GOARCH, runtime.Version()),
+ }
+ makecacheCommand = &cli.Command{
+ Action: makecache,
+ Name: "makecache",
+ Usage: "Generate ethash verification cache (for testing)",
+ ArgsUsage: " ",
+ Description: `
+The makecache command generates an ethash cache in .
+
+This command exists to support the system testing project.
+Regular users do not need to execute it.
+`,
+ }
+ makedagCommand = &cli.Command{
+ Action: makedag,
+ Name: "makedag",
+ Usage: "Generate ethash mining DAG (for testing)",
+ ArgsUsage: " ",
+ Description: `
+The makedag command generates an ethash DAG in .
+
+This command exists to support the system testing project.
+Regular users do not need to execute it.
+`,
+ }
+ versionCommand = &cli.Command{
+ Action: version,
+ Name: "version",
+ Usage: "Print version numbers",
+ ArgsUsage: " ",
+ Description: `
+The output of this command is supposed to be machine-readable.
+`,
+ }
+ versionCheckCommand = &cli.Command{
+ Action: versionCheck,
+ Flags: []cli.Flag{
+ VersionCheckUrlFlag,
+ VersionCheckVersionFlag,
+ },
+ Name: "version-check",
+ Usage: "Checks (online) for known Geth security vulnerabilities",
+ ArgsUsage: "",
+ Description: `
+The version-check command fetches vulnerability-information from https://geth.ethereum.org/docs/vulnerabilities/vulnerabilities.json,
+and displays information about any security vulnerabilities that affect the currently executing version.
+`,
+ }
+ licenseCommand = &cli.Command{
+ Action: license,
+ Name: "license",
+ Usage: "Display license information",
+ ArgsUsage: " ",
+ }
+)
+
+// makecache generates an ethash verification cache into the provided folder.
+func makecache(ctx *cli.Context) error {
+ args := ctx.Args().Slice()
+ if len(args) != 2 {
+ utils.Fatalf(`Usage: geth makecache `)
+ }
+ block, err := strconv.ParseUint(args[0], 0, 64)
+ if err != nil {
+ utils.Fatalf("Invalid block number: %v", err)
+ }
+ ethash.MakeCache(block, args[1])
+
+ return nil
+}
+
+// makedag generates an ethash mining DAG into the provided folder.
+func makedag(ctx *cli.Context) error {
+ args := ctx.Args().Slice()
+ if len(args) != 2 {
+ utils.Fatalf(`Usage: geth makedag `)
+ }
+ block, err := strconv.ParseUint(args[0], 0, 64)
+ if err != nil {
+ utils.Fatalf("Invalid block number: %v", err)
+ }
+ ethash.MakeDataset(block, args[1])
+
+ return nil
+}
+
+func version(ctx *cli.Context) error {
+ fmt.Println(strings.Title(clientIdentifier))
+ fmt.Println("Version:", params.VersionWithMeta)
+ if gitCommit != "" {
+ fmt.Println("Git Commit:", gitCommit)
+ }
+ if gitDate != "" {
+ fmt.Println("Git Commit Date:", gitDate)
+ }
+ fmt.Println("Architecture:", runtime.GOARCH)
+ fmt.Println("Go Version:", runtime.Version())
+ fmt.Println("Operating System:", runtime.GOOS)
+ fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
+ fmt.Printf("GOROOT=%s\n", runtime.GOROOT())
+ return nil
+}
+
+func license(_ *cli.Context) error {
+ fmt.Println(`Geth is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Geth is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with geth. If not, see .`)
+ return nil
+}
diff --git a/cmd/worldland/run_test.go b/cmd/worldland/run_test.go
new file mode 100644
index 0000000000..f9716eab21
--- /dev/null
+++ b/cmd/worldland/run_test.go
@@ -0,0 +1,111 @@
+// Copyright 2016 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package main
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "testing"
+ "time"
+
+ "github.com/docker/docker/pkg/reexec"
+ "github.com/cryptoecc/ETH-ECC/internal/cmdtest"
+ "github.com/cryptoecc/ETH-ECC/rpc"
+)
+
+type testgeth struct {
+ *cmdtest.TestCmd
+
+ // template variables for expect
+ Datadir string
+ Etherbase string
+}
+
+func init() {
+ // Run the app if we've been exec'd as "geth-test" in runGeth.
+ reexec.Register("geth-test", func() {
+ if err := app.Run(os.Args); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ os.Exit(0)
+ })
+}
+
+func TestMain(m *testing.M) {
+ // check if we have been reexec'd
+ if reexec.Init() {
+ return
+ }
+ os.Exit(m.Run())
+}
+
+// spawns geth with the given command line args. If the args don't set --datadir, the
+// child g gets a temporary data directory.
+func runGeth(t *testing.T, args ...string) *testgeth {
+ tt := &testgeth{}
+ tt.TestCmd = cmdtest.NewTestCmd(t, tt)
+ for i, arg := range args {
+ switch arg {
+ case "--datadir":
+ if i < len(args)-1 {
+ tt.Datadir = args[i+1]
+ }
+ case "--miner.etherbase":
+ if i < len(args)-1 {
+ tt.Etherbase = args[i+1]
+ }
+ }
+ }
+ if tt.Datadir == "" {
+ // The temporary datadir will be removed automatically if something fails below.
+ tt.Datadir = t.TempDir()
+ args = append([]string{"--datadir", tt.Datadir}, args...)
+ }
+
+ // Boot "geth". This actually runs the test binary but the TestMain
+ // function will prevent any tests from running.
+ tt.Run("geth-test", args...)
+
+ return tt
+}
+
+// waitForEndpoint attempts to connect to an RPC endpoint until it succeeds.
+func waitForEndpoint(t *testing.T, endpoint string, timeout time.Duration) {
+ probe := func() bool {
+ ctx, cancel := context.WithTimeout(context.Background(), timeout)
+ defer cancel()
+ c, err := rpc.DialContext(ctx, endpoint)
+ if c != nil {
+ _, err = c.SupportedModules()
+ c.Close()
+ }
+ return err == nil
+ }
+
+ start := time.Now()
+ for {
+ if probe() {
+ return
+ }
+ if time.Since(start) > timeout {
+ t.Fatal("endpoint", endpoint, "did not open within", timeout)
+ }
+ time.Sleep(200 * time.Millisecond)
+ }
+}
diff --git a/cmd/worldland/snapshot.go b/cmd/worldland/snapshot.go
new file mode 100644
index 0000000000..59bdb65503
--- /dev/null
+++ b/cmd/worldland/snapshot.go
@@ -0,0 +1,574 @@
+// Copyright 2021 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "os"
+ "time"
+
+ "github.com/cryptoecc/ETH-ECC/cmd/utils"
+ "github.com/cryptoecc/ETH-ECC/common"
+ "github.com/cryptoecc/ETH-ECC/core/rawdb"
+ "github.com/cryptoecc/ETH-ECC/core/state"
+ "github.com/cryptoecc/ETH-ECC/core/state/pruner"
+ "github.com/cryptoecc/ETH-ECC/core/state/snapshot"
+ "github.com/cryptoecc/ETH-ECC/core/types"
+ "github.com/cryptoecc/ETH-ECC/crypto"
+ "github.com/cryptoecc/ETH-ECC/internal/flags"
+ "github.com/cryptoecc/ETH-ECC/log"
+ "github.com/cryptoecc/ETH-ECC/rlp"
+ "github.com/cryptoecc/ETH-ECC/trie"
+ cli "github.com/urfave/cli/v2"
+)
+
+var (
+ // emptyRoot is the known root hash of an empty trie.
+ emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
+
+ // emptyCode is the known hash of the empty EVM bytecode.
+ emptyCode = crypto.Keccak256(nil)
+)
+
+var (
+ snapshotCommand = &cli.Command{
+ Name: "snapshot",
+ Usage: "A set of commands based on the snapshot",
+ Description: "",
+ Subcommands: []*cli.Command{
+ {
+ Name: "prune-state",
+ Usage: "Prune stale ethereum state data based on the snapshot",
+ ArgsUsage: "",
+ Action: pruneState,
+ Flags: flags.Merge([]cli.Flag{
+ utils.CacheTrieJournalFlag,
+ utils.BloomFilterSizeFlag,
+ }, utils.NetworkFlags, utils.DatabasePathFlags),
+ Description: `
+geth snapshot prune-state
+will prune historical state data with the help of the state snapshot.
+All trie nodes and contract codes that do not belong to the specified
+version state will be deleted from the database. After pruning, only
+two version states are available: genesis and the specific one.
+
+The default pruning target is the HEAD-127 state.
+
+WARNING: It's necessary to delete the trie clean cache after the pruning.
+If you specify another directory for the trie clean cache via "--cache.trie.journal"
+during the use of Geth, please also specify it here for correct deletion. Otherwise
+the trie clean cache with default directory will be deleted.
+`,
+ },
+ {
+ Name: "verify-state",
+ Usage: "Recalculate state hash based on the snapshot for verification",
+ ArgsUsage: "",
+ Action: verifyState,
+ Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags),
+ Description: `
+geth snapshot verify-state
+will traverse the whole accounts and storages set based on the specified
+snapshot and recalculate the root hash of state for verification.
+In other words, this command does the snapshot to trie conversion.
+`,
+ },
+ {
+ Name: "check-dangling-storage",
+ Usage: "Check that there is no 'dangling' snap storage",
+ ArgsUsage: "",
+ Action: checkDanglingStorage,
+ Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags),
+ Description: `
+geth snapshot check-dangling-storage traverses the snap storage
+data, and verifies that all snapshot storage data has a corresponding account.
+`,
+ },
+ {
+ Name: "inspect-account",
+ Usage: "Check all snapshot layers for the a specific account",
+ ArgsUsage: "",
+ Action: checkAccount,
+ Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags),
+ Description: `
+geth snapshot inspect-account checks all snapshot layers and prints out
+information about the specified address.
+`,
+ },
+ {
+ Name: "traverse-state",
+ Usage: "Traverse the state with given root hash and perform quick verification",
+ ArgsUsage: "",
+ Action: traverseState,
+ Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags),
+ Description: `
+geth snapshot traverse-state
+will traverse the whole state from the given state root and will abort if any
+referenced trie node or contract code is missing. This command can be used for
+state integrity verification. The default checking target is the HEAD state.
+
+It's also usable without snapshot enabled.
+`,
+ },
+ {
+ Name: "traverse-rawstate",
+ Usage: "Traverse the state with given root hash and perform detailed verification",
+ ArgsUsage: "",
+ Action: traverseRawState,
+ Flags: flags.Merge(utils.NetworkFlags, utils.DatabasePathFlags),
+ Description: `
+geth snapshot traverse-rawstate
+will traverse the whole state from the given root and will abort if any referenced
+trie node or contract code is missing. This command can be used for state integrity
+verification. The default checking target is the HEAD state. It's basically identical
+to traverse-state, but the check granularity is smaller.
+
+It's also usable without snapshot enabled.
+`,
+ },
+ {
+ Name: "dump",
+ Usage: "Dump a specific block from storage (same as 'geth dump' but using snapshots)",
+ ArgsUsage: "[? | ]",
+ Action: dumpState,
+ Flags: flags.Merge([]cli.Flag{
+ utils.ExcludeCodeFlag,
+ utils.ExcludeStorageFlag,
+ utils.StartKeyFlag,
+ utils.DumpLimitFlag,
+ }, utils.NetworkFlags, utils.DatabasePathFlags),
+ Description: `
+This command is semantically equivalent to 'geth dump', but uses the snapshots
+as the backend data source, making this command a lot faster.
+
+The argument is interpreted as block number or hash. If none is provided, the latest
+block is used.
+`,
+ },
+ },
+ }
+)
+
+func pruneState(ctx *cli.Context) error {
+ stack, config := makeConfigNode(ctx)
+ defer stack.Close()
+
+ chaindb := utils.MakeChainDatabase(ctx, stack, false)
+ pruner, err := pruner.NewPruner(chaindb, stack.ResolvePath(""), stack.ResolvePath(config.Eth.TrieCleanCacheJournal), ctx.Uint64(utils.BloomFilterSizeFlag.Name))
+ if err != nil {
+ log.Error("Failed to open snapshot tree", "err", err)
+ return err
+ }
+ if ctx.NArg() > 1 {
+ log.Error("Too many arguments given")
+ return errors.New("too many arguments")
+ }
+ var targetRoot common.Hash
+ if ctx.NArg() == 1 {
+ targetRoot, err = parseRoot(ctx.Args().First())
+ if err != nil {
+ log.Error("Failed to resolve state root", "err", err)
+ return err
+ }
+ }
+ if err = pruner.Prune(targetRoot); err != nil {
+ log.Error("Failed to prune state", "err", err)
+ return err
+ }
+ return nil
+}
+
+func verifyState(ctx *cli.Context) error {
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+
+ chaindb := utils.MakeChainDatabase(ctx, stack, true)
+ headBlock := rawdb.ReadHeadBlock(chaindb)
+ if headBlock == nil {
+ log.Error("Failed to load head block")
+ return errors.New("no head block")
+ }
+ snaptree, err := snapshot.New(chaindb, trie.NewDatabase(chaindb), 256, headBlock.Root(), false, false, false)
+ if err != nil {
+ log.Error("Failed to open snapshot tree", "err", err)
+ return err
+ }
+ if ctx.NArg() > 1 {
+ log.Error("Too many arguments given")
+ return errors.New("too many arguments")
+ }
+ var root = headBlock.Root()
+ if ctx.NArg() == 1 {
+ root, err = parseRoot(ctx.Args().First())
+ if err != nil {
+ log.Error("Failed to resolve state root", "err", err)
+ return err
+ }
+ }
+ if err := snaptree.Verify(root); err != nil {
+ log.Error("Failed to verify state", "root", root, "err", err)
+ return err
+ }
+ log.Info("Verified the state", "root", root)
+ return snapshot.CheckDanglingStorage(chaindb)
+}
+
+// checkDanglingStorage iterates the snap storage data, and verifies that all
+// storage also has corresponding account data.
+func checkDanglingStorage(ctx *cli.Context) error {
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+
+ return snapshot.CheckDanglingStorage(utils.MakeChainDatabase(ctx, stack, true))
+}
+
+// traverseState is a helper function used for pruning verification.
+// Basically it just iterates the trie, ensure all nodes and associated
+// contract codes are present.
+func traverseState(ctx *cli.Context) error {
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+
+ chaindb := utils.MakeChainDatabase(ctx, stack, true)
+ headBlock := rawdb.ReadHeadBlock(chaindb)
+ if headBlock == nil {
+ log.Error("Failed to load head block")
+ return errors.New("no head block")
+ }
+ if ctx.NArg() > 1 {
+ log.Error("Too many arguments given")
+ return errors.New("too many arguments")
+ }
+ var (
+ root common.Hash
+ err error
+ )
+ if ctx.NArg() == 1 {
+ root, err = parseRoot(ctx.Args().First())
+ if err != nil {
+ log.Error("Failed to resolve state root", "err", err)
+ return err
+ }
+ log.Info("Start traversing the state", "root", root)
+ } else {
+ root = headBlock.Root()
+ log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64())
+ }
+ triedb := trie.NewDatabase(chaindb)
+ t, err := trie.NewStateTrie(common.Hash{}, root, triedb)
+ if err != nil {
+ log.Error("Failed to open trie", "root", root, "err", err)
+ return err
+ }
+ var (
+ accounts int
+ slots int
+ codes int
+ lastReport time.Time
+ start = time.Now()
+ )
+ accIter := trie.NewIterator(t.NodeIterator(nil))
+ for accIter.Next() {
+ accounts += 1
+ var acc types.StateAccount
+ if err := rlp.DecodeBytes(accIter.Value, &acc); err != nil {
+ log.Error("Invalid account encountered during traversal", "err", err)
+ return err
+ }
+ if acc.Root != emptyRoot {
+ storageTrie, err := trie.NewStateTrie(common.BytesToHash(accIter.Key), acc.Root, triedb)
+ if err != nil {
+ log.Error("Failed to open storage trie", "root", acc.Root, "err", err)
+ return err
+ }
+ storageIter := trie.NewIterator(storageTrie.NodeIterator(nil))
+ for storageIter.Next() {
+ slots += 1
+ }
+ if storageIter.Err != nil {
+ log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Err)
+ return storageIter.Err
+ }
+ }
+ if !bytes.Equal(acc.CodeHash, emptyCode) {
+ if !rawdb.HasCode(chaindb, common.BytesToHash(acc.CodeHash)) {
+ log.Error("Code is missing", "hash", common.BytesToHash(acc.CodeHash))
+ return errors.New("missing code")
+ }
+ codes += 1
+ }
+ if time.Since(lastReport) > time.Second*8 {
+ log.Info("Traversing state", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
+ lastReport = time.Now()
+ }
+ }
+ if accIter.Err != nil {
+ log.Error("Failed to traverse state trie", "root", root, "err", accIter.Err)
+ return accIter.Err
+ }
+ log.Info("State is complete", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
+ return nil
+}
+
+// traverseRawState is a helper function used for pruning verification.
+// Basically it just iterates the trie, ensure all nodes and associated
+// contract codes are present. It's basically identical to traverseState
+// but it will check each trie node.
+func traverseRawState(ctx *cli.Context) error {
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+
+ chaindb := utils.MakeChainDatabase(ctx, stack, true)
+ headBlock := rawdb.ReadHeadBlock(chaindb)
+ if headBlock == nil {
+ log.Error("Failed to load head block")
+ return errors.New("no head block")
+ }
+ if ctx.NArg() > 1 {
+ log.Error("Too many arguments given")
+ return errors.New("too many arguments")
+ }
+ var (
+ root common.Hash
+ err error
+ )
+ if ctx.NArg() == 1 {
+ root, err = parseRoot(ctx.Args().First())
+ if err != nil {
+ log.Error("Failed to resolve state root", "err", err)
+ return err
+ }
+ log.Info("Start traversing the state", "root", root)
+ } else {
+ root = headBlock.Root()
+ log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64())
+ }
+ triedb := trie.NewDatabase(chaindb)
+ t, err := trie.NewStateTrie(common.Hash{}, root, triedb)
+ if err != nil {
+ log.Error("Failed to open trie", "root", root, "err", err)
+ return err
+ }
+ var (
+ nodes int
+ accounts int
+ slots int
+ codes int
+ lastReport time.Time
+ start = time.Now()
+ hasher = crypto.NewKeccakState()
+ got = make([]byte, 32)
+ )
+ accIter := t.NodeIterator(nil)
+ for accIter.Next(true) {
+ nodes += 1
+ node := accIter.Hash()
+
+ // Check the present for non-empty hash node(embedded node doesn't
+ // have their own hash).
+ if node != (common.Hash{}) {
+ blob := rawdb.ReadTrieNode(chaindb, node)
+ if len(blob) == 0 {
+ log.Error("Missing trie node(account)", "hash", node)
+ return errors.New("missing account")
+ }
+ hasher.Reset()
+ hasher.Write(blob)
+ hasher.Read(got)
+ if !bytes.Equal(got, node.Bytes()) {
+ log.Error("Invalid trie node(account)", "hash", node.Hex(), "value", blob)
+ return errors.New("invalid account node")
+ }
+ }
+ // If it's a leaf node, yes we are touching an account,
+ // dig into the storage trie further.
+ if accIter.Leaf() {
+ accounts += 1
+ var acc types.StateAccount
+ if err := rlp.DecodeBytes(accIter.LeafBlob(), &acc); err != nil {
+ log.Error("Invalid account encountered during traversal", "err", err)
+ return errors.New("invalid account")
+ }
+ if acc.Root != emptyRoot {
+ storageTrie, err := trie.NewStateTrie(common.BytesToHash(accIter.LeafKey()), acc.Root, triedb)
+ if err != nil {
+ log.Error("Failed to open storage trie", "root", acc.Root, "err", err)
+ return errors.New("missing storage trie")
+ }
+ storageIter := storageTrie.NodeIterator(nil)
+ for storageIter.Next(true) {
+ nodes += 1
+ node := storageIter.Hash()
+
+ // Check the present for non-empty hash node(embedded node doesn't
+ // have their own hash).
+ if node != (common.Hash{}) {
+ blob := rawdb.ReadTrieNode(chaindb, node)
+ if len(blob) == 0 {
+ log.Error("Missing trie node(storage)", "hash", node)
+ return errors.New("missing storage")
+ }
+ hasher.Reset()
+ hasher.Write(blob)
+ hasher.Read(got)
+ if !bytes.Equal(got, node.Bytes()) {
+ log.Error("Invalid trie node(storage)", "hash", node.Hex(), "value", blob)
+ return errors.New("invalid storage node")
+ }
+ }
+ // Bump the counter if it's leaf node.
+ if storageIter.Leaf() {
+ slots += 1
+ }
+ }
+ if storageIter.Error() != nil {
+ log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Error())
+ return storageIter.Error()
+ }
+ }
+ if !bytes.Equal(acc.CodeHash, emptyCode) {
+ if !rawdb.HasCode(chaindb, common.BytesToHash(acc.CodeHash)) {
+ log.Error("Code is missing", "account", common.BytesToHash(accIter.LeafKey()))
+ return errors.New("missing code")
+ }
+ codes += 1
+ }
+ if time.Since(lastReport) > time.Second*8 {
+ log.Info("Traversing state", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
+ lastReport = time.Now()
+ }
+ }
+ }
+ if accIter.Error() != nil {
+ log.Error("Failed to traverse state trie", "root", root, "err", accIter.Error())
+ return accIter.Error()
+ }
+ log.Info("State is complete", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
+ return nil
+}
+
+func parseRoot(input string) (common.Hash, error) {
+ var h common.Hash
+ if err := h.UnmarshalText([]byte(input)); err != nil {
+ return h, err
+ }
+ return h, nil
+}
+
+func dumpState(ctx *cli.Context) error {
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+
+ conf, db, root, err := parseDumpConfig(ctx, stack)
+ if err != nil {
+ return err
+ }
+ snaptree, err := snapshot.New(db, trie.NewDatabase(db), 256, root, false, false, false)
+ if err != nil {
+ return err
+ }
+ accIt, err := snaptree.AccountIterator(root, common.BytesToHash(conf.Start))
+ if err != nil {
+ return err
+ }
+ defer accIt.Release()
+
+ log.Info("Snapshot dumping started", "root", root)
+ var (
+ start = time.Now()
+ logged = time.Now()
+ accounts uint64
+ )
+ enc := json.NewEncoder(os.Stdout)
+ enc.Encode(struct {
+ Root common.Hash `json:"root"`
+ }{root})
+ for accIt.Next() {
+ account, err := snapshot.FullAccount(accIt.Account())
+ if err != nil {
+ return err
+ }
+ da := &state.DumpAccount{
+ Balance: account.Balance.String(),
+ Nonce: account.Nonce,
+ Root: account.Root,
+ CodeHash: account.CodeHash,
+ SecureKey: accIt.Hash().Bytes(),
+ }
+ if !conf.SkipCode && !bytes.Equal(account.CodeHash, emptyCode) {
+ da.Code = rawdb.ReadCode(db, common.BytesToHash(account.CodeHash))
+ }
+ if !conf.SkipStorage {
+ da.Storage = make(map[common.Hash]string)
+
+ stIt, err := snaptree.StorageIterator(root, accIt.Hash(), common.Hash{})
+ if err != nil {
+ return err
+ }
+ for stIt.Next() {
+ da.Storage[stIt.Hash()] = common.Bytes2Hex(stIt.Slot())
+ }
+ }
+ enc.Encode(da)
+ accounts++
+ if time.Since(logged) > 8*time.Second {
+ log.Info("Snapshot dumping in progress", "at", accIt.Hash(), "accounts", accounts,
+ "elapsed", common.PrettyDuration(time.Since(start)))
+ logged = time.Now()
+ }
+ if conf.Max > 0 && accounts >= conf.Max {
+ break
+ }
+ }
+ log.Info("Snapshot dumping complete", "accounts", accounts,
+ "elapsed", common.PrettyDuration(time.Since(start)))
+ return nil
+}
+
+// checkAccount iterates the snap data layers, and looks up the given account
+// across all layers.
+func checkAccount(ctx *cli.Context) error {
+ if ctx.NArg() != 1 {
+ return errors.New("need arg")
+ }
+ var (
+ hash common.Hash
+ addr common.Address
+ )
+ switch arg := ctx.Args().First(); len(arg) {
+ case 40, 42:
+ addr = common.HexToAddress(arg)
+ hash = crypto.Keccak256Hash(addr.Bytes())
+ case 64, 66:
+ hash = common.HexToHash(arg)
+ default:
+ return errors.New("malformed address or hash")
+ }
+ stack, _ := makeConfigNode(ctx)
+ defer stack.Close()
+ chaindb := utils.MakeChainDatabase(ctx, stack, true)
+ defer chaindb.Close()
+ start := time.Now()
+ log.Info("Checking difflayer journal", "address", addr, "hash", hash)
+ if err := snapshot.CheckJournalAccount(chaindb, hash); err != nil {
+ return err
+ }
+ log.Info("Checked the snapshot journalled storage", "time", common.PrettyDuration(time.Since(start)))
+ return nil
+}
diff --git a/cmd/worldland/testdata/blockchain.blocks b/cmd/worldland/testdata/blockchain.blocks
new file mode 100644
index 0000000000..d29453d3e5
Binary files /dev/null and b/cmd/worldland/testdata/blockchain.blocks differ
diff --git a/cmd/worldland/testdata/clique.json b/cmd/worldland/testdata/clique.json
new file mode 100644
index 0000000000..b54b4a7d3b
--- /dev/null
+++ b/cmd/worldland/testdata/clique.json
@@ -0,0 +1,24 @@
+{
+ "config": {
+ "chainId": 15,
+ "homesteadBlock": 0,
+ "eip150Block": 0,
+ "eip155Block": 0,
+ "eip158Block": 0,
+ "byzantiumBlock": 0,
+ "constantinopleBlock": 0,
+ "petersburgBlock": 0,
+ "clique": {
+ "period": 5,
+ "epoch": 30000
+ }
+ },
+ "difficulty": "1",
+ "gasLimit": "8000000",
+ "extradata": "0x000000000000000000000000000000000000000000000000000000000000000002f0d131f1f97aef08aec6e3291b957d9efe71050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "alloc": {
+ "02f0d131f1f97aef08aec6e3291b957d9efe7105": {
+ "balance": "300000"
+ }
+ }
+}
\ No newline at end of file
diff --git a/cmd/worldland/testdata/empty.js b/cmd/worldland/testdata/empty.js
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/cmd/worldland/testdata/empty.js
@@ -0,0 +1 @@
+
diff --git a/cmd/worldland/testdata/guswallet.json b/cmd/worldland/testdata/guswallet.json
new file mode 100644
index 0000000000..e8ea4f3326
--- /dev/null
+++ b/cmd/worldland/testdata/guswallet.json
@@ -0,0 +1,6 @@
+{
+ "encseed": "26d87f5f2bf9835f9a47eefae571bc09f9107bb13d54ff12a4ec095d01f83897494cf34f7bed2ed34126ecba9db7b62de56c9d7cd136520a0427bfb11b8954ba7ac39b90d4650d3448e31185affcd74226a68f1e94b1108e6e0a4a91cdd83eba",
+ "ethaddr": "d4584b5f6229b7be90727b0fc8c6b91bb427821f",
+ "email": "gustav.simonsson@gmail.com",
+ "btcaddr": "1EVknXyFC68kKNLkh6YnKzW41svSRoaAcx"
+}
diff --git a/cmd/worldland/testdata/key.prv b/cmd/worldland/testdata/key.prv
new file mode 100644
index 0000000000..1d2687ea63
--- /dev/null
+++ b/cmd/worldland/testdata/key.prv
@@ -0,0 +1 @@
+48aa455c373ec5ce7fefb0e54f44a215decdc85b9047bc4d09801e038909bdbe
\ No newline at end of file
diff --git a/cmd/worldland/testdata/password.txt b/cmd/worldland/testdata/password.txt
new file mode 100644
index 0000000000..f6ea049518
--- /dev/null
+++ b/cmd/worldland/testdata/password.txt
@@ -0,0 +1 @@
+foobar
\ No newline at end of file
diff --git a/cmd/worldland/testdata/passwords.txt b/cmd/worldland/testdata/passwords.txt
new file mode 100644
index 0000000000..96f98c7f43
--- /dev/null
+++ b/cmd/worldland/testdata/passwords.txt
@@ -0,0 +1,3 @@
+foobar
+foobar
+foobar
diff --git a/cmd/worldland/testdata/vcheck/data.json b/cmd/worldland/testdata/vcheck/data.json
new file mode 100644
index 0000000000..b49abfd72a
--- /dev/null
+++ b/cmd/worldland/testdata/vcheck/data.json
@@ -0,0 +1,61 @@
+[
+ {
+ "name": "CorruptedDAG",
+ "uid": "GETH-2020-01",
+ "summary": "Mining nodes will generate erroneous PoW on epochs > `385`.",
+ "description": "A mining flaw could cause miners to erroneously calculate PoW, due to an index overflow, if DAG size is exceeding the maximum 32 bit unsigned value.\n\nThis occurred on the ETC chain on 2020-11-06. This is likely to trigger for ETH mainnet around block `11550000`/epoch `385`, slated to occur early January 2021.\n\nThis issue is relevant only for miners, non-mining nodes are unaffected, since non-mining nodes use a smaller verification cache instead of a full DAG.",
+ "links": [
+ "https://github.com/cryptoecc/ETH-ECC/pull/21793",
+ "https://blog.ethereum.org/2020/11/12/geth_security_release/",
+ "https://github.com/cryptoecc/ETH-ECC/commit/567d41d9363706b4b13ce0903804e8acf214af49"
+ ],
+ "introduced": "v1.6.0",
+ "fixed": "v1.9.24",
+ "published": "2020-11-12",
+ "severity": "Medium",
+ "check": "Geth\\/v1\\.(6|7|8)\\..*|Geth\\/v1\\.9\\.2(1|2|3)-.*"
+ },
+ {
+ "name": "GoCrash",
+ "uid": "GETH-2020-02",
+ "summary": "A denial-of-service issue can be used to crash Geth nodes during block processing, due to an underlying bug in Go (CVE-2020-28362) versions < `1.15.5`, or `<1.14.12`",
+ "description": "The DoS issue can be used to crash all Geth nodes during block processing, the effects of which would be that a major part of the Ethereum network went offline.\n\nOutside of Go-Ethereum, the issue is most likely relevant for all forks of Geth (such as TurboGeth or ETC’s core-geth) which is built with versions of Go which contains the vulnerability.",
+ "links": [
+ "https://blog.ethereum.org/2020/11/12/geth_security_release/",
+ "https://groups.google.com/g/golang-announce/c/NpBGTTmKzpM",
+ "https://github.com/golang/go/issues/42552"
+ ],
+ "fixed": "v1.9.24",
+ "published": "2020-11-12",
+ "severity": "Critical",
+ "check": "Geth.*\\/go1\\.(11(.*)|12(.*)|13(.*)|14|14\\.(\\d|10|11|)|15|15\\.[0-4])$"
+ },
+ {
+ "name": "ShallowCopy",
+ "uid": "GETH-2020-03",
+ "summary": "A consensus flaw in Geth, related to `datacopy` precompile",
+ "description": "Geth erroneously performed a 'shallow' copy when the precompiled `datacopy` (at `0x00...04`) was invoked. An attacker could deploy a contract that uses the shallow copy to corrupt the contents of the `RETURNDATA`, thus causing a consensus failure.",
+ "links": [
+ "https://blog.ethereum.org/2020/11/12/geth_security_release/"
+ ],
+ "introduced": "v1.9.7",
+ "fixed": "v1.9.17",
+ "published": "2020-11-12",
+ "severity": "Critical",
+ "check": "Geth\\/v1\\.9\\.(7|8|9|10|11|12|13|14|15|16).*$"
+ },
+ {
+ "name": "GethCrash",
+ "uid": "GETH-2020-04",
+ "summary": "A denial-of-service issue can be used to crash Geth nodes during block processing",
+ "description": "Full details to be disclosed at a later date",
+ "links": [
+ "https://blog.ethereum.org/2020/11/12/geth_security_release/"
+ ],
+ "introduced": "v1.9.16",
+ "fixed": "v1.9.18",
+ "published": "2020-11-12",
+ "severity": "Critical",
+ "check": "Geth\\/v1\\.9.(16|17).*$"
+ }
+]
diff --git a/cmd/worldland/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.1 b/cmd/worldland/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.1
new file mode 100644
index 0000000000..f9066d4fe0
--- /dev/null
+++ b/cmd/worldland/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.1
@@ -0,0 +1,4 @@
+untrusted comment: signature from minisign secret key
+RWQkliYstQBOKFQFQTjmCd6TPw07VZyWFSB3v4+1BM1kv8eHLE5FDy2OkPEqtdaL53xftlrHoJQie0uCcovdlSV8kpyxiLrxEQ0=
+trusted comment: timestamp:1605618622 file:vulnerabilities.json
+osAPs4QPdDkmiWQxqeMIzYv/b+ZGxJ+19Sbrk1Cpq4t2gHBT+lqFtwL3OCzKWWyjGRTmHfsVGBYpzEdPRQ0/BQ==
diff --git a/cmd/worldland/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.2 b/cmd/worldland/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.2
new file mode 100644
index 0000000000..a89a83d21a
--- /dev/null
+++ b/cmd/worldland/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.2
@@ -0,0 +1,4 @@
+untrusted comment: Here's a comment
+RWQkliYstQBOKFQFQTjmCd6TPw07VZyWFSB3v4+1BM1kv8eHLE5FDy2OkPEqtdaL53xftlrHoJQie0uCcovdlSV8kpyxiLrxEQ0=
+trusted comment: Here's a trusted comment
+3CnkIuz9MEDa7uNyGZAbKZhuirwfiqm7E1uQHrd2SiO4Y8+Akw9vs052AyKw0s5nhbYHCZE2IMQdHNjKwxEGAQ==
diff --git a/cmd/worldland/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.3 b/cmd/worldland/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.3
new file mode 100644
index 0000000000..6fd33b19a3
--- /dev/null
+++ b/cmd/worldland/testdata/vcheck/minisig-sigs/vulnerabilities.json.minisig.3
@@ -0,0 +1,4 @@
+untrusted comment: One more (untrusted) comment
+RWQkliYstQBOKFQFQTjmCd6TPw07VZyWFSB3v4+1BM1kv8eHLE5FDy2OkPEqtdaL53xftlrHoJQie0uCcovdlSV8kpyxiLrxEQ0=
+trusted comment: Here's a trusted comment
+3CnkIuz9MEDa7uNyGZAbKZhuirwfiqm7E1uQHrd2SiO4Y8+Akw9vs052AyKw0s5nhbYHCZE2IMQdHNjKwxEGAQ==
diff --git a/cmd/worldland/testdata/vcheck/minisign.pub b/cmd/worldland/testdata/vcheck/minisign.pub
new file mode 100644
index 0000000000..183dce5f6b
--- /dev/null
+++ b/cmd/worldland/testdata/vcheck/minisign.pub
@@ -0,0 +1,2 @@
+untrusted comment: minisign public key 284E00B52C269624
+RWQkliYstQBOKOdtClfgC3IypIPX6TAmoEi7beZ4gyR3wsaezvqOMWsp
diff --git a/cmd/worldland/testdata/vcheck/minisign.sec b/cmd/worldland/testdata/vcheck/minisign.sec
new file mode 100644
index 0000000000..5c50715b20
--- /dev/null
+++ b/cmd/worldland/testdata/vcheck/minisign.sec
@@ -0,0 +1,2 @@
+untrusted comment: minisign encrypted secret key
+RWRTY0Iyz8kmPMKrqk6DCtlO9a33akKiaOQG1aLolqDxs52qvPoAAAACAAAAAAAAAEAAAAAArEiggdvyn6+WzTprirLtgiYQoU+ihz/HyGgjhuF+Pz2ddMduyCO+xjCHeq+vgVVW039fbsI8hW6LRGJZLBKV5/jdxCXAVVQE7qTQ6xpEdO0z8Z731/pV1hlspQXG2PNd16NMtwd9dWw=
diff --git a/cmd/worldland/testdata/vcheck/signify-sigs/data.json.sig b/cmd/worldland/testdata/vcheck/signify-sigs/data.json.sig
new file mode 100644
index 0000000000..3d5fcacf9a
--- /dev/null
+++ b/cmd/worldland/testdata/vcheck/signify-sigs/data.json.sig
@@ -0,0 +1,2 @@
+untrusted comment: verify with ./signifykey.pub
+RWSKLNhZb0KdAbhRUhW2LQZXdnwttu2SYhM9EuC4mMgOJB85h7/YIPupf8/ldTs4N8e9Y/fhgdY40q5LQpt5IFC62fq0v8U1/w8=
diff --git a/cmd/worldland/testdata/vcheck/signifykey.pub b/cmd/worldland/testdata/vcheck/signifykey.pub
new file mode 100644
index 0000000000..328f973ab4
--- /dev/null
+++ b/cmd/worldland/testdata/vcheck/signifykey.pub
@@ -0,0 +1,2 @@
+untrusted comment: signify public key
+RWSKLNhZb0KdATtRT7mZC/bybI3t3+Hv/O2i3ye04Dq9fnT9slpZ1a2/
diff --git a/cmd/worldland/testdata/vcheck/signifykey.sec b/cmd/worldland/testdata/vcheck/signifykey.sec
new file mode 100644
index 0000000000..3279a2e58b
--- /dev/null
+++ b/cmd/worldland/testdata/vcheck/signifykey.sec
@@ -0,0 +1,2 @@
+untrusted comment: signify secret key
+RWRCSwAAACpLQDLawSQCtI7eAVIvaiHzjTsTyJsfV5aKLNhZb0KdAWeICXJGa93/bHAcsY6jUh9I8RdEcDWEoGxmaXZC+IdVBPxDpkix9fBRGEUdKWHi3dOfqME0YRzErWI5AVg3cRw=
diff --git a/cmd/worldland/testdata/vcheck/sigs/vulnerabilities.json.minisig.1 b/cmd/worldland/testdata/vcheck/sigs/vulnerabilities.json.minisig.1
new file mode 100644
index 0000000000..f9066d4fe0
--- /dev/null
+++ b/cmd/worldland/testdata/vcheck/sigs/vulnerabilities.json.minisig.1
@@ -0,0 +1,4 @@
+untrusted comment: signature from minisign secret key
+RWQkliYstQBOKFQFQTjmCd6TPw07VZyWFSB3v4+1BM1kv8eHLE5FDy2OkPEqtdaL53xftlrHoJQie0uCcovdlSV8kpyxiLrxEQ0=
+trusted comment: timestamp:1605618622 file:vulnerabilities.json
+osAPs4QPdDkmiWQxqeMIzYv/b+ZGxJ+19Sbrk1Cpq4t2gHBT+lqFtwL3OCzKWWyjGRTmHfsVGBYpzEdPRQ0/BQ==
diff --git a/cmd/worldland/testdata/vcheck/sigs/vulnerabilities.json.minisig.2 b/cmd/worldland/testdata/vcheck/sigs/vulnerabilities.json.minisig.2
new file mode 100644
index 0000000000..a89a83d21a
--- /dev/null
+++ b/cmd/worldland/testdata/vcheck/sigs/vulnerabilities.json.minisig.2
@@ -0,0 +1,4 @@
+untrusted comment: Here's a comment
+RWQkliYstQBOKFQFQTjmCd6TPw07VZyWFSB3v4+1BM1kv8eHLE5FDy2OkPEqtdaL53xftlrHoJQie0uCcovdlSV8kpyxiLrxEQ0=
+trusted comment: Here's a trusted comment
+3CnkIuz9MEDa7uNyGZAbKZhuirwfiqm7E1uQHrd2SiO4Y8+Akw9vs052AyKw0s5nhbYHCZE2IMQdHNjKwxEGAQ==
diff --git a/cmd/worldland/testdata/vcheck/sigs/vulnerabilities.json.minisig.3 b/cmd/worldland/testdata/vcheck/sigs/vulnerabilities.json.minisig.3
new file mode 100644
index 0000000000..6fd33b19a3
--- /dev/null
+++ b/cmd/worldland/testdata/vcheck/sigs/vulnerabilities.json.minisig.3
@@ -0,0 +1,4 @@
+untrusted comment: One more (untrusted) comment
+RWQkliYstQBOKFQFQTjmCd6TPw07VZyWFSB3v4+1BM1kv8eHLE5FDy2OkPEqtdaL53xftlrHoJQie0uCcovdlSV8kpyxiLrxEQ0=
+trusted comment: Here's a trusted comment
+3CnkIuz9MEDa7uNyGZAbKZhuirwfiqm7E1uQHrd2SiO4Y8+Akw9vs052AyKw0s5nhbYHCZE2IMQdHNjKwxEGAQ==
diff --git a/cmd/worldland/testdata/vcheck/vulnerabilities.json b/cmd/worldland/testdata/vcheck/vulnerabilities.json
new file mode 100644
index 0000000000..e098406f13
--- /dev/null
+++ b/cmd/worldland/testdata/vcheck/vulnerabilities.json
@@ -0,0 +1,170 @@
+[
+ {
+ "name": "CorruptedDAG",
+ "uid": "GETH-2020-01",
+ "summary": "Mining nodes will generate erroneous PoW on epochs > `385`.",
+ "description": "A mining flaw could cause miners to erroneously calculate PoW, due to an index overflow, if DAG size is exceeding the maximum 32 bit unsigned value.\n\nThis occurred on the ETC chain on 2020-11-06. This is likely to trigger for ETH mainnet around block `11550000`/epoch `385`, slated to occur early January 2021.\n\nThis issue is relevant only for miners, non-mining nodes are unaffected, since non-mining nodes use a smaller verification cache instead of a full DAG.",
+ "links": [
+ "https://github.com/cryptoecc/ETH-ECC/pull/21793",
+ "https://blog.ethereum.org/2020/11/12/geth_security_release/",
+ "https://github.com/cryptoecc/ETH-ECC/commit/567d41d9363706b4b13ce0903804e8acf214af49",
+ "https://github.com/cryptoecc/ETH-ECC/security/advisories/GHSA-v592-xf75-856p"
+ ],
+ "introduced": "v1.6.0",
+ "fixed": "v1.9.24",
+ "published": "2020-11-12",
+ "severity": "Medium",
+ "CVE": "CVE-2020-26240",
+ "check": "Geth\\/v1\\.(6|7|8)\\..*|Geth\\/v1\\.9\\.\\d-.*|Geth\\/v1\\.9\\.1.*|Geth\\/v1\\.9\\.2(0|1|2|3)-.*"
+ },
+ {
+ "name": "Denial of service due to Go CVE-2020-28362",
+ "uid": "GETH-2020-02",
+ "summary": "A denial-of-service issue can be used to crash Geth nodes during block processing, due to an underlying bug in Go (CVE-2020-28362) versions < `1.15.5`, or `<1.14.12`",
+ "description": "The DoS issue can be used to crash all Geth nodes during block processing, the effects of which would be that a major part of the Ethereum network went offline.\n\nOutside of Go-Ethereum, the issue is most likely relevant for all forks of Geth (such as TurboGeth or ETC’s core-geth) which is built with versions of Go which contains the vulnerability.",
+ "links": [
+ "https://blog.ethereum.org/2020/11/12/geth_security_release/",
+ "https://groups.google.com/g/golang-announce/c/NpBGTTmKzpM",
+ "https://github.com/golang/go/issues/42552",
+ "https://github.com/cryptoecc/ETH-ECC/security/advisories/GHSA-m6gx-rhvj-fh52"
+ ],
+ "introduced": "v0.0.0",
+ "fixed": "v1.9.24",
+ "published": "2020-11-12",
+ "severity": "Critical",
+ "CVE": "CVE-2020-28362",
+ "check": "Geth.*\\/go1\\.(11(.*)|12(.*)|13(.*)|14|14\\.(\\d|10|11|)|15|15\\.[0-4])$"
+ },
+ {
+ "name": "ShallowCopy",
+ "uid": "GETH-2020-03",
+ "summary": "A consensus flaw in Geth, related to `datacopy` precompile",
+ "description": "Geth erroneously performed a 'shallow' copy when the precompiled `datacopy` (at `0x00...04`) was invoked. An attacker could deploy a contract that uses the shallow copy to corrupt the contents of the `RETURNDATA`, thus causing a consensus failure.",
+ "links": [
+ "https://blog.ethereum.org/2020/11/12/geth_security_release/",
+ "https://github.com/cryptoecc/ETH-ECC/security/advisories/GHSA-69v6-xc2j-r2jf"
+ ],
+ "introduced": "v1.9.7",
+ "fixed": "v1.9.17",
+ "published": "2020-11-12",
+ "severity": "Critical",
+ "CVE": "CVE-2020-26241",
+ "check": "Geth\\/v1\\.9\\.(7|8|9|10|11|12|13|14|15|16).*$"
+ },
+ {
+ "name": "Geth DoS via MULMOD",
+ "uid": "GETH-2020-04",
+ "summary": "A denial-of-service issue can be used to crash Geth nodes during block processing",
+ "description": "Affected versions suffer from a vulnerability which can be exploited through the `MULMOD` operation, by specifying a modulo of `0`: `mulmod(a,b,0)`, causing a `panic` in the underlying library. \nThe crash was in the `uint256` library, where a buffer [underflowed](https://github.com/holiman/uint256/blob/4ce82e695c10ddad57215bdbeafb68b8c5df2c30/uint256.go#L442).\n\n\tif `d == 0`, `dLen` remains `0`\n\nand https://github.com/holiman/uint256/blob/4ce82e695c10ddad57215bdbeafb68b8c5df2c30/uint256.go#L451 will try to access index `[-1]`.\n\nThe `uint256` library was first merged in this [commit](https://github.com/cryptoecc/ETH-ECC/commit/cf6674539c589f80031f3371a71c6a80addbe454), on 2020-06-08. \nExploiting this vulnerabilty would cause all vulnerable nodes to drop off the network. \n\nThe issue was brought to our attention through a [bug report](https://github.com/cryptoecc/ETH-ECC/issues/21367), showing a `panic` occurring on sync from genesis on the Ropsten network.\n \nIt was estimated that the least obvious way to fix this would be to merge the fix into `uint256`, make a new release of that library and then update the geth-dependency.\n",
+ "links": [
+ "https://blog.ethereum.org/2020/11/12/geth_security_release/",
+ "https://github.com/cryptoecc/ETH-ECC/security/advisories/GHSA-jm5c-rv3w-w83m",
+ "https://github.com/holiman/uint256/releases/tag/v1.1.1",
+ "https://github.com/holiman/uint256/pull/80",
+ "https://github.com/cryptoecc/ETH-ECC/pull/21368"
+ ],
+ "introduced": "v1.9.16",
+ "fixed": "v1.9.18",
+ "published": "2020-11-12",
+ "severity": "Critical",
+ "CVE": "CVE-2020-26242",
+ "check": "Geth\\/v1\\.9.(16|17).*$"
+ },
+ {
+ "name": "LES Server DoS via GetProofsV2",
+ "uid": "GETH-2020-05",
+ "summary": "A DoS vulnerability can make a LES server crash.",
+ "description": "A DoS vulnerability can make a LES server crash via malicious GetProofsV2 request from a connected LES client.\n\nThe vulnerability was patched in #21896.\n\nThis vulnerability only concern users explicitly running geth as a light server",
+ "links": [
+ "https://github.com/cryptoecc/ETH-ECC/security/advisories/GHSA-r33q-22hv-j29q",
+ "https://github.com/cryptoecc/ETH-ECC/pull/21896"
+ ],
+ "introduced": "v1.8.0",
+ "fixed": "v1.9.25",
+ "published": "2020-12-10",
+ "severity": "Medium",
+ "CVE": "CVE-2020-26264",
+ "check": "(Geth\\/v1\\.8\\.*)|(Geth\\/v1\\.9\\.\\d-.*)|(Geth\\/v1\\.9\\.1\\d-.*)|(Geth\\/v1\\.9\\.(20|21|22|23|24)-.*)$"
+ },
+ {
+ "name": "SELFDESTRUCT-recreate consensus flaw",
+ "uid": "GETH-2020-06",
+ "introduced": "v1.9.4",
+ "fixed": "v1.9.20",
+ "summary": "A consensus-vulnerability in Geth could cause a chain split, where vulnerable versions refuse to accept the canonical chain.",
+ "description": "A flaw was repoted at 2020-08-11 by John Youngseok Yang (Software Platform Lab), where a particular sequence of transactions could cause a consensus failure.\n\n- Tx 1:\n - `sender` invokes `caller`.\n - `caller` invokes `0xaa`. `0xaa` has 3 wei, does a self-destruct-to-self\n - `caller` does a `1 wei` -call to `0xaa`, who thereby has 1 wei (the code in `0xaa` still executed, since the tx is still ongoing, but doesn't redo the selfdestruct, it takes a different path if callvalue is non-zero)\n\n-Tx 2:\n - `sender` does a 5-wei call to 0xaa. No exec (since no code). \n\nIn geth, the result would be that `0xaa` had `6 wei`, whereas OE reported (correctly) `5` wei. Furthermore, in geth, if the second tx was not executed, the `0xaa` would be destructed, resulting in `0 wei`. Thus obviously wrong. \n\nIt was determined that the root cause was this [commit](https://github.com/cryptoecc/ETH-ECC/commit/223b950944f494a5b4e0957fd9f92c48b09037ad) from [this PR](https://github.com/cryptoecc/ETH-ECC/pull/19953). The semantics of `createObject` was subtly changd, into returning a non-nil object (with `deleted=true`) where it previously did not if the account had been destructed. This return value caused the new object to inherit the old `balance`.\n",
+ "links": [
+ "https://github.com/cryptoecc/ETH-ECC/security/advisories/GHSA-xw37-57qp-9mm4"
+ ],
+ "published": "2020-12-10",
+ "severity": "High",
+ "CVE": "CVE-2020-26265",
+ "check": "(Geth\\/v1\\.9\\.(4|5|6|7|8|9)-.*)|(Geth\\/v1\\.9\\.1\\d-.*)$"
+ },
+ {
+ "name": "Not ready for London upgrade",
+ "uid": "GETH-2021-01",
+ "summary": "The client is not ready for the 'London' technical upgrade, and will deviate from the canonical chain when the London upgrade occurs (at block '12965000' around August 4, 2021.",
+ "description": "At (or around) August 4, Ethereum will undergo a technical upgrade called 'London'. Clients not upgraded will fail to progress on the canonical chain.",
+ "links": [
+ "https://github.com/ethereum/eth1.0-specs/blob/master/network-upgrades/mainnet-upgrades/london.md",
+ "https://notes.ethereum.org/@timbeiko/ropsten-postmortem"
+ ],
+ "introduced": "v1.10.1",
+ "fixed": "v1.10.6",
+ "published": "2021-07-22",
+ "severity": "High",
+ "check": "(Geth\\/v1\\.10\\.(1|2|3|4|5)-.*)$"
+ },
+ {
+ "name": "RETURNDATA corruption via datacopy",
+ "uid": "GETH-2021-02",
+ "summary": "A consensus-flaw in the Geth EVM could cause a node to deviate from the canonical chain.",
+ "description": "A memory-corruption bug within the EVM can cause a consensus error, where vulnerable nodes obtain a different `stateRoot` when processing a maliciously crafted transaction. This, in turn, would lead to the chain being split: mainnet splitting in two forks.\n\nAll Geth versions supporting the London hard fork are vulnerable (the bug is older than London), so all users should update.\n\nThis bug was exploited on Mainnet at block 13107518.\n\nCredits for the discovery go to @guidovranken (working for Sentnl during an audit of the Telos EVM) and reported via bounty@ethereum.org.",
+ "links": [
+ "https://github.com/cryptoecc/ETH-ECC/blob/master/docs/postmortems/2021-08-22-split-postmortem.md",
+ "https://github.com/cryptoecc/ETH-ECC/security/advisories/GHSA-9856-9gg9-qcmq",
+ "https://github.com/cryptoecc/ETH-ECC/releases/tag/v1.10.8"
+ ],
+ "introduced": "v1.10.0",
+ "fixed": "v1.10.8",
+ "published": "2021-08-24",
+ "severity": "High",
+ "CVE": "CVE-2021-39137",
+ "check": "(Geth\\/v1\\.10\\.(0|1|2|3|4|5|6|7)-.*)$"
+ },
+ {
+ "name": "DoS via malicious `snap/1` request",
+ "uid": "GETH-2021-03",
+ "summary": "A vulnerable node is susceptible to crash when processing a maliciously crafted message from a peer, via the snap/1 protocol. The crash can be triggered by sending a malicious snap/1 GetTrieNodes package.",
+ "description": "The `snap/1` protocol handler contains two vulnerabilities related to the `GetTrieNodes` packet, which can be exploited to crash the node. Full details are available at the Github security [advisory](https://github.com/cryptoecc/ETH-ECC/security/advisories/GHSA-59hh-656j-3p7v)",
+ "links": [
+ "https://github.com/cryptoecc/ETH-ECC/security/advisories/GHSA-59hh-656j-3p7v",
+ "https://geth.ethereum.org/docs/vulnerabilities/vulnerabilities",
+ "https://github.com/cryptoecc/ETH-ECC/pull/23657"
+ ],
+ "introduced": "v1.10.0",
+ "fixed": "v1.10.9",
+ "published": "2021-10-24",
+ "severity": "Medium",
+ "CVE": "CVE-2021-41173",
+ "check": "(Geth\\/v1\\.10\\.(0|1|2|3|4|5|6|7|8)-.*)$"
+ },
+ {
+ "name": "DoS via malicious p2p message",
+ "uid": "GETH-2022-01",
+ "summary": "A vulnerable node can crash via p2p messages sent from an attacker node, if running with non-default log options.",
+ "description": "A vulnerable node, if configured to use high verbosity logging, can be made to crash when handling specially crafted p2p messages sent from an attacker node. Full details are available at the Github security [advisory](https://github.com/cryptoecc/ETH-ECC/security/advisories/GHSA-wjxw-gh3m-7pm5)",
+ "links": [
+ "https://github.com/cryptoecc/ETH-ECC/security/advisories/GHSA-wjxw-gh3m-7pm5",
+ "https://geth.ethereum.org/docs/vulnerabilities/vulnerabilities",
+ "https://github.com/cryptoecc/ETH-ECC/pull/24507"
+ ],
+ "introduced": "v1.10.0",
+ "fixed": "v1.10.17",
+ "published": "2022-05-11",
+ "severity": "Low",
+ "CVE": "CVE-2022-29177",
+ "check": "(Geth\\/v1\\.10\\.(0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16)-.*)$"
+ }
+]
diff --git a/cmd/worldland/testdata/wrong-passwords.txt b/cmd/worldland/testdata/wrong-passwords.txt
new file mode 100644
index 0000000000..7d1e338bbf
--- /dev/null
+++ b/cmd/worldland/testdata/wrong-passwords.txt
@@ -0,0 +1,3 @@
+wrong
+wrong
+wrong
diff --git a/cmd/worldland/version_check.go b/cmd/worldland/version_check.go
new file mode 100644
index 0000000000..cc73e80a57
--- /dev/null
+++ b/cmd/worldland/version_check.go
@@ -0,0 +1,170 @@
+// Copyright 2020 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package main
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+ "os"
+ "regexp"
+ "strings"
+
+ "github.com/cryptoecc/ETH-ECC/log"
+ "github.com/jedisct1/go-minisign"
+ "github.com/urfave/cli/v2"
+)
+
+var gethPubKeys []string = []string{
+ //@holiman, minisign public key FB1D084D39BAEC24
+ "RWQk7Lo5TQgd+wxBNZM+Zoy+7UhhMHaWKzqoes9tvSbFLJYZhNTbrIjx",
+ //minisign public key 138B1CA303E51687
+ "RWSHFuUDoxyLEzjszuWZI1xStS66QTyXFFZG18uDfO26CuCsbckX1e9J",
+ //minisign public key FD9813B2D2098484
+ "RWSEhAnSshOY/b+GmaiDkObbCWefsAoavjoLcPjBo1xn71yuOH5I+Lts",
+}
+
+type vulnJson struct {
+ Name string
+ Uid string
+ Summary string
+ Description string
+ Links []string
+ Introduced string
+ Fixed string
+ Published string
+ Severity string
+ Check string
+ CVE string
+}
+
+func versionCheck(ctx *cli.Context) error {
+ url := ctx.String(VersionCheckUrlFlag.Name)
+ version := ctx.String(VersionCheckVersionFlag.Name)
+ log.Info("Checking vulnerabilities", "version", version, "url", url)
+ return checkCurrent(url, version)
+}
+
+func checkCurrent(url, current string) error {
+ var (
+ data []byte
+ sig []byte
+ err error
+ )
+ if data, err = fetch(url); err != nil {
+ return fmt.Errorf("could not retrieve data: %w", err)
+ }
+ if sig, err = fetch(fmt.Sprintf("%v.minisig", url)); err != nil {
+ return fmt.Errorf("could not retrieve signature: %w", err)
+ }
+ if err = verifySignature(gethPubKeys, data, sig); err != nil {
+ return err
+ }
+ var vulns []vulnJson
+ if err = json.Unmarshal(data, &vulns); err != nil {
+ return err
+ }
+ allOk := true
+ for _, vuln := range vulns {
+ r, err := regexp.Compile(vuln.Check)
+ if err != nil {
+ return err
+ }
+ if r.MatchString(current) {
+ allOk = false
+ fmt.Printf("## Vulnerable to %v (%v)\n\n", vuln.Uid, vuln.Name)
+ fmt.Printf("Severity: %v\n", vuln.Severity)
+ fmt.Printf("Summary : %v\n", vuln.Summary)
+ fmt.Printf("Fixed in: %v\n", vuln.Fixed)
+ if len(vuln.CVE) > 0 {
+ fmt.Printf("CVE: %v\n", vuln.CVE)
+ }
+ if len(vuln.Links) > 0 {
+ fmt.Printf("References:\n")
+ for _, ref := range vuln.Links {
+ fmt.Printf("\t- %v\n", ref)
+ }
+ }
+ fmt.Println()
+ }
+ }
+ if allOk {
+ fmt.Println("No vulnerabilities found")
+ }
+ return nil
+}
+
+// fetch makes an HTTP request to the given url and returns the response body
+func fetch(url string) ([]byte, error) {
+ if filep := strings.TrimPrefix(url, "file://"); filep != url {
+ return os.ReadFile(filep)
+ }
+ res, err := http.Get(url)
+ if err != nil {
+ return nil, err
+ }
+ defer res.Body.Close()
+ body, err := io.ReadAll(res.Body)
+ if err != nil {
+ return nil, err
+ }
+ return body, nil
+}
+
+// verifySignature checks that the sigData is a valid signature of the given
+// data, for pubkey GethPubkey
+func verifySignature(pubkeys []string, data, sigdata []byte) error {
+ sig, err := minisign.DecodeSignature(string(sigdata))
+ if err != nil {
+ return err
+ }
+ // find the used key
+ var key *minisign.PublicKey
+ for _, pubkey := range pubkeys {
+ pub, err := minisign.NewPublicKey(pubkey)
+ if err != nil {
+ // our pubkeys should be parseable
+ return err
+ }
+ if pub.KeyId != sig.KeyId {
+ continue
+ }
+ key = &pub
+ break
+ }
+ if key == nil {
+ log.Info("Signing key not trusted", "keyid", keyID(sig.KeyId), "error", err)
+ return errors.New("signature could not be verified")
+ }
+ if ok, err := key.Verify(data, sig); !ok || err != nil {
+ log.Info("Verification failed error", "keyid", keyID(key.KeyId), "error", err)
+ return errors.New("signature could not be verified")
+ }
+ return nil
+}
+
+// keyID turns a binary minisign key ID into a hex string.
+// Note: key IDs are printed in reverse byte order.
+func keyID(id [8]byte) string {
+ var rev [8]byte
+ for i := range id {
+ rev[len(rev)-1-i] = id[i]
+ }
+ return fmt.Sprintf("%X", rev)
+}
diff --git a/cmd/worldland/version_check_test.go b/cmd/worldland/version_check_test.go
new file mode 100644
index 0000000000..bd4d820a79
--- /dev/null
+++ b/cmd/worldland/version_check_test.go
@@ -0,0 +1,167 @@
+// Copyright 2020 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "path/filepath"
+ "regexp"
+ "strconv"
+ "strings"
+ "testing"
+
+ "github.com/jedisct1/go-minisign"
+)
+
+func TestVerification(t *testing.T) {
+ // Signatures generated with `minisign`
+ t.Run("minisig", func(t *testing.T) {
+ // For this test, the pubkey is in testdata/minisign.pub
+ // (the privkey is `minisign.sec`, if we want to expand this test. Password 'test' )
+ pub := "RWQkliYstQBOKOdtClfgC3IypIPX6TAmoEi7beZ4gyR3wsaezvqOMWsp"
+ testVerification(t, pub, "./testdata/vcheck/minisig-sigs/")
+ })
+ // Signatures generated with `signify-openbsd`
+ t.Run("signify-openbsd", func(t *testing.T) {
+ t.Skip("This currently fails, minisign expects 4 lines of data, signify provides only 2")
+ // For this test, the pubkey is in testdata/signifykey.pub
+ // (the privkey is `signifykey.sec`, if we want to expand this test. Password 'test' )
+ pub := "RWSKLNhZb0KdATtRT7mZC/bybI3t3+Hv/O2i3ye04Dq9fnT9slpZ1a2/"
+ testVerification(t, pub, "./testdata/vcheck/signify-sigs/")
+ })
+}
+
+func testVerification(t *testing.T, pubkey, sigdir string) {
+ // Data to verify
+ data, err := os.ReadFile("./testdata/vcheck/data.json")
+ if err != nil {
+ t.Fatal(err)
+ }
+ // Signatures, with and without comments, both trusted and untrusted
+ files, err := os.ReadDir(sigdir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, f := range files {
+ sig, err := os.ReadFile(filepath.Join(sigdir, f.Name()))
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = verifySignature([]string{pubkey}, data, sig)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+}
+
+func versionUint(v string) int {
+ mustInt := func(s string) int {
+ a, err := strconv.Atoi(s)
+ if err != nil {
+ panic(v)
+ }
+ return a
+ }
+ components := strings.Split(strings.TrimPrefix(v, "v"), ".")
+ a := mustInt(components[0])
+ b := mustInt(components[1])
+ c := mustInt(components[2])
+ return a*100*100 + b*100 + c
+}
+
+// TestMatching can be used to check that the regexps are correct
+func TestMatching(t *testing.T) {
+ data, _ := os.ReadFile("./testdata/vcheck/vulnerabilities.json")
+ var vulns []vulnJson
+ if err := json.Unmarshal(data, &vulns); err != nil {
+ t.Fatal(err)
+ }
+ check := func(version string) {
+ vFull := fmt.Sprintf("Geth/%v-unstable-15339cf1-20201204/linux-amd64/go1.15.4", version)
+ for _, vuln := range vulns {
+ r, err := regexp.Compile(vuln.Check)
+ vulnIntro := versionUint(vuln.Introduced)
+ vulnFixed := versionUint(vuln.Fixed)
+ current := versionUint(version)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if vuln.Name == "Denial of service due to Go CVE-2020-28362" {
+ // this one is not tied to geth-versions
+ continue
+ }
+ if vulnIntro <= current && vulnFixed > current {
+ // Should be vulnerable
+ if !r.MatchString(vFull) {
+ t.Errorf("Should be vulnerable, version %v, intro: %v, fixed: %v %v %v",
+ version, vuln.Introduced, vuln.Fixed, vuln.Name, vuln.Check)
+ }
+ } else {
+ if r.MatchString(vFull) {
+ t.Errorf("Should not be flagged vulnerable, version %v, intro: %v, fixed: %v %v %d %d %d",
+ version, vuln.Introduced, vuln.Fixed, vuln.Name, vulnIntro, current, vulnFixed)
+ }
+ }
+ }
+ }
+ for major := 1; major < 2; major++ {
+ for minor := 0; minor < 30; minor++ {
+ for patch := 0; patch < 30; patch++ {
+ vShort := fmt.Sprintf("v%d.%d.%d", major, minor, patch)
+ check(vShort)
+ }
+ }
+ }
+}
+
+func TestGethPubKeysParseable(t *testing.T) {
+ for _, pubkey := range gethPubKeys {
+ _, err := minisign.NewPublicKey(pubkey)
+ if err != nil {
+ t.Errorf("Should be parseable")
+ }
+ }
+}
+
+func TestKeyID(t *testing.T) {
+ type args struct {
+ id [8]byte
+ }
+ tests := []struct {
+ name string
+ args args
+ want string
+ }{
+ {"@holiman key", args{id: extractKeyId(gethPubKeys[0])}, "FB1D084D39BAEC24"},
+ {"second key", args{id: extractKeyId(gethPubKeys[1])}, "138B1CA303E51687"},
+ {"third key", args{id: extractKeyId(gethPubKeys[2])}, "FD9813B2D2098484"},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := keyID(tt.args.id); got != tt.want {
+ t.Errorf("keyID() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func extractKeyId(pubkey string) [8]byte {
+ p, _ := minisign.NewPublicKey(pubkey)
+ return p.KeyId
+}