Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add gnoland secrets command suite #1593

Merged
merged 24 commits into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
00a36d8
Add secrets init all
zivkovicmilos Jan 25, 2024
a248ff5
Add secrets init single
zivkovicmilos Jan 25, 2024
d21b713
Add secrets init unit tests
zivkovicmilos Jan 26, 2024
dc2ccf9
Add verify subcommands and tests
zivkovicmilos Jan 26, 2024
d81e84b
Add show subcommands
zivkovicmilos Jan 26, 2024
267a4f0
Add show all unit tests
zivkovicmilos Jan 26, 2024
862e796
Add show subcommand unit tests
zivkovicmilos Jan 26, 2024
13ee6be
Merge branch 'master' into feat/secrets
zivkovicmilos Jan 29, 2024
7d24dd4
Make 'secrets' a subcommand of 'gnoland'
zivkovicmilos Jan 29, 2024
8743a3d
Merge branch 'master' into feat/secrets
gfanton Feb 15, 2024
e1af56e
Merge branch 'master' into feat/secrets
zivkovicmilos Mar 14, 2024
402feac
Merge branch 'master' into feat/secrets
zivkovicmilos Mar 27, 2024
5367c44
Standardize gnoland secrets
zivkovicmilos Mar 27, 2024
08660a5
Tidy error handling
zivkovicmilos Mar 27, 2024
ea258b3
Add available keys in the secrets long help
zivkovicmilos Mar 28, 2024
88a6247
Merge branch 'master' into feat/secrets
zivkovicmilos Mar 29, 2024
5096f07
Merge branch 'master' into feat/secrets
zivkovicmilos Apr 1, 2024
572c3b2
Swap indent character in amino marshal
zivkovicmilos Apr 1, 2024
889693b
Add additional explanation on single-key manipulation
zivkovicmilos Apr 1, 2024
bda224e
Move out generation logic to secrets_init
zivkovicmilos Apr 1, 2024
888712c
Add warning about skipping validator signed state verification
zivkovicmilos Apr 1, 2024
6965f12
Rename key value
zivkovicmilos Apr 1, 2024
c058f86
Move common methods to secrets_common
zivkovicmilos Apr 1, 2024
cf5abb6
Add overwrite protection
zivkovicmilos Apr 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions gno.land/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,29 @@ start.gnoland:; go run ./cmd/gnoland start
start.gnoweb:; go run ./cmd/gnoweb

.PHONY: build
build: build.gnoland build.gnokey build.gnoweb build.gnofaucet build.gnotxsync build.genesis
build: build.gnoland build.gnokey build.gnoweb build.gnofaucet build.gnotxsync build.genesis build.secrets

build.gnoland:; go build -o build/gnoland ./cmd/gnoland
build.gnoweb:; go build -o build/gnoweb ./cmd/gnoweb
build.gnofaucet:; go build -o build/gnofaucet ./cmd/gnofaucet
build.gnokey:; go build -o build/gnokey ./cmd/gnokey
build.gnotxsync:; go build -o build/gnotxsync ./cmd/gnotxsync
build.genesis:; go build -o build/genesis ./cmd/genesis
build.secrets:; go build -o build/secrets ./cmd/secrets

run.gnoland:; go run ./cmd/gnoland start
run.gnoweb:; go run ./cmd/gnoweb

.PHONY: install
install: install.gnoland install.gnoweb install.gnofaucet install.gnokey install.gnotxsync install.genesis
install: install.gnoland install.gnoweb install.gnofaucet install.gnokey install.gnotxsync install.genesis install.secrets

install.gnoland:; go install ./cmd/gnoland
install.gnoweb:; go install ./cmd/gnoweb
install.gnofaucet:; go install ./cmd/gnofaucet
install.gnokey:; go install ./cmd/gnokey
install.gnotxsync:; go install ./cmd/gnotxsync
install.genesis:; go install ./cmd/genesis
install.secrets:; go install ./cmd/secrets

.PHONY: fclean
fclean: clean
Expand Down
180 changes: 180 additions & 0 deletions gno.land/cmd/secrets/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
package main

import (
"errors"
"fmt"
"os"

"github.com/gnolang/gno/tm2/pkg/amino"
"github.com/gnolang/gno/tm2/pkg/bft/privval"
"github.com/gnolang/gno/tm2/pkg/crypto"
"github.com/gnolang/gno/tm2/pkg/crypto/ed25519"
"github.com/gnolang/gno/tm2/pkg/p2p"
)

