From 7dde46f850ea7d414a8da109fc127d1d9795329b Mon Sep 17 00:00:00 2001 From: mantre Date: Mon, 4 Mar 2024 22:58:38 +0800 Subject: [PATCH 1/6] feat(config): one reward address in config for all validators --- .golangci.yml | 1 + cmd/cmd.go | 92 +++++++++++++++++++++++++++++-------------------- cmd/cmd_test.go | 74 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+), 37 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 0cdb99235..ffd1da3e0 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -74,6 +74,7 @@ linters: - usestdlibvars - wastedassign - whitespace + # - forcetypeassert # - wrapcheck - zerologlint diff --git a/cmd/cmd.go b/cmd/cmd.go index fd04d506b..39abb9370 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -28,6 +28,7 @@ import ( "github.com/pactus-project/pactus/util" "github.com/pactus-project/pactus/wallet" "github.com/pactus-project/pactus/wallet/addresspath" + "github.com/pactus-project/pactus/wallet/vault" ) const ( @@ -397,11 +398,6 @@ func StartNode(workingDir string, passwordFetcher func(*wallet.Wallet) (string, valAddrsInfo = valAddrsInfo[:32] } - if len(conf.Node.RewardAddresses) > 0 && - len(conf.Node.RewardAddresses) != len(valAddrsInfo) { - return nil, nil, fmt.Errorf("reward addresses should be %v", len(valAddrsInfo)) - } - valAddrs := make([]string, len(valAddrsInfo)) for i := 0; i < len(valAddrs); i++ { valAddr, _ := crypto.AddressFromString(valAddrsInfo[i].Address) @@ -424,38 +420,9 @@ func StartNode(workingDir string, passwordFetcher func(*wallet.Wallet) (string, valKeys[i] = bls.NewValidatorKey(prv.(*bls.PrivateKey)) } - // Create reward addresses - rewardAddrs := make([]crypto.Address, 0, len(valAddrsInfo)) - if len(conf.Node.RewardAddresses) != 0 { - for _, addrStr := range conf.Node.RewardAddresses { - addr, _ := crypto.AddressFromString(addrStr) - rewardAddrs = append(rewardAddrs, addr) - } - } else { - for i := 0; i < len(valAddrsInfo); i++ { - valAddrPath, _ := addresspath.FromString(valAddrsInfo[i].Path) - accAddrPath := addresspath.NewPath( - valAddrPath.Purpose(), - valAddrPath.CoinType(), - uint32(crypto.AddressTypeBLSAccount)+hdkeychain.HardenedKeyStart, - valAddrPath.AddressIndex()) - - addrInfo := walletInstance.AddressFromPath(accAddrPath.String()) - if addrInfo == nil { - return nil, nil, fmt.Errorf("unable to find reward address for: %s [%s]", - valAddrsInfo[i].Address, accAddrPath) - } - - addr, _ := crypto.AddressFromString(addrInfo.Address) - rewardAddrs = append(rewardAddrs, addr) - } - } - - // Check if reward addresses are account address - for _, addr := range rewardAddrs { - if !addr.IsAccountAddress() { - return nil, nil, fmt.Errorf("reward address is not an account address: %s", addr) - } + rewardAddrs, err := MakeRewardAddresses(walletInstance, valAddrsInfo, conf.Node.RewardAddresses) + if err != nil { + return nil, nil, err } nodeInstance, err := node.NewNode(gen, conf, valKeys, rewardAddrs) @@ -575,3 +542,54 @@ func RecoverConfig(confPath string, defConf *config.Config, chainType genesis.Ch return conf, err } + +func MakeRewardAddresses(walletInstance *wallet.Wallet, + valAddrsInfo []vault.AddressInfo, confRewardAddresses []string, +) ([]crypto.Address, error) { + if len(confRewardAddresses) > 1 && + len(confRewardAddresses) != len(valAddrsInfo) { + return nil, fmt.Errorf("reward addresses should be %v", len(valAddrsInfo)) + } + + // Create reward addresses + rewardAddrs := make([]crypto.Address, 0, len(valAddrsInfo)) + if len(confRewardAddresses) != 0 { + for _, addrStr := range confRewardAddresses { + addr, _ := crypto.AddressFromString(addrStr) + rewardAddrs = append(rewardAddrs, addr) + } + + if len(rewardAddrs) == 1 { + for i := 1; i < len(valAddrsInfo); i++ { + rewardAddrs = append(rewardAddrs, rewardAddrs[0]) + } + } + } else { + for i := 0; i < len(valAddrsInfo); i++ { + valAddrPath, _ := addresspath.FromString(valAddrsInfo[i].Path) + accAddrPath := addresspath.NewPath( + valAddrPath.Purpose(), + valAddrPath.CoinType(), + uint32(crypto.AddressTypeBLSAccount)+hdkeychain.HardenedKeyStart, + valAddrPath.AddressIndex()) + + addrInfo := walletInstance.AddressFromPath(accAddrPath.String()) + if addrInfo == nil { + return nil, fmt.Errorf("unable to find reward address for: %s [%s]", + valAddrsInfo[i].Address, accAddrPath) + } + + addr, _ := crypto.AddressFromString(addrInfo.Address) + rewardAddrs = append(rewardAddrs, addr) + } + } + + // Check if reward addresses are account address + for _, addr := range rewardAddrs { + if !addr.IsAccountAddress() { + return nil, fmt.Errorf("reward address is not an account address: %s", addr) + } + } + + return rewardAddrs, nil +} diff --git a/cmd/cmd_test.go b/cmd/cmd_test.go index 3cbeb2f66..bf709e364 100644 --- a/cmd/cmd_test.go +++ b/cmd/cmd_test.go @@ -7,6 +7,10 @@ import ( "runtime" "testing" + "github.com/pactus-project/pactus/genesis" + "github.com/pactus-project/pactus/util" + "github.com/pactus-project/pactus/util/testsuite" + "github.com/pactus-project/pactus/wallet" "github.com/stretchr/testify/assert" ) @@ -161,3 +165,73 @@ func TestPathsWindows(t *testing.T) { assert.Equal(t, test.expectedConfigPath, configPath) } } + +func TestMakeRewardAddresses(t *testing.T) { + ts := testsuite.NewTestSuite(t) + + walletPath := util.TempFilePath() + mnemonic, _ := wallet.GenerateMnemonic(128) + walletInstance, err := wallet.Create(walletPath, mnemonic, "", genesis.Mainnet) + assert.NoError(t, err) + + _, _ = walletInstance.NewValidatorAddress("") + _, _ = walletInstance.NewValidatorAddress("") + _, _ = walletInstance.NewValidatorAddress("") + + // Test 1 - Wallet without reward addresses + valAddrsInfo := walletInstance.AllValidatorAddresses() + confRewardAddresses := []string{} + _, err = MakeRewardAddresses(walletInstance, valAddrsInfo, confRewardAddresses) + assert.ErrorContains(t, err, "unable to find reward address") + + // Test 2 - Not enough reward addresses in wallet + rewardAddr1, _ := walletInstance.NewBLSAccountAddress("") + rewardAddr2, _ := walletInstance.NewBLSAccountAddress("") + + _, err = MakeRewardAddresses(walletInstance, valAddrsInfo, confRewardAddresses) + assert.Error(t, err, "unable to find reward address") + + // Test 3 - Get reward addresses from wallet + rewardAddr3, _ := walletInstance.NewBLSAccountAddress("") + + rewardAddrs, err := MakeRewardAddresses(walletInstance, valAddrsInfo, confRewardAddresses) + assert.NoError(t, err, "unable to find reward address") + assert.Equal(t, rewardAddrs[0].String(), rewardAddr1) + assert.Equal(t, rewardAddrs[1].String(), rewardAddr2) + assert.Equal(t, rewardAddrs[2].String(), rewardAddr3) + + // Test 4 - Not enough reward addresses in config + confRewardAddr1 := ts.RandAccAddress().String() + confRewardAddr2 := ts.RandAccAddress().String() + confRewardAddresses = []string{confRewardAddr1, confRewardAddr2} + + _, err = MakeRewardAddresses(walletInstance, valAddrsInfo, confRewardAddresses) + assert.Error(t, err, "unable to find reward address") + + // Test 5 - Get reward addresses from config + confRewardAddr3 := ts.RandAccAddress().String() + confRewardAddresses = []string{confRewardAddr1, confRewardAddr2, confRewardAddr3} + + rewardAddrs, err = MakeRewardAddresses(walletInstance, valAddrsInfo, confRewardAddresses) + assert.NoError(t, err, "unable to find reward address") + assert.Equal(t, rewardAddrs[0].String(), confRewardAddr1) + assert.Equal(t, rewardAddrs[1].String(), confRewardAddr2) + assert.Equal(t, rewardAddrs[2].String(), confRewardAddr3) + + // Test 6 - Set one reward addresses in config + confRewardAddr := ts.RandAccAddress().String() + confRewardAddresses = []string{confRewardAddr} + + rewardAddrs, err = MakeRewardAddresses(walletInstance, valAddrsInfo, confRewardAddresses) + assert.NoError(t, err, "unable to find reward address") + assert.Equal(t, rewardAddrs[0].String(), confRewardAddr) + assert.Equal(t, rewardAddrs[1].String(), confRewardAddr) + assert.Equal(t, rewardAddrs[2].String(), confRewardAddr) + + // Test 7 - Set validator address as reward addresses in config + confRewardAddr = ts.RandValAddress().String() + confRewardAddresses = []string{confRewardAddr} + + _, err = MakeRewardAddresses(walletInstance, valAddrsInfo, confRewardAddresses) + assert.ErrorContains(t, err, "reward address is not an account address") +} From e55b76fceeee50c37d0082923f4bf1ca6a49ac2e Mon Sep 17 00:00:00 2001 From: mantre Date: Mon, 4 Mar 2024 23:06:22 +0800 Subject: [PATCH 2/6] chore: updating config comments --- config/example_config.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/config/example_config.toml b/config/example_config.toml index a80d89e8a..25ce50d3e 100644 --- a/config/example_config.toml +++ b/config/example_config.toml @@ -4,8 +4,9 @@ [node] # `reward_addresses` specifies the addresses for collecting rewards. - # If it is empty, reward addresses will be obtained from the wallet. - # The number of reward addresses should be the same as the number of validators. + # If empty, reward addresses will be obtained from the wallet. + # If it has only one address, it is used for all validators. + # Otherwise, the number of reward addresses should be the same as the number of validators. ## reward_addresses = [] # `store` contains configuration options for the store module, which manages storage and retrieval of blockchain data. From 9be958500f3dcc22f77225a216bb95356eb606df Mon Sep 17 00:00:00 2001 From: mantre Date: Mon, 4 Mar 2024 23:11:24 +0800 Subject: [PATCH 3/6] refactor: add MakeValidatorKey as function --- cmd/cmd.go | 49 ++++++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/cmd/cmd.go b/cmd/cmd.go index 39abb9370..776349110 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -398,29 +398,12 @@ func StartNode(workingDir string, passwordFetcher func(*wallet.Wallet) (string, valAddrsInfo = valAddrsInfo[:32] } - valAddrs := make([]string, len(valAddrsInfo)) - for i := 0; i < len(valAddrs); i++ { - valAddr, _ := crypto.AddressFromString(valAddrsInfo[i].Address) - if !valAddr.IsValidatorAddress() { - return nil, nil, fmt.Errorf("invalid validator address: %s", valAddrsInfo[i].Address) - } - valAddrs[i] = valAddr.String() - } - - valKeys := make([]*bls.ValidatorKey, len(valAddrsInfo)) - password, ok := passwordFetcher(walletInstance) - if !ok { - return nil, nil, fmt.Errorf("aborted") - } - prvKeys, err := walletInstance.PrivateKeys(password, valAddrs) + rewardAddrs, err := MakeRewardAddresses(walletInstance, valAddrsInfo, conf.Node.RewardAddresses) if err != nil { return nil, nil, err } - for i, prv := range prvKeys { - valKeys[i] = bls.NewValidatorKey(prv.(*bls.PrivateKey)) - } - rewardAddrs, err := MakeRewardAddresses(walletInstance, valAddrsInfo, conf.Node.RewardAddresses) + valKeys, err := MakeValidatorKey(walletInstance, valAddrsInfo, passwordFetcher) if err != nil { return nil, nil, err } @@ -593,3 +576,31 @@ func MakeRewardAddresses(walletInstance *wallet.Wallet, return rewardAddrs, nil } + +func MakeValidatorKey(walletInstance *wallet.Wallet, valAddrsInfo []vault.AddressInfo, + passwordFetcher func(*wallet.Wallet) (string, bool), +) ([]*bls.ValidatorKey, error) { + valAddrs := make([]string, len(valAddrsInfo)) + for i := 0; i < len(valAddrs); i++ { + valAddr, _ := crypto.AddressFromString(valAddrsInfo[i].Address) + if !valAddr.IsValidatorAddress() { + return nil, fmt.Errorf("invalid validator address: %s", valAddrsInfo[i].Address) + } + valAddrs[i] = valAddr.String() + } + + valKeys := make([]*bls.ValidatorKey, len(valAddrsInfo)) + password, ok := passwordFetcher(walletInstance) + if !ok { + return nil, fmt.Errorf("aborted") + } + prvKeys, err := walletInstance.PrivateKeys(password, valAddrs) + if err != nil { + return nil, err + } + for i, prv := range prvKeys { + valKeys[i] = bls.NewValidatorKey(prv.(*bls.PrivateKey)) + } + + return valKeys, nil +} From 8c95b6491c18f3e58cabdf8f886ff1911f06427a Mon Sep 17 00:00:00 2001 From: Mostafa Date: Thu, 7 Mar 2024 03:31:26 +0800 Subject: [PATCH 4/6] chore: remove forcetypeassert linter cehck --- .golangci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.golangci.yml b/.golangci.yml index ffd1da3e0..0cdb99235 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -74,7 +74,6 @@ linters: - usestdlibvars - wastedassign - whitespace - # - forcetypeassert # - wrapcheck - zerologlint From 95b11a2fcd8ef87bec4507b03d877e12fbfd4237 Mon Sep 17 00:00:00 2001 From: Mostafa Date: Thu, 21 Mar 2024 13:27:53 +0800 Subject: [PATCH 5/6] test: update test --- cmd/cmd_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/cmd_test.go b/cmd/cmd_test.go index bf709e364..4a69a973d 100644 --- a/cmd/cmd_test.go +++ b/cmd/cmd_test.go @@ -182,20 +182,20 @@ func TestMakeRewardAddresses(t *testing.T) { valAddrsInfo := walletInstance.AllValidatorAddresses() confRewardAddresses := []string{} _, err = MakeRewardAddresses(walletInstance, valAddrsInfo, confRewardAddresses) - assert.ErrorContains(t, err, "unable to find reward address") + assert.ErrorContains(t, err, "unable to find reward address for") // Test 2 - Not enough reward addresses in wallet rewardAddr1, _ := walletInstance.NewBLSAccountAddress("") rewardAddr2, _ := walletInstance.NewBLSAccountAddress("") _, err = MakeRewardAddresses(walletInstance, valAddrsInfo, confRewardAddresses) - assert.Error(t, err, "unable to find reward address") + assert.ErrorContains(t, err, "unable to find reward address for") // Test 3 - Get reward addresses from wallet rewardAddr3, _ := walletInstance.NewBLSAccountAddress("") rewardAddrs, err := MakeRewardAddresses(walletInstance, valAddrsInfo, confRewardAddresses) - assert.NoError(t, err, "unable to find reward address") + assert.NoError(t, err) assert.Equal(t, rewardAddrs[0].String(), rewardAddr1) assert.Equal(t, rewardAddrs[1].String(), rewardAddr2) assert.Equal(t, rewardAddrs[2].String(), rewardAddr3) @@ -206,14 +206,14 @@ func TestMakeRewardAddresses(t *testing.T) { confRewardAddresses = []string{confRewardAddr1, confRewardAddr2} _, err = MakeRewardAddresses(walletInstance, valAddrsInfo, confRewardAddresses) - assert.Error(t, err, "unable to find reward address") + assert.ErrorContains(t, err, "reward addresses should be 3") // Test 5 - Get reward addresses from config confRewardAddr3 := ts.RandAccAddress().String() confRewardAddresses = []string{confRewardAddr1, confRewardAddr2, confRewardAddr3} rewardAddrs, err = MakeRewardAddresses(walletInstance, valAddrsInfo, confRewardAddresses) - assert.NoError(t, err, "unable to find reward address") + assert.NoError(t, err) assert.Equal(t, rewardAddrs[0].String(), confRewardAddr1) assert.Equal(t, rewardAddrs[1].String(), confRewardAddr2) assert.Equal(t, rewardAddrs[2].String(), confRewardAddr3) @@ -223,7 +223,7 @@ func TestMakeRewardAddresses(t *testing.T) { confRewardAddresses = []string{confRewardAddr} rewardAddrs, err = MakeRewardAddresses(walletInstance, valAddrsInfo, confRewardAddresses) - assert.NoError(t, err, "unable to find reward address") + assert.NoError(t, err) assert.Equal(t, rewardAddrs[0].String(), confRewardAddr) assert.Equal(t, rewardAddrs[1].String(), confRewardAddr) assert.Equal(t, rewardAddrs[2].String(), confRewardAddr) From 084bad38a1dc3e8dcc4f000b186907865c08bf6a Mon Sep 17 00:00:00 2001 From: Mostafa Date: Sun, 24 Mar 2024 02:53:50 +0800 Subject: [PATCH 6/6] test: update create node test --- cmd/cmd.go | 4 ++- cmd/cmd_test.go | 88 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/cmd/cmd.go b/cmd/cmd.go index 59122afb3..91c02f455 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -277,7 +277,6 @@ func TrapSignal(cleanupFunc func()) { }() } -// TODO: write test for me. func CreateNode(numValidators int, chain genesis.ChainType, workingDir string, mnemonic string, walletPassword string, ) ([]string, []string, error) { @@ -330,6 +329,9 @@ func CreateNode(numValidators int, chain genesis.ChainType, workingDir string, } case genesis.Localnet: + if numValidators < 4 { + return nil, nil, fmt.Errorf("LocalNeed needs at least 4 validators") + } genDoc := makeLocalGenesis(*walletInstance) if err := genDoc.SaveToFile(genPath); err != nil { return nil, nil, err diff --git a/cmd/cmd_test.go b/cmd/cmd_test.go index 4a69a973d..7d2f73dd1 100644 --- a/cmd/cmd_test.go +++ b/cmd/cmd_test.go @@ -235,3 +235,91 @@ func TestMakeRewardAddresses(t *testing.T) { _, err = MakeRewardAddresses(walletInstance, valAddrsInfo, confRewardAddresses) assert.ErrorContains(t, err, "reward address is not an account address") } + +func TestCreateNode(t *testing.T) { + tests := []struct { + name string + numValidators int + chain genesis.ChainType + workingDir string + mnemonic string + withErr bool + validatorAddrs []string + rewardAddrs []string + }{ + { + name: "Create node for Mainnet", + numValidators: 1, + chain: genesis.Mainnet, + workingDir: util.TempDirPath(), + mnemonic: "legal winner thank year wave sausage worth useful legal winner thank yellow", + validatorAddrs: []string{"pc1pqpu5tkuctj6ecxjs85f9apm802hhc65amwhuyw"}, + rewardAddrs: []string{"pc1zmpnme0xrgzhml77e3k70ey9hwwwsfed6l04pqc"}, + withErr: false, + }, + { + name: "Create node for Testnet", + numValidators: 1, + chain: genesis.Testnet, + workingDir: util.TempDirPath(), + mnemonic: "legal winner thank year wave sausage worth useful legal winner thank yellow", + validatorAddrs: []string{"tpc1p54ex6jvqkz6qyld5wgm77qm7walgy664hxz2pc"}, + rewardAddrs: []string{"tpc1zlkjrgfkrh7f9enpt730tp5vgx7tgtqzplhfksa"}, + withErr: false, + }, + + { + name: "Create node for Localnet", + numValidators: 4, + chain: genesis.Localnet, + workingDir: util.TempDirPath(), + mnemonic: "legal winner thank year wave sausage worth useful legal winner thank yellow", + validatorAddrs: []string{ + "tpc1p54ex6jvqkz6qyld5wgm77qm7walgy664hxz2pc", + "tpc1pdf5e0q4d6eaww3uq5pmw5aayqpaqplra0pj8z2", + "tpc1pe5px2dddn6g4zgnu3wpwgrqpdjrufvda57a4wm", + "tpc1p8yyhysp380j9q9gxa6vlhstgkd94238kunttpr", + }, + rewardAddrs: []string{ + "tpc1zlkjrgfkrh7f9enpt730tp5vgx7tgtqzplhfksa", + "tpc1ztzwc9x98j88wctmzm5t09z592lqw0sqc3rn6lu", + "tpc1zslef8hjkwqxdcekcqxra6djgjr5gryrj8l3fyf", + "tpc1zru3xxmgz5dqqkv0mesqq3t3luepzg3e6jeqkeu", + }, + withErr: false, + }, + { + name: "Localnet with one validator", + numValidators: 1, + chain: genesis.Localnet, + workingDir: util.TempDirPath(), + mnemonic: "legal winner thank year wave sausage worth useful legal winner thank yellow", + validatorAddrs: nil, + rewardAddrs: nil, + withErr: true, + }, + { + name: "Invalid mnemonic", + numValidators: 4, + chain: genesis.Mainnet, + workingDir: util.TempDirPath(), + mnemonic: "", + validatorAddrs: nil, + rewardAddrs: nil, + withErr: true, + }, + } + + for _, test := range tests { + validatorAddrs, rewardAddrs, err := CreateNode( + test.numValidators, test.chain, test.workingDir, test.mnemonic, "") + + if test.withErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, test.validatorAddrs, validatorAddrs) + assert.Equal(t, test.rewardAddrs, rewardAddrs) + } + } +}