From f86b79ed68b3a280ddc211697ebde61365606314 Mon Sep 17 00:00:00 2001 From: Manav Darji Date: Fri, 21 Jan 2022 18:09:13 +0530 Subject: [PATCH 1/5] add dev mode for new cli --- internal/cli/server/chains/developer.go | 47 ++++++++++++++++++ internal/cli/server/config.go | 64 +++++++++++++++++++++++-- internal/cli/server/flags.go | 12 +++++ 3 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 internal/cli/server/chains/developer.go diff --git a/internal/cli/server/chains/developer.go b/internal/cli/server/chains/developer.go new file mode 100644 index 0000000000..61a8a186fd --- /dev/null +++ b/internal/cli/server/chains/developer.go @@ -0,0 +1,47 @@ +package chains + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" +) + +// GetDeveloperChain returns the developer mode configs. +func GetDeveloperChain(period uint64, faucet common.Address) *Chain { + // Override the default period to the user requested one + config := *params.AllCliqueProtocolChanges + config.Clique = ¶ms.CliqueConfig{ + Period: period, + Epoch: config.Clique.Epoch, + } + + // Assemble and return the chain having genesis with the + // precompiles and faucet pre-funded + return &Chain{ + Hash: common.Hash{}, + NetworkId: 1337, + Genesis: &core.Genesis{ + Config: &config, + ExtraData: append(append(make([]byte, 32), faucet[:]...), make([]byte, crypto.SignatureLength)...), + GasLimit: 11500000, + BaseFee: big.NewInt(params.InitialBaseFee), + Difficulty: big.NewInt(1), + Alloc: map[common.Address]core.GenesisAccount{ + common.BytesToAddress([]byte{1}): {Balance: big.NewInt(1)}, // ECRecover + common.BytesToAddress([]byte{2}): {Balance: big.NewInt(1)}, // SHA256 + common.BytesToAddress([]byte{3}): {Balance: big.NewInt(1)}, // RIPEMD + common.BytesToAddress([]byte{4}): {Balance: big.NewInt(1)}, // Identity + common.BytesToAddress([]byte{5}): {Balance: big.NewInt(1)}, // ModExp + common.BytesToAddress([]byte{6}): {Balance: big.NewInt(1)}, // ECAdd + common.BytesToAddress([]byte{7}): {Balance: big.NewInt(1)}, // ECScalarMul + common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing + common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b + faucet: {Balance: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9))}, + }, + }, + Bootnodes: []string{}, + } +} diff --git a/internal/cli/server/config.go b/internal/cli/server/config.go index 457ee53aa9..98bafb5a92 100644 --- a/internal/cli/server/config.go +++ b/internal/cli/server/config.go @@ -91,6 +91,9 @@ type Config struct { // GRPC has the grpc server related settings GRPC *GRPCConfig + + // Developer has the developer mode related settings + Developer *DeveloperConfig } type P2PConfig struct { @@ -365,6 +368,14 @@ type AccountsConfig struct { UseLightweightKDF bool `hcl:"use-lightweight-kdf,optional"` } +type DeveloperConfig struct { + // Enabled enables the developer mode + Enabled bool `hcl:"dev,optional"` + + // Period is the block period to use in developer mode + Period uint64 `hcl:"period,optional"` +} + func DefaultConfig() *Config { return &Config{ Chain: "mainnet", @@ -486,6 +497,10 @@ func DefaultConfig() *Config { GRPC: &GRPCConfig{ Addr: ":3131", }, + Developer: &DeveloperConfig{ + Enabled: false, + Period: 0, + }, } } @@ -573,6 +588,9 @@ func readConfigFile(path string) (*Config, error) { } func (c *Config) loadChain() error { + if c.Developer.Enabled { + return nil + } chain, ok := chains.GetChain(c.Chain) if !ok { return fmt.Errorf("chain '%s' not found", c.Chain) @@ -585,7 +603,7 @@ func (c *Config) loadChain() error { } // depending on the chain we have different cache values - if c.Chain != "mainnet" { + if c.Chain == "mainnet" { c.Cache.Cache = 4096 } else { c.Cache.Cache = 1024 @@ -599,8 +617,13 @@ func (c *Config) buildEth() (*ethconfig.Config, error) { return nil, err } n := ethconfig.Defaults - n.NetworkId = c.chain.NetworkId - n.Genesis = c.chain.Genesis + + // only update for non-developer mode as we don't yet + // have the chain object for it. + if !c.Developer.Enabled { + n.NetworkId = c.chain.NetworkId + n.Genesis = c.chain.Genesis + } n.HeimdallURL = c.Heimdall.URL n.WithoutHeimdall = c.Heimdall.Without @@ -640,6 +663,27 @@ func (c *Config) buildEth() (*ethconfig.Config, error) { } } + // update for developer mode + if c.Developer.Enabled { + // get developer mode chain config + c.chain = chains.GetDeveloperChain(c.Developer.Period, n.Miner.Etherbase) + + // update the parameters + n.NetworkId = c.chain.NetworkId + n.Genesis = c.chain.Genesis + + // Update cache + c.Cache.Cache = 1024 + + // Update sync mode + c.SyncMode = "full" + + // update miner gas price + if n.Miner.GasPrice == nil { + n.Miner.GasPrice = big.NewInt(1) + } + } + // discovery (this params should be in node.Config) { n.EthDiscoveryURLs = c.P2P.Discovery.DNS @@ -788,6 +832,20 @@ func (c *Config) buildNode() (*node.Config, error) { GraphQLVirtualHosts: c.JsonRPC.VHost, } + // dev mode + if c.Developer.Enabled { + cfg.UseLightweightKDF = true + + // disable p2p networking + c.P2P.NoDiscover = true + cfg.P2P.ListenAddr = "" + cfg.P2P.NoDial = true + cfg.P2P.DiscoveryV5 = false + + // data dir + cfg.DataDir = "" + } + // enable jsonrpc endpoints { if c.JsonRPC.Http.Enabled { diff --git a/internal/cli/server/flags.go b/internal/cli/server/flags.go index 09e5a7bf78..7403fc6c88 100644 --- a/internal/cli/server/flags.go +++ b/internal/cli/server/flags.go @@ -461,5 +461,17 @@ func (c *Command) Flags() *flagset.Flagset { Usage: "Address and port to bind the GRPC server", Value: &c.cliConfig.GRPC.Addr, }) + + // developer + f.BoolFlag(&flagset.BoolFlag{ + Name: "dev", + Usage: "Enable developer mode with ephemeral proof-of-authority network and a pre-funded developer account, mining enabled", + Value: &c.cliConfig.Developer.Enabled, + }) + f.Uint64Flag(&flagset.Uint64Flag{ + Name: "dev.period", + Usage: "Block period to use in developer mode (0 = mine only if transaction pending)", + Value: &c.cliConfig.Developer.Period, + }) return f } From ca0f04cfcafc001b61f49dd7518e0e90901af8ae Mon Sep 17 00:00:00 2001 From: Manav Darji Date: Fri, 21 Jan 2022 18:28:20 +0530 Subject: [PATCH 2/5] add developer enabled check for mining --- internal/cli/server/server.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/cli/server/server.go b/internal/cli/server/server.go index 67c335dff4..cb64654a69 100644 --- a/internal/cli/server/server.go +++ b/internal/cli/server/server.go @@ -107,8 +107,8 @@ func NewServer(config *Config) (*Server, error) { stack.AccountManager().AddBackend(keystore.NewKeyStore(keydir, n, p)) } - // sealing (if enabled) - if config.Sealer.Enabled { + // sealing (if enabled) or in dev mode + if config.Sealer.Enabled || config.Developer.Enabled { if err := backend.StartMining(1); err != nil { return nil, err } From 0662b70db209de7fce0960ac1bcc32d4aaaa0be5 Mon Sep 17 00:00:00 2001 From: Manav Darji Date: Mon, 24 Jan 2022 18:39:51 +0530 Subject: [PATCH 3/5] enable dummy developer account for dev mode --- internal/cli/server/config.go | 74 +++++++++++++++++++++--------- internal/cli/server/config_test.go | 2 +- internal/cli/server/server.go | 44 +++++++++++++----- 3 files changed, 86 insertions(+), 34 deletions(-) diff --git a/internal/cli/server/config.go b/internal/cli/server/config.go index 98bafb5a92..6c47de92c4 100644 --- a/internal/cli/server/config.go +++ b/internal/cli/server/config.go @@ -14,6 +14,8 @@ import ( godebug "runtime/debug" + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/fdlimit" "github.com/ethereum/go-ethereum/eth/downloader" @@ -611,7 +613,7 @@ func (c *Config) loadChain() error { return nil } -func (c *Config) buildEth() (*ethconfig.Config, error) { +func (c *Config) buildEth(stack *node.Node) (*ethconfig.Config, error) { dbHandles, err := makeDatabaseHandles() if err != nil { return nil, err @@ -665,8 +667,36 @@ func (c *Config) buildEth() (*ethconfig.Config, error) { // update for developer mode if c.Developer.Enabled { + // Get a keystore + var ks *keystore.KeyStore + if keystores := stack.AccountManager().Backends(keystore.KeyStoreType); len(keystores) > 0 { + ks = keystores[0].(*keystore.KeyStore) + } + + // Create new developer account or reuse existing one + var ( + developer accounts.Account + passphrase string + err error + ) + // etherbase has been set above, configuring the miner address from command line flags. + if n.Miner.Etherbase != (common.Address{}) { + developer = accounts.Account{Address: n.Miner.Etherbase} + } else if accs := ks.Accounts(); len(accs) > 0 { + developer = ks.Accounts()[0] + } else { + developer, err = ks.NewAccount(passphrase) + if err != nil { + Fatalf("Failed to create developer account: %v", err) + } + } + if err := ks.Unlock(developer, passphrase); err != nil { + Fatalf("Failed to unlock developer account: %v", err) + } + log.Info("Using developer account", "address", developer.Address) + // get developer mode chain config - c.chain = chains.GetDeveloperChain(c.Developer.Period, n.Miner.Etherbase) + c.chain = chains.GetDeveloperChain(c.Developer.Period, developer.Address) // update the parameters n.NetworkId = c.chain.NetworkId @@ -841,9 +871,6 @@ func (c *Config) buildNode() (*node.Config, error) { cfg.P2P.ListenAddr = "" cfg.P2P.NoDial = true cfg.P2P.DiscoveryV5 = false - - // data dir - cfg.DataDir = "" } // enable jsonrpc endpoints @@ -864,23 +891,26 @@ func (c *Config) buildNode() (*node.Config, error) { } cfg.P2P.NAT = natif - // Discovery - // if no bootnodes are defined, use the ones from the chain file. - bootnodes := c.P2P.Discovery.Bootnodes - if len(bootnodes) == 0 { - bootnodes = c.chain.Bootnodes - } - if cfg.P2P.BootstrapNodes, err = parseBootnodes(bootnodes); err != nil { - return nil, err - } - if cfg.P2P.BootstrapNodesV5, err = parseBootnodes(c.P2P.Discovery.BootnodesV5); err != nil { - return nil, err - } - if cfg.P2P.StaticNodes, err = parseBootnodes(c.P2P.Discovery.StaticNodes); err != nil { - return nil, err - } - if cfg.P2P.TrustedNodes, err = parseBootnodes(c.P2P.Discovery.TrustedNodes); err != nil { - return nil, err + // only check for non-developer modes + if !c.Developer.Enabled { + // Discovery + // if no bootnodes are defined, use the ones from the chain file. + bootnodes := c.P2P.Discovery.Bootnodes + if len(bootnodes) == 0 { + bootnodes = c.chain.Bootnodes + } + if cfg.P2P.BootstrapNodes, err = parseBootnodes(bootnodes); err != nil { + return nil, err + } + if cfg.P2P.BootstrapNodesV5, err = parseBootnodes(c.P2P.Discovery.BootnodesV5); err != nil { + return nil, err + } + if cfg.P2P.StaticNodes, err = parseBootnodes(c.P2P.Discovery.StaticNodes); err != nil { + return nil, err + } + if cfg.P2P.TrustedNodes, err = parseBootnodes(c.P2P.Discovery.TrustedNodes); err != nil { + return nil, err + } } if c.P2P.NoDiscover { diff --git a/internal/cli/server/config_test.go b/internal/cli/server/config_test.go index 6383c3e003..62296d82a4 100644 --- a/internal/cli/server/config_test.go +++ b/internal/cli/server/config_test.go @@ -16,7 +16,7 @@ func TestConfigDefault(t *testing.T) { _, err := config.buildNode() assert.NoError(t, err) - _, err = config.buildEth() + _, err = config.buildEth(nil) assert.NoError(t, err) } diff --git a/internal/cli/server/server.go b/internal/cli/server/server.go index cb64654a69..13b143d7c9 100644 --- a/internal/cli/server/server.go +++ b/internal/cli/server/server.go @@ -7,6 +7,7 @@ import ( "net" "net/http" "os" + "runtime" "strings" "time" @@ -69,11 +70,22 @@ func NewServer(config *Config) (*Server, error) { } srv.node = stack + // setup account manager (only keystore) + { + keydir := stack.KeyStoreDir() + n, p := keystore.StandardScryptN, keystore.StandardScryptP + if config.Accounts.UseLightweightKDF { + n, p = keystore.LightScryptN, keystore.LightScryptP + } + stack.AccountManager().AddBackend(keystore.NewKeyStore(keydir, n, p)) + } + // register the ethereum backend - ethCfg, err := config.buildEth() + ethCfg, err := config.buildEth(stack) if err != nil { return nil, err } + backend, err := eth.New(stack, ethCfg) if err != nil { return nil, err @@ -97,16 +109,6 @@ func NewServer(config *Config) (*Server, error) { } } - // setup account manager (only keystore) - { - keydir := stack.KeyStoreDir() - n, p := keystore.StandardScryptN, keystore.StandardScryptP - if config.Accounts.UseLightweightKDF { - n, p = keystore.LightScryptN, keystore.LightScryptP - } - stack.AccountManager().AddBackend(keystore.NewKeyStore(keydir, n, p)) - } - // sealing (if enabled) or in dev mode if config.Sealer.Enabled || config.Developer.Enabled { if err := backend.StartMining(1); err != nil { @@ -283,3 +285,23 @@ func setupLogger(logLevel string) { } log.Root().SetHandler(glogger) } + +// Fatalf formats a message to standard error and exits the program. +// The message is also printed to standard output if standard error +// is redirected to a different file. +func Fatalf(format string, args ...interface{}) { + w := io.MultiWriter(os.Stdout, os.Stderr) + if runtime.GOOS == "windows" { + // The SameFile check below doesn't work on Windows. + // stdout is unlikely to get redirected though, so just print there. + w = os.Stdout + } else { + outf, _ := os.Stdout.Stat() + errf, _ := os.Stderr.Stat() + if outf != nil && errf != nil && os.SameFile(outf, errf) { + w = os.Stderr + } + } + fmt.Fprintf(w, "Fatal: "+format+"\n", args...) + os.Exit(1) +} From 08f9931147cdcafc5129e542751d6dab91c8ec45 Mon Sep 17 00:00:00 2001 From: Manav Darji Date: Tue, 25 Jan 2022 16:33:09 +0530 Subject: [PATCH 4/5] add test for dev flag --- internal/cli/server/server_test.go | 41 ++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 internal/cli/server/server_test.go diff --git a/internal/cli/server/server_test.go b/internal/cli/server/server_test.go new file mode 100644 index 0000000000..070739c687 --- /dev/null +++ b/internal/cli/server/server_test.go @@ -0,0 +1,41 @@ +package server + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestServer_DeveloperMode(t *testing.T) { + + // get the default config + config := DefaultConfig() + + // enable developer mode + config.Developer.Enabled = true + config.Developer.Period = 2 // block time + + // start the server + server, err1 := NewServer(config) + if err1 != nil { + t.Fatalf("failed to start server: %v", err1) + } + + // record the initial block number + blockNumber := server.backend.BlockChain().CurrentBlock().Header().Number.Int64() + + var i int64 = 0 + for i = 0; i < 10; i++ { + // We expect the node to mine blocks every `config.Developer.Period` time period + time.Sleep(time.Duration(config.Developer.Period) * time.Second) + currBlock := server.backend.BlockChain().CurrentBlock().Header().Number.Int64() + expected := blockNumber + i + 1 + if res := assert.Equal(t, currBlock, expected); res == false { + break + } + } + + // stop the server + server.Stop() +} From 6ccd75c186d4e84f3a62ec21c630e0484ce776e6 Mon Sep 17 00:00:00 2001 From: Manav Darji Date: Tue, 25 Jan 2022 16:41:05 +0530 Subject: [PATCH 5/5] replace Fatalf function with fmt.Errorf --- internal/cli/server/config.go | 4 ++-- internal/cli/server/server.go | 21 --------------------- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/internal/cli/server/config.go b/internal/cli/server/config.go index 6c47de92c4..a42470af45 100644 --- a/internal/cli/server/config.go +++ b/internal/cli/server/config.go @@ -687,11 +687,11 @@ func (c *Config) buildEth(stack *node.Node) (*ethconfig.Config, error) { } else { developer, err = ks.NewAccount(passphrase) if err != nil { - Fatalf("Failed to create developer account: %v", err) + return nil, fmt.Errorf("failed to create developer account: %v", err) } } if err := ks.Unlock(developer, passphrase); err != nil { - Fatalf("Failed to unlock developer account: %v", err) + return nil, fmt.Errorf("failed to unlock developer account: %v", err) } log.Info("Using developer account", "address", developer.Address) diff --git a/internal/cli/server/server.go b/internal/cli/server/server.go index 13b143d7c9..f20ac43d1c 100644 --- a/internal/cli/server/server.go +++ b/internal/cli/server/server.go @@ -7,7 +7,6 @@ import ( "net" "net/http" "os" - "runtime" "strings" "time" @@ -285,23 +284,3 @@ func setupLogger(logLevel string) { } log.Root().SetHandler(glogger) } - -// Fatalf formats a message to standard error and exits the program. -// The message is also printed to standard output if standard error -// is redirected to a different file. -func Fatalf(format string, args ...interface{}) { - w := io.MultiWriter(os.Stdout, os.Stderr) - if runtime.GOOS == "windows" { - // The SameFile check below doesn't work on Windows. - // stdout is unlikely to get redirected though, so just print there. - w = os.Stdout - } else { - outf, _ := os.Stdout.Stat() - errf, _ := os.Stderr.Stat() - if outf != nil && errf != nil && os.SameFile(outf, errf) { - w = os.Stderr - } - } - fmt.Fprintf(w, "Fatal: "+format+"\n", args...) - os.Exit(1) -}