-
Notifications
You must be signed in to change notification settings - Fork 385
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add
genesis validator
subcommand (#1259)
## 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
1 parent
69a1714
commit 347f3c0
Showing
9 changed files
with
678 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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", | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
Oops, something went wrong.