Skip to content

Commit

Permalink
Add validator remove subcommand
Browse files Browse the repository at this point in the history
  • Loading branch information
zivkovicmilos committed Oct 19, 2023
1 parent d932213 commit 600f02e
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 29 deletions.
1 change: 1 addition & 0 deletions gno.land/cmd/genesis/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func newValidatorCmd(io *commands.IO) *commands.Command {

cmd.AddSubCommands(
newValidatorAddCmd(cfg, io),
newValidatorRemoveCmd(cfg, io),
)

return cmd
Expand Down
3 changes: 1 addition & 2 deletions gno.land/cmd/genesis/validator_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ var (
errInvalidName = errors.New("invalid validator name")
errPublicKeyMismatch = errors.New("provided public key and address do not match")
errAddressPresent = errors.New("validator with same address already present in genesis.json")
errPubKeyPresent = errors.New("validator with same public key already present in genesis.json")
)

type validatorAddCfg struct {
Expand Down Expand Up @@ -124,7 +123,7 @@ func execValidatorAdd(cfg *validatorAddCfg, io *commands.IO) error {
// Add the validator
genesis.Validators = append(genesis.Validators, validator)

// Save the updated
// Save the updated genesis
if err := genesis.SaveAs(cfg.validatorCfg.genesisPath); err != nil {
return fmt.Errorf("unable to save genesis.json, %w", err)
}
Expand Down
58 changes: 31 additions & 27 deletions gno.land/cmd/genesis/validator_add_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,32 @@ import (
"github.com/stretchr/testify/require"
)

// getDummyKey generates a random public key,
// and returns the key info
func getDummyKey(t *testing.T) keys.Info {
t.Helper()

mnemonic, err := client.GenerateMnemonic(256)
require.NoError(t, err)

kb := keys.NewInMemory()

info, err := kb.CreateAccount(
"dummy",
mnemonic,
"",
"",
uint32(0),
uint32(0),
)
require.NoError(t, err)

return info
}

func TestGenesis_Validator_Add(t *testing.T) {
t.Parallel()

getDummyKey := func() keys.Info {
mnemonic, err := client.GenerateMnemonic(256)
require.NoError(t, err)

kb := keys.NewInMemory()

info, err := kb.CreateAccount(
"dummy",
mnemonic,
"",
"",
uint32(0),
uint32(0),
)
require.NoError(t, err)

return info
}

t.Run("invalid genesis file", func(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -87,7 +91,7 @@ func TestGenesis_Validator_Add(t *testing.T) {
genesis := getDefaultGenesis()
require.NoError(t, genesis.SaveAs(tempGenesis.Name()))

key := getDummyKey()
key := getDummyKey(t)

// Create the command
cmd := newRootCmd(commands.NewTestIO())
Expand Down Expand Up @@ -116,7 +120,7 @@ func TestGenesis_Validator_Add(t *testing.T) {
genesis := getDefaultGenesis()
require.NoError(t, genesis.SaveAs(tempGenesis.Name()))

key := getDummyKey()
key := getDummyKey(t)

// Create the command
cmd := newRootCmd(commands.NewTestIO())
Expand Down Expand Up @@ -145,7 +149,7 @@ func TestGenesis_Validator_Add(t *testing.T) {
genesis := getDefaultGenesis()
require.NoError(t, genesis.SaveAs(tempGenesis.Name()))

key := getDummyKey()
key := getDummyKey(t)

// Create the command
cmd := newRootCmd(commands.NewTestIO())
Expand Down Expand Up @@ -177,8 +181,8 @@ func TestGenesis_Validator_Add(t *testing.T) {
require.NoError(t, genesis.SaveAs(tempGenesis.Name()))

dummyKeys := []keys.Info{
getDummyKey(),
getDummyKey(),
getDummyKey(t),
getDummyKey(t),
}

// Create the command
Expand Down Expand Up @@ -208,8 +212,8 @@ func TestGenesis_Validator_Add(t *testing.T) {
t.Cleanup(cleanup)

dummyKeys := []keys.Info{
getDummyKey(),
getDummyKey(),
getDummyKey(t),
getDummyKey(t),
}

genesis := getDefaultGenesis()
Expand Down Expand Up @@ -250,7 +254,7 @@ func TestGenesis_Validator_Add(t *testing.T) {
tempGenesis, cleanup := testutils.NewTestFile(t)
t.Cleanup(cleanup)

key := getDummyKey()
key := getDummyKey(t)
genesis := getDefaultGenesis()

require.NoError(t, genesis.SaveAs(tempGenesis.Name()))
Expand Down
71 changes: 71 additions & 0 deletions gno.land/cmd/genesis/validator_remove.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package main

import (
"context"
"errors"
"fmt"

"github.com/gnolang/gno/tm2/pkg/bft/types"
"github.com/gnolang/gno/tm2/pkg/commands"
"github.com/gnolang/gno/tm2/pkg/crypto"
)

var errValidatorNotPresent = errors.New("validator not present in genesis.json")

// newValidatorRemoveCmd creates the genesis validator remove subcommand
func newValidatorRemoveCmd(validatorCfg *validatorCfg, io *commands.IO) *commands.Command {
return commands.NewCommand(
commands.Metadata{
Name: "remove",
ShortUsage: "validator remove [flags]",
LongHelp: "Removes a validator from the genesis.json",
},
commands.NewEmptyConfig(),
func(_ context.Context, _ []string) error {
return execValidatorRemove(validatorCfg, io)
},
)
}

func execValidatorRemove(cfg *validatorCfg, io *commands.IO) error {
// Load the genesis
genesis, loadErr := types.GenesisDocFromFile(cfg.genesisPath)
if loadErr != nil {
return fmt.Errorf("unable to load genesis, %w", loadErr)
}

// Check the validator address
address, err := crypto.AddressFromString(cfg.address)
if err != nil {
return fmt.Errorf("invalid validator address, %w", err)
}

index := -1

for indx, validator := range genesis.Validators {
if validator.Address == address {
index = indx

break
}
}

if index < 0 {
return errors.New("validator not present in genesis.json")
}

// Drop the validator
genesis.Validators = append(genesis.Validators[:index], genesis.Validators[index+1:]...)

// Save the updated genesis
if err := genesis.SaveAs(cfg.genesisPath); err != nil {
return fmt.Errorf("unable to save genesis.json, %w", err)
}

io.Printfln(
"Validator with address %s removed from genesis file",
cfg.address,
)

return nil
}
134 changes: 134 additions & 0 deletions gno.land/cmd/genesis/validator_remove_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package main

import (
"context"
"testing"

"github.com/gnolang/gno/tm2/pkg/bft/types"
"github.com/gnolang/gno/tm2/pkg/commands"
"github.com/gnolang/gno/tm2/pkg/crypto/keys"
"github.com/gnolang/gno/tm2/pkg/testutils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestGenesis_Validator_Remove(t *testing.T) {
t.Parallel()

t.Run("invalid genesis file", func(t *testing.T) {
t.Parallel()

// Create the command
cmd := newRootCmd(commands.NewTestIO())
args := []string{
"validator",
"remove",
"--genesis-path",
"dummy-path",
}

// Run the command
cmdErr := cmd.ParseAndRun(context.Background(), args)
assert.ErrorContains(t, cmdErr, "unable to load genesis")
})

t.Run("invalid validator address", func(t *testing.T) {
t.Parallel()

tempGenesis, cleanup := testutils.NewTestFile(t)
t.Cleanup(cleanup)

genesis := getDefaultGenesis()
require.NoError(t, genesis.SaveAs(tempGenesis.Name()))

// Create the command
cmd := newRootCmd(commands.NewTestIO())
args := []string{
"validator",
"remove",
"--genesis-path",
tempGenesis.Name(),
"--address",
"dummyaddress",
}

// Run the command
cmdErr := cmd.ParseAndRun(context.Background(), args)
assert.ErrorContains(t, cmdErr, "invalid validator address")
})

t.Run("validator not found", func(t *testing.T) {
t.Parallel()

tempGenesis, cleanup := testutils.NewTestFile(t)
t.Cleanup(cleanup)

dummyKeys := []keys.Info{
getDummyKey(t),
getDummyKey(t),
}

genesis := getDefaultGenesis()

// Set an existing validator
genesis.Validators = append(genesis.Validators, types.GenesisValidator{
Address: dummyKeys[0].GetAddress(),
PubKey: dummyKeys[0].GetPubKey(),
Power: 1,
Name: "example",
})

require.NoError(t, genesis.SaveAs(tempGenesis.Name()))

// Create the command
cmd := newRootCmd(commands.NewTestIO())
args := []string{
"validator",
"remove",
"--genesis-path",
tempGenesis.Name(),
"--address",
dummyKeys[1].GetPubKey().Address().String(),
}

// Run the command
cmdErr := cmd.ParseAndRun(context.Background(), args)
assert.ErrorContains(t, cmdErr, errValidatorNotPresent.Error())
})

t.Run("validator removed", func(t *testing.T) {
t.Parallel()

tempGenesis, cleanup := testutils.NewTestFile(t)
t.Cleanup(cleanup)

dummyKey := getDummyKey(t)

genesis := getDefaultGenesis()

// Set an existing validator
genesis.Validators = append(genesis.Validators, types.GenesisValidator{
Address: dummyKey.GetAddress(),
PubKey: dummyKey.GetPubKey(),
Power: 1,
Name: "example",
})

require.NoError(t, genesis.SaveAs(tempGenesis.Name()))

// Create the command
cmd := newRootCmd(commands.NewTestIO())
args := []string{
"validator",
"remove",
"--genesis-path",
tempGenesis.Name(),
"--address",
dummyKey.GetPubKey().Address().String(),
}

// Run the command
cmdErr := cmd.ParseAndRun(context.Background(), args)
assert.NoError(t, cmdErr)
})
}

0 comments on commit 600f02e

Please sign in to comment.