diff --git a/Dockerfile-versioned-source b/Dockerfile-versioned-source index 67c4ac4273..528cf3d525 100644 --- a/Dockerfile-versioned-source +++ b/Dockerfile-versioned-source @@ -17,7 +17,7 @@ WORKDIR /go/delivery/zeta-node RUN mkdir -p $GOPATH/bin/old RUN mkdir -p $GOPATH/bin/new -ENV NEW_VERSION=v12.1.0 +ENV NEW_VERSION=v12.3.0 # Build new release from the current source COPY go.mod /go/delivery/zeta-node/ diff --git a/Makefile b/Makefile index 03bb656d1d..0c00dd615a 100644 --- a/Makefile +++ b/Makefile @@ -240,7 +240,7 @@ stateful-upgrade: stateful-upgrade-source: @echo "--> Starting stateful smoketest" - $(DOCKER) build --build-arg old_version=v12.0.0 -t zetanode -f ./Dockerfile-versioned-source . + $(DOCKER) build --build-arg old_version=v12.2.1 -t zetanode -f ./Dockerfile-versioned-source . $(DOCKER) build -t orchestrator -f contrib/localnet/orchestrator/Dockerfile-upgrade.fastbuild . cd contrib/localnet/ && $(DOCKER) compose -f docker-compose-stateful.yml up -d diff --git a/app/setup_handlers.go b/app/setup_handlers.go index 0ed66638d1..da037ba1a2 100644 --- a/app/setup_handlers.go +++ b/app/setup_handlers.go @@ -5,9 +5,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/upgrade/types" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" ) -const releaseVersion = "v12.2.0" +const releaseVersion = "v12.3.0" func SetupHandlers(app *App) { app.UpgradeKeeper.SetUpgradeHandler(releaseVersion, func(ctx sdk.Context, plan types.Plan, vm module.VersionMap) (module.VersionMap, error) { @@ -16,6 +17,7 @@ func SetupHandlers(app *App) { for m, mb := range app.mm.Modules { vm[m] = mb.ConsensusVersion() } + VersionMigrator{v: vm}.TriggerMigration(crosschaintypes.ModuleName) return app.mm.RunMigrations(ctx, app.configurator, vm) }) diff --git a/changelog.md b/changelog.md index 0119174c74..cf21cc81ba 100644 --- a/changelog.md +++ b/changelog.md @@ -12,6 +12,10 @@ * [1731](https://github.com/zeta-chain/node/pull/1731) added doc for hotkey and tss key-share password prompts. +### Features + +*[1728] (https://github.com/zeta-chain/node/pull/1728) - allow aborted transactions to be refunded by minting tokens to zEvm. + ### Refactor * [1630](https://github.com/zeta-chain/node/pull/1630) added password prompts for hotkey and tss keyshare in zetaclient diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/smoketests.go b/contrib/localnet/orchestrator/smoketest/smoketests/smoketests.go index 92d4c97030..300ec2e866 100644 --- a/contrib/localnet/orchestrator/smoketest/smoketests/smoketests.go +++ b/contrib/localnet/orchestrator/smoketest/smoketests/smoketests.go @@ -2,6 +2,8 @@ package smoketests import "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" +// TODO : Add smoke test for abort refund +// https://github.com/zeta-chain/node/issues/1745 const ( TestContextUpgradeName = "context_upgrade" TestDepositAndCallRefundName = "deposit_and_call_refund" diff --git a/contrib/localnet/orchestrator/smoketest/smoketests/test_erc20_refund.go b/contrib/localnet/orchestrator/smoketest/smoketests/test_erc20_refund.go index dcfc86acc1..e7728dda15 100644 --- a/contrib/localnet/orchestrator/smoketest/smoketests/test_erc20_refund.go +++ b/contrib/localnet/orchestrator/smoketest/smoketests/test_erc20_refund.go @@ -10,11 +10,11 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/runner" "github.com/zeta-chain/zetacore/contrib/localnet/orchestrator/smoketest/utils" - "github.com/zeta-chain/zetacore/x/crosschain/types" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" ) func TestERC20DepositAndCallRefund(sm *runner.SmokeTestRunner) { - // Get the initial balance of the deployer + //Get the initial balance of the deployer initialBal, err := sm.USDTZRC20.BalanceOf(&bind.CallOpts{}, sm.DeployerAddress) if err != nil { panic(err) @@ -33,11 +33,25 @@ func TestERC20DepositAndCallRefund(sm *runner.SmokeTestRunner) { // There is no liquidity pool, therefore the cctx should abort cctx := utils.WaitCctxMinedByInTxHash(sm.Ctx, inTxHash, sm.CctxClient, sm.Logger, sm.CctxTimeout) sm.Logger.CCTX(*cctx, "deposit") - if cctx.CctxStatus.Status != types.CctxStatus_Aborted { + if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_Aborted { panic(fmt.Sprintf("expected cctx status to be Aborted; got %s", cctx.CctxStatus.Status)) } - // Check that the erc20 in the aborted cctx was refunded on ZetaChain + if cctx.CctxStatus.IsAbortRefunded != false { + panic(fmt.Sprintf("expected cctx status to be not refunded; got %t", cctx.CctxStatus.IsAbortRefunded)) + } + + sm.Logger.Info("Refunding the cctx via admin") + msg := crosschaintypes.NewMsgRefundAbortedCCTX( + sm.ZetaTxServer.GetAccountAddress(0), + cctx.Index, + sm.DeployerAddress.String()) + _, err = sm.ZetaTxServer.BroadcastTx(utils.FungibleAdminName, msg) + if err != nil { + panic(err) + } + + //Check that the erc20 in the aborted cctx was refunded on ZetaChain newBalance, err := sm.USDTZRC20.BalanceOf(&bind.CallOpts{}, sm.DeployerAddress) if err != nil { panic(err) @@ -46,7 +60,7 @@ func TestERC20DepositAndCallRefund(sm *runner.SmokeTestRunner) { if newBalance.Cmp(expectedBalance) != 0 { panic(fmt.Sprintf("expected balance to be %s after refund; got %s", expectedBalance.String(), newBalance.String())) } - sm.Logger.Info("CCTX has been aborted and the erc20 has been refunded on ZetaChain") + sm.Logger.Info("CCTX has been aborted on ZetaChain") // test refund when there is a liquidity pool sm.Logger.Info("Sending a deposit that should revert with a liquidity pool") @@ -75,7 +89,7 @@ func TestERC20DepositAndCallRefund(sm *runner.SmokeTestRunner) { cctx = utils.WaitCctxMinedByInTxHash(sm.Ctx, inTxHash, sm.CctxClient, sm.Logger, sm.CctxTimeout) // the revert tx creation will fail because the sender, used as the recipient, is not defined in the cctx - if cctx.CctxStatus.Status != types.CctxStatus_Reverted { + if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_Reverted { panic(fmt.Sprintf( "expected cctx status to be PendingRevert; got %s, aborted message: %s", cctx.CctxStatus.Status, diff --git a/contrib/localnet/orchestrator/smoketest/txserver/zeta_tx_server.go b/contrib/localnet/orchestrator/smoketest/txserver/zeta_tx_server.go index d43cbd7fe6..ee63851a74 100644 --- a/contrib/localnet/orchestrator/smoketest/txserver/zeta_tx_server.go +++ b/contrib/localnet/orchestrator/smoketest/txserver/zeta_tx_server.go @@ -121,6 +121,11 @@ func (zts ZetaTxServer) GetAccountAddress(index int) string { return zts.address[index] } +func (zts ZetaTxServer) GetAllAccountAddress() []string { + return zts.address + +} + // GetAccountMnemonic returns the account name from the given index // returns empty string if index is out of bound, error should be handled by caller func (zts ZetaTxServer) GetAccountMnemonic(index int) string { diff --git a/docs/cli/zetacored/zetacored_tx_crosschain.md b/docs/cli/zetacored/zetacored_tx_crosschain.md index bb8a200308..8b66894327 100644 --- a/docs/cli/zetacored/zetacored_tx_crosschain.md +++ b/docs/cli/zetacored/zetacored_tx_crosschain.md @@ -34,6 +34,7 @@ zetacored tx crosschain [flags] * [zetacored tx crosschain inbound-voter](zetacored_tx_crosschain_inbound-voter.md) - Broadcast message sendVoter * [zetacored tx crosschain migrate-tss-funds](zetacored_tx_crosschain_migrate-tss-funds.md) - Migrate TSS funds to the latest TSS address * [zetacored tx crosschain outbound-voter](zetacored_tx_crosschain_outbound-voter.md) - Broadcast message receiveConfirmation +* [zetacored tx crosschain refund-aborted](zetacored_tx_crosschain_refund-aborted.md) - Refund an aborted tx , the refund address is optional, if not provided, the refund will be sent to the sender/tx origin of the cctx. * [zetacored tx crosschain remove-from-out-tx-tracker](zetacored_tx_crosschain_remove-from-out-tx-tracker.md) - Remove a out-tx-tracker * [zetacored tx crosschain update-tss-address](zetacored_tx_crosschain_update-tss-address.md) - Create a new TSSVoter diff --git a/docs/cli/zetacored/zetacored_tx_crosschain_refund-aborted.md b/docs/cli/zetacored/zetacored_tx_crosschain_refund-aborted.md new file mode 100644 index 0000000000..515d85dc32 --- /dev/null +++ b/docs/cli/zetacored/zetacored_tx_crosschain_refund-aborted.md @@ -0,0 +1,52 @@ +# tx crosschain refund-aborted + +Refund an aborted tx , the refund address is optional, if not provided, the refund will be sent to the sender/tx origin of the cctx. + +``` +zetacored tx crosschain refund-aborted [cctx-index] [refund-address] [flags] +``` + +### Options + +``` + -a, --account-number uint The account number of the signing account (offline mode only) + --aux Generate aux signer data instead of sending a tx + -b, --broadcast-mode string Transaction broadcasting mode (sync|async|block) + --dry-run ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it (when enabled, the local Keybase is not accessible) + --fee-granter string Fee granter grants fees for the transaction + --fee-payer string Fee payer pays fees for the transaction instead of deducting from the signer + --fees string Fees to pay along with transaction; eg: 10uatom + --from string Name or address of private key with which to sign + --gas string gas limit to set per-transaction; set to "auto" to calculate sufficient gas automatically. Note: "auto" option doesn't always report accurate results. Set a valid coin value to adjust the result. Can be used instead of "fees". (default 200000) + --gas-adjustment float adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored (default 1) + --gas-prices string Gas prices in decimal format to determine the transaction fee (e.g. 0.1uatom) + --generate-only Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase only accessed when providing a key name) + -h, --help help for refund-aborted + --keyring-backend string Select keyring's backend (os|file|kwallet|pass|test|memory) + --keyring-dir string The client Keyring directory; if omitted, the default 'home' directory will be used + --ledger Use a connected Ledger device + --node string [host]:[port] to tendermint rpc interface for this chain + --note string Note to add a description to the transaction (previously --memo) + --offline Offline mode (does not allow any online functionality) + -o, --output string Output format (text|json) + -s, --sequence uint The sequence number of the signing account (offline mode only) + --sign-mode string Choose sign mode (direct|amino-json|direct-aux), this is an advanced feature + --timeout-height uint Set a block timeout height to prevent the tx from being committed past a certain height + --tip string Tip is the amount that is going to be transferred to the fee payer on the target chain. This flag is only valid when used with --aux, and is ignored if the target chain didn't enable the TipDecorator + -y, --yes Skip tx broadcasting prompt confirmation +``` + +### Options inherited from parent commands + +``` + --chain-id string The network chain ID + --home string directory for config and data + --log_format string The logging format (json|plain) + --log_level string The logging level (trace|debug|info|warn|error|fatal|panic) + --trace print out full stack trace on errors +``` + +### SEE ALSO + +* [zetacored tx crosschain](zetacored_tx_crosschain.md) - crosschain transactions subcommands + diff --git a/docs/openapi/openapi.swagger.yaml b/docs/openapi/openapi.swagger.yaml index f9a8b1cd43..a2108f73b1 100644 --- a/docs/openapi/openapi.swagger.yaml +++ b/docs/openapi/openapi.swagger.yaml @@ -53588,7 +53588,7 @@ definitions: - OutboundMined: the corresponding outbound tx is mined - PendingRevert: outbound cannot succeed; should revert inbound - Reverted: inbound reverted. - - Aborted: inbound tx error or invalid paramters and cannot revert; just abort + - Aborted: inbound tx error or invalid paramters and cannot revert; just abort. But the amount can be refunded to zetachain using and admin proposal crosschainCrossChainTx: type: object properties: @@ -53718,6 +53718,8 @@ definitions: type: object crosschainMsgMigrateTssFundsResponse: type: object + crosschainMsgRefundAbortedCCTXResponse: + type: object crosschainMsgRemoveFromOutTxTrackerResponse: type: object crosschainMsgUpdateTssAddressResponse: @@ -54717,6 +54719,8 @@ definitions: lastUpdate_timestamp: type: string format: int64 + isAbortRefunded: + type: boolean zetacoreemissionsParams: type: object properties: diff --git a/docs/spec/crosschain/messages.md b/docs/spec/crosschain/messages.md index 1659a95bf0..f69a931598 100644 --- a/docs/spec/crosschain/messages.md +++ b/docs/spec/crosschain/messages.md @@ -270,3 +270,19 @@ message MsgAbortStuckCCTX { } ``` +## MsgRefundAbortedCCTX + +RefundAbortedCCTX refunds the aborted CCTX. +It verifies if the CCTX is aborted and not refunded, and if the refund address is valid. +It refunds the amount to the refund address and sets the CCTX as refunded. +Refer to documentation for GetRefundAddress for the refund address logic. +Refer to documentation for GetAbortedAmount for the aborted amount logic. + +```proto +message MsgRefundAbortedCCTX { + string creator = 1; + string cctx_index = 2; + string refund_address = 3; +} +``` + diff --git a/proto/crosschain/cross_chain_tx.proto b/proto/crosschain/cross_chain_tx.proto index 28f9d92452..28aeab85db 100644 --- a/proto/crosschain/cross_chain_tx.proto +++ b/proto/crosschain/cross_chain_tx.proto @@ -13,7 +13,7 @@ enum CctxStatus { OutboundMined = 3; // the corresponding outbound tx is mined PendingRevert = 4; // outbound cannot succeed; should revert inbound Reverted = 5; // inbound reverted. - Aborted = 6; // inbound tx error or invalid paramters and cannot revert; just abort + Aborted = 6; // inbound tx error or invalid paramters and cannot revert; just abort. But the amount can be refunded to zetachain using and admin proposal } enum TxFinalizationStatus { @@ -77,6 +77,7 @@ message Status { CctxStatus status = 1; string status_message = 2; int64 lastUpdate_timestamp = 3; + bool isAbortRefunded = 4; } message CrossChainTx { diff --git a/proto/crosschain/tx.proto b/proto/crosschain/tx.proto index 3ccfba1911..5cee0f1f21 100644 --- a/proto/crosschain/tx.proto +++ b/proto/crosschain/tx.proto @@ -21,6 +21,7 @@ service Msg { rpc CreateTSSVoter(MsgCreateTSSVoter) returns (MsgCreateTSSVoterResponse); rpc AbortStuckCCTX(MsgAbortStuckCCTX) returns (MsgAbortStuckCCTXResponse); + rpc RefundAbortedCCTX(MsgRefundAbortedCCTX) returns (MsgRefundAbortedCCTXResponse); } message MsgCreateTSSVoter { @@ -162,3 +163,11 @@ message MsgAbortStuckCCTX { } message MsgAbortStuckCCTXResponse {} + +message MsgRefundAbortedCCTX { + string creator = 1; + string cctx_index = 2; + string refund_address = 3; // if not provided, the refund will be sent to the sender/txOrgin +} + +message MsgRefundAbortedCCTXResponse {} diff --git a/testutil/sample/crosschain.go b/testutil/sample/crosschain.go index 35082be7d1..b516100597 100644 --- a/testutil/sample/crosschain.go +++ b/testutil/sample/crosschain.go @@ -5,6 +5,7 @@ import ( "testing" "cosmossdk.io/math" + "github.com/ethereum/go-ethereum/crypto" "github.com/zeta-chain/zetacore/common" "github.com/zeta-chain/zetacore/x/crosschain/types" ) @@ -75,12 +76,16 @@ func Status(t *testing.T, index string) *types.Status { } } +func GetCctxIndexFromString(index string) string { + return crypto.Keccak256Hash([]byte(index)).String() +} + func CrossChainTx(t *testing.T, index string) *types.CrossChainTx { r := newRandFromStringSeed(t, index) return &types.CrossChainTx{ Creator: AccAddress(), - Index: index, + Index: GetCctxIndexFromString(index), ZetaFees: math.NewUint(uint64(r.Int63())), RelayedMessage: StringRandom(r, 32), CctxStatus: Status(t, index), diff --git a/typescript/crosschain/cross_chain_tx_pb.d.ts b/typescript/crosschain/cross_chain_tx_pb.d.ts index d8216afcff..0157548d54 100644 --- a/typescript/crosschain/cross_chain_tx_pb.d.ts +++ b/typescript/crosschain/cross_chain_tx_pb.d.ts @@ -47,7 +47,7 @@ export declare enum CctxStatus { Reverted = 5, /** - * inbound tx error or invalid paramters and cannot revert; just abort + * inbound tx error or invalid paramters and cannot revert; just abort. But the amount can be refunded to zetachain using and admin proposal * * @generated from enum value: Aborted = 6; */ @@ -302,6 +302,11 @@ export declare class Status extends Message { */ lastUpdateTimestamp: bigint; + /** + * @generated from field: bool isAbortRefunded = 4; + */ + isAbortRefunded: boolean; + constructor(data?: PartialMessage); static readonly runtime: typeof proto3; diff --git a/typescript/crosschain/tx_pb.d.ts b/typescript/crosschain/tx_pb.d.ts index b96408964c..2371a9405c 100644 --- a/typescript/crosschain/tx_pb.d.ts +++ b/typescript/crosschain/tx_pb.d.ts @@ -778,3 +778,58 @@ export declare class MsgAbortStuckCCTXResponse extends Message | undefined, b: MsgAbortStuckCCTXResponse | PlainMessage | undefined): boolean; } +/** + * @generated from message zetachain.zetacore.crosschain.MsgRefundAbortedCCTX + */ +export declare class MsgRefundAbortedCCTX extends Message { + /** + * @generated from field: string creator = 1; + */ + creator: string; + + /** + * @generated from field: string cctx_index = 2; + */ + cctxIndex: string; + + /** + * if not provided, the refund will be sent to the sender/txOrgin + * + * @generated from field: string refund_address = 3; + */ + refundAddress: string; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.crosschain.MsgRefundAbortedCCTX"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): MsgRefundAbortedCCTX; + + static fromJson(jsonValue: JsonValue, options?: Partial): MsgRefundAbortedCCTX; + + static fromJsonString(jsonString: string, options?: Partial): MsgRefundAbortedCCTX; + + static equals(a: MsgRefundAbortedCCTX | PlainMessage | undefined, b: MsgRefundAbortedCCTX | PlainMessage | undefined): boolean; +} + +/** + * @generated from message zetachain.zetacore.crosschain.MsgRefundAbortedCCTXResponse + */ +export declare class MsgRefundAbortedCCTXResponse extends Message { + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.crosschain.MsgRefundAbortedCCTXResponse"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): MsgRefundAbortedCCTXResponse; + + static fromJson(jsonValue: JsonValue, options?: Partial): MsgRefundAbortedCCTXResponse; + + static fromJsonString(jsonString: string, options?: Partial): MsgRefundAbortedCCTXResponse; + + static equals(a: MsgRefundAbortedCCTXResponse | PlainMessage | undefined, b: MsgRefundAbortedCCTXResponse | PlainMessage | undefined): boolean; +} + diff --git a/x/crosschain/client/cli/cli_refund_aborted.go b/x/crosschain/client/cli/cli_refund_aborted.go new file mode 100644 index 0000000000..12930dcbf0 --- /dev/null +++ b/x/crosschain/client/cli/cli_refund_aborted.go @@ -0,0 +1,31 @@ +package cli + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/spf13/cobra" + "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func CmdRefundAborted() *cobra.Command { + cmd := &cobra.Command{ + Use: "refund-aborted [cctx-index] [refund-address]", + Short: `Refund an aborted tx , the refund address is optional, if not provided, the refund will be sent to the sender/tx origin of the cctx.`, + Args: cobra.MaximumNArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + msg := types.NewMsgRefundAbortedCCTX(clientCtx.GetFromAddress().String(), args[0], args[1]) + err = msg.ValidateBasic() + if err != nil { + return err + } + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + flags.AddTxFlagsToCmd(cmd) + return cmd +} diff --git a/x/crosschain/client/cli/tx.go b/x/crosschain/client/cli/tx.go index d8e6b3bc0e..f5668fae44 100644 --- a/x/crosschain/client/cli/tx.go +++ b/x/crosschain/client/cli/tx.go @@ -31,6 +31,7 @@ func GetTxCmd() *cobra.Command { CmdMigrateTssFunds(), CmdAddToInTxTracker(), CmdAbortStuckCCTX(), + CmdRefundAborted(), ) return cmd diff --git a/x/crosschain/keeper/abci_test.go b/x/crosschain/keeper/abci_test.go index ea46e70dc4..4487d7dcec 100644 --- a/x/crosschain/keeper/abci_test.go +++ b/x/crosschain/keeper/abci_test.go @@ -58,7 +58,7 @@ func TestKeeper_IterateAndUpdateCctxGasPrice(t *testing.T) { createCctxWithNonceRange(t, ctx, *k, 40, 45, common.ZetaChainMainnet().ChainId, tss, zk) // set a cctx where the update function should fail to test that the next cctx are not updated but the next chains are - failMap["1-12"] = struct{}{} + failMap[sample.GetCctxIndexFromString("1-12")] = struct{}{} // test that the default crosschain flags are used when not set and the epoch length is not reached ctx = ctx.WithBlockHeight(observertypes.DefaultCrosschainFlags().GasPriceIncreaseFlags.EpochLength + 1) @@ -84,7 +84,6 @@ func TestKeeper_IterateAndUpdateCctxGasPrice(t *testing.T) { require.Equal(t, customFlags, flags) // test that cctx are iterated and updated when the epoch length is reached - ctx = ctx.WithBlockHeight(observertypes.DefaultCrosschainFlags().GasPriceIncreaseFlags.EpochLength * 2) cctxCount, flags = k.IterateAndUpdateCctxGasPrice(ctx, supportedChains, updateFunc) @@ -94,14 +93,14 @@ func TestKeeper_IterateAndUpdateCctxGasPrice(t *testing.T) { // check that the update function was called with the cctx index require.Equal(t, 7, len(updateFuncMap)) - require.Contains(t, updateFuncMap, "1-10") - require.Contains(t, updateFuncMap, "1-11") + require.Contains(t, updateFuncMap, sample.GetCctxIndexFromString("1-10")) + require.Contains(t, updateFuncMap, sample.GetCctxIndexFromString("1-11")) - require.Contains(t, updateFuncMap, "56-30") - require.Contains(t, updateFuncMap, "56-31") - require.Contains(t, updateFuncMap, "56-32") - require.Contains(t, updateFuncMap, "56-33") - require.Contains(t, updateFuncMap, "56-34") + require.Contains(t, updateFuncMap, sample.GetCctxIndexFromString("56-30")) + require.Contains(t, updateFuncMap, sample.GetCctxIndexFromString("56-31")) + require.Contains(t, updateFuncMap, sample.GetCctxIndexFromString("56-32")) + require.Contains(t, updateFuncMap, sample.GetCctxIndexFromString("56-33")) + require.Contains(t, updateFuncMap, sample.GetCctxIndexFromString("56-34")) } func TestCheckAndUpdateCctxGasPrice(t *testing.T) { diff --git a/x/crosschain/keeper/cctx.go b/x/crosschain/keeper/cctx.go index e3a2a06f71..7dad9f4197 100644 --- a/x/crosschain/keeper/cctx.go +++ b/x/crosschain/keeper/cctx.go @@ -49,7 +49,7 @@ func (k Keeper) SetCctxAndNonceToCctxAndInTxHashToCctx(ctx sdk.Context, cctx typ }) } if cctx.CctxStatus.Status == types.CctxStatus_Aborted && cctx.GetCurrentOutTxParam().CoinType == common.CoinType_Zeta { - k.AddZetaAbortedAmount(ctx, cctx.GetCurrentOutTxParam().Amount) + k.AddZetaAbortedAmount(ctx, GetAbortedAmount(cctx)) } } @@ -133,6 +133,7 @@ func (k Keeper) CreateNewCCTX(ctx sdk.Context, msg *types.MsgVoteOnObservedInbou Status: s, StatusMessage: "", LastUpdateTimestamp: ctx.BlockHeader().Time.Unix(), + IsAbortRefunded: false, } newCctx := types.CrossChainTx{ Creator: msg.Creator, diff --git a/x/crosschain/keeper/cctx_utils.go b/x/crosschain/keeper/cctx_utils.go index 7b43123cbe..8c2edd5013 100644 --- a/x/crosschain/keeper/cctx_utils.go +++ b/x/crosschain/keeper/cctx_utils.go @@ -4,7 +4,7 @@ import ( "fmt" cosmoserrors "cosmossdk.io/errors" - "cosmossdk.io/math" + sdkmath "cosmossdk.io/math" "github.com/pkg/errors" sdk "github.com/cosmos/cosmos-sdk/types" @@ -52,42 +52,6 @@ func (k Keeper) UpdateNonce(ctx sdk.Context, receiveChainID int64, cctx *types.C return nil } -// RefundAmountOnZetaChain refunds the amount of the cctx on ZetaChain in case of aborted cctx -// NOTE: GetCurrentOutTxParam should contain the last up to date cctx amount -func (k Keeper) RefundAmountOnZetaChain(ctx sdk.Context, cctx types.CrossChainTx, inputAmount math.Uint) error { - // preliminary checks - if cctx.InboundTxParams.CoinType != common.CoinType_ERC20 { - return errors.New("unsupported coin type for refund on ZetaChain") - } - if !common.IsEVMChain(cctx.InboundTxParams.SenderChainId) { - return errors.New("only EVM chains are supported for refund on ZetaChain") - } - sender := ethcommon.HexToAddress(cctx.InboundTxParams.Sender) - if sender == (ethcommon.Address{}) { - return errors.New("invalid sender address") - } - if inputAmount.IsNil() || inputAmount.IsZero() { - return errors.New("no amount to refund") - } - - // get address of the zrc20 - fc, found := k.fungibleKeeper.GetForeignCoinFromAsset(ctx, cctx.InboundTxParams.Asset, cctx.InboundTxParams.SenderChainId) - if !found { - return fmt.Errorf("asset %s zrc not found", cctx.InboundTxParams.Asset) - } - zrc20 := ethcommon.HexToAddress(fc.Zrc20ContractAddress) - if zrc20 == (ethcommon.Address{}) { - return fmt.Errorf("asset %s invalid zrc address", cctx.InboundTxParams.Asset) - } - - // deposit the amount to the sender - if _, err := k.fungibleKeeper.DepositZRC20(ctx, zrc20, sender, inputAmount.BigInt()); err != nil { - return errors.New("failed to deposit zrc20 on ZetaChain" + err.Error()) - } - - return nil -} - // GetRevertGasLimit returns the gas limit for the revert transaction in a CCTX // It returns 0 if there is no error but the gas limit can't be determined from the CCTX data func (k Keeper) GetRevertGasLimit(ctx sdk.Context, cctx types.CrossChainTx) (uint64, error) { @@ -126,3 +90,18 @@ func IsPending(cctx types.CrossChainTx) bool { // pending inbound is not considered a "pending" state because it has not reached consensus yet return cctx.CctxStatus.Status == types.CctxStatus_PendingOutbound || cctx.CctxStatus.Status == types.CctxStatus_PendingRevert } + +// GetAbortedAmount returns the amount to refund for a given CCTX . +// If the CCTX has an outbound transaction, it returns the amount of the outbound transaction. +// If OutTxParams is nil or the amount is zero, it returns the amount of the inbound transaction. +// This is because there might be a case where the transaction is set to be aborted before paying gas or creating an outbound transaction.In such a situation we can refund the entire amount that has been locked in connector or TSS +func GetAbortedAmount(cctx types.CrossChainTx) sdkmath.Uint { + if cctx.OutboundTxParams != nil && !cctx.GetCurrentOutTxParam().Amount.IsZero() { + return cctx.GetCurrentOutTxParam().Amount + } + if cctx.InboundTxParams != nil { + return cctx.InboundTxParams.Amount + } + + return sdkmath.ZeroUint() +} diff --git a/x/crosschain/keeper/cctx_utils_test.go b/x/crosschain/keeper/cctx_utils_test.go index 2d122bce7a..319ef35a54 100644 --- a/x/crosschain/keeper/cctx_utils_test.go +++ b/x/crosschain/keeper/cctx_utils_test.go @@ -4,142 +4,16 @@ import ( "math/big" "testing" - "cosmossdk.io/math" - + sdkmath "cosmossdk.io/math" "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/common" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" + crosschainkeeper "github.com/zeta-chain/zetacore/x/crosschain/keeper" "github.com/zeta-chain/zetacore/x/crosschain/types" fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" ) -func TestKeeper_RefundAmountOnZetaChain(t *testing.T) { - t.Run("should refund amount on zeta chain", func(t *testing.T) { - k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) - k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) - asset := sample.EthAddress().String() - sender := sample.EthAddress() - chainID := getValidEthChainID(t) - - // deploy zrc20 - deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) - zrc20Addr := deployZRC20( - t, - ctx, - zk.FungibleKeeper, - sdkk.EvmKeeper, - chainID, - "bar", - asset, - "bar", - ) - - err := k.RefundAmountOnZetaChain(ctx, types.CrossChainTx{ - InboundTxParams: &types.InboundTxParams{ - CoinType: common.CoinType_ERC20, - SenderChainId: chainID, - Sender: sender.String(), - Asset: asset, - }}, - math.NewUint(42), - ) - require.NoError(t, err) - - // check amount deposited in balance - balance, err := zk.FungibleKeeper.BalanceOfZRC4(ctx, zrc20Addr, sender) - require.NoError(t, err) - require.Equal(t, uint64(42), balance.Uint64()) - - // can refund again - err = k.RefundAmountOnZetaChain(ctx, types.CrossChainTx{ - InboundTxParams: &types.InboundTxParams{ - CoinType: common.CoinType_ERC20, - SenderChainId: chainID, - Sender: sender.String(), - Asset: asset, - }}, - math.NewUint(42), - ) - require.NoError(t, err) - balance, err = zk.FungibleKeeper.BalanceOfZRC4(ctx, zrc20Addr, sender) - require.NoError(t, err) - require.Equal(t, uint64(84), balance.Uint64()) - }) - - t.Run("should fail with invalid cctx", func(t *testing.T) { - k, ctx, _, _ := keepertest.CrosschainKeeper(t) - - err := k.RefundAmountOnZetaChain(ctx, types.CrossChainTx{ - InboundTxParams: &types.InboundTxParams{ - CoinType: common.CoinType_Zeta, - }}, - math.NewUint(42), - ) - require.ErrorContains(t, err, "unsupported coin type") - - err = k.RefundAmountOnZetaChain(ctx, types.CrossChainTx{ - InboundTxParams: &types.InboundTxParams{ - CoinType: common.CoinType_Gas, - }}, - math.NewUint(42), - ) - require.ErrorContains(t, err, "unsupported coin type") - - err = k.RefundAmountOnZetaChain(ctx, types.CrossChainTx{ - InboundTxParams: &types.InboundTxParams{ - CoinType: common.CoinType_ERC20, - SenderChainId: 999999, - }}, - math.NewUint(42), - ) - require.ErrorContains(t, err, "only EVM chains are supported") - - err = k.RefundAmountOnZetaChain(ctx, types.CrossChainTx{ - InboundTxParams: &types.InboundTxParams{ - CoinType: common.CoinType_ERC20, - SenderChainId: getValidEthChainID(t), - Sender: "invalid", - }}, - math.NewUint(42), - ) - require.ErrorContains(t, err, "invalid sender address") - - err = k.RefundAmountOnZetaChain(ctx, types.CrossChainTx{ - InboundTxParams: &types.InboundTxParams{ - CoinType: common.CoinType_ERC20, - SenderChainId: getValidEthChainID(t), - Sender: sample.EthAddress().String(), - }, - }, - math.Uint{}, - ) - require.ErrorContains(t, err, "no amount to refund") - - err = k.RefundAmountOnZetaChain(ctx, types.CrossChainTx{ - InboundTxParams: &types.InboundTxParams{ - CoinType: common.CoinType_ERC20, - SenderChainId: getValidEthChainID(t), - Sender: sample.EthAddress().String(), - }}, - math.ZeroUint(), - ) - require.ErrorContains(t, err, "no amount to refund") - - // the foreign coin has not been set - err = k.RefundAmountOnZetaChain(ctx, types.CrossChainTx{ - InboundTxParams: &types.InboundTxParams{ - CoinType: common.CoinType_ERC20, - SenderChainId: getValidEthChainID(t), - Sender: sample.EthAddress().String(), - Asset: sample.EthAddress().String(), - }}, - math.NewUint(42), - ) - require.ErrorContains(t, err, "zrc not found") - }) -} - func TestGetRevertGasLimit(t *testing.T) { t.Run("should return 0 if no inbound tx params", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) @@ -278,3 +152,42 @@ func TestGetRevertGasLimit(t *testing.T) { require.ErrorIs(t, err, fungibletypes.ErrContractCall) }) } + +func TestGetAbortedAmount(t *testing.T) { + amount := sdkmath.NewUint(100) + t.Run("should return the inbound amount if outbound not present", func(t *testing.T) { + cctx := types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + Amount: amount, + }, + } + a := crosschainkeeper.GetAbortedAmount(cctx) + require.Equal(t, amount, a) + }) + t.Run("should return the amount outbound amount", func(t *testing.T) { + cctx := types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + Amount: sdkmath.ZeroUint(), + }, + OutboundTxParams: []*types.OutboundTxParams{ + {Amount: amount}, + }, + } + a := crosschainkeeper.GetAbortedAmount(cctx) + require.Equal(t, amount, a) + }) + t.Run("should return the zero if outbound amount is not present and inbound is 0", func(t *testing.T) { + cctx := types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + Amount: sdkmath.ZeroUint(), + }, + } + a := crosschainkeeper.GetAbortedAmount(cctx) + require.Equal(t, sdkmath.ZeroUint(), a) + }) + t.Run("should return the zero if no amounts are present", func(t *testing.T) { + cctx := types.CrossChainTx{} + a := crosschainkeeper.GetAbortedAmount(cctx) + require.Equal(t, sdkmath.ZeroUint(), a) + }) +} diff --git a/x/crosschain/keeper/grpc_query_cctx_test.go b/x/crosschain/keeper/grpc_query_cctx_test.go index c326f41a42..6865d6d51e 100644 --- a/x/crosschain/keeper/grpc_query_cctx_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_test.go @@ -135,12 +135,12 @@ func TestKeeper_CctxListPending(t *testing.T) { cctxs := createCctxWithNonceRange(t, ctx, *k, 1000, 2000, chainID, tss, zk) // set some cctxs as pending below nonce - cctx1, found := k.GetCrossChainTx(ctx, "1337-940") + cctx1, found := k.GetCrossChainTx(ctx, sample.GetCctxIndexFromString("1337-940")) require.True(t, found) cctx1.CctxStatus.Status = types.CctxStatus_PendingOutbound k.SetCrossChainTx(ctx, cctx1) - cctx2, found := k.GetCrossChainTx(ctx, "1337-955") + cctx2, found := k.GetCrossChainTx(ctx, sample.GetCctxIndexFromString("1337-955")) require.True(t, found) cctx2.CctxStatus.Status = types.CctxStatus_PendingOutbound k.SetCrossChainTx(ctx, cctx2) diff --git a/x/crosschain/keeper/migrator.go b/x/crosschain/keeper/migrator.go index cfdc1bba94..4de64140c8 100644 --- a/x/crosschain/keeper/migrator.go +++ b/x/crosschain/keeper/migrator.go @@ -5,6 +5,7 @@ import ( v2 "github.com/zeta-chain/zetacore/x/crosschain/migrations/v2" v3 "github.com/zeta-chain/zetacore/x/crosschain/migrations/v3" v4 "github.com/zeta-chain/zetacore/x/crosschain/migrations/v4" + v5 "github.com/zeta-chain/zetacore/x/crosschain/migrations/v5" ) // Migrator is a struct for handling in-place store migrations. @@ -33,3 +34,8 @@ func (m Migrator) Migrate2to3(ctx sdk.Context) error { func (m Migrator) Migrate3to4(ctx sdk.Context) error { return v4.MigrateStore(ctx, m.crossChainKeeper.zetaObserverKeeper, m.crossChainKeeper) } + +// Migrate4to5 migrates the store from consensus version 4 to 5 +func (m Migrator) Migrate4to5(ctx sdk.Context) error { + return v5.MigrateStore(ctx, m.crossChainKeeper, m.crossChainKeeper.zetaObserverKeeper) +} diff --git a/x/crosschain/keeper/msg_server_abort_stuck_cctx_test.go b/x/crosschain/keeper/msg_server_abort_stuck_cctx_test.go index f9b7c380e0..fadda6b74f 100644 --- a/x/crosschain/keeper/msg_server_abort_stuck_cctx_test.go +++ b/x/crosschain/keeper/msg_server_abort_stuck_cctx_test.go @@ -29,11 +29,11 @@ func TestMsgServer_AbortStuckCCTX(t *testing.T) { // abort the cctx _, err := msgServer.AbortStuckCCTX(ctx, &crosschaintypes.MsgAbortStuckCCTX{ Creator: admin, - CctxIndex: "cctx_index", + CctxIndex: sample.GetCctxIndexFromString("cctx_index"), }) require.NoError(t, err) - cctxFound, found := k.GetCrossChainTx(ctx, "cctx_index") + cctxFound, found := k.GetCrossChainTx(ctx, sample.GetCctxIndexFromString("cctx_index")) require.True(t, found) require.Equal(t, crosschaintypes.CctxStatus_Aborted, cctxFound.CctxStatus.Status) require.Equal(t, crosschainkeeper.AbortMessage, cctxFound.CctxStatus.StatusMessage) @@ -56,11 +56,11 @@ func TestMsgServer_AbortStuckCCTX(t *testing.T) { // abort the cctx _, err := msgServer.AbortStuckCCTX(ctx, &crosschaintypes.MsgAbortStuckCCTX{ Creator: admin, - CctxIndex: "cctx_index", + CctxIndex: sample.GetCctxIndexFromString("cctx_index"), }) require.NoError(t, err) - cctxFound, found := k.GetCrossChainTx(ctx, "cctx_index") + cctxFound, found := k.GetCrossChainTx(ctx, sample.GetCctxIndexFromString("cctx_index")) require.True(t, found) require.Equal(t, crosschaintypes.CctxStatus_Aborted, cctxFound.CctxStatus.Status) require.Equal(t, crosschainkeeper.AbortMessage, cctxFound.CctxStatus.StatusMessage) @@ -83,11 +83,11 @@ func TestMsgServer_AbortStuckCCTX(t *testing.T) { // abort the cctx _, err := msgServer.AbortStuckCCTX(ctx, &crosschaintypes.MsgAbortStuckCCTX{ Creator: admin, - CctxIndex: "cctx_index", + CctxIndex: sample.GetCctxIndexFromString("cctx_index"), }) require.NoError(t, err) - cctxFound, found := k.GetCrossChainTx(ctx, "cctx_index") + cctxFound, found := k.GetCrossChainTx(ctx, sample.GetCctxIndexFromString("cctx_index")) require.True(t, found) require.Equal(t, crosschaintypes.CctxStatus_Aborted, cctxFound.CctxStatus.Status) require.Equal(t, crosschainkeeper.AbortMessage, cctxFound.CctxStatus.StatusMessage) @@ -108,7 +108,7 @@ func TestMsgServer_AbortStuckCCTX(t *testing.T) { // abort the cctx _, err := msgServer.AbortStuckCCTX(ctx, &crosschaintypes.MsgAbortStuckCCTX{ Creator: sample.AccAddress(), - CctxIndex: "cctx_index", + CctxIndex: sample.GetCctxIndexFromString("cctx_index"), }) require.ErrorIs(t, err, observertypes.ErrNotAuthorized) }) @@ -122,7 +122,7 @@ func TestMsgServer_AbortStuckCCTX(t *testing.T) { // abort the cctx _, err := msgServer.AbortStuckCCTX(ctx, &crosschaintypes.MsgAbortStuckCCTX{ Creator: admin, - CctxIndex: "cctx_index", + CctxIndex: sample.GetCctxIndexFromString("cctx_index"), }) require.ErrorIs(t, err, crosschaintypes.ErrCannotFindCctx) }) @@ -144,7 +144,7 @@ func TestMsgServer_AbortStuckCCTX(t *testing.T) { // abort the cctx _, err := msgServer.AbortStuckCCTX(ctx, &crosschaintypes.MsgAbortStuckCCTX{ Creator: admin, - CctxIndex: "cctx_index", + CctxIndex: sample.GetCctxIndexFromString("cctx_index"), }) require.ErrorIs(t, err, crosschaintypes.ErrStatusNotPending) }) diff --git a/x/crosschain/keeper/msg_server_refund_aborted_tx.go b/x/crosschain/keeper/msg_server_refund_aborted_tx.go new file mode 100644 index 0000000000..b011c35163 --- /dev/null +++ b/x/crosschain/keeper/msg_server_refund_aborted_tx.go @@ -0,0 +1,98 @@ +package keeper + +import ( + "errors" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/zeta-chain/zetacore/common" + "github.com/zeta-chain/zetacore/x/crosschain/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" + "golang.org/x/net/context" +) + +// RefundAbortedCCTX refunds the aborted CCTX. +// It verifies if the CCTX is aborted and not refunded, and if the refund address is valid. +// It refunds the amount to the refund address and sets the CCTX as refunded. +// Refer to documentation for GetRefundAddress for the refund address logic. +// Refer to documentation for GetAbortedAmount for the aborted amount logic. +func (k msgServer) RefundAbortedCCTX(goCtx context.Context, msg *types.MsgRefundAbortedCCTX) (*types.MsgRefundAbortedCCTXResponse, error) { + + ctx := sdk.UnwrapSDKContext(goCtx) + + // check if authorized + if msg.Creator != k.zetaObserverKeeper.GetParams(ctx).GetAdminPolicyAccount(observertypes.Policy_Type_group2) { + return nil, observertypes.ErrNotAuthorized + } + + // check if the cctx exists + cctx, found := k.GetCrossChainTx(ctx, msg.CctxIndex) + if !found { + return nil, types.ErrCannotFindCctx + } + + // check if the cctx is aborted + if cctx.CctxStatus.Status != types.CctxStatus_Aborted { + return nil, errorsmod.Wrap(types.ErrInvalidStatus, "CCTX is not aborted") + } + // check if the cctx is not refunded + if cctx.CctxStatus.IsAbortRefunded { + return nil, errorsmod.Wrap(types.ErrUnableProcessRefund, "CCTX is already refunded") + } + + // Check if aborted amount is available to maintain zeta accounting + if cctx.InboundTxParams.CoinType == common.CoinType_Zeta { + err := k.RemoveZetaAbortedAmount(ctx, GetAbortedAmount(cctx)) + // if the zeta accounting is not found, it means the zeta accounting is not set yet and the refund should not be processed + if errors.Is(err, types.ErrUnableToFindZetaAccounting) { + return nil, errorsmod.Wrap(types.ErrUnableProcessRefund, err.Error()) + } + // if the zeta accounting is found but the amount is insufficient, it means the refund can be processed but the zeta accounting is not maintained properly + if errors.Is(err, types.ErrInsufficientZetaAmount) { + ctx.Logger().Error("Zeta Accounting Error: ", err) + } + } + + refundAddress, err := GetRefundAddress(msg.RefundAddress) + if err != nil { + return nil, errorsmod.Wrap(types.ErrInvalidAddress, err.Error()) + } + // refund the amount + // use temporary context to avoid gas refunding issues and side effects + tmpCtx, commit := ctx.CacheContext() + err = k.RefundAbortedAmountOnZetaChain(tmpCtx, cctx, refundAddress) + if err != nil { + return nil, errorsmod.Wrap(types.ErrUnableProcessRefund, err.Error()) + } + commit() + + // set the cctx as refunded + cctx.CctxStatus.AbortRefunded(ctx.BlockTime().Unix()) + + k.SetCrossChainTx(ctx, cctx) + + return &types.MsgRefundAbortedCCTXResponse{}, nil +} + +// GetRefundAddress gets the proper refund address. +// For BTC sender chain the refund address is the one provided in the message in the RefundAddress field. +// For EVM chain with coin type ERC20 the refund address is the sender , but can be overridden by the RefundAddress field in the message. +// For EVM chain with coin type Zeta the refund address is the tx origin, but can be overridden by the RefundAddress field in the message. +// For EVM chain with coin type Gas the refund address is the tx origin, but can be overridden by the RefundAddress field in the message. +func GetRefundAddress(refundAddress string) (ethcommon.Address, error) { + // make sure a separate refund address is provided for a bitcoin chain as we cannot refund to tx origin or sender in this case + if refundAddress == "" { + return ethcommon.Address{}, errorsmod.Wrap(types.ErrInvalidAddress, "refund address is required") + } + if !ethcommon.IsHexAddress(refundAddress) { + return ethcommon.Address{}, errorsmod.Wrap(types.ErrInvalidAddress, "invalid refund address provided") + } + ethRefundAddress := ethcommon.HexToAddress(refundAddress) + // Double check to make sure the refund address is valid + if ethRefundAddress == (ethcommon.Address{}) { + return ethcommon.Address{}, errorsmod.Wrap(types.ErrInvalidAddress, "invalid refund address") + } + return ethRefundAddress, nil + +} diff --git a/x/crosschain/keeper/msg_server_refund_aborted_tx_test.go b/x/crosschain/keeper/msg_server_refund_aborted_tx_test.go new file mode 100644 index 0000000000..e42360975e --- /dev/null +++ b/x/crosschain/keeper/msg_server_refund_aborted_tx_test.go @@ -0,0 +1,424 @@ +package keeper_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/cmd/zetacored/config" + "github.com/zeta-chain/zetacore/common" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/crosschain/keeper" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" +) + +func Test_GetRefundAddress(t *testing.T) { + t.Run("should return refund address if provided coin-type gas", func(t *testing.T) { + validEthAddress := sample.EthAddress() + address, err := keeper.GetRefundAddress(validEthAddress.String()) + require.NoError(t, err) + require.Equal(t, validEthAddress, address) + }) + t.Run("should fail if refund address is empty", func(t *testing.T) { + address, err := keeper.GetRefundAddress("") + require.ErrorIs(t, crosschaintypes.ErrInvalidAddress, err) + assert.Equal(t, ethcommon.Address{}, address) + }) + t.Run("should fail if refund address is invalid", func(t *testing.T) { + address, err := keeper.GetRefundAddress("invalid-address") + require.ErrorIs(t, crosschaintypes.ErrInvalidAddress, err) + assert.Equal(t, ethcommon.Address{}, address) + }) + +} +func TestMsgServer_RefundAbortedCCTX(t *testing.T) { + t.Run("successfully refund tx for coin-type gas", func(t *testing.T) { + k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + admin := sample.AccAddress() + chainID := getValidEthChainID(t) + setAdminPolicies(ctx, zk, admin) + msgServer := keeper.NewMsgServerImpl(*k) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + + cctx := sample.CrossChainTx(t, "sample-index") + cctx.CctxStatus.Status = crosschaintypes.CctxStatus_Aborted + cctx.CctxStatus.IsAbortRefunded = false + cctx.InboundTxParams.TxOrigin = cctx.InboundTxParams.Sender + cctx.InboundTxParams.SenderChainId = chainID + cctx.InboundTxParams.CoinType = common.CoinType_Gas + k.SetCrossChainTx(ctx, *cctx) + deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) + zrc20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, cctx.InboundTxParams.SenderChainId, "foobar", "foobar") + + _, err := msgServer.RefundAbortedCCTX(ctx, &crosschaintypes.MsgRefundAbortedCCTX{ + Creator: admin, + CctxIndex: cctx.Index, + RefundAddress: cctx.InboundTxParams.Sender, + }) + require.NoError(t, err) + + refundAddress := ethcommon.HexToAddress(cctx.InboundTxParams.TxOrigin) + balance, err := zk.FungibleKeeper.BalanceOfZRC4(ctx, zrc20, refundAddress) + require.NoError(t, err) + require.Equal(t, cctx.GetCurrentOutTxParam().Amount.Uint64(), balance.Uint64()) + c, found := k.GetCrossChainTx(ctx, cctx.Index) + require.True(t, found) + require.True(t, c.CctxStatus.IsAbortRefunded) + }) + t.Run("successfully refund tx for coin-type zeta", func(t *testing.T) { + k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + admin := sample.AccAddress() + chainID := getValidEthChainID(t) + setAdminPolicies(ctx, zk, admin) + msgServer := keeper.NewMsgServerImpl(*k) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + + cctx := sample.CrossChainTx(t, "sample-index") + cctx.CctxStatus.Status = crosschaintypes.CctxStatus_Aborted + cctx.CctxStatus.IsAbortRefunded = false + cctx.InboundTxParams.TxOrigin = cctx.InboundTxParams.Sender + cctx.InboundTxParams.SenderChainId = chainID + cctx.InboundTxParams.CoinType = common.CoinType_Zeta + k.SetCrossChainTx(ctx, *cctx) + k.SetZetaAccounting(ctx, crosschaintypes.ZetaAccounting{AbortedZetaAmount: cctx.GetCurrentOutTxParam().Amount}) + deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) + + _, err := msgServer.RefundAbortedCCTX(ctx, &crosschaintypes.MsgRefundAbortedCCTX{ + Creator: admin, + CctxIndex: cctx.Index, + RefundAddress: cctx.InboundTxParams.Sender, + }) + require.NoError(t, err) + + refundAddress := ethcommon.HexToAddress(cctx.InboundTxParams.TxOrigin) + refundAddressCosmos := sdk.AccAddress(refundAddress.Bytes()) + balance := sdkk.BankKeeper.GetBalance(ctx, refundAddressCosmos, config.BaseDenom) + require.Equal(t, cctx.GetCurrentOutTxParam().Amount.Uint64(), balance.Amount.Uint64()) + c, found := k.GetCrossChainTx(ctx, cctx.Index) + require.True(t, found) + require.True(t, c.CctxStatus.IsAbortRefunded) + }) + t.Run("successfully refund tx to inbound amount if outbound is not found for coin-type zeta", func(t *testing.T) { + k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + admin := sample.AccAddress() + chainID := getValidEthChainID(t) + setAdminPolicies(ctx, zk, admin) + msgServer := keeper.NewMsgServerImpl(*k) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + + cctx := sample.CrossChainTx(t, "sample-index") + cctx.CctxStatus.Status = crosschaintypes.CctxStatus_Aborted + cctx.CctxStatus.IsAbortRefunded = false + cctx.InboundTxParams.TxOrigin = cctx.InboundTxParams.Sender + cctx.InboundTxParams.SenderChainId = chainID + cctx.InboundTxParams.CoinType = common.CoinType_Zeta + cctx.OutboundTxParams = nil + k.SetCrossChainTx(ctx, *cctx) + k.SetZetaAccounting(ctx, crosschaintypes.ZetaAccounting{AbortedZetaAmount: cctx.GetCurrentOutTxParam().Amount}) + deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) + + _, err := msgServer.RefundAbortedCCTX(ctx, &crosschaintypes.MsgRefundAbortedCCTX{ + Creator: admin, + CctxIndex: cctx.Index, + RefundAddress: cctx.InboundTxParams.Sender, + }) + require.NoError(t, err) + + refundAddress := ethcommon.HexToAddress(cctx.InboundTxParams.TxOrigin) + refundAddressCosmos := sdk.AccAddress(refundAddress.Bytes()) + balance := sdkk.BankKeeper.GetBalance(ctx, refundAddressCosmos, config.BaseDenom) + require.Equal(t, cctx.InboundTxParams.Amount.Uint64(), balance.Amount.Uint64()) + c, found := k.GetCrossChainTx(ctx, cctx.Index) + require.True(t, found) + require.True(t, c.CctxStatus.IsAbortRefunded) + }) + t.Run("successfully refund to optional refund address if provided", func(t *testing.T) { + k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + admin := sample.AccAddress() + chainID := getValidEthChainID(t) + setAdminPolicies(ctx, zk, admin) + msgServer := keeper.NewMsgServerImpl(*k) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + + cctx := sample.CrossChainTx(t, "sample-index") + cctx.CctxStatus.Status = crosschaintypes.CctxStatus_Aborted + cctx.CctxStatus.IsAbortRefunded = false + cctx.InboundTxParams.TxOrigin = cctx.InboundTxParams.Sender + cctx.InboundTxParams.SenderChainId = chainID + cctx.InboundTxParams.CoinType = common.CoinType_Zeta + k.SetCrossChainTx(ctx, *cctx) + k.SetZetaAccounting(ctx, crosschaintypes.ZetaAccounting{AbortedZetaAmount: cctx.InboundTxParams.Amount}) + deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) + + refundAddress := sample.EthAddress() + _, err := msgServer.RefundAbortedCCTX(ctx, &crosschaintypes.MsgRefundAbortedCCTX{ + Creator: admin, + CctxIndex: cctx.Index, + RefundAddress: refundAddress.String(), + }) + require.NoError(t, err) + + refundAddressCosmos := sdk.AccAddress(refundAddress.Bytes()) + balance := sdkk.BankKeeper.GetBalance(ctx, refundAddressCosmos, config.BaseDenom) + require.Equal(t, cctx.GetCurrentOutTxParam().Amount.Uint64(), balance.Amount.Uint64()) + c, found := k.GetCrossChainTx(ctx, cctx.Index) + require.True(t, found) + require.True(t, c.CctxStatus.IsAbortRefunded) + }) + t.Run("successfully refund tx for coin-type ERC20", func(t *testing.T) { + k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + admin := sample.AccAddress() + chainID := getValidEthChainID(t) + asset := sample.EthAddress().String() + setAdminPolicies(ctx, zk, admin) + msgServer := keeper.NewMsgServerImpl(*k) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + + cctx := sample.CrossChainTx(t, "sample-index") + cctx.CctxStatus.Status = crosschaintypes.CctxStatus_Aborted + cctx.CctxStatus.IsAbortRefunded = false + cctx.InboundTxParams.SenderChainId = chainID + cctx.InboundTxParams.CoinType = common.CoinType_ERC20 + cctx.InboundTxParams.Asset = asset + k.SetCrossChainTx(ctx, *cctx) + // deploy zrc20 + deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) + zrc20Addr := deployZRC20( + t, + ctx, + zk.FungibleKeeper, + sdkk.EvmKeeper, + chainID, + "bar", + asset, + "bar", + ) + + _, err := msgServer.RefundAbortedCCTX(ctx, &crosschaintypes.MsgRefundAbortedCCTX{ + Creator: admin, + CctxIndex: cctx.Index, + RefundAddress: cctx.InboundTxParams.Sender, + }) + require.NoError(t, err) + + refundAddress := ethcommon.HexToAddress(cctx.InboundTxParams.Sender) + balance, err := zk.FungibleKeeper.BalanceOfZRC4(ctx, zrc20Addr, refundAddress) + require.NoError(t, err) + require.Equal(t, cctx.GetCurrentOutTxParam().Amount.Uint64(), balance.Uint64()) + c, found := k.GetCrossChainTx(ctx, cctx.Index) + require.True(t, found) + require.True(t, c.CctxStatus.IsAbortRefunded) + }) + t.Run("successfully refund tx for coin-type Gas with BTC sender", func(t *testing.T) { + k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + admin := sample.AccAddress() + chainID := getValidBtcChainID() + setAdminPolicies(ctx, zk, admin) + msgServer := keeper.NewMsgServerImpl(*k) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + + cctx := sample.CrossChainTx(t, "sample-index") + cctx.CctxStatus.Status = crosschaintypes.CctxStatus_Aborted + cctx.CctxStatus.IsAbortRefunded = false + cctx.InboundTxParams.TxOrigin = cctx.InboundTxParams.Sender + cctx.InboundTxParams.SenderChainId = chainID + cctx.InboundTxParams.CoinType = common.CoinType_Gas + k.SetCrossChainTx(ctx, *cctx) + deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) + zrc20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, cctx.InboundTxParams.SenderChainId, "foobar", "foobar") + + _, err := msgServer.RefundAbortedCCTX(ctx, &crosschaintypes.MsgRefundAbortedCCTX{ + Creator: admin, + CctxIndex: cctx.Index, + RefundAddress: cctx.InboundTxParams.TxOrigin, + }) + require.NoError(t, err) + + refundAddress := ethcommon.HexToAddress(cctx.InboundTxParams.TxOrigin) + balance, err := zk.FungibleKeeper.BalanceOfZRC4(ctx, zrc20, refundAddress) + require.NoError(t, err) + require.Equal(t, cctx.GetCurrentOutTxParam().Amount.Uint64(), balance.Uint64()) + c, found := k.GetCrossChainTx(ctx, cctx.Index) + require.True(t, found) + require.True(t, c.CctxStatus.IsAbortRefunded) + }) + t.Run("fail refund if address provided is invalid", func(t *testing.T) { + k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + admin := sample.AccAddress() + chainID := getValidEthChainID(t) + setAdminPolicies(ctx, zk, admin) + msgServer := keeper.NewMsgServerImpl(*k) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + + cctx := sample.CrossChainTx(t, "sample-index") + cctx.CctxStatus.Status = crosschaintypes.CctxStatus_Aborted + cctx.CctxStatus.IsAbortRefunded = false + cctx.InboundTxParams.TxOrigin = cctx.InboundTxParams.Sender + cctx.InboundTxParams.SenderChainId = chainID + cctx.InboundTxParams.CoinType = common.CoinType_Zeta + k.SetCrossChainTx(ctx, *cctx) + k.SetZetaAccounting(ctx, crosschaintypes.ZetaAccounting{AbortedZetaAmount: cctx.InboundTxParams.Amount}) + deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) + + _, err := msgServer.RefundAbortedCCTX(ctx, &crosschaintypes.MsgRefundAbortedCCTX{ + Creator: admin, + CctxIndex: cctx.Index, + RefundAddress: "invalid-address", + }) + require.ErrorContains(t, err, "invalid refund address") + }) + t.Run("fail refund if address provided is null ", func(t *testing.T) { + k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + admin := sample.AccAddress() + chainID := getValidEthChainID(t) + setAdminPolicies(ctx, zk, admin) + msgServer := keeper.NewMsgServerImpl(*k) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + + cctx := sample.CrossChainTx(t, "sample-index") + cctx.CctxStatus.Status = crosschaintypes.CctxStatus_Aborted + cctx.CctxStatus.IsAbortRefunded = false + cctx.InboundTxParams.TxOrigin = cctx.InboundTxParams.Sender + cctx.InboundTxParams.SenderChainId = chainID + cctx.InboundTxParams.CoinType = common.CoinType_Zeta + k.SetCrossChainTx(ctx, *cctx) + k.SetZetaAccounting(ctx, crosschaintypes.ZetaAccounting{AbortedZetaAmount: cctx.InboundTxParams.Amount}) + deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) + + _, err := msgServer.RefundAbortedCCTX(ctx, &crosschaintypes.MsgRefundAbortedCCTX{ + Creator: admin, + CctxIndex: cctx.Index, + RefundAddress: "0x0000000000000000000000000000000000000000", + }) + require.ErrorContains(t, err, "invalid refund address") + }) + t.Run("fail refund if status is not aborted", func(t *testing.T) { + k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + admin := sample.AccAddress() + chainID := getValidEthChainID(t) + setAdminPolicies(ctx, zk, admin) + msgServer := keeper.NewMsgServerImpl(*k) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + + cctx := sample.CrossChainTx(t, "sample-index") + cctx.CctxStatus.Status = crosschaintypes.CctxStatus_PendingOutbound + cctx.CctxStatus.IsAbortRefunded = false + cctx.InboundTxParams.TxOrigin = cctx.InboundTxParams.Sender + cctx.InboundTxParams.SenderChainId = chainID + cctx.InboundTxParams.CoinType = common.CoinType_Gas + k.SetCrossChainTx(ctx, *cctx) + deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) + + _, err := msgServer.RefundAbortedCCTX(ctx, &crosschaintypes.MsgRefundAbortedCCTX{ + Creator: admin, + CctxIndex: cctx.Index, + RefundAddress: "", + }) + require.ErrorContains(t, err, "CCTX is not aborted") + c, found := k.GetCrossChainTx(ctx, cctx.Index) + require.True(t, found) + require.False(t, c.CctxStatus.IsAbortRefunded) + }) + t.Run("fail refund if status cctx not found", func(t *testing.T) { + k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + admin := sample.AccAddress() + chainID := getValidEthChainID(t) + setAdminPolicies(ctx, zk, admin) + msgServer := keeper.NewMsgServerImpl(*k) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + + cctx := sample.CrossChainTx(t, "sample-index") + cctx.CctxStatus.Status = crosschaintypes.CctxStatus_PendingOutbound + cctx.CctxStatus.IsAbortRefunded = false + cctx.InboundTxParams.TxOrigin = cctx.InboundTxParams.Sender + cctx.InboundTxParams.SenderChainId = chainID + cctx.InboundTxParams.CoinType = common.CoinType_Gas + deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) + + _, err := msgServer.RefundAbortedCCTX(ctx, &crosschaintypes.MsgRefundAbortedCCTX{ + Creator: admin, + CctxIndex: cctx.Index, + RefundAddress: "", + }) + require.ErrorContains(t, err, "cannot find cctx") + }) + t.Run("fail refund if refund address not provided", func(t *testing.T) { + k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + admin := sample.AccAddress() + chainID := getValidBtcChainID() + setAdminPolicies(ctx, zk, admin) + msgServer := keeper.NewMsgServerImpl(*k) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + + cctx := sample.CrossChainTx(t, "sample-index") + cctx.CctxStatus.Status = crosschaintypes.CctxStatus_Aborted + cctx.CctxStatus.IsAbortRefunded = false + cctx.InboundTxParams.TxOrigin = cctx.InboundTxParams.Sender + cctx.InboundTxParams.SenderChainId = chainID + cctx.InboundTxParams.CoinType = common.CoinType_Gas + k.SetCrossChainTx(ctx, *cctx) + deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) + _ = setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, cctx.InboundTxParams.SenderChainId, "foobar", "foobar") + + _, err := msgServer.RefundAbortedCCTX(ctx, &crosschaintypes.MsgRefundAbortedCCTX{ + Creator: admin, + CctxIndex: cctx.Index, + RefundAddress: "", + }) + require.ErrorContains(t, err, "refund address is required") + }) + t.Run("fail refund tx for coin-type Zeta if zeta accounting object is not present", func(t *testing.T) { + k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + admin := sample.AccAddress() + chainID := getValidEthChainID(t) + setAdminPolicies(ctx, zk, admin) + msgServer := keeper.NewMsgServerImpl(*k) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + + cctx := sample.CrossChainTx(t, "sample-index") + cctx.CctxStatus.Status = crosschaintypes.CctxStatus_Aborted + cctx.CctxStatus.IsAbortRefunded = false + cctx.InboundTxParams.TxOrigin = cctx.InboundTxParams.Sender + cctx.InboundTxParams.SenderChainId = chainID + cctx.InboundTxParams.CoinType = common.CoinType_Zeta + k.SetCrossChainTx(ctx, *cctx) + deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) + + _, err := msgServer.RefundAbortedCCTX(ctx, &crosschaintypes.MsgRefundAbortedCCTX{ + Creator: admin, + CctxIndex: cctx.Index, + RefundAddress: cctx.InboundTxParams.Sender, + }) + require.ErrorContains(t, err, "unable to find zeta accounting") + }) + t.Run("fail refund if non admin account is the creator", func(t *testing.T) { + k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + admin := sample.AccAddress() + chainID := getValidEthChainID(t) + setAdminPolicies(ctx, zk, admin) + msgServer := keeper.NewMsgServerImpl(*k) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + + cctx := sample.CrossChainTx(t, "sample-index") + cctx.CctxStatus.Status = crosschaintypes.CctxStatus_Aborted + cctx.CctxStatus.IsAbortRefunded = false + cctx.InboundTxParams.TxOrigin = cctx.InboundTxParams.Sender + cctx.InboundTxParams.SenderChainId = chainID + cctx.InboundTxParams.CoinType = common.CoinType_Gas + k.SetCrossChainTx(ctx, *cctx) + deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) + _ = setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, cctx.InboundTxParams.SenderChainId, "foobar", "foobar") + + _, err := msgServer.RefundAbortedCCTX(ctx, &crosschaintypes.MsgRefundAbortedCCTX{ + Creator: sample.AccAddress(), + CctxIndex: cctx.Index, + RefundAddress: cctx.InboundTxParams.Sender, + }) + require.ErrorIs(t, err, observertypes.ErrNotAuthorized) + }) +} diff --git a/x/crosschain/keeper/msg_server_update_tss_test.go b/x/crosschain/keeper/msg_server_update_tss_test.go index 9d1207d427..fbc8567d5f 100644 --- a/x/crosschain/keeper/msg_server_update_tss_test.go +++ b/x/crosschain/keeper/msg_server_update_tss_test.go @@ -28,7 +28,7 @@ func TestMsgServer_UpdateTssAddress(t *testing.T) { index := chain.ChainName.String() + "_migration_tx_index" k.GetObserverKeeper().SetFundMigrator(ctx, types.TssFundMigratorInfo{ ChainId: chain.ChainId, - MigrationCctxIndex: index, + MigrationCctxIndex: sample.GetCctxIndexFromString(index), }) cctx := sample.CrossChainTx(t, index) cctx.CctxStatus.Status = crosschaintypes.CctxStatus_OutboundMined @@ -60,7 +60,7 @@ func TestMsgServer_UpdateTssAddress(t *testing.T) { index := chain.ChainName.String() + "_migration_tx_index" k.GetObserverKeeper().SetFundMigrator(ctx, types.TssFundMigratorInfo{ ChainId: chain.ChainId, - MigrationCctxIndex: index, + MigrationCctxIndex: sample.GetCctxIndexFromString(index), }) cctx := sample.CrossChainTx(t, index) cctx.CctxStatus.Status = crosschaintypes.CctxStatus_OutboundMined @@ -91,7 +91,7 @@ func TestMsgServer_UpdateTssAddress(t *testing.T) { index := chain.ChainName.String() + "_migration_tx_index" k.GetObserverKeeper().SetFundMigrator(ctx, types.TssFundMigratorInfo{ ChainId: chain.ChainId, - MigrationCctxIndex: index, + MigrationCctxIndex: sample.GetCctxIndexFromString(index), }) cctx := sample.CrossChainTx(t, index) cctx.CctxStatus.Status = crosschaintypes.CctxStatus_OutboundMined @@ -128,7 +128,7 @@ func TestMsgServer_UpdateTssAddress(t *testing.T) { index := chain.ChainName.String() + "_migration_tx_index" k.GetObserverKeeper().SetFundMigrator(ctx, types.TssFundMigratorInfo{ ChainId: chain.ChainId, - MigrationCctxIndex: index, + MigrationCctxIndex: sample.GetCctxIndexFromString(index), }) cctx := sample.CrossChainTx(t, index) cctx.CctxStatus.Status = crosschaintypes.CctxStatus_OutboundMined @@ -165,7 +165,7 @@ func TestMsgServer_UpdateTssAddress(t *testing.T) { index := chain.ChainName.String() + "_migration_tx_index" k.GetObserverKeeper().SetFundMigrator(ctx, types.TssFundMigratorInfo{ ChainId: chain.ChainId, - MigrationCctxIndex: index, + MigrationCctxIndex: sample.GetCctxIndexFromString(index), }) cctx := sample.CrossChainTx(t, index) cctx.CctxStatus.Status = crosschaintypes.CctxStatus_PendingOutbound @@ -202,7 +202,7 @@ func TestMsgServer_UpdateTssAddress(t *testing.T) { index := chain.ChainName.String() + "_migration_tx_index" k.GetObserverKeeper().SetFundMigrator(ctx, types.TssFundMigratorInfo{ ChainId: chain.ChainId, - MigrationCctxIndex: index, + MigrationCctxIndex: sample.GetCctxIndexFromString(index), }) } assert.Equal(t, len(k.GetObserverKeeper().GetAllTssFundMigrators(ctx)), len(k.GetObserverKeeper().GetSupportedChains(ctx))) diff --git a/x/crosschain/keeper/msg_server_vote_inbound_tx.go b/x/crosschain/keeper/msg_server_vote_inbound_tx.go index 57c3e54041..ef98c85bf8 100644 --- a/x/crosschain/keeper/msg_server_vote_inbound_tx.go +++ b/x/crosschain/keeper/msg_server_vote_inbound_tx.go @@ -188,22 +188,6 @@ func (k msgServer) VoteOnObservedInboundTx(goCtx context.Context, msg *types.Msg return k.UpdateNonce(tmpCtx, chain.ChainId, &cctx) }() if err != nil { - // do not commit anything here as the CCTX should be aborted - - // gas payment for erc20 type might fail because no liquidity pool is defined to swap the zrc20 token into the gas token - // in this gas we should refund the sender on ZetaChain - if cctx.InboundTxParams.CoinType == common.CoinType_ERC20 { - - if err := k.RefundAmountOnZetaChain(ctx, cctx, cctx.InboundTxParams.Amount); err != nil { - // log the error - k.Logger(ctx).Error("failed to refund amount of aborted cctx on ZetaChain", - "error", err, - "sender", cctx.InboundTxParams.Sender, - "amount", cctx.InboundTxParams.Amount.String(), - ) - } - } - cctx.CctxStatus.ChangeStatus(types.CctxStatus_Aborted, err.Error()+" deposit revert message: "+revertMessage) return &types.MsgVoteOnObservedInboundTxResponse{}, nil } diff --git a/x/crosschain/keeper/refund.go b/x/crosschain/keeper/refund.go new file mode 100644 index 0000000000..9d10b7ce13 --- /dev/null +++ b/x/crosschain/keeper/refund.go @@ -0,0 +1,105 @@ +package keeper + +import ( + "errors" + "fmt" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/zeta-chain/zetacore/common" + "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func (k Keeper) RefundAbortedAmountOnZetaChain(ctx sdk.Context, cctx types.CrossChainTx, refundAddress ethcommon.Address) error { + coinType := cctx.InboundTxParams.CoinType + switch coinType { + case common.CoinType_Gas: + return k.RefundAmountOnZetaChainGas(ctx, cctx, refundAddress) + case common.CoinType_Zeta: + return k.RefundAmountOnZetaChainZeta(ctx, cctx, refundAddress) + case common.CoinType_ERC20: + return k.RefundAmountOnZetaChainERC20(ctx, cctx, refundAddress) + default: + return errors.New("unsupported coin type for refund on ZetaChain") + } +} + +// RefundAmountOnZetaChainGas refunds the amount of the cctx on ZetaChain in case of aborted cctx with cointype gas +func (k Keeper) RefundAmountOnZetaChainGas(ctx sdk.Context, cctx types.CrossChainTx, refundAddress ethcommon.Address) error { + // refund in gas token to refund address + // Refund the the amount was previously + refundAmount := GetAbortedAmount(cctx) + if refundAmount.IsNil() || refundAmount.IsZero() { + return errors.New("no amount to refund") + } + chainID := cctx.InboundTxParams.SenderChainId + // get the zrc20 contract address + fcSenderChain, found := k.fungibleKeeper.GetGasCoinForForeignCoin(ctx, chainID) + if !found { + return types.ErrForeignCoinNotFound + } + zrc20 := ethcommon.HexToAddress(fcSenderChain.Zrc20ContractAddress) + if zrc20 == (ethcommon.Address{}) { + return errorsmod.Wrapf(types.ErrForeignCoinNotFound, "zrc20 contract address not found for chain %d", chainID) + } + // deposit the amount to the tx origin instead of receiver as this is a refund + if _, err := k.fungibleKeeper.DepositZRC20(ctx, zrc20, refundAddress, refundAmount.BigInt()); err != nil { + return errors.New("failed to refund zeta on ZetaChain" + err.Error()) + } + return nil +} + +// RefundAmountOnZetaChainGas refunds the amount of the cctx on ZetaChain in case of aborted cctx with cointype zeta +func (k Keeper) RefundAmountOnZetaChainZeta(ctx sdk.Context, cctx types.CrossChainTx, refundAddress ethcommon.Address) error { + // if coin type is Zeta, handle this as a deposit ZETA to zEVM. + refundAmount := GetAbortedAmount(cctx) + chainID := cctx.InboundTxParams.SenderChainId + // check if chain is an EVM chain + if !common.IsEVMChain(chainID) { + return errors.New("only EVM chains are supported for refund when coin type is Zeta") + } + if cctx.InboundTxParams.Amount.IsNil() || cctx.InboundTxParams.Amount.IsZero() { + return errors.New("no amount to refund") + } + // deposit the amount to refund address + if err := k.fungibleKeeper.DepositCoinZeta(ctx, refundAddress, refundAmount.BigInt()); err != nil { + return errors.New("failed to refund zeta on ZetaChain" + err.Error()) + } + return nil +} + +// RefundAmountOnZetaChainERC20 refunds the amount of the cctx on ZetaChain in case of aborted cctx +// NOTE: GetCurrentOutTxParam should contain the last up to date cctx amount +// Refund address should already be validated before calling this function +func (k Keeper) RefundAmountOnZetaChainERC20(ctx sdk.Context, cctx types.CrossChainTx, refundAddress ethcommon.Address) error { + refundAmount := GetAbortedAmount(cctx) + // preliminary checks + if cctx.InboundTxParams.CoinType != common.CoinType_ERC20 { + return errors.New("unsupported coin type for refund on ZetaChain") + } + if !common.IsEVMChain(cctx.InboundTxParams.SenderChainId) { + return errors.New("only EVM chains are supported for refund on ZetaChain") + } + + if refundAmount.IsNil() || refundAmount.IsZero() { + return errors.New("no amount to refund") + } + + // get address of the zrc20 + fc, found := k.fungibleKeeper.GetForeignCoinFromAsset(ctx, cctx.InboundTxParams.Asset, cctx.InboundTxParams.SenderChainId) + if !found { + return fmt.Errorf("asset %s zrc not found", cctx.InboundTxParams.Asset) + } + zrc20 := ethcommon.HexToAddress(fc.Zrc20ContractAddress) + if zrc20 == (ethcommon.Address{}) { + return fmt.Errorf("asset %s invalid zrc address", cctx.InboundTxParams.Asset) + } + + // deposit the amount to the sender + if _, err := k.fungibleKeeper.DepositZRC20(ctx, zrc20, refundAddress, refundAmount.BigInt()); err != nil { + return errors.New("failed to deposit zrc20 on ZetaChain" + err.Error()) + } + + return nil +} diff --git a/x/crosschain/keeper/refund_test.go b/x/crosschain/keeper/refund_test.go new file mode 100644 index 0000000000..e9ecfd346f --- /dev/null +++ b/x/crosschain/keeper/refund_test.go @@ -0,0 +1,315 @@ +package keeper_test + +import ( + "fmt" + "testing" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/cmd/zetacored/config" + "github.com/zeta-chain/zetacore/common" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/crosschain/types" + fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" +) + +func TestKeeper_RefundAmountOnZetaChainGas(t *testing.T) { + t.Run("should refund amount zrc20 gas on zeta chain", func(t *testing.T) { + k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + sender := sample.EthAddress() + chainID := getValidEthChainID(t) + deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) + zrc20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "foobar", "foobar") + + err := k.RefundAmountOnZetaChainGas(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + CoinType: common.CoinType_Gas, + SenderChainId: chainID, + Sender: sender.String(), + TxOrigin: sender.String(), + Amount: math.NewUint(20), + }, + OutboundTxParams: []*types.OutboundTxParams{{ + Amount: math.NewUint(42), + }}, + }, + sender, + ) + require.NoError(t, err) + balance, err := zk.FungibleKeeper.BalanceOfZRC4(ctx, zrc20, sender) + require.NoError(t, err) + require.Equal(t, uint64(42), balance.Uint64()) + }) + t.Run("should refund inbound amount zrc20 gas on zeta chain", func(t *testing.T) { + k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + sender := sample.EthAddress() + chainID := getValidEthChainID(t) + deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) + zrc20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "foobar", "foobar") + + err := k.RefundAmountOnZetaChainGas(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + CoinType: common.CoinType_Gas, + SenderChainId: chainID, + Sender: sender.String(), + TxOrigin: sender.String(), + Amount: math.NewUint(20), + }, + }, + sender, + ) + require.NoError(t, err) + balance, err := zk.FungibleKeeper.BalanceOfZRC4(ctx, zrc20, sender) + require.NoError(t, err) + require.Equal(t, uint64(20), balance.Uint64()) + }) + t.Run("failed refund zrc20 gas on zeta chain if gas coin not found", func(t *testing.T) { + k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + sender := sample.EthAddress() + chainID := getValidEthChainID(t) + deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) + err := k.RefundAmountOnZetaChainGas(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + CoinType: common.CoinType_Gas, + SenderChainId: chainID, + Sender: sender.String(), + TxOrigin: sender.String(), + Amount: math.NewUint(20), + }, + OutboundTxParams: []*types.OutboundTxParams{{ + Amount: math.NewUint(42), + }}, + }, + + sender, + ) + require.ErrorContains(t, err, types.ErrForeignCoinNotFound.Error()) + }) + t.Run("failed refund amount zrc20 gas on zeta chain if amount is 0", func(t *testing.T) { + k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + sender := sample.EthAddress() + chainID := getValidEthChainID(t) + deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) + _ = setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chainID, "foobar", "foobar") + + err := k.RefundAmountOnZetaChainGas(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + CoinType: common.CoinType_Gas, + SenderChainId: chainID, + Sender: sender.String(), + TxOrigin: sender.String(), + Amount: math.ZeroUint(), + }, + OutboundTxParams: []*types.OutboundTxParams{{ + Amount: math.ZeroUint(), + }}, + }, + sender, + ) + require.ErrorContains(t, err, "no amount to refund") + }) + +} + +func TestKeeper_RefundAmountOnZetaChainZeta(t *testing.T) { + t.Run("should refund amount on zeta chain", func(t *testing.T) { + k, ctx, sdkk, _ := keepertest.CrosschainKeeper(t) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + sender := sample.EthAddress() + chainID := getValidEthChainID(t) + + err := k.RefundAmountOnZetaChainZeta(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + CoinType: common.CoinType_Gas, + SenderChainId: chainID, + Sender: sender.String(), + TxOrigin: sender.String(), + Amount: math.NewUint(20), + }, + OutboundTxParams: []*types.OutboundTxParams{{ + Amount: math.NewUint(42), + }}, + }, + sender, + ) + require.NoError(t, err) + coin := sdkk.BankKeeper.GetBalance(ctx, sdk.AccAddress(sender.Bytes()), config.BaseDenom) + fmt.Println(coin.Amount.String()) + require.Equal(t, "42", coin.Amount.String()) + }) + t.Run("should refund inbound amount on zeta chain if outbound is not present", func(t *testing.T) { + k, ctx, sdkk, _ := keepertest.CrosschainKeeper(t) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + sender := sample.EthAddress() + chainID := getValidEthChainID(t) + + err := k.RefundAmountOnZetaChainZeta(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + CoinType: common.CoinType_Gas, + SenderChainId: chainID, + Sender: sender.String(), + TxOrigin: sender.String(), + Amount: math.NewUint(20), + }, + }, + sender, + ) + require.NoError(t, err) + coin := sdkk.BankKeeper.GetBalance(ctx, sdk.AccAddress(sender.Bytes()), config.BaseDenom) + require.Equal(t, "20", coin.Amount.String()) + }) + t.Run("failed refund amount on zeta chain amount is 0", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + sender := sample.EthAddress() + chainID := getValidEthChainID(t) + + err := k.RefundAmountOnZetaChainZeta(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + CoinType: common.CoinType_Gas, + SenderChainId: chainID, + Sender: sender.String(), + TxOrigin: sender.String(), + Amount: math.ZeroUint(), + }, + OutboundTxParams: []*types.OutboundTxParams{{ + Amount: math.ZeroUint(), + }}, + }, + sender, + ) + require.ErrorContains(t, err, "no amount to refund") + }) +} + +func TestKeeper_RefundAmountOnZetaChainERC20(t *testing.T) { + t.Run("should refund amount on zeta chain", func(t *testing.T) { + k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + asset := sample.EthAddress().String() + sender := sample.EthAddress() + chainID := getValidEthChainID(t) + + // deploy zrc20 + deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) + zrc20Addr := deployZRC20( + t, + ctx, + zk.FungibleKeeper, + sdkk.EvmKeeper, + chainID, + "bar", + asset, + "bar", + ) + + err := k.RefundAmountOnZetaChainERC20(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + CoinType: common.CoinType_ERC20, + SenderChainId: chainID, + Sender: sender.String(), + Asset: asset, + Amount: math.NewUint(42), + }, + OutboundTxParams: []*types.OutboundTxParams{{ + Amount: math.NewUint(42), + }}, + }, + sender, + ) + require.NoError(t, err) + + // check amount deposited in balance + balance, err := zk.FungibleKeeper.BalanceOfZRC4(ctx, zrc20Addr, sender) + require.NoError(t, err) + require.Equal(t, uint64(42), balance.Uint64()) + + // can refund again + err = k.RefundAmountOnZetaChainERC20(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + CoinType: common.CoinType_ERC20, + SenderChainId: chainID, + Sender: sender.String(), + Asset: asset, + Amount: math.NewUint(42), + }}, + sender, + ) + require.NoError(t, err) + balance, err = zk.FungibleKeeper.BalanceOfZRC4(ctx, zrc20Addr, sender) + require.NoError(t, err) + require.Equal(t, uint64(84), balance.Uint64()) + }) + + t.Run("should fail with invalid cctx", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + + err := k.RefundAmountOnZetaChainERC20(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + CoinType: common.CoinType_Zeta, + Amount: math.NewUint(42), + }}, + sample.EthAddress(), + ) + require.ErrorContains(t, err, "unsupported coin type") + + err = k.RefundAmountOnZetaChainERC20(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + CoinType: common.CoinType_Gas, + }}, + sample.EthAddress(), + ) + require.ErrorContains(t, err, "unsupported coin type") + + err = k.RefundAmountOnZetaChainERC20(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + CoinType: common.CoinType_ERC20, + SenderChainId: 999999, + Amount: math.NewUint(42), + }}, + sample.EthAddress(), + ) + require.ErrorContains(t, err, "only EVM chains are supported") + + err = k.RefundAmountOnZetaChainERC20(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + CoinType: common.CoinType_ERC20, + SenderChainId: getValidEthChainID(t), + Sender: sample.EthAddress().String(), + Amount: math.Uint{}, + }}, + sample.EthAddress(), + ) + require.ErrorContains(t, err, "no amount to refund") + + err = k.RefundAmountOnZetaChainERC20(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + CoinType: common.CoinType_ERC20, + SenderChainId: getValidEthChainID(t), + Sender: sample.EthAddress().String(), + Amount: math.ZeroUint(), + }}, + sample.EthAddress(), + ) + require.ErrorContains(t, err, "no amount to refund") + + // the foreign coin has not been set + err = k.RefundAmountOnZetaChainERC20(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + CoinType: common.CoinType_ERC20, + SenderChainId: getValidEthChainID(t), + Sender: sample.EthAddress().String(), + Asset: sample.EthAddress().String(), + Amount: math.NewUint(42), + }}, + sample.EthAddress(), + ) + require.ErrorContains(t, err, "zrc not found") + }) +} diff --git a/x/crosschain/keeper/utils_test.go b/x/crosschain/keeper/utils_test.go index 83f37b2799..b5735ac0de 100644 --- a/x/crosschain/keeper/utils_test.go +++ b/x/crosschain/keeper/utils_test.go @@ -32,6 +32,15 @@ func getValidEthChain(_ *testing.T) *zetacommon.Chain { return &goerli } +func getValidBTCChain() *zetacommon.Chain { + btcRegNet := zetacommon.BtcRegtestChain() + return &btcRegNet +} + +func getValidBtcChainID() int64 { + return getValidBTCChain().ChainId +} + // getValidEthChainIDWithIndex get a valid eth chain id with index func getValidEthChainIDWithIndex(t *testing.T, index int) int64 { switch index { diff --git a/x/crosschain/keeper/zeta_accounting.go b/x/crosschain/keeper/zeta_accounting.go index ee3184b5e3..2f9a849666 100644 --- a/x/crosschain/keeper/zeta_accounting.go +++ b/x/crosschain/keeper/zeta_accounting.go @@ -33,3 +33,16 @@ func (k Keeper) AddZetaAbortedAmount(ctx sdk.Context, amount sdkmath.Uint) { } k.SetZetaAccounting(ctx, zetaAccounting) } + +func (k Keeper) RemoveZetaAbortedAmount(ctx sdk.Context, amount sdkmath.Uint) error { + zetaAccounting, found := k.GetZetaAccounting(ctx) + if !found { + return types.ErrUnableToFindZetaAccounting + } + if zetaAccounting.AbortedZetaAmount.LT(amount) { + return types.ErrInsufficientZetaAmount + } + zetaAccounting.AbortedZetaAmount = zetaAccounting.AbortedZetaAmount.Sub(amount) + k.SetZetaAccounting(ctx, zetaAccounting) + return nil +} diff --git a/x/crosschain/keeper/zeta_accounting_test.go b/x/crosschain/keeper/zeta_accounting_test.go index 1d5e1f7921..8802fe7666 100644 --- a/x/crosschain/keeper/zeta_accounting_test.go +++ b/x/crosschain/keeper/zeta_accounting_test.go @@ -47,3 +47,43 @@ func TestKeeper_AddZetaAccounting(t *testing.T) { }) } + +func TestKeeper_RemoveZetaAbortedAmount(t *testing.T) { + t.Run("should remove aborted zeta amount", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + originalAmount := sdkmath.NewUintFromString("100000000000000000000000000000000000000000000000") + k.SetZetaAccounting(ctx, types.ZetaAccounting{ + AbortedZetaAmount: originalAmount, + }) + val, found := k.GetZetaAccounting(ctx) + require.True(t, found) + require.Equal(t, originalAmount, val.AbortedZetaAmount) + removeAmount := originalAmount.Sub(sdkmath.NewUintFromString("10000000000000000000000000000000000000000000000")) + err := k.RemoveZetaAbortedAmount(ctx, removeAmount) + require.NoError(t, err) + val, found = k.GetZetaAccounting(ctx) + require.True(t, found) + require.Equal(t, originalAmount.Sub(removeAmount), val.AbortedZetaAmount) + }) + t.Run("fail remove aborted zeta amount if accounting not set", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + err := k.RemoveZetaAbortedAmount(ctx, sdkmath.OneUint()) + require.ErrorIs(t, err, types.ErrUnableToFindZetaAccounting) + }) + t.Run("fail remove aborted zeta amount if insufficient amount", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + originalAmount := sdkmath.NewUint(100) + k.SetZetaAccounting(ctx, types.ZetaAccounting{ + AbortedZetaAmount: originalAmount, + }) + val, found := k.GetZetaAccounting(ctx) + require.True(t, found) + require.Equal(t, originalAmount, val.AbortedZetaAmount) + removeAmount := originalAmount.Add(sdkmath.NewUint(500)) + err := k.RemoveZetaAbortedAmount(ctx, removeAmount) + require.ErrorIs(t, err, types.ErrInsufficientZetaAmount) + val, found = k.GetZetaAccounting(ctx) + require.True(t, found) + require.Equal(t, originalAmount, val.AbortedZetaAmount) + }) +} diff --git a/x/crosschain/migrations/v5/migrate.go b/x/crosschain/migrations/v5/migrate.go new file mode 100644 index 0000000000..b5e40ceee3 --- /dev/null +++ b/x/crosschain/migrations/v5/migrate.go @@ -0,0 +1,90 @@ +package v5 + +import ( + "fmt" + + sdkmath "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/zeta-chain/zetacore/common" + "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// crosschainKeeper is an interface to prevent cyclic dependency +type crosschainKeeper interface { + GetStoreKey() storetypes.StoreKey + GetCodec() codec.Codec + GetAllCrossChainTx(ctx sdk.Context) []types.CrossChainTx + + SetCrossChainTx(ctx sdk.Context, cctx types.CrossChainTx) + AddFinalizedInbound(ctx sdk.Context, inboundTxHash string, senderChainID int64, height uint64) + + SetZetaAccounting(ctx sdk.Context, accounting types.ZetaAccounting) +} + +// MigrateStore migrates the x/crosschain module state from the consensus version 4 to 5 +// It resets the aborted zeta amount to use the inbound tx amount instead in situations where the outbound cctx is never created. +func MigrateStore( + ctx sdk.Context, + crosschainKeeper crosschainKeeper, + observerKeeper types.ObserverKeeper, +) error { + + ccctxList := crosschainKeeper.GetAllCrossChainTx(ctx) + abortedAmountZeta := sdkmath.ZeroUint() + for _, cctx := range ccctxList { + if cctx.CctxStatus.Status == types.CctxStatus_Aborted { + + switch cctx.InboundTxParams.CoinType { + case common.CoinType_ERC20: + { + receiverChain := observerKeeper.GetSupportedChainFromChainID(ctx, cctx.GetCurrentOutTxParam().ReceiverChainId) + if receiverChain == nil { + ctx.Logger().Error(fmt.Sprintf("Error getting chain from chain id: %d , cctx index", cctx.GetCurrentOutTxParam().ReceiverChainId), cctx.Index) + continue + } + // There is a chance that this cctx has already been refunded, so we set the isRefunded flag to true. + // Even though, there is a slight possibility that the refund tx failed when doing an auto refund; there is no way for us to know. Which is why we can mark this type of cctx as non-refundable + // Auto refunds are done for ERC20 cctx's when the receiver chain is a zeta chain. + if receiverChain.IsZetaChain() { + cctx.CctxStatus.IsAbortRefunded = true + } else { + cctx.CctxStatus.IsAbortRefunded = false + } + } + case common.CoinType_Zeta: + { + // add the required amount into the zeta accounting. + // GetAbortedAmount replaces using Outbound Amount directly, to make sure we refund the amount deposited by the user if the outbound is never created and the cctx is aborted. + // For these cctx's we allow the refund to be processed later and the Aborted amount would be adjusted when the refund is processed. + abortedValue := GetAbortedAmount(cctx) + abortedAmountZeta = abortedAmountZeta.Add(abortedValue) + cctx.CctxStatus.IsAbortRefunded = false + + } + case common.CoinType_Gas: + { + // CointType gas can be processed as normal and we can issue the refund using the admin refund tx . + cctx.CctxStatus.IsAbortRefunded = false + } + } + crosschainKeeper.SetCrossChainTx(ctx, cctx) + } + + } + crosschainKeeper.SetZetaAccounting(ctx, types.ZetaAccounting{AbortedZetaAmount: abortedAmountZeta}) + + return nil +} + +func GetAbortedAmount(cctx types.CrossChainTx) sdkmath.Uint { + if cctx.OutboundTxParams != nil && !cctx.GetCurrentOutTxParam().Amount.IsZero() { + return cctx.GetCurrentOutTxParam().Amount + } + if cctx.InboundTxParams != nil { + return cctx.InboundTxParams.Amount + } + + return sdkmath.ZeroUint() +} diff --git a/x/crosschain/migrations/v5/migrate_test.go b/x/crosschain/migrations/v5/migrate_test.go new file mode 100644 index 0000000000..8033ddc3bf --- /dev/null +++ b/x/crosschain/migrations/v5/migrate_test.go @@ -0,0 +1,147 @@ +package v5_test + +import ( + "fmt" + "math/rand" + "testing" + + "cosmossdk.io/math" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/common" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + crosschainkeeper "github.com/zeta-chain/zetacore/x/crosschain/keeper" + v5 "github.com/zeta-chain/zetacore/x/crosschain/migrations/v5" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func TestMigrateStore(t *testing.T) { + t.Run("TestMigrateStore", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + cctxList := CrossChainTxList(100) + v5ZetaAccountingAmount := math.ZeroUint() + v4ZetaAccountingAmount := math.ZeroUint() + for _, cctx := range cctxList { + k.SetCrossChainTx(ctx, cctx) + if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_Aborted || cctx.GetCurrentOutTxParam().CoinType != common.CoinType_Zeta { + continue + } + v5ZetaAccountingAmount = v5ZetaAccountingAmount.Add(crosschainkeeper.GetAbortedAmount(cctx)) + v4ZetaAccountingAmount = v4ZetaAccountingAmount.Add(cctx.GetCurrentOutTxParam().Amount) + } + + assert.True(t, v5ZetaAccountingAmount.GT(v4ZetaAccountingAmount)) + // Previously set the zeta accounting + k.SetZetaAccounting(ctx, crosschaintypes.ZetaAccounting{ + AbortedZetaAmount: v4ZetaAccountingAmount, + }) + err := v5.MigrateStore(ctx, k, k.GetObserverKeeper()) + require.NoError(t, err) + zetaAccounting, found := k.GetZetaAccounting(ctx) + require.True(t, found) + require.True(t, v5ZetaAccountingAmount.Equal(zetaAccounting.AbortedZetaAmount)) + cctxListUpdated := k.GetAllCrossChainTx(ctx) + // Check refund status of the cctx + for _, cctx := range cctxListUpdated { + switch cctx.InboundTxParams.CoinType { + case common.CoinType_ERC20: + receiverChain := zk.ObserverKeeper.GetSupportedChainFromChainID(ctx, cctx.GetCurrentOutTxParam().ReceiverChainId) + require.NotNil(t, receiverChain) + if receiverChain.IsZetaChain() { + require.True(t, cctx.CctxStatus.IsAbortRefunded) + } else { + require.False(t, cctx.CctxStatus.IsAbortRefunded) + } + case common.CoinType_Zeta: + require.False(t, cctx.CctxStatus.IsAbortRefunded) + case common.CoinType_Gas: + require.False(t, cctx.CctxStatus.IsAbortRefunded) + } + } + }) + +} + +func CrossChainTxList(count int) []crosschaintypes.CrossChainTx { + cctxList := make([]crosschaintypes.CrossChainTx, count+100) + i := 0 + r := rand.New(rand.NewSource(9)) + for ; i < count/2; i++ { + amount := math.NewUint(uint64(r.Uint32())) + cctxList[i] = crosschaintypes.CrossChainTx{ + Index: fmt.Sprintf("%d", i), + CctxStatus: &crosschaintypes.Status{Status: crosschaintypes.CctxStatus_Aborted}, + InboundTxParams: &crosschaintypes.InboundTxParams{ + Amount: amount.Add(math.NewUint(uint64(r.Uint32()))), + CoinType: common.CoinType_Zeta, + }, + OutboundTxParams: []*crosschaintypes.OutboundTxParams{{ + Amount: amount, + CoinType: common.CoinType_Zeta, + }}, + } + for ; i < count; i++ { + amount := math.NewUint(uint64(r.Uint32())) + cctxList[i] = crosschaintypes.CrossChainTx{ + Index: fmt.Sprintf("%d", i), + CctxStatus: &crosschaintypes.Status{Status: crosschaintypes.CctxStatus_Aborted}, + InboundTxParams: &crosschaintypes.InboundTxParams{ + Amount: amount, + CoinType: common.CoinType_Zeta, + }, + OutboundTxParams: []*crosschaintypes.OutboundTxParams{{ + Amount: math.ZeroUint(), + CoinType: common.CoinType_Zeta, + }}, + } + } + for ; i < count+20; i++ { + amount := math.NewUint(uint64(r.Uint32())) + cctxList[i] = crosschaintypes.CrossChainTx{ + Index: fmt.Sprintf("%d", i), + CctxStatus: &crosschaintypes.Status{Status: crosschaintypes.CctxStatus_Aborted}, + InboundTxParams: &crosschaintypes.InboundTxParams{ + Amount: amount, + CoinType: common.CoinType_ERC20, + }, + OutboundTxParams: []*crosschaintypes.OutboundTxParams{{ + Amount: math.ZeroUint(), + CoinType: common.CoinType_ERC20, + ReceiverChainId: common.ZetaPrivnetChain().ChainId, + }}, + } + } + for ; i < count+50; i++ { + amount := math.NewUint(uint64(r.Uint32())) + cctxList[i] = crosschaintypes.CrossChainTx{ + Index: fmt.Sprintf("%d", i), + CctxStatus: &crosschaintypes.Status{Status: crosschaintypes.CctxStatus_Aborted}, + InboundTxParams: &crosschaintypes.InboundTxParams{ + Amount: amount, + CoinType: common.CoinType_ERC20, + }, + OutboundTxParams: []*crosschaintypes.OutboundTxParams{{ + Amount: math.ZeroUint(), + CoinType: common.CoinType_ERC20, + ReceiverChainId: common.GoerliLocalnetChain().ChainId, + }}, + } + } + for ; i < count+100; i++ { + amount := math.NewUint(uint64(r.Uint32())) + cctxList[i] = crosschaintypes.CrossChainTx{ + Index: fmt.Sprintf("%d", i), + CctxStatus: &crosschaintypes.Status{Status: crosschaintypes.CctxStatus_Aborted}, + InboundTxParams: &crosschaintypes.InboundTxParams{ + Amount: amount, + CoinType: common.CoinType_Gas, + }, + OutboundTxParams: []*crosschaintypes.OutboundTxParams{{ + Amount: amount, + CoinType: common.CoinType_Gas, + }}, + } + } + } + return cctxList +} diff --git a/x/crosschain/module.go b/x/crosschain/module.go index 421f92c895..8821a7942e 100644 --- a/x/crosschain/module.go +++ b/x/crosschain/module.go @@ -157,6 +157,9 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { if err := cfg.RegisterMigration(types.ModuleName, 3, m.Migrate3to4); err != nil { panic(err) } + if err := cfg.RegisterMigration(types.ModuleName, 4, m.Migrate4to5); err != nil { + panic(err) + } } // RegisterInvariants registers the crosschain module's invariants. diff --git a/x/crosschain/types/cross_chain_tx.pb.go b/x/crosschain/types/cross_chain_tx.pb.go index f74b500a81..94f34e6e46 100644 --- a/x/crosschain/types/cross_chain_tx.pb.go +++ b/x/crosschain/types/cross_chain_tx.pb.go @@ -394,6 +394,7 @@ type Status struct { Status CctxStatus `protobuf:"varint,1,opt,name=status,proto3,enum=zetachain.zetacore.crosschain.CctxStatus" json:"status,omitempty"` StatusMessage string `protobuf:"bytes,2,opt,name=status_message,json=statusMessage,proto3" json:"status_message,omitempty"` LastUpdateTimestamp int64 `protobuf:"varint,3,opt,name=lastUpdate_timestamp,json=lastUpdateTimestamp,proto3" json:"lastUpdate_timestamp,omitempty"` + IsAbortRefunded bool `protobuf:"varint,4,opt,name=isAbortRefunded,proto3" json:"isAbortRefunded,omitempty"` } func (m *Status) Reset() { *m = Status{} } @@ -450,6 +451,13 @@ func (m *Status) GetLastUpdateTimestamp() int64 { return 0 } +func (m *Status) GetIsAbortRefunded() bool { + if m != nil { + return m.IsAbortRefunded + } + return false +} + type CrossChainTx struct { Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` Index string `protobuf:"bytes,2,opt,name=index,proto3" json:"index,omitempty"` @@ -548,76 +556,77 @@ func init() { func init() { proto.RegisterFile("crosschain/cross_chain_tx.proto", fileDescriptor_af3a0ad055343c21) } var fileDescriptor_af3a0ad055343c21 = []byte{ - // 1100 bytes of a gzipped FileDescriptorProto + // 1120 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xcd, 0x6e, 0x1b, 0x37, 0x10, 0xd6, 0x46, 0x8a, 0x2c, 0x8d, 0x6c, 0x6b, 0x4d, 0xcb, 0xe9, 0xc2, 0x69, 0x24, 0x41, 0x6d, 0x12, 0x25, 0x80, 0x25, 0xd8, 0x41, 0x11, 0xa0, 0x37, 0xdb, 0xb5, 0x13, 0x23, 0x89, 0x6d, 0x6c, 0xed, 0x8b, 0x81, 0x62, 0x4b, 0xed, 0xd2, 0x12, 0x11, 0x69, 0xa9, 0x2e, 0x29, 0x43, 0x0e, 0xfa, - 0x10, 0x3d, 0xf4, 0x11, 0x5a, 0xa0, 0x8f, 0x92, 0x43, 0x0f, 0x39, 0x16, 0x3d, 0x18, 0x85, 0x7d, + 0x10, 0x3d, 0xf4, 0x11, 0x7a, 0xe8, 0xa3, 0xe4, 0x50, 0xa0, 0x39, 0x16, 0x3d, 0x18, 0x85, 0x7d, 0xee, 0xa5, 0x4f, 0x50, 0xf0, 0x67, 0x57, 0x6b, 0xd5, 0x3f, 0xfd, 0x3b, 0xed, 0x70, 0xc8, 0xef, - 0x1b, 0x72, 0xe6, 0x1b, 0x2e, 0xa1, 0xe6, 0x47, 0x8c, 0x73, 0xbf, 0x87, 0x69, 0xd8, 0x56, 0xa6, + 0x9b, 0x21, 0xe7, 0x1b, 0x72, 0xa1, 0xe6, 0x47, 0x8c, 0x73, 0xbf, 0x87, 0x69, 0xd8, 0x56, 0xa6, 0xa7, 0x6c, 0x4f, 0x8c, 0x5b, 0xc3, 0x88, 0x09, 0x86, 0x1e, 0xbc, 0x23, 0x02, 0x2b, 0x5f, 0x4b, 0x59, 0x2c, 0x22, 0xad, 0x09, 0x66, 0x79, 0xd1, 0x67, 0x83, 0x01, 0x0b, 0xdb, 0xfa, 0xa3, 0x31, 0xcb, 0x95, 0x2e, 0xeb, 0x32, 0x65, 0xb6, 0xa5, 0xa5, 0xbd, 0x8d, 0xdf, 0x73, 0x50, 0xde, 0x09, 0x3b, 0x6c, 0x14, 0x06, 0x07, 0xe3, 0x7d, 0x1c, 0xe1, 0x01, 0x47, 0xf7, 0x20, 0xcf, 0x49, 0x18, - 0x90, 0xc8, 0xb1, 0xea, 0x56, 0xb3, 0xe8, 0x9a, 0x11, 0x7a, 0x04, 0x65, 0x6d, 0x99, 0xed, 0xd0, - 0xc0, 0xb9, 0x53, 0xb7, 0x9a, 0x59, 0x77, 0x4e, 0xbb, 0x37, 0xa5, 0x77, 0x27, 0x40, 0xf7, 0xa1, - 0x28, 0xc6, 0x1e, 0x8b, 0x68, 0x97, 0x86, 0x4e, 0x56, 0x51, 0x14, 0xc4, 0x78, 0x4f, 0x8d, 0xd1, - 0x0a, 0x14, 0x7d, 0x26, 0xcf, 0x72, 0x3a, 0x24, 0x4e, 0xae, 0x6e, 0x35, 0xe7, 0xd7, 0xec, 0x96, - 0xd9, 0xe8, 0x26, 0xa3, 0xe1, 0xc1, 0xe9, 0x90, 0xb8, 0x05, 0xdf, 0x58, 0xa8, 0x02, 0x77, 0x31, - 0xe7, 0x44, 0x38, 0x77, 0x15, 0x8f, 0x1e, 0xa0, 0x17, 0x90, 0xc7, 0x03, 0x36, 0x0a, 0x85, 0x93, - 0x97, 0xee, 0x8d, 0xf6, 0xfb, 0xb3, 0x5a, 0xe6, 0xd7, 0xb3, 0xda, 0xe3, 0x2e, 0x15, 0xbd, 0x51, - 0x47, 0xf2, 0xb5, 0x7d, 0xc6, 0x07, 0x8c, 0x9b, 0xcf, 0x0a, 0x0f, 0xde, 0xb6, 0x65, 0x48, 0xde, - 0x3a, 0xa4, 0xa1, 0x70, 0x0d, 0x1c, 0x3d, 0x07, 0x87, 0xea, 0xd3, 0x7b, 0x72, 0xcb, 0x1d, 0x4e, - 0xa2, 0x13, 0x12, 0x78, 0x3d, 0xcc, 0x7b, 0xce, 0x8c, 0x8a, 0xb8, 0x44, 0xe3, 0xec, 0xec, 0x99, - 0xd9, 0x97, 0x98, 0xf7, 0xd0, 0x6b, 0xf8, 0xe4, 0x2a, 0x20, 0x19, 0x0b, 0x12, 0x85, 0xb8, 0xef, - 0xf5, 0x08, 0xed, 0xf6, 0x84, 0x53, 0xa8, 0x5b, 0xcd, 0x9c, 0x5b, 0xfb, 0x0b, 0xc7, 0x96, 0x59, - 0xf7, 0x52, 0x2d, 0x43, 0x9f, 0xc1, 0x47, 0x29, 0xb6, 0x0e, 0xee, 0xf7, 0x99, 0xf0, 0x68, 0x18, - 0x90, 0xb1, 0x53, 0x54, 0xbb, 0xa8, 0x24, 0x0c, 0x1b, 0x6a, 0x72, 0x47, 0xce, 0xa1, 0x6d, 0xa8, - 0xa7, 0x60, 0xc7, 0x34, 0xc4, 0x7d, 0xfa, 0x8e, 0x04, 0x9e, 0xd4, 0x44, 0xbc, 0x03, 0x50, 0x3b, - 0xf8, 0x38, 0xc1, 0x6f, 0xc7, 0xab, 0x8e, 0x88, 0xc0, 0x26, 0x3c, 0x85, 0x7b, 0x13, 0x3c, 0x16, - 0x94, 0x85, 0x1e, 0x17, 0x58, 0x8c, 0xb8, 0x53, 0x52, 0x05, 0x7a, 0xd6, 0xba, 0x51, 0x6f, 0xad, - 0x84, 0x55, 0x61, 0xbf, 0x54, 0x50, 0xb7, 0x22, 0xae, 0xf0, 0x36, 0xbe, 0x81, 0x79, 0x19, 0x78, - 0xdd, 0xf7, 0x65, 0xfe, 0x69, 0xd8, 0x45, 0x1e, 0x2c, 0xe2, 0x0e, 0x8b, 0x44, 0xbc, 0x6f, 0x53, - 0x58, 0xeb, 0xdf, 0x15, 0x76, 0xc1, 0x70, 0xa9, 0x20, 0x8a, 0xa9, 0xf1, 0xfd, 0x0c, 0xd8, 0x7b, - 0x23, 0x71, 0x59, 0xe3, 0xcb, 0x50, 0x88, 0x88, 0x4f, 0xe8, 0x49, 0xa2, 0xf2, 0x64, 0x8c, 0x9e, - 0x80, 0x1d, 0xdb, 0x5a, 0xe9, 0x3b, 0xb1, 0xd0, 0xcb, 0xb1, 0x3f, 0x96, 0xfa, 0x25, 0x35, 0x67, - 0x6f, 0x55, 0xf3, 0x44, 0xb7, 0xb9, 0xff, 0xa6, 0xdb, 0x55, 0x58, 0x62, 0xe6, 0x48, 0xb2, 0xf4, - 0x82, 0x73, 0x2f, 0x64, 0xa1, 0x4f, 0x54, 0x9b, 0xe4, 0x5c, 0xc4, 0x92, 0xf3, 0x1e, 0x70, 0xbe, - 0x2b, 0x67, 0xa6, 0x21, 0x5d, 0xcc, 0xbd, 0x3e, 0x1d, 0x50, 0xdd, 0x42, 0x97, 0x20, 0x2f, 0x30, - 0x7f, 0x2d, 0x67, 0xae, 0x82, 0x0c, 0x23, 0xea, 0x13, 0xd3, 0x1a, 0x97, 0x21, 0xfb, 0x72, 0x06, - 0x35, 0xc1, 0x4e, 0x43, 0x54, 0x23, 0x15, 0xd4, 0xea, 0xf9, 0xc9, 0x6a, 0xd5, 0x41, 0xcf, 0xc1, - 0x49, 0xaf, 0xbc, 0x42, 0xf4, 0x4b, 0x13, 0x44, 0x5a, 0xf5, 0xbb, 0xf0, 0x69, 0x1a, 0x78, 0x6d, - 0xef, 0x69, 0xe5, 0xd7, 0x27, 0x24, 0xd7, 0x34, 0x5f, 0x1b, 0x2a, 0xd3, 0xa7, 0x1c, 0x71, 0x12, - 0x38, 0x15, 0x85, 0x5f, 0xb8, 0x74, 0xc8, 0x43, 0x4e, 0x02, 0x24, 0xa0, 0x96, 0x06, 0x90, 0xe3, - 0x63, 0xe2, 0x0b, 0x7a, 0x42, 0x52, 0x09, 0x5a, 0x52, 0xe5, 0x6d, 0x99, 0xf2, 0x3e, 0xfa, 0x1b, - 0xe5, 0xdd, 0x09, 0x85, 0x7b, 0x7f, 0x12, 0x6b, 0x2b, 0x26, 0x4d, 0x32, 0xfb, 0xc5, 0x4d, 0x51, - 0x75, 0x25, 0xef, 0xa9, 0x1d, 0x5f, 0xc3, 0xa2, 0x4b, 0xfa, 0x00, 0x40, 0x8a, 0x65, 0x38, 0xea, - 0xbc, 0x25, 0xa7, 0xaa, 0xbd, 0x8b, 0x6e, 0x51, 0x70, 0xbe, 0xaf, 0x1c, 0x37, 0xdc, 0x04, 0xb3, - 0xff, 0xf7, 0x4d, 0xf0, 0xa3, 0x05, 0x79, 0x6d, 0xa2, 0x75, 0xc8, 0x9b, 0x28, 0x96, 0x8a, 0xf2, - 0xe4, 0x96, 0x28, 0x9b, 0xbe, 0x18, 0x1b, 0x6e, 0x03, 0x44, 0x0f, 0x61, 0x5e, 0x5b, 0xde, 0x80, - 0x70, 0x8e, 0xbb, 0x44, 0x75, 0x6c, 0xd1, 0x9d, 0xd3, 0xde, 0x37, 0xda, 0x89, 0x56, 0xa1, 0xd2, - 0xc7, 0x5c, 0x1c, 0x0e, 0x03, 0x2c, 0x88, 0x27, 0xe8, 0x80, 0x70, 0x81, 0x07, 0x43, 0xd5, 0xba, - 0x59, 0x77, 0x71, 0x32, 0x77, 0x10, 0x4f, 0x35, 0x7e, 0xce, 0xc2, 0xec, 0xa6, 0x8c, 0xad, 0x7a, - 0xfe, 0x60, 0x8c, 0x1c, 0x98, 0xf1, 0x23, 0x82, 0x05, 0x8b, 0x6f, 0x8e, 0x78, 0x28, 0x7f, 0x56, - 0x5a, 0xbf, 0x3a, 0xb6, 0x1e, 0xa0, 0xaf, 0xa1, 0xa8, 0x2e, 0xb6, 0x63, 0x42, 0xb8, 0xfe, 0x8d, - 0x6d, 0x6c, 0xfe, 0xc3, 0xbe, 0xff, 0xe3, 0xac, 0x66, 0x9f, 0xe2, 0x41, 0xff, 0xf3, 0x46, 0xc2, - 0xd4, 0x70, 0x0b, 0xd2, 0xde, 0x26, 0x84, 0xa3, 0xc7, 0x50, 0x8e, 0x48, 0x1f, 0x9f, 0x92, 0x20, - 0x39, 0x7d, 0x5e, 0xf7, 0x9c, 0x71, 0xc7, 0xc7, 0xdf, 0x86, 0x92, 0xef, 0x8b, 0x71, 0x5c, 0x53, - 0xd9, 0x98, 0xa5, 0xb5, 0x87, 0xb7, 0x64, 0xdb, 0x64, 0x1a, 0xfc, 0x24, 0xeb, 0xe8, 0x08, 0x16, - 0x52, 0x3f, 0x9e, 0xa1, 0xba, 0x52, 0x55, 0xd3, 0x96, 0xd6, 0x5a, 0xb7, 0xb0, 0x4d, 0x3d, 0x36, - 0xdc, 0x32, 0x9d, 0x7a, 0x7d, 0x7c, 0x05, 0x28, 0xad, 0x73, 0x43, 0x0e, 0xf5, 0x6c, 0xb3, 0xb4, - 0xd6, 0xbe, 0x85, 0x7c, 0xfa, 0x9a, 0x77, 0x6d, 0x36, 0xe5, 0x79, 0xfa, 0x2d, 0xc0, 0x44, 0x3e, - 0x08, 0xc1, 0xfc, 0x3e, 0x09, 0x03, 0x1a, 0x76, 0xcd, 0xbe, 0xec, 0x0c, 0x5a, 0x84, 0xb2, 0xf1, - 0xc5, 0x74, 0xb6, 0x85, 0x16, 0x60, 0x2e, 0x1e, 0xbd, 0xa1, 0x21, 0x09, 0xec, 0xac, 0x74, 0x99, - 0x75, 0x2e, 0x39, 0x21, 0x91, 0xb0, 0x73, 0x68, 0x16, 0x0a, 0xda, 0x26, 0x81, 0x7d, 0x17, 0x95, - 0x60, 0x66, 0x5d, 0xff, 0x8d, 0xec, 0xfc, 0x72, 0xee, 0xa7, 0x1f, 0xaa, 0xd6, 0xd3, 0x57, 0x50, - 0xb9, 0xaa, 0x45, 0x90, 0x0d, 0xb3, 0xbb, 0x4c, 0x24, 0xff, 0x66, 0x3b, 0x83, 0xe6, 0xa0, 0x38, - 0x19, 0x5a, 0x92, 0x79, 0x6b, 0x4c, 0xfc, 0x91, 0x24, 0xbb, 0xa3, 0xc9, 0x36, 0x5e, 0xbd, 0x3f, - 0xaf, 0x5a, 0x1f, 0xce, 0xab, 0xd6, 0x6f, 0xe7, 0x55, 0xeb, 0xbb, 0x8b, 0x6a, 0xe6, 0xc3, 0x45, - 0x35, 0xf3, 0xcb, 0x45, 0x35, 0x73, 0xb4, 0x9a, 0xd2, 0x95, 0xcc, 0xd3, 0x8a, 0x7e, 0x4b, 0xc6, - 0x29, 0x6b, 0x8f, 0xdb, 0xa9, 0x17, 0xa6, 0x92, 0x59, 0x27, 0xaf, 0xde, 0x83, 0xcf, 0xfe, 0x0c, - 0x00, 0x00, 0xff, 0xff, 0xd3, 0x52, 0xba, 0xe1, 0x7c, 0x0a, 0x00, 0x00, + 0x90, 0xc8, 0xb1, 0xea, 0x56, 0xb3, 0xe8, 0x9a, 0x11, 0x7a, 0x04, 0x65, 0x6d, 0x99, 0x74, 0x68, + 0xe0, 0xdc, 0xa9, 0x5b, 0xcd, 0xac, 0x3b, 0xa7, 0xdd, 0x9b, 0xd2, 0xbb, 0x13, 0xa0, 0xfb, 0x50, + 0x14, 0x63, 0x8f, 0x45, 0xb4, 0x4b, 0x43, 0x27, 0xab, 0x28, 0x0a, 0x62, 0xbc, 0xa7, 0xc6, 0x68, + 0x05, 0x8a, 0x3e, 0x93, 0x7b, 0x39, 0x1d, 0x12, 0x27, 0x57, 0xb7, 0x9a, 0xf3, 0x6b, 0x76, 0xcb, + 0x24, 0xba, 0xc9, 0x68, 0x78, 0x70, 0x3a, 0x24, 0x6e, 0xc1, 0x37, 0x16, 0xaa, 0xc0, 0x5d, 0xcc, + 0x39, 0x11, 0xce, 0x5d, 0xc5, 0xa3, 0x07, 0xe8, 0x05, 0xe4, 0xf1, 0x80, 0x8d, 0x42, 0xe1, 0xe4, + 0xa5, 0x7b, 0xa3, 0xfd, 0xfe, 0xac, 0x96, 0xf9, 0xf5, 0xac, 0xf6, 0xb8, 0x4b, 0x45, 0x6f, 0xd4, + 0x91, 0x7c, 0x6d, 0x9f, 0xf1, 0x01, 0xe3, 0xe6, 0xb3, 0xc2, 0x83, 0xb7, 0x6d, 0x19, 0x92, 0xb7, + 0x0e, 0x69, 0x28, 0x5c, 0x03, 0x47, 0xcf, 0xc1, 0xa1, 0x7a, 0xf7, 0x9e, 0x4c, 0xb9, 0xc3, 0x49, + 0x74, 0x42, 0x02, 0xaf, 0x87, 0x79, 0xcf, 0x99, 0x51, 0x11, 0x97, 0x68, 0x7c, 0x3a, 0x7b, 0x66, + 0xf6, 0x25, 0xe6, 0x3d, 0xf4, 0x1a, 0x3e, 0xb9, 0x0a, 0x48, 0xc6, 0x82, 0x44, 0x21, 0xee, 0x7b, + 0x3d, 0x42, 0xbb, 0x3d, 0xe1, 0x14, 0xea, 0x56, 0x33, 0xe7, 0xd6, 0xfe, 0xc2, 0xb1, 0x65, 0xd6, + 0xbd, 0x54, 0xcb, 0xd0, 0x67, 0xf0, 0x51, 0x8a, 0xad, 0x83, 0xfb, 0x7d, 0x26, 0x3c, 0x1a, 0x06, + 0x64, 0xec, 0x14, 0x55, 0x16, 0x95, 0x84, 0x61, 0x43, 0x4d, 0xee, 0xc8, 0x39, 0xb4, 0x0d, 0xf5, + 0x14, 0xec, 0x98, 0x86, 0xb8, 0x4f, 0xdf, 0x91, 0xc0, 0x93, 0x9a, 0x88, 0x33, 0x00, 0x95, 0xc1, + 0xc7, 0x09, 0x7e, 0x3b, 0x5e, 0x75, 0x44, 0x04, 0x36, 0xe1, 0x29, 0xdc, 0x9b, 0xe0, 0xb1, 0xa0, + 0x2c, 0xf4, 0xb8, 0xc0, 0x62, 0xc4, 0x9d, 0x92, 0x2a, 0xd0, 0xb3, 0xd6, 0x8d, 0x7a, 0x6b, 0x25, + 0xac, 0x0a, 0xfb, 0xa5, 0x82, 0xba, 0x15, 0x71, 0x85, 0xb7, 0xf1, 0x0d, 0xcc, 0xcb, 0xc0, 0xeb, + 0xbe, 0x2f, 0xcf, 0x9f, 0x86, 0x5d, 0xe4, 0xc1, 0x22, 0xee, 0xb0, 0x48, 0xc4, 0x79, 0x9b, 0xc2, + 0x5a, 0xff, 0xae, 0xb0, 0x0b, 0x86, 0x4b, 0x05, 0x51, 0x4c, 0x8d, 0xef, 0x67, 0xc0, 0xde, 0x1b, + 0x89, 0xcb, 0x1a, 0x5f, 0x86, 0x42, 0x44, 0x7c, 0x42, 0x4f, 0x12, 0x95, 0x27, 0x63, 0xf4, 0x04, + 0xec, 0xd8, 0xd6, 0x4a, 0xdf, 0x89, 0x85, 0x5e, 0x8e, 0xfd, 0xb1, 0xd4, 0x2f, 0xa9, 0x39, 0x7b, + 0xab, 0x9a, 0x27, 0xba, 0xcd, 0xfd, 0x37, 0xdd, 0xae, 0xc2, 0x12, 0x33, 0x5b, 0x92, 0xa5, 0x17, + 0x9c, 0x7b, 0x21, 0x0b, 0x7d, 0xa2, 0xda, 0x24, 0xe7, 0x22, 0x96, 0xec, 0xf7, 0x80, 0xf3, 0x5d, + 0x39, 0x33, 0x0d, 0xe9, 0x62, 0xee, 0xf5, 0xe9, 0x80, 0xea, 0x16, 0xba, 0x04, 0x79, 0x81, 0xf9, + 0x6b, 0x39, 0x73, 0x15, 0x64, 0x18, 0x51, 0x9f, 0x98, 0xd6, 0xb8, 0x0c, 0xd9, 0x97, 0x33, 0xa8, + 0x09, 0x76, 0x1a, 0xa2, 0x1a, 0xa9, 0xa0, 0x56, 0xcf, 0x4f, 0x56, 0xab, 0x0e, 0x7a, 0x0e, 0x4e, + 0x7a, 0xe5, 0x15, 0xa2, 0x5f, 0x9a, 0x20, 0xd2, 0xaa, 0xdf, 0x85, 0x4f, 0xd3, 0xc0, 0x6b, 0x7b, + 0x4f, 0x2b, 0xbf, 0x3e, 0x21, 0xb9, 0xa6, 0xf9, 0xda, 0x50, 0x99, 0xde, 0xe5, 0x88, 0x93, 0xc0, + 0xa9, 0x28, 0xfc, 0xc2, 0xa5, 0x4d, 0x1e, 0x72, 0x12, 0x20, 0x01, 0xb5, 0x34, 0x80, 0x1c, 0x1f, + 0x13, 0x5f, 0xd0, 0x13, 0x92, 0x3a, 0xa0, 0x25, 0x55, 0xde, 0x96, 0x29, 0xef, 0xa3, 0xbf, 0x51, + 0xde, 0x9d, 0x50, 0xb8, 0xf7, 0x27, 0xb1, 0xb6, 0x62, 0xd2, 0xe4, 0x64, 0xbf, 0xb8, 0x29, 0xaa, + 0xae, 0xe4, 0x3d, 0x95, 0xf1, 0x35, 0x2c, 0xba, 0xa4, 0x0f, 0x00, 0xa4, 0x58, 0x86, 0xa3, 0xce, + 0x5b, 0x72, 0xaa, 0xda, 0xbb, 0xe8, 0x16, 0x05, 0xe7, 0xfb, 0xca, 0x71, 0xc3, 0x4d, 0x30, 0xfb, + 0x7f, 0xdf, 0x04, 0x3f, 0x5b, 0x90, 0xd7, 0x26, 0x5a, 0x87, 0xbc, 0x89, 0x62, 0xa9, 0x28, 0x4f, + 0x6e, 0x89, 0xb2, 0xe9, 0x8b, 0xb1, 0xe1, 0x36, 0x40, 0xf4, 0x10, 0xe6, 0xb5, 0xe5, 0x0d, 0x08, + 0xe7, 0xb8, 0x4b, 0x54, 0xc7, 0x16, 0xdd, 0x39, 0xed, 0x7d, 0xa3, 0x9d, 0x68, 0x15, 0x2a, 0x7d, + 0xcc, 0xc5, 0xe1, 0x30, 0xc0, 0x82, 0x78, 0x82, 0x0e, 0x08, 0x17, 0x78, 0x30, 0x54, 0xad, 0x9b, + 0x75, 0x17, 0x27, 0x73, 0x07, 0xf1, 0x14, 0x6a, 0x42, 0x99, 0xf2, 0x75, 0x79, 0xab, 0xb8, 0xe4, + 0x78, 0x14, 0x06, 0x24, 0x50, 0xcd, 0x5b, 0x70, 0xa7, 0xdd, 0x8d, 0x9f, 0xb2, 0x30, 0xbb, 0x29, + 0xb3, 0x54, 0xb7, 0xc3, 0xc1, 0x18, 0x39, 0x30, 0xe3, 0x47, 0x04, 0x0b, 0x16, 0xdf, 0x31, 0xf1, + 0x50, 0x3e, 0x6b, 0x5a, 0xe9, 0x3a, 0x4b, 0x3d, 0x40, 0x5f, 0x43, 0x51, 0x5d, 0x81, 0xc7, 0x84, + 0x70, 0xfd, 0xe0, 0x6d, 0x6c, 0xfe, 0xc3, 0x1b, 0xe2, 0x8f, 0xb3, 0x9a, 0x7d, 0x8a, 0x07, 0xfd, + 0xcf, 0x1b, 0x09, 0x53, 0xc3, 0x2d, 0x48, 0x7b, 0x9b, 0x10, 0x8e, 0x1e, 0x43, 0x39, 0x22, 0x7d, + 0x7c, 0x4a, 0x82, 0xe4, 0x9c, 0xf2, 0xba, 0x3b, 0x8d, 0x3b, 0x3e, 0xa8, 0x6d, 0x28, 0xf9, 0xbe, + 0x18, 0xc7, 0xd5, 0x97, 0x2d, 0x5c, 0x5a, 0x7b, 0x78, 0x4b, 0x5d, 0x4c, 0x4d, 0xc0, 0x4f, 0xea, + 0x83, 0x8e, 0x60, 0x21, 0xf5, 0x44, 0x0d, 0xd5, 0xe5, 0xab, 0xda, 0xbb, 0xb4, 0xd6, 0xba, 0x85, + 0x6d, 0xea, 0xb7, 0xc4, 0x2d, 0xd3, 0xa9, 0xff, 0x94, 0xaf, 0x00, 0xa5, 0x3b, 0xc2, 0x90, 0x43, + 0x3d, 0xdb, 0x2c, 0xad, 0xb5, 0x6f, 0x21, 0x9f, 0x7e, 0x10, 0x5c, 0x9b, 0x4d, 0x79, 0x9e, 0x7e, + 0x0b, 0x30, 0x11, 0x1a, 0x42, 0x30, 0xbf, 0x4f, 0xc2, 0x80, 0x86, 0x5d, 0x93, 0x97, 0x9d, 0x41, + 0x8b, 0x50, 0x36, 0xbe, 0x98, 0xce, 0xb6, 0xd0, 0x02, 0xcc, 0xc5, 0xa3, 0x37, 0x34, 0x24, 0x81, + 0x9d, 0x95, 0x2e, 0xb3, 0xce, 0x25, 0x27, 0x24, 0x12, 0x76, 0x0e, 0xcd, 0x42, 0x41, 0xdb, 0x24, + 0xb0, 0xef, 0xa2, 0x12, 0xcc, 0xac, 0xeb, 0x77, 0xcb, 0xce, 0x2f, 0xe7, 0x7e, 0xfc, 0xa1, 0x6a, + 0x3d, 0x7d, 0x05, 0x95, 0xab, 0x9a, 0x09, 0xd9, 0x30, 0xbb, 0xcb, 0x44, 0xf2, 0x8a, 0xdb, 0x19, + 0x34, 0x07, 0xc5, 0xc9, 0xd0, 0x92, 0xcc, 0x5b, 0x63, 0xe2, 0x8f, 0x24, 0xd9, 0x1d, 0x4d, 0xb6, + 0xf1, 0xea, 0xfd, 0x79, 0xd5, 0xfa, 0x70, 0x5e, 0xb5, 0x7e, 0x3b, 0xaf, 0x5a, 0xdf, 0x5d, 0x54, + 0x33, 0x1f, 0x2e, 0xaa, 0x99, 0x5f, 0x2e, 0xaa, 0x99, 0xa3, 0xd5, 0x94, 0xae, 0xe4, 0x39, 0xad, + 0xe8, 0xbf, 0xce, 0xf8, 0xc8, 0xda, 0xe3, 0x76, 0xea, 0x5f, 0x54, 0xc9, 0xac, 0x93, 0x57, 0x7f, + 0x8e, 0xcf, 0xfe, 0x0c, 0x00, 0x00, 0xff, 0xff, 0x13, 0xc7, 0x24, 0x8d, 0xa6, 0x0a, 0x00, 0x00, } func (m *InboundTxParams) Marshal() (dAtA []byte, err error) { @@ -890,6 +899,16 @@ func (m *Status) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.IsAbortRefunded { + i-- + if m.IsAbortRefunded { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } if m.LastUpdateTimestamp != 0 { i = encodeVarintCrossChainTx(dAtA, i, uint64(m.LastUpdateTimestamp)) i-- @@ -1143,6 +1162,9 @@ func (m *Status) Size() (n int) { if m.LastUpdateTimestamp != 0 { n += 1 + sovCrossChainTx(uint64(m.LastUpdateTimestamp)) } + if m.IsAbortRefunded { + n += 2 + } return n } @@ -2141,6 +2163,26 @@ func (m *Status) Unmarshal(dAtA []byte) error { break } } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsAbortRefunded", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCrossChainTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IsAbortRefunded = bool(v != 0) default: iNdEx = preIndex skippy, err := skipCrossChainTx(dAtA[iNdEx:]) diff --git a/x/crosschain/types/errors.go b/x/crosschain/types/errors.go index 12bbd4ad84..0f7fb8341a 100644 --- a/x/crosschain/types/errors.go +++ b/x/crosschain/types/errors.go @@ -5,44 +5,43 @@ import ( ) var ( - ErrUnsupportedChain = errorsmod.Register(ModuleName, 1102, "chain parse error") - ErrInvalidChainID = errorsmod.Register(ModuleName, 1101, "chain id cannot be negative") - ErrInvalidPubKeySet = errorsmod.Register(ModuleName, 1106, "invalid pubkeyset") - ErrUnableToGetGasPrice = errorsmod.Register(ModuleName, 1107, "unable to get gas price") - ErrNotEnoughZetaBurnt = errorsmod.Register(ModuleName, 1109, "not enough zeta burnt") - ErrCannotFindReceiverNonce = errorsmod.Register(ModuleName, 1110, "cannot find receiver chain nonce") - - ErrGasCoinNotFound = errorsmod.Register(ModuleName, 1113, "gas coin not found for sender chain") - ErrUnableToParseAddress = errorsmod.Register(ModuleName, 1115, "cannot parse address and data") - ErrCannotProcessWithdrawal = errorsmod.Register(ModuleName, 1116, "cannot process withdrawal event") - ErrForeignCoinNotFound = errorsmod.Register(ModuleName, 1118, "foreign coin not found for sender chain") - ErrNotEnoughPermissions = errorsmod.Register(ModuleName, 1119, "not enough permissions for current actions") - - ErrCannotFindPendingNonces = errorsmod.Register(ModuleName, 1121, "cannot find pending nonces") - ErrCannotFindTSSKeys = errorsmod.Register(ModuleName, 1122, "cannot find TSS keys") - ErrNonceMismatch = errorsmod.Register(ModuleName, 1123, "nonce mismatch") - ErrNotFoundChainParams = errorsmod.Register(ModuleName, 1126, "not found chain chain params") - ErrUnableToSendCoinType = errorsmod.Register(ModuleName, 1127, "unable to send this coin type to a receiver chain") - - ErrInvalidAddress = errorsmod.Register(ModuleName, 1128, "invalid address") - ErrDeployContract = errorsmod.Register(ModuleName, 1129, "unable to deploy contract") - ErrUnableToUpdateTss = errorsmod.Register(ModuleName, 1130, "unable to update TSS address") - ErrNotEnoughGas = errorsmod.Register(ModuleName, 1131, "not enough gas") - ErrNotEnoughFunds = errorsmod.Register(ModuleName, 1132, "not enough funds") - - ErrProofVerificationFail = errorsmod.Register(ModuleName, 1133, "proof verification fail") - ErrCannotFindCctx = errorsmod.Register(ModuleName, 1134, "cannot find cctx") - ErrStatusNotPending = errorsmod.Register(ModuleName, 1135, "Status not pending") - - ErrCannotFindGasParams = errorsmod.Register(ModuleName, 1136, "cannot find gas params") - ErrInvalidGasAmount = errorsmod.Register(ModuleName, 1137, "invalid gas amount") - ErrNoLiquidityPool = errorsmod.Register(ModuleName, 1138, "no liquidity pool") - ErrInvalidCoinType = errorsmod.Register(ModuleName, 1139, "invalid coin type") - ErrCannotMigrateTssFunds = errorsmod.Register(ModuleName, 1140, "cannot migrate TSS funds") - ErrTxBodyVerificationFail = errorsmod.Register(ModuleName, 1141, "transaction body verification fail") - ErrReceiverIsEmpty = errorsmod.Register(ModuleName, 1142, "receiver is empty") - ErrUnsupportedStatus = errorsmod.Register(ModuleName, 1143, "unsupported status") - ErrObservedTxAlreadyFinalized = errorsmod.Register(ModuleName, 1144, "observed tx already finalized") - + ErrUnsupportedChain = errorsmod.Register(ModuleName, 1102, "chain parse error") + ErrInvalidChainID = errorsmod.Register(ModuleName, 1101, "chain id cannot be negative") + ErrInvalidPubKeySet = errorsmod.Register(ModuleName, 1106, "invalid pubkeyset") + ErrUnableToGetGasPrice = errorsmod.Register(ModuleName, 1107, "unable to get gas price") + ErrNotEnoughZetaBurnt = errorsmod.Register(ModuleName, 1109, "not enough zeta burnt") + ErrCannotFindReceiverNonce = errorsmod.Register(ModuleName, 1110, "cannot find receiver chain nonce") + ErrGasCoinNotFound = errorsmod.Register(ModuleName, 1113, "gas coin not found for sender chain") + ErrUnableToParseAddress = errorsmod.Register(ModuleName, 1115, "cannot parse address and data") + ErrCannotProcessWithdrawal = errorsmod.Register(ModuleName, 1116, "cannot process withdrawal event") + ErrForeignCoinNotFound = errorsmod.Register(ModuleName, 1118, "foreign coin not found for sender chain") + ErrNotEnoughPermissions = errorsmod.Register(ModuleName, 1119, "not enough permissions for current actions") + ErrCannotFindPendingNonces = errorsmod.Register(ModuleName, 1121, "cannot find pending nonces") + ErrCannotFindTSSKeys = errorsmod.Register(ModuleName, 1122, "cannot find TSS keys") + ErrNonceMismatch = errorsmod.Register(ModuleName, 1123, "nonce mismatch") + ErrNotFoundChainParams = errorsmod.Register(ModuleName, 1126, "not found chain chain params") + ErrUnableToSendCoinType = errorsmod.Register(ModuleName, 1127, "unable to send this coin type to a receiver chain") + ErrInvalidAddress = errorsmod.Register(ModuleName, 1128, "invalid address") + ErrDeployContract = errorsmod.Register(ModuleName, 1129, "unable to deploy contract") + ErrUnableToUpdateTss = errorsmod.Register(ModuleName, 1130, "unable to update TSS address") + ErrNotEnoughGas = errorsmod.Register(ModuleName, 1131, "not enough gas") + ErrNotEnoughFunds = errorsmod.Register(ModuleName, 1132, "not enough funds") + ErrProofVerificationFail = errorsmod.Register(ModuleName, 1133, "proof verification fail") + ErrCannotFindCctx = errorsmod.Register(ModuleName, 1134, "cannot find cctx") + ErrStatusNotPending = errorsmod.Register(ModuleName, 1135, "Status not pending") + ErrCannotFindGasParams = errorsmod.Register(ModuleName, 1136, "cannot find gas params") + ErrInvalidGasAmount = errorsmod.Register(ModuleName, 1137, "invalid gas amount") + ErrNoLiquidityPool = errorsmod.Register(ModuleName, 1138, "no liquidity pool") + ErrInvalidCoinType = errorsmod.Register(ModuleName, 1139, "invalid coin type") + ErrCannotMigrateTssFunds = errorsmod.Register(ModuleName, 1140, "cannot migrate TSS funds") + ErrTxBodyVerificationFail = errorsmod.Register(ModuleName, 1141, "transaction body verification fail") + ErrReceiverIsEmpty = errorsmod.Register(ModuleName, 1142, "receiver is empty") + ErrUnsupportedStatus = errorsmod.Register(ModuleName, 1143, "unsupported status") + ErrObservedTxAlreadyFinalized = errorsmod.Register(ModuleName, 1144, "observed tx already finalized") ErrInsufficientFundsTssMigration = errorsmod.Register(ModuleName, 1145, "insufficient funds for TSS migration") + ErrInvalidCCTXIndex = errorsmod.Register(ModuleName, 1146, "invalid cctx index") + ErrInvalidStatus = errorsmod.Register(ModuleName, 1147, "invalid cctx status") + ErrUnableProcessRefund = errorsmod.Register(ModuleName, 1148, "unable to process refund") + ErrUnableToFindZetaAccounting = errorsmod.Register(ModuleName, 1149, "unable to find zeta accounting") + ErrInsufficientZetaAmount = errorsmod.Register(ModuleName, 1150, "insufficient zeta amount") ) diff --git a/x/crosschain/types/message_refund_aborted.go b/x/crosschain/types/message_refund_aborted.go new file mode 100644 index 0000000000..698d499b09 --- /dev/null +++ b/x/crosschain/types/message_refund_aborted.go @@ -0,0 +1,55 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + ethcommon "github.com/ethereum/go-ethereum/common" +) + +const RefundAborted = "RefundAbortedCCTX" + +var _ sdk.Msg = &MsgRefundAbortedCCTX{} + +func NewMsgRefundAbortedCCTX(creator string, cctxIndex string, refundAddress string) *MsgRefundAbortedCCTX { + return &MsgRefundAbortedCCTX{ + Creator: creator, + CctxIndex: cctxIndex, + RefundAddress: refundAddress, + } +} + +func (msg *MsgRefundAbortedCCTX) Route() string { + return RouterKey +} + +func (msg *MsgRefundAbortedCCTX) Type() string { + return RefundAborted +} + +func (msg *MsgRefundAbortedCCTX) GetSigners() []sdk.AccAddress { + creator, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + panic(err) + } + return []sdk.AccAddress{creator} +} + +func (msg *MsgRefundAbortedCCTX) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +func (msg *MsgRefundAbortedCCTX) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err) + } + if len(msg.CctxIndex) != 66 { + return ErrInvalidCCTXIndex + } + if msg.RefundAddress != "" && !ethcommon.IsHexAddress(msg.RefundAddress) { + return ErrInvalidAddress + } + return nil +} diff --git a/x/crosschain/types/message_refund_aborted_test.go b/x/crosschain/types/message_refund_aborted_test.go new file mode 100644 index 0000000000..7f1a0643f6 --- /dev/null +++ b/x/crosschain/types/message_refund_aborted_test.go @@ -0,0 +1,36 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func TestNewMsgRefundAbortedCCTX(t *testing.T) { + t.Run("successfully validate message", func(t *testing.T) { + cctx := sample.CrossChainTx(t, "test") + msg := types.NewMsgRefundAbortedCCTX(sample.AccAddress(), cctx.Index, "") + assert.NoError(t, msg.ValidateBasic()) + }) + t.Run("invalid creator address", func(t *testing.T) { + cctx := sample.CrossChainTx(t, "test") + msg := types.NewMsgRefundAbortedCCTX("invalid", cctx.Index, "") + assert.ErrorContains(t, msg.ValidateBasic(), "invalid creator address") + }) + t.Run("invalid cctx index", func(t *testing.T) { + msg := types.NewMsgRefundAbortedCCTX(sample.AccAddress(), "invalid", "") + assert.ErrorContains(t, msg.ValidateBasic(), "invalid cctx index") + }) + t.Run("invalid refund address", func(t *testing.T) { + cctx := sample.CrossChainTx(t, "test") + msg := types.NewMsgRefundAbortedCCTX(sample.AccAddress(), cctx.Index, "invalid") + assert.ErrorContains(t, msg.ValidateBasic(), "invalid address") + }) + t.Run("invalid refund address 2", func(t *testing.T) { + cctx := sample.CrossChainTx(t, "test") + msg := types.NewMsgRefundAbortedCCTX(sample.AccAddress(), cctx.Index, "0x91da5bf3F8Eb72724E6f50Ec6C3D199C6355c59") + assert.ErrorContains(t, msg.ValidateBasic(), "invalid address") + }) +} diff --git a/x/crosschain/types/status.go b/x/crosschain/types/status.go index 1364e5946c..be2b4af62e 100644 --- a/x/crosschain/types/status.go +++ b/x/crosschain/types/status.go @@ -4,6 +4,12 @@ import ( "fmt" ) +func (m *Status) AbortRefunded(timeStamp int64) { + m.IsAbortRefunded = true + m.StatusMessage = "CCTX aborted and Refunded" + m.LastUpdateTimestamp = timeStamp +} + // empty msg does not overwrite old status message func (m *Status) ChangeStatus(newStatus CctxStatus, msg string) { if len(msg) > 0 { diff --git a/x/crosschain/types/status_test.go b/x/crosschain/types/status_test.go new file mode 100644 index 0000000000..cadf64490e --- /dev/null +++ b/x/crosschain/types/status_test.go @@ -0,0 +1,25 @@ +package types_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func TestStatus_AbortRefunded(t *testing.T) { + t.Run("should set status to aborted and message to CCTX aborted and Refunded", func(t *testing.T) { + status := types.Status{ + Status: 0, + StatusMessage: "", + LastUpdateTimestamp: 0, + IsAbortRefunded: false, + } + timestamp := time.Now().Unix() + status.AbortRefunded(timestamp) + require.Equal(t, status.IsAbortRefunded, true) + require.Equal(t, status.StatusMessage, "CCTX aborted and Refunded") + require.Equal(t, status.LastUpdateTimestamp, timestamp) + }) +} diff --git a/x/crosschain/types/tx.pb.go b/x/crosschain/types/tx.pb.go index c784912394..650ef991dd 100644 --- a/x/crosschain/types/tx.pb.go +++ b/x/crosschain/types/tx.pb.go @@ -1350,6 +1350,102 @@ func (m *MsgAbortStuckCCTXResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgAbortStuckCCTXResponse proto.InternalMessageInfo +type MsgRefundAbortedCCTX struct { + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + CctxIndex string `protobuf:"bytes,2,opt,name=cctx_index,json=cctxIndex,proto3" json:"cctx_index,omitempty"` + RefundAddress string `protobuf:"bytes,3,opt,name=refund_address,json=refundAddress,proto3" json:"refund_address,omitempty"` +} + +func (m *MsgRefundAbortedCCTX) Reset() { *m = MsgRefundAbortedCCTX{} } +func (m *MsgRefundAbortedCCTX) String() string { return proto.CompactTextString(m) } +func (*MsgRefundAbortedCCTX) ProtoMessage() {} +func (*MsgRefundAbortedCCTX) Descriptor() ([]byte, []int) { + return fileDescriptor_81d6d611190b7635, []int{22} +} +func (m *MsgRefundAbortedCCTX) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRefundAbortedCCTX) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRefundAbortedCCTX.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgRefundAbortedCCTX) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRefundAbortedCCTX.Merge(m, src) +} +func (m *MsgRefundAbortedCCTX) XXX_Size() int { + return m.Size() +} +func (m *MsgRefundAbortedCCTX) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRefundAbortedCCTX.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRefundAbortedCCTX proto.InternalMessageInfo + +func (m *MsgRefundAbortedCCTX) GetCreator() string { + if m != nil { + return m.Creator + } + return "" +} + +func (m *MsgRefundAbortedCCTX) GetCctxIndex() string { + if m != nil { + return m.CctxIndex + } + return "" +} + +func (m *MsgRefundAbortedCCTX) GetRefundAddress() string { + if m != nil { + return m.RefundAddress + } + return "" +} + +type MsgRefundAbortedCCTXResponse struct { +} + +func (m *MsgRefundAbortedCCTXResponse) Reset() { *m = MsgRefundAbortedCCTXResponse{} } +func (m *MsgRefundAbortedCCTXResponse) String() string { return proto.CompactTextString(m) } +func (*MsgRefundAbortedCCTXResponse) ProtoMessage() {} +func (*MsgRefundAbortedCCTXResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_81d6d611190b7635, []int{23} +} +func (m *MsgRefundAbortedCCTXResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRefundAbortedCCTXResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRefundAbortedCCTXResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgRefundAbortedCCTXResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRefundAbortedCCTXResponse.Merge(m, src) +} +func (m *MsgRefundAbortedCCTXResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgRefundAbortedCCTXResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRefundAbortedCCTXResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRefundAbortedCCTXResponse proto.InternalMessageInfo + func init() { proto.RegisterType((*MsgCreateTSSVoter)(nil), "zetachain.zetacore.crosschain.MsgCreateTSSVoter") proto.RegisterType((*MsgCreateTSSVoterResponse)(nil), "zetachain.zetacore.crosschain.MsgCreateTSSVoterResponse") @@ -1373,103 +1469,108 @@ func init() { proto.RegisterType((*MsgVoteOnObservedInboundTxResponse)(nil), "zetachain.zetacore.crosschain.MsgVoteOnObservedInboundTxResponse") proto.RegisterType((*MsgAbortStuckCCTX)(nil), "zetachain.zetacore.crosschain.MsgAbortStuckCCTX") proto.RegisterType((*MsgAbortStuckCCTXResponse)(nil), "zetachain.zetacore.crosschain.MsgAbortStuckCCTXResponse") + proto.RegisterType((*MsgRefundAbortedCCTX)(nil), "zetachain.zetacore.crosschain.MsgRefundAbortedCCTX") + proto.RegisterType((*MsgRefundAbortedCCTXResponse)(nil), "zetachain.zetacore.crosschain.MsgRefundAbortedCCTXResponse") } func init() { proto.RegisterFile("crosschain/tx.proto", fileDescriptor_81d6d611190b7635) } var fileDescriptor_81d6d611190b7635 = []byte{ - // 1449 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0x6d, 0x4f, 0xdb, 0xd6, - 0x17, 0xc7, 0x7f, 0x20, 0x24, 0x07, 0x02, 0xd4, 0xd0, 0x36, 0x35, 0x25, 0x50, 0xf3, 0x6f, 0x87, - 0x26, 0x91, 0xb4, 0x54, 0xd3, 0xda, 0x6e, 0x93, 0x06, 0x51, 0x4b, 0xd9, 0x4a, 0xa9, 0x4c, 0xba, - 0x4d, 0x7d, 0x63, 0x39, 0xf6, 0xc5, 0xb1, 0x88, 0x7d, 0x23, 0xdf, 0x6b, 0x94, 0xa0, 0x49, 0x93, - 0x2a, 0xed, 0xfd, 0x34, 0x4d, 0xda, 0xb4, 0x2f, 0xb0, 0xaf, 0xd2, 0x97, 0xd5, 0x5e, 0xad, 0x9b, - 0x54, 0x4d, 0xed, 0x27, 0xd8, 0x3e, 0xc1, 0x74, 0x1f, 0x6c, 0xe2, 0x40, 0x1e, 0xa0, 0xea, 0xab, - 0xdc, 0x73, 0xee, 0x3d, 0x4f, 0xbf, 0x73, 0xce, 0x3d, 0x37, 0x86, 0x39, 0x3b, 0xc4, 0x84, 0xd8, - 0x75, 0xcb, 0x0b, 0xca, 0xb4, 0x55, 0x6a, 0x86, 0x98, 0x62, 0x75, 0xf1, 0x08, 0x51, 0x8b, 0xf3, - 0x4a, 0x7c, 0x85, 0x43, 0x54, 0x3a, 0x3e, 0xa7, 0xcd, 0xd9, 0xd8, 0xf7, 0x71, 0x50, 0x16, 0x3f, - 0x42, 0x46, 0x9b, 0x77, 0xb1, 0x8b, 0xf9, 0xb2, 0xcc, 0x56, 0x82, 0xab, 0xff, 0xa6, 0xc0, 0x85, - 0x1d, 0xe2, 0x56, 0x42, 0x64, 0x51, 0x54, 0xdd, 0xdb, 0xfb, 0x0a, 0x53, 0x14, 0xaa, 0x05, 0x98, - 0xb0, 0x19, 0x07, 0x87, 0x05, 0x65, 0x59, 0x59, 0xcd, 0x19, 0x31, 0xa9, 0x2e, 0x02, 0x50, 0x42, - 0xcc, 0x66, 0x54, 0x3b, 0x40, 0xed, 0xc2, 0xff, 0xf8, 0x66, 0x8e, 0x12, 0xf2, 0x84, 0x33, 0xd4, - 0x0f, 0x61, 0xf6, 0x00, 0xb5, 0xb7, 0x50, 0xf0, 0x0c, 0x51, 0xeb, 0x21, 0xf2, 0xdc, 0x3a, 0x2d, - 0x8c, 0x2e, 0x2b, 0xab, 0xa3, 0xc6, 0x09, 0xbe, 0xba, 0x06, 0x19, 0x42, 0x2d, 0x1a, 0x91, 0xc2, - 0xd8, 0xb2, 0xb2, 0x3a, 0xbd, 0x7e, 0xb1, 0x24, 0xfd, 0x35, 0x90, 0x8d, 0xbc, 0x43, 0xb4, 0xc7, - 0x37, 0x0d, 0x79, 0x48, 0x5f, 0x80, 0x2b, 0x27, 0x1c, 0x35, 0x10, 0x69, 0xe2, 0x80, 0x20, 0xfd, - 0x47, 0x05, 0xd4, 0x1d, 0xe2, 0xee, 0x78, 0x6e, 0xc8, 0xb6, 0x09, 0x79, 0x10, 0x05, 0x0e, 0xe9, - 0x13, 0xc7, 0x15, 0xc8, 0x72, 0xac, 0x4c, 0xcf, 0xe1, 0x51, 0x8c, 0x1a, 0x13, 0x9c, 0xde, 0x76, - 0xd4, 0x2d, 0xc8, 0x58, 0x3e, 0x8e, 0x02, 0xe1, 0x79, 0x6e, 0xb3, 0xfc, 0xe2, 0xf5, 0xd2, 0xc8, - 0x9f, 0xaf, 0x97, 0x3e, 0x70, 0x3d, 0x5a, 0x8f, 0x6a, 0xcc, 0xcb, 0xb2, 0x8d, 0x89, 0x8f, 0x89, - 0xfc, 0x59, 0x23, 0xce, 0x41, 0x99, 0xb6, 0x9b, 0x88, 0x94, 0x9e, 0x7a, 0x01, 0x35, 0xa4, 0xb8, - 0x7e, 0x15, 0xb4, 0x93, 0x3e, 0x25, 0x2e, 0x3f, 0x86, 0xb9, 0x1d, 0xe2, 0x3e, 0x6d, 0x3a, 0x62, - 0x73, 0xc3, 0x71, 0x42, 0x44, 0xc8, 0xb9, 0xa1, 0xd7, 0x17, 0x61, 0xe1, 0x14, 0x7d, 0x89, 0xb9, - 0x7f, 0x14, 0x6e, 0x6f, 0xc3, 0x71, 0xaa, 0x78, 0x3b, 0xa8, 0xb6, 0xaa, 0xa1, 0x65, 0x1f, 0xf4, - 0x4d, 0x75, 0x1f, 0x88, 0x2e, 0xc3, 0x04, 0x6d, 0x99, 0x75, 0x8b, 0xd4, 0x05, 0x46, 0x46, 0x86, - 0xb6, 0x1e, 0x5a, 0xa4, 0xae, 0xae, 0x41, 0xce, 0xc6, 0x5e, 0x60, 0x32, 0x34, 0x64, 0x5a, 0x67, - 0xe3, 0xb4, 0x56, 0xb0, 0x17, 0x54, 0xdb, 0x4d, 0x64, 0x64, 0x6d, 0xb9, 0x52, 0x57, 0x60, 0xbc, - 0x19, 0x62, 0xbc, 0x5f, 0x18, 0x5f, 0x56, 0x56, 0x27, 0xd7, 0xf3, 0xf1, 0xd1, 0x27, 0x8c, 0x69, - 0x88, 0x3d, 0x16, 0x77, 0xad, 0x81, 0xed, 0x03, 0x61, 0x2f, 0x23, 0xe2, 0xe6, 0x1c, 0x6e, 0xf2, - 0x0a, 0x64, 0x69, 0xcb, 0xf4, 0x02, 0x07, 0xb5, 0x0a, 0x13, 0xc2, 0x4d, 0xda, 0xda, 0x66, 0xa4, - 0x84, 0xa4, 0x3b, 0xe4, 0x04, 0x92, 0xdf, 0x45, 0xed, 0x7f, 0x5d, 0xf7, 0x28, 0x6a, 0x78, 0x84, - 0xde, 0x37, 0x2a, 0xeb, 0x37, 0xfb, 0x00, 0xb2, 0x02, 0x79, 0x14, 0xda, 0xeb, 0x37, 0x4d, 0x4b, - 0x60, 0x2b, 0x73, 0x30, 0xc5, 0x99, 0x71, 0xfe, 0x3a, 0x51, 0x1b, 0x4d, 0xa3, 0xa6, 0xc2, 0x58, - 0x60, 0xf9, 0x02, 0x97, 0x9c, 0xc1, 0xd7, 0xea, 0x25, 0xc8, 0x90, 0xb6, 0x5f, 0xc3, 0x0d, 0x0e, - 0x41, 0xce, 0x90, 0x94, 0xaa, 0x41, 0xd6, 0x41, 0xb6, 0xe7, 0x5b, 0x0d, 0xc2, 0x43, 0xce, 0x1b, - 0x09, 0xad, 0x2e, 0x40, 0xce, 0xb5, 0x88, 0xd9, 0xf0, 0x7c, 0x8f, 0xca, 0x90, 0xb3, 0xae, 0x45, - 0x1e, 0x31, 0x5a, 0x37, 0x79, 0x9b, 0xa4, 0x63, 0x8a, 0x23, 0x66, 0x11, 0x1c, 0xa5, 0x22, 0x10, - 0x11, 0x4e, 0x1d, 0x75, 0x46, 0xb0, 0x08, 0x60, 0xdb, 0x09, 0xa4, 0xb2, 0xce, 0x18, 0x47, 0x80, - 0xfa, 0x4a, 0x81, 0xf9, 0x18, 0xd5, 0xdd, 0x88, 0xbe, 0x63, 0x25, 0xcd, 0xc3, 0x78, 0x80, 0x03, - 0x1b, 0x71, 0xac, 0xc6, 0x0c, 0x41, 0x74, 0xd6, 0xd7, 0x58, 0xaa, 0xbe, 0xde, 0x73, 0xc1, 0x7c, - 0x06, 0x57, 0x4f, 0x0b, 0x2d, 0xc1, 0x6f, 0x11, 0xc0, 0x23, 0x66, 0x88, 0x7c, 0x7c, 0x88, 0x1c, - 0x1e, 0x65, 0xd6, 0xc8, 0x79, 0xc4, 0x10, 0x0c, 0x7d, 0x9f, 0x63, 0x2f, 0xa8, 0x07, 0x21, 0xf6, - 0xdf, 0x13, 0x3c, 0xfa, 0x0a, 0x5c, 0xeb, 0x69, 0x27, 0xa9, 0xee, 0x5f, 0x14, 0x98, 0xdd, 0x21, - 0xee, 0x96, 0x45, 0x9e, 0x84, 0x9e, 0x8d, 0x06, 0x5d, 0xec, 0xfd, 0x9d, 0x68, 0x32, 0x15, 0xb1, - 0x13, 0x9c, 0x50, 0xaf, 0xc1, 0x94, 0x40, 0x39, 0x88, 0xfc, 0x1a, 0x0a, 0x79, 0xa2, 0xc6, 0x8c, - 0x49, 0xce, 0x7b, 0xcc, 0x59, 0xbc, 0xb8, 0xa3, 0x66, 0xb3, 0xd1, 0x4e, 0x8a, 0x9b, 0x53, 0xba, - 0x06, 0x85, 0x6e, 0xcf, 0x12, 0xb7, 0x5f, 0x8d, 0xf3, 0xa6, 0x65, 0xcc, 0xdd, 0x60, 0xb7, 0x46, - 0x50, 0x78, 0x88, 0x9c, 0xdd, 0x88, 0xd6, 0x70, 0x14, 0x38, 0xd5, 0x56, 0x9f, 0x08, 0x16, 0x80, - 0x57, 0xa9, 0xc8, 0xba, 0x28, 0xdb, 0x2c, 0x63, 0xf0, 0xa4, 0x97, 0x60, 0x0e, 0x4b, 0x65, 0x26, - 0x66, 0x70, 0x75, 0xde, 0x5e, 0x17, 0xf0, 0xb1, 0x9d, 0xaa, 0x38, 0xff, 0x29, 0x68, 0x5d, 0xe7, - 0x45, 0x01, 0x89, 0x91, 0x26, 0x62, 0x2d, 0xa4, 0xc4, 0x36, 0x8f, 0xf7, 0xd5, 0x8f, 0xe0, 0x72, - 0x97, 0x34, 0x6b, 0xd8, 0x88, 0x20, 0xa7, 0x00, 0x5c, 0x74, 0x3e, 0x25, 0xba, 0x65, 0x91, 0xa7, - 0x04, 0x39, 0xea, 0x11, 0xe8, 0x5d, 0x62, 0x68, 0x7f, 0x1f, 0xd9, 0xd4, 0x3b, 0x44, 0x5c, 0x81, - 0xc8, 0xc2, 0x24, 0x9f, 0x4a, 0x25, 0x39, 0x95, 0x6e, 0x0c, 0x31, 0x95, 0xb6, 0x03, 0x6a, 0x14, - 0x53, 0x16, 0xef, 0xc7, 0x7a, 0xe3, 0x24, 0xa8, 0x5f, 0x0c, 0xb0, 0x2d, 0x6e, 0x9b, 0x29, 0xee, - 0x7d, 0x6f, 0x5d, 0xfc, 0x0e, 0x52, 0x31, 0x4c, 0x1f, 0x5a, 0x8d, 0x08, 0x99, 0xa1, 0x98, 0xe4, - 0x8e, 0xc8, 0xff, 0xe6, 0xc3, 0x33, 0x4e, 0xd2, 0x7f, 0x5f, 0x2f, 0x5d, 0x6c, 0x5b, 0x7e, 0xe3, - 0x9e, 0x9e, 0x56, 0xa7, 0x1b, 0x79, 0xce, 0x90, 0x0f, 0x05, 0xa7, 0xe3, 0x29, 0x91, 0x19, 0xe2, - 0x29, 0xa1, 0x2e, 0xc1, 0xa4, 0x08, 0x91, 0x57, 0xb8, 0xbc, 0x04, 0x80, 0xb3, 0x2a, 0x8c, 0xa3, - 0xde, 0x80, 0x19, 0x71, 0x80, 0x0d, 0x5c, 0xd1, 0x80, 0x59, 0x1e, 0x79, 0x9e, 0xb3, 0xab, 0x84, - 0x3c, 0xe6, 0xf7, 0x54, 0x6a, 0xdc, 0xe5, 0x06, 0x8d, 0x3b, 0xfd, 0x3a, 0xac, 0xf4, 0x29, 0xed, - 0xa4, 0x05, 0x9e, 0x8f, 0xf1, 0x87, 0x43, 0xfa, 0xdc, 0x76, 0x30, 0xb8, 0x03, 0x58, 0xbf, 0xa1, - 0xc0, 0x41, 0xa1, 0x2c, 0x7f, 0x49, 0xb1, 0x70, 0xc4, 0xca, 0xec, 0x1a, 0x4d, 0x79, 0xc1, 0xae, - 0xc8, 0x46, 0xd7, 0x20, 0x2b, 0x21, 0x0e, 0xe5, 0xbd, 0x9b, 0xd0, 0xea, 0x75, 0x98, 0x8e, 0xd7, - 0x12, 0xb6, 0x71, 0xa1, 0x22, 0xe6, 0x0a, 0xe4, 0x8e, 0x1f, 0x4f, 0x99, 0x77, 0x7a, 0x3c, 0xb1, - 0x28, 0x7d, 0x44, 0x88, 0xe5, 0x0a, 0xe8, 0x73, 0x46, 0x4c, 0xaa, 0x57, 0x01, 0x18, 0xe4, 0xb2, - 0x83, 0x73, 0xc2, 0x4f, 0x2f, 0x90, 0x8d, 0x7b, 0x03, 0x66, 0xbc, 0xc0, 0x94, 0xf7, 0xbf, 0xe8, - 0x56, 0xd1, 0x72, 0x79, 0x2f, 0xe8, 0x6c, 0xd1, 0xd4, 0x10, 0x9d, 0xe4, 0x27, 0x92, 0x21, 0x9a, - 0xce, 0xeb, 0xd4, 0xc0, 0x67, 0xcc, 0x02, 0xe4, 0x68, 0xcb, 0xc4, 0xa1, 0xe7, 0x7a, 0x41, 0x21, - 0x2f, 0x1c, 0xa2, 0xad, 0x5d, 0x4e, 0xb3, 0xdb, 0xd3, 0x22, 0x04, 0xd1, 0xc2, 0x34, 0xdf, 0x10, - 0x04, 0x2b, 0x41, 0x74, 0x88, 0x02, 0x2a, 0xe7, 0xd0, 0x0c, 0x77, 0x00, 0x38, 0x4b, 0x8c, 0xa2, - 0xff, 0x83, 0xde, 0xbb, 0x06, 0x92, 0x52, 0x79, 0xc4, 0x5f, 0x30, 0x1b, 0x35, 0x1c, 0xd2, 0x3d, - 0x1a, 0xd9, 0x07, 0x95, 0x4a, 0xf5, 0x9b, 0xfe, 0x4f, 0xc8, 0x7e, 0xa3, 0x5d, 0x3c, 0xb1, 0xd3, - 0xda, 0x62, 0x53, 0xeb, 0x7f, 0x4d, 0xc2, 0xe8, 0x0e, 0x71, 0xd5, 0xef, 0x15, 0xb8, 0x70, 0x72, - 0xf8, 0xdf, 0x2e, 0xf5, 0xfd, 0x4b, 0x52, 0x3a, 0x6d, 0xac, 0x6a, 0x9f, 0x9c, 0x43, 0x28, 0x99, - 0xc5, 0xcf, 0x15, 0x98, 0x3d, 0xf1, 0x9a, 0x5d, 0x1f, 0x52, 0x63, 0x87, 0x8c, 0x76, 0xef, 0xec, - 0x32, 0x89, 0x13, 0x3f, 0x29, 0x70, 0xa9, 0xc7, 0xbc, 0xbf, 0x33, 0x58, 0xed, 0xe9, 0x92, 0xda, - 0xe7, 0xe7, 0x95, 0x4c, 0xdc, 0x6a, 0x43, 0x3e, 0x3d, 0xf7, 0xcb, 0x83, 0x55, 0xa6, 0x04, 0xb4, - 0x8f, 0xcf, 0x28, 0x90, 0x98, 0xfe, 0x55, 0x81, 0x42, 0xcf, 0xe1, 0x3d, 0x04, 0xd4, 0xbd, 0x64, - 0xb5, 0xcd, 0xf3, 0xcb, 0x26, 0xce, 0xfd, 0xac, 0xc0, 0xe5, 0x5e, 0xd7, 0xea, 0xdd, 0xb3, 0xea, - 0x4f, 0x44, 0xb5, 0x8d, 0x73, 0x8b, 0x26, 0x9e, 0x7d, 0x0b, 0xd3, 0x5d, 0xff, 0x43, 0x6e, 0x0e, - 0x56, 0x9a, 0x96, 0xd0, 0xee, 0x9c, 0x55, 0x22, 0xd5, 0x4b, 0x27, 0xfe, 0x89, 0x0e, 0xd1, 0x4b, - 0xdd, 0x32, 0xc3, 0xf4, 0x52, 0xaf, 0x7f, 0xa8, 0xea, 0x77, 0x30, 0xd3, 0xfd, 0xff, 0xfd, 0xd6, - 0x60, 0x75, 0x5d, 0x22, 0xda, 0xdd, 0x33, 0x8b, 0x74, 0xe6, 0xa0, 0xeb, 0x3b, 0xc8, 0x10, 0x39, - 0x48, 0x4b, 0x0c, 0x93, 0x83, 0xd3, 0x3f, 0x61, 0x30, 0xeb, 0x5d, 0xf7, 0xf8, 0x10, 0xd6, 0xd3, - 0x12, 0xc3, 0x58, 0x3f, 0xfd, 0x76, 0xdf, 0xfc, 0xf2, 0xc5, 0x9b, 0xa2, 0xf2, 0xf2, 0x4d, 0x51, - 0xf9, 0xfb, 0x4d, 0x51, 0xf9, 0xe1, 0x6d, 0x71, 0xe4, 0xe5, 0xdb, 0xe2, 0xc8, 0x1f, 0x6f, 0x8b, - 0x23, 0xcf, 0x6e, 0x75, 0x4c, 0x6e, 0xa6, 0x73, 0x4d, 0x7c, 0x8b, 0x8a, 0xd5, 0x97, 0x5b, 0xe5, - 0xce, 0x2f, 0x54, 0x6c, 0x90, 0xd7, 0x32, 0xfc, 0xdb, 0xd2, 0xed, 0xff, 0x02, 0x00, 0x00, 0xff, - 0xff, 0x1e, 0x53, 0x7a, 0xa6, 0xbc, 0x12, 0x00, 0x00, + // 1502 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0x6f, 0x4f, 0xdb, 0x56, + 0x17, 0xc7, 0x0f, 0x10, 0x92, 0x03, 0x01, 0x6a, 0x68, 0x9b, 0x9a, 0x12, 0xa8, 0x79, 0xda, 0x07, + 0x3d, 0x12, 0x49, 0x4b, 0x35, 0xad, 0xed, 0x36, 0x69, 0x10, 0xb5, 0x94, 0xad, 0x94, 0xca, 0xa4, + 0xdb, 0xd4, 0x37, 0x96, 0x63, 0x5f, 0x1c, 0x8b, 0xc4, 0x37, 0xf2, 0xbd, 0x8e, 0x12, 0x34, 0x69, + 0x52, 0xa5, 0xbd, 0x9f, 0xa6, 0x49, 0x9b, 0xf6, 0x05, 0xf6, 0x55, 0xfa, 0xb2, 0xda, 0xab, 0x75, + 0x2f, 0xaa, 0xa9, 0x7c, 0x82, 0xed, 0x13, 0x4c, 0xf7, 0x8f, 0x4d, 0x9c, 0x90, 0x3f, 0x50, 0xf5, + 0x55, 0x7c, 0x8e, 0xef, 0xf9, 0xf7, 0x3b, 0xe7, 0xdc, 0x73, 0x62, 0x58, 0xb0, 0x03, 0x4c, 0x88, + 0x5d, 0xb5, 0x3c, 0xbf, 0x48, 0x5b, 0x85, 0x46, 0x80, 0x29, 0x56, 0x97, 0x8f, 0x11, 0xb5, 0x38, + 0xaf, 0xc0, 0x9f, 0x70, 0x80, 0x0a, 0xa7, 0xe7, 0xb4, 0x05, 0x1b, 0xd7, 0xeb, 0xd8, 0x2f, 0x8a, + 0x1f, 0x21, 0xa3, 0x2d, 0xba, 0xd8, 0xc5, 0xfc, 0xb1, 0xc8, 0x9e, 0x04, 0x57, 0xff, 0x4d, 0x81, + 0x4b, 0x7b, 0xc4, 0x2d, 0x05, 0xc8, 0xa2, 0xa8, 0x7c, 0x70, 0xf0, 0x15, 0xa6, 0x28, 0x50, 0x73, + 0x30, 0x65, 0x33, 0x0e, 0x0e, 0x72, 0xca, 0xaa, 0xb2, 0x9e, 0x31, 0x22, 0x52, 0x5d, 0x06, 0xa0, + 0x84, 0x98, 0x8d, 0xb0, 0x72, 0x84, 0xda, 0xb9, 0xff, 0xf0, 0x97, 0x19, 0x4a, 0xc8, 0x33, 0xce, + 0x50, 0xff, 0x0f, 0xf3, 0x47, 0xa8, 0xbd, 0x83, 0xfc, 0x17, 0x88, 0x5a, 0x8f, 0x91, 0xe7, 0x56, + 0x69, 0x6e, 0x7c, 0x55, 0x59, 0x1f, 0x37, 0x7a, 0xf8, 0xea, 0x06, 0xa4, 0x08, 0xb5, 0x68, 0x48, + 0x72, 0x13, 0xab, 0xca, 0xfa, 0xec, 0xe6, 0xe5, 0x82, 0xf4, 0xd7, 0x40, 0x36, 0xf2, 0x9a, 0xe8, + 0x80, 0xbf, 0x34, 0xe4, 0x21, 0x7d, 0x09, 0xae, 0xf5, 0x38, 0x6a, 0x20, 0xd2, 0xc0, 0x3e, 0x41, + 0xfa, 0x8f, 0x0a, 0xa8, 0x7b, 0xc4, 0xdd, 0xf3, 0xdc, 0x80, 0xbd, 0x26, 0xe4, 0x51, 0xe8, 0x3b, + 0x64, 0x40, 0x1c, 0xd7, 0x20, 0xcd, 0xb1, 0x32, 0x3d, 0x87, 0x47, 0x31, 0x6e, 0x4c, 0x71, 0x7a, + 0xd7, 0x51, 0x77, 0x20, 0x65, 0xd5, 0x71, 0xe8, 0x0b, 0xcf, 0x33, 0xdb, 0xc5, 0x57, 0x6f, 0x57, + 0xc6, 0xfe, 0x7c, 0xbb, 0xf2, 0x3f, 0xd7, 0xa3, 0xd5, 0xb0, 0xc2, 0xbc, 0x2c, 0xda, 0x98, 0xd4, + 0x31, 0x91, 0x3f, 0x1b, 0xc4, 0x39, 0x2a, 0xd2, 0x76, 0x03, 0x91, 0xc2, 0x73, 0xcf, 0xa7, 0x86, + 0x14, 0xd7, 0xaf, 0x83, 0xd6, 0xeb, 0x53, 0xec, 0xf2, 0x53, 0x58, 0xd8, 0x23, 0xee, 0xf3, 0x86, + 0x23, 0x5e, 0x6e, 0x39, 0x4e, 0x80, 0x08, 0xb9, 0x30, 0xf4, 0xfa, 0x32, 0x2c, 0x9d, 0xa1, 0x2f, + 0x36, 0xf7, 0xb7, 0xc2, 0xed, 0x6d, 0x39, 0x4e, 0x19, 0xef, 0xfa, 0xe5, 0x56, 0x39, 0xb0, 0xec, + 0xa3, 0x81, 0xa9, 0x1e, 0x00, 0xd1, 0x55, 0x98, 0xa2, 0x2d, 0xb3, 0x6a, 0x91, 0xaa, 0xc0, 0xc8, + 0x48, 0xd1, 0xd6, 0x63, 0x8b, 0x54, 0xd5, 0x0d, 0xc8, 0xd8, 0xd8, 0xf3, 0x4d, 0x86, 0x86, 0x4c, + 0xeb, 0x7c, 0x94, 0xd6, 0x12, 0xf6, 0xfc, 0x72, 0xbb, 0x81, 0x8c, 0xb4, 0x2d, 0x9f, 0xd4, 0x35, + 0x98, 0x6c, 0x04, 0x18, 0x1f, 0xe6, 0x26, 0x57, 0x95, 0xf5, 0xe9, 0xcd, 0x6c, 0x74, 0xf4, 0x19, + 0x63, 0x1a, 0xe2, 0x1d, 0x8b, 0xbb, 0x52, 0xc3, 0xf6, 0x91, 0xb0, 0x97, 0x12, 0x71, 0x73, 0x0e, + 0x37, 0x79, 0x0d, 0xd2, 0xb4, 0x65, 0x7a, 0xbe, 0x83, 0x5a, 0xb9, 0x29, 0xe1, 0x26, 0x6d, 0xed, + 0x32, 0x52, 0x42, 0xd2, 0x1d, 0x72, 0x0c, 0xc9, 0xef, 0xa2, 0xf6, 0xbf, 0xae, 0x7a, 0x14, 0xd5, + 0x3c, 0x42, 0x1f, 0x1a, 0xa5, 0xcd, 0xdb, 0x03, 0x00, 0x59, 0x83, 0x2c, 0x0a, 0xec, 0xcd, 0xdb, + 0xa6, 0x25, 0xb0, 0x95, 0x39, 0x98, 0xe1, 0xcc, 0x28, 0x7f, 0x9d, 0xa8, 0x8d, 0x27, 0x51, 0x53, + 0x61, 0xc2, 0xb7, 0xea, 0x02, 0x97, 0x8c, 0xc1, 0x9f, 0xd5, 0x2b, 0x90, 0x22, 0xed, 0x7a, 0x05, + 0xd7, 0x38, 0x04, 0x19, 0x43, 0x52, 0xaa, 0x06, 0x69, 0x07, 0xd9, 0x5e, 0xdd, 0xaa, 0x11, 0x1e, + 0x72, 0xd6, 0x88, 0x69, 0x75, 0x09, 0x32, 0xae, 0x45, 0xcc, 0x9a, 0x57, 0xf7, 0xa8, 0x0c, 0x39, + 0xed, 0x5a, 0xe4, 0x09, 0xa3, 0x75, 0x93, 0xb7, 0x49, 0x32, 0xa6, 0x28, 0x62, 0x16, 0xc1, 0x71, + 0x22, 0x02, 0x11, 0xe1, 0xcc, 0x71, 0x67, 0x04, 0xcb, 0x00, 0xb6, 0x1d, 0x43, 0x2a, 0xeb, 0x8c, + 0x71, 0x04, 0xa8, 0x6f, 0x14, 0x58, 0x8c, 0x50, 0xdd, 0x0f, 0xe9, 0x7b, 0x56, 0xd2, 0x22, 0x4c, + 0xfa, 0xd8, 0xb7, 0x11, 0xc7, 0x6a, 0xc2, 0x10, 0x44, 0x67, 0x7d, 0x4d, 0x24, 0xea, 0xeb, 0x03, + 0x17, 0xcc, 0x67, 0x70, 0xfd, 0xac, 0xd0, 0x62, 0xfc, 0x96, 0x01, 0x3c, 0x62, 0x06, 0xa8, 0x8e, + 0x9b, 0xc8, 0xe1, 0x51, 0xa6, 0x8d, 0x8c, 0x47, 0x0c, 0xc1, 0xd0, 0x0f, 0x39, 0xf6, 0x82, 0x7a, + 0x14, 0xe0, 0xfa, 0x07, 0x82, 0x47, 0x5f, 0x83, 0x1b, 0x7d, 0xed, 0xc4, 0xd5, 0xfd, 0x8b, 0x02, + 0xf3, 0x7b, 0xc4, 0xdd, 0xb1, 0xc8, 0xb3, 0xc0, 0xb3, 0xd1, 0xb0, 0x8b, 0x7d, 0xb0, 0x13, 0x0d, + 0xa6, 0x22, 0x72, 0x82, 0x13, 0xea, 0x0d, 0x98, 0x11, 0x28, 0xfb, 0x61, 0xbd, 0x82, 0x02, 0x9e, + 0xa8, 0x09, 0x63, 0x9a, 0xf3, 0x9e, 0x72, 0x16, 0x2f, 0xee, 0xb0, 0xd1, 0xa8, 0xb5, 0xe3, 0xe2, + 0xe6, 0x94, 0xae, 0x41, 0xae, 0xdb, 0xb3, 0xd8, 0xed, 0x37, 0x93, 0xbc, 0x69, 0x19, 0x73, 0xdf, + 0xdf, 0xaf, 0x10, 0x14, 0x34, 0x91, 0xb3, 0x1f, 0xd2, 0x0a, 0x0e, 0x7d, 0xa7, 0xdc, 0x1a, 0x10, + 0xc1, 0x12, 0xf0, 0x2a, 0x15, 0x59, 0x17, 0x65, 0x9b, 0x66, 0x0c, 0x9e, 0xf4, 0x02, 0x2c, 0x60, + 0xa9, 0xcc, 0xc4, 0x0c, 0xae, 0xce, 0xdb, 0xeb, 0x12, 0x3e, 0xb5, 0x53, 0x16, 0xe7, 0x3f, 0x05, + 0xad, 0xeb, 0xbc, 0x28, 0x20, 0x31, 0xd2, 0x44, 0xac, 0xb9, 0x84, 0xd8, 0xf6, 0xe9, 0x7b, 0xf5, + 0x23, 0xb8, 0xda, 0x25, 0xcd, 0x1a, 0x36, 0x24, 0xc8, 0xc9, 0x01, 0x17, 0x5d, 0x4c, 0x88, 0xee, + 0x58, 0xe4, 0x39, 0x41, 0x8e, 0x7a, 0x0c, 0x7a, 0x97, 0x18, 0x3a, 0x3c, 0x44, 0x36, 0xf5, 0x9a, + 0x88, 0x2b, 0x10, 0x59, 0x98, 0xe6, 0x53, 0xa9, 0x20, 0xa7, 0xd2, 0xad, 0x11, 0xa6, 0xd2, 0xae, + 0x4f, 0x8d, 0x7c, 0xc2, 0xe2, 0xc3, 0x48, 0x6f, 0x94, 0x04, 0xf5, 0x8b, 0x21, 0xb6, 0xc5, 0x6d, + 0x33, 0xc3, 0xbd, 0xef, 0xaf, 0x8b, 0xdf, 0x41, 0x2a, 0x86, 0xd9, 0xa6, 0x55, 0x0b, 0x91, 0x19, + 0x88, 0x49, 0xee, 0x88, 0xfc, 0x6f, 0x3f, 0x3e, 0xe7, 0x24, 0xfd, 0xe7, 0xed, 0xca, 0xe5, 0xb6, + 0x55, 0xaf, 0x3d, 0xd0, 0x93, 0xea, 0x74, 0x23, 0xcb, 0x19, 0x72, 0x51, 0x70, 0x3a, 0x56, 0x89, + 0xd4, 0x08, 0xab, 0x84, 0xba, 0x02, 0xd3, 0x22, 0x44, 0x5e, 0xe1, 0xf2, 0x12, 0x00, 0xce, 0x2a, + 0x31, 0x8e, 0x7a, 0x0b, 0xe6, 0xc4, 0x01, 0x36, 0x70, 0x45, 0x03, 0xa6, 0x79, 0xe4, 0x59, 0xce, + 0x2e, 0x13, 0xf2, 0x94, 0xdf, 0x53, 0x89, 0x71, 0x97, 0x19, 0x36, 0xee, 0xf4, 0x9b, 0xb0, 0x36, + 0xa0, 0xb4, 0xe3, 0x16, 0x78, 0x39, 0xc1, 0x17, 0x87, 0xe4, 0xb9, 0x5d, 0x7f, 0x78, 0x07, 0xb0, + 0x7e, 0x43, 0xbe, 0x83, 0x02, 0x59, 0xfe, 0x92, 0x62, 0xe1, 0x88, 0x27, 0xb3, 0x6b, 0x34, 0x65, + 0x05, 0xbb, 0x24, 0x1b, 0x5d, 0x83, 0xb4, 0x84, 0x38, 0x90, 0xf7, 0x6e, 0x4c, 0xab, 0x37, 0x61, + 0x36, 0x7a, 0x96, 0xb0, 0x4d, 0x0a, 0x15, 0x11, 0x57, 0x20, 0x77, 0xba, 0x3c, 0xa5, 0xde, 0x6b, + 0x79, 0x62, 0x51, 0xd6, 0x11, 0x21, 0x96, 0x2b, 0xa0, 0xcf, 0x18, 0x11, 0xa9, 0x5e, 0x07, 0x60, + 0x90, 0xcb, 0x0e, 0xce, 0x08, 0x3f, 0x3d, 0x5f, 0x36, 0xee, 0x2d, 0x98, 0xf3, 0x7c, 0x53, 0xde, + 0xff, 0xa2, 0x5b, 0x45, 0xcb, 0x65, 0x3d, 0xbf, 0xb3, 0x45, 0x13, 0x43, 0x74, 0x9a, 0x9f, 0x88, + 0x87, 0x68, 0x32, 0xaf, 0x33, 0x43, 0xd7, 0x98, 0x25, 0xc8, 0xd0, 0x96, 0x89, 0x03, 0xcf, 0xf5, + 0xfc, 0x5c, 0x56, 0x38, 0x44, 0x5b, 0xfb, 0x9c, 0x66, 0xb7, 0xa7, 0x45, 0x08, 0xa2, 0xb9, 0x59, + 0xfe, 0x42, 0x10, 0xac, 0x04, 0x51, 0x13, 0xf9, 0x54, 0xce, 0xa1, 0x39, 0xee, 0x00, 0x70, 0x96, + 0x18, 0x45, 0xff, 0x05, 0xbd, 0x7f, 0x0d, 0xc4, 0xa5, 0xf2, 0x84, 0x6f, 0x30, 0x5b, 0x15, 0x1c, + 0xd0, 0x03, 0x1a, 0xda, 0x47, 0xa5, 0x52, 0xf9, 0x9b, 0xc1, 0x2b, 0xe4, 0xa0, 0xd1, 0x2e, 0x56, + 0xec, 0xa4, 0xb6, 0xd8, 0x54, 0x93, 0x8f, 0x7d, 0x03, 0x1d, 0x86, 0xbe, 0xc3, 0x8f, 0x20, 0xe7, + 0xbd, 0xac, 0x89, 0x8a, 0x62, 0xda, 0xe2, 0x6d, 0x44, 0xdc, 0xc6, 0x59, 0xc1, 0x95, 0xeb, 0x88, + 0x9e, 0xe7, 0x33, 0xb9, 0xc7, 0x6e, 0xe4, 0xd7, 0xe6, 0xc9, 0x0c, 0x8c, 0xef, 0x11, 0x57, 0xfd, + 0x5e, 0x81, 0x4b, 0xbd, 0x4b, 0xc9, 0xdd, 0xc2, 0xc0, 0xbf, 0x4a, 0x85, 0xb3, 0xc6, 0xbd, 0xf6, + 0xc9, 0x05, 0x84, 0xe2, 0x1d, 0xe1, 0xa5, 0x02, 0xf3, 0x3d, 0x5b, 0xf6, 0xe6, 0x88, 0x1a, 0x3b, + 0x64, 0xb4, 0x07, 0xe7, 0x97, 0x89, 0x9d, 0xf8, 0x49, 0x81, 0x2b, 0x7d, 0xf6, 0x90, 0x7b, 0xc3, + 0xd5, 0x9e, 0x2d, 0xa9, 0x7d, 0x7e, 0x51, 0xc9, 0xd8, 0xad, 0x36, 0x64, 0x93, 0xfb, 0x48, 0x71, + 0xb8, 0xca, 0x84, 0x80, 0xf6, 0xf1, 0x39, 0x05, 0x62, 0xd3, 0xbf, 0x2a, 0x90, 0xeb, 0xbb, 0x54, + 0x8c, 0x00, 0x75, 0x3f, 0x59, 0x6d, 0xfb, 0xe2, 0xb2, 0xb1, 0x73, 0x3f, 0x2b, 0x70, 0xb5, 0xdf, + 0x75, 0x7f, 0xff, 0xbc, 0xfa, 0x63, 0x51, 0x6d, 0xeb, 0xc2, 0xa2, 0xb1, 0x67, 0xdf, 0xc2, 0x6c, + 0xd7, 0xff, 0xa3, 0xdb, 0xc3, 0x95, 0x26, 0x25, 0xb4, 0x7b, 0xe7, 0x95, 0x48, 0xf4, 0x52, 0xcf, + 0x3f, 0xe4, 0x11, 0x7a, 0xa9, 0x5b, 0x66, 0x94, 0x5e, 0xea, 0xf7, 0xcf, 0x59, 0xfd, 0x0e, 0xe6, + 0xba, 0xbf, 0x2b, 0xdc, 0x19, 0xae, 0xae, 0x4b, 0x44, 0xbb, 0x7f, 0x6e, 0x91, 0xce, 0x1c, 0x74, + 0x7d, 0x9f, 0x19, 0x21, 0x07, 0x49, 0x89, 0x51, 0x72, 0x70, 0xf6, 0xa7, 0x15, 0x66, 0xbd, 0x6b, + 0xbe, 0x8c, 0x60, 0x3d, 0x29, 0x31, 0x8a, 0xf5, 0xb3, 0xa7, 0x0e, 0xbf, 0xd5, 0x7b, 0x67, 0xce, + 0xdd, 0x51, 0x6e, 0xa2, 0x2e, 0xa1, 0x51, 0x6e, 0xf5, 0xbe, 0x53, 0x66, 0xfb, 0xcb, 0x57, 0xef, + 0xf2, 0xca, 0xeb, 0x77, 0x79, 0xe5, 0xaf, 0x77, 0x79, 0xe5, 0x87, 0x93, 0xfc, 0xd8, 0xeb, 0x93, + 0xfc, 0xd8, 0x1f, 0x27, 0xf9, 0xb1, 0x17, 0x77, 0x3a, 0x36, 0x1b, 0xa6, 0x76, 0x43, 0x7c, 0xab, + 0x8b, 0x2c, 0x14, 0x5b, 0xc5, 0xce, 0x2f, 0x78, 0x6c, 0xd1, 0xa9, 0xa4, 0xf8, 0xb7, 0xb7, 0xbb, + 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x8e, 0x99, 0xcd, 0x4c, 0xdc, 0x13, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1495,6 +1596,7 @@ type MsgClient interface { MigrateTssFunds(ctx context.Context, in *MsgMigrateTssFunds, opts ...grpc.CallOption) (*MsgMigrateTssFundsResponse, error) CreateTSSVoter(ctx context.Context, in *MsgCreateTSSVoter, opts ...grpc.CallOption) (*MsgCreateTSSVoterResponse, error) AbortStuckCCTX(ctx context.Context, in *MsgAbortStuckCCTX, opts ...grpc.CallOption) (*MsgAbortStuckCCTXResponse, error) + RefundAbortedCCTX(ctx context.Context, in *MsgRefundAbortedCCTX, opts ...grpc.CallOption) (*MsgRefundAbortedCCTXResponse, error) } type msgClient struct { @@ -1604,6 +1706,15 @@ func (c *msgClient) AbortStuckCCTX(ctx context.Context, in *MsgAbortStuckCCTX, o return out, nil } +func (c *msgClient) RefundAbortedCCTX(ctx context.Context, in *MsgRefundAbortedCCTX, opts ...grpc.CallOption) (*MsgRefundAbortedCCTXResponse, error) { + out := new(MsgRefundAbortedCCTXResponse) + err := c.cc.Invoke(ctx, "/zetachain.zetacore.crosschain.Msg/RefundAbortedCCTX", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // MsgServer is the server API for Msg service. type MsgServer interface { AddToOutTxTracker(context.Context, *MsgAddToOutTxTracker) (*MsgAddToOutTxTrackerResponse, error) @@ -1617,6 +1728,7 @@ type MsgServer interface { MigrateTssFunds(context.Context, *MsgMigrateTssFunds) (*MsgMigrateTssFundsResponse, error) CreateTSSVoter(context.Context, *MsgCreateTSSVoter) (*MsgCreateTSSVoterResponse, error) AbortStuckCCTX(context.Context, *MsgAbortStuckCCTX) (*MsgAbortStuckCCTXResponse, error) + RefundAbortedCCTX(context.Context, *MsgRefundAbortedCCTX) (*MsgRefundAbortedCCTXResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. @@ -1656,6 +1768,9 @@ func (*UnimplementedMsgServer) CreateTSSVoter(ctx context.Context, req *MsgCreat func (*UnimplementedMsgServer) AbortStuckCCTX(ctx context.Context, req *MsgAbortStuckCCTX) (*MsgAbortStuckCCTXResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AbortStuckCCTX not implemented") } +func (*UnimplementedMsgServer) RefundAbortedCCTX(ctx context.Context, req *MsgRefundAbortedCCTX) (*MsgRefundAbortedCCTXResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RefundAbortedCCTX not implemented") +} func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) @@ -1859,6 +1974,24 @@ func _Msg_AbortStuckCCTX_Handler(srv interface{}, ctx context.Context, dec func( return interceptor(ctx, in, info, handler) } +func _Msg_RefundAbortedCCTX_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgRefundAbortedCCTX) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).RefundAbortedCCTX(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/zetachain.zetacore.crosschain.Msg/RefundAbortedCCTX", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).RefundAbortedCCTX(ctx, req.(*MsgRefundAbortedCCTX)) + } + return interceptor(ctx, in, info, handler) +} + var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "zetachain.zetacore.crosschain.Msg", HandlerType: (*MsgServer)(nil), @@ -1907,6 +2040,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "AbortStuckCCTX", Handler: _Msg_AbortStuckCCTX_Handler, }, + { + MethodName: "RefundAbortedCCTX", + Handler: _Msg_RefundAbortedCCTX_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "crosschain/tx.proto", @@ -2866,6 +3003,73 @@ func (m *MsgAbortStuckCCTXResponse) MarshalToSizedBuffer(dAtA []byte) (int, erro return len(dAtA) - i, nil } +func (m *MsgRefundAbortedCCTX) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgRefundAbortedCCTX) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgRefundAbortedCCTX) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.RefundAddress) > 0 { + i -= len(m.RefundAddress) + copy(dAtA[i:], m.RefundAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.RefundAddress))) + i-- + dAtA[i] = 0x1a + } + if len(m.CctxIndex) > 0 { + i -= len(m.CctxIndex) + copy(dAtA[i:], m.CctxIndex) + i = encodeVarintTx(dAtA, i, uint64(len(m.CctxIndex))) + i-- + dAtA[i] = 0x12 + } + if len(m.Creator) > 0 { + i -= len(m.Creator) + copy(dAtA[i:], m.Creator) + i = encodeVarintTx(dAtA, i, uint64(len(m.Creator))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgRefundAbortedCCTXResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgRefundAbortedCCTXResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgRefundAbortedCCTXResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + func encodeVarintTx(dAtA []byte, offset int, v uint64) int { offset -= sovTx(v) base := offset @@ -3312,6 +3516,36 @@ func (m *MsgAbortStuckCCTXResponse) Size() (n int) { return n } +func (m *MsgRefundAbortedCCTX) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Creator) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.CctxIndex) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.RefundAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgRefundAbortedCCTXResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + func sovTx(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -6240,6 +6474,202 @@ func (m *MsgAbortStuckCCTXResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgRefundAbortedCCTX) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgRefundAbortedCCTX: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRefundAbortedCCTX: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Creator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Creator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CctxIndex", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CctxIndex = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RefundAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RefundAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgRefundAbortedCCTXResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgRefundAbortedCCTXResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRefundAbortedCCTXResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTx(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0