From ad1a70cc4dfd392d7da0ce6cefc48dc0d7b66d5a Mon Sep 17 00:00:00 2001 From: Lazar Date: Mon, 9 Dec 2024 15:25:53 +0100 Subject: [PATCH 1/6] create fp from file --- .../cmd/fpd/daemon/daemon_commands.go | 294 +++++++++++++----- finality-provider/cmd/fpd/daemon/flags.go | 1 + itest/e2e_test.go | 71 ++++- 3 files changed, 292 insertions(+), 74 deletions(-) diff --git a/finality-provider/cmd/fpd/daemon/daemon_commands.go b/finality-provider/cmd/fpd/daemon/daemon_commands.go index a943c82..03f8e80 100644 --- a/finality-provider/cmd/fpd/daemon/daemon_commands.go +++ b/finality-provider/cmd/fpd/daemon/daemon_commands.go @@ -5,7 +5,9 @@ import ( "encoding/hex" "encoding/json" "fmt" + "os" "strconv" + "strings" "cosmossdk.io/math" "github.com/babylonlabs-io/babylon/types" @@ -74,9 +76,33 @@ func CommandCreateFP() *cobra.Command { Short: "Create a finality provider object and save it in database.", Long: "Create a new finality provider object and store it in the finality provider database. " + "It needs to have an operating EOTS manager available and running.", - Example: fmt.Sprintf(`fpd create-finality-provider --daemon-address %s ...`, defaultFpdDaemonAddress), - Args: cobra.NoArgs, - RunE: fpcmd.RunEWithClientCtx(runCommandCreateFP), + Example: strings.TrimSpace( + fmt.Sprintf(` +Either by specifying all flags manually: + +$fpd create-finality-provider --daemon-address %s ... + +Or providing the path to finality-provider.json: +$fpd create-finality-provider path/to/finality-provider.json + +Where finality-provider.json contains: + +{ + "daemonAddress": "The RPC server address of fpd (e.g. 127.0.0.1:12581)", + "keyName": "The unique key name of the finality provider's Babylon account", + "chainID": "The identifier of the consumer chain", + "passphrase": "The pass phrase used to encrypt the keys", + "commissionRate": "The commission rate for the finality provider, e.g., 0.05"", + "moniker": ""A human-readable name for the finality provider", + "identity": "A optional identity signature", + "website": "Validator's (optional) website", + "securityContract": "Validator's (optional) security contact email", + "details": "Validator's (optional) details", + "eotsPK": "The hex string of the finality provider's EOTS public key" +} +`, defaultFpdDaemonAddress)), + Args: cobra.NoArgs, + RunE: fpcmd.RunEWithClientCtx(runCommandCreateFP), } f := cmd.Flags() @@ -92,22 +118,29 @@ func CommandCreateFP() *cobra.Command { f.String(securityContactFlag, "", "An email for security contact") f.String(detailsFlag, "", "Other optional details") f.String(fpEotsPkFlag, "", "The hex string of the finality provider's EOTS public key") - - // make flags required - if err := cmd.MarkFlagRequired(chainIDFlag); err != nil { - panic(err) - } - if err := cmd.MarkFlagRequired(keyNameFlag); err != nil { - panic(err) - } - if err := cmd.MarkFlagRequired(monikerFlag); err != nil { - panic(err) - } - if err := cmd.MarkFlagRequired(commissionRateFlag); err != nil { - panic(err) - } - if err := cmd.MarkFlagRequired(fpEotsPkFlag); err != nil { - panic(err) + f.String(fromFile, "", "Path to a json file containing finality provider data") + + cmd.PreRunE = func(cmd *cobra.Command, args []string) error { + fromFilePath, _ := cmd.Flags().GetString(fromFile) + if fromFilePath == "" { + // Mark flags as required only if --from-file is not provided + if err := cmd.MarkFlagRequired(chainIDFlag); err != nil { + return err + } + if err := cmd.MarkFlagRequired(keyNameFlag); err != nil { + return err + } + if err := cmd.MarkFlagRequired(monikerFlag); err != nil { + return err + } + if err := cmd.MarkFlagRequired(commissionRateFlag); err != nil { + return err + } + if err := cmd.MarkFlagRequired(fpEotsPkFlag); err != nil { + return err + } + } + return nil } return cmd @@ -115,35 +148,26 @@ func CommandCreateFP() *cobra.Command { func runCommandCreateFP(ctx client.Context, cmd *cobra.Command, _ []string) error { flags := cmd.Flags() - daemonAddress, err := flags.GetString(fpdDaemonAddressFlag) - if err != nil { - return fmt.Errorf("failed to read flag %s: %w", fpdDaemonAddressFlag, err) - } - commissionRateStr, err := flags.GetString(commissionRateFlag) + fpJsonPath, err := flags.GetString(fromFile) if err != nil { - return fmt.Errorf("failed to read flag %s: %w", commissionRateFlag, err) - } - commissionRate, err := math.LegacyNewDecFromStr(commissionRateStr) - if err != nil { - return fmt.Errorf("invalid commission rate: %w", err) - } - - description, err := getDescriptionFromFlags(flags) - if err != nil { - return fmt.Errorf("invalid description: %w", err) - } - - keyName, err := loadKeyName(ctx.HomeDir, cmd) - if err != nil { - return fmt.Errorf("not able to load key name: %w", err) + return fmt.Errorf("failed to read flag %s: %w", fromFile, err) } - if keyName == "" { - return fmt.Errorf("keyname cannot be empty") + var fp *parsedFinalityProvider + if fpJsonPath != "" { + fp, err = parseFinalityProviderJSON(fpJsonPath, ctx.HomeDir) + if err != nil { + panic(err) + } + } else { + fp, err = parseFinalityProviderFlags(cmd, ctx.HomeDir) + if err != nil { + panic(err) + } } - client, cleanUp, err := dc.NewFinalityProviderServiceGRpcClient(daemonAddress) + client, cleanUp, err := dc.NewFinalityProviderServiceGRpcClient(fp.daemonAddress) if err != nil { return err } @@ -153,37 +177,14 @@ func runCommandCreateFP(ctx client.Context, cmd *cobra.Command, _ []string) erro } }() - chainID, err := flags.GetString(chainIDFlag) - if err != nil { - return fmt.Errorf("failed to read flag %s: %w", chainIDFlag, err) - } - - if chainID == "" { - return fmt.Errorf("chain-id cannot be empty") - } - - passphrase, err := flags.GetString(passphraseFlag) - if err != nil { - return fmt.Errorf("failed to read flag %s: %w", passphraseFlag, err) - } - - eotsPkHex, err := flags.GetString(fpEotsPkFlag) - if err != nil { - return fmt.Errorf("failed to read flag %s: %w", fpEotsPkFlag, err) - } - - if eotsPkHex == "" { - return fmt.Errorf("eots-pk cannot be empty") - } - res, err := client.CreateFinalityProvider( context.Background(), - keyName, - chainID, - eotsPkHex, - passphrase, - description, - &commissionRate, + fp.keyName, + fp.chainID, + fp.eotsPK, + fp.passphrase, + fp.description, + &fp.commissionRate, ) if err != nil { return err @@ -516,3 +517,154 @@ func loadKeyName(homeDir string, cmd *cobra.Command) (string, error) { } return keyName, nil } + +type parsedFinalityProvider struct { + keyName string + chainID string + eotsPK string + passphrase string + daemonAddress string + description stakingtypes.Description + commissionRate math.LegacyDec +} + +func parseFinalityProviderJSON(path string, homeDir string) (*parsedFinalityProvider, error) { + type internalFpJSON struct { + KeyName string `json:"keyName"` + ChainID string `json:"chainID"` + Passphrase string `json:"passphrase"` + CommissionRate string `json:"commissionRate"` + Moniker string `json:"moniker"` + Identity string `json:"identity"` + Website string `json:"website"` + SecurityContract string `json:"securityContract"` + Details string `json:"details"` + EotsPK string `json:"eotsPK"` + DaemonAddress string `json:"daemonAddress"` + } + + contents, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + var fp internalFpJSON + if err := json.Unmarshal(contents, &fp); err != nil { + return nil, err + } + + if fp.DaemonAddress == "" { + return nil, fmt.Errorf("daemonAddress is required") + } + + if fp.ChainID == "" { + return nil, fmt.Errorf("chainID is required") + } + + if fp.KeyName == "" { + cfg, err := fpcfg.LoadConfig(homeDir) + if err != nil { + return nil, fmt.Errorf("failed to load config from %s: %w", fpcfg.CfgFile(homeDir), err) + } + fp.KeyName = cfg.BabylonConfig.Key + if fp.KeyName == "" { + return nil, fmt.Errorf("the key is neither in config nor provided in the json file") + } + } + + if fp.Moniker == "" { + return nil, fmt.Errorf("moniker is required") + } + + if fp.CommissionRate == "" { + return nil, fmt.Errorf("commissionRate is required") + } + + if fp.EotsPK == "" { + return nil, fmt.Errorf("eotsPK is required") + } + + commissionRate, err := math.LegacyNewDecFromStr(fp.CommissionRate) + if err != nil { + return nil, fmt.Errorf("invalid commission rate: %w", err) + } + + description, err := stakingtypes.NewDescription(fp.Moniker, fp.Identity, fp.Website, fp.SecurityContract, fp.Details).EnsureLength() + if err != nil { + return nil, err + } + + return &parsedFinalityProvider{ + keyName: fp.KeyName, + chainID: fp.ChainID, + eotsPK: fp.EotsPK, + passphrase: fp.Passphrase, + description: description, + commissionRate: commissionRate, + daemonAddress: fp.DaemonAddress, + }, nil +} + +func parseFinalityProviderFlags(cmd *cobra.Command, homeDir string) (*parsedFinalityProvider, error) { + flags := cmd.Flags() + daemonAddress, err := flags.GetString(fpdDaemonAddressFlag) + if err != nil { + return nil, fmt.Errorf("failed to read flag %s: %w", fpdDaemonAddressFlag, err) + } + + commissionRateStr, err := flags.GetString(commissionRateFlag) + if err != nil { + return nil, fmt.Errorf("failed to read flag %s: %w", commissionRateFlag, err) + } + commissionRate, err := math.LegacyNewDecFromStr(commissionRateStr) + if err != nil { + return nil, fmt.Errorf("invalid commission rate: %w", err) + } + + description, err := getDescriptionFromFlags(flags) + if err != nil { + return nil, fmt.Errorf("invalid description: %w", err) + } + + keyName, err := loadKeyName(homeDir, cmd) + if err != nil { + return nil, fmt.Errorf("not able to load key name: %w", err) + } + + if keyName == "" { + return nil, fmt.Errorf("keyname cannot be empty") + } + + chainID, err := flags.GetString(chainIDFlag) + if err != nil { + return nil, fmt.Errorf("failed to read flag %s: %w", chainIDFlag, err) + } + + if chainID == "" { + return nil, fmt.Errorf("chain-id cannot be empty") + } + + passphrase, err := flags.GetString(passphraseFlag) + if err != nil { + return nil, fmt.Errorf("failed to read flag %s: %w", passphraseFlag, err) + } + + eotsPkHex, err := flags.GetString(fpEotsPkFlag) + if err != nil { + return nil, fmt.Errorf("failed to read flag %s: %w", fpEotsPkFlag, err) + } + + if eotsPkHex == "" { + return nil, fmt.Errorf("eots-pk cannot be empty") + } + + return &parsedFinalityProvider{ + keyName: keyName, + chainID: chainID, + eotsPK: eotsPkHex, + passphrase: passphrase, + description: description, + commissionRate: commissionRate, + daemonAddress: daemonAddress, + }, nil +} diff --git a/finality-provider/cmd/fpd/daemon/flags.go b/finality-provider/cmd/fpd/daemon/flags.go index e20a2fe..4a343a2 100644 --- a/finality-provider/cmd/fpd/daemon/flags.go +++ b/finality-provider/cmd/fpd/daemon/flags.go @@ -12,6 +12,7 @@ const ( chainIDFlag = "chain-id" signedFlag = "signed" checkDoubleSignFlag = "check-double-sign" + fromFile = "from-file" // flags for description monikerFlag = "moniker" diff --git a/itest/e2e_test.go b/itest/e2e_test.go index a7f6b66..63f608d 100644 --- a/itest/e2e_test.go +++ b/itest/e2e_test.go @@ -1,10 +1,12 @@ -//go:build e2e -// +build e2e - package e2etest import ( + "encoding/json" + "fmt" + bbntypes "github.com/babylonlabs-io/babylon/types" + "log" "math/rand" + "os" "testing" "time" @@ -240,3 +242,66 @@ func TestFinalityProviderEditCmd(t *testing.T) { require.Equal(t, updateFpDesc.SecurityContact, oldDesc.SecurityContact) require.Equal(t, updatedFp.FinalityProvider.Commission, &rate) } + +func TestFinalityProviderCreateCmd(t *testing.T) { + tm, _ := StartManagerWithFinalityProvider(t) + defer tm.Stop(t) + + cmd := daemon.CommandCreateFP() + + eotsKeyName := "eots-key-2" + eotsPkBz, err := tm.EOTSClient.CreateKey(eotsKeyName, passphrase, hdPath) + require.NoError(t, err) + eotsPk, err := bbntypes.NewBIP340PubKey(eotsPkBz) + require.NoError(t, err) + + data := struct { + KeyName string `json:"keyName"` + ChainID string `json:"chainID"` + Passphrase string `json:"passphrase"` + CommissionRate string `json:"commissionRate"` + Moniker string `json:"moniker"` + Identity string `json:"identity"` + Website string `json:"website"` + SecurityContract string `json:"securityContract"` + Details string `json:"details"` + EotsPK string `json:"eotsPK"` + DaemonAddress string `json:"daemonAddress"` + }{ + KeyName: tm.FpConfig.BabylonConfig.Key, + ChainID: testChainID, + Passphrase: passphrase, + CommissionRate: "0.10", + Moniker: "some moniker", + Identity: "F123456789ABCDEF", + Website: "https://fp.example.com", + SecurityContract: "https://fp.example.com/security", + Details: "This is a highly secure and reliable fp.", + EotsPK: eotsPk.MarshalHex(), + DaemonAddress: tm.FpConfig.RPCListener, + } + + file, err := os.Create(fmt.Sprintf("%s/%s", t.TempDir(), "finality-provider.json")) + if err != nil { + log.Fatalf("Failed to create file: %v", err) + } + t.Cleanup(func() { + _ = os.Remove(file.Name()) + }) + + if err := json.NewEncoder(file).Encode(data); err != nil { + log.Fatalf("Failed to write JSON to file: %v", err) + } + + cmd.SetArgs([]string{ + "--from-file=" + file.Name(), + }) + + // Run the command + err = cmd.Execute() + require.NoError(t, err) + + fp, err := tm.BBNClient.QueryFinalityProvider(eotsPk.MustToBTCPK()) + require.NoError(t, err) + require.NotNil(t, fp) +} From 6efd879999896a84eee0761fa6b5f61ef1aafab6 Mon Sep 17 00:00:00 2001 From: Lazar Date: Mon, 9 Dec 2024 15:27:52 +0100 Subject: [PATCH 2/6] lint --- finality-provider/cmd/fpd/daemon/daemon_commands.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/finality-provider/cmd/fpd/daemon/daemon_commands.go b/finality-provider/cmd/fpd/daemon/daemon_commands.go index 03f8e80..bebe3cd 100644 --- a/finality-provider/cmd/fpd/daemon/daemon_commands.go +++ b/finality-provider/cmd/fpd/daemon/daemon_commands.go @@ -120,7 +120,7 @@ Where finality-provider.json contains: f.String(fpEotsPkFlag, "", "The hex string of the finality provider's EOTS public key") f.String(fromFile, "", "Path to a json file containing finality provider data") - cmd.PreRunE = func(cmd *cobra.Command, args []string) error { + cmd.PreRunE = func(cmd *cobra.Command, _ []string) error { fromFilePath, _ := cmd.Flags().GetString(fromFile) if fromFilePath == "" { // Mark flags as required only if --from-file is not provided @@ -149,14 +149,14 @@ Where finality-provider.json contains: func runCommandCreateFP(ctx client.Context, cmd *cobra.Command, _ []string) error { flags := cmd.Flags() - fpJsonPath, err := flags.GetString(fromFile) + fpJSONPath, err := flags.GetString(fromFile) if err != nil { return fmt.Errorf("failed to read flag %s: %w", fromFile, err) } var fp *parsedFinalityProvider - if fpJsonPath != "" { - fp, err = parseFinalityProviderJSON(fpJsonPath, ctx.HomeDir) + if fpJSONPath != "" { + fp, err = parseFinalityProviderJSON(fpJSONPath, ctx.HomeDir) if err != nil { panic(err) } From 01418db70dbdcee314051a02e6f50fce799240ae Mon Sep 17 00:00:00 2001 From: Lazar Date: Mon, 9 Dec 2024 15:28:53 +0100 Subject: [PATCH 3/6] changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee51162..bec28b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ## Unreleased +### Improvements + +* [#207](https://github.com/babylonlabs-io/finality-provider/pull/207) create finality provider from JSON file + ## v0.13.1 ### Bug Fixes From 4e964990c396d070687534446b67a9755fe85f5a Mon Sep 17 00:00:00 2001 From: Lazar Date: Mon, 9 Dec 2024 15:49:46 +0100 Subject: [PATCH 4/6] nosec --- finality-provider/cmd/fpd/daemon/daemon_commands.go | 1 + 1 file changed, 1 insertion(+) diff --git a/finality-provider/cmd/fpd/daemon/daemon_commands.go b/finality-provider/cmd/fpd/daemon/daemon_commands.go index bebe3cd..bfcae24 100644 --- a/finality-provider/cmd/fpd/daemon/daemon_commands.go +++ b/finality-provider/cmd/fpd/daemon/daemon_commands.go @@ -543,6 +543,7 @@ func parseFinalityProviderJSON(path string, homeDir string) (*parsedFinalityProv DaemonAddress string `json:"daemonAddress"` } + // #nosec G304 - The log file path is provided by the user and not externally contents, err := os.ReadFile(path) if err != nil { return nil, err From 07ec3955035c0f8871f1132841e71345f12e5406 Mon Sep 17 00:00:00 2001 From: Lazar Date: Tue, 10 Dec 2024 10:52:45 +0100 Subject: [PATCH 5/6] pr comment dmn addr --- finality-provider/cmd/fpd/daemon/daemon_commands.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/finality-provider/cmd/fpd/daemon/daemon_commands.go b/finality-provider/cmd/fpd/daemon/daemon_commands.go index bfcae24..9c145f9 100644 --- a/finality-provider/cmd/fpd/daemon/daemon_commands.go +++ b/finality-provider/cmd/fpd/daemon/daemon_commands.go @@ -167,6 +167,16 @@ func runCommandCreateFP(ctx client.Context, cmd *cobra.Command, _ []string) erro } } + // read flag regardless + daemonAddress, err := flags.GetString(fpdDaemonAddressFlag) + if err != nil { + return fmt.Errorf("failed to read flag %s: %w", fpdDaemonAddressFlag, err) + } + + if daemonAddress != "" { + fp.daemonAddress = daemonAddress + } + client, cleanUp, err := dc.NewFinalityProviderServiceGRpcClient(fp.daemonAddress) if err != nil { return err From aaef3a6ae0fad793b50174ee07f3141c0a9a50e6 Mon Sep 17 00:00:00 2001 From: Lazar Date: Tue, 10 Dec 2024 11:20:06 +0100 Subject: [PATCH 6/6] amend dmn addr --- .../cmd/fpd/daemon/daemon_commands.go | 24 +++---------------- itest/e2e_test.go | 3 +-- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/finality-provider/cmd/fpd/daemon/daemon_commands.go b/finality-provider/cmd/fpd/daemon/daemon_commands.go index 9c145f9..6ed9050 100644 --- a/finality-provider/cmd/fpd/daemon/daemon_commands.go +++ b/finality-provider/cmd/fpd/daemon/daemon_commands.go @@ -83,12 +83,11 @@ Either by specifying all flags manually: $fpd create-finality-provider --daemon-address %s ... Or providing the path to finality-provider.json: -$fpd create-finality-provider path/to/finality-provider.json +$fpd create-finality-provider --daemon-address %s --from-file /path/to/finality-provider.json Where finality-provider.json contains: { - "daemonAddress": "The RPC server address of fpd (e.g. 127.0.0.1:12581)", "keyName": "The unique key name of the finality provider's Babylon account", "chainID": "The identifier of the consumer chain", "passphrase": "The pass phrase used to encrypt the keys", @@ -100,7 +99,7 @@ Where finality-provider.json contains: "details": "Validator's (optional) details", "eotsPK": "The hex string of the finality provider's EOTS public key" } -`, defaultFpdDaemonAddress)), +`, defaultFpdDaemonAddress, defaultFpdDaemonAddress)), Args: cobra.NoArgs, RunE: fpcmd.RunEWithClientCtx(runCommandCreateFP), } @@ -167,17 +166,12 @@ func runCommandCreateFP(ctx client.Context, cmd *cobra.Command, _ []string) erro } } - // read flag regardless daemonAddress, err := flags.GetString(fpdDaemonAddressFlag) if err != nil { return fmt.Errorf("failed to read flag %s: %w", fpdDaemonAddressFlag, err) } - if daemonAddress != "" { - fp.daemonAddress = daemonAddress - } - - client, cleanUp, err := dc.NewFinalityProviderServiceGRpcClient(fp.daemonAddress) + client, cleanUp, err := dc.NewFinalityProviderServiceGRpcClient(daemonAddress) if err != nil { return err } @@ -533,7 +527,6 @@ type parsedFinalityProvider struct { chainID string eotsPK string passphrase string - daemonAddress string description stakingtypes.Description commissionRate math.LegacyDec } @@ -550,7 +543,6 @@ func parseFinalityProviderJSON(path string, homeDir string) (*parsedFinalityProv SecurityContract string `json:"securityContract"` Details string `json:"details"` EotsPK string `json:"eotsPK"` - DaemonAddress string `json:"daemonAddress"` } // #nosec G304 - The log file path is provided by the user and not externally @@ -564,10 +556,6 @@ func parseFinalityProviderJSON(path string, homeDir string) (*parsedFinalityProv return nil, err } - if fp.DaemonAddress == "" { - return nil, fmt.Errorf("daemonAddress is required") - } - if fp.ChainID == "" { return nil, fmt.Errorf("chainID is required") } @@ -612,16 +600,11 @@ func parseFinalityProviderJSON(path string, homeDir string) (*parsedFinalityProv passphrase: fp.Passphrase, description: description, commissionRate: commissionRate, - daemonAddress: fp.DaemonAddress, }, nil } func parseFinalityProviderFlags(cmd *cobra.Command, homeDir string) (*parsedFinalityProvider, error) { flags := cmd.Flags() - daemonAddress, err := flags.GetString(fpdDaemonAddressFlag) - if err != nil { - return nil, fmt.Errorf("failed to read flag %s: %w", fpdDaemonAddressFlag, err) - } commissionRateStr, err := flags.GetString(commissionRateFlag) if err != nil { @@ -676,6 +659,5 @@ func parseFinalityProviderFlags(cmd *cobra.Command, homeDir string) (*parsedFina passphrase: passphrase, description: description, commissionRate: commissionRate, - daemonAddress: daemonAddress, }, nil } diff --git a/itest/e2e_test.go b/itest/e2e_test.go index 63f608d..5591a0f 100644 --- a/itest/e2e_test.go +++ b/itest/e2e_test.go @@ -266,7 +266,6 @@ func TestFinalityProviderCreateCmd(t *testing.T) { SecurityContract string `json:"securityContract"` Details string `json:"details"` EotsPK string `json:"eotsPK"` - DaemonAddress string `json:"daemonAddress"` }{ KeyName: tm.FpConfig.BabylonConfig.Key, ChainID: testChainID, @@ -278,7 +277,6 @@ func TestFinalityProviderCreateCmd(t *testing.T) { SecurityContract: "https://fp.example.com/security", Details: "This is a highly secure and reliable fp.", EotsPK: eotsPk.MarshalHex(), - DaemonAddress: tm.FpConfig.RPCListener, } file, err := os.Create(fmt.Sprintf("%s/%s", t.TempDir(), "finality-provider.json")) @@ -295,6 +293,7 @@ func TestFinalityProviderCreateCmd(t *testing.T) { cmd.SetArgs([]string{ "--from-file=" + file.Name(), + "--daemon-address=" + tm.FpConfig.RPCListener, }) // Run the command