diff --git a/CHANGELOG.md b/CHANGELOG.md index 14d21cc..7a38801 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Improvements +* [#102](https://github.com/babylonlabs-io/finality-provider/pull/102) Improve `eotsd keys add` command * [#104](https://github.com/babylonlabs-io/finality-provider/pull/104) Print fpd binary version ### Bug Fixes @@ -54,6 +55,7 @@ block tip * [#101](https://github.com/babylonlabs-io/finality-provider/pull/101) Add finality activation height check in finality voting and commit pub rand start height and bump Babylon version to v0.14.0 +>>>>>>> main ## v0.8.0 diff --git a/eotsmanager/cmd/eotsd/daemon/flags.go b/eotsmanager/cmd/eotsd/daemon/flags.go deleted file mode 100644 index 42ec890..0000000 --- a/eotsmanager/cmd/eotsd/daemon/flags.go +++ /dev/null @@ -1,22 +0,0 @@ -package daemon - -import "github.com/cosmos/cosmos-sdk/crypto/keyring" - -const ( - homeFlag = "home" - forceFlag = "force" - rpcListenerFlag = "rpc-listener" - eotsPkFlag = "eots-pk" - signatureFlag = "signature" - - // flags for keys - keyNameFlag = "key-name" - passphraseFlag = "passphrase" - hdPathFlag = "hd-path" - keyringBackendFlag = "keyring-backend" - recoverFlag = "recover" - - defaultKeyringBackend = keyring.BackendTest - defaultHdPath = "" - defaultPassphrase = "" -) diff --git a/eotsmanager/cmd/eotsd/daemon/keys.go b/eotsmanager/cmd/eotsd/daemon/keys.go deleted file mode 100644 index 32db17e..0000000 --- a/eotsmanager/cmd/eotsd/daemon/keys.go +++ /dev/null @@ -1,167 +0,0 @@ -package daemon - -import ( - "bufio" - "encoding/json" - "errors" - "fmt" - "os" - - "github.com/cosmos/cosmos-sdk/client/input" - "github.com/cosmos/go-bip39" - "github.com/urfave/cli" - - bbntypes "github.com/babylonlabs-io/babylon/types" - "github.com/babylonlabs-io/finality-provider/eotsmanager" - "github.com/babylonlabs-io/finality-provider/eotsmanager/config" - "github.com/babylonlabs-io/finality-provider/log" -) - -type KeyOutput struct { - Name string `json:"name" yaml:"name"` - PubKeyHex string `json:"pub_key_hex" yaml:"pub_key_hex"` - Mnemonic string `json:"mnemonic,omitempty" yaml:"mnemonic"` -} - -var KeysCommands = []cli.Command{ - { - Name: "keys", - Usage: "Command sets of managing keys for interacting with BTC eots keys.", - Category: "Key management", - Subcommands: []cli.Command{ - AddKeyCmd, - }, - }, -} - -var AddKeyCmd = cli.Command{ - Name: "add", - Usage: "Add a key to the EOTS manager keyring.", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: homeFlag, - Usage: "Path to the keyring directory", - Value: config.DefaultEOTSDir, - }, - cli.StringFlag{ - Name: keyNameFlag, - Usage: "The name of the key to be created", - Required: true, - }, - cli.StringFlag{ - Name: passphraseFlag, - Usage: "The pass phrase used to encrypt the keys", - Value: defaultPassphrase, - }, - cli.StringFlag{ - Name: hdPathFlag, - Usage: "The hd path used to derive the private key", - Value: defaultHdPath, - }, - cli.StringFlag{ - Name: keyringBackendFlag, - Usage: "The backend of the keyring", - Value: defaultKeyringBackend, - }, - cli.BoolFlag{ - Name: recoverFlag, - Usage: `Will need to provide a seed phrase to recover - the existing key instead of creating`, - }, - }, - Action: addKey, -} - -func addKey(ctx *cli.Context) error { - keyName := ctx.String(keyNameFlag) - keyringBackend := ctx.String(keyringBackendFlag) - - homePath, err := getHomeFlag(ctx) - if err != nil { - return fmt.Errorf("failed to load home flag: %w", err) - } - - cfg, err := config.LoadConfig(homePath) - if err != nil { - return fmt.Errorf("failed to load config at %s: %w", homePath, err) - } - - logger, err := log.NewRootLoggerWithFile(config.LogFile(homePath), cfg.LogLevel) - if err != nil { - return fmt.Errorf("failed to load the logger") - } - - dbBackend, err := cfg.DatabaseConfig.GetDbBackend() - if err != nil { - return fmt.Errorf("failed to create db backend: %w", err) - } - defer dbBackend.Close() - - eotsManager, err := eotsmanager.NewLocalEOTSManager(homePath, keyringBackend, dbBackend, logger) - if err != nil { - return fmt.Errorf("failed to create EOTS manager: %w", err) - } - - eotsPk, mnemonic, err := createKey(ctx, eotsManager, keyName) - if err != nil { - return fmt.Errorf("failed to create key: %w", err) - } - - printRespJSONKeys( - KeyOutput{ - Name: keyName, - PubKeyHex: eotsPk.MarshalHex(), - Mnemonic: mnemonic, - }, - ) - return nil -} - -// createKey checks if recover flag is set to create a key from mnemonic or if not set, randomly creates it. -func createKey( - ctx *cli.Context, - eotsManager *eotsmanager.LocalEOTSManager, - keyName string, -) (eotsPk *bbntypes.BIP340PubKey, mnemonic string, err error) { - passphrase := ctx.String(passphraseFlag) - hdPath := ctx.String(hdPathFlag) - - mnemonic, err = getMnemonic(ctx) - if err != nil { - return nil, "", err - } - - eotsPk, err = eotsManager.CreateKeyWithMnemonic(keyName, passphrase, hdPath, mnemonic) - if err != nil { - return nil, "", err - } - return eotsPk, mnemonic, nil -} - -func getMnemonic(ctx *cli.Context) (string, error) { - if ctx.Bool(recoverFlag) { - reader := bufio.NewReader(os.Stdin) - mnemonic, err := input.GetString("Enter your mnemonic", reader) - if err != nil { - return "", fmt.Errorf("failed to read mnemonic from stdin: %w", err) - } - if !bip39.IsMnemonicValid(mnemonic) { - return "", errors.New("invalid mnemonic") - } - - return mnemonic, nil - } - - return eotsmanager.NewMnemonic() -} - -func printRespJSONKeys(resp interface{}) { - jsonBytes, err := json.MarshalIndent(resp, "", " ") - if err != nil { - fmt.Println("unable to decode response: ", err) - return - } - - fmt.Printf("New key for the BTC chain is created "+ - "(mnemonic should be kept in a safe place for recovery):\n%s\n", jsonBytes) -} diff --git a/eotsmanager/cmd/eotsd/daemon/pop.go b/eotsmanager/cmd/eotsd/daemon/pop.go deleted file mode 100644 index a000673..0000000 --- a/eotsmanager/cmd/eotsd/daemon/pop.go +++ /dev/null @@ -1,139 +0,0 @@ -package daemon - -import ( - "fmt" - - "github.com/cometbft/cometbft/crypto/tmhash" - sdk "github.com/cosmos/cosmos-sdk/types" - - bbnparams "github.com/babylonlabs-io/babylon/app/params" - bbn "github.com/babylonlabs-io/babylon/types" - btcstktypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" - "github.com/babylonlabs-io/finality-provider/eotsmanager" - "github.com/babylonlabs-io/finality-provider/eotsmanager/config" - "github.com/babylonlabs-io/finality-provider/log" - "github.com/urfave/cli" -) - -func init() { - bbnparams.SetAddressPrefixes() -} - -// PoPExport the data for exporting the PoP. -// The PubKeyHex is the public key of the finality provider EOTS key to load -// the private key and sign the AddressSiged. -type PoPExport struct { - PubKeyHex string `json:"pub_key_hex"` - PoPHex string `json:"pop_hex"` - BabylonAddress string `json:"babylon_address"` -} - -var ExportPoPCommand = cli.Command{ - Name: "pop-export", - Usage: "Exports the Proof of Possession by signing over the finality provider's Babylon address with the EOTS private key.", - UsageText: "pop-export [bbn-address]", - Description: `Parse the address received as argument, hash it with - sha256 and sign based on the EOTS key associated with the key-name or eots-pk flag. - If the both flags are supplied, eots-pk takes priority. Use the generated signature - to build a Proof of Possession and export it.`, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: homeFlag, - Usage: "Path to the keyring directory", - Value: config.DefaultEOTSDir, - }, - cli.StringFlag{ - Name: keyNameFlag, - Usage: "The name of the key to load private key for signing", - }, - cli.StringFlag{ - Name: eotsPkFlag, - Usage: "The public key of the finality-provider to load private key for signing", - }, - cli.StringFlag{ - Name: passphraseFlag, - Usage: "The passphrase used to decrypt the keyring", - Value: defaultPassphrase, - }, - cli.StringFlag{ - Name: keyringBackendFlag, - Usage: "The backend of the keyring", - Value: defaultKeyringBackend, - }, - }, - Action: ExportPoP, -} - -func ExportPoP(ctx *cli.Context) error { - keyName := ctx.String(keyNameFlag) - fpPkStr := ctx.String(eotsPkFlag) - passphrase := ctx.String(passphraseFlag) - keyringBackend := ctx.String(keyringBackendFlag) - - args := ctx.Args() - bbnAddressStr := args.First() - bbnAddr, err := sdk.AccAddressFromBech32(bbnAddressStr) - if err != nil { - return fmt.Errorf("invalid argument %s, please provide a valid bbn address as argument, err: %w", bbnAddressStr, err) - } - - if len(fpPkStr) == 0 && len(keyName) == 0 { - return fmt.Errorf("at least one of the flags: %s, %s needs to be informed", keyNameFlag, eotsPkFlag) - } - - homePath, err := getHomeFlag(ctx) - if err != nil { - return fmt.Errorf("failed to load home flag: %w", err) - } - - cfg, err := config.LoadConfig(homePath) - if err != nil { - return fmt.Errorf("failed to load config at %s: %w", homePath, err) - } - - logger, err := log.NewRootLoggerWithFile(config.LogFile(homePath), cfg.LogLevel) - if err != nil { - return fmt.Errorf("failed to load the logger") - } - - dbBackend, err := cfg.DatabaseConfig.GetDbBackend() - if err != nil { - return fmt.Errorf("failed to create db backend: %w", err) - } - defer dbBackend.Close() - - eotsManager, err := eotsmanager.NewLocalEOTSManager(homePath, keyringBackend, dbBackend, logger) - if err != nil { - return fmt.Errorf("failed to create EOTS manager: %w", err) - } - - hashOfMsgToSign := tmhash.Sum(bbnAddr.Bytes()) - btcSig, pubKey, err := singMsg(eotsManager, keyName, fpPkStr, passphrase, hashOfMsgToSign) - if err != nil { - return fmt.Errorf("failed to sign address %s: %w", bbnAddr.String(), err) - } - - bip340Sig := bbn.NewBIP340SignatureFromBTCSig(btcSig) - btcSigBz, err := bip340Sig.Marshal() - if err != nil { - return fmt.Errorf("failed to marshal BTC Sig: %w", err) - } - - pop := btcstktypes.ProofOfPossessionBTC{ - BtcSigType: btcstktypes.BTCSigType_BIP340, - BtcSig: btcSigBz, - } - - popHex, err := pop.ToHexStr() - if err != nil { - return fmt.Errorf("failed to marshal pop to hex: %w", err) - } - - printRespJSON(PoPExport{ - PubKeyHex: pubKey.MarshalHex(), - PoPHex: popHex, - BabylonAddress: bbnAddr.String(), - }) - - return nil -} diff --git a/eotsmanager/cmd/eotsd/daemon/pop_test.go b/eotsmanager/cmd/eotsd/daemon/pop_test.go deleted file mode 100644 index 57bf90b..0000000 --- a/eotsmanager/cmd/eotsd/daemon/pop_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package daemon_test - -import ( - "encoding/json" - "fmt" - "math/rand" - "path/filepath" - "testing" - - "github.com/babylonlabs-io/babylon/testutil/datagen" - bbn "github.com/babylonlabs-io/babylon/types" - btcstktypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" - dcli "github.com/babylonlabs-io/finality-provider/eotsmanager/cmd/eotsd/daemon" - "github.com/babylonlabs-io/finality-provider/testutil" - "github.com/btcsuite/btcd/chaincfg" - "github.com/stretchr/testify/require" - "github.com/urfave/cli" -) - -func FuzzPoPExport(f *testing.F) { - testutil.AddRandomSeedsToFuzzer(f, 10) - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - - tempDir := t.TempDir() - homeDir := filepath.Join(tempDir, "eots-home") - app := testApp() - - // init config in home folder - hFlag := fmt.Sprintf("--home=%s", homeDir) - err := app.Run([]string{"eotsd", "init", hFlag}) - require.NoError(t, err) - - keyName := testutil.GenRandomHexStr(r, 10) - keyNameFlag := fmt.Sprintf("--key-name=%s", keyName) - - outputKeysAdd := appRunWithOutput(r, t, app, []string{"eotsd", "keys", "add", hFlag, keyNameFlag}) - keyOutJson := searchInTxt(outputKeysAdd, "for recovery):") - - var keyOut dcli.KeyOutput - err = json.Unmarshal([]byte(keyOutJson), &keyOut) - require.NoError(t, err) - - bbnAddr := datagen.GenRandomAccount().GetAddress() - - eotsBtcPkFlag := fmt.Sprintf("--eots-pk=%s", keyOut.PubKeyHex) - exportedPoP := appRunPoPExport(r, t, app, []string{bbnAddr.String(), hFlag, eotsBtcPkFlag}) - pop, err := btcstktypes.NewPoPBTCFromHex(exportedPoP.PoPHex) - require.NoError(t, err) - - require.NotNil(t, exportedPoP) - require.NoError(t, pop.ValidateBasic()) - - btcPubKey, err := bbn.NewBIP340PubKeyFromHex(exportedPoP.PubKeyHex) - require.NoError(t, err) - require.NoError(t, pop.Verify(bbnAddr, btcPubKey, &chaincfg.SigNetParams)) - }) -} - -func appRunPoPExport(r *rand.Rand, t *testing.T, app *cli.App, arguments []string) dcli.PoPExport { - args := []string{"eotsd", "pop-export"} - args = append(args, arguments...) - outputSign := appRunWithOutput(r, t, app, args) - signatureStr := searchInTxt(outputSign, "") - - var dataSigned dcli.PoPExport - err := json.Unmarshal([]byte(signatureStr), &dataSigned) - require.NoError(t, err) - - return dataSigned -} diff --git a/eotsmanager/cmd/eotsd/daemon/sign.go b/eotsmanager/cmd/eotsd/daemon/sign.go deleted file mode 100644 index c2404c7..0000000 --- a/eotsmanager/cmd/eotsd/daemon/sign.go +++ /dev/null @@ -1,231 +0,0 @@ -package daemon - -import ( - "crypto/sha256" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "io" - "os" - - bbntypes "github.com/babylonlabs-io/babylon/types" - "github.com/babylonlabs-io/finality-provider/eotsmanager" - "github.com/babylonlabs-io/finality-provider/eotsmanager/config" - "github.com/babylonlabs-io/finality-provider/log" - "github.com/btcsuite/btcd/btcec/v2/schnorr" - "github.com/urfave/cli" -) - -type DataSigned struct { - KeyName string `json:"key_name"` - PubKeyHex string `json:"pub_key_hex"` - SignedDataHashHex string `json:"signed_data_hash_hex"` - SchnorrSignatureHex string `json:"schnorr_signature_hex"` -} - -var SignSchnorrSig = cli.Command{ - Name: "sign-schnorr", - Usage: "Signs a Schnorr signature over arbitrary data with the EOTS private key.", - UsageText: "sign-schnorr [file-path]", - Description: fmt.Sprintf(`Read the file received as argument, hash it with - sha256 and sign based on the Schnorr key associated with the %s or %s flag. - If the both flags are supplied, %s takes priority`, keyNameFlag, eotsPkFlag, eotsPkFlag), - Flags: []cli.Flag{ - cli.StringFlag{ - Name: homeFlag, - Usage: "Path to the keyring directory", - Value: config.DefaultEOTSDir, - }, - cli.StringFlag{ - Name: keyNameFlag, - Usage: "The name of the key to load private key for signing", - }, - cli.StringFlag{ - Name: eotsPkFlag, - Usage: "The public key of the finality-provider to load private key for signing", - }, - cli.StringFlag{ - Name: passphraseFlag, - Usage: "The passphrase used to decrypt the keyring", - Value: defaultPassphrase, - }, - cli.StringFlag{ - Name: keyringBackendFlag, - Usage: "The backend of the keyring", - Value: defaultKeyringBackend, - }, - }, - Action: SignSchnorr, -} - -var VerifySchnorrSig = cli.Command{ - Name: "verify-schnorr-sig", - Usage: "Verify a Schnorr signature over arbitrary data with the given public key.", - UsageText: "verify-schnorr-sig [file-path]", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: eotsPkFlag, - Usage: "The EOTS public key that will be used to verify the signature", - Required: true, - }, - cli.StringFlag{ - Name: signatureFlag, - Usage: "The hex signature to verify", - Required: true, - }, - }, - Action: SignSchnorrVerify, -} - -func SignSchnorrVerify(ctx *cli.Context) error { - fpPkStr := ctx.String(eotsPkFlag) - signatureHex := ctx.String(signatureFlag) - - args := ctx.Args() - inputFilePath := args.First() - if len(inputFilePath) == 0 { - return errors.New("invalid argument, please provide a valid file path as input argument") - } - - hashOfMsgToSign, err := hashFromFile(inputFilePath) - if err != nil { - return fmt.Errorf("failed to generate hash from file %s: %w", inputFilePath, err) - } - - fpPk, err := bbntypes.NewBIP340PubKeyFromHex(fpPkStr) - if err != nil { - return fmt.Errorf("invalid finality-provider public key %s: %w", fpPkStr, err) - } - - pubKey, err := schnorr.ParsePubKey(*fpPk) - if err != nil { - return fmt.Errorf("unable to parse public key %s: %w", fpPkStr, err) - } - - signatureBz, err := hex.DecodeString(signatureHex) - if err != nil { - return fmt.Errorf("unable to decode signature %s: %w", signatureHex, err) - } - - signature, err := schnorr.ParseSignature(signatureBz) - if err != nil { - return fmt.Errorf("unable to parse schnorr signature %s: %w", signatureBz, err) - } - - if !signature.Verify(hashOfMsgToSign, pubKey) { - return errors.New("invalid signature") - } - - fmt.Print("Verification is successful!") - return nil -} - -func SignSchnorr(ctx *cli.Context) error { - keyName := ctx.String(keyNameFlag) - fpPkStr := ctx.String(eotsPkFlag) - passphrase := ctx.String(passphraseFlag) - keyringBackend := ctx.String(keyringBackendFlag) - - args := ctx.Args() - inputFilePath := args.First() - if len(inputFilePath) == 0 { - return errors.New("invalid argument, please provide a valid file path as input argument") - } - - if len(fpPkStr) == 0 && len(keyName) == 0 { - return fmt.Errorf("at least one of the flags: %s, %s needs to be informed", keyNameFlag, eotsPkFlag) - } - - homePath, err := getHomeFlag(ctx) - if err != nil { - return fmt.Errorf("failed to load home flag: %w", err) - } - - cfg, err := config.LoadConfig(homePath) - if err != nil { - return fmt.Errorf("failed to load config at %s: %w", homePath, err) - } - - logger, err := log.NewRootLoggerWithFile(config.LogFile(homePath), cfg.LogLevel) - if err != nil { - return fmt.Errorf("failed to load the logger") - } - - dbBackend, err := cfg.DatabaseConfig.GetDbBackend() - if err != nil { - return fmt.Errorf("failed to create db backend: %w", err) - } - defer dbBackend.Close() - - eotsManager, err := eotsmanager.NewLocalEOTSManager(homePath, keyringBackend, dbBackend, logger) - if err != nil { - return fmt.Errorf("failed to create EOTS manager: %w", err) - } - - hashOfMsgToSign, err := hashFromFile(inputFilePath) - if err != nil { - return fmt.Errorf("failed to generate hash from file %s: %w", inputFilePath, err) - } - - signature, pubKey, err := singMsg(eotsManager, keyName, fpPkStr, passphrase, hashOfMsgToSign) - if err != nil { - return fmt.Errorf("failed to sign msg: %w", err) - } - - printRespJSON(DataSigned{ - KeyName: keyName, - PubKeyHex: pubKey.MarshalHex(), - SignedDataHashHex: hex.EncodeToString(hashOfMsgToSign), - SchnorrSignatureHex: hex.EncodeToString(signature.Serialize()), - }) - - return nil -} - -func hashFromFile(inputFilePath string) ([]byte, error) { - h := sha256.New() - - // #nosec G304 - The log file path is provided by the user and not externally - f, err := os.Open(inputFilePath) - if err != nil { - return nil, fmt.Errorf("failed to open the file %s: %w", inputFilePath, err) - } - defer f.Close() - - if _, err := io.Copy(h, f); err != nil { - return nil, err - } - - return h.Sum(nil), nil -} - -func singMsg( - eotsManager *eotsmanager.LocalEOTSManager, - keyName, fpPkStr, passphrase string, - hashOfMsgToSign []byte, -) (*schnorr.Signature, *bbntypes.BIP340PubKey, error) { - if len(fpPkStr) > 0 { - fpPk, err := bbntypes.NewBIP340PubKeyFromHex(fpPkStr) - if err != nil { - return nil, nil, fmt.Errorf("invalid finality-provider public key %s: %w", fpPkStr, err) - } - signature, err := eotsManager.SignSchnorrSig(*fpPk, hashOfMsgToSign, passphrase) - if err != nil { - return nil, nil, fmt.Errorf("unable to sign msg with pk %s: %w", fpPkStr, err) - } - return signature, fpPk, nil - } - - return eotsManager.SignSchnorrSigFromKeyname(keyName, passphrase, hashOfMsgToSign) -} - -func printRespJSON(resp interface{}) { - jsonBytes, err := json.MarshalIndent(resp, "", " ") - if err != nil { - fmt.Println("unable to decode response: ", err) - return - } - - fmt.Printf("%s\n", jsonBytes) -} diff --git a/eotsmanager/cmd/eotsd/daemon/sign_test.go b/eotsmanager/cmd/eotsd/daemon/sign_test.go deleted file mode 100644 index 48a85f9..0000000 --- a/eotsmanager/cmd/eotsd/daemon/sign_test.go +++ /dev/null @@ -1,155 +0,0 @@ -package daemon_test - -import ( - "bytes" - "encoding/json" - "fmt" - "math/rand" - "os" - "path/filepath" - "strings" - "testing" - - sdkmath "cosmossdk.io/math" - dcli "github.com/babylonlabs-io/finality-provider/eotsmanager/cmd/eotsd/daemon" - "github.com/babylonlabs-io/finality-provider/testutil" - "github.com/stretchr/testify/require" - "github.com/urfave/cli" - - stktypes "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -type FpInfo struct { - Description stktypes.Description `json:"description"` - BtcPk string `json:"btc_pk"` - Commision sdkmath.LegacyDec `json:"commission"` -} - -func FuzzSignAndVerifySchnorrSig(f *testing.F) { - testutil.AddRandomSeedsToFuzzer(f, 10) - f.Fuzz(func(t *testing.T, seed int64) { - r := rand.New(rand.NewSource(seed)) - - tempDir := t.TempDir() - homeDir := filepath.Join(tempDir, "eots-home") - app := testApp() - - // init config in home folder - hFlag := fmt.Sprintf("--home=%s", homeDir) - err := app.Run([]string{"eotsd", "init", hFlag}) - require.NoError(t, err) - - keyName := testutil.GenRandomHexStr(r, 10) - keyNameFlag := fmt.Sprintf("--key-name=%s", keyName) - - outputKeysAdd := appRunWithOutput(r, t, app, []string{"eotsd", "keys", "add", hFlag, keyNameFlag}) - keyOutJson := searchInTxt(outputKeysAdd, "for recovery):") - - var keyOut dcli.KeyOutput - err = json.Unmarshal([]byte(keyOutJson), &keyOut) - require.NoError(t, err) - - fpInfoPath := filepath.Join(tempDir, "fpInfo.json") - writeFpInfoToFile(r, t, fpInfoPath, keyOut.PubKeyHex) - - eotsBtcPkFlag := fmt.Sprintf("--eots-pk=%s", keyOut.PubKeyHex) - dataSignedBtcPk := appRunSignSchnorr(r, t, app, []string{fpInfoPath, hFlag, eotsBtcPkFlag}) - err = app.Run([]string{"eotsd", "verify-schnorr-sig", fpInfoPath, eotsBtcPkFlag, fmt.Sprintf("--signature=%s", dataSignedBtcPk.SchnorrSignatureHex)}) - require.NoError(t, err) - - dataSignedKeyName := appRunSignSchnorr(r, t, app, []string{fpInfoPath, hFlag, keyNameFlag}) - err = app.Run([]string{"eotsd", "verify-schnorr-sig", fpInfoPath, eotsBtcPkFlag, fmt.Sprintf("--signature=%s", dataSignedKeyName.SchnorrSignatureHex)}) - require.NoError(t, err) - - // check if both generated signatures match - require.Equal(t, dataSignedBtcPk.PubKeyHex, dataSignedKeyName.PubKeyHex) - require.Equal(t, dataSignedBtcPk.SchnorrSignatureHex, dataSignedKeyName.SchnorrSignatureHex) - require.Equal(t, dataSignedBtcPk.SignedDataHashHex, dataSignedKeyName.SignedDataHashHex) - - // sign with both keys and eots-pk, should give eots-pk preference - dataSignedBoth := appRunSignSchnorr(r, t, app, []string{fpInfoPath, hFlag, eotsBtcPkFlag, keyNameFlag}) - require.Equal(t, dataSignedBoth, dataSignedKeyName) - - // the keyname can even be from a invalid keyname, since it gives eots-pk preference - badKeyname := "badKeyName" - dataSignedBothBadKeyName := appRunSignSchnorr(r, t, app, []string{fpInfoPath, hFlag, eotsBtcPkFlag, fmt.Sprintf("--key-name=%s", badKeyname)}) - require.Equal(t, badKeyname, dataSignedBothBadKeyName.KeyName) - require.Equal(t, dataSignedBtcPk.PubKeyHex, dataSignedBothBadKeyName.PubKeyHex) - require.Equal(t, dataSignedBtcPk.SchnorrSignatureHex, dataSignedBothBadKeyName.SchnorrSignatureHex) - require.Equal(t, dataSignedBtcPk.SignedDataHashHex, dataSignedBothBadKeyName.SignedDataHashHex) - }) -} - -func appRunSignSchnorr(r *rand.Rand, t *testing.T, app *cli.App, arguments []string) dcli.DataSigned { - args := []string{"eotsd", "sign-schnorr"} - args = append(args, arguments...) - outputSign := appRunWithOutput(r, t, app, args) - signatureStr := searchInTxt(outputSign, "") - - var dataSigned dcli.DataSigned - err := json.Unmarshal([]byte(signatureStr), &dataSigned) - require.NoError(t, err) - - return dataSigned -} - -func appRunWithOutput(r *rand.Rand, t *testing.T, app *cli.App, arguments []string) (output string) { - outPut := filepath.Join(t.TempDir(), fmt.Sprintf("%s-out.txt", testutil.GenRandomHexStr(r, 10))) - outPutFile, err := os.Create(outPut) - require.NoError(t, err) - defer outPutFile.Close() - - // set file to stdout to read. - oldStd := os.Stdout - os.Stdout = outPutFile - - err = app.Run(arguments) - require.NoError(t, err) - - // set to old stdout - os.Stdout = oldStd - return readFromFile(t, outPutFile) -} - -func searchInTxt(text, search string) string { - idxOfRecovery := strings.Index(text, search) - jsonKeyOutputOut := text[idxOfRecovery+len(search):] - return strings.ReplaceAll(jsonKeyOutputOut, "\n", "") -} - -func readFromFile(t *testing.T, f *os.File) string { - _, err := f.Seek(0, 0) - require.NoError(t, err) - - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(f) - require.NoError(t, err) - return buf.String() -} - -func writeFpInfoToFile(r *rand.Rand, t *testing.T, fpInfoPath, btcPk string) { - desc := testutil.RandomDescription(r) - fpInfo := FpInfo{ - BtcPk: btcPk, - Commision: sdkmath.LegacyMustNewDecFromStr("0.5"), - Description: *desc, - } - - bzFpInfo, err := json.Marshal(fpInfo) - require.NoError(t, err) - - fpInfoFile, err := os.Create(fpInfoPath) - require.NoError(t, err) - - _, err = fpInfoFile.Write(bzFpInfo) - require.NoError(t, err) - fpInfoFile.Close() -} - -func testApp() *cli.App { - app := cli.NewApp() - app.Name = "eotsd" - app.Commands = append(app.Commands, dcli.StartCommand, dcli.InitCommand, dcli.SignSchnorrSig, dcli.VerifySchnorrSig, dcli.ExportPoPCommand) - app.Commands = append(app.Commands, dcli.KeysCommands...) - return app -} diff --git a/eotsmanager/cmd/eotsd/flags.go b/eotsmanager/cmd/eotsd/flags.go new file mode 100644 index 0000000..4046f82 --- /dev/null +++ b/eotsmanager/cmd/eotsd/flags.go @@ -0,0 +1,6 @@ +package main + +const ( + forceFlag = "force" + rpcListenerFlag = "rpc-listener" +) diff --git a/eotsmanager/cmd/eotsd/daemon/init.go b/eotsmanager/cmd/eotsd/init.go similarity index 59% rename from eotsmanager/cmd/eotsd/daemon/init.go rename to eotsmanager/cmd/eotsd/init.go index b2f33dc..79b30e2 100644 --- a/eotsmanager/cmd/eotsd/daemon/init.go +++ b/eotsmanager/cmd/eotsd/init.go @@ -1,42 +1,36 @@ -package daemon +package main import ( "fmt" - "path/filepath" "github.com/jessevdk/go-flags" - "github.com/urfave/cli" + "github.com/spf13/cobra" eotscfg "github.com/babylonlabs-io/finality-provider/eotsmanager/config" "github.com/babylonlabs-io/finality-provider/util" ) -var InitCommand = cli.Command{ - Name: "init", - Usage: "Initialize the eotsd home directory.", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: homeFlag, - Usage: "Path to where the home directory will be initialized", - Value: eotscfg.DefaultEOTSDir, - }, - cli.BoolFlag{ - Name: forceFlag, - Usage: "Override existing configuration", - Required: false, - }, - }, - Action: initHome, +func NewInitCmd() *cobra.Command { + initCmd := &cobra.Command{ + Use: "init ", + Short: "Initialize the eotsd home directory.", + RunE: initHome, + } + + initCmd.Flags().Bool(forceFlag, false, "Override existing configuration") + + return initCmd } -func initHome(c *cli.Context) error { - homePath, err := filepath.Abs(c.String(homeFlag)) +func initHome(cmd *cobra.Command, _ []string) error { + homePath, err := getHomePath(cmd) + if err != nil { + return err + } + force, err := cmd.Flags().GetBool(forceFlag) if err != nil { return err } - // Create home directory - homePath = util.CleanAndExpandPath(homePath) - force := c.Bool(forceFlag) if util.FileExists(homePath) && !force { return fmt.Errorf("home path %s already exists", homePath) diff --git a/eotsmanager/cmd/eotsd/keys.go b/eotsmanager/cmd/eotsd/keys.go new file mode 100644 index 0000000..a5692f3 --- /dev/null +++ b/eotsmanager/cmd/eotsd/keys.go @@ -0,0 +1,120 @@ +package main + +import ( + "bytes" + "fmt" + + "sigs.k8s.io/yaml" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/spf13/cobra" + + "github.com/babylonlabs-io/finality-provider/eotsmanager" + "github.com/babylonlabs-io/finality-provider/eotsmanager/config" + "github.com/babylonlabs-io/finality-provider/log" +) + +type KeyOutput struct { + Name string `json:"name" yaml:"name"` + PubKeyHex string `json:"pub_key_hex" yaml:"pub_key_hex"` +} + +func NewKeysCmd() *cobra.Command { + keysCmd := keys.Commands() + + // Find the "add" subcommand + addCmd := findSubCommand(keysCmd, "add") + if addCmd == nil { + panic("failed to find keys add command") + } + + // Wrap the original RunE function + originalRunE := addCmd.RunE + addCmd.RunE = func(cmd *cobra.Command, args []string) error { + // Create a buffer to intercept the key items + var buf bytes.Buffer + cmd.SetOut(&buf) + + // Run the original command + err := originalRunE(cmd, args) + if err != nil { + return err + } + + return saveKeyNameMapping(cmd, args) + } + + return keysCmd +} + +func findSubCommand(cmd *cobra.Command, name string) *cobra.Command { + for _, subCmd := range cmd.Commands() { + if subCmd.Name() == name { + return subCmd + } + } + return nil +} + +func saveKeyNameMapping(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + keyName := args[0] + + // Load configuration + cfg, err := config.LoadConfig(clientCtx.HomeDir) + if err != nil { + return fmt.Errorf("failed to load config: %w", err) + } + + // Setup logger + logger, err := log.NewRootLoggerWithFile(config.LogFile(clientCtx.HomeDir), cfg.LogLevel) + if err != nil { + return fmt.Errorf("failed to load the logger: %w", err) + } + + // Get database backend + dbBackend, err := cfg.DatabaseConfig.GetDbBackend() + if err != nil { + return fmt.Errorf("failed to create db backend: %w", err) + } + defer dbBackend.Close() + + // Create EOTS manager + eotsManager, err := eotsmanager.NewLocalEOTSManager(clientCtx.HomeDir, clientCtx.Keyring.Backend(), dbBackend, logger) + if err != nil { + return fmt.Errorf("failed to create EOTS manager: %w", err) + } + + // Get the public key for the newly added key + eotsPk, err := eotsManager.LoadBIP340PubKeyFromKeyName(keyName) + if err != nil { + return fmt.Errorf("failed to get public key for key %s: %w", keyName, err) + } + + // Save the public key to key name mapping + if err := eotsManager.SaveEOTSKeyName(eotsPk.MustToBTCPK(), keyName); err != nil { + return fmt.Errorf("failed to save key name mapping: %w", err) + } + + return printKey( + &KeyOutput{ + Name: keyName, + PubKeyHex: eotsPk.MarshalHex(), + }, + ) +} + +func printKey(keyRecord *KeyOutput) error { + out, err := yaml.Marshal(keyRecord) + if err != nil { + return err + } + + fmt.Printf("\n%s\n", out) + + return nil +} diff --git a/eotsmanager/cmd/eotsd/keys_test.go b/eotsmanager/cmd/eotsd/keys_test.go new file mode 100644 index 0000000..18646a8 --- /dev/null +++ b/eotsmanager/cmd/eotsd/keys_test.go @@ -0,0 +1,92 @@ +package main + +import ( + "bytes" + "fmt" + "math/rand" + "os" + "path/filepath" + "testing" + + sdkflags "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/spf13/cobra" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + + "github.com/babylonlabs-io/finality-provider/eotsmanager" + eotscfg "github.com/babylonlabs-io/finality-provider/eotsmanager/config" + "github.com/babylonlabs-io/finality-provider/testutil" +) + +func FuzzNewKeysCmd(f *testing.F) { + testutil.AddRandomSeedsToFuzzer(f, 10) + + f.Fuzz(func(t *testing.T, seed int64) { + r := rand.New(rand.NewSource(seed)) + + root := NewRootCmd() + + tDir := t.TempDir() + tempHome := filepath.Join(tDir, "homeeots") + homeFlagFilled := fmt.Sprintf("--%s=%s", sdkflags.FlagHome, tempHome) + rootCmdBuff := new(bytes.Buffer) + defer func() { + err := os.RemoveAll(tempHome) + require.NoError(t, err) + }() + + // Initialize the EOTS manager + _, _ = exec(t, root, rootCmdBuff, "init", homeFlagFilled) + + // Generate a random key name + keyName := testutil.GenRandomHexStr(r, 5) + + // Execute the keys add command + keyringBackendFlagFilled := fmt.Sprintf("--%s=%s", sdkflags.FlagKeyringBackend, keyring.BackendTest) + _, _ = exec(t, root, rootCmdBuff, "keys", "add", keyName, homeFlagFilled, keyringBackendFlagFilled) + + // Execute the keys list command + _, listOutput := exec(t, root, rootCmdBuff, "keys", "list", homeFlagFilled) + + // Check if the added key is in the list + require.Contains(t, listOutput, keyName) + + // Load the EOTS manager and verify the key existence + eotsCfg := eotscfg.DefaultConfigWithHomePath(tempHome) + dbBackend, err := eotsCfg.DatabaseConfig.GetDbBackend() + require.NoError(t, err) + defer func() { + err := dbBackend.Close() + require.NoError(t, err) + }() + + eotsManager, err := eotsmanager.NewLocalEOTSManager(tempHome, "test", dbBackend, zap.NewNop()) + require.NoError(t, err, "Should be able to create EOTS manager") + + pubKey, err := eotsManager.LoadBIP340PubKeyFromKeyName(keyName) + require.NoError(t, err, "Should be able to load public key") + require.NotNil(t, pubKey, "Public key should not be nil") + }) +} + +// exec executes a command based on the cmd passed, the args should only search for subcommands, not parent commands +func exec(t *testing.T, root *cobra.Command, rootCmdBuf *bytes.Buffer, args ...string) (c *cobra.Command, output string) { + buf := new(bytes.Buffer) + root.SetOut(buf) + root.SetErr(buf) + root.SetArgs(args) + + c, err := root.ExecuteC() + require.NoError(t, err) + + outStr := buf.String() + if len(outStr) > 0 { + return c, outStr + } + + _, err = buf.Write(rootCmdBuf.Bytes()) + require.NoError(t, err) + + return c, buf.String() +} diff --git a/eotsmanager/cmd/eotsd/main.go b/eotsmanager/cmd/eotsd/main.go index 285296b..7e05a38 100644 --- a/eotsmanager/cmd/eotsd/main.go +++ b/eotsmanager/cmd/eotsd/main.go @@ -3,28 +3,11 @@ package main import ( "fmt" "os" - - "github.com/urfave/cli" - - dcli "github.com/babylonlabs-io/finality-provider/eotsmanager/cmd/eotsd/daemon" ) -func fatal(err error) { - fmt.Fprintf(os.Stderr, "[eotsd] %v\n", err) - os.Exit(1) -} - func main() { - app := cli.NewApp() - app.Name = "eotsd" - app.Usage = "Extractable One Time Signature Daemon (eotsd)." - app.Commands = append( - app.Commands, dcli.StartCommand, dcli.InitCommand, dcli.SignSchnorrSig, dcli.VerifySchnorrSig, - dcli.ExportPoPCommand, - ) - app.Commands = append(app.Commands, dcli.KeysCommands...) - - if err := app.Run(os.Args); err != nil { - fatal(err) + if err := NewRootCmd().Execute(); err != nil { + fmt.Fprintf(os.Stderr, "Error while executing eotsd CLI: %s", err.Error()) + os.Exit(1) } } diff --git a/eotsmanager/cmd/eotsd/root.go b/eotsmanager/cmd/eotsd/root.go new file mode 100644 index 0000000..00d867f --- /dev/null +++ b/eotsmanager/cmd/eotsd/root.go @@ -0,0 +1,29 @@ +package main + +import ( + "github.com/cosmos/cosmos-sdk/client" + sdkflags "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/spf13/cobra" + + "github.com/babylonlabs-io/finality-provider/eotsmanager/config" +) + +// NewRootCmd creates a new root command for fpd. It is called once in the main function. +func NewRootCmd() *cobra.Command { + rootCmd := &cobra.Command{ + Use: "eotsd", + Short: "A daemon program from managing Extractable One Time Signatures (eotsd).", + SilenceErrors: false, + PersistentPreRunE: PersistClientCtx(client.Context{}), + } + + rootCmd.PersistentFlags().String(sdkflags.FlagHome, config.DefaultEOTSDir, "The application home directory") + + rootCmd.AddCommand( + NewInitCmd(), + NewKeysCmd(), + NewStartCmd(), + ) + + return rootCmd +} diff --git a/eotsmanager/cmd/eotsd/daemon/start.go b/eotsmanager/cmd/eotsd/start.go similarity index 58% rename from eotsmanager/cmd/eotsd/daemon/start.go rename to eotsmanager/cmd/eotsd/start.go index e32513e..3350a11 100644 --- a/eotsmanager/cmd/eotsd/daemon/start.go +++ b/eotsmanager/cmd/eotsd/start.go @@ -1,40 +1,35 @@ -package daemon +package main import ( "fmt" "net" - "path/filepath" + sdkflags "github.com/cosmos/cosmos-sdk/client/flags" "github.com/lightningnetwork/lnd/signal" - "github.com/urfave/cli" + "github.com/spf13/cobra" "github.com/babylonlabs-io/finality-provider/eotsmanager" "github.com/babylonlabs-io/finality-provider/eotsmanager/config" eotsservice "github.com/babylonlabs-io/finality-provider/eotsmanager/service" "github.com/babylonlabs-io/finality-provider/log" - "github.com/babylonlabs-io/finality-provider/util" ) -var StartCommand = cli.Command{ - Name: "start", - Usage: "Start the Extractable One Time Signature Daemon.", - Description: "Start the Extractable One Time Signature Daemon.", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: homeFlag, - Usage: "The path to the eotsd home directory", - Value: config.DefaultEOTSDir, - }, - cli.StringFlag{ - Name: rpcListenerFlag, - Usage: "The address that the RPC server listens to", - }, - }, - Action: startFn, +func NewStartCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "start", + Short: "Start the Extractable One Time Signature Daemon", + Long: "Start the Extractable One Time Signature Daemon and run it until shutdown.", + RunE: startFn, + } + + cmd.Flags().String(sdkflags.FlagHome, config.DefaultEOTSDir, "The path to the eotsd home directory") + cmd.Flags().String(rpcListenerFlag, "", "The address that the RPC server listens to") + + return cmd } -func startFn(ctx *cli.Context) error { - homePath, err := getHomeFlag(ctx) +func startFn(cmd *cobra.Command, args []string) error { + homePath, err := getHomePath(cmd) if err != nil { return fmt.Errorf("failed to load home flag: %w", err) } @@ -44,18 +39,21 @@ func startFn(ctx *cli.Context) error { return fmt.Errorf("failed to load config at %s: %w", homePath, err) } - rpcListener := ctx.String(rpcListenerFlag) + rpcListener, err := cmd.Flags().GetString(rpcListenerFlag) + if err != nil { + return fmt.Errorf("failed to get RPC listener flag: %w", err) + } if rpcListener != "" { _, err := net.ResolveTCPAddr("tcp", rpcListener) if err != nil { - return fmt.Errorf("invalid RPC listener address %s, %w", rpcListener, err) + return fmt.Errorf("invalid RPC listener address %s: %w", rpcListener, err) } cfg.RpcListener = rpcListener } logger, err := log.NewRootLoggerWithFile(config.LogFile(homePath), cfg.LogLevel) if err != nil { - return fmt.Errorf("failed to load the logger") + return fmt.Errorf("failed to load the logger: %w", err) } dbBackend, err := cfg.DatabaseConfig.GetDbBackend() @@ -71,18 +69,10 @@ func startFn(ctx *cli.Context) error { // Hook interceptor for os signals. shutdownInterceptor, err := signal.Intercept() if err != nil { - return err + return fmt.Errorf("failed to set up shutdown interceptor: %w", err) } eotsServer := eotsservice.NewEOTSManagerServer(cfg, logger, eotsManager, dbBackend, shutdownInterceptor) return eotsServer.RunUntilShutdown() } - -func getHomeFlag(ctx *cli.Context) (string, error) { - homePath, err := filepath.Abs(ctx.String(homeFlag)) - if err != nil { - return "", err - } - return util.CleanAndExpandPath(homePath), nil -} diff --git a/eotsmanager/cmd/eotsd/utils.go b/eotsmanager/cmd/eotsd/utils.go new file mode 100644 index 0000000..6fe50f3 --- /dev/null +++ b/eotsmanager/cmd/eotsd/utils.go @@ -0,0 +1,56 @@ +package main + +import ( + "os" + "path/filepath" + + "github.com/babylonlabs-io/babylon/app/params" + "github.com/cosmos/cosmos-sdk/client" + sdkflags "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/std" + "github.com/spf13/cobra" + + "github.com/babylonlabs-io/finality-provider/util" +) + +func getHomePath(cmd *cobra.Command) (string, error) { + rawHomePath, err := cmd.Flags().GetString(sdkflags.FlagHome) + if err != nil { + return "", err + } + + homePath, err := filepath.Abs(rawHomePath) + if err != nil { + return "", err + } + // Create home directory + homePath = util.CleanAndExpandPath(homePath) + + return homePath, nil +} + +// PersistClientCtx persist some vars from the cmd or config to the client context. +// It gives preferences to flags over the values in the config. If the flag is not set +// and exists a value in the config that could be used, it will be set in the ctx. +func PersistClientCtx(ctx client.Context) func(cmd *cobra.Command, _ []string) error { + return func(cmd *cobra.Command, _ []string) error { + encCfg := params.DefaultEncodingConfig() + std.RegisterInterfaces(encCfg.InterfaceRegistry) + + ctx = ctx. + WithCodec(encCfg.Codec). + WithInterfaceRegistry(encCfg.InterfaceRegistry). + WithTxConfig(encCfg.TxConfig). + WithLegacyAmino(encCfg.Amino). + WithInput(os.Stdin) + + // set the default command outputs + cmd.SetOut(cmd.OutOrStdout()) + cmd.SetErr(cmd.ErrOrStderr()) + + ctx = ctx.WithCmdContext(cmd.Context()) + + // updates the ctx in the cmd in case something was modified bt the config + return client.SetCmdClientContextHandler(ctx, cmd) + } +} diff --git a/eotsmanager/localmanager.go b/eotsmanager/localmanager.go index eb81928..b1ef7c4 100644 --- a/eotsmanager/localmanager.go +++ b/eotsmanager/localmanager.go @@ -119,17 +119,17 @@ func (lm *LocalEOTSManager) CreateKeyWithMnemonic(name, passphrase, hdPath, mnem // as when creating an account, passphrase will be asked twice // by the keyring lm.input.Reset(passphrase + "\n" + passphrase) - record, err := lm.kr.NewAccount(name, mnemonic, passphrase, hdPath, algo) + _, err = lm.kr.NewAccount(name, mnemonic, passphrase, hdPath, algo) if err != nil { return nil, err } - eotsPk, err := loadBIP340PubKeyFromKeyringRecord(record) + eotsPk, err := lm.LoadBIP340PubKeyFromKeyName(name) if err != nil { return nil, err } - if err := lm.es.AddEOTSKeyName(eotsPk.MustToBTCPK(), name); err != nil { + if err := lm.SaveEOTSKeyName(eotsPk.MustToBTCPK(), name); err != nil { return nil, err } @@ -143,8 +143,16 @@ func (lm *LocalEOTSManager) CreateKeyWithMnemonic(name, passphrase, hdPath, mnem return eotsPk, nil } -func loadBIP340PubKeyFromKeyringRecord(record *keyring.Record) (*bbntypes.BIP340PubKey, error) { - pubKey, err := record.GetPubKey() +func (lm *LocalEOTSManager) SaveEOTSKeyName(pk *btcec.PublicKey, keyName string) error { + return lm.es.AddEOTSKeyName(pk, keyName) +} + +func (lm *LocalEOTSManager) LoadBIP340PubKeyFromKeyName(keyName string) (*bbntypes.BIP340PubKey, error) { + info, err := lm.kr.Key(keyName) + if err != nil { + return nil, fmt.Errorf("failed to load keyring record for key %s: %w", keyName, err) + } + pubKey, err := info.GetPubKey() if err != nil { return nil, err } @@ -221,17 +229,13 @@ func (lm *LocalEOTSManager) signSchnorrSigFromPrivKey(privKey *btcec.PrivateKey, func (lm *LocalEOTSManager) SignSchnorrSigFromKeyname(keyName, passphrase string, msg []byte) (*schnorr.Signature, *bbntypes.BIP340PubKey, error) { lm.input.Reset(passphrase) - k, err := lm.kr.Key(keyName) - if err != nil { - return nil, nil, fmt.Errorf("failed to load keyring record for key %s: %w", keyName, err) - } - eotsPk, err := loadBIP340PubKeyFromKeyringRecord(k) + eotsPk, err := lm.LoadBIP340PubKeyFromKeyName(keyName) if err != nil { return nil, nil, err } - privKey, err := eotsPrivKeyFromRecord(k) + privKey, err := lm.eotsPrivKeyFromKeyName(keyName) if err != nil { return nil, nil, err } @@ -283,15 +287,15 @@ func (lm *LocalEOTSManager) getEOTSPrivKey(fpPk []byte, passphrase string) (*btc } lm.input.Reset(passphrase) + + return lm.eotsPrivKeyFromKeyName(keyName) +} + +func (lm *LocalEOTSManager) eotsPrivKeyFromKeyName(keyName string) (*btcec.PrivateKey, error) { k, err := lm.kr.Key(keyName) if err != nil { return nil, err } - - return eotsPrivKeyFromRecord(k) -} - -func eotsPrivKeyFromRecord(k *keyring.Record) (*btcec.PrivateKey, error) { privKeyCached := k.GetLocal().PrivKey.GetCachedValue() var privKey *btcec.PrivateKey diff --git a/finality-provider/cmd/cmd.go b/finality-provider/cmd/cmd.go index b671a69..b5189d6 100644 --- a/finality-provider/cmd/cmd.go +++ b/finality-provider/cmd/cmd.go @@ -3,12 +3,14 @@ package cmd import ( "os" + "github.com/babylonlabs-io/babylon/app/params" + bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/std" "github.com/spf13/cobra" "github.com/spf13/pflag" - "github.com/babylonlabs-io/babylon/app" fpcfg "github.com/babylonlabs-io/finality-provider/finality-provider/config" ) @@ -17,17 +19,15 @@ import ( // and exists a value in the config that could be used, it will be set in the ctx. func PersistClientCtx(ctx client.Context) func(cmd *cobra.Command, _ []string) error { return func(cmd *cobra.Command, _ []string) error { - // TODO(verify): if it uses the default encoding config it fails to list keys! output: - // "xx" is not a valid name or address: unable to unmarshal item.Data: - // Bytes left over in UnmarshalBinaryLengthPrefixed, should read 10 more bytes but have 154 - // [cosmos/cosmos-sdk@v0.50.6/crypto/keyring/keyring.go:973 - tempApp := app.NewTmpBabylonApp() + encCfg := params.DefaultEncodingConfig() + std.RegisterInterfaces(encCfg.InterfaceRegistry) + bstypes.RegisterInterfaces(encCfg.InterfaceRegistry) ctx = ctx. - WithCodec(tempApp.AppCodec()). - WithInterfaceRegistry(tempApp.InterfaceRegistry()). - WithTxConfig(tempApp.TxConfig()). - WithLegacyAmino(tempApp.LegacyAmino()). + WithCodec(encCfg.Codec). + WithInterfaceRegistry(encCfg.InterfaceRegistry). + WithTxConfig(encCfg.TxConfig). + WithLegacyAmino(encCfg.Amino). WithInput(os.Stdin) // set the default command outputs