diff --git a/README.md b/README.md index 7c77ee2e..18bba08e 100644 --- a/README.md +++ b/README.md @@ -28,41 +28,68 @@ make build-relayer-docker export COREUM_CHAIN_ID={Coreum chain id} export COREUM_GRPC_URL={Coreum GRPC URL} export XRPL_RPC_URL={XRPL RPC URL} +export RELEASE_VERSION={Relayer release version} ``` -#### Init the config +### Install relayer (with docker) + +For the document simplicity we use the alias for the command which will be executed in docker. +Pay attention that all files outputs are related to docker container. + +```bash +alias coreumbridge-xrpl-relayer="docker run --user $(id -u):$(id -g) -it --rm -v $HOME/.coreumbridge-xrpl-relayer:/root/.coreumbridge-xrpl-relayer coreumfoundation/coreumbridge-xrpl-relayer:$RELEASE_VERSION" +``` + +### Init relayer config ```bash -./coreumbridge-xrpl-relayer init --coreum-chain-id $COREUM_CHAIN_ID --coreum-grpc-url $COREUM_GRPC_URL --xrpl-rpc-url $XRPL_RPC_URL +coreumbridge-xrpl-relayer init \ + --coreum-chain-id $COREUM_CHAIN_ID \ + --coreum-grpc-url $COREUM_GRPC_URL \ + --xrpl-rpc-url $XRPL_RPC_URL ``` ## Bootstrap the bridge -### Init relayer (for each relayer) +### Init relayer -#### Pass the [Init relayer](#init-relayer) section. +#### Pass the [Init relayer](#init-relayer ) section. #### Generate the relayer keys +Generate new keys: + ```bash -./coreumbridge-xrpl-relayer keys add coreum-relayer +coreumbridge-xrpl-relayer keys-coreum add coreum-relayer -./coreumbridge-xrpl-relayer keys add xrpl-relayer +coreumbridge-xrpl-relayer keys-xrpl add xrpl-relayer ``` +!!! Save output mnemonics to a safe place to be able to recover the relayer later. !!! + The `coreum-relayer` and `xrpl-relayer` are key names set by default in the `relayer.yaml`. If for some reason you want to update them, then updated them in the `relayer.yaml` as well. -#### Extract data for the contract deployment +Or import the existing mnemonics: +```bash +coreumbridge-xrpl-relayer keys-coreum add coreum-relayer --recover + +coreumbridge-xrpl-relayer keys-xrpl add xrpl-relayer --recover +``` + +#### Extract keys info for the contract deployment ```bash -./coreumbridge-xrpl-relayer relayer-keys-info +coreumbridge-xrpl-relayer relayer-keys-info ``` Output example: ```bash -2023-12-10T18:04:55.235+0300 info cli/cli.go:205 Keys info {"coreumAddress": "core1dukhz42p4qxkrtxg8ap7nj6wn3f2lqjqwf8gny", "xrplAddress": "r3YU6MLbmnxnLwCrRQYBAbaXmBR1RgK5mu", "xrplPubKey": "02ED720F8BF89D333CF7C4EAC763DA6EB7051895924DEB33AD34E87A624FE6B8F0"} +Keys info + coreumAddress: "testcore1lwzy78a7ulernmvdgvjyagaslsmp7x7g496jj4" + xrplAddress: "r41Cc8WLZMeUvZfvB4Fc4hRjpHya4T4Nqq" + xrplPubKey: "022ED182ACEBFE4C55CE0A0EA561468C31336F9B4E71FB487FC84D94A2826F1C10" ``` The output contains the `coreumAddress`, `xrplAddress` and `xrplPubKey` used for the contract deployment. @@ -74,30 +101,42 @@ deployer. #### Pass the [Init relayer](#init-relayer) section. -#### Generate new key which will be used for the bridge bootstrapping +#### Generate new key which will be used for the XRPL bridge account creation ```bash -./coreumbridge-xrpl-relayer keys add bridge-account +coreumbridge-xrpl-relayer keys-xrpl add bridge-account ``` -#### Fund the Coreum account +#### Generate new key which will be used for the contract deployment ```bash -./coreumbridge-xrpl-relayer keys show -a bridge-account +coreumbridge-xrpl-relayer keys-coreum add contract-deployer ``` -Get the Coreum address from the output and fund it on the Coreum side. -The balance should cover the token issuance fee and fee for the deployment transaction. +Send some core tokens to the generated address, to have enough for the contract deployment. #### Generate config template ```bash -export RELAYERS_COUNT={Relayes count to be used} -./coreumbridge-xrpl-relayer bootstrap-bridge bootstrapping.yaml --key-name bridge-account --init-only --relayers-count $RELAYERS_COUNT +coreumbridge-xrpl-relayer bootstrap-bridge /root/.coreumbridge-xrpl-relayer/bootstrapping.yaml \ + --xrpl-key-name bridge-account --coreum-key-name contract-deployer --init-only --relayers-count 32 ``` The output will print the XRPL bridge address and min XRPL bridge account balance. Fund it and proceed to the nex step. +Output example: + +```bash +XRPL bridge address + address: "rDtBdHaGpZpgQ4vEZv3nKujhudd5kUHVQ" +Coreum deployer address + address: "testcore1qfhm09t9wyf5ttuj9e52v90h7rhrk72zwjxv5l" +Initializing default bootstrapping config + path: "/root/.coreumbridge-xrpl-relayer/bootstrapping.yaml" +Computed minimum XRPL bridge balance + balance: 594 +``` + #### Modify the `bootstrapping.yaml` config Collect the config from the relayer and modify the bootstrapping config. @@ -111,10 +150,11 @@ relayers: - coreum_address: "" xrpl_address: "" xrpl_pub_key: "" -evidence_threshold: 0 +evidence_threshold: 2 used_ticket_sequence_threshold: 150 -trust_set_limit_amount: "100000000000000000000000000000000000" +trust_set_limit_amount: "340000000000000000000000000000000000000" contract_bytecode_path: "" +xrpl_base_fee: 10 ``` If you don't have the contract bytecode download it. @@ -122,7 +162,8 @@ If you don't have the contract bytecode download it. #### Run the bootstrapping ```bash -./coreumbridge-xrpl-relayer bootstrap-bridge bootstrapping.yaml --key-name bridge-account +coreumbridge-xrpl-relayer bootstrap-bridge /root/.coreumbridge-xrpl-relayer/bootstrapping.yaml \ + --xrpl-key-name bridge-account --coreum-key-name contract-deployer ``` Once the command is executed get the bridge contract address from the output and share among the relayers to update in @@ -131,7 +172,7 @@ the relayers config. #### Remove the bridge-account key ```bash -./coreumbridge-xrpl-relayer keys delete bridge-account +./coreumbridge-xrpl-relayer xrpl-keys delete bridge-account ``` #### Run all relayers @@ -144,25 +185,25 @@ Use [Recover tickets](#recover-tickets) instruction and recover tickets. ### Run relayer -#### Run relayer using binary +#### Run relayer with docker -```bash -./coreumbridge-xrpl-relayer start --keyring-dir $HOME/.coreumbridge-xrpl-relayer/keys -``` +If relayer docker image is not built, build it. -#### Run relayer in docker +##### Set up release version -If relayer docker image is not built, build it. +```bash +export RELEASE_VERSION={Relayer release version} +``` -##### Run relayer +##### Run ```bash docker run -dit --name coreumbridge-xrpl-relayer \ - -v $HOME/.coreumbridge-xrpl-relayer:/.coreumbridge-xrpl-relayer \ - coreumbridge-xrpl-relayer:local start \ - --home /.coreumbridge-xrpl-relayer + -v $HOME/.coreumbridge-xrpl-relayer:/root/.coreumbridge-xrpl-relayer \ + coreumfoundation/coreumbridge-xrpl-relayer:$RELEASE_VERSION \ + start -docker attach coreumbridge-xrpl-relayer +docker attach coreumbridge-xrpl-relayer ``` Once you are attached, press any key and enter the keyring password. @@ -175,75 +216,3 @@ docker restart coreumbridge-xrpl-relayer && docker attach coreumbridge-xrpl-rela ``` Once you are attached, press any key and enter the keyring password. - -## Public CLI - -### Pass the [Init relayer](#init-relayer) section. - -Additionally, set the bridge contract address in the `relayer.yaml` - -### Send from coreum to XRPL - -```bash -./coreumbridge-xrpl-relayer send-from-coreum-to-xrpl 1000000ucore rrrrrrrrrrrrrrrrrrrrrhoLvTp --key-name sender --keyring-dir $HOME/.coreumbridge-xrpl-relayer/keys -``` - -### Send from XRPL to coreum - -```bash -./coreumbridge-xrpl-relayer send-from-xrpl-to-coreum 1000000 rrrrrrrrrrrrrrrrrrrrrhoLvTp XRP testcore1adst6w4e79tddzhcgaru2l2gms8jjep6a4caa7 --key-name sender --keyring-dir $HOME/.coreumbridge-xrpl-relayer/keys -``` - -### Get contact config - -```bash -./coreumbridge-xrpl-relayer contract-config -``` - -### Get all registered tokens - -```bash -./coreumbridge-xrpl-relayer registered-tokens -``` - -### Get Coreum balances - -```bash -./coreumbridge-xrpl-relayer coreum-balances testcore1adst6w4e79tddzhcgaru2l2gms8jjep6a4caa7 -``` - -### Get XRPL balances - -```bash -./coreumbridge-xrpl-relayer xrpl-balances rrrrrrrrrrrrrrrrrrrrrhoLvTp -``` - -### Set XRPL TrustSet - -```bash -./coreumbridge-xrpl-relayer set-xrpl-trust-set 1e80 rrrrrrrrrrrrrrrrrrrrrhoLvTp XRP --key-name sender --keyring-dir $HOME/.coreumbridge-xrpl-relayer/keys -``` - -## Owner CLI - -### Pass the [Init relayer](#init-relayer) section. - -Additionally, set the bridge contract address in the `relayer.yaml` - -### Recover tickets to allow XRPL to coreum operations - -```bash -./coreumbridge-xrpl-relayer recovery-tickets --key-name owner --keyring-dir $HOME/.coreumbridge-xrpl-relayer/keys -``` - -### Register Coreum token - -```bash -./coreumbridge-xrpl-relayer register-coreum-token ucore 6 2 500000000000000 --key-name owner --keyring-dir $HOME/.coreumbridge-xrpl-relayer/keys -``` - -### Register XRPL token - -```bash -./coreumbridge-xrpl-relayer register-xrpl-token rcoreNywaoz2ZCQ8Lg2EbSLnGuRBmun6D 434F524500000000000000000000000000000000 2 500000000000000 --key-name owner --keyring-dir $HOME/.coreumbridge-xrpl-relayer/keys -``` diff --git a/integration-tests/processes/env_test.go b/integration-tests/processes/env_test.go index 694b96af..975eff9f 100644 --- a/integration-tests/processes/env_test.go +++ b/integration-tests/processes/env_test.go @@ -627,7 +627,7 @@ func createDevRunner( // exit on errors relayerRunnerCfg.Processes.ExitOnError = true - components, err := runner.NewComponents(relayerRunnerCfg, xrplKeyring, coreumKeyring, chains.Log, false) + components, err := runner.NewComponents(relayerRunnerCfg, xrplKeyring, coreumKeyring, chains.Log, false, false) require.NoError(t, err) relayerRunner, err := runner.NewRunner(ctx, components, relayerRunnerCfg) diff --git a/relayer/client/bridge.go b/relayer/client/bridge.go index a8715439..5a1e8dcf 100644 --- a/relayer/client/bridge.go +++ b/relayer/client/bridge.go @@ -295,10 +295,10 @@ func (b *BridgeClient) Bootstrap( } b.log.Info(ctx, "Deploying contract", zap.Any("settings", instantiationCfg)) contractAddress, err := b.contractClient.DeployAndInstantiate(ctx, senderAddress, contactByteCode, instantiationCfg) - b.log.Info(ctx, "Contract is deployed successfully", zap.String("address", contractAddress.String())) if err != nil { return nil, errors.Wrap(err, "failed to deploy contract") } + b.log.Info(ctx, "Contract is deployed successfully", zap.String("address", contractAddress.String())) if err := b.setUpXRPLBridgeAccount(ctx, bridgeAccountKeyName, cfg, xrplSignerEntries); err != nil { return nil, err @@ -359,7 +359,6 @@ func (b *BridgeClient) RecoverTickets( b.log.Info( ctx, "Successfully submitted recovery tickets transaction", - zap.Uint32("numberOfTickets", xrpl.MaxTicketsToAllocate), zap.String("txHash", txRes.TxHash), ) @@ -1178,12 +1177,7 @@ func (b *BridgeClient) autoFillSignSubmitAndAwaitXRPLTx( return err } - b.log.Info( - ctx, - "Submitting XRPL transaction", - zap.String("txHash", tx.GetHash().String()), - zap.Any("tx", tx), - ) + b.log.Info(ctx, "Submitting XRPL transaction", zap.String("txHash", tx.GetHash().String())) if err = b.xrplRPCClient.SubmitAndAwaitSuccess(ctx, tx); err != nil { return err } diff --git a/relayer/cmd/cli/cli.go b/relayer/cmd/cli/cli.go index a8c67c85..05789e82 100644 --- a/relayer/cmd/cli/cli.go +++ b/relayer/cmd/cli/cli.go @@ -56,6 +56,10 @@ const ( FlagHome = "home" // FlagKeyName is key name flag. FlagKeyName = "key-name" + // FlagCoreumKeyName is coreum key name flag. + FlagCoreumKeyName = "coreum-key-name" + // FlagXRPLKeyName is XRPL key name flag. + FlagXRPLKeyName = "xrpl-key-name" // FlagCoreumChainID is chain-id flag. FlagCoreumChainID = "coreum-chain-id" // FlagCoreumGRPCURL is Coreum GRPC URL flag. @@ -226,7 +230,7 @@ func NewRunnerFromHome(cmd *cobra.Command) (*runner.Runner, error) { return nil, err } - components, err := runner.NewComponents(cfg, xrplClientCtx.Keyring, coreumClientCtx.Keyring, zapLogger, true) + components, err := runner.NewComponents(cfg, xrplClientCtx.Keyring, coreumClientCtx.Keyring, zapLogger, true, true) if err != nil { return nil, err } @@ -487,32 +491,50 @@ $ bootstrap-bridge bootstrapping.yaml --%s bridge-account `, FlagKeyName)), RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() + // get bridgeClient first to set cosmos SDK config + bridgeClient, err := bcp(cmd) + if err != nil { + return err + } clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return errors.Wrap(err, "failed to get client context") } - log, err := GetCLILogger() if err != nil { return err } - - keyName, err := cmd.Flags().GetString(FlagKeyName) + xrplKeyName, err := cmd.Flags().GetString(FlagXRPLKeyName) if err != nil { - return errors.Wrapf(err, "failed to get %s", FlagKeyName) + return errors.Wrapf(err, "failed to get %s", FlagXRPLKeyName) } - xrplClientCtx, err := WithKeyring(clientCtx, cmd.Flags(), xrpl.KeyringSuffix) if err != nil { return err } - xrplKeyringTxSigner := xrpl.NewKeyringTxSigner(xrplClientCtx.Keyring) - xrplBridgeAddress, err := xrplKeyringTxSigner.Account(keyName) + xrplBridgeAddress, err := xrplKeyringTxSigner.Account(xrplKeyName) if err != nil { return err } log.Info(ctx, "XRPL bridge address", zap.String("address", xrplBridgeAddress.String())) + coreumKeyName, err := cmd.Flags().GetString(FlagCoreumKeyName) + if err != nil { + return errors.Wrapf(err, "failed to get %s", FlagCoreumKeyName) + } + coreumClientCtx, err := WithKeyring(clientCtx, cmd.Flags(), coreum.KeyringSuffix) + if err != nil { + return err + } + coreumKRRecord, err := coreumClientCtx.Keyring.Key(coreumKeyName) + if err != nil { + return errors.Wrapf(err, "failed to get key by name:%s", coreumKeyName) + } + coreumAddress, err := coreumKRRecord.GetAddress() + if err != nil { + return errors.Wrapf(err, "failed to address for key name:%s", coreumKeyName) + } + log.Info(ctx, "Coreum deployer address", zap.String("address", coreumAddress.String())) filePath := args[0] initOnly, err := cmd.Flags().GetBool(FlagInitOnly) @@ -536,14 +558,6 @@ $ bootstrap-bridge bootstrapping.yaml --%s bridge-account return nil } - record, err := xrplClientCtx.Keyring.Key(keyName) - if err != nil { - return errors.Wrapf(err, "failed to get key by name:%s", keyName) - } - addr, err := record.GetAddress() - if err != nil { - return errors.Wrapf(err, "failed to address for key name:%s", keyName) - } cfg, err := bridgeclient.ReadBootstrappingConfig(filePath) if err != nil { return err @@ -553,21 +567,17 @@ $ bootstrap-bridge bootstrapping.yaml --%s bridge-account input := bufio.NewScanner(os.Stdin) input.Scan() - bridgeClient, err := bcp(cmd) - if err != nil { - return err - } - - _, err = bridgeClient.Bootstrap(ctx, addr, keyName, cfg) + _, err = bridgeClient.Bootstrap(ctx, coreumAddress, xrplKeyName, cfg) return err }, } addKeyringFlags(cmd) - addKeyNameFlag(cmd) addHomeFlag(cmd) cmd.PersistentFlags().Bool(FlagInitOnly, false, "Init default config") cmd.PersistentFlags().Int(FlagRelayersCount, 0, "Relayers count") + cmd.PersistentFlags().String(FlagCoreumKeyName, "", "Key name from the Coreum keyring") + cmd.PersistentFlags().String(FlagXRPLKeyName, "", "Key name from the XRPL keyring") return cmd } @@ -579,11 +589,11 @@ func ContractConfigCmd(bcp BridgeClientProvider) *cobra.Command { Short: "Prints contract config.", RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() + // get bridgeClient first to set cosmos SDK config bridgeClient, err := bcp(cmd) if err != nil { return err } - cfg, err := bridgeClient.GetContractConfig(ctx) if err != nil { return err @@ -606,15 +616,20 @@ func ContractConfigCmd(bcp BridgeClientProvider) *cobra.Command { // RecoverTicketsCmd recovers 250 tickets in the bridge contract. func RecoverTicketsCmd(bcp BridgeClientProvider) *cobra.Command { cmd := &cobra.Command{ - Use: "recovery-tickets", + Use: "recover-tickets", Short: "Recovers tickets in the bridge contract.", Long: strings.TrimSpace(fmt.Sprintf( - `Recovers 250 tickets in the bridge contract. + `Recovers tickets in the bridge contract. Example: -$ recovery-tickets --%s 250 --%s owner +$ recover-tickets --%s 250 --%s owner `, FlagTicketsToAllocate, FlagKeyName)), RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() + // get bridgeClient first to set cosmos SDK config + bridgeClient, err := bcp(cmd) + if err != nil { + return err + } clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return errors.Wrap(err, "failed to get client context") @@ -625,7 +640,7 @@ $ recovery-tickets --%s 250 --%s owner return errors.Wrapf(err, "failed to get %s", FlagTicketsToAllocate) } - xrplClientCtx, err := WithKeyring(clientCtx, cmd.Flags(), xrpl.KeyringSuffix) + xrplClientCtx, err := WithKeyring(clientCtx, cmd.Flags(), coreum.KeyringSuffix) if err != nil { return err } @@ -633,10 +648,6 @@ $ recovery-tickets --%s 250 --%s owner if err != nil { return err } - bridgeClient, err := bcp(cmd) - if err != nil { - return err - } return bridgeClient.RecoverTickets(ctx, owner, ticketsToAllocated) }, @@ -665,6 +676,11 @@ $ register-coreum-token ucore 6 2 500000000000000 4000 --%s owner Args: cobra.ExactArgs(5), RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() + // get bridgeClient first to set cosmos SDK config + bridgeClient, err := bcp(cmd) + if err != nil { + return err + } clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return errors.Wrap(err, "failed to get client context") @@ -701,11 +717,6 @@ $ register-coreum-token ucore 6 2 500000000000000 4000 --%s owner return errors.Wrapf(err, "invalid bridgingFee: %s", args[4]) } - bridgeClient, err := bcp(cmd) - if err != nil { - return err - } - _, err = bridgeClient.RegisterCoreumToken( ctx, owner, @@ -739,6 +750,11 @@ $ update-coreum-token ucore --%s enabled --%s 2 --%s 10000000 --%s 4000 --%s own Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() + // get bridgeClient first to set cosmos SDK config + bridgeClient, err := bcp(cmd) + if err != nil { + return err + } clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return errors.Wrap(err, "failed to get client context") @@ -765,10 +781,6 @@ $ update-coreum-token ucore --%s enabled --%s 2 --%s 10000000 --%s 4000 --%s own return err } - bridgeClient, err := bcp(cmd) - if err != nil { - return err - } return bridgeClient.UpdateCoreumToken( ctx, owner, @@ -804,6 +816,11 @@ $ register-xrpl-token rcoreNywaoz2ZCQ8Lg2EbSLnGuRBmun6D 434F52450000000000000000 Args: cobra.ExactArgs(5), RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() + // get bridgeClient first to set cosmos SDK config + bridgeClient, err := bcp(cmd) + if err != nil { + return err + } clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return errors.Wrap(err, "failed to get client context") @@ -844,10 +861,6 @@ $ register-xrpl-token rcoreNywaoz2ZCQ8Lg2EbSLnGuRBmun6D 434F52450000000000000000 return errors.Wrapf(err, "invalid bridgeFee: %s", args[4]) } - bridgeClient, err := bcp(cmd) - if err != nil { - return err - } _, err = bridgeClient.RegisterXRPLToken( ctx, owner, @@ -881,6 +894,11 @@ $ recover-xrpl-token-registration [issuer] [currency] --%s owner Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() + // get bridgeClient first to set cosmos SDK config + bridgeClient, err := bcp(cmd) + if err != nil { + return err + } clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return errors.Wrap(err, "failed to get client context") @@ -906,11 +924,6 @@ $ recover-xrpl-token-registration [issuer] [currency] --%s owner return errors.Wrapf(err, "failed to convert currency string to rippledata.Currency: %s", args[1]) } - bridgeClient, err := bcp(cmd) - if err != nil { - return err - } - return bridgeClient.RecoverXRPLTokenRegistration(ctx, owner, issuer.String(), currency.String()) }, } @@ -925,7 +938,7 @@ $ recover-xrpl-token-registration [issuer] [currency] --%s owner // UpdateXRPLTokenCmd updates the XRPL originated token in the bridge contract. func UpdateXRPLTokenCmd(bcp BridgeClientProvider) *cobra.Command { cmd := &cobra.Command{ - Use: "update-xrpl-token [denom]", + Use: "update-xrpl-token [issuer] [currency]", Short: "Updates XRPL token in the bridge contract.", //nolint:lll // long example Long: strings.TrimSpace( @@ -936,6 +949,11 @@ $ update-xrpl-token rcoreNywaoz2ZCQ8Lg2EbSLnGuRBmun6D 434F5245000000000000000000 Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() + // get bridgeClient first to set cosmos SDK config + bridgeClient, err := bcp(cmd) + if err != nil { + return err + } clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return errors.Wrap(err, "failed to get client context") @@ -963,10 +981,6 @@ $ update-xrpl-token rcoreNywaoz2ZCQ8Lg2EbSLnGuRBmun6D 434F5245000000000000000000 return err } - bridgeClient, err := bcp(cmd) - if err != nil { - return err - } return bridgeClient.UpdateXRPLToken( ctx, owner, @@ -1001,6 +1015,11 @@ $ rotate-keys new-keys.yaml --%s owner `, FlagKeyName)), RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() + // get bridgeClient first to set cosmos SDK config + bridgeClient, err := bcp(cmd) + if err != nil { + return err + } clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return errors.Wrap(err, "failed to get client context") @@ -1048,11 +1067,6 @@ $ rotate-keys new-keys.yaml --%s owner input := bufio.NewScanner(os.Stdin) input.Scan() - bridgeClient, err := bcp(cmd) - if err != nil { - return err - } - return bridgeClient.RotateKeys(ctx, addr, cfg) }, } @@ -1079,6 +1093,11 @@ $ update-xrpl-base-fee 20 --%s owner Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() + // get bridgeClient first to set cosmos SDK config + bridgeClient, err := bcp(cmd) + if err != nil { + return err + } clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return errors.Wrap(err, "failed to get client context") @@ -1099,11 +1118,6 @@ $ update-xrpl-base-fee 20 --%s owner return errors.Wrapf(err, "invalid XRPL base fee: %s", args[0]) } - bridgeClient, err := bcp(cmd) - if err != nil { - return err - } - return bridgeClient.UpdateXRPLBaseFee( ctx, owner, @@ -1125,6 +1139,7 @@ func RegisteredTokensCmd(bcp BridgeClientProvider) *cobra.Command { Short: "Prints all registered tokens.", RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() + // get bridgeClient first to set cosmos SDK config bridgeClient, err := bcp(cmd) if err != nil { return err @@ -1167,6 +1182,11 @@ $ send-from-coreum-to-xrpl 1000000ucore rrrrrrrrrrrrrrrrrrrrrhoLvTp --%s sender Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() + // get bridgeClient first to set cosmos SDK config + bridgeClient, err := bcp(cmd) + if err != nil { + return err + } clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return errors.Wrap(err, "failed to get client context") @@ -1194,10 +1214,6 @@ $ send-from-coreum-to-xrpl 1000000ucore rrrrrrrrrrrrrrrrrrrrrhoLvTp --%s sender if err != nil { return errors.Wrapf(err, "failed to convert recipient string to rippledata.Account: %s", args[1]) } - bridgeClient, err := bcp(cmd) - if err != nil { - return err - } return bridgeClient.SendFromCoreumToXRPL(ctx, sender, *recipient, amount, deliverAmount) }, @@ -1231,7 +1247,11 @@ $ send-from-xrpl-to-coreum 1000000 %s %s %s --%s sender Args: cobra.ExactArgs(4), RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() - + // get bridgeClient first to set cosmos SDK config + bridgeClient, err := bcp(cmd) + if err != nil { + return err + } issuer, err := rippledata.NewAccountFromAddress(args[1]) if err != nil { return errors.Wrapf(err, "failed to convert issuer string to rippledata.Account: %s", args[2]) @@ -1263,11 +1283,6 @@ $ send-from-xrpl-to-coreum 1000000 %s %s %s --%s sender return errors.Wrapf(err, "failed to get flag %s", FlagKeyName) } - bridgeClient, err := bcp(cmd) - if err != nil { - return err - } - return bridgeClient.SendFromXRPLToCoreum( ctx, keyName, @@ -1295,15 +1310,16 @@ func CoreumBalancesCmd(bcp BridgeClientProvider) *cobra.Command { Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() + // get bridgeClient first to set cosmos SDK config + bridgeClient, err := bcp(cmd) + if err != nil { + return err + } address, err := sdk.AccAddressFromBech32(args[0]) if err != nil { return errors.Wrapf(err, "failed to convert address string to sdk.AccAddress: %s", args[0]) } - bridgeClient, err := bcp(cmd) - if err != nil { - return err - } coins, err := bridgeClient.GetCoreumBalances(ctx, address) if err != nil { return err @@ -1329,16 +1345,15 @@ func XRPLBalancesCmd(bcp BridgeClientProvider) *cobra.Command { Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() - acc, err := rippledata.NewAccountFromAddress(args[0]) - if err != nil { - return errors.Wrapf(err, "failed to convert address to rippledata.Address, address:%s", args[0]) - } - + // get bridgeClient first to set cosmos SDK config bridgeClient, err := bcp(cmd) if err != nil { return err } - + acc, err := rippledata.NewAccountFromAddress(args[0]) + if err != nil { + return errors.Wrapf(err, "failed to convert address to rippledata.Address, address:%s", args[0]) + } balances, err := bridgeClient.GetXRPLBalances(ctx, *acc) if err != nil { return err @@ -1380,7 +1395,11 @@ $ set-xrpl-trust-set 1e80 %s %s --%s sender Args: cobra.ExactArgs(3), RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() - + // get bridgeClient first to set cosmos SDK config + bridgeClient, err := bcp(cmd) + if err != nil { + return err + } issuer, err := rippledata.NewAccountFromAddress(args[1]) if err != nil { return errors.Wrapf(err, "failed to convert issuer string to rippledata.Account: %s", args[2]) @@ -1407,11 +1426,6 @@ $ set-xrpl-trust-set 1e80 %s %s --%s sender return errors.Wrapf(err, "failed to get flag %s", FlagKeyName) } - bridgeClient, err := bcp(cmd) - if err != nil { - return err - } - return bridgeClient.SetXRPLTrustSet( ctx, keyName, @@ -1465,12 +1479,11 @@ $ pending-refunds %s Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() - + // get bridgeClient first to set cosmos SDK config bridgeClient, err := bcp(cmd) if err != nil { return err } - address, err := sdk.AccAddressFromBech32(args[0]) if err != nil { return err @@ -1509,6 +1522,11 @@ $ claim-refund --%s claimer --%s 1705664693-2 Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() + // get bridgeClient first to set cosmos SDK config + bridgeClient, err := bcp(cmd) + if err != nil { + return err + } clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return errors.Wrap(err, "failed to get client context") @@ -1529,11 +1547,6 @@ $ claim-refund --%s claimer --%s 1705664693-2 return err } - bridgeClient, err := bcp(cmd) - if err != nil { - return err - } - if refundID != "" { return bridgeClient.ClaimRefund(ctx, address, refundID) } @@ -1575,7 +1588,7 @@ $ relayer-fees %s Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() - + // get bridgeClient first to set cosmos SDK config bridgeClient, err := bcp(cmd) if err != nil { return err @@ -1621,6 +1634,11 @@ $ claim-relayer-fees --key-name address --%s %s Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() + // get bridgeClient first to set cosmos SDK config + bridgeClient, err := bcp(cmd) + if err != nil { + return err + } clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return errors.Wrap(err, "failed to get client context") @@ -1641,11 +1659,6 @@ $ claim-relayer-fees --key-name address --%s %s return err } - bridgeClient, err := bcp(cmd) - if err != nil { - return err - } - if amountStr != "" { amount, err := sdk.ParseCoinsNormalized(amountStr) if err != nil { @@ -1686,6 +1699,11 @@ $ halt-bridge --%s owner Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() + // get bridgeClient first to set cosmos SDK config + bridgeClient, err := bcp(cmd) + if err != nil { + return err + } clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return errors.Wrap(err, "failed to get client context") @@ -1699,10 +1717,6 @@ $ halt-bridge --%s owner return err } - bridgeClient, err := bcp(cmd) - if err != nil { - return err - } return bridgeClient.HaltBridge( ctx, owner, @@ -1733,6 +1747,11 @@ $ resume-bridge --%s owner Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() + // get bridgeClient first to set cosmos SDK config + bridgeClient, err := bcp(cmd) + if err != nil { + return err + } clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return errors.Wrap(err, "failed to get client context") @@ -1745,11 +1764,6 @@ $ resume-bridge --%s owner if err != nil { return err } - - bridgeClient, err := bcp(cmd) - if err != nil { - return err - } return bridgeClient.ResumeBridge( ctx, owner, diff --git a/relayer/cmd/cli/cli_test.go b/relayer/cmd/cli/cli_test.go index 02e6f2fa..151c8f84 100644 --- a/relayer/cmd/cli/cli_test.go +++ b/relayer/cmd/cli/cli_test.go @@ -114,25 +114,29 @@ func TestBootstrapCmd(t *testing.T) { configPath := path.Join(t.TempDir(), "bootstrapping.yaml") keyringDir := t.TempDir() - keyName := "deployer" - addKeyToTestKeyring(t, keyringDir, keyName, xrpl.KeyringSuffix, xrpl.XRPLHDPath) + xrplKeyName := "xrpl-bridge" + addKeyToTestKeyring(t, keyringDir, xrplKeyName, xrpl.KeyringSuffix, xrpl.XRPLHDPath) + contractDeployer := "contract-deployer" + addKeyToTestKeyring(t, keyringDir, contractDeployer, coreum.KeyringSuffix, xrpl.XRPLHDPath) // call bootstrap with init only args := []string{ configPath, flagWithPrefix(cli.FlagInitOnly), flagWithPrefix(cli.FlagRelayersCount), "3", - flagWithPrefix(cli.FlagKeyName), keyName, + flagWithPrefix(cli.FlagXRPLKeyName), xrplKeyName, + flagWithPrefix(cli.FlagCoreumKeyName), contractDeployer, } args = append(args, testKeyringFlags(keyringDir)...) - executeCmd(t, cli.BootstrapBridgeCmd(nil), args...) + executeCmd(t, cli.BootstrapBridgeCmd(mockBridgeClientProvider(nil)), args...) // use generated file bridgeClientMock := NewMockBridgeClient(ctrl) - bridgeClientMock.EXPECT().Bootstrap(gomock.Any(), gomock.Any(), keyName, bridgeclient.DefaultBootstrappingConfig()) + bridgeClientMock.EXPECT().Bootstrap(gomock.Any(), gomock.Any(), xrplKeyName, bridgeclient.DefaultBootstrappingConfig()) args = []string{ configPath, - flagWithPrefix(cli.FlagKeyName), keyName, + flagWithPrefix(cli.FlagXRPLKeyName), xrplKeyName, + flagWithPrefix(cli.FlagCoreumKeyName), contractDeployer, } args = append(args, testKeyringFlags(keyringDir)...) executeCmd(t, cli.BootstrapBridgeCmd(mockBridgeClientProvider(bridgeClientMock)), args...) @@ -153,7 +157,7 @@ func TestRecoverTicketsCmd(t *testing.T) { keyringDir := t.TempDir() keyName := "owner" //nolint:goconst // testing only variable - addKeyToTestKeyring(t, keyringDir, keyName, xrpl.KeyringSuffix, xrpl.XRPLHDPath) + addKeyToTestKeyring(t, keyringDir, keyName, coreum.KeyringSuffix, xrpl.XRPLHDPath) args := []string{ flagWithPrefix(cli.FlagKeyName), keyName, @@ -804,7 +808,7 @@ func TestRotateKeysCmd(t *testing.T) { flagWithPrefix(cli.FlagKeyName), keyName, } args = append(args, testKeyringFlags(keyringDir)...) - executeCmd(t, cli.RotateKeysCmd(nil), args...) + executeCmd(t, cli.RotateKeysCmd(mockBridgeClientProvider(nil)), args...) // use generated file bridgeClientMock := NewMockBridgeClient(ctrl) diff --git a/relayer/cmd/main.go b/relayer/cmd/main.go index 9ea613d0..af979d78 100644 --- a/relayer/cmd/main.go +++ b/relayer/cmd/main.go @@ -88,6 +88,8 @@ func RootCmd(ctx context.Context) (*cobra.Command, error) { cmd.AddCommand(cli.ClaimRefundCmd(bridgeClientProvider)) cmd.AddCommand(cli.ClaimRelayerFeesCmd(bridgeClientProvider)) cmd.AddCommand(cli.GetRelayerFeesCmd(bridgeClientProvider)) + cmd.AddCommand(cli.HaltBridgeCmd(bridgeClientProvider)) + cmd.AddCommand(cli.ResumeBridgeCmd(bridgeClientProvider)) return cmd, nil } @@ -128,7 +130,7 @@ func bridgeClientProvider(cmd *cobra.Command) (cli.BridgeClient, error) { return nil, errors.Wrap(err, "failed to configure coreum keyring") } - components, err := runner.NewComponents(cfg, xrplClientCtx.Keyring, coreumClientCtx.Keyring, log, true) + components, err := runner.NewComponents(cfg, xrplClientCtx.Keyring, coreumClientCtx.Keyring, log, true, false) if err != nil { return nil, err } diff --git a/relayer/logger/yaml_console.go b/relayer/logger/yaml_console.go index 48fe4b67..05e65a28 100644 --- a/relayer/logger/yaml_console.go +++ b/relayer/logger/yaml_console.go @@ -7,6 +7,7 @@ import ( "strings" "time" + sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/pkg/errors" "go.uber.org/zap" @@ -446,11 +447,16 @@ func (c *yamlConsoleEncoder) AppendReflected(value interface{}) error { } func (c *yamlConsoleEncoder) appendCustomTypes(value interface{}) bool { - if addr, ok := value.(sdk.AccAddress); ok { - c.AppendString(addr.String()) - return true + switch v := value.(type) { + case sdk.AccAddress: + c.AppendString(v.String()) + case sdkmath.Int: + c.AppendString(v.String()) + default: + return false } - return false + + return true } func (c *yamlConsoleEncoder) indentation() string { diff --git a/relayer/processes/coreum_to_xrpl.go b/relayer/processes/coreum_to_xrpl.go index e858724f..998ce706 100644 --- a/relayer/processes/coreum_to_xrpl.go +++ b/relayer/processes/coreum_to_xrpl.go @@ -90,45 +90,48 @@ func NewCoreumToXRPLProcess( } // Start starts the process. -func (s *CoreumToXRPLProcess) Start(ctx context.Context) error { +func (p *CoreumToXRPLProcess) Start(ctx context.Context) error { + p.log.Info(ctx, "Starting Coreum to XRPL process") for { select { case <-ctx.Done(): return errors.WithStack(ctx.Err()) default: - if err := s.processPendingOperations(ctx); err != nil && !errors.Is(err, context.Canceled) { + if err := p.processPendingOperations(ctx); err != nil && !errors.Is(err, context.Canceled) { return errors.Wrap(err, "failed to process pending operations") } - if !s.cfg.RepeatRecentScan { - s.log.Info(ctx, "Process repeating is disabled, process is finished") + if !p.cfg.RepeatRecentScan { + p.log.Info(ctx, "Process repeating is disabled, process is finished") return nil } - s.log.Info(ctx, "Waiting before the next execution", zap.String("delay", s.cfg.RepeatDelay.String())) + p.log.Info(ctx, "Waiting before the next execution", zap.String("delay", p.cfg.RepeatDelay.String())) select { case <-ctx.Done(): return errors.WithStack(ctx.Err()) - case <-time.After(s.cfg.RepeatDelay): + case <-time.After(p.cfg.RepeatDelay): } } } } -func (s *CoreumToXRPLProcess) processPendingOperations(ctx context.Context) error { - operations, err := s.contractClient.GetPendingOperations(ctx) +func (p *CoreumToXRPLProcess) processPendingOperations(ctx context.Context) error { + operations, err := p.contractClient.GetPendingOperations(ctx) if err != nil { return err } if len(operations) == 0 { + p.log.Debug(ctx, "No pending operations to process") return nil } + p.log.Info(ctx, "Processing pending operations", zap.Int("count", len(operations))) - bridgeSigners, err := s.getBridgeSigners(ctx) + bridgeSigners, err := p.getBridgeSigners(ctx) if err != nil { return err } for _, operation := range operations { - if err := s.signOrSubmitOperation(ctx, operation, bridgeSigners); err != nil { + if err := p.signOrSubmitOperation(ctx, operation, bridgeSigners); err != nil { return err } } @@ -136,12 +139,12 @@ func (s *CoreumToXRPLProcess) processPendingOperations(ctx context.Context) erro return nil } -func (s *CoreumToXRPLProcess) getBridgeSigners(ctx context.Context) (BridgeSigners, error) { - xrplWeights, xrplWeightsQuorum, err := s.getBridgeXRPLSignerAccountsWithWeights(ctx) +func (p *CoreumToXRPLProcess) getBridgeSigners(ctx context.Context) (BridgeSigners, error) { + xrplWeights, xrplWeightsQuorum, err := p.getBridgeXRPLSignerAccountsWithWeights(ctx) if err != nil { return BridgeSigners{}, err } - contractConfig, err := s.contractClient.GetContractConfig(ctx) + contractConfig, err := p.contractClient.GetContractConfig(ctx) if err != nil { return BridgeSigners{}, err } @@ -179,10 +182,10 @@ func (s *CoreumToXRPLProcess) getBridgeSigners(ctx context.Context) (BridgeSigne }, nil } -func (s *CoreumToXRPLProcess) getBridgeXRPLSignerAccountsWithWeights( +func (p *CoreumToXRPLProcess) getBridgeXRPLSignerAccountsWithWeights( ctx context.Context, ) (map[rippledata.Account]uint16, uint32, error) { - accountInfo, err := s.xrplRPCClient.AccountInfo(ctx, s.cfg.BridgeXRPLAddress) + accountInfo, err := p.xrplRPCClient.AccountInfo(ctx, p.cfg.BridgeXRPLAddress) if err != nil { return nil, 0, err } @@ -200,34 +203,39 @@ func (s *CoreumToXRPLProcess) getBridgeXRPLSignerAccountsWithWeights( return accountWights, weightsQuorum, nil } -func (s *CoreumToXRPLProcess) signOrSubmitOperation( +func (p *CoreumToXRPLProcess) signOrSubmitOperation( ctx context.Context, operation coreum.Operation, bridgeSigners BridgeSigners, ) error { - valid, err := s.preValidateOperation(ctx, operation) + valid, err := p.preValidateOperation(ctx, operation) if err != nil { return err } if !valid { - s.log.Info(ctx, "Operation is invalid", zap.Any("operation", operation)) + p.log.Warn(ctx, "Operation is invalid", zap.Any("operation", operation)) return nil } + p.log.Debug( + ctx, + "Pre-validation of the operation passed, operation is valid", + zap.Any("operation", operation), + ) - tx, quorumIsReached, err := s.buildSubmittableTransaction(ctx, operation, bridgeSigners) + tx, quorumIsReached, err := p.buildSubmittableTransaction(ctx, operation, bridgeSigners) if err != nil { return err } if !quorumIsReached { - return s.registerTxSignature(ctx, operation) + return p.registerTxSignature(ctx, operation) } - txRes, err := s.xrplRPCClient.Submit(ctx, tx) + txRes, err := p.xrplRPCClient.Submit(ctx, tx) if err != nil { return errors.Wrapf(err, "failed to submit transaction:%+v", tx) } if txRes.EngineResult.Success() { - s.log.Info( + p.log.Info( ctx, "XRPL multi-sign transaction has been successfully submitted", zap.String("txHash", strings.ToUpper(tx.GetHash().String())), @@ -237,7 +245,7 @@ func (s *CoreumToXRPLProcess) signOrSubmitOperation( } // These codes indicate that the transaction failed, but it was applied to a ledger to apply the transaction cost. if strings.HasPrefix(txRes.EngineResult.String(), xrpl.TecTxResultPrefix) { - s.log.Info( + p.log.Info( ctx, fmt.Sprintf( "The transaction has been sent, but will be reverted, code:%s, description:%s", @@ -249,13 +257,13 @@ func (s *CoreumToXRPLProcess) signOrSubmitOperation( switch txRes.EngineResult.String() { case xrpl.TefNOTicketTxResult, xrpl.TefPastSeqTxResult: - s.log.Debug( + p.log.Debug( ctx, "Transaction has been already submitted", ) return nil case xrpl.TelInsufFeeP: - s.log.Warn( + p.log.Warn( ctx, "The Fee from the transaction is not high enough to meet the server's current transaction cost requirement.", ) @@ -266,7 +274,7 @@ func (s *CoreumToXRPLProcess) signOrSubmitOperation( } } -func (s *CoreumToXRPLProcess) buildSubmittableTransaction( +func (p *CoreumToXRPLProcess) buildSubmittableTransaction( ctx context.Context, operation coreum.Operation, bridgeSigners BridgeSigners, @@ -277,12 +285,12 @@ func (s *CoreumToXRPLProcess) buildSubmittableTransaction( for _, signature := range operation.Signatures { xrplAcc, ok := bridgeSigners.CoreumToXRPLAccount[signature.RelayerCoreumAddress.String()] if !ok { - s.log.Warn(ctx, "Found unknown signer", zap.String("coreumAddress", signature.RelayerCoreumAddress.String())) + p.log.Warn(ctx, "Found unknown signer", zap.String("coreumAddress", signature.RelayerCoreumAddress.String())) continue } xrplPubKey, ok := bridgeSigners.XRPLPubKeys[xrplAcc] if !ok { - s.log.Warn( + p.log.Warn( ctx, "Found XRPL signer address without pub key in the contract", zap.String("xrplAddress", xrplAcc.String()), @@ -291,12 +299,12 @@ func (s *CoreumToXRPLProcess) buildSubmittableTransaction( } xrplAccWeight, ok := bridgeSigners.XRPLWeights[xrplAcc] if !ok { - s.log.Warn(ctx, "Found XRPL signer address without weight", zap.String("xrplAddress", xrplAcc.String())) + p.log.Warn(ctx, "Found XRPL signer address without weight", zap.String("xrplAddress", xrplAcc.String())) continue } var txSignature rippledata.VariableLength if err := txSignature.UnmarshalText([]byte(signature.Signature)); err != nil { - s.log.Error( + p.log.Error( ctx, "Failed to unmarshal tx signature", zap.Error(err), @@ -312,7 +320,7 @@ func (s *CoreumToXRPLProcess) buildSubmittableTransaction( SigningPubKey: &xrplPubKey, }, } - tx, err := s.buildXRPLTxFromOperation(operation) + tx, err := p.buildXRPLTxFromOperation(operation) if err != nil { return nil, false, err } @@ -321,7 +329,7 @@ func (s *CoreumToXRPLProcess) buildSubmittableTransaction( } isValid, _, err := rippledata.CheckMultiSignature(tx) if err != nil { - s.log.Error( + p.log.Error( ctx, "failed to check transaction signature, err:%s, signer:%+v", zap.Error(err), @@ -330,7 +338,7 @@ func (s *CoreumToXRPLProcess) buildSubmittableTransaction( continue } if !isValid { - s.log.Error( + p.log.Error( ctx, "Invalid tx signature", zap.Error(err), @@ -351,7 +359,7 @@ func (s *CoreumToXRPLProcess) buildSubmittableTransaction( return nil, false, nil } // build tx one more time to be sure that it is not affected - tx, err := s.buildXRPLTxFromOperation(operation) + tx, err := p.buildXRPLTxFromOperation(operation) if err != nil { return nil, false, err } @@ -365,11 +373,11 @@ func (s *CoreumToXRPLProcess) buildSubmittableTransaction( // preValidateOperation checks if the operation is valid, and it makes sense to submit the corresponding transaction // or the operation should be canceled with the `invalid` state. For now the main purpose of the function is to filter // out the `AllocateTickets` operation with the invalid sequence. -func (s *CoreumToXRPLProcess) preValidateOperation(ctx context.Context, operation coreum.Operation) (bool, error) { +func (p *CoreumToXRPLProcess) preValidateOperation(ctx context.Context, operation coreum.Operation) (bool, error) { // no need to check if the current relayer has already provided the signature // this check prevents the state when relayer votes and then changes its vote because of different current state for _, signature := range operation.Signatures { - if signature.RelayerCoreumAddress.String() == s.cfg.RelayerCoreumAddress.String() { + if signature.RelayerCoreumAddress.String() == p.cfg.RelayerCoreumAddress.String() { return true, nil } } @@ -381,7 +389,7 @@ func (s *CoreumToXRPLProcess) preValidateOperation(ctx context.Context, operatio return true, nil } - bridgeXRPLAccInfo, err := s.xrplRPCClient.AccountInfo(ctx, s.cfg.BridgeXRPLAddress) + bridgeXRPLAccInfo, err := p.xrplRPCClient.AccountInfo(ctx, p.cfg.BridgeXRPLAddress) if err != nil { return false, err } @@ -389,7 +397,7 @@ func (s *CoreumToXRPLProcess) preValidateOperation(ctx context.Context, operatio if *bridgeXRPLAccInfo.AccountData.Sequence == operation.AccountSequence { return true, nil } - s.log.Info( + p.log.Info( ctx, "Invalid bridge account sequence", zap.Uint32("expected", *bridgeXRPLAccInfo.AccountData.Sequence), @@ -403,37 +411,37 @@ func (s *CoreumToXRPLProcess) preValidateOperation(ctx context.Context, operatio // tx with the ticket number }, } - s.log.Info(ctx, "Sending invalid tx evidence") - _, err = s.contractClient.SendXRPLTicketsAllocationTransactionResultEvidence(ctx, s.cfg.RelayerCoreumAddress, evidence) + p.log.Info(ctx, "Sending invalid tx evidence") + _, err = p.contractClient.SendXRPLTicketsAllocationTransactionResultEvidence(ctx, p.cfg.RelayerCoreumAddress, evidence) if err == nil { return false, nil } if IsExpectedEvidenceSubmissionError(err) { - s.log.Debug(ctx, "Received expected evidence submission error", zap.String("errText", err.Error())) + p.log.Debug(ctx, "Received expected evidence submission error", zap.String("errText", err.Error())) return false, nil } return false, nil } -func (s *CoreumToXRPLProcess) registerTxSignature(ctx context.Context, operation coreum.Operation) error { - tx, err := s.buildXRPLTxFromOperation(operation) +func (p *CoreumToXRPLProcess) registerTxSignature(ctx context.Context, operation coreum.Operation) error { + tx, err := p.buildXRPLTxFromOperation(operation) if err != nil { return err } - signer, err := s.xrplSigner.MultiSign(tx, s.cfg.XRPLTxSignerKeyName) + signer, err := p.xrplSigner.MultiSign(tx, p.cfg.XRPLTxSignerKeyName) if err != nil { - return errors.Wrapf(err, "failed to sign transaction, keyName:%s", s.cfg.XRPLTxSignerKeyName) + return errors.Wrapf(err, "failed to sign transaction, keyName:%s", p.cfg.XRPLTxSignerKeyName) } - _, err = s.contractClient.SaveSignature( + _, err = p.contractClient.SaveSignature( ctx, - s.cfg.RelayerCoreumAddress, + p.cfg.RelayerCoreumAddress, operation.GetOperationID(), operation.Version, signer.Signer.TxnSignature.String(), ) if err == nil { - s.log.Info( + p.log.Info( ctx, "Signature registered for the operation", zap.String("signature", signer.Signer.TxnSignature.String()), @@ -445,22 +453,28 @@ func (s *CoreumToXRPLProcess) registerTxSignature(ctx context.Context, operation coreum.IsPendingOperationNotFoundError(err) || coreum.IsOperationVersionMismatchError(err) || coreum.IsBridgeHaltedError(err) { + p.log.Debug( + ctx, + "Received expected evidence error on saving signature", + zap.String("errText", err.Error()), + ) + return nil } return errors.Wrap(err, "failed to register transaction signature") } -func (s *CoreumToXRPLProcess) buildXRPLTxFromOperation(operation coreum.Operation) (MultiSignableTransaction, error) { +func (p *CoreumToXRPLProcess) buildXRPLTxFromOperation(operation coreum.Operation) (MultiSignableTransaction, error) { switch { case isAllocateTicketsOperation(operation): - return BuildTicketCreateTxForMultiSigning(s.cfg.BridgeXRPLAddress, operation) + return BuildTicketCreateTxForMultiSigning(p.cfg.BridgeXRPLAddress, operation) case isTrustSetOperation(operation): - return BuildTrustSetTxForMultiSigning(s.cfg.BridgeXRPLAddress, operation) + return BuildTrustSetTxForMultiSigning(p.cfg.BridgeXRPLAddress, operation) case isCoreumToXRPLTransferOperation(operation): - return BuildCoreumToXRPLXRPLOriginatedTokenTransferPaymentTxForMultiSigning(s.cfg.BridgeXRPLAddress, operation) + return BuildCoreumToXRPLXRPLOriginatedTokenTransferPaymentTxForMultiSigning(p.cfg.BridgeXRPLAddress, operation) case isRotateKeysOperation(operation): - return BuildSignerListSetTxForMultiSigning(s.cfg.BridgeXRPLAddress, operation) + return BuildSignerListSetTxForMultiSigning(p.cfg.BridgeXRPLAddress, operation) default: return nil, errors.Errorf("failed to process operation, unable to determine operation type, operation:%+v", operation) } diff --git a/relayer/processes/xrpl_to_coreum.go b/relayer/processes/xrpl_to_coreum.go index 36a8133f..863cc2f8 100644 --- a/relayer/processes/xrpl_to_coreum.go +++ b/relayer/processes/xrpl_to_coreum.go @@ -54,18 +54,19 @@ func NewXRPLToCoreumProcess( } // Start starts the process. -func (o *XRPLToCoreumProcess) Start(ctx context.Context) error { +func (p *XRPLToCoreumProcess) Start(ctx context.Context) error { + p.log.Info(ctx, "Starting XRPL to Coreum process") txCh := make(chan rippledata.TransactionWithMetaData) return parallel.Run(ctx, func(ctx context.Context, spawn parallel.SpawnFn) error { spawn("tx-scanner", parallel.Continue, func(ctx context.Context) error { defer close(txCh) - return o.txScanner.ScanTxs(ctx, txCh) + return p.txScanner.ScanTxs(ctx, txCh) }) spawn("tx-processor", parallel.Fail, func(ctx context.Context) error { for tx := range txCh { - if err := o.processTx(ctx, tx); err != nil { + if err := p.processTx(ctx, tx); err != nil { if errors.Is(err, context.Canceled) { - o.log.Warn(ctx, "Context canceled during the XRPL tx processing", zap.String("error", err.Error())) + p.log.Warn(ctx, "Context canceled during the XRPL tx processing", zap.String("error", err.Error())) } else { return errors.Wrapf(err, "failed to process XRPL tx, txHash:%s", strings.ToUpper(tx.GetHash().String())) } @@ -75,26 +76,26 @@ func (o *XRPLToCoreumProcess) Start(ctx context.Context) error { }) return nil - }, parallel.WithGroupLogger(o.log)) + }, parallel.WithGroupLogger(p.log)) } -func (o *XRPLToCoreumProcess) processTx(ctx context.Context, tx rippledata.TransactionWithMetaData) error { +func (p *XRPLToCoreumProcess) processTx(ctx context.Context, tx rippledata.TransactionWithMetaData) error { ctx = tracing.WithTracingXRPLTxHash(tracing.WithTracingID(ctx), strings.ToUpper(tx.GetHash().String())) if !txIsFinal(tx) { - o.log.Debug(ctx, "Transaction is not final", zap.String("txStatus", tx.MetaData.TransactionResult.String())) + p.log.Debug(ctx, "Transaction is not final", zap.String("txStatus", tx.MetaData.TransactionResult.String())) return nil } - if o.cfg.BridgeXRPLAddress == tx.GetBase().Account { - return o.processOutgoingTx(ctx, tx) + if p.cfg.BridgeXRPLAddress == tx.GetBase().Account { + return p.processOutgoingTx(ctx, tx) } - return o.processIncomingTx(ctx, tx) + return p.processIncomingTx(ctx, tx) } -func (o *XRPLToCoreumProcess) processIncomingTx(ctx context.Context, tx rippledata.TransactionWithMetaData) error { +func (p *XRPLToCoreumProcess) processIncomingTx(ctx context.Context, tx rippledata.TransactionWithMetaData) error { txType := tx.GetType() if !tx.MetaData.TransactionResult.Success() { - o.log.Debug( + p.log.Debug( ctx, "Skipping not successful transaction", zap.String("type", txType), @@ -103,10 +104,10 @@ func (o *XRPLToCoreumProcess) processIncomingTx(ctx context.Context, tx rippleda return nil } - o.log.Debug(ctx, "Start processing of XRPL incoming tx", zap.String("type", txType)) + p.log.Debug(ctx, "Start processing of XRPL incoming tx", zap.String("type", txType)) // we process only incoming payment transactions, other transactions are ignored if txType != rippledata.PAYMENT.String() { - o.log.Debug(ctx, "Skipping not payment transaction", zap.String("type", txType)) + p.log.Debug(ctx, "Skipping not payment transaction", zap.String("type", txType)) return nil } paymentTx, ok := tx.Transaction.(*rippledata.Payment) @@ -115,7 +116,7 @@ func (o *XRPLToCoreumProcess) processIncomingTx(ctx context.Context, tx rippleda } coreumRecipient := xrpl.DecodeCoreumRecipientFromMemo(paymentTx.Memos) if coreumRecipient == nil { - o.log.Info(ctx, "Bridge memo does not include expected structure", zap.Any("memos", paymentTx.Memos)) + p.log.Info(ctx, "Bridge memo does not include expected structure", zap.Any("memos", paymentTx.Memos)) return nil } @@ -124,7 +125,7 @@ func (o *XRPLToCoreumProcess) processIncomingTx(ctx context.Context, tx rippleda coreumAmount, err := ConvertXRPLAmountToCoreumAmount(*deliveredXRPLAmount) if err != nil { if errors.Is(err, ErrSDKMathIntOutOfBounds) || errors.Is(err, ErrContractUint128OutOfBounds) { - o.log.Info( + p.log.Info( ctx, "Found XRPL transaction with out of bounds amount", zap.String("amount", deliveredXRPLAmount.String()), @@ -135,7 +136,7 @@ func (o *XRPLToCoreumProcess) processIncomingTx(ctx context.Context, tx rippleda } if coreumAmount.IsZero() { - o.log.Info(ctx, "Nothing to send, amount is zero") + p.log.Info(ctx, "Nothing to send, amount is zero") return nil } @@ -147,23 +148,24 @@ func (o *XRPLToCoreumProcess) processIncomingTx(ctx context.Context, tx rippleda Recipient: coreumRecipient, } - _, err = o.contractClient.SendXRPLToCoreumTransferEvidence(ctx, o.cfg.RelayerCoreumAddress, evidence) + _, err = p.contractClient.SendXRPLToCoreumTransferEvidence(ctx, p.cfg.RelayerCoreumAddress, evidence) if err == nil { + p.log.Info(ctx, "Successfully sent XRPL to Coreum transfer evidence", zap.Any("evidence", evidence)) return nil } if coreum.IsTokenNotRegisteredError(err) { - o.log.Info(ctx, "Token not registered") + p.log.Info(ctx, "Token not registered") return nil } if IsExpectedEvidenceSubmissionError(err) { - o.log.Debug(ctx, "Received expected evidence submission error", zap.String("errText", err.Error())) + p.log.Debug(ctx, "Received expected evidence submission error", zap.String("errText", err.Error())) return nil } if coreum.IsAssetFTStateError(err) { - o.log.Info( + p.log.Info( ctx, "The evidence saving is failed because of the asset FT rules, the evidence is skipped", zap.Any("evidence", evidence), @@ -172,7 +174,7 @@ func (o *XRPLToCoreumProcess) processIncomingTx(ctx context.Context, tx rippleda } if coreum.IsRecipientBlockedError(err) { - o.log.Info( + p.log.Info( ctx, "The evidence saving is failed because of the recipient address is blocked, the evidence is skipped", zap.Any("evidence", evidence), @@ -183,32 +185,32 @@ func (o *XRPLToCoreumProcess) processIncomingTx(ctx context.Context, tx rippleda return err } -func (o *XRPLToCoreumProcess) processOutgoingTx(ctx context.Context, tx rippledata.TransactionWithMetaData) error { +func (p *XRPLToCoreumProcess) processOutgoingTx(ctx context.Context, tx rippledata.TransactionWithMetaData) error { txType := tx.GetType() - o.log.Debug(ctx, "Start processing of XRPL outgoing tx", + p.log.Debug(ctx, "Start processing of XRPL outgoing tx", zap.String("type", txType), ) switch txType { case rippledata.TICKET_CREATE.String(): - return o.sendXRPLTicketsAllocationTransactionResultEvidence(ctx, tx) + return p.sendXRPLTicketsAllocationTransactionResultEvidence(ctx, tx) case rippledata.TRUST_SET.String(): - return o.sendXRPLTrustSetTransactionResultEvidence(ctx, tx) + return p.sendXRPLTrustSetTransactionResultEvidence(ctx, tx) case rippledata.PAYMENT.String(): - return o.sendCoreumToXRPLTransferTransactionResultEvidence(ctx, tx) + return p.sendCoreumToXRPLTransferTransactionResultEvidence(ctx, tx) case rippledata.SIGNER_LIST_SET.String(): - return o.sendKeysRotationTransactionResultEvidence(ctx, tx) + return p.sendKeysRotationTransactionResultEvidence(ctx, tx) // types which we use initially for the account set up case rippledata.ACCOUNT_SET.String(): - o.log.Debug(ctx, "Skipped expected tx type", zap.String("txType", txType), zap.Any("tx", tx)) + p.log.Debug(ctx, "Skipped expected tx type", zap.String("txType", txType), zap.Any("tx", tx)) return nil default: - o.log.Error(ctx, "Found unsupported transaction type", zap.Any("tx", tx)) + p.log.Error(ctx, "Found unsupported transaction type", zap.Any("tx", tx)) return nil } } -func (o *XRPLToCoreumProcess) sendXRPLTicketsAllocationTransactionResultEvidence( +func (p *XRPLToCoreumProcess) sendXRPLTicketsAllocationTransactionResultEvidence( ctx context.Context, tx rippledata.TransactionWithMetaData, ) error { @@ -234,16 +236,16 @@ func (o *XRPLToCoreumProcess) sendXRPLTicketsAllocationTransactionResultEvidence if ticketCreateTx.TicketSequence != nil && *ticketCreateTx.TicketSequence != 0 { evidence.TicketSequence = lo.ToPtr(*ticketCreateTx.TicketSequence) } - _, err := o.contractClient.SendXRPLTicketsAllocationTransactionResultEvidence( + _, err := p.contractClient.SendXRPLTicketsAllocationTransactionResultEvidence( ctx, - o.cfg.RelayerCoreumAddress, + p.cfg.RelayerCoreumAddress, evidence, ) - return o.handleEvidenceSubmissionError(ctx, err, tx, evidence.XRPLTransactionResultEvidence) + return p.handleOperationEvidenceSubmissionError(ctx, err, tx, evidence.XRPLTransactionResultEvidence) } -func (o *XRPLToCoreumProcess) sendXRPLTrustSetTransactionResultEvidence( +func (p *XRPLToCoreumProcess) sendXRPLTrustSetTransactionResultEvidence( ctx context.Context, tx rippledata.TransactionWithMetaData, ) error { @@ -259,16 +261,16 @@ func (o *XRPLToCoreumProcess) sendXRPLTrustSetTransactionResultEvidence( }, } - _, err := o.contractClient.SendXRPLTrustSetTransactionResultEvidence( + _, err := p.contractClient.SendXRPLTrustSetTransactionResultEvidence( ctx, - o.cfg.RelayerCoreumAddress, + p.cfg.RelayerCoreumAddress, evidence, ) - return o.handleEvidenceSubmissionError(ctx, err, tx, evidence.XRPLTransactionResultEvidence) + return p.handleOperationEvidenceSubmissionError(ctx, err, tx, evidence.XRPLTransactionResultEvidence) } -func (o *XRPLToCoreumProcess) sendCoreumToXRPLTransferTransactionResultEvidence( +func (p *XRPLToCoreumProcess) sendCoreumToXRPLTransferTransactionResultEvidence( ctx context.Context, tx rippledata.TransactionWithMetaData, ) error { @@ -284,16 +286,16 @@ func (o *XRPLToCoreumProcess) sendCoreumToXRPLTransferTransactionResultEvidence( }, } - _, err := o.contractClient.SendCoreumToXRPLTransferTransactionResultEvidence( + _, err := p.contractClient.SendCoreumToXRPLTransferTransactionResultEvidence( ctx, - o.cfg.RelayerCoreumAddress, + p.cfg.RelayerCoreumAddress, evidence, ) - return o.handleEvidenceSubmissionError(ctx, err, tx, evidence.XRPLTransactionResultEvidence) + return p.handleOperationEvidenceSubmissionError(ctx, err, tx, evidence.XRPLTransactionResultEvidence) } -func (o *XRPLToCoreumProcess) sendKeysRotationTransactionResultEvidence( +func (p *XRPLToCoreumProcess) sendKeysRotationTransactionResultEvidence( ctx context.Context, tx rippledata.TransactionWithMetaData, ) error { @@ -309,7 +311,7 @@ func (o *XRPLToCoreumProcess) sendKeysRotationTransactionResultEvidence( } // handle the case when the tx was set initially to set up the bridge if signerListSetTx.Sequence != 0 { - o.log.Debug( + p.log.Debug( ctx, "Skipping the evidence sending for the tx, since the SignerListSet tx contains account sequence.", zap.Any("tx", tx), @@ -319,29 +321,32 @@ func (o *XRPLToCoreumProcess) sendKeysRotationTransactionResultEvidence( if signerListSetTx.TicketSequence != nil && *signerListSetTx.TicketSequence != 0 { evidence.TicketSequence = lo.ToPtr(*signerListSetTx.TicketSequence) } - _, err := o.contractClient.SendKeysRotationTransactionResultEvidence( + _, err := p.contractClient.SendKeysRotationTransactionResultEvidence( ctx, - o.cfg.RelayerCoreumAddress, + p.cfg.RelayerCoreumAddress, evidence, ) - return o.handleEvidenceSubmissionError(ctx, err, tx, evidence.XRPLTransactionResultEvidence) + return p.handleOperationEvidenceSubmissionError(ctx, err, tx, evidence.XRPLTransactionResultEvidence) } -func (o *XRPLToCoreumProcess) handleEvidenceSubmissionError( +func (p *XRPLToCoreumProcess) handleOperationEvidenceSubmissionError( ctx context.Context, err error, tx rippledata.TransactionWithMetaData, evidence coreum.XRPLTransactionResultEvidence, ) error { if err == nil { - if evidence.TransactionResult != coreum.TransactionResultAccepted { - o.log.Info(ctx, "Transaction was rejected", zap.String("txResult", tx.MetaData.TransactionResult.String())) - } + p.log.Info( + ctx, + "Successfully sent operation evidence", + zap.String("txResult", tx.MetaData.TransactionResult.String()), + zap.Any("evidence", evidence), + ) return nil } if IsExpectedEvidenceSubmissionError(err) { - o.log.Debug(ctx, "Received expected evidence submission error", zap.String("errText", err.Error())) + p.log.Debug(ctx, "Received expected evidence submission error", zap.String("errText", err.Error())) return nil } return err diff --git a/relayer/runner/runner.go b/relayer/runner/runner.go index 3cae223c..c9cfe030 100644 --- a/relayer/runner/runner.go +++ b/relayer/runner/runner.go @@ -10,6 +10,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keyring" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/google/uuid" "github.com/pkg/errors" rippledata "github.com/rubblelabs/ripple/data" "go.uber.org/zap" @@ -23,6 +24,7 @@ import ( coreumchainclient "github.com/CoreumFoundation/coreum/v4/pkg/client" coreumchainconfig "github.com/CoreumFoundation/coreum/v4/pkg/config" coreumchainconstant "github.com/CoreumFoundation/coreum/v4/pkg/config/constant" + coreumkeyring "github.com/CoreumFoundation/coreum/v4/pkg/keyring" "github.com/CoreumFoundation/coreumbridge-xrpl/relayer/coreum" "github.com/CoreumFoundation/coreumbridge-xrpl/relayer/logger" "github.com/CoreumFoundation/coreumbridge-xrpl/relayer/metrics" @@ -54,11 +56,17 @@ func NewRunner(ctx context.Context, components Components, cfg Config) (*Runner, return nil, errors.New("contract address is not configured") } - relayerAddress, err := getAddressFromKeyring(components.CoreumClientCtx.Keyring(), cfg.Coreum.RelayerKeyName) + coreumRelayerAddress, err := getAddressFromKeyring(components.CoreumClientCtx.Keyring(), cfg.Coreum.RelayerKeyName) if err != nil { return nil, err } + // load the key form the XRPL KR to check that it exists, and to let the user give access to the KR + _, err = components.XRPLKeyringTxSigner.Account(cfg.XRPL.MultiSignerKeyName) + if err != nil { + return nil, errors.Wrapf(err, "failed to get key from the XRPL keyring, key name:%s", cfg.XRPL.MultiSignerKeyName) + } + contractConfig, err := components.CoreumContractClient.GetContractConfig(ctx) if err != nil { return nil, errors.Wrapf(err, "failed to get contract config for the runner intialization") @@ -82,7 +90,7 @@ func NewRunner(ctx context.Context, components Components, cfg Config) (*Runner, xrplToCoreumProcess, err := processes.NewXRPLToCoreumProcess( processes.XRPLToCoreumProcessConfig{ BridgeXRPLAddress: *bridgeXRPLAddress, - RelayerCoreumAddress: relayerAddress, + RelayerCoreumAddress: coreumRelayerAddress, }, components.Log, xrplScanner, @@ -95,7 +103,7 @@ func NewRunner(ctx context.Context, components Components, cfg Config) (*Runner, coreumToXRPLProcess, err := processes.NewCoreumToXRPLProcess( processes.CoreumToXRPLProcessConfig{ BridgeXRPLAddress: *bridgeXRPLAddress, - RelayerCoreumAddress: relayerAddress, + RelayerCoreumAddress: coreumRelayerAddress, XRPLTxSignerKeyName: cfg.XRPL.MultiSignerKeyName, RepeatRecentScan: true, RepeatDelay: cfg.Processes.CoreumToXRPLProcess.RepeatDelay, @@ -179,13 +187,26 @@ func NewComponents( coreumKeyring keyring.Keyring, log logger.Logger, setCoreumSDKConfig bool, + reimportRelayerKeys bool, ) (Components, error) { + // if enabled, re-import the relayer keys from the config to in-memory keyring + if reimportRelayerKeys { + var err error + xrplKeyring, err = reimportKeyIntoInMemoryKR(xrplKeyring, cfg.XRPL.MultiSignerKeyName) + if err != nil { + return Components{}, err + } + coreumKeyring, err = reimportKeyIntoInMemoryKR(coreumKeyring, cfg.Coreum.RelayerKeyName) + if err != nil { + return Components{}, err + } + } + metricSet := metrics.NewRegistry() log, err := logger.WithMetrics(log, metricSet.ErrorCounter) if err != nil { return Components{}, err } - components := Components{ Metrics: metricSet, Log: log, @@ -260,7 +281,6 @@ func NewComponents( } components.CoreumClientCtx = coreumClientCtx - return components, nil } @@ -280,6 +300,25 @@ func getAddressFromKeyring(kr keyring.Keyring, keyName string) (sdk.AccAddress, return addr, nil } +func reimportKeyIntoInMemoryKR(sourceKr keyring.Keyring, keyName string) (keyring.Keyring, error) { + keyInfo, err := sourceKr.Key(keyName) + if err != nil { + return nil, errors.Wrapf(err, fmt.Sprintf("failed to get key from keyring, key name:%s", keyName)) + } + pass := uuid.NewString() + armor, err := sourceKr.ExportPrivKeyArmor(keyInfo.Name, pass) + if err != nil { + return nil, errors.Wrapf(err, "failed to export key") + } + encodingConfig := coreumchainconfig.NewEncodingConfig(coreumapp.ModuleBasics) + kr := coreumkeyring.NewConcurrentSafeKeyring(keyring.NewInMemory(encodingConfig.Codec)) + if err := kr.ImportPrivKey(keyInfo.Name, armor, pass); err != nil { + return nil, errors.Wrapf(err, "failed to import key") + } + + return kr, nil +} + func getGRPCClientConn(grpcURL string) (*grpc.ClientConn, error) { parsedURL, err := url.Parse(grpcURL) if err != nil {