Skip to content

Commit f667455

Browse files
Setup supernode keyring
1 parent 734e7bd commit f667455

File tree

13 files changed

+542
-243
lines changed

13 files changed

+542
-243
lines changed

.vscode/launch.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
"type": "go",
77
"request": "launch",
88
"mode": "debug",
9-
"program": "${workspaceFolder}/main.go",
9+
"program": "${workspaceFolder}/supernode/main.go",
1010
"env": {},
11-
"args": [],
11+
"args": ["start"],
1212
"showLog": true
1313
}
1414
]

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ require (
2222
github.com/patrickmn/go-cache v2.1.0+incompatible
2323
github.com/pkg/errors v0.9.1
2424
github.com/sirupsen/logrus v1.9.3
25+
github.com/spf13/cobra v1.8.1
2526
github.com/stretchr/testify v1.10.0
2627
github.com/x-cray/logrus-prefixed-formatter v0.5.2
2728
go.uber.org/ratelimit v0.3.1
@@ -31,6 +32,7 @@ require (
3132
google.golang.org/grpc v1.70.0
3233
google.golang.org/protobuf v1.36.5
3334
gopkg.in/natefinch/lumberjack.v2 v2.2.1
35+
gopkg.in/yaml.v3 v3.0.1
3436
)
3537

3638
require (
@@ -145,7 +147,6 @@ require (
145147
github.com/sourcegraph/conc v0.3.0 // indirect
146148
github.com/spf13/afero v1.11.0 // indirect
147149
github.com/spf13/cast v1.7.1 // indirect
148-
github.com/spf13/cobra v1.8.1 // indirect
149150
github.com/spf13/pflag v1.0.5 // indirect
150151
github.com/spf13/viper v1.19.0 // indirect
151152
github.com/stretchr/objx v0.5.2 // indirect
@@ -165,7 +166,6 @@ require (
165166
google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a // indirect
166167
google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect
167168
gopkg.in/ini.v1 v1.67.0 // indirect
168-
gopkg.in/yaml.v3 v3.0.1 // indirect
169169
gotest.tools/v3 v3.5.1 // indirect
170170
lukechampine.com/uint128 v1.3.0 // indirect
171171
nhooyr.io/websocket v1.8.6 // indirect

pkg/keyring/keyring.go

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package keyring
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/cosmos/cosmos-sdk/codec"
7+
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
8+
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
9+
"github.com/cosmos/cosmos-sdk/crypto/hd"
10+
"github.com/cosmos/cosmos-sdk/crypto/keyring"
11+
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
12+
sdk "github.com/cosmos/cosmos-sdk/types"
13+
"github.com/cosmos/go-bip39"
14+
)
15+
16+
const (
17+
// Default BIP39 passphrase
18+
DefaultBIP39Passphrase = ""
19+
20+
// Default HD path for Cosmos accounts
21+
DefaultHDPath = "m/44'/118'/0'/0/0" // Cosmos HD path
22+
23+
// Lumera address prefixes
24+
AccountAddressPrefix = "lumera"
25+
Name = "lumera"
26+
27+
// Default keyring name
28+
KeyringServiceName = "lumera-keyring"
29+
)
30+
31+
// InitSDKConfig initializes the SDK configuration with Lumera-specific settings
32+
func InitSDKConfig() {
33+
// Set prefixes
34+
accountPubKeyPrefix := AccountAddressPrefix + "pub"
35+
validatorAddressPrefix := AccountAddressPrefix + "valoper"
36+
validatorPubKeyPrefix := AccountAddressPrefix + "valoperpub"
37+
consNodeAddressPrefix := AccountAddressPrefix + "valcons"
38+
consNodePubKeyPrefix := AccountAddressPrefix + "valconspub"
39+
40+
// Set and seal config
41+
config := sdk.GetConfig()
42+
config.SetBech32PrefixForAccount(AccountAddressPrefix, accountPubKeyPrefix)
43+
config.SetBech32PrefixForValidator(validatorAddressPrefix, validatorPubKeyPrefix)
44+
config.SetBech32PrefixForConsensusNode(consNodeAddressPrefix, consNodePubKeyPrefix)
45+
config.Seal()
46+
}
47+
48+
// InitKeyring initializes the keyring
49+
func InitKeyring(backend, dir string) (keyring.Keyring, error) {
50+
// Determine keyring backend type
51+
var backendType string
52+
switch backend {
53+
case "file":
54+
backendType = "file"
55+
case "os":
56+
backendType = "os"
57+
case "test":
58+
backendType = "test"
59+
case "memory", "mem":
60+
backendType = "memory"
61+
default:
62+
return nil, fmt.Errorf("unsupported keyring backend: %s", backend)
63+
}
64+
65+
// Create interface registry and codec
66+
interfaceRegistry := codectypes.NewInterfaceRegistry()
67+
cryptocodec.RegisterInterfaces(interfaceRegistry)
68+
cdc := codec.NewProtoCodec(interfaceRegistry)
69+
70+
// Create keyring
71+
kr, err := keyring.New(
72+
KeyringServiceName,
73+
backendType,
74+
dir,
75+
nil, //TODO : Fix this, Using nil for stdin to avoid interactive prompts when using test backend
76+
cdc,
77+
)
78+
if err != nil {
79+
return nil, fmt.Errorf("failed to initialize keyring: %w", err)
80+
}
81+
82+
return kr, nil
83+
}
84+
85+
// GenerateMnemonic generates a new BIP39 mnemonic
86+
func GenerateMnemonic(entropySize int) (string, error) {
87+
entropy, err := bip39.NewEntropy(entropySize)
88+
if err != nil {
89+
return "", fmt.Errorf("failed to generate entropy: %w", err)
90+
}
91+
92+
mnemonic, err := bip39.NewMnemonic(entropy)
93+
if err != nil {
94+
return "", fmt.Errorf("failed to generate mnemonic: %w", err)
95+
}
96+
97+
return mnemonic, nil
98+
}
99+
100+
// CreateNewAccount creates a new account in the keyring
101+
func CreateNewAccount(kr keyring.Keyring, name string, entropySize int) (string, *keyring.Record, error) {
102+
// Generate a new mnemonic
103+
mnemonic, err := GenerateMnemonic(entropySize)
104+
if err != nil {
105+
return "", nil, fmt.Errorf("failed to generate mnemonic: %w", err)
106+
}
107+
108+
// Create a new account with the generated mnemonic
109+
info, err := kr.NewAccount(
110+
name,
111+
mnemonic,
112+
DefaultBIP39Passphrase,
113+
DefaultHDPath,
114+
hd.Secp256k1,
115+
)
116+
if err != nil {
117+
return "", nil, fmt.Errorf("failed to create new account: %w", err)
118+
}
119+
120+
return mnemonic, info, nil
121+
}
122+
123+
// RecoverAccountFromMnemonic recovers an account from a mnemonic
124+
func RecoverAccountFromMnemonic(kr keyring.Keyring, name, mnemonic string) (*keyring.Record, error) {
125+
// Import account from mnemonic
126+
info, err := kr.NewAccount(
127+
name,
128+
mnemonic,
129+
DefaultBIP39Passphrase,
130+
DefaultHDPath,
131+
hd.Secp256k1,
132+
)
133+
if err != nil {
134+
return nil, fmt.Errorf("failed to recover account from mnemonic: %w", err)
135+
}
136+
137+
return info, nil
138+
}
139+
140+
// DerivePrivKeyFromMnemonic derives a private key directly from a mnemonic
141+
func DerivePrivKeyFromMnemonic(mnemonic, hdPath string) (*secp256k1.PrivKey, error) {
142+
if hdPath == "" {
143+
hdPath = DefaultHDPath
144+
}
145+
146+
seed := bip39.NewSeed(mnemonic, DefaultBIP39Passphrase)
147+
master, ch := hd.ComputeMastersFromSeed(seed)
148+
149+
derivedKey, err := hd.DerivePrivateKeyForPath(master, ch, hdPath)
150+
if err != nil {
151+
return nil, fmt.Errorf("failed to derive private key: %w", err)
152+
}
153+
154+
return &secp256k1.PrivKey{Key: derivedKey}, nil
155+
}

pkg/testutil/accounts.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
package testutil
22

33
import (
4-
"testing"
54
"crypto/ecdh"
65
"github.com/stretchr/testify/require"
6+
"testing"
77

8-
"github.com/cosmos/go-bip39"
98
"github.com/cosmos/cosmos-sdk/codec"
109
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
10+
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
1111
"github.com/cosmos/cosmos-sdk/crypto/hd"
1212
"github.com/cosmos/cosmos-sdk/crypto/keyring"
13-
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
13+
"github.com/cosmos/go-bip39"
1414

1515
"github.com/LumeraProtocol/lumera/x/lumeraid/securekeyx"
1616
)
17-
17+
1818
// setupTestKeyExchange creates a key exchange instance for testing
1919
func SetupTestKeyExchange(t *testing.T, kb keyring.Keyring, addr string, peerType securekeyx.PeerType) *securekeyx.SecureKeyExchange {
2020
ke, err := securekeyx.NewSecureKeyExchange(kb, addr, peerType, ecdh.P256())
@@ -23,7 +23,7 @@ func SetupTestKeyExchange(t *testing.T, kb keyring.Keyring, addr string, peerTyp
2323
}
2424

2525
func generateMnemonic() (string, error) {
26-
entropy, err := bip39.NewEntropy(128) // 128 bits for a 12-word mnemonic
26+
entropy, err := bip39.NewEntropy(256) // 128 bits for a 12-word mnemonic
2727
if err != nil {
2828
return "", err
2929
}

supernode/cmd/keys.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package cmd
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
)
6+
7+
// keysCmd represents the keys command
8+
var keysCmd = &cobra.Command{
9+
Use: "keys",
10+
Short: "Manage keys",
11+
Long: `Manage keys for the Lumera blockchain.
12+
This command provides subcommands for adding, recovering, and listing keys.`,
13+
}
14+
15+
func init() {
16+
rootCmd.AddCommand(keysCmd)
17+
}

supernode/cmd/keys_add.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/spf13/cobra"
7+
8+
"github.com/LumeraProtocol/supernode/pkg/keyring"
9+
)
10+
11+
// keysAddCmd represents the add command for creating a new key
12+
var keysAddCmd = &cobra.Command{
13+
Use: "add [name]",
14+
Short: "Add a new key",
15+
Long: `Add a new key with the given name.
16+
This command will generate a new mnemonic and derive a key pair from it.
17+
The generated key pair will be stored in the keyring.
18+
19+
Example:
20+
lumera-cli keys add mykey`,
21+
Args: cobra.MaximumNArgs(1),
22+
RunE: func(cmd *cobra.Command, args []string) error {
23+
var keyName string
24+
if len(args) > 0 {
25+
keyName = args[0]
26+
} else {
27+
// Use the key_name from config file as default
28+
keyName = appConfig.SupernodeConfig.KeyName
29+
}
30+
31+
if keyName == "" {
32+
return fmt.Errorf("key name is required")
33+
}
34+
35+
// Initialize keyring using config values
36+
kr, err := keyring.InitKeyring(
37+
appConfig.KeyringConfig.Backend,
38+
appConfig.KeyringConfig.Dir,
39+
)
40+
if err != nil {
41+
return fmt.Errorf("failed to initialize keyring: %w", err)
42+
}
43+
44+
// Generate mnemonic and create new account
45+
// Default to 256 bits of entropy (24 words)
46+
mnemonic, info, err := keyring.CreateNewAccount(kr, keyName, 256)
47+
if err != nil {
48+
return fmt.Errorf("failed to create new account: %w", err)
49+
}
50+
51+
// Get address
52+
address, err := info.GetAddress()
53+
if err != nil {
54+
return fmt.Errorf("failed to get address: %w", err)
55+
}
56+
57+
// Print results
58+
fmt.Println("Key generated successfully!")
59+
fmt.Printf("- Name: %s\n", info.Name)
60+
fmt.Printf("- Address: %s\n", address.String())
61+
fmt.Printf("- Mnemonic: %s\n", mnemonic)
62+
fmt.Println("\nIMPORTANT: Write down the mnemonic and keep it in a safe place.")
63+
fmt.Println("The mnemonic is the only way to recover your account if you forget your password.")
64+
65+
return nil
66+
},
67+
}
68+
69+
func init() {
70+
keysCmd.AddCommand(keysAddCmd)
71+
}

0 commit comments

Comments
 (0)