From 1239d92bfcdb6fb2338bd5500e90dcd493ad2214 Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Fri, 3 Jan 2025 17:44:05 -0300 Subject: [PATCH 01/21] chore: add pop command --- eotsmanager/cmd/eotsd/daemon/pop.go | 139 ++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 eotsmanager/cmd/eotsd/daemon/pop.go diff --git a/eotsmanager/cmd/eotsd/daemon/pop.go b/eotsmanager/cmd/eotsd/daemon/pop.go new file mode 100644 index 00000000..a0006739 --- /dev/null +++ b/eotsmanager/cmd/eotsd/daemon/pop.go @@ -0,0 +1,139 @@ +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 +} From 83ef17fa49cbed5c968d6ee51aa2ab385b9f5fd1 Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Sun, 5 Jan 2025 13:10:58 -0300 Subject: [PATCH 02/21] feat: add new command to export pop for finality providers --- eotsmanager/cmd/eotsd/daemon/flags.go | 3 + eotsmanager/cmd/eotsd/daemon/pop.go | 308 ++++++++++++++++++++------ eotsmanager/cmd/eotsd/daemon/root.go | 1 + eotsmanager/cmd/eotsd/daemon/utils.go | 12 +- 4 files changed, 254 insertions(+), 70 deletions(-) diff --git a/eotsmanager/cmd/eotsd/daemon/flags.go b/eotsmanager/cmd/eotsd/daemon/flags.go index b5e7a2ba..0a21ac87 100644 --- a/eotsmanager/cmd/eotsd/daemon/flags.go +++ b/eotsmanager/cmd/eotsd/daemon/flags.go @@ -1,6 +1,9 @@ package daemon const ( + keyNameFlag = "key-name" + eotsPkFlag = "eots-pk" + passphraseFlag = "passphrase" forceFlag = "force" rpcListenerFlag = "rpc-listener" rpcClientFlag = "rpc-client" diff --git a/eotsmanager/cmd/eotsd/daemon/pop.go b/eotsmanager/cmd/eotsd/daemon/pop.go index a0006739..72655f24 100644 --- a/eotsmanager/cmd/eotsd/daemon/pop.go +++ b/eotsmanager/cmd/eotsd/daemon/pop.go @@ -1,18 +1,32 @@ package daemon import ( + "encoding/base64" + "encoding/json" "fmt" + "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/cometbft/cometbft/crypto/tmhash" + sdkflags "github.com/cosmos/cosmos-sdk/client/flags" + "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/tx/signing" + "github.com/spf13/cobra" bbnparams "github.com/babylonlabs-io/babylon/app/params" - bbn "github.com/babylonlabs-io/babylon/types" + bbntypes "github.com/babylonlabs-io/babylon/types" btcstktypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" + "github.com/babylonlabs-io/finality-provider/codec" "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" +) + +const ( + flagHomeBaby = "baby-home" + flagKeyNameBaby = "baby-key-name" + flagKeyringBackendBaby = "baby-keyring-backend" ) func init() { @@ -23,117 +37,281 @@ func init() { // 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"` + // Btc public key is the eots *bbntypes.BIP340PubKey marshal hex + BtcPublicKey string `json:"btcPublicKey"` + // Baby public key is the *secp256k1.PubKey marshal hex + BabyPublicKey string `json:"babyPublicKey"` + + BabySignBtc string `json:"babySignBtc"` + BtcSignBaby string `json:"btcSignBaby"` + + // Btc address is the same as the btc pub key + BtcAddress string `json:"btcAddress"` + BabyAddress string `json:"babyAddress"` } -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 NewExportPopCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "pop-export [bbn-address]", + Short: "Exports the Proof of Possession by signing over the finality provider's Babylon address with the EOTS private key.", + Long: `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.`, + RunE: exportPop, + } + + f := cmd.Flags() + + f.String(sdkflags.FlagHome, config.DefaultEOTSDir, "EOTS home directory") + f.String(keyNameFlag, "", "EOTS key name") + f.String(eotsPkFlag, "", "EOTS public key of the finality-provider") + f.String(passphraseFlag, "", "EOTS passphrase used to decrypt the keyring") + f.String(sdkflags.FlagKeyringBackend, keyring.BackendTest, "EOTS backend of the keyring") + + f.String(flagHomeBaby, "", "BABY home directory") + f.String(flagKeyNameBaby, "", "BABY key name") + f.String(flagKeyringBackendBaby, keyring.BackendTest, "BABY backend of the keyring") + + return cmd } -func ExportPoP(ctx *cli.Context) error { - keyName := ctx.String(keyNameFlag) - fpPkStr := ctx.String(eotsPkFlag) - passphrase := ctx.String(passphraseFlag) - keyringBackend := ctx.String(keyringBackendFlag) +func exportPop(cmd *cobra.Command, _ []string) error { + f := cmd.Flags() - args := ctx.Args() - bbnAddressStr := args.First() - bbnAddr, err := sdk.AccAddressFromBech32(bbnAddressStr) + eotsKeyName, err := f.GetString(keyNameFlag) if err != nil { - return fmt.Errorf("invalid argument %s, please provide a valid bbn address as argument, err: %w", bbnAddressStr, err) + return 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) + eotsFpPubKeyStr, err := f.GetString(eotsPkFlag) + if err != nil { + return err + } + + eotsPassphrase, err := f.GetString(passphraseFlag) + if err != nil { + return err + } + + eotsKeyringBackend, err := f.GetString(sdkflags.FlagKeyringBackend) + if err != nil { + return err } - homePath, err := getHomeFlag(ctx) + eotsHomePath, err := getHomePath(cmd) if err != nil { return fmt.Errorf("failed to load home flag: %w", err) } - cfg, err := config.LoadConfig(homePath) + babyHomePath, err := getCleanPath(cmd, flagHomeBaby) + if err != nil { + return fmt.Errorf("failed to load baby home flag: %w", err) + } + + babyKeyName, err := f.GetString(flagKeyNameBaby) + if err != nil { + return err + } + + babyKeyringBackend, err := f.GetString(flagKeyringBackendBaby) + if err != nil { + return err + } + + cdc := codec.MakeCodec() + babyKeyring, err := keyring.New("baby", babyKeyringBackend, babyHomePath, cmd.InOrStdin(), cdc) + if err != nil { + return fmt.Errorf("failed to create keyring: %w", err) + } + + babyKeyRecord, err := babyKeyring.Key(babyKeyName) + if err != nil { + return err + } + + babyAccAddr, err := babyKeyRecord.GetAddress() + if err != nil { + return err + } + + bbnAddrBz, err := sdk.GetFromBech32(babyAccAddr.String(), "bbn") + if err != nil { + return err + } + bbnAddr := sdk.AccAddress(bbnAddrBz) + + if len(eotsFpPubKeyStr) == 0 && len(eotsKeyName) == 0 { + return fmt.Errorf("at least one of the flags: %s, %s needs to be informed", keyNameFlag, eotsPkFlag) + } + + cfg, err := config.LoadConfig(eotsHomePath) if err != nil { - return fmt.Errorf("failed to load config at %s: %w", homePath, err) + return fmt.Errorf("failed to load config at %s: %w", eotsHomePath, err) } - logger, err := log.NewRootLoggerWithFile(config.LogFile(homePath), cfg.LogLevel) + logger, err := log.NewRootLoggerWithFile(config.LogFile(eotsHomePath), cfg.LogLevel) if err != nil { return fmt.Errorf("failed to load the logger") } - dbBackend, err := cfg.DatabaseConfig.GetDbBackend() + 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) + eotsManager, err := eotsmanager.NewLocalEOTSManager(eotsHomePath, eotsKeyringBackend, 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) + btcSig, btcPubKey, err := eotsSignMsg(eotsManager, eotsKeyName, eotsFpPubKeyStr, eotsPassphrase, hashOfMsgToSign) if err != nil { return fmt.Errorf("failed to sign address %s: %w", bbnAddr.String(), err) } - bip340Sig := bbn.NewBIP340SignatureFromBTCSig(btcSig) + bip340Sig := bbntypes.NewBIP340SignatureFromBTCSig(btcSig) btcSigBz, err := bip340Sig.Marshal() if err != nil { return fmt.Errorf("failed to marshal BTC Sig: %w", err) } - pop := btcstktypes.ProofOfPossessionBTC{ + popBtcSignBaby := btcstktypes.ProofOfPossessionBTC{ BtcSigType: btcstktypes.BTCSigType_BIP340, BtcSig: btcSigBz, } - popHex, err := pop.ToHexStr() + popHexBtcSignBaby, err := popBtcSignBaby.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(), - }) + babyPubKey, err := babyPk(babyKeyRecord) + if err != nil { + return err + } + + eotsPkHex := btcPubKey.MarshalHex() + + babySignBtcDoc := NewCosmosSignDoc( + bbnAddr.String(), + eotsPkHex, + ) + + babySignBtcMarshaled, err := json.Marshal(babySignBtcDoc) + if err != nil { + return fmt.Errorf("failed to marshal sign doc: %w", err) + } + + babySignBtcBz := sdk.MustSortJSON(babySignBtcMarshaled) + babySignBtc, _, err := babyKeyring.Sign(babyKeyName, babySignBtcBz, signing.SignMode_SIGN_MODE_DIRECT) + if err != nil { + return err + } + + out := PoPExport{ + BtcPublicKey: eotsPkHex, + BabyPublicKey: base64.StdEncoding.EncodeToString(babyPubKey.Bytes()), + + BtcAddress: eotsPkHex, + BabyAddress: bbnAddr.String(), + + BtcSignBaby: popHexBtcSignBaby, + BabySignBtc: base64.StdEncoding.EncodeToString(babySignBtc), + } + + jsonString, err := json.MarshalIndent(out, "", " ") + if err != nil { + return err + } + + cmd.Println(string(jsonString)) return nil } + +func babyPk(babyRecord *keyring.Record) (*secp256k1.PubKey, error) { + pubKey, err := babyRecord.GetPubKey() + if err != nil { + return nil, err + } + + switch v := pubKey.(type) { + case *secp256k1.PubKey: + return v, nil + default: + return nil, fmt.Errorf("unsupported key type in keyring") + } +} + +func eotsSignMsg( + 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) +} + +type Msg struct { + Type string `json:"type"` + Value MsgValue `json:"value"` +} + +type SignDoc struct { + ChainID string `json:"chain_id"` + AccountNumber string `json:"account_number"` + Sequence string `json:"sequence"` + Fee Fee `json:"fee"` + Msgs []Msg `json:"msgs"` + Memo string `json:"memo"` +} + +type Fee struct { + Gas string `json:"gas"` + Amount []string `json:"amount"` +} + +type MsgValue struct { + Signer string `json:"signer"` + Data string `json:"data"` +} + +func NewCosmosSignDoc( + signer string, + data string, +) *SignDoc { + return &SignDoc{ + ChainID: "", + AccountNumber: "0", + Sequence: "0", + Fee: Fee{ + Gas: "0", + Amount: []string{}, + }, + Msgs: []Msg{ + { + Type: "sign/MsgSignData", + Value: MsgValue{ + Signer: signer, + Data: data, + }, + }, + }, + Memo: "", + } +} diff --git a/eotsmanager/cmd/eotsd/daemon/root.go b/eotsmanager/cmd/eotsd/daemon/root.go index de200793..22f7d7bc 100644 --- a/eotsmanager/cmd/eotsd/daemon/root.go +++ b/eotsmanager/cmd/eotsd/daemon/root.go @@ -26,6 +26,7 @@ func NewRootCmd() *cobra.Command { NewStartCmd(), version.CommandVersion("eotsd"), CommandPrintAllKeys(), + NewExportPopCmd(), ) return rootCmd diff --git a/eotsmanager/cmd/eotsd/daemon/utils.go b/eotsmanager/cmd/eotsd/daemon/utils.go index 9b3e6300..46920818 100644 --- a/eotsmanager/cmd/eotsd/daemon/utils.go +++ b/eotsmanager/cmd/eotsd/daemon/utils.go @@ -14,19 +14,21 @@ import ( ) func getHomePath(cmd *cobra.Command) (string, error) { - rawHomePath, err := cmd.Flags().GetString(sdkflags.FlagHome) + return getCleanPath(cmd, sdkflags.FlagHome) +} + +func getCleanPath(cmd *cobra.Command, flag string) (string, error) { + rawPath, err := cmd.Flags().GetString(flag) if err != nil { return "", err } - homePath, err := filepath.Abs(rawHomePath) + cleanPath, err := filepath.Abs(rawPath) if err != nil { return "", err } - // Create home directory - homePath = util.CleanAndExpandPath(homePath) - return homePath, nil + return util.CleanAndExpandPath(cleanPath), nil } // PersistClientCtx persist some vars from the cmd or config to the client context. From 84f6a9c7b8fbe0f0a65a46593b2e115b5e865836 Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Sun, 5 Jan 2025 13:13:46 -0300 Subject: [PATCH 03/21] chore: removed unnecessary command --- eotsmanager/cmd/eotsd/daemon/pop.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/eotsmanager/cmd/eotsd/daemon/pop.go b/eotsmanager/cmd/eotsd/daemon/pop.go index 72655f24..3444889c 100644 --- a/eotsmanager/cmd/eotsd/daemon/pop.go +++ b/eotsmanager/cmd/eotsd/daemon/pop.go @@ -33,9 +33,7 @@ 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. +// PoPExport the data for exporting the PoP type PoPExport struct { // Btc public key is the eots *bbntypes.BIP340PubKey marshal hex BtcPublicKey string `json:"btcPublicKey"` From b388f50f16b4c716fbffccf544a21d7e3f5aefd9 Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Sun, 5 Jan 2025 13:19:34 -0300 Subject: [PATCH 04/21] chore: update struct comments --- eotsmanager/cmd/eotsd/daemon/pop.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/eotsmanager/cmd/eotsd/daemon/pop.go b/eotsmanager/cmd/eotsd/daemon/pop.go index 3444889c..a045d7ad 100644 --- a/eotsmanager/cmd/eotsd/daemon/pop.go +++ b/eotsmanager/cmd/eotsd/daemon/pop.go @@ -40,11 +40,14 @@ type PoPExport struct { // Baby public key is the *secp256k1.PubKey marshal hex BabyPublicKey string `json:"babyPublicKey"` + // Babylon key pair signs adr36 BabySignBtc string `json:"babySignBtc"` + // BIP340 ProofOfPossessionBTC as hex BtcSignBaby string `json:"btcSignBaby"` // Btc address is the same as the btc pub key - BtcAddress string `json:"btcAddress"` + BtcAddress string `json:"btcAddress"` + // Babylon address ex.: bbn1f04czxeqprn0s9fe7kdzqyde2e6nqj63dllwsm BabyAddress string `json:"babyAddress"` } From 900f94983d649631cb69e9d591c674a13718f1d4 Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Sun, 5 Jan 2025 13:21:27 -0300 Subject: [PATCH 05/21] chore: add #262 to changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb593898..f357bcb4 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 +* [#262](https://github.com/babylonlabs-io/finality-provider/pull/262) chore: add new command to export pop * [#251](https://github.com/babylonlabs-io/finality-provider/pull/251) chore: nlreturn lint * [#252](https://github.com/babylonlabs-io/finality-provider/pull/252) feat: rm interceptors and use context * [#253](https://github.com/babylonlabs-io/finality-provider/issues/253) Refactor to start from the last finalized height From e81a87f3e7fac2aa0d891b327bbb3b39189bb49e Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Mon, 6 Jan 2025 09:14:50 -0300 Subject: [PATCH 06/21] chore: update comments --- eotsmanager/cmd/eotsd/daemon/pop.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/eotsmanager/cmd/eotsd/daemon/pop.go b/eotsmanager/cmd/eotsd/daemon/pop.go index a045d7ad..614a7553 100644 --- a/eotsmanager/cmd/eotsd/daemon/pop.go +++ b/eotsmanager/cmd/eotsd/daemon/pop.go @@ -33,7 +33,7 @@ func init() { bbnparams.SetAddressPrefixes() } -// PoPExport the data for exporting the PoP +// PoPExport the data needed to prove ownership of the eots and baby key pairs. type PoPExport struct { // Btc public key is the eots *bbntypes.BIP340PubKey marshal hex BtcPublicKey string `json:"btcPublicKey"` @@ -53,12 +53,13 @@ type PoPExport struct { func NewExportPopCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "pop-export [bbn-address]", - Short: "Exports the Proof of Possession by signing over the finality provider's Babylon address with the EOTS private key.", - Long: `Parse the address received as argument, hash it with + Use: "pop-export", + Short: "Exports the Proof of Possession by signing over the BABY address with the EOTS private key and the EOTS public key with the BABY private key.", + Long: `Parse the address from the BABY keyring, loads the address 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.`, + to build a Proof of Possession. For the creation of the BABY signature over the eots pk, + it loads the BABY key pair and signs the eots-pk hex with adr036 and exports it.`, RunE: exportPop, } From e451da067bd998c60341a0285f5c612d005b3e9a Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Mon, 6 Jan 2025 11:27:08 -0300 Subject: [PATCH 07/21] chore: address pr comments --- eotsmanager/cmd/eotsd/daemon/pop.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/eotsmanager/cmd/eotsd/daemon/pop.go b/eotsmanager/cmd/eotsd/daemon/pop.go index 614a7553..35904882 100644 --- a/eotsmanager/cmd/eotsd/daemon/pop.go +++ b/eotsmanager/cmd/eotsd/daemon/pop.go @@ -53,13 +53,13 @@ type PoPExport struct { func NewExportPopCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "pop-export", - Short: "Exports the Proof of Possession by signing over the BABY address with the EOTS private key and the EOTS public key with the BABY private key.", - Long: `Parse the address from the BABY keyring, loads the address hash it with + Use: "export-pop", + Short: "Exports the Proof of Possession by (1) signing over the BABY address with the EOTS private key and (2) signing over the EOTS public key with the BABY private key.", + Long: `Parse the address from the BABY keyring, load the address, 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. For the creation of the BABY signature over the eots pk, - it loads the BABY key pair and signs the eots-pk hex with adr036 and exports it.`, + it loads the BABY key pair and signs the eots-pk hex and exports it.`, RunE: exportPop, } From 59b3022edfffad2f7c1b4754a96ba06f1b38a91d Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Mon, 6 Jan 2025 12:45:57 -0300 Subject: [PATCH 08/21] chore: modified to sign address directly and export the schnirr signature instead of the PoP --- eotsmanager/cmd/eotsd/daemon/pop.go | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/eotsmanager/cmd/eotsd/daemon/pop.go b/eotsmanager/cmd/eotsd/daemon/pop.go index 35904882..42d9df22 100644 --- a/eotsmanager/cmd/eotsd/daemon/pop.go +++ b/eotsmanager/cmd/eotsd/daemon/pop.go @@ -16,7 +16,6 @@ import ( bbnparams "github.com/babylonlabs-io/babylon/app/params" bbntypes "github.com/babylonlabs-io/babylon/types" - btcstktypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" "github.com/babylonlabs-io/finality-provider/codec" "github.com/babylonlabs-io/finality-provider/eotsmanager" "github.com/babylonlabs-io/finality-provider/eotsmanager/config" @@ -40,9 +39,9 @@ type PoPExport struct { // Baby public key is the *secp256k1.PubKey marshal hex BabyPublicKey string `json:"babyPublicKey"` - // Babylon key pair signs adr36 + // Babylon key pair signs EOTS public key as hex (BtcAddress) BabySignBtc string `json:"babySignBtc"` - // BIP340 ProofOfPossessionBTC as hex + // Schnorr signature of EOTS private key over the Baby address BtcSignBaby string `json:"btcSignBaby"` // Btc address is the same as the btc pub key @@ -169,27 +168,11 @@ func exportPop(cmd *cobra.Command, _ []string) error { } hashOfMsgToSign := tmhash.Sum(bbnAddr.Bytes()) - btcSig, btcPubKey, err := eotsSignMsg(eotsManager, eotsKeyName, eotsFpPubKeyStr, eotsPassphrase, hashOfMsgToSign) + btcSigOverBabyAddr, btcPubKey, err := eotsSignMsg(eotsManager, eotsKeyName, eotsFpPubKeyStr, eotsPassphrase, hashOfMsgToSign) if err != nil { return fmt.Errorf("failed to sign address %s: %w", bbnAddr.String(), err) } - bip340Sig := bbntypes.NewBIP340SignatureFromBTCSig(btcSig) - btcSigBz, err := bip340Sig.Marshal() - if err != nil { - return fmt.Errorf("failed to marshal BTC Sig: %w", err) - } - - popBtcSignBaby := btcstktypes.ProofOfPossessionBTC{ - BtcSigType: btcstktypes.BTCSigType_BIP340, - BtcSig: btcSigBz, - } - - popHexBtcSignBaby, err := popBtcSignBaby.ToHexStr() - if err != nil { - return fmt.Errorf("failed to marshal pop to hex: %w", err) - } - babyPubKey, err := babyPk(babyKeyRecord) if err != nil { return err @@ -220,7 +203,7 @@ func exportPop(cmd *cobra.Command, _ []string) error { BtcAddress: eotsPkHex, BabyAddress: bbnAddr.String(), - BtcSignBaby: popHexBtcSignBaby, + BtcSignBaby: base64.StdEncoding.EncodeToString(btcSigOverBabyAddr.Serialize()), BabySignBtc: base64.StdEncoding.EncodeToString(babySignBtc), } From 61402346cbb680cd96cd95c124b644ed1540110a Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Mon, 6 Jan 2025 12:51:27 -0300 Subject: [PATCH 09/21] chore: remove unnecessary addr convertion --- eotsmanager/cmd/eotsd/daemon/pop.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/eotsmanager/cmd/eotsd/daemon/pop.go b/eotsmanager/cmd/eotsd/daemon/pop.go index 42d9df22..2d9e85c0 100644 --- a/eotsmanager/cmd/eotsd/daemon/pop.go +++ b/eotsmanager/cmd/eotsd/daemon/pop.go @@ -131,17 +131,11 @@ func exportPop(cmd *cobra.Command, _ []string) error { return err } - babyAccAddr, err := babyKeyRecord.GetAddress() + bbnAddr, err := babyKeyRecord.GetAddress() if err != nil { return err } - bbnAddrBz, err := sdk.GetFromBech32(babyAccAddr.String(), "bbn") - if err != nil { - return err - } - bbnAddr := sdk.AccAddress(bbnAddrBz) - if len(eotsFpPubKeyStr) == 0 && len(eotsKeyName) == 0 { return fmt.Errorf("at least one of the flags: %s, %s needs to be informed", keyNameFlag, eotsPkFlag) } From cbf9a070bab64e10dc8ab847e414fe27c21c410c Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Mon, 6 Jan 2025 12:53:00 -0300 Subject: [PATCH 10/21] chore: remove bad comment --- eotsmanager/cmd/eotsd/daemon/pop.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eotsmanager/cmd/eotsd/daemon/pop.go b/eotsmanager/cmd/eotsd/daemon/pop.go index 2d9e85c0..71f57cda 100644 --- a/eotsmanager/cmd/eotsd/daemon/pop.go +++ b/eotsmanager/cmd/eotsd/daemon/pop.go @@ -39,7 +39,7 @@ type PoPExport struct { // Baby public key is the *secp256k1.PubKey marshal hex BabyPublicKey string `json:"babyPublicKey"` - // Babylon key pair signs EOTS public key as hex (BtcAddress) + // Babylon key pair signs EOTS public key as hex BabySignBtc string `json:"babySignBtc"` // Schnorr signature of EOTS private key over the Baby address BtcSignBaby string `json:"btcSignBaby"` From 91dca21873c54b9d1c400b56e35152d59fccb1ff Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Mon, 6 Jan 2025 13:05:44 -0300 Subject: [PATCH 11/21] chore: modified to sign the bbn address as string direclty --- eotsmanager/cmd/eotsd/daemon/pop.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eotsmanager/cmd/eotsd/daemon/pop.go b/eotsmanager/cmd/eotsd/daemon/pop.go index 71f57cda..a7e7533e 100644 --- a/eotsmanager/cmd/eotsd/daemon/pop.go +++ b/eotsmanager/cmd/eotsd/daemon/pop.go @@ -161,7 +161,7 @@ func exportPop(cmd *cobra.Command, _ []string) error { return fmt.Errorf("failed to create EOTS manager: %w", err) } - hashOfMsgToSign := tmhash.Sum(bbnAddr.Bytes()) + hashOfMsgToSign := tmhash.Sum([]byte(bbnAddr.String())) btcSigOverBabyAddr, btcPubKey, err := eotsSignMsg(eotsManager, eotsKeyName, eotsFpPubKeyStr, eotsPassphrase, hashOfMsgToSign) if err != nil { return fmt.Errorf("failed to sign address %s: %w", bbnAddr.String(), err) From 4959109585ed7559d5c9f2bb5a77396fc890bccd Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Mon, 6 Jan 2025 13:56:52 -0300 Subject: [PATCH 12/21] chore: update pop export structure naming accordingly to fp data --- eotsmanager/cmd/eotsd/daemon/pop.go | 21 ++++---- eotsmanager/cmd/eotsd/daemon/pop_test.go | 64 ++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 12 deletions(-) create mode 100644 eotsmanager/cmd/eotsd/daemon/pop_test.go diff --git a/eotsmanager/cmd/eotsd/daemon/pop.go b/eotsmanager/cmd/eotsd/daemon/pop.go index a7e7533e..4716f63f 100644 --- a/eotsmanager/cmd/eotsd/daemon/pop.go +++ b/eotsmanager/cmd/eotsd/daemon/pop.go @@ -34,18 +34,16 @@ func init() { // PoPExport the data needed to prove ownership of the eots and baby key pairs. type PoPExport struct { - // Btc public key is the eots *bbntypes.BIP340PubKey marshal hex - BtcPublicKey string `json:"btcPublicKey"` + // Btc public key is the EOTS PK *bbntypes.BIP340PubKey marshal hex + EotsPublicKey string `json:"eotsPublicKey"` // Baby public key is the *secp256k1.PubKey marshal hex BabyPublicKey string `json:"babyPublicKey"` // Babylon key pair signs EOTS public key as hex - BabySignBtc string `json:"babySignBtc"` - // Schnorr signature of EOTS private key over the Baby address - BtcSignBaby string `json:"btcSignBaby"` + BabySignEotsPk string `json:"babySignBtc"` + // Schnorr signature of EOTS private key over the SHA256(Baby address) + EotsSignBaby string `json:"btcSignBaby"` - // Btc address is the same as the btc pub key - BtcAddress string `json:"btcAddress"` // Babylon address ex.: bbn1f04czxeqprn0s9fe7kdzqyde2e6nqj63dllwsm BabyAddress string `json:"babyAddress"` } @@ -162,7 +160,7 @@ func exportPop(cmd *cobra.Command, _ []string) error { } hashOfMsgToSign := tmhash.Sum([]byte(bbnAddr.String())) - btcSigOverBabyAddr, btcPubKey, err := eotsSignMsg(eotsManager, eotsKeyName, eotsFpPubKeyStr, eotsPassphrase, hashOfMsgToSign) + schnorrSigOverBabyAddr, btcPubKey, err := eotsSignMsg(eotsManager, eotsKeyName, eotsFpPubKeyStr, eotsPassphrase, hashOfMsgToSign) if err != nil { return fmt.Errorf("failed to sign address %s: %w", bbnAddr.String(), err) } @@ -191,14 +189,13 @@ func exportPop(cmd *cobra.Command, _ []string) error { } out := PoPExport{ - BtcPublicKey: eotsPkHex, + EotsPublicKey: eotsPkHex, BabyPublicKey: base64.StdEncoding.EncodeToString(babyPubKey.Bytes()), - BtcAddress: eotsPkHex, BabyAddress: bbnAddr.String(), - BtcSignBaby: base64.StdEncoding.EncodeToString(btcSigOverBabyAddr.Serialize()), - BabySignBtc: base64.StdEncoding.EncodeToString(babySignBtc), + EotsSignBaby: base64.StdEncoding.EncodeToString(schnorrSigOverBabyAddr.Serialize()), + BabySignEotsPk: base64.StdEncoding.EncodeToString(babySignBtc), } jsonString, err := json.MarshalIndent(out, "", " ") diff --git a/eotsmanager/cmd/eotsd/daemon/pop_test.go b/eotsmanager/cmd/eotsd/daemon/pop_test.go new file mode 100644 index 00000000..75463763 --- /dev/null +++ b/eotsmanager/cmd/eotsd/daemon/pop_test.go @@ -0,0 +1,64 @@ +package daemon_test + +import ( + "encoding/base64" + "encoding/json" + "testing" + + bbntypes "github.com/babylonlabs-io/babylon/types" + "github.com/babylonlabs-io/finality-provider/eotsmanager/cmd/eotsd/daemon" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/cometbft/cometbft/crypto/tmhash" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" +) + +var hardcodedPopToVerify daemon.PoPExport = daemon.PoPExport{ + EotsPublicKey: "3d0bebcbe800236ce8603c5bb1ab6c2af0932e947db4956a338f119797c37f1e", + BabyPublicKey: "A0V6yw74EdvoAWVauFqkH/GVM9YIpZitZf6bVEzG69tT", + + BabySignEotsPk: "AOoIG2cwC2IMiJL3OL0zLEIUY201X1qKumDr/1qDJ4oQvAp78W1nb5EnVasRPQ/XrKXqudUDnZFprLd0jaRJtQ==", + EotsSignBaby: "pR6vxgU0gXq+VqO+y7dHpZgHTz3zr5hdqXXh0WcWNkqUnRjHrizhYAHDMV8gh4vks4PqzKAIgZ779Wqwf5UrXQ==", + + BabyAddress: "bbn1f04czxeqprn0s9fe7kdzqyde2e6nqj63dllwsm", +} + +func TestPoPVerifyEotsSignBaby(t *testing.T) { + eotsPubKey, err := bbntypes.NewBIP340PubKeyFromHex(hardcodedPopToVerify.EotsPublicKey) + require.NoError(t, err) + + schnorrSigBase64, err := base64.StdEncoding.DecodeString(hardcodedPopToVerify.EotsSignBaby) + require.NoError(t, err) + + schnorrSig, err := schnorr.ParseSignature(schnorrSigBase64) + require.NoError(t, err) + + sha256Addr := tmhash.Sum([]byte(hardcodedPopToVerify.BabyAddress)) + require.True(t, schnorrSig.Verify(sha256Addr, eotsPubKey.MustToBTCPK())) +} + +func TestPoPVerifyBabySignEotsPk(t *testing.T) { + + babyPubKeyBz, err := base64.StdEncoding.DecodeString(hardcodedPopToVerify.BabyPublicKey) + require.NoError(t, err) + + babyPubKey := &secp256k1.PubKey{ + Key: babyPubKeyBz, + } + require.NotNil(t, babyPubKey) + + babySignBtcDoc := daemon.NewCosmosSignDoc( + hardcodedPopToVerify.BabyAddress, + hardcodedPopToVerify.EotsPublicKey, + ) + babySignBtcMarshaled, err := json.Marshal(babySignBtcDoc) + require.NoError(t, err) + + babySignEotsBz := sdk.MustSortJSON(babySignBtcMarshaled) + + secp256SigBase64, err := base64.StdEncoding.DecodeString(hardcodedPopToVerify.BabySignEotsPk) + require.NoError(t, err) + + require.True(t, babyPubKey.VerifySignature(babySignEotsBz, secp256SigBase64)) +} From bb080829ca4f770c2c78efdc28dec8b482416882 Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Mon, 6 Jan 2025 14:33:08 -0300 Subject: [PATCH 13/21] chore: add public functions to verify signature, allowing import to easy verify --- eotsmanager/cmd/eotsd/daemon/pop.go | 46 ++++++++++++++++++++++++ eotsmanager/cmd/eotsd/daemon/pop_test.go | 45 ++++++----------------- 2 files changed, 56 insertions(+), 35 deletions(-) diff --git a/eotsmanager/cmd/eotsd/daemon/pop.go b/eotsmanager/cmd/eotsd/daemon/pop.go index 4716f63f..cd9602e3 100644 --- a/eotsmanager/cmd/eotsd/daemon/pop.go +++ b/eotsmanager/cmd/eotsd/daemon/pop.go @@ -208,6 +208,52 @@ func exportPop(cmd *cobra.Command, _ []string) error { return nil } +func VerifyEotsSignBaby(eotsPk, babyAddr, eotsSigOverBabyAddr string) (valid bool, err error) { + eotsPubKey, err := bbntypes.NewBIP340PubKeyFromHex(eotsPk) + if err != nil { + return false, err + } + + schnorrSigBase64, err := base64.StdEncoding.DecodeString(eotsSigOverBabyAddr) + if err != nil { + return false, err + } + + schnorrSig, err := schnorr.ParseSignature(schnorrSigBase64) + if err != nil { + return false, err + } + + sha256Addr := tmhash.Sum([]byte(babyAddr)) + return schnorrSig.Verify(sha256Addr, eotsPubKey.MustToBTCPK()), nil +} + +func VerifyBabySignEots(babyPk, babyAddr, eotsPk, babySigOverEotsPk string) (valid bool, err error) { + babyPubKeyBz, err := base64.StdEncoding.DecodeString(babyPk) + if err != nil { + return false, err + } + + babyPubKey := &secp256k1.PubKey{ + Key: babyPubKeyBz, + } + + babySignBtcDoc := NewCosmosSignDoc(babyAddr, eotsPk) + babySignBtcMarshaled, err := json.Marshal(babySignBtcDoc) + if err != nil { + return false, err + } + + babySignEotsBz := sdk.MustSortJSON(babySignBtcMarshaled) + + secp256SigBase64, err := base64.StdEncoding.DecodeString(babySigOverEotsPk) + if err != nil { + return false, err + } + + return babyPubKey.VerifySignature(babySignEotsBz, secp256SigBase64), nil +} + func babyPk(babyRecord *keyring.Record) (*secp256k1.PubKey, error) { pubKey, err := babyRecord.GetPubKey() if err != nil { diff --git a/eotsmanager/cmd/eotsd/daemon/pop_test.go b/eotsmanager/cmd/eotsd/daemon/pop_test.go index 75463763..95a81d0e 100644 --- a/eotsmanager/cmd/eotsd/daemon/pop_test.go +++ b/eotsmanager/cmd/eotsd/daemon/pop_test.go @@ -1,16 +1,9 @@ package daemon_test import ( - "encoding/base64" - "encoding/json" "testing" - bbntypes "github.com/babylonlabs-io/babylon/types" "github.com/babylonlabs-io/finality-provider/eotsmanager/cmd/eotsd/daemon" - "github.com/btcsuite/btcd/btcec/v2/schnorr" - "github.com/cometbft/cometbft/crypto/tmhash" - "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" ) @@ -25,40 +18,22 @@ var hardcodedPopToVerify daemon.PoPExport = daemon.PoPExport{ } func TestPoPVerifyEotsSignBaby(t *testing.T) { - eotsPubKey, err := bbntypes.NewBIP340PubKeyFromHex(hardcodedPopToVerify.EotsPublicKey) - require.NoError(t, err) - - schnorrSigBase64, err := base64.StdEncoding.DecodeString(hardcodedPopToVerify.EotsSignBaby) - require.NoError(t, err) - - schnorrSig, err := schnorr.ParseSignature(schnorrSigBase64) + valid, err := daemon.VerifyEotsSignBaby( + hardcodedPopToVerify.EotsPublicKey, + hardcodedPopToVerify.BabyAddress, + hardcodedPopToVerify.EotsSignBaby, + ) require.NoError(t, err) - - sha256Addr := tmhash.Sum([]byte(hardcodedPopToVerify.BabyAddress)) - require.True(t, schnorrSig.Verify(sha256Addr, eotsPubKey.MustToBTCPK())) + require.True(t, valid) } func TestPoPVerifyBabySignEotsPk(t *testing.T) { - - babyPubKeyBz, err := base64.StdEncoding.DecodeString(hardcodedPopToVerify.BabyPublicKey) - require.NoError(t, err) - - babyPubKey := &secp256k1.PubKey{ - Key: babyPubKeyBz, - } - require.NotNil(t, babyPubKey) - - babySignBtcDoc := daemon.NewCosmosSignDoc( + valid, err := daemon.VerifyBabySignEots( + hardcodedPopToVerify.BabyPublicKey, hardcodedPopToVerify.BabyAddress, hardcodedPopToVerify.EotsPublicKey, + hardcodedPopToVerify.BabySignEotsPk, ) - babySignBtcMarshaled, err := json.Marshal(babySignBtcDoc) require.NoError(t, err) - - babySignEotsBz := sdk.MustSortJSON(babySignBtcMarshaled) - - secp256SigBase64, err := base64.StdEncoding.DecodeString(hardcodedPopToVerify.BabySignEotsPk) - require.NoError(t, err) - - require.True(t, babyPubKey.VerifySignature(babySignEotsBz, secp256SigBase64)) + require.True(t, valid) } From 56f7a3908402f0ffb895d92fe692219540f80e69 Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Mon, 6 Jan 2025 14:50:15 -0300 Subject: [PATCH 14/21] fix: lint --- .golangci.yml | 2 +- eotsmanager/cmd/eotsd/daemon/pop.go | 2 +- eotsmanager/cmd/eotsd/daemon/pop_test.go | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 2d230c01..7c6a40c2 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -39,7 +39,7 @@ linters: - nilerr - nlreturn - noctx - - nonamedreturns + # - nonamedreturns named returns are good - nosprintfhostport - paralleltest - reassign diff --git a/eotsmanager/cmd/eotsd/daemon/pop.go b/eotsmanager/cmd/eotsd/daemon/pop.go index cd9602e3..fb60c004 100644 --- a/eotsmanager/cmd/eotsd/daemon/pop.go +++ b/eotsmanager/cmd/eotsd/daemon/pop.go @@ -223,8 +223,8 @@ func VerifyEotsSignBaby(eotsPk, babyAddr, eotsSigOverBabyAddr string) (valid boo if err != nil { return false, err } - sha256Addr := tmhash.Sum([]byte(babyAddr)) + return schnorrSig.Verify(sha256Addr, eotsPubKey.MustToBTCPK()), nil } diff --git a/eotsmanager/cmd/eotsd/daemon/pop_test.go b/eotsmanager/cmd/eotsd/daemon/pop_test.go index 95a81d0e..274bae9a 100644 --- a/eotsmanager/cmd/eotsd/daemon/pop_test.go +++ b/eotsmanager/cmd/eotsd/daemon/pop_test.go @@ -18,6 +18,7 @@ var hardcodedPopToVerify daemon.PoPExport = daemon.PoPExport{ } func TestPoPVerifyEotsSignBaby(t *testing.T) { + t.Parallel() valid, err := daemon.VerifyEotsSignBaby( hardcodedPopToVerify.EotsPublicKey, hardcodedPopToVerify.BabyAddress, @@ -28,6 +29,7 @@ func TestPoPVerifyEotsSignBaby(t *testing.T) { } func TestPoPVerifyBabySignEotsPk(t *testing.T) { + t.Parallel() valid, err := daemon.VerifyBabySignEots( hardcodedPopToVerify.BabyPublicKey, hardcodedPopToVerify.BabyAddress, From 4515a79569e73717f6f649798b2fa707043e02ac Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Mon, 6 Jan 2025 14:52:58 -0300 Subject: [PATCH 15/21] chore: moved json attributes to more fp like naming --- eotsmanager/cmd/eotsd/daemon/pop.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eotsmanager/cmd/eotsd/daemon/pop.go b/eotsmanager/cmd/eotsd/daemon/pop.go index fb60c004..3bf7fea3 100644 --- a/eotsmanager/cmd/eotsd/daemon/pop.go +++ b/eotsmanager/cmd/eotsd/daemon/pop.go @@ -40,9 +40,9 @@ type PoPExport struct { BabyPublicKey string `json:"babyPublicKey"` // Babylon key pair signs EOTS public key as hex - BabySignEotsPk string `json:"babySignBtc"` + BabySignEotsPk string `json:"babySignEotsPk"` // Schnorr signature of EOTS private key over the SHA256(Baby address) - EotsSignBaby string `json:"btcSignBaby"` + EotsSignBaby string `json:"eotsSignBaby"` // Babylon address ex.: bbn1f04czxeqprn0s9fe7kdzqyde2e6nqj63dllwsm BabyAddress string `json:"babyAddress"` From cc07bcd4dc434f7095668724aad6865bffe425e0 Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Mon, 6 Jan 2025 14:59:45 -0300 Subject: [PATCH 16/21] chore: add func to verify the PopExport --- eotsmanager/cmd/eotsd/daemon/pop.go | 14 ++++++++++++++ eotsmanager/cmd/eotsd/daemon/pop_test.go | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/eotsmanager/cmd/eotsd/daemon/pop.go b/eotsmanager/cmd/eotsd/daemon/pop.go index 3bf7fea3..84c6ba91 100644 --- a/eotsmanager/cmd/eotsd/daemon/pop.go +++ b/eotsmanager/cmd/eotsd/daemon/pop.go @@ -208,6 +208,20 @@ func exportPop(cmd *cobra.Command, _ []string) error { return nil } +func VerifyPopExport(pop PoPExport) (valid bool, err error) { + valid, err = VerifyEotsSignBaby(pop.EotsPublicKey, pop.BabyAddress, pop.EotsSignBaby) + if err != nil { + return false, err + } + + return VerifyBabySignEots( + pop.BabyPublicKey, + pop.BabyAddress, + pop.EotsPublicKey, + pop.BabySignEotsPk, + ) +} + func VerifyEotsSignBaby(eotsPk, babyAddr, eotsSigOverBabyAddr string) (valid bool, err error) { eotsPubKey, err := bbntypes.NewBIP340PubKeyFromHex(eotsPk) if err != nil { diff --git a/eotsmanager/cmd/eotsd/daemon/pop_test.go b/eotsmanager/cmd/eotsd/daemon/pop_test.go index 274bae9a..78dc7fb0 100644 --- a/eotsmanager/cmd/eotsd/daemon/pop_test.go +++ b/eotsmanager/cmd/eotsd/daemon/pop_test.go @@ -39,3 +39,10 @@ func TestPoPVerifyBabySignEotsPk(t *testing.T) { require.NoError(t, err) require.True(t, valid) } + +func TestPoPVerify(t *testing.T) { + t.Parallel() + valid, err := daemon.VerifyPopExport(hardcodedPopToVerify) + require.NoError(t, err) + require.True(t, valid) +} From 0793189f293a461bdaab9cd5784c519136cfa1e9 Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Mon, 6 Jan 2025 15:02:16 -0300 Subject: [PATCH 17/21] chore: draft spec --- docs/pop_format_spec.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 docs/pop_format_spec.md diff --git a/docs/pop_format_spec.md b/docs/pop_format_spec.md new file mode 100644 index 00000000..bf0de3c2 --- /dev/null +++ b/docs/pop_format_spec.md @@ -0,0 +1,32 @@ +# Proof of Possession + +This PoP (Proof of Possession) has a goal proves that one EOTS +key pair owener, also owns an BABY key pair. + +This leads us to the need of 5 attributes to validate it all, the structure +[`PoPExport`](https://github.com/babylonlabs-io/finality-provider/blob/56f7a3908402f0ffb895d92fe692219540f80e69/eotsmanager/cmd/eotsd/daemon/pop.go#L36) +exposes: + +- `EotsPublicKey` is the finality provider EOTS public key marshal as hex. +- `BabyPublicKey` is the secp256k1 public key marshal as hex. +- `BabyAddress` is the BABY address with `bbn` prefix. +- `EotsSignBaby` is the schnorr signature that the private key of the EOTS +public key signed the `sha256(BabyAddress)` in base 64. +- `BabySignEotsPk` is the cosmos +[ADR-036](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-036-arbitrary-signature.md) +compatible signature of the BABY private key over the `EotsPublicKey` in base 64. + +One example of JSON representation structure of the `PoPExport` is the following: + +```json +{ + "eotsPublicKey": "3d0bebcbe800236ce8603c5bb1ab6c2af0932e947db4956a338f119797c37f1e", + "babyPublicKey": "A0V6yw74EdvoAWVauFqkH/GVM9YIpZitZf6bVEzG69tT", + "babySignBtc": "AOoIG2cwC2IMiJL3OL0zLEIUY201X1qKumDr/1qDJ4oQvAp78W1nb5EnVasRPQ/XrKXqudUDnZFprLd0jaRJtQ==", + "btcSignBaby": "pR6vxgU0gXq+VqO+y7dHpZgHTz3zr5hdqXXh0WcWNkqUnRjHrizhYAHDMV8gh4vks4PqzKAIgZ779Wqwf5UrXQ==", + "babyAddress": "bbn1f04czxeqprn0s9fe7kdzqyde2e6nqj63dllwsm" +} +``` + +Function to validate the `PoPExport` was created as +[`VerifyPopExport`](https://github.com/babylonlabs-io/finality-provider/blob/cc07bcd4dc434f7095668724aad6865bffe425e0/eotsmanager/cmd/eotsd/daemon/pop.go#L211). From 6135525af795b220a9a7c86cf09087a9b2ca19b9 Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Mon, 6 Jan 2025 15:12:05 -0300 Subject: [PATCH 18/21] chore: modify spec to reflect new json naming --- docs/pop_format_spec.md | 70 ++++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 18 deletions(-) diff --git a/docs/pop_format_spec.md b/docs/pop_format_spec.md index bf0de3c2..49b1d1dc 100644 --- a/docs/pop_format_spec.md +++ b/docs/pop_format_spec.md @@ -1,32 +1,66 @@ -# Proof of Possession +# Proof of Possession (PoP) Specification -This PoP (Proof of Possession) has a goal proves that one EOTS -key pair owener, also owns an BABY key pair. +## Overview -This leads us to the need of 5 attributes to validate it all, the structure -[`PoPExport`](https://github.com/babylonlabs-io/finality-provider/blob/56f7a3908402f0ffb895d92fe692219540f80e69/eotsmanager/cmd/eotsd/daemon/pop.go#L36) -exposes: +The Proof of Possession (PoP) mechanism verifies that the owner of an EOTS key +pair also controls a BABY key pair. This validation requires five essential +attributes, which are exposed by the `PoPExport` structure. -- `EotsPublicKey` is the finality provider EOTS public key marshal as hex. -- `BabyPublicKey` is the secp256k1 public key marshal as hex. -- `BabyAddress` is the BABY address with `bbn` prefix. -- `EotsSignBaby` is the schnorr signature that the private key of the EOTS -public key signed the `sha256(BabyAddress)` in base 64. -- `BabySignEotsPk` is the cosmos +## Attributes + +The `PoPExport` structure is defined +[here](https://github.com/babylonlabs-io/finality-provider/blob/cc07bcd4dc434f7095668724aad6865bffe425e0/eotsmanager/cmd/eotsd/daemon/pop.go#L36). +It contains the following fields: + +- `EotsPublicKey` – The EOTS public key of the finality provider, marshaled as +a hexadecimal string. This value represents the public component of the EOTS +key pair used in the signing process. +- `BabyPublicKey` – The secp256k1 public key, marshaled as a base64 string. +This key is extracted from the BABY keyring and uniquely identifies the BABY +key pair. +- `BabyAddress` – The BABY address, prefixed with `bbn`. It is derived from the +`BabyPublicKey` and used as the primary identifier on the Babylon network. +- `EotsSignBaby` – A Schnorr signature, created by signing the +`sha256(BabyAddress)` with the private key corresponding to the EOTS public +key. Encoded in base64, this ensures that the EOTS key can verify ownership +of the BABY address. +- `BabySignEotsPk` – A signature of the `EotsPublicKey`, created by the BABY +private key. This signature follows the Cosmos [ADR-036](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-036-arbitrary-signature.md) -compatible signature of the BABY private key over the `EotsPublicKey` in base 64. +specification and is encoded in base64. It demonstrates that the BABY key pair +acknowledges and signs the EOTS public key. -One example of JSON representation structure of the `PoPExport` is the following: +## Example + +Below is an example JSON representation of the PoPExport structure: ```json { "eotsPublicKey": "3d0bebcbe800236ce8603c5bb1ab6c2af0932e947db4956a338f119797c37f1e", "babyPublicKey": "A0V6yw74EdvoAWVauFqkH/GVM9YIpZitZf6bVEzG69tT", - "babySignBtc": "AOoIG2cwC2IMiJL3OL0zLEIUY201X1qKumDr/1qDJ4oQvAp78W1nb5EnVasRPQ/XrKXqudUDnZFprLd0jaRJtQ==", - "btcSignBaby": "pR6vxgU0gXq+VqO+y7dHpZgHTz3zr5hdqXXh0WcWNkqUnRjHrizhYAHDMV8gh4vks4PqzKAIgZ779Wqwf5UrXQ==", + "babySignEotsPk": "AOoIG2cwC2IMiJL3OL0zLEIUY201X1qKumDr/1qDJ4oQvAp78W1nb5EnVasRPQ/XrKXqudUDnZFprLd0jaRJtQ==", + "eotsSignBaby": "pR6vxgU0gXq+VqO+y7dHpZgHTz3zr5hdqXXh0WcWNkqUnRjHrizhYAHDMV8gh4vks4PqzKAIgZ779Wqwf5UrXQ==", "babyAddress": "bbn1f04czxeqprn0s9fe7kdzqyde2e6nqj63dllwsm" } ``` -Function to validate the `PoPExport` was created as -[`VerifyPopExport`](https://github.com/babylonlabs-io/finality-provider/blob/cc07bcd4dc434f7095668724aad6865bffe425e0/eotsmanager/cmd/eotsd/daemon/pop.go#L211). +## Validation + +The function responsible for validating the `PoPExport` is `VerifyPopExport`, +which can be found [here](https://github.com/babylonlabs-io/finality-provider/blob/cc07bcd4dc434f7095668724aad6865bffe425e0/eotsmanager/cmd/eotsd/daemon/pop.go#L211). + +`VerifyPopExport` ensures the authenticity and integrity of the `PoPExport` +by cross-verifying the provided signatures and public keys. This process +consists of two core validation steps: + +- `VerifyEotsSignBaby` – This function checks the validity of the Schnorr +signature `(EotsSignBaby)` by verifying that the EOTS private key has correctly +signed the SHA256 hash of the BABY address. +- `VerifyBabySignEots` – This function confirms that the BABY private key has +signed the EOTS public key `(EotsPublicKey)`, ensuring mutual validation +between the key pairs. + +If both signatures pass verification, the export is deemed valid, confirming +that the finality provider holds both key pairs. This function plays a critical +role in maintaining trust and security in the finality provider's key +management process. From a77fc9fe5e4a4dd66cfe343d86e3ad61508b099b Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Mon, 6 Jan 2025 15:22:54 -0300 Subject: [PATCH 19/21] fix: ineffectual assignment --- eotsmanager/cmd/eotsd/daemon/pop.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eotsmanager/cmd/eotsd/daemon/pop.go b/eotsmanager/cmd/eotsd/daemon/pop.go index 84c6ba91..b39e535b 100644 --- a/eotsmanager/cmd/eotsd/daemon/pop.go +++ b/eotsmanager/cmd/eotsd/daemon/pop.go @@ -210,7 +210,7 @@ func exportPop(cmd *cobra.Command, _ []string) error { func VerifyPopExport(pop PoPExport) (valid bool, err error) { valid, err = VerifyEotsSignBaby(pop.EotsPublicKey, pop.BabyAddress, pop.EotsSignBaby) - if err != nil { + if err != nil || !valid { return false, err } From c1a77bfbb54b9ac0891c2f3b1b39ae0f88885261 Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Mon, 6 Jan 2025 16:50:29 -0300 Subject: [PATCH 20/21] chore: address pr comments --- docs/pop_format_spec.md | 58 +++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/docs/pop_format_spec.md b/docs/pop_format_spec.md index 49b1d1dc..031c3c52 100644 --- a/docs/pop_format_spec.md +++ b/docs/pop_format_spec.md @@ -2,33 +2,47 @@ ## Overview -The Proof of Possession (PoP) mechanism verifies that the owner of an EOTS key -pair also controls a BABY key pair. This validation requires five essential -attributes, which are exposed by the `PoPExport` structure. +The Proof of Possession (PoP) structured specification outlined in this +document allows for the verification of the mutual ownership of a Babylon +key pair and an EOTS key pair. In the following, we outline the five essential +attributes exposed by the `PoPExport` structure and provide examples and +validation procedures. ## Attributes -The `PoPExport` structure is defined -[here](https://github.com/babylonlabs-io/finality-provider/blob/cc07bcd4dc434f7095668724aad6865bffe425e0/eotsmanager/cmd/eotsd/daemon/pop.go#L36). -It contains the following fields: - -- `EotsPublicKey` – The EOTS public key of the finality provider, marshaled as -a hexadecimal string. This value represents the public component of the EOTS -key pair used in the signing process. -- `BabyPublicKey` – The secp256k1 public key, marshaled as a base64 string. -This key is extracted from the BABY keyring and uniquely identifies the BABY -key pair. -- `BabyAddress` – The BABY address, prefixed with `bbn`. It is derived from the -`BabyPublicKey` and used as the primary identifier on the Babylon network. -- `EotsSignBaby` – A Schnorr signature, created by signing the -`sha256(BabyAddress)` with the private key corresponding to the EOTS public -key. Encoded in base64, this ensures that the EOTS key can verify ownership -of the BABY address. -- `BabySignEotsPk` – A signature of the `EotsPublicKey`, created by the BABY +The `PoPExport` structure is defined bellow: + +```go +// PoPExport the data needed to prove ownership of the eots and babylon key pairs. +type PoPExport struct { + // Btc public key is the EOTS PK *bbntypes.BIP340PubKey marshal hex + EotsPublicKey string `json:"eotsPublicKey"` + // Babylon public key is the *secp256k1.PubKey marshal hex + BabyPublicKey string `json:"babyPublicKey"` + + // Babylon key pair signs EOTS public key as hex + BabySignEotsPk string `json:"babySignEotsPk"` + // Schnorr signature of EOTS private key over the SHA256(Baby address) + EotsSignBaby string `json:"eotsSignBaby"` + + // Babylon address ex.: bbn1f04czxeqprn0s9fe7kdzqyde2e6nqj63dllwsm + BabyAddress string `json:"babyAddress"` +} +``` + +Detailed specification of each field: + +- `EotsPublicKey`: The EOTS public key of the finality provider in hexadecimal format. +- `BabyPublicKey` – The Babylon secp256k1 public key in base64 format. +- `BabyAddress` – The Babylon account address (`bbn` prefix). The address is +derived from the `BabyPublicKey` and used as the primary identifier on the +Babylon network. +- `EotsSignBaby` – A Schnorr signature in base64 format, created by signing the +`sha256(BabyAddress)` with the EOTS private key. +- `BabySignEotsPk` – A signature of the `EotsPublicKey`, created by the Babylon private key. This signature follows the Cosmos [ADR-036](https://github.com/cosmos/cosmos-sdk/blob/main/docs/architecture/adr-036-arbitrary-signature.md) -specification and is encoded in base64. It demonstrates that the BABY key pair -acknowledges and signs the EOTS public key. +specification and is encoded in base64. ## Example From c409bef88a1013ab5574d64ace797572d31b332e Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Tue, 7 Jan 2025 09:02:02 -0300 Subject: [PATCH 21/21] chore: rollback nonamedreturns and modify previr of Verify to Valid --- .golangci.yml | 2 +- docs/pop_format_spec.md | 4 ++-- eotsmanager/cmd/eotsd/daemon/pop.go | 10 +++++----- eotsmanager/cmd/eotsd/daemon/pop_test.go | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 7c6a40c2..2d230c01 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -39,7 +39,7 @@ linters: - nilerr - nlreturn - noctx - # - nonamedreturns named returns are good + - nonamedreturns - nosprintfhostport - paralleltest - reassign diff --git a/docs/pop_format_spec.md b/docs/pop_format_spec.md index 031c3c52..0a390be7 100644 --- a/docs/pop_format_spec.md +++ b/docs/pop_format_spec.md @@ -67,10 +67,10 @@ which can be found [here](https://github.com/babylonlabs-io/finality-provider/bl by cross-verifying the provided signatures and public keys. This process consists of two core validation steps: -- `VerifyEotsSignBaby` – This function checks the validity of the Schnorr +- `ValidEotsSignBaby` – This function checks the validity of the Schnorr signature `(EotsSignBaby)` by verifying that the EOTS private key has correctly signed the SHA256 hash of the BABY address. -- `VerifyBabySignEots` – This function confirms that the BABY private key has +- `ValidBabySignEots` – This function confirms that the BABY private key has signed the EOTS public key `(EotsPublicKey)`, ensuring mutual validation between the key pairs. diff --git a/eotsmanager/cmd/eotsd/daemon/pop.go b/eotsmanager/cmd/eotsd/daemon/pop.go index b39e535b..5891d5f8 100644 --- a/eotsmanager/cmd/eotsd/daemon/pop.go +++ b/eotsmanager/cmd/eotsd/daemon/pop.go @@ -208,13 +208,13 @@ func exportPop(cmd *cobra.Command, _ []string) error { return nil } -func VerifyPopExport(pop PoPExport) (valid bool, err error) { - valid, err = VerifyEotsSignBaby(pop.EotsPublicKey, pop.BabyAddress, pop.EotsSignBaby) +func VerifyPopExport(pop PoPExport) (bool, error) { + valid, err := ValidEotsSignBaby(pop.EotsPublicKey, pop.BabyAddress, pop.EotsSignBaby) if err != nil || !valid { return false, err } - return VerifyBabySignEots( + return ValidBabySignEots( pop.BabyPublicKey, pop.BabyAddress, pop.EotsPublicKey, @@ -222,7 +222,7 @@ func VerifyPopExport(pop PoPExport) (valid bool, err error) { ) } -func VerifyEotsSignBaby(eotsPk, babyAddr, eotsSigOverBabyAddr string) (valid bool, err error) { +func ValidEotsSignBaby(eotsPk, babyAddr, eotsSigOverBabyAddr string) (bool, error) { eotsPubKey, err := bbntypes.NewBIP340PubKeyFromHex(eotsPk) if err != nil { return false, err @@ -242,7 +242,7 @@ func VerifyEotsSignBaby(eotsPk, babyAddr, eotsSigOverBabyAddr string) (valid boo return schnorrSig.Verify(sha256Addr, eotsPubKey.MustToBTCPK()), nil } -func VerifyBabySignEots(babyPk, babyAddr, eotsPk, babySigOverEotsPk string) (valid bool, err error) { +func ValidBabySignEots(babyPk, babyAddr, eotsPk, babySigOverEotsPk string) (bool, error) { babyPubKeyBz, err := base64.StdEncoding.DecodeString(babyPk) if err != nil { return false, err diff --git a/eotsmanager/cmd/eotsd/daemon/pop_test.go b/eotsmanager/cmd/eotsd/daemon/pop_test.go index 78dc7fb0..8a9627dc 100644 --- a/eotsmanager/cmd/eotsd/daemon/pop_test.go +++ b/eotsmanager/cmd/eotsd/daemon/pop_test.go @@ -17,9 +17,9 @@ var hardcodedPopToVerify daemon.PoPExport = daemon.PoPExport{ BabyAddress: "bbn1f04czxeqprn0s9fe7kdzqyde2e6nqj63dllwsm", } -func TestPoPVerifyEotsSignBaby(t *testing.T) { +func TestPoPValidEotsSignBaby(t *testing.T) { t.Parallel() - valid, err := daemon.VerifyEotsSignBaby( + valid, err := daemon.ValidEotsSignBaby( hardcodedPopToVerify.EotsPublicKey, hardcodedPopToVerify.BabyAddress, hardcodedPopToVerify.EotsSignBaby, @@ -28,9 +28,9 @@ func TestPoPVerifyEotsSignBaby(t *testing.T) { require.True(t, valid) } -func TestPoPVerifyBabySignEotsPk(t *testing.T) { +func TestPoPValidBabySignEotsPk(t *testing.T) { t.Parallel() - valid, err := daemon.VerifyBabySignEots( + valid, err := daemon.ValidBabySignEots( hardcodedPopToVerify.BabyPublicKey, hardcodedPopToVerify.BabyAddress, hardcodedPopToVerify.EotsPublicKey,