Skip to content

Commit

Permalink
refactor: revert secret struct
Browse files Browse the repository at this point in the history
  • Loading branch information
aeddi committed Feb 18, 2025
1 parent a4fb4ba commit bc8245f
Show file tree
Hide file tree
Showing 9 changed files with 291 additions and 94 deletions.
26 changes: 21 additions & 5 deletions gno.land/cmd/gnoland/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import (
"path/filepath"

"github.com/gnolang/gno/tm2/pkg/bft/config"
signer "github.com/gnolang/gno/tm2/pkg/bft/privval/signer/local"
"github.com/gnolang/gno/tm2/pkg/bft/privval/state"
fstate "github.com/gnolang/gno/tm2/pkg/bft/privval/state"
"github.com/gnolang/gno/tm2/pkg/commands"
)

Expand Down Expand Up @@ -77,11 +76,28 @@ func constructSecretsPath(nodeDir string) string {

type (
secrets struct {
ValidatorKeyInfo *signer.FileKey `json:"validator_key,omitempty" toml:"validator_key" comment:"the validator private key info"`
ValidatorStateInfo *state.FileState `json:"validator_state,omitempty" toml:"validator_state" comment:"the last signed validator state info"`
NodeIDInfo *nodeIDInfo `json:"node_id,omitempty" toml:"node_id" comment:"the derived node ID info"`
ValidatorKeyInfo *validatorKeyInfo `json:"validator_key,omitempty" toml:"validator_key" comment:"the validator private key info"`
ValidatorStateInfo *validatorStateInfo `json:"validator_state,omitempty" toml:"validator_state" comment:"the last signed validator state info"`
NodeIDInfo *nodeIDInfo `json:"node_id,omitempty" toml:"node_id" comment:"the derived node ID info"`
}

// NOTE: keep in sync with tm2/pkg/bft/privval/state/state.go
validatorKeyInfo struct {
Address string `json:"address" toml:"address" comment:"the validator address"`
PubKey string `json:"pub_key" toml:"pub_key" comment:"the validator public key"`
}

// NOTE: keep in sync with tm2/pkg/bft/privval/signer/local/key.go
validatorStateInfo struct {
Height int64 `json:"height" toml:"height" comment:"the height of the last sign"`
Round int `json:"round" toml:"round" comment:"the round of the last sign"`
Step fstate.Step `json:"step" toml:"step" comment:"the step of the last sign"`

Signature []byte `json:"signature,omitempty" toml:"signature,omitempty" comment:"the signature of the last sign"`
SignBytes []byte `json:"sign_bytes,omitempty" toml:"sign_bytes,omitempty" comment:"the raw signature bytes of the last sign"`
}

// NOTE: keep in sync with tm2/pkg/p2p/types/key.go
nodeIDInfo struct {
ID string `json:"id" toml:"id" comment:"the node ID derived from the private key"`
P2PAddress string `json:"p2p_address" toml:"p2p_address" comment:"the node's constructed P2P address'"`
Expand Down
54 changes: 43 additions & 11 deletions gno.land/cmd/gnoland/secrets_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,24 @@ import (
"os"

"github.com/gnolang/gno/tm2/pkg/amino"
signer "github.com/gnolang/gno/tm2/pkg/bft/privval/signer/local"
fstate "github.com/gnolang/gno/tm2/pkg/bft/privval/state"
"github.com/gnolang/gno/tm2/pkg/crypto"
"github.com/gnolang/gno/tm2/pkg/p2p/types"
)

var errInvalidNodeKey = errors.New("invalid node p2p key")
var (
errSignatureMismatch = errors.New("signature does not match signature bytes")

// saveNodeKey saves the given NodeKey as Amino JSON to the path
func saveNodeKey(nodeKey *types.NodeKey, path string) error {
errInvalidNodeKey = errors.New("invalid node p2p key")
)

// 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(nodeKey, "", "\t")
marshalledData, err := amino.MarshalJSONIndent(data, "", "\t")
if err != nil {
return fmt.Errorf("unable to marshal NodeKey into JSON, %w", err)
return fmt.Errorf("unable to marshal data into JSON, %w", err)
}

// Save the data to disk
Expand All @@ -38,19 +45,44 @@ func isValidDirectory(dirPath string) bool {
return fileInfo.IsDir()
}

// readNodeKey reads the NodeKey from the given path
func readNodeKey(path string) (*types.NodeKey, error) {
type secretData interface {
signer.FileKey | fstate.FileState | types.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 nodeKey types.NodeKey
if err := amino.UnmarshalJSON(dataRaw, &nodeKey); err != nil {
return nil, fmt.Errorf("unable to unmarshal NodeKey, %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
}

// validateValidatorStateSignature validates the signature section
// of the last sign validator state
func validateValidatorStateSignature(
state *fstate.FileState,
key crypto.PubKey,
) error {
if state.Signature == nil {
// No need to verify further
return nil
}

return &nodeKey, 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
Expand Down
80 changes: 75 additions & 5 deletions gno.land/cmd/gnoland/secrets_common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,30 @@ import (
"path/filepath"
"testing"

signer "github.com/gnolang/gno/tm2/pkg/bft/privval/signer/local"
fstate "github.com/gnolang/gno/tm2/pkg/bft/privval/state"
"github.com/gnolang/gno/tm2/pkg/p2p/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestCommon_SaveReadNodeKey(t *testing.T) {
func TestCommon_SaveReadData(t *testing.T) {
t.Parallel()

t.Run("invalid data save path", func(t *testing.T) {
t.Parallel()

assert.ErrorContains(
t,
saveNodeKey(nil, ""),
saveSecretData(nil, ""),
"unable to save data to path",
)
})

t.Run("invalid data read path", func(t *testing.T) {
t.Parallel()

readData, err := readNodeKey("")
readData, err := readSecretData[types.NodeKey]("")
assert.Nil(t, readData)

assert.ErrorContains(
Expand All @@ -35,22 +37,90 @@ func TestCommon_SaveReadNodeKey(t *testing.T) {
)
})

t.Run("invalid data read", func(t *testing.T) {
t.Parallel()

dir := t.TempDir()
path := filepath.Join(dir, "key.json")

require.NoError(t, saveSecretData("totally valid key", path))

readData, err := readSecretData[types.NodeKey](path)
require.Nil(t, readData)

assert.ErrorContains(t, err, "unable to unmarshal data")
})

t.Run("valid data save and read", func(t *testing.T) {
t.Parallel()

dir := t.TempDir()
path := filepath.Join(dir, "key.json")
key := types.GenerateNodeKey()

require.NoError(t, saveNodeKey(key, path))
require.NoError(t, saveSecretData(key, path))

readKey, err := readNodeKey(path)
readKey, err := readSecretData[types.NodeKey](path)
require.NoError(t, err)

assert.Equal(t, key, readKey)
})
}

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

t.Run("valid state signature", func(t *testing.T) {
t.Parallel()

var (
key = signer.GenerateFileKey("")
state = &fstate.FileState{SignBytes: []byte("random data")}
)

// Prepare the signature
signature, err := key.PrivKey.Sign(state.SignBytes)
require.NoError(t, err)

state.Signature = signature

assert.NoError(t, validateValidatorStateSignature(state, key.PubKey))
})

t.Run("no state signature", func(t *testing.T) {
t.Parallel()

var (
key = signer.GenerateFileKey("")
state = &fstate.FileState{}
)

assert.NoError(t, validateValidatorStateSignature(state, key.PubKey))
})

t.Run("signature mismatch", func(t *testing.T) {
t.Parallel()

var (
key = signer.GenerateFileKey("")
state = &fstate.FileState{SignBytes: []byte("random data")}
)

// Prepare the signature
signature, err := key.PrivKey.Sign(state.SignBytes)
require.NoError(t, err)

state.Signature = signature
state.SignBytes = []byte("something different")

assert.ErrorIs(
t,
validateValidatorStateSignature(state, key.PubKey),
errSignatureMismatch,
)
})
}

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

Expand Down
41 changes: 35 additions & 6 deletions gno.land/cmd/gnoland/secrets_get.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (

"github.com/gnolang/gno/tm2/pkg/bft/config"
signer "github.com/gnolang/gno/tm2/pkg/bft/privval/signer/local"
"github.com/gnolang/gno/tm2/pkg/bft/privval/state"
fstate "github.com/gnolang/gno/tm2/pkg/bft/privval/state"
"github.com/gnolang/gno/tm2/pkg/commands"
osm "github.com/gnolang/gno/tm2/pkg/os"
"github.com/gnolang/gno/tm2/pkg/p2p/types"
Expand Down Expand Up @@ -103,23 +103,23 @@ func loadSecrets(dirPath string) (*secrets, error) {
)

var (
vkInfo *signer.FileKey
vsInfo *state.FileState
vkInfo *validatorKeyInfo
vsInfo *validatorStateInfo
niInfo *nodeIDInfo

err error
)

// Load the secrets
if osm.FileExists(validatorKeyPath) {
vkInfo, err = signer.LoadFileKey(validatorKeyPath)
vkInfo, err = readValidatorKey(validatorKeyPath)
if err != nil {
return nil, fmt.Errorf("unable to load secrets, %w", err)
}
}

if osm.FileExists(validatorStatePath) {
vsInfo, err = state.LoadFileState(validatorStatePath)
vsInfo, err = readValidatorState(validatorStatePath)
if err != nil {
return nil, fmt.Errorf("unable to load secrets, %w", err)
}
Expand All @@ -139,9 +139,38 @@ func loadSecrets(dirPath string) (*secrets, error) {
}, nil
}

// readValidatorKey reads the validator key from the given path
func readValidatorKey(path string) (*validatorKeyInfo, error) {
validatorKey, err := signer.LoadFileKey(path)
if err != nil {
return nil, fmt.Errorf("unable to read validator key, %w", err)
}

return &validatorKeyInfo{
Address: validatorKey.Address.String(),
PubKey: validatorKey.PubKey.String(),
}, nil
}

// readValidatorState reads the validator state from the given path
func readValidatorState(path string) (*validatorStateInfo, error) {
validatorState, err := fstate.LoadFileState(path)
if err != nil {
return nil, fmt.Errorf("unable to read validator state, %w", err)
}

return &validatorStateInfo{
Height: validatorState.Height,
Round: validatorState.Round,
Step: validatorState.Step,
Signature: validatorState.Signature,
SignBytes: validatorState.SignBytes,
}, nil
}

// readNodeID reads the node p2p info from the given path
func readNodeID(path string) (*nodeIDInfo, error) {
nodeKey, err := readNodeKey(path)
nodeKey, err := readSecretData[types.NodeKey](path)
if err != nil {
return nil, fmt.Errorf("unable to read node key, %w", err)
}
Expand Down
Loading

0 comments on commit bc8245f

Please sign in to comment.