var (
errInvalidPrivateKey = errors.New("invalid validator private key")
errPublicKeyMismatch = errors.New("public key does not match private key derivation")
errAddressMismatch = errors.New("address does not match public key")

errInvalidSignStateStep = errors.New("invalid sign state step value")
errInvalidSignStateHeight = errors.New("invalid sign state height value")
errInvalidSignStateRound = errors.New("invalid sign state round value")

errSignatureMismatch = errors.New("signature does not match signature bytes")
errSignatureValuesMissing = errors.New("missing signature value")

errInvalidNodeKey = errors.New("invalid node p2p key")
)

// generateValidatorPrivateKey generates the validator's private key
func generateValidatorPrivateKey() *privval.FilePVKey {
privKey := ed25519.GenPrivKey()

return &privval.FilePVKey{
Address: privKey.PubKey().Address(),
PubKey: privKey.PubKey(),
PrivKey: privKey,
}
}

// generateLastSignValidatorState generates the empty last sign state
func generateLastSignValidatorState() *privval.FilePVLastSignState {
return &privval.FilePVLastSignState{} // Empty last sign state
}

// generateNodeKey generates the p2p node key
func generateNodeKey() *p2p.NodeKey {
privKey := ed25519.GenPrivKey()

return &p2p.NodeKey{
PrivKey: privKey,
}
}

// saveSecretData saves the given data as Amino JSON to the path
func saveSecretData(data any, path string) error {
// Get Amino JSON
marshalledData, err := amino.MarshalJSONIndent(data, "", " ")
if err != nil {
return fmt.Errorf("unable to marshal data into JSON, %w", err)
}

// Save the data to disk
if err := os.WriteFile(path, marshalledData, 0o644); err != nil {
return fmt.Errorf("unable to save data to path, %w", err)
}

return nil
}

// isValidDirectory verifies the directory at the given path exists
func isValidDirectory(dirPath string) bool {
fileInfo, err := os.Stat(dirPath)
if err != nil {
return false
}

// Check if the path is indeed a directory
return fileInfo.IsDir()
}

type secretData interface {
privval.FilePVKey | privval.FilePVLastSignState | p2p.NodeKey
}

// readSecretData reads the secret data from the given path
func readSecretData[T secretData](
path string,
) (*T, error) {
dataRaw, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("unable to read data, %w", err)
}

var data T
if err := amino.UnmarshalJSON(dataRaw, &data); err != nil {
return nil, fmt.Errorf("unable to unmarshal data, %w", err)
}

return &data, nil
}

// validateValidatorKey validates the validator's private key
func validateValidatorKey(key *privval.FilePVKey) error {
// Make sure the private key is set
if key.PrivKey == nil {
return errInvalidPrivateKey
}

// Make sure the public key is derived
// from the private one
if !key.PrivKey.PubKey().Equals(key.PubKey) {
return errPublicKeyMismatch
}

// Make sure the address is derived
// from the public key
if key.PubKey.Address().Compare(key.Address) != 0 {
return errAddressMismatch
}

return nil
}

// validateValidatorState validates the validator's last sign state
func validateValidatorState(state *privval.FilePVLastSignState) error {
// Make sure the sign step is valid
if state.Step < 0 {
return errInvalidSignStateStep
}

// Make sure the height is valid
if state.Height < 0 {
return errInvalidSignStateHeight
}

// Make sure the round is valid
if state.Round < 0 {
return errInvalidSignStateRound
}

return nil
}

// validateValidatorStateSignature validates the signature section
// of the last sign validator state
func validateValidatorStateSignature(
state *privval.FilePVLastSignState,
key crypto.PubKey,
) error {
// Make sure the signature and signature bytes are valid
signBytesPresent := state.SignBytes != nil
signaturePresent := state.Signature != nil

if signBytesPresent && !signaturePresent ||
!signBytesPresent && signaturePresent {
return errSignatureValuesMissing
}

if !signaturePresent {
// No need to verify further
return nil
}

// Make sure the signature bytes match the signature
if !key.VerifyBytes(state.SignBytes, state.Signature) {
return errSignatureMismatch
}

return nil
}

// validateNodeKey validates the node's p2p key
func validateNodeKey(key *p2p.NodeKey) error {
if key.PrivKey == nil {
return errInvalidNodeKey
}

return nil
}
Loading
Loading