diff --git a/cmd/approve.go b/cmd/approve.go index 3ee723a7ca..1dc0353383 100644 --- a/cmd/approve.go +++ b/cmd/approve.go @@ -11,7 +11,7 @@ import ( ) func approveTokens(ctx *cli.Context) error { - a := ctx.String(flagAmount) + a := ctx.String(config.FlagAmount) amount, _ := new(big.Float).SetString(a) if amount == nil { fmt.Println("Please, introduce a valid amount. Use '.' instead of ',' if it is a decimal number") @@ -22,7 +22,7 @@ func approveTokens(ctx *cli.Context) error { return err } - if !ctx.Bool(flagYes) { + if !ctx.Bool(config.FlagYes) { fmt.Print("*WARNING* Are you sure you want to approve ", amount, " tokens to be spent by the smc ? [y/N]: ") var input string diff --git a/cmd/main.go b/cmd/main.go index 89a7165508..203699cc36 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -3,19 +3,11 @@ package main import ( "os" + "github.com/hermeznetwork/hermez-core/config" "github.com/hermeznetwork/hermez-core/log" "github.com/urfave/cli/v2" ) -const ( - flagYes = "yes" - flagCfg = "cfg" - flagNetwork = "network" - flagNetworkCfg = "network-cfg" - flagAmount = "amount" - flagRemoteMT = "remote-merkletree" -) - const ( // App name appName = "hermez-node" @@ -33,30 +25,37 @@ func main() { app.Version = version flags := []cli.Flag{ &cli.StringFlag{ - Name: flagCfg, + Name: config.FlagCfg, Aliases: []string{"c"}, Usage: "Configuration `FILE`", Required: false, }, &cli.StringFlag{ - Name: flagNetwork, + Name: config.FlagNetwork, Aliases: []string{"n"}, - Usage: "Network: mainnet, testnet, internaltestnet, local. By default it uses mainnet", + Usage: "Network: mainnet, testnet, internaltestnet, local, custom, merge. By default it uses mainnet", Required: false, }, &cli.StringFlag{ - Name: flagNetworkCfg, + Name: config.FlagNetworkCfg, Aliases: []string{"nc"}, Usage: "Custom network configuration `FILE` when using --network custom parameter", }, + &cli.StringFlag{ + Name: config.FlagNetworkBase, + Aliases: []string{"nb"}, + Usage: "Base existing network configuration to be merged with the custom configuration passed with --network-cfg, by default it uses internaltestnet", + Value: "internaltestnet", + Required: false, + }, &cli.BoolFlag{ - Name: flagYes, + Name: config.FlagYes, Aliases: []string{"y"}, Usage: "Automatically accepts any confirmation to execute the command", Required: false, }, &cli.BoolFlag{ - Name: flagRemoteMT, + Name: config.FlagRemoteMT, Aliases: []string{"mt"}, Usage: "Connect to merkletree service instead of use local libraries", Required: false, @@ -89,7 +88,7 @@ func main() { Usage: "Approve tokens to be spent by the smart contract", Action: approveTokens, Flags: append(flags, &cli.StringFlag{ - Name: flagAmount, + Name: config.FlagAmount, Aliases: []string{"am"}, Usage: "Amount that is gonna be approved", Required: true, diff --git a/cmd/register.go b/cmd/register.go index b50c6260d4..6003e35ecb 100644 --- a/cmd/register.go +++ b/cmd/register.go @@ -16,7 +16,7 @@ import ( func registerSequencer(ctx *cli.Context) error { url := ctx.Args().First() var input string - if !ctx.Bool(flagYes) { + if !ctx.Bool(config.FlagYes) { fmt.Print("*WARNING* Are you sure you want to register " + "the sequencer in the rollup using the domain <" + url + ">? [y/N]: ") if _, err := fmt.Scanln(&input); err != nil { @@ -78,7 +78,7 @@ func registerSequencer(ctx *cli.Context) error { } // If Sequencer exists in the db - if !ctx.Bool(flagYes) { + if !ctx.Bool(config.FlagYes) { fmt.Print("*WARNING* Sequencer is already registered. Do you want to update " + "the sequencer url in the rollup usign the domain <" + url + ">? [y/N]: ") if _, err := fmt.Scanln(&input); err != nil { diff --git a/cmd/run.go b/cmd/run.go index 12511cd33e..d3f2c060a3 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -84,7 +84,7 @@ func start(ctx *cli.Context) error { grpcClientConns []*grpc.ClientConn cancelFuncs []context.CancelFunc ) - if ctx.Bool(flagRemoteMT) { + if ctx.Bool(config.FlagRemoteMT) { log.Debugf("running with remote MT") srvCfg := &tree.ServerConfig{ Host: c.MTServer.Host, diff --git a/config/config.go b/config/config.go index 2c0bc0fba8..877938f177 100644 --- a/config/config.go +++ b/config/config.go @@ -22,9 +22,13 @@ import ( ) const ( - flagCfg = "cfg" - flagNetwork = "network" - flagNetworkCfg = "network-cfg" + FlagYes = "yes" + FlagCfg = "cfg" + FlagNetwork = "network" + FlagNetworkCfg = "network-cfg" + FlagNetworkBase = "network-base" + FlagAmount = "amount" + FlagRemoteMT = "remote-merkletree" ) // Config represents the configuration of the entire Hermez Node @@ -56,7 +60,7 @@ func Load(ctx *cli.Context) (*Config, error) { if err != nil { return nil, err } - configFilePath := ctx.String(flagCfg) + configFilePath := ctx.String(FlagCfg) if configFilePath != "" { dirName, fileName := filepath.Split(configFilePath) diff --git a/config/config_test.go b/config/config_test.go index e50d405574..7f86adc936 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -7,13 +7,10 @@ import ( "strings" "testing" - "github.com/ethereum/go-ethereum/common" "github.com/hermeznetwork/hermez-core/config" - "github.com/hermeznetwork/hermez-core/encoding" "github.com/hermeznetwork/hermez-core/pricegetter" "github.com/hermeznetwork/hermez-core/sequencer" "github.com/hermeznetwork/hermez-core/state/tree" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" ) @@ -96,49 +93,6 @@ func Test_Defaults(t *testing.T) { } } -func Test_CustomNetwork(t *testing.T) { - var err error - - app := cli.NewApp() - var n string - flag.StringVar(&n, "network", "custom", "") - var nc string - flag.StringVar(&nc, "network-cfg", "./network-config.example.json", "") - ctx := cli.NewContext(app, flag.CommandLine, nil) - - cfg, err := config.Load(ctx) - require.NoError(t, err) - - assert.Equal(t, uint8(4), cfg.NetworkConfig.Arity) - assert.Equal(t, uint64(1), cfg.NetworkConfig.GenBlockNumber) - assert.Equal(t, common.HexToAddress("0xCF7ED3ACCA5A467E9E704C703E8D87F634FB0FC9").Hex(), cfg.NetworkConfig.PoEAddr.Hex()) - assert.Equal(t, common.HexToAddress("0x37AFFAF737C3683AB73F6E1B0933B725AB9796AA").Hex(), cfg.NetworkConfig.MaticAddr.Hex()) - assert.Equal(t, uint64(1337), cfg.NetworkConfig.L1ChainID) - assert.Equal(t, uint64(1000), cfg.NetworkConfig.L2DefaultChainID) - assert.Equal(t, uint64(123456), cfg.NetworkConfig.MaxCumulativeGasUsed) - - assert.Equal(t, 3, len(cfg.NetworkConfig.Genesis.Balances)) - - assertBalance := func(t *testing.T, a, b string) { - balance, ok := big.NewInt(0).SetString(b, encoding.Base10) - assert.True(t, ok) - - addr := common.HexToAddress(a) - balanceFound, found := cfg.NetworkConfig.Genesis.Balances[addr] - assert.True(t, found) - - if !found { - return - } - - assert.Equal(t, 0, balance.Cmp(balanceFound)) - } - - assertBalance(t, "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", "1000000000000000000000") - assertBalance(t, "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "2000000000000000000000") - assertBalance(t, "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", "3000000000000000000000") -} - func getValueFromStruct(path string, object interface{}) interface{} { keySlice := strings.Split(path, ".") v := reflect.ValueOf(object) diff --git a/config/network-config.example.json b/config/network-config.example.json deleted file mode 100644 index 88a2e26d90..0000000000 --- a/config/network-config.example.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "arity": 4, - "genBlockNumber": 1, - "poeAddr": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9", - "maticAddr": "0x37AffAf737C3683aB73F6E1B0933b725Ab9796Aa", - "l2globalExitRootManagerAddr": "0x0000000000000000000000000000000000000000", - "systemSCAddr": "0x0000000000000000000000000000000000000000", - "globalExitRootStoragePosition": 2, - "localExitRootStoragePosition": 2, - "oldStateRootPosition": 0, - "l1ChainID": 1337, - "l2DefaultChainID": 1000, - "genesis": { - "balances": { - "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266": "1000000000000000000000", - "0x70997970C51812dc3A010C7d01b50e0d17dc79C8": "2000000000000000000000", - "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC": "3000000000000000000000" - } - }, - "maxCumulativeGasUsed": 123456 -} \ No newline at end of file diff --git a/config/network.go b/config/network.go index 00a46109ba..373892aa82 100644 --- a/config/network.go +++ b/config/network.go @@ -6,10 +6,12 @@ import ( "io/ioutil" "math/big" "os" + "reflect" "github.com/ethereum/go-ethereum/common" "github.com/hermeznetwork/hermez-core/encoding" "github.com/hermeznetwork/hermez-core/log" + "github.com/imdario/mergo" "github.com/urfave/cli/v2" ) @@ -39,32 +41,34 @@ type Genesis struct { } type networkConfigFromJSON struct { - Arity uint8 `json:"arity"` - GenBlockNumber uint64 `json:"genBlockNumber"` - PoEAddr string `json:"poeAddr"` - MaticAddr string `json:"maticAddr"` - L2GlobalExitRootManagerAddr string `json:"l2globalExitRootManagerAddr"` - SystemSCAddr string `json:"systemSCAddr"` - GlobalExitRootStoragePosition uint64 `json:"globalExitRootStoragePosition"` - LocalExitRootStoragePosition uint64 `json:"localExitRootStoragePosition"` - OldStateRootPosition uint64 `json:"oldStateRootPosition"` - L1ChainID uint64 `json:"l1ChainID"` - L2DefaultChainID uint64 `json:"l2DefaultChainID"` - Genesis genesisFromJSON - MaxCumulativeGasUsed uint64 `json:"maxCumulativeGasUsed"` + Arity uint8 `json:"arity"` + PoEAddr string `json:"proofOfEfficiencyAddress"` + MaticAddr string `json:"maticTokenAddress"` + GenBlockNumber uint64 `json:"deploymentBlockNumber"` + SystemSCAddr string `json:"systemSCAddr"` + GlobalExitRootStoragePosition uint64 `json:"globalExitRootStoragePosition"` + LocalExitRootStoragePosition uint64 `json:"localExitRootStoragePosition"` + OldStateRootPosition uint64 `json:"oldStateRootPosition"` + L1ChainID uint64 `json:"l1ChainID"` + L2DefaultChainID uint64 `json:"defaultChainID"` + Genesis []genesisAccountFromJSON `json:"genesis"` + MaxCumulativeGasUsed uint64 `json:"maxCumulativeGasUsed"` } -type genesisFromJSON struct { - Balances map[string]string `json:"balances"` - SmartContracts map[string][]byte `json:"smartContracts"` - Storage map[string]map[string]string `json:"storage"` - Nonces map[string]*string `json:"nonces"` +type genesisAccountFromJSON struct { + Balance string `json:"balance"` + Nonce string `json:"nonce"` + Address string `json:"address"` + Bytecode string `json:"bytecode"` + Storage map[string]string `json:"storage"` + ContractName string `json:"contractName"` } const ( testnet = "testnet" internalTestnet = "internaltestnet" local = "local" + merge = "merge" custom = "custom" ) @@ -248,10 +252,16 @@ var ( }, MaxCumulativeGasUsed: 30000000, } + + networkConfigByName = map[string]NetworkConfig{ + testnet: testnetConfig, + internalTestnet: internalTestnetConfig, + local: localConfig, + } ) func (cfg *Config) loadNetworkConfig(ctx *cli.Context) { - network := ctx.String(flagNetwork) + network := ctx.String(FlagNetwork) switch network { case testnet: log.Debug("Testnet network selected") @@ -268,6 +278,21 @@ func (cfg *Config) loadNetworkConfig(ctx *cli.Context) { log.Fatalf("Failed to load custom network configuration, err:", err) } cfg.NetworkConfig = customNetworkConfig + case merge: + customNetworkConfig, err := loadCustomNetworkConfig(ctx) + if err != nil { + log.Fatalf("Failed to load custom network configuration, err:", err) + } + baseNetworkConfigName := ctx.String(FlagNetworkBase) + baseNetworkConfig, ok := networkConfigByName[baseNetworkConfigName] + if !ok { + log.Fatalf("Base network configuration %q not found:", baseNetworkConfigName) + } + mergedNetworkConfig, err := mergeNetworkConfigs(customNetworkConfig, baseNetworkConfig) + if err != nil { + log.Fatalf("Failed to merge network configurations network configuration, err:", err) + } + cfg.NetworkConfig = mergedNetworkConfig default: log.Debug("Mainnet network selected") cfg.NetworkConfig = mainnetConfig @@ -283,7 +308,7 @@ func bigIntFromBase10String(s string) *big.Int { } func loadCustomNetworkConfig(ctx *cli.Context) (NetworkConfig, error) { - cfgPath := ctx.String(flagNetworkCfg) + cfgPath := ctx.String(FlagNetworkCfg) f, err := os.Open(cfgPath) //nolint:gosec if err != nil { @@ -312,24 +337,82 @@ func loadCustomNetworkConfig(ctx *cli.Context) (NetworkConfig, error) { cfg.GenBlockNumber = cfgJSON.GenBlockNumber cfg.PoEAddr = common.HexToAddress(cfgJSON.PoEAddr) cfg.MaticAddr = common.HexToAddress(cfgJSON.MaticAddr) - cfg.L2GlobalExitRootManagerAddr = common.HexToAddress(cfgJSON.L2GlobalExitRootManagerAddr) cfg.SystemSCAddr = common.HexToAddress(cfgJSON.SystemSCAddr) cfg.GlobalExitRootStoragePosition = cfgJSON.GlobalExitRootStoragePosition cfg.LocalExitRootStoragePosition = cfgJSON.LocalExitRootStoragePosition cfg.OldStateRootPosition = cfgJSON.OldStateRootPosition cfg.L1ChainID = cfgJSON.L1ChainID cfg.L2DefaultChainID = cfgJSON.L2DefaultChainID - cfg.Genesis = Genesis{Balances: make(map[common.Address]*big.Int, len(cfgJSON.Genesis.Balances))} cfg.MaxCumulativeGasUsed = cfgJSON.MaxCumulativeGasUsed - for k, v := range cfgJSON.Genesis.Balances { - addr := common.HexToAddress(k) - balance, ok := big.NewInt(0).SetString(v, encoding.Base10) - if !ok { - return NetworkConfig{}, fmt.Errorf("Invalid balance for account %s", addr) - } - cfg.Genesis.Balances[addr] = balance + if len(cfgJSON.Genesis) == 0 { + return cfg, nil + } + + cfg.Genesis = Genesis{ + Balances: make(map[common.Address]*big.Int, len(cfgJSON.Genesis)), + SmartContracts: make(map[common.Address][]byte, len(cfgJSON.Genesis)), + Storage: make(map[common.Address]map[*big.Int]*big.Int, len(cfgJSON.Genesis)), + Nonces: make(map[common.Address]*big.Int, len(cfgJSON.Genesis)), } + const l2GlobalExitRootManagerSCName = "GlobalExitRootManagerL2" + + for _, account := range cfgJSON.Genesis { + addr := common.HexToAddress(account.Address) + if account.ContractName == l2GlobalExitRootManagerSCName { + cfg.L2GlobalExitRootManagerAddr = common.HexToAddress(account.Address) + } + + if account.Balance != "" && account.Balance != "0" { + balance, ok := big.NewInt(0).SetString(account.Balance, encoding.Base10) + if !ok { + return NetworkConfig{}, fmt.Errorf("Invalid balance for account %s", addr) + } + cfg.Genesis.Balances[addr] = balance + } + if account.Bytecode != "" { + cfg.Genesis.SmartContracts[addr] = common.FromHex(account.Bytecode) + } + if len(account.Storage) > 0 { + cfg.Genesis.Storage[addr] = make(map[*big.Int]*big.Int, len(account.Storage)) + for storageKey, storageValue := range account.Storage { + cfg.Genesis.Storage[addr][new(big.Int).SetBytes(common.FromHex(storageKey))] = new(big.Int).SetBytes(common.FromHex(storageValue)) + } + } + if account.Nonce != "" && account.Nonce != "0" { + nonce, ok := big.NewInt(0).SetString(account.Nonce, encoding.Base10) + if !ok { + return NetworkConfig{}, fmt.Errorf("Invalid nonce for account %s", addr) + } + cfg.Genesis.Nonces[addr] = nonce + } + } return cfg, nil } + +type addressTransformer struct{} + +func (a addressTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error { + if typ != reflect.TypeOf(common.Address{}) { + return nil + } + return func(dst, src reflect.Value) error { + if !dst.CanSet() { + return nil + } + hex := src.MethodByName("Hex") + result := hex.Call([]reflect.Value{}) + if result[0].Interface().(string) != "0x0000000000000000000000000000000000000000" { + dst.Set(src) + } + return nil + } +} + +func mergeNetworkConfigs(custom, base NetworkConfig) (NetworkConfig, error) { + if err := mergo.MergeWithOverwrite(&base, custom, mergo.WithTransformers(addressTransformer{})); err != nil { + return NetworkConfig{}, err + } + return base, nil +} diff --git a/config/network_test.go b/config/network_test.go new file mode 100644 index 0000000000..bf5c7fc607 --- /dev/null +++ b/config/network_test.go @@ -0,0 +1,348 @@ +package config + +import ( + "flag" + "io/ioutil" + "math/big" + "os" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/hermeznetwork/hermez-core/test/testutils" + "github.com/stretchr/testify/require" + "github.com/urfave/cli/v2" +) + +func TestLoadCustomNetworkConfig(t *testing.T) { + tcs := []struct { + description string + inputConfigStr string + expectedConfig NetworkConfig + expectedError bool + expectedErrorMsg string + }{ + { + description: "happy path", + inputConfigStr: `{ + "deploymentBlockNumber": 6934972, + "proofOfEfficiencyAddress": "0x2f612dc8fB986E7976AEfc13d8bB0Eb18488a4C9", + "maticTokenAddress": "0xEa2f9aC0cd926C92923355e88Af73Ee83F2D9C67", + + "arity": 4, + "globalExitRootStoragePosition": 0, + "localExitRootStoragePosition": 1, + "oldStateRootPosition": 0, + "l1ChainID": 5, + + "defaultChainID": 1000, + "genesis": [ + { + "balance": "0", + "nonce": "2", + "address": "0xc949254d682d8c9ad5682521675b8f43b102aec4" + }, + { + "balance": "0", + "nonce": "1", + "address": "0xae4bb80be56b819606589de61d5ec3b522eeb032", + "bytecode": "0xbeef1", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x9d98deabc42dd696deb9e40b4f1cab7ddbf55988" + }, + "contractName": "GlobalExitRootManagerL2" + }, + { + "balance": "100000000000000000000000", + "nonce": "2", + "address": "0x9d98deabc42dd696deb9e40b4f1cab7ddbf55988", + "bytecode": "0xbeef2", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0xc949254d682d8c9ad5682521675b8f43b102aec4" + }, + "contractName": "Bridge" + }, + { + "balance": "0", + "nonce": "1", + "address": "0x61ba0248b0986c2480181c6e76b6adeeaa962483", + "bytecode": "0xbeef3", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x01" + } + } + ], + "maxCumulativeGasUsed": 300000 +}`, + expectedConfig: NetworkConfig{ + Arity: 4, + GenBlockNumber: 6934972, + PoEAddr: common.HexToAddress("0x2f612dc8fB986E7976AEfc13d8bB0Eb18488a4C9"), + MaticAddr: common.HexToAddress("0xEa2f9aC0cd926C92923355e88Af73Ee83F2D9C67"), + + L2GlobalExitRootManagerAddr: common.HexToAddress("0xae4bb80be56b819606589de61d5ec3b522eeb032"), + SystemSCAddr: common.Address{}, + GlobalExitRootStoragePosition: 0, + LocalExitRootStoragePosition: 1, + OldStateRootPosition: 0, + L1ChainID: 5, + L2DefaultChainID: 1000, + Genesis: Genesis{ + Balances: map[common.Address]*big.Int{ + common.HexToAddress("0x9d98deabc42dd696deb9e40b4f1cab7ddbf55988"): bigIntFromBase10String("100000000000000000000000"), + }, + SmartContracts: map[common.Address][]byte{ + common.HexToAddress("0xae4bb80be56b819606589de61d5ec3b522eeb032"): common.FromHex("0xbeef1"), + common.HexToAddress("0x9d98deabc42dd696deb9e40b4f1cab7ddbf55988"): common.FromHex("0xbeef2"), + common.HexToAddress("0x61ba0248b0986c2480181c6e76b6adeeaa962483"): common.FromHex("0xbeef3"), + }, + Storage: map[common.Address]map[*big.Int]*big.Int{ + common.HexToAddress("0xae4bb80be56b819606589de61d5ec3b522eeb032"): { + new(big.Int).SetBytes(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")): new(big.Int).SetBytes(common.Hex2Bytes("9d98deabc42dd696deb9e40b4f1cab7ddbf55988")), + }, + common.HexToAddress("0x9d98deabc42dd696deb9e40b4f1cab7ddbf55988"): { + new(big.Int).SetBytes(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")): new(big.Int).SetBytes(common.Hex2Bytes("c949254d682d8c9ad5682521675b8f43b102aec4")), + }, + common.HexToAddress("0x61ba0248b0986c2480181c6e76b6adeeaa962483"): { + new(big.Int).SetBytes(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")): new(big.Int).SetBytes(common.Hex2Bytes("01")), + }, + }, + Nonces: map[common.Address]*big.Int{ + common.HexToAddress("0xc949254d682d8c9ad5682521675b8f43b102aec4"): bigIntFromBase10String("2"), + common.HexToAddress("0xae4bb80be56b819606589de61d5ec3b522eeb032"): bigIntFromBase10String("1"), + common.HexToAddress("0x9d98deabc42dd696deb9e40b4f1cab7ddbf55988"): bigIntFromBase10String("2"), + common.HexToAddress("0x61ba0248b0986c2480181c6e76b6adeeaa962483"): bigIntFromBase10String("1"), + }, + }, + MaxCumulativeGasUsed: 300000, + }, + }, + { + description: "imported from network-config.example.json", + inputConfigStr: `{ + "arity": 4, + "deploymentBlockNumber": 1, + "proofOfEfficiencyAddress": "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9", + "maticTokenAddress": "0x37AffAf737C3683aB73F6E1B0933b725Ab9796Aa", + "systemSCAddr": "0x0000000000000000000000000000000000000000", + "globalExitRootStoragePosition": 2, + "localExitRootStoragePosition": 2, + "oldStateRootPosition": 0, + "l1ChainID": 1337, + "defaultChainID": 1000, + "genesis": [ + { + "address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + "balance": "1000000000000000000000" + }, + { + "address": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", + "balance": "2000000000000000000000" + }, + { + "address": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", + "balance": "3000000000000000000000" + } + ], + "maxCumulativeGasUsed": 123456 +}`, + expectedConfig: NetworkConfig{ + Arity: 4, + GenBlockNumber: 1, + PoEAddr: common.HexToAddress("0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9"), + MaticAddr: common.HexToAddress("0x37AffAf737C3683aB73F6E1B0933b725Ab9796Aa"), + + SystemSCAddr: common.Address{}, + GlobalExitRootStoragePosition: 2, + LocalExitRootStoragePosition: 2, + OldStateRootPosition: 0, + L1ChainID: 1337, + L2DefaultChainID: 1000, + Genesis: Genesis{ + Balances: map[common.Address]*big.Int{ + common.HexToAddress("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"): bigIntFromBase10String("1000000000000000000000"), + common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"): bigIntFromBase10String("2000000000000000000000"), + common.HexToAddress("0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC"): bigIntFromBase10String("3000000000000000000000"), + }, + SmartContracts: map[common.Address][]byte{}, + Storage: map[common.Address]map[*big.Int]*big.Int{}, + Nonces: map[common.Address]*big.Int{}, + }, + MaxCumulativeGasUsed: 123456, + }, + }, + { + description: "not valid JSON gives error", + inputConfigStr: "not a valid json", + expectedError: true, + expectedErrorMsg: "invalid character", + }, + { + description: "empty JSON gives error", + expectedError: true, + expectedErrorMsg: "unexpected end of JSON input", + }, + } + + for _, tc := range tcs { + tc := tc + t.Run(tc.description, func(t *testing.T) { + file, err := ioutil.TempFile("", "loadCustomNetworkConfig") + require.NoError(t, err) + defer func() { + require.NoError(t, os.Remove(file.Name())) + }() + require.NoError(t, os.WriteFile(file.Name(), []byte(tc.inputConfigStr), 0600)) + + flagSet := flag.NewFlagSet("test", flag.ExitOnError) + flagSet.String(FlagNetworkCfg, file.Name(), "") + ctx := cli.NewContext(nil, flagSet, nil) + + actualConfig, err := loadCustomNetworkConfig(ctx) + require.NoError(t, testutils.CheckError(err, tc.expectedError, tc.expectedErrorMsg)) + + require.Equal(t, tc.expectedConfig.Arity, actualConfig.Arity) + require.Equal(t, tc.expectedConfig.GenBlockNumber, actualConfig.GenBlockNumber) + require.Equal(t, tc.expectedConfig.PoEAddr, actualConfig.PoEAddr) + require.Equal(t, tc.expectedConfig.MaticAddr, actualConfig.MaticAddr) + require.Equal(t, tc.expectedConfig.L2GlobalExitRootManagerAddr, actualConfig.L2GlobalExitRootManagerAddr) + require.Equal(t, tc.expectedConfig.SystemSCAddr, actualConfig.SystemSCAddr) + require.Equal(t, tc.expectedConfig.GlobalExitRootStoragePosition, actualConfig.GlobalExitRootStoragePosition) + require.Equal(t, tc.expectedConfig.LocalExitRootStoragePosition, actualConfig.LocalExitRootStoragePosition) + require.Equal(t, tc.expectedConfig.OldStateRootPosition, actualConfig.OldStateRootPosition) + require.Equal(t, tc.expectedConfig.L1ChainID, actualConfig.L1ChainID) + require.Equal(t, tc.expectedConfig.L2DefaultChainID, actualConfig.L2DefaultChainID) + + require.Equal(t, tc.expectedConfig.Genesis.Balances, actualConfig.Genesis.Balances) + require.Equal(t, tc.expectedConfig.Genesis.SmartContracts, actualConfig.Genesis.SmartContracts) + require.Equal(t, tc.expectedConfig.Genesis.Nonces, actualConfig.Genesis.Nonces) + require.Equal(t, len(tc.expectedConfig.Genesis.Storage), len(tc.expectedConfig.Genesis.Storage)) + for address, expectedStoragePair := range tc.expectedConfig.Genesis.Storage { + actualStoragePair := actualConfig.Genesis.Storage[address] + require.NotNil(t, actualStoragePair) + for expectedStorageKey := range expectedStoragePair { + expectedStorageKeyStr := expectedStorageKey.String() + expectedStorageValueStr := tc.expectedConfig.Genesis.Storage[address][expectedStorageKey].String() + found := false + for actualStorageKey := range actualStoragePair { + actualStorageKeyStr := actualStorageKey.String() + actualStorageValueStr := actualConfig.Genesis.Storage[address][actualStorageKey].String() + if expectedStorageKeyStr == actualStorageKeyStr && expectedStorageValueStr == actualStorageValueStr { + found = true + break + } + } + require.True(t, found) + } + } + }) + } +} + +func TestMergeNetworkConfig(t *testing.T) { + tcs := []struct { + description string + inputCustomConfig NetworkConfig + inputBaseConfig NetworkConfig + expectedOutputConfig NetworkConfig + }{ + { + description: "empty", + inputCustomConfig: NetworkConfig{}, + inputBaseConfig: NetworkConfig{}, + expectedOutputConfig: NetworkConfig{}, + }, + { + description: "matching keys", + inputCustomConfig: NetworkConfig{ + GenBlockNumber: 300, + PoEAddr: common.HexToAddress("0xc949254d682d8c9ad5682521675b8f43b102aec4"), + MaticAddr: common.HexToAddress("0x1D217d81831009a5fE44C9a1Ee2480e48830CbD4"), + }, + inputBaseConfig: NetworkConfig{ + GenBlockNumber: 100, + PoEAddr: common.HexToAddress("0xb1Fe4a65D3392df68F96daC8eB4df56B2411afBf"), + MaticAddr: common.HexToAddress("0x6bad17aC92f0E9313E8c7c3B80E902f1c4D5255F"), + }, + expectedOutputConfig: NetworkConfig{ + GenBlockNumber: 300, + PoEAddr: common.HexToAddress("0xc949254d682d8c9ad5682521675b8f43b102aec4"), + MaticAddr: common.HexToAddress("0x1D217d81831009a5fE44C9a1Ee2480e48830CbD4"), + }, + }, + { + description: "non-matching keys", + inputCustomConfig: NetworkConfig{ + GenBlockNumber: 300, + PoEAddr: common.HexToAddress("0xc949254d682d8c9ad5682521675b8f43b102aec4"), + MaticAddr: common.HexToAddress("0x1D217d81831009a5fE44C9a1Ee2480e48830CbD4"), + }, + inputBaseConfig: NetworkConfig{ + PoEAddr: common.HexToAddress("0xb1Fe4a65D3392df68F96daC8eB4df56B2411afBf"), + Arity: 4, + L1ChainID: 5, + MaxCumulativeGasUsed: 300, + }, + expectedOutputConfig: NetworkConfig{ + Arity: 4, + L1ChainID: 5, + MaxCumulativeGasUsed: 300, + GenBlockNumber: 300, + PoEAddr: common.HexToAddress("0xc949254d682d8c9ad5682521675b8f43b102aec4"), + MaticAddr: common.HexToAddress("0x1D217d81831009a5fE44C9a1Ee2480e48830CbD4"), + }, + }, + { + description: "nested keys", + inputCustomConfig: NetworkConfig{ + GenBlockNumber: 300, + Genesis: Genesis{ + Balances: map[common.Address]*big.Int{ + common.HexToAddress("0x9d98deabc42dd696deb9e40b4f1cab7ddbf55988"): bigIntFromBase10String("100000000000000000000000"), + common.HexToAddress("0x1D217d81831009a5fE44C9a1Ee2480e48830CbD4"): bigIntFromBase10String("900000000000000000000000"), + }, + }, + }, + inputBaseConfig: NetworkConfig{ + GenBlockNumber: 10, + Genesis: Genesis{ + Balances: map[common.Address]*big.Int{ + common.HexToAddress("0xb1Fe4a65D3392df68F96daC8eB4df56B2411afBf"): bigIntFromBase10String("200000000000000000000000"), + common.HexToAddress("0x1D217d81831009a5fE44C9a1Ee2480e48830CbD4"): bigIntFromBase10String("700000000000000000000000"), + }, + }, + }, + expectedOutputConfig: NetworkConfig{ + GenBlockNumber: 300, + Genesis: Genesis{ + Balances: map[common.Address]*big.Int{ + common.HexToAddress("0x9d98deabc42dd696deb9e40b4f1cab7ddbf55988"): bigIntFromBase10String("100000000000000000000000"), + common.HexToAddress("0x1D217d81831009a5fE44C9a1Ee2480e48830CbD4"): bigIntFromBase10String("900000000000000000000000"), + common.HexToAddress("0xb1Fe4a65D3392df68F96daC8eB4df56B2411afBf"): bigIntFromBase10String("200000000000000000000000"), + }, + }, + }, + }, + { + description: "zero address doesn't overwrite destination", + inputCustomConfig: NetworkConfig{ + PoEAddr: common.Address{}, + }, + inputBaseConfig: NetworkConfig{ + PoEAddr: common.HexToAddress("0xc949254d682d8c9ad5682521675b8f43b102aec4"), + }, + expectedOutputConfig: NetworkConfig{ + PoEAddr: common.HexToAddress("0xc949254d682d8c9ad5682521675b8f43b102aec4"), + }, + }, + } + + for _, tc := range tcs { + tc := tc + t.Run(tc.description, func(t *testing.T) { + actualOutputConfig, err := mergeNetworkConfigs(tc.inputCustomConfig, tc.inputBaseConfig) + require.NoError(t, err) + + require.Equal(t, tc.expectedOutputConfig, actualOutputConfig) + }) + } +} diff --git a/go.mod b/go.mod index 9e35a8649a..65684432a6 100644 --- a/go.mod +++ b/go.mod @@ -75,7 +75,7 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/holiman/uint256 v1.2.0 // indirect - github.com/imdario/mergo v0.3.12 // indirect + github.com/imdario/mergo v0.3.13 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect diff --git a/go.sum b/go.sum index c61e647bdb..7f6c455434 100644 --- a/go.sum +++ b/go.sum @@ -507,6 +507,8 @@ github.com/iden3/go-iden3-crypto v0.0.14-0.20220413123345-edc36bfa5247/go.mod h1 github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY= github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI=