diff --git a/README.md b/README.md index 918a1b15..fd68b107 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,68 @@ enum SupernodeEventType { } ``` +## HTTP Gateway + +The supernode provides an HTTP gateway that exposes the gRPC services via REST API. The gateway runs on a separate port (default: 8002) and provides: + +### Endpoints + +#### GET /status +Returns the current supernode status including CPU, memory usage and active services. + +```bash +curl http://localhost:8002/status +``` + +Response: +```json +{ + "cpu": { + "usage": "15.2%", + "remaining": "84.8%" + }, + "memory": { + "total": "16777216000", + "used": "8388608000", + "available": "8388608000", + "usedPerc": 50.0 + }, + "services": [ + { + "serviceName": "cascade", + "taskIds": ["task1", "task2"], + "taskCount": 2 + } + ], + "availableServices": ["cascade", "sense"] +} +``` + +#### GET /services +Returns the list of available services on the supernode. + +```bash +curl http://localhost:8002/services +``` + +Response: +```json +{ + "services": ["cascade", "sense"] +} +``` + +The gateway automatically translates between HTTP/JSON and gRPC/protobuf formats, making it easy to integrate with web applications and monitoring tools. + +### API Documentation + +The gateway provides interactive API documentation via Swagger UI: + +- **Swagger UI**: http://localhost:8002/swagger-ui/ +- **OpenAPI Spec**: http://localhost:8002/swagger.json + +The Swagger UI provides an interactive interface to explore and test all available API endpoints. + ## CLI Commands ### Core Commands @@ -117,13 +179,49 @@ enum SupernodeEventType { Initialize a new supernode with interactive setup. ```bash -supernode init # Interactive setup -supernode init --force # Override existing installation -supernode init -y # Use defaults, skip prompts -supernode init --keyring-backend os --key-name mykey # Specify keyring and key -supernode init --recover --mnemonic "word1 word2..." # Recover from mnemonic -supernode init --supernode-addr 0.0.0.0 --supernode-port 4444 # Set network -supernode init --lumera-grpc localhost:9090 --chain-id lumera-mainnet-1 # Set chain +# Interactive setup +supernode init + +# Non-interactive setup with all flags (plain passphrase - use only for testing) +supernode init -y \ + --keyring-backend file \ + --keyring-passphrase "your-secure-passphrase" \ + --key-name mykey \ + --recover \ + --mnemonic "word1 word2 word3 ... word24" \ + --supernode-addr 0.0.0.0 \ + --supernode-port 4444 \ + --gateway-port 8002 \ + --lumera-grpc https://grpc.testnet.lumera.io \ + --chain-id lumera-testnet-1 + +# Non-interactive setup with passphrase from environment variable +export KEYRING_PASS="your-secure-passphrase" +supernode init -y \ + --keyring-backend file \ + --keyring-passphrase-env KEYRING_PASS \ + --key-name mykey \ + --recover \ + --mnemonic "word1 word2 word3 ... word24" \ + --supernode-addr 0.0.0.0 \ + --supernode-port 4444 \ + --gateway-port 8002 \ + --lumera-grpc https://grpc.testnet.lumera.io \ + --chain-id lumera-testnet-1 + +# Non-interactive setup with passphrase from file +echo "your-secure-passphrase" > /path/to/passphrase.txt +supernode init -y \ + --keyring-backend file \ + --keyring-passphrase-file /path/to/passphrase.txt \ + --key-name mykey \ + --recover \ + --mnemonic "word1 word2 word3 ... word24" \ + --supernode-addr 0.0.0.0 \ + --supernode-port 4444 \ + --gateway-port 8002 \ + --lumera-grpc https://grpc.testnet.lumera.io \ + --chain-id lumera-testnet-1 ``` **Available flags:** @@ -135,8 +233,12 @@ supernode init --lumera-grpc localhost:9090 --chain-id lumera-mainnet-1 # Set c - `--mnemonic` - Mnemonic phrase for recovery (use with --recover) - `--supernode-addr` - IP address for supernode service - `--supernode-port` - Port for supernode service +- `--gateway-port` - Port for the HTTP gateway to listen on - `--lumera-grpc` - Lumera gRPC address (host:port) - `--chain-id` - Lumera blockchain chain ID +- `--keyring-passphrase` - Keyring passphrase for non-interactive mode (plain text) +- `--keyring-passphrase-env` - Environment variable containing keyring passphrase +- `--keyring-passphrase-file` - File containing keyring passphrase #### `supernode start` Start the supernode service. @@ -207,13 +309,17 @@ supernode: identity: "lumera15t2e8gjgmuqtj..." # Lumera address for this supernode ip_address: "0.0.0.0" # IP address to bind the service port: 4444 # Port for the supernode service + gateway_port: 8002 # Port for the HTTP gateway service ``` ### Keyring Configuration ```yaml keyring: - backend: "os" # Key storage backend (os, file, test) - dir: "keys" # Directory to store keyring files (relative to basedir) + backend: "os" # Key storage backend (os, file, test) + dir: "keys" # Directory to store keyring files (relative to basedir) + passphrase_plain: "" # Plain text passphrase (use only for testing) + passphrase_env: "" # Environment variable containing passphrase + passphrase_file: "" # Path to file containing passphrase ``` ### P2P Configuration diff --git a/pkg/keyring/keyring.go b/pkg/keyring/keyring.go index 97e420f7..30fddfed 100644 --- a/pkg/keyring/keyring.go +++ b/pkg/keyring/keyring.go @@ -1,188 +1,176 @@ +// Package keyring provides helpers around Cosmos-SDK key management. package keyring import ( + "bufio" "fmt" "io" "os" + "strings" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/hd" - "github.com/cosmos/cosmos-sdk/crypto/keyring" + sdkkeyring "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx/signing" "github.com/cosmos/go-bip39" + + "github.com/LumeraProtocol/supernode/supernode/config" ) const ( - // Default BIP39 passphrase DefaultBIP39Passphrase = "" + DefaultHDPath = "m/44'/118'/0'/0/0" + AccountAddressPrefix = "lumera" + KeyringServiceName = "supernode-keyring" + defaultEntropySize = 256 +) - // Default HD path for Cosmos accounts - DefaultHDPath = "m/44'/118'/0'/0/0" // Cosmos HD path +func InitSDKConfig() { + cfg := types.GetConfig() + cfg.SetBech32PrefixForAccount(AccountAddressPrefix, AccountAddressPrefix+"pub") + cfg.SetBech32PrefixForValidator(AccountAddressPrefix+"valoper", AccountAddressPrefix+"valoperpub") + cfg.SetBech32PrefixForConsensusNode(AccountAddressPrefix+"valcons", AccountAddressPrefix+"valconspub") + cfg.Seal() +} - // Lumera address prefixes - AccountAddressPrefix = "lumera" - Name = "lumera" +func InitKeyring(cfg config.KeyringConfig) (sdkkeyring.Keyring, error) { + backend, err := normaliseBackend(cfg.Backend) + if err != nil { + return nil, err + } + // Use the directory as-is, it should already be resolved by the config + dir := cfg.Dir - // Default keyring name - KeyringServiceName = "supernode-keyring" + reader, err := buildReaderAndPossiblySwapStdin(cfg, backend) + if err != nil { + return nil, err + } - defaultEntropySize = 256 // Default entropy size for mnemonic generation -) + reg := codectypes.NewInterfaceRegistry() + cryptocodec.RegisterInterfaces(reg) + cdc := codec.NewProtoCodec(reg) -// InitSDKConfig initializes the SDK configuration with Lumera-specific settings -func InitSDKConfig() { - // Set prefixes - accountPubKeyPrefix := AccountAddressPrefix + "pub" - validatorAddressPrefix := AccountAddressPrefix + "valoper" - validatorPubKeyPrefix := AccountAddressPrefix + "valoperpub" - consNodeAddressPrefix := AccountAddressPrefix + "valcons" - consNodePubKeyPrefix := AccountAddressPrefix + "valconspub" - - // Set and seal config - config := sdk.GetConfig() - config.SetBech32PrefixForAccount(AccountAddressPrefix, accountPubKeyPrefix) - config.SetBech32PrefixForValidator(validatorAddressPrefix, validatorPubKeyPrefix) - config.SetBech32PrefixForConsensusNode(consNodeAddressPrefix, consNodePubKeyPrefix) - config.Seal() + return sdkkeyring.New(KeyringServiceName, backend, dir, reader, cdc) } -// InitKeyring initializes the keyring -func InitKeyring(backend, dir string) (keyring.Keyring, error) { - // Determine keyring backend type - var backendType string - switch backend { - case "file": - backendType = "file" - case "os": - backendType = "os" - case "test": - backendType = "test" - case "memory", "mem": - backendType = "memory" - default: - return nil, fmt.Errorf("unsupported keyring backend: %s", backend) +// buildReaderAndPossiblySwapStdin returns the reader handed to Cosmos-SDK. +// For automation we replace os.Stdin with the read-end of an os.Pipe whose +// FD is *not* a TTY, so input.GetPassword() falls into its non-interactive +// code path. +func buildReaderAndPossiblySwapStdin(cfg config.KeyringConfig, backend string) (io.Reader, error) { + if backend == "test" { + return nil, nil } - // Create interface registry and codec - interfaceRegistry := codectypes.NewInterfaceRegistry() - cryptocodec.RegisterInterfaces(interfaceRegistry) - cdc := codec.NewProtoCodec(interfaceRegistry) - - // Create keyring with proper stdin handling - var stdin io.Reader = os.Stdin - if backendType == "test" { - stdin = nil // Only use nil for test backend + pass := selectPassphrase(cfg) + if pass == "" { + // Interactive: leave the real terminal in place. + return os.Stdin, nil } - // Create keyring - kr, err := keyring.New( - KeyringServiceName, - backendType, - dir, - stdin, - cdc, - ) + // ---------- non-interactive ---------- + + // Create a pipe (both ends are *os.File, so assignment is legal). + r, w, err := os.Pipe() if err != nil { - return nil, fmt.Errorf("failed to initialize keyring: %w", err) + return nil, fmt.Errorf("create stdin pipe: %w", err) } - return kr, nil + // Replace os.Stdin with the pipe's read end (non-TTY). + os.Stdin = r + + // Continuously write the passphrase + newline from a goroutine. + go func() { + line := []byte(pass + "\n") + for { + if _, err := w.Write(line); err != nil { + return // pipe closed on shutdown + } + } + }() + + // The keyring prompt reads from the reader we pass to the SDK, so wrap + // the same *os.File r in a bufio.Reader and return it. + return bufio.NewReader(r), nil } -// GenerateMnemonic generates a new BIP39 mnemonic -func GenerateMnemonic() (string, error) { - entropy, err := bip39.NewEntropy(defaultEntropySize) - if err != nil { - return "", fmt.Errorf("failed to generate entropy: %w", err) +func selectPassphrase(cfg config.KeyringConfig) string { + switch { + case cfg.PassPlain != "": + return cfg.PassPlain + case cfg.PassEnv != "": + return os.Getenv(cfg.PassEnv) + case cfg.PassFile != "": + if b, err := os.ReadFile(cfg.PassFile); err == nil { + return strings.TrimSpace(string(b)) + } } + return "" +} - mnemonic, err := bip39.NewMnemonic(entropy) - if err != nil { - return "", fmt.Errorf("failed to generate mnemonic: %w", err) +func normaliseBackend(b string) (string, error) { + switch strings.ToLower(b) { + case "file": + return "file", nil + case "os": + return "os", nil + case "test": + return "test", nil + case "memory", "mem": + return "memory", nil + default: + return "", fmt.Errorf("unsupported keyring backend: %s", b) } - - return mnemonic, nil } -// CreateNewAccount creates a new account in the keyring -func CreateNewAccount(kr keyring.Keyring, name string) (string, *keyring.Record, error) { - // Generate a new mnemonic - mnemonic, err := GenerateMnemonic() - if err != nil { - return "", nil, fmt.Errorf("failed to generate mnemonic: %w", err) - } - // Create a new account with the generated mnemonic - info, err := kr.NewAccount( - name, - mnemonic, - DefaultBIP39Passphrase, - DefaultHDPath, - hd.Secp256k1, - ) +func GenerateMnemonic() (string, error) { + entropy, err := bip39.NewEntropy(defaultEntropySize) if err != nil { - return "", nil, fmt.Errorf("failed to create new account: %w", err) + return "", err } - - return mnemonic, info, nil + return bip39.NewMnemonic(entropy) } -// RecoverAccountFromMnemonic recovers an account from a mnemonic -func RecoverAccountFromMnemonic(kr keyring.Keyring, name, mnemonic string) (*keyring.Record, error) { - // Import account from mnemonic - info, err := kr.NewAccount( - name, - mnemonic, - DefaultBIP39Passphrase, - DefaultHDPath, - hd.Secp256k1, - ) +func CreateNewAccount(kr sdkkeyring.Keyring, name string) (string, *sdkkeyring.Record, error) { + mn, err := GenerateMnemonic() if err != nil { - return nil, fmt.Errorf("failed to recover account from mnemonic: %w", err) + return "", nil, err } + info, err := kr.NewAccount(name, mn, DefaultBIP39Passphrase, DefaultHDPath, hd.Secp256k1) + return mn, info, err +} - return info, nil +func RecoverAccountFromMnemonic(kr sdkkeyring.Keyring, name, mnemonic string) (*sdkkeyring.Record, error) { + return kr.NewAccount(name, mnemonic, DefaultBIP39Passphrase, DefaultHDPath, hd.Secp256k1) } -// DerivePrivKeyFromMnemonic derives a private key directly from a mnemonic func DerivePrivKeyFromMnemonic(mnemonic, hdPath string) (*secp256k1.PrivKey, error) { if hdPath == "" { hdPath = DefaultHDPath } - seed := bip39.NewSeed(mnemonic, DefaultBIP39Passphrase) master, ch := hd.ComputeMastersFromSeed(seed) - - derivedKey, err := hd.DerivePrivateKeyForPath(master, ch, hdPath) + derived, err := hd.DerivePrivateKeyForPath(master, ch, hdPath) if err != nil { - return nil, fmt.Errorf("failed to derive private key: %w", err) + return nil, err } - - return &secp256k1.PrivKey{Key: derivedKey}, nil + return &secp256k1.PrivKey{Key: derived}, nil } -// SignBytes signs a byte array using a key from the keyring -func SignBytes(kr keyring.Keyring, name string, bytes []byte) ([]byte, error) { - // Get the key from the keyring - record, err := kr.Key(name) +func SignBytes(kr sdkkeyring.Keyring, name string, bz []byte) ([]byte, error) { + rec, err := kr.Key(name) if err != nil { - return nil, fmt.Errorf("failed to get key '%s' from keyring: %w", name, err) + return nil, err } - - // Get the address from the record - addr, err := record.GetAddress() + addr, err := rec.GetAddress() if err != nil { - return nil, fmt.Errorf("failed to get address from record: %w", err) + return nil, err } - // Sign the bytes - signature, _, err := kr.SignByAddress(addr, bytes, signing.SignMode_SIGN_MODE_DIRECT) - if err != nil { - return nil, fmt.Errorf("failed to sign bytes: %w", err) - } - - return signature, nil + sig, _, err := kr.SignByAddress(addr, bz, signing.SignMode_SIGN_MODE_DIRECT) + return sig, err } diff --git a/supernode/cmd/helpers.go b/supernode/cmd/helpers.go index a1bd6965..bb939586 100644 --- a/supernode/cmd/helpers.go +++ b/supernode/cmd/helpers.go @@ -12,7 +12,10 @@ import ( // initKeyringFromConfig initializes keyring using app configuration func initKeyringFromConfig(config *config.Config) (cKeyring.Keyring, error) { - return keyring.InitKeyring(config.KeyringConfig.Backend, config.GetKeyringDir()) + // Create a copy of the config with the full keyring directory path + cfg := config.KeyringConfig + cfg.Dir = config.GetKeyringDir() + return keyring.InitKeyring(cfg) } // getAddressFromKeyName extracts address from keyring using key name diff --git a/supernode/cmd/init.go b/supernode/cmd/init.go index f6bcfaa1..ecb17abf 100644 --- a/supernode/cmd/init.go +++ b/supernode/cmd/init.go @@ -32,6 +32,9 @@ var ( gatewayPortFlag int lumeraGrpcFlag string chainIDFlag string + passphrasePlain string + passphraseEnv string + passphraseFile string ) // Default configuration values @@ -47,15 +50,18 @@ const ( // InitInputs holds all user inputs for initialization type InitInputs struct { - KeyringBackend string - KeyName string - ShouldRecover bool - Mnemonic string - SupernodeAddr string - SupernodePort int - GatewayPort int - LumeraGRPC string - ChainID string + KeyringBackend string + PassphrasePlain string + PassphraseEnv string + PassphraseFile string + KeyName string + ShouldRecover bool + Mnemonic string + SupernodeAddr string + SupernodePort int + GatewayPort int + LumeraGRPC string + ChainID string } // initCmd represents the init command @@ -108,7 +114,8 @@ Example: } // Create and setup configuration - if err := createAndSetupConfig(inputs.KeyName, inputs.ChainID, inputs.KeyringBackend); err != nil { + if err := createAndSetupConfig(inputs.KeyName, inputs.ChainID, inputs.KeyringBackend, + inputs.PassphrasePlain, inputs.PassphraseEnv, inputs.PassphraseFile); err != nil { return err } @@ -172,8 +179,26 @@ func setupBaseDirectory() error { return nil } +func passphraseFlagCount() int { + c := 0 + if passphrasePlain != "" { + c++ + } + if passphraseEnv != "" { + c++ + } + if passphraseFile != "" { + c++ + } + return c +} + // gatherUserInputs collects all user inputs through interactive prompts or uses defaults/flags func gatherUserInputs() (InitInputs, error) { + if count := passphraseFlagCount(); count > 1 { + return InitInputs{}, fmt.Errorf("specify only one of --keyring-passphrase, --keyring-passphrase-env, or --keyring-passphrase-file") + } + // Check if all required parameters are provided via flags allFlagsProvided := keyNameFlag != "" && (supernodeAddrFlag != "" && supernodePortFlag != 0 && lumeraGrpcFlag != "" && chainIDFlag != "") && @@ -264,15 +289,18 @@ func gatherUserInputs() (InitInputs, error) { } return InitInputs{ - KeyringBackend: backend, - KeyName: keyName, - ShouldRecover: shouldRecoverFlag, - Mnemonic: mnemonicFlag, - SupernodeAddr: supernodeAddr, - SupernodePort: supernodePort, - GatewayPort: gatewayPort, - LumeraGRPC: lumeraGRPC, - ChainID: chainID, + KeyringBackend: backend, + PassphrasePlain: passphrasePlain, + PassphraseEnv: passphraseEnv, + PassphraseFile: passphraseFile, + KeyName: keyName, + ShouldRecover: shouldRecoverFlag, + Mnemonic: mnemonicFlag, + SupernodeAddr: supernodeAddr, + SupernodePort: supernodePort, + GatewayPort: gatewayPort, + LumeraGRPC: lumeraGRPC, + ChainID: chainID, }, nil } @@ -285,6 +313,32 @@ func gatherUserInputs() (InitInputs, error) { return InitInputs{}, fmt.Errorf("failed to select keyring backend: %w", err) } + backend := strings.ToLower(inputs.KeyringBackend) + switch backend { + case "file", "os": + switch passphraseFlagCount() { + case 0: + prompt := &survey.Password{ + Message: "Enter keyring passphrase:", + Help: "Required for 'file' or 'os' keyring back-ends – Ctrl-C to abort.", + } + if err = survey.AskOne(prompt, &inputs.PassphrasePlain, survey.WithValidator(survey.Required)); err != nil { + return InitInputs{}, fmt.Errorf("failed to get keyring passphrase: %w", err) + } + case 1: + inputs.PassphrasePlain = passphrasePlain + inputs.PassphraseEnv = passphraseEnv + inputs.PassphraseFile = passphraseFile + default: + return InitInputs{}, fmt.Errorf("specify only one of --keyring-passphrase, --keyring-passphrase-env, or --keyring-passphrase-file") + } + + default: + inputs.PassphrasePlain = passphrasePlain + inputs.PassphraseEnv = passphraseEnv + inputs.PassphraseFile = passphraseFile + } + inputs.KeyName, inputs.ShouldRecover, inputs.Mnemonic, err = promptKeyManagement(keyNameFlag, shouldRecoverFlag, mnemonicFlag) if err != nil { return InitInputs{}, fmt.Errorf("failed to configure key management: %w", err) @@ -300,14 +354,14 @@ func gatherUserInputs() (InitInputs, error) { } // createAndSetupConfig creates default configuration and necessary directories -func createAndSetupConfig(keyName, chainID, keyringBackend string) error { +func createAndSetupConfig(keyName, chainID, keyringBackend, passPlain, passEnv, passFile string) error { // Set config file path cfgFile := filepath.Join(baseDir, DefaultConfigFile) fmt.Printf("Using config file: %s\n", cfgFile) // Create default configuration - appConfig = config.CreateDefaultConfig(keyName, "", chainID, keyringBackend, "") + appConfig = config.CreateDefaultConfig(keyName, "", chainID, keyringBackend, "", passPlain, passEnv, passFile) appConfig.BaseDir = baseDir // Create directories @@ -511,7 +565,7 @@ func promptNetworkConfig(passedAddrs string, passedPort int, passedGatewayPort i } else { port = fmt.Sprintf("%d", DefaultSupernodePort) } - + var gPort string if passedGatewayPort != 0 { gPort = fmt.Sprintf("%d", passedGatewayPort) @@ -561,7 +615,7 @@ func promptNetworkConfig(passedAddrs string, passedPort int, passedGatewayPort i if err != nil || supernodePort < 1 || supernodePort > 65535 { return "", 0, 0, "", "", fmt.Errorf("invalid supernode port: %s", portStr) } - + // Gateway port var gatewayPortStr string gatewayPortPrompt := &survey.Input{ @@ -573,7 +627,7 @@ func promptNetworkConfig(passedAddrs string, passedPort int, passedGatewayPort i if err != nil { return "", 0, 0, "", "", err } - + gatewayPort, err = strconv.Atoi(gatewayPortStr) if err != nil || gatewayPort < 1 || gatewayPort > 65535 { return "", 0, 0, "", "", fmt.Errorf("invalid gateway port: %s", gatewayPortStr) @@ -715,4 +769,7 @@ func init() { initCmd.Flags().IntVar(&gatewayPortFlag, "gateway-port", 0, "Port for the HTTP gateway to listen on") initCmd.Flags().StringVar(&lumeraGrpcFlag, "lumera-grpc", "", "GRPC address of the Lumera node (host:port)") initCmd.Flags().StringVar(&chainIDFlag, "chain-id", "", "Chain ID of the Lumera network") + initCmd.Flags().StringVar(&passphrasePlain, "keyring-passphrase", "", "Keyring passphrase for non-interactive mode") + initCmd.Flags().StringVar(&passphraseEnv, "keyring-passphrase-env", "", "Environment variable containing keyring passphrase") + initCmd.Flags().StringVar(&passphraseFile, "keyring-passphrase-file", "", "File containing keyring passphrase") } diff --git a/supernode/cmd/start.go b/supernode/cmd/start.go index 6dd141f8..7a991a40 100644 --- a/supernode/cmd/start.go +++ b/supernode/cmd/start.go @@ -12,7 +12,6 @@ import ( "github.com/LumeraProtocol/supernode/p2p/kademlia/store/cloud.go" "github.com/LumeraProtocol/supernode/p2p/kademlia/store/sqlite" "github.com/LumeraProtocol/supernode/pkg/codec" - "github.com/LumeraProtocol/supernode/pkg/keyring" "github.com/LumeraProtocol/supernode/pkg/logtrace" "github.com/LumeraProtocol/supernode/pkg/lumera" "github.com/LumeraProtocol/supernode/pkg/storage/rqstore" @@ -56,7 +55,7 @@ The supernode will connect to the Lumera network and begin participating in the logtrace.Info(ctx, "Starting supernode with configuration", logtrace.Fields{"config_file": cfgFile, "keyring_dir": appConfig.GetKeyringDir(), "key_name": appConfig.SupernodeConfig.KeyName}) // Initialize keyring - kr, err := keyring.InitKeyring(appConfig.KeyringConfig.Backend, appConfig.GetKeyringDir()) + kr, err := initKeyringFromConfig(appConfig) if err != nil { logtrace.Fatal(ctx, "Failed to initialize keyring", logtrace.Fields{"error": err.Error()}) } diff --git a/supernode/cmd/supernode.go b/supernode/cmd/supernode.go index 44c8b828..8693d4fb 100644 --- a/supernode/cmd/supernode.go +++ b/supernode/cmd/supernode.go @@ -15,13 +15,12 @@ import ( // Supernode represents a supernode in the Lumera network type Supernode struct { - config *config.Config - lumeraClient lumera.Client - p2pService p2p.P2P - keyring keyring.Keyring - rqStore rqstore.Store - keyName string // String that represents the supernode account in keyring - accountAddress string // String that represents the supernode account address lemera12Xxxxx + config *config.Config + lumeraClient lumera.Client + p2pService p2p.P2P + keyring keyring.Keyring + rqStore rqstore.Store + keyName string // String that represents the supernode account in keyring } // NewSupernode creates a new supernode instance diff --git a/supernode/config/config.go b/supernode/config/config.go index 9b16c716..1f236df7 100644 --- a/supernode/config/config.go +++ b/supernode/config/config.go @@ -19,8 +19,11 @@ type SupernodeConfig struct { } type KeyringConfig struct { - Backend string `yaml:"backend"` - Dir string `yaml:"dir"` + Backend string `yaml:"backend"` + Dir string `yaml:"dir"` + PassPlain string `yaml:"passphrase_plain"` + PassEnv string `yaml:"passphrase_env"` + PassFile string `yaml:"passphrase_file"` } type P2PConfig struct { diff --git a/supernode/config/save.go b/supernode/config/save.go index 0e537aab..e821f458 100644 --- a/supernode/config/save.go +++ b/supernode/config/save.go @@ -31,7 +31,7 @@ func SaveConfig(config *Config, filename string) error { } // CreateDefaultConfig creates a default configuration with the specified values -func CreateDefaultConfig(keyName, identity, chainID string, keyringBackend, keyringDir string) *Config { +func CreateDefaultConfig(keyName, identity, chainID string, keyringBackend, keyringDir string, passPlain, passEnv, passFile string) *Config { // Set default values if keyringBackend == "" { keyringBackend = "test" @@ -49,8 +49,11 @@ func CreateDefaultConfig(keyName, identity, chainID string, keyringBackend, keyr GatewayPort: 8002, }, KeyringConfig: KeyringConfig{ - Backend: keyringBackend, - Dir: keyringDir, + Backend: keyringBackend, + Dir: keyringDir, + PassPlain: passPlain, + PassEnv: passEnv, + PassFile: passFile, }, P2PConfig: P2PConfig{ ListenAddress: "0.0.0.0", diff --git a/tests/system/e2e_cascade_test.go b/tests/system/e2e_cascade_test.go index 130610b7..7b94f995 100644 --- a/tests/system/e2e_cascade_test.go +++ b/tests/system/e2e_cascade_test.go @@ -17,6 +17,7 @@ import ( "github.com/LumeraProtocol/supernode/pkg/codec" "github.com/LumeraProtocol/supernode/pkg/keyring" "github.com/LumeraProtocol/supernode/pkg/lumera" + "github.com/LumeraProtocol/supernode/supernode/config" "github.com/LumeraProtocol/supernode/sdk/action" "github.com/LumeraProtocol/supernode/sdk/event" @@ -201,7 +202,10 @@ func TestCascadeE2E(t *testing.T) { // Create an in-memory keyring for cryptographic operations // This keyring is separate from the blockchain keyring and used for local signing - keplrKeyring, err := keyring.InitKeyring("memory", "") + keplrKeyring, err := keyring.InitKeyring(config.KeyringConfig{ + Backend: "memory", + Dir: "", + }) require.NoError(t, err, "Failed to initialize in-memory keyring") // Add the test key to the in-memory keyring