Skip to content

Commit

Permalink
feat: add genesis validator subcommand (#1259)
Browse files Browse the repository at this point in the history
## Description

This PR adds support for the `genesis validator` sub-commands, as
outlined in #1228.

Closes #1228 

Example `genesis validator add` command you can use to test out the
functionality:
```bash
genesis validator add --name milos -pub-key gpub1pgfj7ard9eg82cjtv4u4xetrwqer2dntxyfzxz3pqvknvnspy43c9zp0ts7wgvupldfzkws8kmvvz2eelfmzzupfymwpwzhh9m3 -address g1wpewvuxtyvqqwysndt4f8425tpdg3lf0mhu6fm
```
<details><summary>Contributors' checklist...</summary>

- [x] Added new tests, or not needed, or not feasible
- [x] Provided an example (e.g. screenshot) to aid review or the PR is
self-explanatory
- [x] Updated the official documentation or not needed
- [x] No breaking changes were made, or a `BREAKING CHANGE: xxx` message
was included in the description
- [x] Added references to related issues and PRs
- [x] Provided any useful hints for running manual tests
- [ ] Added new benchmarks to [generated
graphs](https://gnoland.github.io/benchmarks), if any. More info
[here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
</details>
  • Loading branch information
zivkovicmilos authored Oct 19, 2023
1 parent 69a1714 commit 347f3c0
Show file tree
Hide file tree
Showing 9 changed files with 678 additions and 4 deletions.
1 change: 1 addition & 0 deletions gno.land/cmd/genesis/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func newRootCmd(io *commands.IO) *commands.Command {

cmd.AddSubCommands(
newGenerateCmd(io),
newValidatorCmd(io),
newVerifyCmd(io),
)

Expand Down
50 changes: 50 additions & 0 deletions gno.land/cmd/genesis/validator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package main

import (
"flag"

"github.com/gnolang/gno/tm2/pkg/commands"
)

type validatorCfg struct {
genesisPath string
address string
}

// newValidatorCmd creates the genesis validator subcommand
func newValidatorCmd(io *commands.IO) *commands.Command {
cfg := &validatorCfg{}

cmd := commands.NewCommand(
commands.Metadata{
Name: "validator",
ShortUsage: "validator <subcommand> [flags]",
LongHelp: "Manipulates the genesis.json validator set",
},
cfg,
commands.HelpExec,
)

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

return cmd
}

func (c *validatorCfg) RegisterFlags(fs *flag.FlagSet) {
fs.StringVar(
&c.genesisPath,
"genesis-path",
"./genesis.json",
"the path to the genesis.json",
)

fs.StringVar(
&c.address,
"address",
"",
"the output path for the genesis.json",
)
}
137 changes: 137 additions & 0 deletions gno.land/cmd/genesis/validator_add.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package main

import (
"context"
"errors"
"flag"
"fmt"

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

var (
errInvalidPower = errors.New("invalid validator power")
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")
)

type validatorAddCfg struct {
rootCfg *validatorCfg

pubKey string
name string
power int64
}

// newValidatorAddCmd creates the genesis validator add subcommand
func newValidatorAddCmd(validatorCfg *validatorCfg, io *commands.IO) *commands.Command {
cfg := &validatorAddCfg{
rootCfg: validatorCfg,
}

return commands.NewCommand(
commands.Metadata{
Name: "add",
ShortUsage: "validator add [flags]",
LongHelp: "Adds a new validator to the genesis.json",
},
cfg,
func(_ context.Context, _ []string) error {
return execValidatorAdd(cfg, io)
},
)
}

func (c *validatorAddCfg) RegisterFlags(fs *flag.FlagSet) {
fs.StringVar(
&c.pubKey,
"pub-key",
"",
"the bech32 string representation of the validator's public key",
)

fs.StringVar(
&c.name,
"name",
"",
"the name of the validator (must be unique)",
)

fs.Int64Var(
&c.power,
"power",
1,
"the voting power of the validator (must be > 0)",
)
}

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

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

// Check the voting power
if cfg.power < 1 {
return errInvalidPower
}

// Check the name
if cfg.name == "" {
return errors.New("invalid validator name")
}

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

// Check the public key matches the address
if pubKey.Address() != address {
return errors.New("provided public key and address do not match")
}

validator := types.GenesisValidator{
Address: address,
PubKey: pubKey,
Power: cfg.power,
Name: cfg.name,
}

// Check if the validator exists
for _, genesisValidator := range genesis.Validators {
// There is no need to check if the public keys match
// since the address is derived from it, and the derivation
// is checked already
if validator.Address == genesisValidator.Address {
return errAddressPresent
}
}

// Add the validator
genesis.Validators = append(genesis.Validators, validator)

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

io.Printfln(
"Validator with address %s added to genesis file",
cfg.rootCfg.address,
)

return nil
}
Loading

0 comments on commit 347f3c0

Please sign in to comment.