diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 40fa15acbf..a118324779 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,3 +1,3 @@ -* @brewmaster012 @kingpinXD @kevinssgh @fadeev @lumtis @ws4charlie @skosito +* @brewmaster012 @kingpinXD @kevinssgh @lumtis @ws4charlie @skosito .github/** @zeta-chain/devops diff --git a/.github/workflows/change-log-check.yml b/.github/workflows/change-log-check.yml index 6fe9ac2a9f..e720c4ac68 100644 --- a/.github/workflows/change-log-check.yml +++ b/.github/workflows/change-log-check.yml @@ -2,7 +2,7 @@ name: Changelog Check on: pull_request: - types: [opened, synchronize, reopened] + types: [opened, synchronize, reopened, labeled, unlabeled] jobs: check-changelog: diff --git a/app/ante/ante.go b/app/ante/ante.go index 54f5ac6f5a..ef38309429 100644 --- a/app/ante/ante.go +++ b/app/ante/ante.go @@ -172,7 +172,7 @@ func IsSystemTx(tx sdk.Tx, isAuthorizedSigner func(string) bool) bool { *cctxtypes.MsgVoteOnObservedInboundTx, *cctxtypes.MsgVoteOnObservedOutboundTx, *cctxtypes.MsgAddToOutTxTracker, - *cctxtypes.MsgCreateTSSVoter, + *observertypes.MsgVoteTSS, *observertypes.MsgAddBlockHeader, *observertypes.MsgAddBlameVote: signers := innerMsg.GetSigners() diff --git a/app/ante/ante_test.go b/app/ante/ante_test.go index 146e50c6ae..f013784285 100644 --- a/app/ante/ante_test.go +++ b/app/ante/ante_test.go @@ -36,7 +36,7 @@ func TestIsSystemTx(t *testing.T) { // *cctxtypes.MsgVoteOnObservedInboundTx, // *cctxtypes.MsgVoteOnObservedOutboundTx, // *cctxtypes.MsgAddToOutTxTracker, - // *cctxtypes.MsgCreateTSSVoter, + // *observertypes.MsgVoteTSS, // *observertypes.MsgAddBlockHeader, // *observertypes.MsgAddBlameVote: buildTxFromMsg := func(msg sdk.Msg) sdk.Tx { @@ -64,8 +64,8 @@ func TestIsSystemTx(t *testing.T) { wantIs bool }{ { - "MsgCreateTSSVoter", - buildTxFromMsg(&crosschaintypes.MsgCreateTSSVoter{ + "MsgVoteTSS", + buildTxFromMsg(&observertypes.MsgVoteTSS{ Creator: sample.AccAddress(), TssPubkey: "pubkey1234", }), @@ -73,8 +73,8 @@ func TestIsSystemTx(t *testing.T) { false, }, { - "MsgCreateTSSVoter", - buildTxFromMsg(&crosschaintypes.MsgCreateTSSVoter{ + "MsgVoteTSS", + buildTxFromMsg(&observertypes.MsgVoteTSS{ Creator: sample.AccAddress(), TssPubkey: "pubkey1234", }), @@ -82,8 +82,8 @@ func TestIsSystemTx(t *testing.T) { true, }, { - "MsgExec{MsgCreateTSSVoter}", - buildAuthzTxFromMsg(&crosschaintypes.MsgCreateTSSVoter{ + "MsgExec{MsgVoteTSS}", + buildAuthzTxFromMsg(&observertypes.MsgVoteTSS{ Creator: sample.AccAddress(), TssPubkey: "pubkey1234", }), @@ -169,8 +169,8 @@ func TestIsSystemTx(t *testing.T) { true, }, { - "MsgCreateTSSVoter", - buildTxFromMsg(&crosschaintypes.MsgCreateTSSVoter{ + "MsgVoteTSS", + buildTxFromMsg(&observertypes.MsgVoteTSS{ Creator: sample.AccAddress(), }), isAuthorized, @@ -178,8 +178,8 @@ func TestIsSystemTx(t *testing.T) { true, }, { - "MsgExec{MsgCreateTSSVoter}", - buildAuthzTxFromMsg(&crosschaintypes.MsgCreateTSSVoter{ + "MsgExec{MsgVoteTSS}", + buildAuthzTxFromMsg(&observertypes.MsgVoteTSS{ Creator: sample.AccAddress(), }), isAuthorized, diff --git a/changelog.md b/changelog.md index 8cceff3e79..dac0b2bb99 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,6 @@ # CHANGELOG ## Unreleased - ### Breaking Changes * Admin policies have been moved from `observer` to a new module `authority`. @@ -9,6 +8,9 @@ * The `Policies` query of the `authority` module must be used to get the current admin policies. * `PolicyType_group1` has been renamed into `PolicyType_groupEmergency` and `PolicyType_group2` has been renamed into `PolicyType_groupAdmin`. +* `MsgCreateTSSVoter` message in the `crosschain` module has been moved to the `observer` module and renamed to `MsgVoteTSS`. + * The structure of the message remains the same. + ### Refactor * [1511](https://github.com/zeta-chain/node/pull/1511) - move ballot voting logic from `crosschain` to `observer` @@ -22,6 +24,8 @@ * [1914](https://github.com/zeta-chain/node/pull/1914) - move crosschain flags to core context in zetaclient * [1948](https://github.com/zeta-chain/node/pull/1948) - remove deprecated GetTSSAddress query in crosschain module * [1936](https://github.com/zeta-chain/node/pull/1936) - refactor common package into subpackages and rename to pkg +* [1966](https://github.com/zeta-chain/node/pull/1966) - move TSS vote message from crosschain to observer +* [1853](https://github.com/zeta-chain/node/pull/1853) - refactor vote inbound tx and vote outbound tx ### Features @@ -47,6 +51,7 @@ * [1879](https://github.com/zeta-chain/node/pull/1879) - full coverage for messages in types packages * [1899](https://github.com/zeta-chain/node/pull/1899) - add empty test files so packages are included in coverage * [1903](https://github.com/zeta-chain/node/pull/1903) - common package tests +* [1955](https://github.com/zeta-chain/node/pull/1955) - improve emissions module coverage ### Fixes diff --git a/cmd/zetaclientd/debug.go b/cmd/zetaclientd/debug.go index 76450875cb..32cb32c772 100644 --- a/cmd/zetaclientd/debug.go +++ b/cmd/zetaclientd/debug.go @@ -8,25 +8,24 @@ import ( "strings" "sync" + "github.com/btcsuite/btcd/rpcclient" + sdk "github.com/cosmos/cosmos-sdk/types" + ethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" "github.com/onrik/ethrpc" + "github.com/rs/zerolog" + "github.com/spf13/cobra" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" + "github.com/zeta-chain/zetacore/testutil/sample" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/bitcoin" + "github.com/zeta-chain/zetacore/zetaclient/config" corecontext "github.com/zeta-chain/zetacore/zetaclient/core_context" "github.com/zeta-chain/zetacore/zetaclient/evm" "github.com/zeta-chain/zetacore/zetaclient/keys" "github.com/zeta-chain/zetacore/zetaclient/metrics" "github.com/zeta-chain/zetacore/zetaclient/zetabridge" - - "github.com/btcsuite/btcd/rpcclient" - sdk "github.com/cosmos/cosmos-sdk/types" - ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/rs/zerolog" - "github.com/spf13/cobra" - "github.com/zeta-chain/zetacore/testutil/sample" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" - "github.com/zeta-chain/zetacore/zetaclient/config" ) var debugArgs = debugArguments{} diff --git a/codecov.yml b/codecov.yml index 93d974911b..1a9ba2063d 100644 --- a/codecov.yml +++ b/codecov.yml @@ -38,7 +38,7 @@ flags: - "common/" ignore: - - "x/**/client/" + - "x/**/client/**/*" - "x/**/keeper/keeper.go" - "x/**/keeper/msg_server.go" - "x/**/keeper/grpc_query_params.go" @@ -50,7 +50,7 @@ ignore: - "x/**/types/expected_keepers.go" - "x/**/module.go" - "x/**/module_simulation.go" - - "x/**/simulation/" + - "x/**/simulation/**/*" - "**/*.proto" - "**/*.md" - "**/*.yml" @@ -58,16 +58,16 @@ ignore: - "**/*.pb.go" - "**/*.pb.gw.go" - "**/*.json" - - ".github/" - - "app/" - - "cmd/" - - "contrib/" - - "docs/" - - "rpc/" - - "proto/" - - "scripts/" - - "server/" - - "testutil/" - - "testutils/" - - "errors/" - - "typescript/" \ No newline at end of file + - ".github/**/*" + - "app/**/*" + - "cmd/**/*" + - "contrib/**/*" + - "docs/**/*" + - "rpc/**/*" + - "proto/**/*" + - "scripts/**/*" + - "server/**/*" + - "testutil/**/*" + - "testutils/**/*" + - "errors/**/*" + - "typescript/**/*" \ No newline at end of file diff --git a/docs/cli/zetacored/zetacored_tx_crosschain.md b/docs/cli/zetacored/zetacored_tx_crosschain.md index 5e61146b17..40582875aa 100644 --- a/docs/cli/zetacored/zetacored_tx_crosschain.md +++ b/docs/cli/zetacored/zetacored_tx_crosschain.md @@ -29,7 +29,6 @@ zetacored tx crosschain [flags] * [zetacored tx crosschain add-to-in-tx-tracker](zetacored_tx_crosschain_add-to-in-tx-tracker.md) - Add a in-tx-tracker Use 0:Zeta,1:Gas,2:ERC20 * [zetacored tx crosschain add-to-out-tx-tracker](zetacored_tx_crosschain_add-to-out-tx-tracker.md) - Add a out-tx-tracker -* [zetacored tx crosschain create-tss-voter](zetacored_tx_crosschain_create-tss-voter.md) - Create a new TSSVoter * [zetacored tx crosschain gas-price-voter](zetacored_tx_crosschain_gas-price-voter.md) - Broadcast message gasPriceVoter * [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 diff --git a/docs/cli/zetacored/zetacored_tx_observer.md b/docs/cli/zetacored/zetacored_tx_observer.md index de1145c512..ec0e245fe0 100644 --- a/docs/cli/zetacored/zetacored_tx_observer.md +++ b/docs/cli/zetacored/zetacored_tx_observer.md @@ -34,4 +34,5 @@ zetacored tx observer [flags] * [zetacored tx observer update-crosschain-flags](zetacored_tx_observer_update-crosschain-flags.md) - Update crosschain flags * [zetacored tx observer update-keygen](zetacored_tx_observer_update-keygen.md) - command to update the keygen block via a group proposal * [zetacored tx observer update-observer](zetacored_tx_observer_update-observer.md) - Broadcast message add-observer +* [zetacored tx observer vote-tss](zetacored_tx_observer_vote-tss.md) - Vote for a new TSS creation diff --git a/docs/cli/zetacored/zetacored_tx_crosschain_create-tss-voter.md b/docs/cli/zetacored/zetacored_tx_observer_vote-tss.md similarity index 91% rename from docs/cli/zetacored/zetacored_tx_crosschain_create-tss-voter.md rename to docs/cli/zetacored/zetacored_tx_observer_vote-tss.md index b6932a3cd8..388c8c3793 100644 --- a/docs/cli/zetacored/zetacored_tx_crosschain_create-tss-voter.md +++ b/docs/cli/zetacored/zetacored_tx_observer_vote-tss.md @@ -1,9 +1,9 @@ -# tx crosschain create-tss-voter +# tx observer vote-tss -Create a new TSSVoter +Vote for a new TSS creation ``` -zetacored tx crosschain create-tss-voter [pubkey] [keygenBlock] [status] [flags] +zetacored tx observer vote-tss [pubkey] [keygen-block] [status] [flags] ``` ### Options @@ -21,7 +21,7 @@ zetacored tx crosschain create-tss-voter [pubkey] [keygenBlock] [status] [flags] --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 create-tss-voter + -h, --help help for vote-tss --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 @@ -48,5 +48,5 @@ zetacored tx crosschain create-tss-voter [pubkey] [keygenBlock] [status] [flags] ### SEE ALSO -* [zetacored tx crosschain](zetacored_tx_crosschain.md) - crosschain transactions subcommands +* [zetacored tx observer](zetacored_tx_observer.md) - observer transactions subcommands diff --git a/docs/openapi/openapi.swagger.yaml b/docs/openapi/openapi.swagger.yaml index 1c59768854..2dd9812b64 100644 --- a/docs/openapi/openapi.swagger.yaml +++ b/docs/openapi/openapi.swagger.yaml @@ -53701,8 +53701,6 @@ definitions: is_removed: type: boolean title: if the tx was removed from the tracker due to no pending cctx - crosschainMsgCreateTSSVoterResponse: - type: object crosschainMsgGasPriceVoterResponse: type: object crosschainMsgMigrateTssFundsResponse: @@ -54324,6 +54322,15 @@ definitions: type: object observerMsgUpdateObserverResponse: type: object + observerMsgVoteTSSResponse: + type: object + properties: + ballot_created: + type: boolean + vote_finalized: + type: boolean + keygen_success: + type: boolean observerNode: type: object properties: diff --git a/docs/spec/crosschain/messages.md b/docs/spec/crosschain/messages.md index 506ca0f94c..e64f4bb99a 100644 --- a/docs/spec/crosschain/messages.md +++ b/docs/spec/crosschain/messages.md @@ -236,28 +236,6 @@ message MsgMigrateTssFunds { } ``` -## MsgCreateTSSVoter - -CreateTSSVoter votes on creating a TSS key and recording the information about it (public -key, participant and operator addresses, finalized and keygen heights). - -If the vote passes, the information about the TSS key is recorded on chain -and the status of the keygen is set to "success". - -Fails if the keygen does not exist, the keygen has been already -completed, or the keygen has failed. - -Only node accounts are authorized to broadcast this message. - -```proto -message MsgCreateTSSVoter { - string creator = 1; - string tss_pubkey = 2; - int64 keyGenZetaHeight = 3; - chains.ReceiveStatus status = 4; -} -``` - ## MsgAbortStuckCCTX AbortStuckCCTX aborts a stuck CCTX diff --git a/docs/spec/crosschain/overview.md b/docs/spec/crosschain/overview.md index 32e1035e41..e2c1780922 100644 --- a/docs/spec/crosschain/overview.md +++ b/docs/spec/crosschain/overview.md @@ -69,8 +69,8 @@ status is changed to final. ## Permissions | Message | Admin policy account | Observer validator | -| --------------------------- | -------------------- | ------------------ | -| MsgCreateTSSVoter | | ✅ | +|-----------------------------| -------------------- | ------------------ | +| MsgVoteTSS | | ✅ | | MsgGasPriceVoter | | ✅ | | MsgVoteOnObservedOutboundTx | | ✅ | | MsgVoteOnObservedInboundTx | | ✅ | diff --git a/docs/spec/observer/messages.md b/docs/spec/observer/messages.md index c2a6039dbe..82c6423757 100644 --- a/docs/spec/observer/messages.md +++ b/docs/spec/observer/messages.md @@ -122,3 +122,25 @@ message MsgResetChainNonces { } ``` +## MsgVoteTSS + +VoteTSS votes on creating a TSS key and recording the information about it (public +key, participant and operator addresses, finalized and keygen heights). + +If the vote passes, the information about the TSS key is recorded on chain +and the status of the keygen is set to "success". + +Fails if the keygen does not exist, the keygen has been already +completed, or the keygen has failed. + +Only node accounts are authorized to broadcast this message. + +```proto +message MsgVoteTSS { + string creator = 1; + string tss_pubkey = 2; + int64 keygen_zeta_height = 3; + chains.ReceiveStatus status = 4; +} +``` + diff --git a/pkg/chains/status.go b/pkg/chains/status.go new file mode 100644 index 0000000000..25140267f7 --- /dev/null +++ b/pkg/chains/status.go @@ -0,0 +1,18 @@ +package chains + +import "errors" + +// ReceiveStatusFromString returns a ReceiveStatus from a string using in CLI +// 0 for success, 1 for failed +// TODO: remove "receive" naming ans use outbound +// https://github.com/zeta-chain/node/issues/1797 +func ReceiveStatusFromString(str string) (ReceiveStatus, error) { + switch str { + case "0": + return ReceiveStatus_Success, nil + case "1": + return ReceiveStatus_Failed, nil + default: + return ReceiveStatus(0), errors.New("wrong status, must be 0 for success or 1 for failed") + } +} diff --git a/pkg/chains/status_test.go b/pkg/chains/status_test.go new file mode 100644 index 0000000000..65f5e7e319 --- /dev/null +++ b/pkg/chains/status_test.go @@ -0,0 +1,47 @@ +package chains_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" +) + +func TestReceiveStatusFromString(t *testing.T) { + tests := []struct { + name string + str string + want chains.ReceiveStatus + wantErr bool + }{ + { + name: "success", + str: "0", + want: chains.ReceiveStatus_Success, + wantErr: false, + }, + { + name: "failed", + str: "1", + want: chains.ReceiveStatus_Failed, + wantErr: false, + }, + { + name: "wrong status", + str: "2", + want: chains.ReceiveStatus(0), + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := chains.ReceiveStatusFromString(tt.str) + if tt.wantErr { + require.Error(t, err) + } else if got != tt.want { + require.NoError(t, err) + require.Equal(t, tt.want, got) + } + }) + } +} diff --git a/proto/crosschain/cross_chain_tx.proto b/proto/crosschain/cross_chain_tx.proto index 4ed336ed8f..45ad6a7181 100644 --- a/proto/crosschain/cross_chain_tx.proto +++ b/proto/crosschain/cross_chain_tx.proto @@ -4,6 +4,8 @@ package zetachain.zetacore.crosschain; import "gogoproto/gogo.proto"; import "pkg/coin/coin.proto"; +//TODO : fix the descriptor numbers for the fields +// https://github.com/zeta-chain/node/issues/1951 option go_package = "github.com/zeta-chain/zetacore/x/crosschain/types"; enum CctxStatus { diff --git a/proto/crosschain/tx.proto b/proto/crosschain/tx.proto index d6e4fc093b..7e668d6325 100644 --- a/proto/crosschain/tx.proto +++ b/proto/crosschain/tx.proto @@ -20,21 +20,11 @@ service Msg { rpc WhitelistERC20(MsgWhitelistERC20) returns (MsgWhitelistERC20Response); rpc UpdateTssAddress(MsgUpdateTssAddress) returns (MsgUpdateTssAddressResponse); rpc MigrateTssFunds(MsgMigrateTssFunds) returns (MsgMigrateTssFundsResponse); - rpc CreateTSSVoter(MsgCreateTSSVoter) returns (MsgCreateTSSVoterResponse); rpc AbortStuckCCTX(MsgAbortStuckCCTX) returns (MsgAbortStuckCCTXResponse); rpc RefundAbortedCCTX(MsgRefundAbortedCCTX) returns (MsgRefundAbortedCCTXResponse); } -message MsgCreateTSSVoter { - string creator = 1; - string tss_pubkey = 2; - int64 keyGenZetaHeight = 3; - chains.ReceiveStatus status = 4; -} - -message MsgCreateTSSVoterResponse {} - message MsgMigrateTssFunds { string creator = 1; int64 chain_id = 2; @@ -43,6 +33,7 @@ message MsgMigrateTssFunds { (gogoproto.nullable) = false ]; } + message MsgMigrateTssFundsResponse {} message MsgUpdateTssAddress { diff --git a/proto/observer/tx.proto b/proto/observer/tx.proto index 8245827f16..f6b9424b97 100644 --- a/proto/observer/tx.proto +++ b/proto/observer/tx.proto @@ -8,6 +8,7 @@ import "observer/observer.proto"; import "observer/params.proto"; import "observer/pending_nonces.proto"; import "observer/tss.proto"; +import "pkg/chains/chains.proto"; import "pkg/proofs/proofs.proto"; option go_package = "github.com/zeta-chain/zetacore/x/observer/types"; @@ -23,6 +24,7 @@ service Msg { rpc UpdateKeygen(MsgUpdateKeygen) returns (MsgUpdateKeygenResponse); rpc AddBlockHeader(MsgAddBlockHeader) returns (MsgAddBlockHeaderResponse); rpc ResetChainNonces(MsgResetChainNonces) returns (MsgResetChainNoncesResponse); + rpc VoteTSS(MsgVoteTSS) returns (MsgVoteTSSResponse); } message MsgUpdateObserver { @@ -98,3 +100,16 @@ message MsgResetChainNonces { } message MsgResetChainNoncesResponse {} + +message MsgVoteTSS { + string creator = 1; + string tss_pubkey = 2; + int64 keygen_zeta_height = 3; + chains.ReceiveStatus status = 4; +} + +message MsgVoteTSSResponse { + bool ballot_created = 1; + bool vote_finalized = 2; + bool keygen_success = 3; +} diff --git a/testutil/keeper/authority.go b/testutil/keeper/authority.go index 2c599e66a3..534f8b8dee 100644 --- a/testutil/keeper/authority.go +++ b/testutil/keeper/authority.go @@ -77,3 +77,14 @@ func AuthorityKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) { func MockIsAuthorized(m *mock.Mock, address string, policyType types.PolicyType, isAuthorized bool) { m.On("IsAuthorized", mock.Anything, address, policyType).Return(isAuthorized).Once() } + +func SetAdminPolices(ctx sdk.Context, ak *keeper.Keeper) string { + admin := sample.AccAddress() + ak.SetPolicies(ctx, types.Policies{Items: []*types.Policy{ + { + Address: admin, + PolicyType: types.PolicyType_groupAdmin, + }, + }}) + return admin +} diff --git a/testutil/keeper/crosschain.go b/testutil/keeper/crosschain.go index 715383ee92..04bbcf7ac9 100644 --- a/testutil/keeper/crosschain.go +++ b/testutil/keeper/crosschain.go @@ -1,16 +1,25 @@ package keeper import ( + "math/big" "testing" "github.com/cosmos/cosmos-sdk/store" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" + ethcommon "github.com/ethereum/go-ethereum/common" + evmtypes "github.com/evmos/ethermint/x/evm/types" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" tmdb "github.com/tendermint/tm-db" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" crosschainmocks "github.com/zeta-chain/zetacore/testutil/keeper/mocks/crosschain" + "github.com/zeta-chain/zetacore/testutil/sample" "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" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) type CrosschainMockOptions struct { @@ -183,3 +192,110 @@ func GetCrosschainFungibleMock(t testing.TB, keeper *keeper.Keeper) *crosschainm require.True(t, ok) return cfk } + +func MockGetSupportedChainFromChainID(m *crosschainmocks.CrosschainObserverKeeper, senderChain *chains.Chain) { + m.On("GetSupportedChainFromChainID", mock.Anything, senderChain.ChainId). + Return(senderChain).Once() + +} +func MockGetRevertGasLimitForERC20(m *crosschainmocks.CrosschainFungibleKeeper, asset string, senderChain chains.Chain) { + m.On("GetForeignCoinFromAsset", mock.Anything, asset, senderChain.ChainId). + Return(fungibletypes.ForeignCoins{ + Zrc20ContractAddress: sample.EthAddress().String(), + }, true).Once() + m.On("QueryGasLimit", mock.Anything, mock.Anything). + Return(big.NewInt(100), nil).Once() + +} +func MockPayGasAndUpdateCCTX(m *crosschainmocks.CrosschainFungibleKeeper, m2 *crosschainmocks.CrosschainObserverKeeper, ctx sdk.Context, k keeper.Keeper, senderChain chains.Chain, asset string) { + m2.On("GetSupportedChainFromChainID", mock.Anything, senderChain.ChainId). + Return(&senderChain).Twice() + m.On("GetForeignCoinFromAsset", mock.Anything, asset, senderChain.ChainId). + Return(fungibletypes.ForeignCoins{ + Zrc20ContractAddress: sample.EthAddress().String(), + }, true).Once() + m.On("QuerySystemContractGasCoinZRC20", mock.Anything, mock.Anything). + Return(ethcommon.Address{}, nil).Once() + m.On("QueryGasLimit", mock.Anything, mock.Anything). + Return(big.NewInt(100), nil).Once() + m.On("QueryProtocolFlatFee", mock.Anything, mock.Anything). + Return(big.NewInt(1), nil).Once() + k.SetGasPrice(ctx, types.GasPrice{ + ChainId: senderChain.ChainId, + MedianIndex: 0, + Prices: []uint64{1}, + }) + + m.On("QueryUniswapV2RouterGetZRC4ToZRC4AmountsIn", mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Return(big.NewInt(0), nil).Once() + m.On("DepositZRC20", mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Return(&evmtypes.MsgEthereumTxResponse{}, nil) + m.On("GetUniswapV2Router02Address", mock.Anything). + Return(ethcommon.Address{}, nil).Once() + m.On("CallZRC20Approve", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Return(nil).Once() + m.On("CallUniswapV2RouterSwapExactTokensForTokens", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Return([]*big.Int{big.NewInt(0), big.NewInt(1), big.NewInt(1000)}, nil).Once() + m.On("CallZRC20Burn", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Return(nil).Once() + +} + +func MockUpdateNonce(m *crosschainmocks.CrosschainObserverKeeper, senderChain chains.Chain) (nonce uint64) { + nonce = uint64(1) + tss := sample.Tss() + m.On("GetSupportedChainFromChainID", mock.Anything, senderChain.ChainId). + Return(senderChain) + m.On("GetChainNonces", mock.Anything, senderChain.ChainName.String()). + Return(observertypes.ChainNonces{Nonce: nonce}, true) + m.On("GetTSS", mock.Anything). + Return(tss, true) + m.On("GetPendingNonces", mock.Anything, tss.TssPubkey, mock.Anything). + Return(observertypes.PendingNonces{NonceHigh: int64(nonce)}, true) + m.On("SetChainNonces", mock.Anything, mock.Anything) + m.On("SetPendingNonces", mock.Anything, mock.Anything) + return +} + +func MockRevertForHandleEVMDeposit(m *crosschainmocks.CrosschainFungibleKeeper, receiver ethcommon.Address, amount *big.Int, senderChainID int64, errDeposit error) { + m.On( + "ZRC20DepositAndCallContract", + mock.Anything, + mock.Anything, + receiver, + amount, + senderChainID, + mock.Anything, + coin.CoinType_ERC20, + mock.Anything, + ).Return(&evmtypes.MsgEthereumTxResponse{VmError: "reverted"}, false, errDeposit) +} + +func MockVoteOnOutboundSuccessBallot(m *crosschainmocks.CrosschainObserverKeeper, ctx sdk.Context, cctx *types.CrossChainTx, senderChain chains.Chain, observer string) { + m.On("VoteOnOutboundBallot", ctx, mock.Anything, cctx.GetCurrentOutTxParam().ReceiverChainId, chains.ReceiveStatus_Success, observer). + Return(true, true, observertypes.Ballot{BallotStatus: observertypes.BallotStatus_BallotFinalized_SuccessObservation}, senderChain.ChainName.String(), nil).Once() +} + +func MockVoteOnOutboundFailedBallot(m *crosschainmocks.CrosschainObserverKeeper, ctx sdk.Context, cctx *types.CrossChainTx, senderChain chains.Chain, observer string) { + m.On("VoteOnOutboundBallot", ctx, mock.Anything, cctx.GetCurrentOutTxParam().ReceiverChainId, chains.ReceiveStatus_Failed, observer). + Return(true, true, observertypes.Ballot{BallotStatus: observertypes.BallotStatus_BallotFinalized_FailureObservation}, senderChain.ChainName.String(), nil).Once() +} + +func MockGetOutBound(m *crosschainmocks.CrosschainObserverKeeper, ctx sdk.Context) { + m.On("GetTSS", ctx).Return(observertypes.TSS{}, true).Once() +} + +func MockSaveOutBound(m *crosschainmocks.CrosschainObserverKeeper, ctx sdk.Context, cctx *types.CrossChainTx, tss observertypes.TSS) { + m.On("RemoveFromPendingNonces", + ctx, tss.TssPubkey, cctx.GetCurrentOutTxParam().ReceiverChainId, mock.Anything). + Return().Once() + m.On("GetTSS", ctx).Return(observertypes.TSS{}, true) +} + +func MockSaveOutBoundNewRevertCreated(m *crosschainmocks.CrosschainObserverKeeper, ctx sdk.Context, cctx *types.CrossChainTx, tss observertypes.TSS) { + m.On("RemoveFromPendingNonces", + ctx, tss.TssPubkey, cctx.GetCurrentOutTxParam().ReceiverChainId, mock.Anything). + Return().Once() + m.On("GetTSS", ctx).Return(observertypes.TSS{}, true) + m.On("SetNonceToCctx", mock.Anything, mock.Anything).Return().Once() +} diff --git a/testutil/keeper/emissions.go b/testutil/keeper/emissions.go index e61f6cd76e..57c5b64527 100644 --- a/testutil/keeper/emissions.go +++ b/testutil/keeper/emissions.go @@ -128,3 +128,9 @@ func GetEmissionsParamStoreMock(t testing.TB, keeper *keeper.Keeper) *emissionsm require.True(t, ok) return m } + +func GetEmissionsStakingMock(t testing.TB, keeper *keeper.Keeper) *emissionsmocks.EmissionStakingKeeper { + cbk, ok := keeper.GetStakingKeeper().(*emissionsmocks.EmissionStakingKeeper) + require.True(t, ok) + return cbk +} diff --git a/testutil/sample/crosschain.go b/testutil/sample/crosschain.go index 3fa5d04983..0fe91e0c2e 100644 --- a/testutil/sample/crosschain.go +++ b/testutil/sample/crosschain.go @@ -9,6 +9,7 @@ import ( ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/x/crosschain/types" ) @@ -52,6 +53,20 @@ func InboundTxParams(r *rand.Rand) *types.InboundTxParams { } } +func InboundTxParamsValidChainID(r *rand.Rand) *types.InboundTxParams { + return &types.InboundTxParams{ + Sender: EthAddress().String(), + SenderChainId: chains.GoerliChain().ChainId, + TxOrigin: EthAddress().String(), + Asset: StringRandom(r, 32), + Amount: math.NewUint(uint64(r.Int63())), + InboundTxObservedHash: StringRandom(r, 32), + InboundTxObservedExternalHeight: r.Uint64(), + InboundTxBallotIndex: StringRandom(r, 32), + InboundTxFinalizedZetaHeight: r.Uint64(), + } +} + func OutboundTxParams(r *rand.Rand) *types.OutboundTxParams { return &types.OutboundTxParams{ Receiver: EthAddress().String(), @@ -69,6 +84,22 @@ func OutboundTxParams(r *rand.Rand) *types.OutboundTxParams { } } +func OutboundTxParamsValidChainID(r *rand.Rand) *types.OutboundTxParams { + return &types.OutboundTxParams{ + Receiver: EthAddress().String(), + ReceiverChainId: chains.GoerliChain().ChainId, + Amount: math.NewUint(uint64(r.Int63())), + OutboundTxTssNonce: r.Uint64(), + OutboundTxGasLimit: r.Uint64(), + OutboundTxGasPrice: math.NewUint(uint64(r.Int63())).String(), + OutboundTxHash: StringRandom(r, 32), + OutboundTxBallotIndex: StringRandom(r, 32), + OutboundTxObservedExternalHeight: r.Uint64(), + OutboundTxGasUsed: r.Uint64(), + OutboundTxEffectiveGasPrice: math.NewInt(r.Int63()), + } +} + func Status(t *testing.T, index string) *types.Status { r := newRandFromStringSeed(t, index) diff --git a/testutil/sample/observer.go b/testutil/sample/observer.go index c90036ff07..7d9d54c646 100644 --- a/testutil/sample/observer.go +++ b/testutil/sample/observer.go @@ -41,10 +41,8 @@ func ObserverSet(n int) types.ObserverSet { } func NodeAccount() *types.NodeAccount { - operator := AccAddress() - return &types.NodeAccount{ - Operator: operator, + Operator: AccAddress(), GranteeAddress: AccAddress(), GranteePubkey: PubKeySet(), NodeStatus: types.NodeStatus_Active, diff --git a/testutil/sample/sample.go b/testutil/sample/sample.go index 0ac8621411..71b9eac564 100644 --- a/testutil/sample/sample.go +++ b/testutil/sample/sample.go @@ -8,6 +8,7 @@ import ( "testing" sdkmath "cosmossdk.io/math" + ethcrypto "github.com/ethereum/go-ethereum/crypto" "github.com/zeta-chain/zetacore/cmd/zetacored/config" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" @@ -111,6 +112,12 @@ func Hash() ethcommon.Hash { return EthAddress().Hash() } +func ZetaIndex(t *testing.T) string { + msg := CrossChainTx(t, "foo") + hash := ethcrypto.Keccak256Hash([]byte(msg.String())) + return hash.Hex() +} + // Bytes returns a sample byte array func Bytes() []byte { return []byte("sample") diff --git a/typescript/crosschain/tx_pb.d.ts b/typescript/crosschain/tx_pb.d.ts index b1900d7c66..eec35c2dc8 100644 --- a/typescript/crosschain/tx_pb.d.ts +++ b/typescript/crosschain/tx_pb.d.ts @@ -5,67 +5,9 @@ import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; import { Message, proto3 } from "@bufbuild/protobuf"; -import type { ReceiveStatus } from "../pkg/chains/chains_pb.js"; import type { CoinType } from "../pkg/coin/coin_pb.js"; import type { Proof } from "../pkg/proofs/proofs_pb.js"; - -/** - * @generated from message zetachain.zetacore.crosschain.MsgCreateTSSVoter - */ -export declare class MsgCreateTSSVoter extends Message { - /** - * @generated from field: string creator = 1; - */ - creator: string; - - /** - * @generated from field: string tss_pubkey = 2; - */ - tssPubkey: string; - - /** - * @generated from field: int64 keyGenZetaHeight = 3; - */ - keyGenZetaHeight: bigint; - - /** - * @generated from field: chains.ReceiveStatus status = 4; - */ - status: ReceiveStatus; - - constructor(data?: PartialMessage); - - static readonly runtime: typeof proto3; - static readonly typeName = "zetachain.zetacore.crosschain.MsgCreateTSSVoter"; - static readonly fields: FieldList; - - static fromBinary(bytes: Uint8Array, options?: Partial): MsgCreateTSSVoter; - - static fromJson(jsonValue: JsonValue, options?: Partial): MsgCreateTSSVoter; - - static fromJsonString(jsonString: string, options?: Partial): MsgCreateTSSVoter; - - static equals(a: MsgCreateTSSVoter | PlainMessage | undefined, b: MsgCreateTSSVoter | PlainMessage | undefined): boolean; -} - -/** - * @generated from message zetachain.zetacore.crosschain.MsgCreateTSSVoterResponse - */ -export declare class MsgCreateTSSVoterResponse extends Message { - constructor(data?: PartialMessage); - - static readonly runtime: typeof proto3; - static readonly typeName = "zetachain.zetacore.crosschain.MsgCreateTSSVoterResponse"; - static readonly fields: FieldList; - - static fromBinary(bytes: Uint8Array, options?: Partial): MsgCreateTSSVoterResponse; - - static fromJson(jsonValue: JsonValue, options?: Partial): MsgCreateTSSVoterResponse; - - static fromJsonString(jsonString: string, options?: Partial): MsgCreateTSSVoterResponse; - - static equals(a: MsgCreateTSSVoterResponse | PlainMessage | undefined, b: MsgCreateTSSVoterResponse | PlainMessage | undefined): boolean; -} +import type { ReceiveStatus } from "../pkg/chains/chains_pb.js"; /** * @generated from message zetachain.zetacore.crosschain.MsgMigrateTssFunds diff --git a/typescript/observer/tx_pb.d.ts b/typescript/observer/tx_pb.d.ts index 4418e5569e..21fe737462 100644 --- a/typescript/observer/tx_pb.d.ts +++ b/typescript/observer/tx_pb.d.ts @@ -10,6 +10,7 @@ import type { HeaderData } from "../pkg/proofs/proofs_pb.js"; import type { ChainParams } from "./params_pb.js"; import type { Blame } from "./blame_pb.js"; import type { BlockHeaderVerificationFlags, GasPriceIncreaseFlags } from "./crosschain_flags_pb.js"; +import type { ReceiveStatus } from "../pkg/chains/chains_pb.js"; /** * @generated from message zetachain.zetacore.observer.MsgUpdateObserver @@ -508,3 +509,76 @@ export declare class MsgResetChainNoncesResponse extends Message | undefined, b: MsgResetChainNoncesResponse | PlainMessage | undefined): boolean; } +/** + * @generated from message zetachain.zetacore.observer.MsgVoteTSS + */ +export declare class MsgVoteTSS extends Message { + /** + * @generated from field: string creator = 1; + */ + creator: string; + + /** + * @generated from field: string tss_pubkey = 2; + */ + tssPubkey: string; + + /** + * @generated from field: int64 keygen_zeta_height = 3; + */ + keygenZetaHeight: bigint; + + /** + * @generated from field: chains.ReceiveStatus status = 4; + */ + status: ReceiveStatus; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.observer.MsgVoteTSS"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): MsgVoteTSS; + + static fromJson(jsonValue: JsonValue, options?: Partial): MsgVoteTSS; + + static fromJsonString(jsonString: string, options?: Partial): MsgVoteTSS; + + static equals(a: MsgVoteTSS | PlainMessage | undefined, b: MsgVoteTSS | PlainMessage | undefined): boolean; +} + +/** + * @generated from message zetachain.zetacore.observer.MsgVoteTSSResponse + */ +export declare class MsgVoteTSSResponse extends Message { + /** + * @generated from field: bool ballot_created = 1; + */ + ballotCreated: boolean; + + /** + * @generated from field: bool vote_finalized = 2; + */ + voteFinalized: boolean; + + /** + * @generated from field: bool keygen_success = 3; + */ + keygenSuccess: boolean; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.observer.MsgVoteTSSResponse"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): MsgVoteTSSResponse; + + static fromJson(jsonValue: JsonValue, options?: Partial): MsgVoteTSSResponse; + + static fromJsonString(jsonString: string, options?: Partial): MsgVoteTSSResponse; + + static equals(a: MsgVoteTSSResponse | PlainMessage | undefined, b: MsgVoteTSSResponse | PlainMessage | undefined): boolean; +} + diff --git a/x/authority/types/genesis_test.go b/x/authority/types/genesis_test.go index ec53fc1fad..7b835c3ea4 100644 --- a/x/authority/types/genesis_test.go +++ b/x/authority/types/genesis_test.go @@ -9,7 +9,7 @@ import ( ) func TestGenesisState_Validate(t *testing.T) { - setConfig() + setConfig(t) tests := []struct { name string diff --git a/x/authority/types/policies_test.go b/x/authority/types/policies_test.go index 93981da11c..8db173eb86 100644 --- a/x/authority/types/policies_test.go +++ b/x/authority/types/policies_test.go @@ -11,14 +11,19 @@ import ( ) // setConfig sets the global config to use zeta chain's bech32 prefixes -func setConfig() { +func setConfig(t *testing.T) { + defer func(t *testing.T) { + if r := recover(); r != nil { + t.Log("config is already sealed", r) + } + }(t) cfg := sdk.GetConfig() cfg.SetBech32PrefixForAccount(app.Bech32PrefixAccAddr, app.Bech32PrefixAccPub) + cfg.Seal() } func TestPolicies_Validate(t *testing.T) { - setConfig() - + setConfig(t) // use table driven tests to test the validation of policies tests := []struct { name string diff --git a/x/crosschain/client/cli/cli_cctx.go b/x/crosschain/client/cli/cli_cctx.go index 92fed899d3..87991442d1 100644 --- a/x/crosschain/client/cli/cli_cctx.go +++ b/x/crosschain/client/cli/cli_cctx.go @@ -225,13 +225,9 @@ func CmdCCTXOutboundVoter() *cobra.Command { argsMMint := args[6] - var status chains.ReceiveStatus - if args[7] == "0" { - status = chains.ReceiveStatus_Success - } else if args[7] == "1" { - status = chains.ReceiveStatus_Failed - } else { - return fmt.Errorf("wrong status") + status, err := chains.ReceiveStatusFromString(args[7]) + if err != nil { + return err } chain, err := strconv.ParseInt(args[8], 10, 64) diff --git a/x/crosschain/client/cli/cli_tss.go b/x/crosschain/client/cli/cli_tss.go index 7c85ea8b72..9b9d551ca8 100644 --- a/x/crosschain/client/cli/cli_tss.go +++ b/x/crosschain/client/cli/cli_tss.go @@ -1,7 +1,6 @@ package cli import ( - "fmt" "strconv" "cosmossdk.io/math" @@ -10,49 +9,9 @@ import ( "github.com/cosmos/cosmos-sdk/client/tx" "github.com/spf13/cast" "github.com/spf13/cobra" - "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/x/crosschain/types" ) -func CmdCreateTSSVoter() *cobra.Command { - cmd := &cobra.Command{ - Use: "create-tss-voter [pubkey] [keygenBlock] [status]", - Short: "Create a new TSSVoter", - Args: cobra.ExactArgs(3), - RunE: func(cmd *cobra.Command, args []string) error { - - argsPubkey, err := cast.ToStringE(args[0]) - if err != nil { - return err - } - keygenBlock, err := strconv.ParseInt(args[1], 10, 64) - if err != nil { - return err - } - var status chains.ReceiveStatus - if args[2] == "0" { - status = chains.ReceiveStatus_Success - } else if args[2] == "1" { - status = chains.ReceiveStatus_Failed - } else { - return fmt.Errorf("wrong status") - } - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - - msg := types.NewMsgCreateTSSVoter(clientCtx.GetFromAddress().String(), argsPubkey, keygenBlock, status) - - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) - }, - } - - flags.AddTxFlagsToCmd(cmd) - - return cmd -} - func CmdUpdateTss() *cobra.Command { cmd := &cobra.Command{ Use: "update-tss-address [pubkey]", diff --git a/x/crosschain/client/cli/tx.go b/x/crosschain/client/cli/tx.go index bb9831fb31..c82c55f8fd 100644 --- a/x/crosschain/client/cli/tx.go +++ b/x/crosschain/client/cli/tx.go @@ -22,7 +22,6 @@ func GetTxCmd() *cobra.Command { cmd.AddCommand( CmdAddToWatchList(), - CmdCreateTSSVoter(), CmdGasPriceVoter(), CmdCCTXOutboundVoter(), CmdCCTXInboundVoter(), diff --git a/x/crosschain/client/integrationtests/cli_helpers.go b/x/crosschain/client/integrationtests/cli_helpers.go deleted file mode 100644 index 9cc05b32d6..0000000000 --- a/x/crosschain/client/integrationtests/cli_helpers.go +++ /dev/null @@ -1,323 +0,0 @@ -package integrationtests - -import ( - "fmt" - "os" - "strconv" - "testing" - - "cosmossdk.io/math" - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - "github.com/cosmos/cosmos-sdk/testutil" - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - sdk "github.com/cosmos/cosmos-sdk/types" - authcli "github.com/cosmos/cosmos-sdk/x/auth/client/cli" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/stretchr/testify/require" - tmcli "github.com/tendermint/tendermint/libs/cli" - "github.com/zeta-chain/zetacore/pkg/chains" - "github.com/zeta-chain/zetacore/pkg/coin" - "github.com/zeta-chain/zetacore/testutil/network" - "github.com/zeta-chain/zetacore/x/crosschain/client/cli" - "github.com/zeta-chain/zetacore/x/crosschain/types" - fungiblecli "github.com/zeta-chain/zetacore/x/fungible/client/cli" -) - -func TxSignExec(clientCtx client.Context, from fmt.Stringer, filename string, extraArgs ...string) (testutil.BufferWriter, error) { - args := []string{ - fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), - fmt.Sprintf("--from=%s", from.String()), - fmt.Sprintf("--%s=%s", flags.FlagChainID, clientCtx.ChainID), - filename, - } - - cmd := authcli.GetSignCommand() - tmcli.PrepareBaseCmd(cmd, "", "") - - return clitestutil.ExecTestCLICmd(clientCtx, cmd, append(args, extraArgs...)) -} - -func WriteToNewTempFile(t testing.TB, s string) *os.File { - t.Helper() - - fp := TempFile(t) - _, err := fp.WriteString(s) - - require.Nil(t, err) - - return fp -} - -// TempFile returns a writable temporary file for the test to use. -func TempFile(t testing.TB) *os.File { - t.Helper() - - fp, err := os.CreateTemp(GetTempDir(t), "") - require.NoError(t, err) - - return fp -} - -// GetTempDir returns a writable temporary director for the test to use. -func GetTempDir(t testing.TB) string { - t.Helper() - // os.MkDir() is used instead of testing.T.TempDir() - // see https://github.com/cosmos/cosmos-sdk/pull/8475 and - // https://github.com/cosmos/cosmos-sdk/pull/10341 for - // this change's rationale. - tempdir, err := os.MkdirTemp("", "") - require.NoError(t, err) - t.Cleanup(func() { - err := os.RemoveAll(tempdir) - require.NoError(t, err) - }) - return tempdir -} - -func BuildSignedDeploySystemContract(t testing.TB, val *network.Validator, denom string, account authtypes.AccountI) *os.File { - cmd := fungiblecli.CmdDeploySystemContracts() - txArgs := []string{ - fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(denom, sdk.NewInt(100))).String()), - // gas limit - fmt.Sprintf("--%s=%d", flags.FlagGas, 4000000), - } - out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, txArgs) - require.NoError(t, err) - unsignerdTx := WriteToNewTempFile(t, out.String()) - res, err := TxSignExec(val.ClientCtx, val.Address, unsignerdTx.Name(), - "--offline", "--account-number", strconv.FormatUint(account.GetAccountNumber(), 10), "--sequence", strconv.FormatUint(account.GetSequence(), 10)) - require.NoError(t, err) - return WriteToNewTempFile(t, res.String()) -} - -func BuildSignedUpdateSystemContract( - t testing.TB, - val *network.Validator, - denom string, - account authtypes.AccountI, - systemContractAddress string, -) *os.File { - cmd := fungiblecli.CmdUpdateSystemContract() - txArgs := []string{ - fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(denom, sdk.NewInt(100))).String()), - // gas limit - fmt.Sprintf("--%s=%d", flags.FlagGas, 4000000), - } - args := append([]string{systemContractAddress}, txArgs...) - out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, args) - require.NoError(t, err) - unsignerdTx := WriteToNewTempFile(t, out.String()) - res, err := TxSignExec(val.ClientCtx, val.Address, unsignerdTx.Name(), - "--offline", "--account-number", strconv.FormatUint(account.GetAccountNumber(), 10), "--sequence", strconv.FormatUint(account.GetSequence(), 10)) - require.NoError(t, err) - return WriteToNewTempFile(t, res.String()) -} - -func BuildSignedDeployETHZRC20( - t testing.TB, - val *network.Validator, - denom string, - account authtypes.AccountI, -) *os.File { - cmd := fungiblecli.CmdDeployFungibleCoinZRC4() - txArgs := []string{ - fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(denom, sdk.NewInt(100))).String()), - // gas limit - fmt.Sprintf("--%s=%d", flags.FlagGas, 10000000), - } - args := append([]string{ - "", - strconv.FormatInt(chains.GoerliLocalnetChain().ChainId, 10), - "18", - "ETH", - "gETH", - strconv.FormatInt(int64(coin.CoinType_Gas), 10), - "1000000", - }, txArgs...) - out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, args) - require.NoError(t, err) - unsignerdTx := WriteToNewTempFile(t, out.String()) - res, err := TxSignExec(val.ClientCtx, val.Address, unsignerdTx.Name(), - "--offline", "--account-number", strconv.FormatUint(account.GetAccountNumber(), 10), "--sequence", strconv.FormatUint(account.GetSequence(), 10)) - require.NoError(t, err) - return WriteToNewTempFile(t, res.String()) -} - -func BuildSignedGasPriceVote(t testing.TB, val *network.Validator, denom string, account authtypes.AccountI) *os.File { - cmd := cli.CmdGasPriceVoter() - inboundVoterArgs := []string{ - strconv.FormatInt(chains.GoerliLocalnetChain().ChainId, 10), - "10000000000", - "100", - "100", - } - txArgs := []string{ - fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), - fmt.Sprintf("--%s=%s", flags.FlagGas, "400000"), - fmt.Sprintf("--%s=%s", flags.FlagGasAdjustment, "1.5"), - fmt.Sprintf("--%s=%s", flags.FlagGasPrices, fmt.Sprintf("%s%s", "10", denom)), - } - args := append(inboundVoterArgs, txArgs...) - out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, args) - require.NoError(t, err) - unsignerdTx := WriteToNewTempFile(t, out.String()) - res, err := TxSignExec(val.ClientCtx, val.Address, unsignerdTx.Name(), - "--offline", "--account-number", strconv.FormatUint(account.GetAccountNumber(), 10), "--sequence", strconv.FormatUint(account.GetSequence(), 10)) - require.NoError(t, err) - return WriteToNewTempFile(t, res.String()) -} - -func BuildSignedTssVote(t testing.TB, val *network.Validator, denom string, account authtypes.AccountI) *os.File { - cmd := cli.CmdCreateTSSVoter() - inboundVoterArgs := []string{ - "tsspubkey", - "1", - "0", - } - txArgs := []string{ - fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), - fmt.Sprintf("--%s=%s", flags.FlagGas, "400000"), - fmt.Sprintf("--%s=%s", flags.FlagGasAdjustment, "1.5"), - fmt.Sprintf("--%s=%s", flags.FlagGasPrices, fmt.Sprintf("%s%s", "10", denom)), - } - args := append(inboundVoterArgs, txArgs...) - out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, args) - require.NoError(t, err) - unsignerdTx := WriteToNewTempFile(t, out.String()) - res, err := TxSignExec(val.ClientCtx, val.Address, unsignerdTx.Name(), - "--offline", "--account-number", strconv.FormatUint(account.GetAccountNumber(), 10), "--sequence", strconv.FormatUint(account.GetSequence(), 10)) - require.NoError(t, err) - return WriteToNewTempFile(t, res.String()) -} - -func BuildSignedOutboundVote( - t testing.TB, - val *network.Validator, - denom string, - account authtypes.AccountI, - nonce uint64, - cctxIndex, - outTxHash, - valueReceived, - status string, -) *os.File { - cmd := cli.CmdCCTXOutboundVoter() - outboundVoterArgs := []string{ - cctxIndex, - outTxHash, - "1", - "0", - "0", - "0", - valueReceived, - status, - strconv.FormatInt(chains.GoerliLocalnetChain().ChainId, 10), - strconv.FormatUint(nonce, 10), - "Zeta", - } - txArgs := []string{ - fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), - fmt.Sprintf("--%s=%s", flags.FlagGas, "400000"), - fmt.Sprintf("--%s=%s", flags.FlagGasAdjustment, "1.5"), - fmt.Sprintf("--%s=%s", flags.FlagGasPrices, fmt.Sprintf("%s%s", "10", denom)), - } - args := append(outboundVoterArgs, txArgs...) - out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, args) - require.NoError(t, err) - - unsignerdTx := WriteToNewTempFile(t, out.String()) - res, err := TxSignExec(val.ClientCtx, val.Address, unsignerdTx.Name(), - "--offline", "--account-number", strconv.FormatUint(account.GetAccountNumber(), 10), "--sequence", strconv.FormatUint(account.GetSequence(), 10)) - require.NoError(t, err) - return WriteToNewTempFile(t, res.String()) -} - -func BuildSignedInboundVote(t testing.TB, val *network.Validator, denom string, account authtypes.AccountI, message string, eventIndex int) *os.File { - cmd := cli.CmdCCTXInboundVoter() - inboundVoterArgs := []string{ - "0x96B05C238b99768F349135de0653b687f9c13fEE", - strconv.FormatInt(chains.GoerliLocalnetChain().ChainId, 10), - "0x3b9Fe88DE29efD13240829A0c18E9EC7A44C3CA7", - "0x96B05C238b99768F349135de0653b687f9c13fEE", - strconv.FormatInt(chains.GoerliLocalnetChain().ChainId, 10), - "10000000000000000000", - message, - "0x19398991572a825894b34b904ac1e3692720895351466b5c9e6bb7ae1e21d680", - "100", - "Zeta", - "", - strconv.Itoa(eventIndex), - } - txArgs := []string{ - fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), - fmt.Sprintf("--%s=%s", flags.FlagGas, "4000000"), - fmt.Sprintf("--%s=%s", flags.FlagGasAdjustment, "1.5"), - fmt.Sprintf("--%s=%s", flags.FlagGasPrices, fmt.Sprintf("%s%s", "10", denom)), - } - args := append(inboundVoterArgs, txArgs...) - out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, args) - require.NoError(t, err) - unsignerdTx := WriteToNewTempFile(t, out.String()) - res, err := TxSignExec(val.ClientCtx, val.Address, unsignerdTx.Name(), - "--offline", "--account-number", strconv.FormatUint(account.GetAccountNumber(), 10), "--sequence", strconv.FormatUint(account.GetSequence(), 10)) - require.NoError(t, err) - return WriteToNewTempFile(t, res.String()) -} - -func GetBallotIdentifier(message string, eventIndex int) string { - msg := types.NewMsgVoteOnObservedInboundTx( - "", - "0x96B05C238b99768F349135de0653b687f9c13fEE", - chains.GoerliLocalnetChain().ChainId, - "0x3b9Fe88DE29efD13240829A0c18E9EC7A44C3CA7", - "0x96B05C238b99768F349135de0653b687f9c13fEE", - chains.GoerliLocalnetChain().ChainId, - sdk.NewUint(10000000000000000000), - message, - "0x19398991572a825894b34b904ac1e3692720895351466b5c9e6bb7ae1e21d680", - 100, - 250_000, - coin.CoinType_Zeta, - "", - // #nosec G701 always positive - uint(eventIndex), - ) - return msg.Digest() -} - -func GetBallotIdentifierOutBound(nonce uint64, cctxindex, outtxHash, valueReceived string) string { - msg := types.NewMsgVoteOnObservedOutboundTx( - "", - cctxindex, - outtxHash, - 1, - 0, - math.ZeroInt(), - 0, - math.NewUintFromString(valueReceived), - 0, - chains.GoerliLocalnetChain().ChainId, - nonce, - coin.CoinType_Zeta, - ) - return msg.Digest() -} diff --git a/x/crosschain/client/integrationtests/cli_test.go b/x/crosschain/client/integrationtests/cli_test.go deleted file mode 100644 index de594398ce..0000000000 --- a/x/crosschain/client/integrationtests/cli_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package integrationtests - -import ( - "testing" - - "github.com/stretchr/testify/suite" - "github.com/zeta-chain/zetacore/testutil/network" -) - -func TestIntegrationTestSuite(t *testing.T) { - cfg := network.DefaultConfig() - suite.Run(t, NewIntegrationTestSuite(cfg)) -} diff --git a/x/crosschain/client/integrationtests/inbound_voter_test.go b/x/crosschain/client/integrationtests/inbound_voter_test.go deleted file mode 100644 index 50cf041a64..0000000000 --- a/x/crosschain/client/integrationtests/inbound_voter_test.go +++ /dev/null @@ -1,304 +0,0 @@ -package integrationtests - -import ( - "encoding/json" - "fmt" - "strings" - - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - authcli "github.com/cosmos/cosmos-sdk/x/auth/client/cli" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - crosschaincli "github.com/zeta-chain/zetacore/x/crosschain/client/cli" - crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" - observercli "github.com/zeta-chain/zetacore/x/observer/client/cli" - observerTypes "github.com/zeta-chain/zetacore/x/observer/types" -) - -type messageLog struct { - Events []event `json:"events"` -} - -type event struct { - Type string `json:"type"` - Attributes []attribute `json:"attributes"` -} - -type attribute struct { - Key string `json:"key"` - Value string `json:"value"` -} - -// fetchAttribute fetches the attribute from the tx response -func fetchAttribute(rawLog string, key string) (string, error) { - var logs []messageLog - err := json.Unmarshal([]byte(rawLog), &logs) - if err != nil { - return "", err - } - - var attributes []string - for _, log := range logs { - for _, event := range log.Events { - for _, attr := range event.Attributes { - attributes = append(attributes, attr.Key) - if strings.EqualFold(attr.Key, key) { - address := attr.Value - - // trim the quotes - address = address[1 : len(address)-1] - - return address, nil - } - - } - } - } - - return "", fmt.Errorf("attribute %s not found, attributes: %+v", key, attributes) -} - -type txRes struct { - RawLog string `json:"raw_log"` -} - -func ExtractRawLog(str string) (string, error) { - var data txRes - - err := json.Unmarshal([]byte(str), &data) - if err != nil { - return "", err - } - - return data.RawLog, nil -} - -func (s *IntegrationTestSuite) TestCCTXInboundVoter() { - broadcaster := s.network.Validators[0] - - var systemContractAddr string - // Initialize system contract - { - out, err := clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, authcli.GetAccountCmd(), []string{broadcaster.Address.String(), "--output", "json"}) - s.Require().NoError(err) - var account authtypes.AccountI - s.NoError(broadcaster.ClientCtx.Codec.UnmarshalInterfaceJSON(out.Bytes(), &account)) - signedTx := BuildSignedDeploySystemContract(s.T(), broadcaster, s.cfg.BondDenom, account) - res, err := clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, authcli.GetBroadcastCommand(), []string{signedTx.Name(), "--broadcast-mode", "block"}) - s.Require().NoError(err) - - rawLog, err := ExtractRawLog(res.String()) - s.Require().NoError(err) - - systemContractAddr, err = fetchAttribute(rawLog, "system_contract") - s.Require().NoError(err) - - // update system contract - out, err = clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, authcli.GetAccountCmd(), []string{broadcaster.Address.String(), "--output", "json"}) - s.Require().NoError(err) - s.NoError(broadcaster.ClientCtx.Codec.UnmarshalInterfaceJSON(out.Bytes(), &account)) - signedTx = BuildSignedUpdateSystemContract(s.T(), broadcaster, s.cfg.BondDenom, account, systemContractAddr) - res, err = clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, authcli.GetBroadcastCommand(), []string{signedTx.Name(), "--broadcast-mode", "block"}) - s.Require().NoError(err) - } - - // Deploy ETH ZRC20 - { - out, err := clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, authcli.GetAccountCmd(), []string{broadcaster.Address.String(), "--output", "json"}) - s.Require().NoError(err) - var account authtypes.AccountI - s.NoError(broadcaster.ClientCtx.Codec.UnmarshalInterfaceJSON(out.Bytes(), &account)) - signedTx := BuildSignedDeployETHZRC20(s.T(), broadcaster, s.cfg.BondDenom, account) - _, err = clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, authcli.GetBroadcastCommand(), []string{signedTx.Name(), "--broadcast-mode", "block"}) - s.Require().NoError(err) - } - - tt := []struct { - name string - votes map[string]observerTypes.VoteType - ballotResult observerTypes.BallotStatus - cctxStatus crosschaintypes.CctxStatus - falseBallotIdentifier string - }{ - { - name: "All observers voted success", - votes: map[string]observerTypes.VoteType{ - "zeta13c7p3xrhd6q2rx3h235jpt8pjdwvacyw6twpax": observerTypes.VoteType_SuccessObservation, - "zeta1f203dypqg5jh9hqfx0gfkmmnkdfuat3jr45ep2": observerTypes.VoteType_SuccessObservation, - "zeta1szrskhdeleyt6wmn0nfxvcvt2l6f4fn06uaga4": observerTypes.VoteType_SuccessObservation, - "zeta16h3y7s7030l4chcznwq3n6uz2m9wvmzu5vwt7c": observerTypes.VoteType_SuccessObservation, - "zeta1xl2rfsrmx8nxryty3lsjuxwdxs59cn2q65e5ca": observerTypes.VoteType_SuccessObservation, - "zeta1ktmprjdvc72jq0mpu8tn8sqx9xwj685qx0q6kt": observerTypes.VoteType_SuccessObservation, - "zeta1ygeyr8pqfjvclxay5234gulnjzv2mkz6lph9y4": observerTypes.VoteType_SuccessObservation, - "zeta1zegyenj7xg5nck04ykkzndm2qxdzc6v83mklsy": observerTypes.VoteType_SuccessObservation, - "zeta1us2qpqdcctk6q7qv2c9d9jvjxlv88jscf68kav": observerTypes.VoteType_SuccessObservation, - "zeta1e9fyaulgntkrnqnl0es4nyxghp3petpn2ntu3t": observerTypes.VoteType_SuccessObservation, - }, - ballotResult: observerTypes.BallotStatus_BallotFinalized_SuccessObservation, - cctxStatus: crosschaintypes.CctxStatus_PendingOutbound, - }, - { - name: "5 votes only ballot does not get finalized", - votes: map[string]observerTypes.VoteType{ - "zeta13c7p3xrhd6q2rx3h235jpt8pjdwvacyw6twpax": observerTypes.VoteType_SuccessObservation, - "zeta1f203dypqg5jh9hqfx0gfkmmnkdfuat3jr45ep2": observerTypes.VoteType_SuccessObservation, - "zeta1szrskhdeleyt6wmn0nfxvcvt2l6f4fn06uaga4": observerTypes.VoteType_SuccessObservation, - "zeta16h3y7s7030l4chcznwq3n6uz2m9wvmzu5vwt7c": observerTypes.VoteType_SuccessObservation, - "zeta1xl2rfsrmx8nxryty3lsjuxwdxs59cn2q65e5ca": observerTypes.VoteType_SuccessObservation, - "zeta1ktmprjdvc72jq0mpu8tn8sqx9xwj685qx0q6kt": observerTypes.VoteType_NotYetVoted, - "zeta1ygeyr8pqfjvclxay5234gulnjzv2mkz6lph9y4": observerTypes.VoteType_NotYetVoted, - "zeta1zegyenj7xg5nck04ykkzndm2qxdzc6v83mklsy": observerTypes.VoteType_NotYetVoted, - "zeta1us2qpqdcctk6q7qv2c9d9jvjxlv88jscf68kav": observerTypes.VoteType_NotYetVoted, - "zeta1e9fyaulgntkrnqnl0es4nyxghp3petpn2ntu3t": observerTypes.VoteType_NotYetVoted, - }, - ballotResult: observerTypes.BallotStatus_BallotInProgress, - cctxStatus: crosschaintypes.CctxStatus_PendingRevert, - }, - { - name: "1 false vote but correct ballot is still finalized", - votes: map[string]observerTypes.VoteType{ - "zeta13c7p3xrhd6q2rx3h235jpt8pjdwvacyw6twpax": observerTypes.VoteType_SuccessObservation, - "zeta1f203dypqg5jh9hqfx0gfkmmnkdfuat3jr45ep2": observerTypes.VoteType_SuccessObservation, - "zeta1szrskhdeleyt6wmn0nfxvcvt2l6f4fn06uaga4": observerTypes.VoteType_SuccessObservation, - "zeta16h3y7s7030l4chcznwq3n6uz2m9wvmzu5vwt7c": observerTypes.VoteType_SuccessObservation, - "zeta1xl2rfsrmx8nxryty3lsjuxwdxs59cn2q65e5ca": observerTypes.VoteType_SuccessObservation, - "zeta1ktmprjdvc72jq0mpu8tn8sqx9xwj685qx0q6kt": observerTypes.VoteType_SuccessObservation, - "zeta1ygeyr8pqfjvclxay5234gulnjzv2mkz6lph9y4": observerTypes.VoteType_SuccessObservation, - "zeta1zegyenj7xg5nck04ykkzndm2qxdzc6v83mklsy": observerTypes.VoteType_FailureObservation, - "zeta1us2qpqdcctk6q7qv2c9d9jvjxlv88jscf68kav": observerTypes.VoteType_SuccessObservation, - "zeta1e9fyaulgntkrnqnl0es4nyxghp3petpn2ntu3t": observerTypes.VoteType_NotYetVoted, - }, - ballotResult: observerTypes.BallotStatus_BallotFinalized_SuccessObservation, - cctxStatus: crosschaintypes.CctxStatus_PendingOutbound, - }, - { - name: "2 ballots with 5 votes each no ballot gets finalized", - votes: map[string]observerTypes.VoteType{ - "zeta13c7p3xrhd6q2rx3h235jpt8pjdwvacyw6twpax": observerTypes.VoteType_SuccessObservation, - "zeta1f203dypqg5jh9hqfx0gfkmmnkdfuat3jr45ep2": observerTypes.VoteType_SuccessObservation, - "zeta1szrskhdeleyt6wmn0nfxvcvt2l6f4fn06uaga4": observerTypes.VoteType_SuccessObservation, - "zeta16h3y7s7030l4chcznwq3n6uz2m9wvmzu5vwt7c": observerTypes.VoteType_SuccessObservation, - "zeta1xl2rfsrmx8nxryty3lsjuxwdxs59cn2q65e5ca": observerTypes.VoteType_SuccessObservation, - "zeta1ktmprjdvc72jq0mpu8tn8sqx9xwj685qx0q6kt": observerTypes.VoteType_FailureObservation, - "zeta1ygeyr8pqfjvclxay5234gulnjzv2mkz6lph9y4": observerTypes.VoteType_FailureObservation, - "zeta1zegyenj7xg5nck04ykkzndm2qxdzc6v83mklsy": observerTypes.VoteType_FailureObservation, - "zeta1us2qpqdcctk6q7qv2c9d9jvjxlv88jscf68kav": observerTypes.VoteType_FailureObservation, - "zeta1e9fyaulgntkrnqnl0es4nyxghp3petpn2ntu3t": observerTypes.VoteType_FailureObservation, - }, - ballotResult: observerTypes.BallotStatus_BallotInProgress, - cctxStatus: crosschaintypes.CctxStatus_PendingRevert, - }, - { - name: "majority wrong votes incorrect ballot finalized / correct ballot still in progress", - votes: map[string]observerTypes.VoteType{ - "zeta13c7p3xrhd6q2rx3h235jpt8pjdwvacyw6twpax": observerTypes.VoteType_SuccessObservation, - "zeta1f203dypqg5jh9hqfx0gfkmmnkdfuat3jr45ep2": observerTypes.VoteType_SuccessObservation, - "zeta1szrskhdeleyt6wmn0nfxvcvt2l6f4fn06uaga4": observerTypes.VoteType_SuccessObservation, - "zeta16h3y7s7030l4chcznwq3n6uz2m9wvmzu5vwt7c": observerTypes.VoteType_FailureObservation, - "zeta1xl2rfsrmx8nxryty3lsjuxwdxs59cn2q65e5ca": observerTypes.VoteType_FailureObservation, - "zeta1ktmprjdvc72jq0mpu8tn8sqx9xwj685qx0q6kt": observerTypes.VoteType_FailureObservation, - "zeta1ygeyr8pqfjvclxay5234gulnjzv2mkz6lph9y4": observerTypes.VoteType_FailureObservation, - "zeta1zegyenj7xg5nck04ykkzndm2qxdzc6v83mklsy": observerTypes.VoteType_FailureObservation, - "zeta1us2qpqdcctk6q7qv2c9d9jvjxlv88jscf68kav": observerTypes.VoteType_FailureObservation, - "zeta1e9fyaulgntkrnqnl0es4nyxghp3petpn2ntu3t": observerTypes.VoteType_FailureObservation, - }, - ballotResult: observerTypes.BallotStatus_BallotInProgress, - cctxStatus: crosschaintypes.CctxStatus_PendingOutbound, - falseBallotIdentifier: "majority wrong votes incorrect ballot finalized / correct ballot still in progress" + "falseVote", - }, - { - name: "7 votes only just crossed threshold", - votes: map[string]observerTypes.VoteType{ - "zeta13c7p3xrhd6q2rx3h235jpt8pjdwvacyw6twpax": observerTypes.VoteType_SuccessObservation, - "zeta1f203dypqg5jh9hqfx0gfkmmnkdfuat3jr45ep2": observerTypes.VoteType_SuccessObservation, - "zeta1szrskhdeleyt6wmn0nfxvcvt2l6f4fn06uaga4": observerTypes.VoteType_SuccessObservation, - "zeta16h3y7s7030l4chcznwq3n6uz2m9wvmzu5vwt7c": observerTypes.VoteType_SuccessObservation, - "zeta1xl2rfsrmx8nxryty3lsjuxwdxs59cn2q65e5ca": observerTypes.VoteType_SuccessObservation, - "zeta1ktmprjdvc72jq0mpu8tn8sqx9xwj685qx0q6kt": observerTypes.VoteType_NotYetVoted, - "zeta1ygeyr8pqfjvclxay5234gulnjzv2mkz6lph9y4": observerTypes.VoteType_SuccessObservation, - "zeta1zegyenj7xg5nck04ykkzndm2qxdzc6v83mklsy": observerTypes.VoteType_NotYetVoted, - "zeta1us2qpqdcctk6q7qv2c9d9jvjxlv88jscf68kav": observerTypes.VoteType_NotYetVoted, - "zeta1e9fyaulgntkrnqnl0es4nyxghp3petpn2ntu3t": observerTypes.VoteType_SuccessObservation, - }, - ballotResult: observerTypes.BallotStatus_BallotFinalized_SuccessObservation, - cctxStatus: crosschaintypes.CctxStatus_PendingOutbound, - }, - } - for i, test := range tt { - test := test - s.Run(test.name, func() { - // Vote the gas price - for _, val := range s.network.Validators { - out, err := clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, authcli.GetAccountCmd(), []string{val.Address.String(), "--output", "json"}) - s.Require().NoError(err) - - var account authtypes.AccountI - s.NoError(val.ClientCtx.Codec.UnmarshalInterfaceJSON(out.Bytes(), &account)) - signedTx := BuildSignedGasPriceVote(s.T(), val, s.cfg.BondDenom, account) - _, err = clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, authcli.GetBroadcastCommand(), []string{signedTx.Name(), "--broadcast-mode", "sync"}) - s.Require().NoError(err) - } - - s.Require().NoError(s.network.WaitForNBlocks(2)) - out, err := clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, observercli.CmdListPendingNonces(), []string{"--output", "json"}) - s.Require().NoError(err) - out, err = clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, observercli.CmdGetSupportedChains(), []string{"--output", "json"}) - s.Require().NoError(err) - out, err = clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, crosschaincli.CmdListGasPrice(), []string{"--output", "json"}) - s.Require().NoError(err) - - // Vote the inbound tx - for _, val := range s.network.Validators { - vote := test.votes[val.Address.String()] - if vote == observerTypes.VoteType_NotYetVoted { - continue - } - out, err := clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, authcli.GetAccountCmd(), []string{val.Address.String(), "--output", "json"}) - var account authtypes.AccountI - s.NoError(val.ClientCtx.Codec.UnmarshalInterfaceJSON(out.Bytes(), &account)) - - message := test.name - if vote == observerTypes.VoteType_FailureObservation { - message = message + "falseVote" - } - signedTx := BuildSignedInboundVote(s.T(), val, s.cfg.BondDenom, account, message, i) - out, err = clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, authcli.GetBroadcastCommand(), []string{signedTx.Name(), "--broadcast-mode", "block"}) - s.Require().NoError(err) - fmt.Println(out.String()) - } - s.Require().NoError(s.network.WaitForNBlocks(2)) - - // Get the ballot - ballotIdentifier := GetBallotIdentifier(test.name, i) - out, err = clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, observercli.CmdBallotByIdentifier(), []string{ballotIdentifier, "--output", "json"}) - s.Require().NoError(err) - ballot := observerTypes.QueryBallotByIdentifierResponse{} - s.NoError(broadcaster.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &ballot)) - - // Check the vote in the ballot - s.Require().Equal(len(test.votes), len(ballot.Voters)) - for _, vote := range ballot.Voters { - if test.votes[vote.VoterAddress] == observerTypes.VoteType_FailureObservation { - s.Assert().Equal(observerTypes.VoteType_NotYetVoted.String(), vote.VoteType.String()) - continue - } - s.Assert().Equal(test.votes[vote.VoterAddress].String(), vote.VoteType.String(), "incorrect vote for voter: %s", vote.VoterAddress) - } - s.Require().Equal(test.ballotResult.String(), ballot.BallotStatus.String()) - - // Get the cctx and check its status - cctxIdentifier := ballotIdentifier - if test.falseBallotIdentifier != "" { - cctxIdentifier = GetBallotIdentifier(test.falseBallotIdentifier, i) - } - out, err = clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, crosschaincli.CmdShowSend(), []string{cctxIdentifier, "--output", "json"}) - cctx := crosschaintypes.QueryGetCctxResponse{} - if test.cctxStatus == crosschaintypes.CctxStatus_PendingRevert { - s.Require().Contains(out.String(), "not found") - } else { - s.NoError(broadcaster.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &cctx)) - s.Require().Equal(test.cctxStatus.String(), cctx.CrossChainTx.CctxStatus.Status.String(), cctx.CrossChainTx.CctxStatus.StatusMessage) - } - }) - } - -} diff --git a/x/crosschain/client/integrationtests/outbound_voter_test.go b/x/crosschain/client/integrationtests/outbound_voter_test.go deleted file mode 100644 index d388adc003..0000000000 --- a/x/crosschain/client/integrationtests/outbound_voter_test.go +++ /dev/null @@ -1,248 +0,0 @@ -package integrationtests - -import ( - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - authcli "github.com/cosmos/cosmos-sdk/x/auth/client/cli" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - crosschaincli "github.com/zeta-chain/zetacore/x/crosschain/client/cli" - crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" - observercli "github.com/zeta-chain/zetacore/x/observer/client/cli" - observerTypes "github.com/zeta-chain/zetacore/x/observer/types" -) - -func (s *IntegrationTestSuite) TestCCTXOutBoundVoter() { - type Vote struct { - voterAddress string - voteType observerTypes.VoteType - isFakeVote bool - } - tt := []struct { - name string - votes []Vote - valueReceived string // TODO : calculate this value - correctBallotResult observerTypes.BallotStatus - cctxStatus crosschaintypes.CctxStatus - falseBallotIdentifier string - }{ - { - name: "All observers voted success or not voted", - votes: []Vote{ - {voterAddress: "zeta13c7p3xrhd6q2rx3h235jpt8pjdwvacyw6twpax", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1f203dypqg5jh9hqfx0gfkmmnkdfuat3jr45ep2", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1szrskhdeleyt6wmn0nfxvcvt2l6f4fn06uaga4", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta16h3y7s7030l4chcznwq3n6uz2m9wvmzu5vwt7c", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1xl2rfsrmx8nxryty3lsjuxwdxs59cn2q65e5ca", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1ktmprjdvc72jq0mpu8tn8sqx9xwj685qx0q6kt", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1ygeyr8pqfjvclxay5234gulnjzv2mkz6lph9y4", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1zegyenj7xg5nck04ykkzndm2qxdzc6v83mklsy", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1us2qpqdcctk6q7qv2c9d9jvjxlv88jscf68kav", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1e9fyaulgntkrnqnl0es4nyxghp3petpn2ntu3t", voteType: observerTypes.VoteType_NotYetVoted, isFakeVote: false}, - }, - correctBallotResult: observerTypes.BallotStatus_BallotFinalized_SuccessObservation, - cctxStatus: crosschaintypes.CctxStatus_OutboundMined, - valueReceived: "7991636132140714751", - }, - { - name: "1 fake vote but ballot still success", - votes: []Vote{ - {voterAddress: "zeta13c7p3xrhd6q2rx3h235jpt8pjdwvacyw6twpax", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1f203dypqg5jh9hqfx0gfkmmnkdfuat3jr45ep2", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1szrskhdeleyt6wmn0nfxvcvt2l6f4fn06uaga4", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta16h3y7s7030l4chcznwq3n6uz2m9wvmzu5vwt7c", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1xl2rfsrmx8nxryty3lsjuxwdxs59cn2q65e5ca", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1ktmprjdvc72jq0mpu8tn8sqx9xwj685qx0q6kt", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1ygeyr8pqfjvclxay5234gulnjzv2mkz6lph9y4", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1zegyenj7xg5nck04ykkzndm2qxdzc6v83mklsy", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1us2qpqdcctk6q7qv2c9d9jvjxlv88jscf68kav", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: true}, - {voterAddress: "zeta1e9fyaulgntkrnqnl0es4nyxghp3petpn2ntu3t", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - }, - correctBallotResult: observerTypes.BallotStatus_BallotFinalized_SuccessObservation, - cctxStatus: crosschaintypes.CctxStatus_OutboundMined, - valueReceived: "7990439496224753106", - }, - { - name: "Half success and half false", - votes: []Vote{ - {voterAddress: "zeta13c7p3xrhd6q2rx3h235jpt8pjdwvacyw6twpax", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1f203dypqg5jh9hqfx0gfkmmnkdfuat3jr45ep2", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1szrskhdeleyt6wmn0nfxvcvt2l6f4fn06uaga4", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta16h3y7s7030l4chcznwq3n6uz2m9wvmzu5vwt7c", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1xl2rfsrmx8nxryty3lsjuxwdxs59cn2q65e5ca", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1ktmprjdvc72jq0mpu8tn8sqx9xwj685qx0q6kt", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: true}, - {voterAddress: "zeta1ygeyr8pqfjvclxay5234gulnjzv2mkz6lph9y4", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: true}, - {voterAddress: "zeta1zegyenj7xg5nck04ykkzndm2qxdzc6v83mklsy", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: true}, - {voterAddress: "zeta1us2qpqdcctk6q7qv2c9d9jvjxlv88jscf68kav", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: true}, - {voterAddress: "zeta1e9fyaulgntkrnqnl0es4nyxghp3petpn2ntu3t", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: true}, - }, - correctBallotResult: observerTypes.BallotStatus_BallotInProgress, - cctxStatus: crosschaintypes.CctxStatus_PendingOutbound, - valueReceived: "7990439496224753106", - }, - { - name: "Fake ballot has more votes outbound gets finalized", - votes: []Vote{ - {voterAddress: "zeta13c7p3xrhd6q2rx3h235jpt8pjdwvacyw6twpax", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1f203dypqg5jh9hqfx0gfkmmnkdfuat3jr45ep2", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1szrskhdeleyt6wmn0nfxvcvt2l6f4fn06uaga4", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: true}, - {voterAddress: "zeta16h3y7s7030l4chcznwq3n6uz2m9wvmzu5vwt7c", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: true}, - {voterAddress: "zeta1xl2rfsrmx8nxryty3lsjuxwdxs59cn2q65e5ca", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: true}, - {voterAddress: "zeta1ktmprjdvc72jq0mpu8tn8sqx9xwj685qx0q6kt", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: true}, - {voterAddress: "zeta1ygeyr8pqfjvclxay5234gulnjzv2mkz6lph9y4", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: true}, - {voterAddress: "zeta1zegyenj7xg5nck04ykkzndm2qxdzc6v83mklsy", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: true}, - {voterAddress: "zeta1us2qpqdcctk6q7qv2c9d9jvjxlv88jscf68kav", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: true}, - {voterAddress: "zeta1e9fyaulgntkrnqnl0es4nyxghp3petpn2ntu3t", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: true}, - }, - correctBallotResult: observerTypes.BallotStatus_BallotInProgress, - cctxStatus: crosschaintypes.CctxStatus_OutboundMined, - valueReceived: "7987124742653889020", - }, - { - name: "5 success 5 Failed votes ", - votes: []Vote{ - {voterAddress: "zeta13c7p3xrhd6q2rx3h235jpt8pjdwvacyw6twpax", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1f203dypqg5jh9hqfx0gfkmmnkdfuat3jr45ep2", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1szrskhdeleyt6wmn0nfxvcvt2l6f4fn06uaga4", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta16h3y7s7030l4chcznwq3n6uz2m9wvmzu5vwt7c", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1xl2rfsrmx8nxryty3lsjuxwdxs59cn2q65e5ca", voteType: observerTypes.VoteType_SuccessObservation, isFakeVote: false}, - {voterAddress: "zeta1ktmprjdvc72jq0mpu8tn8sqx9xwj685qx0q6kt", voteType: observerTypes.VoteType_FailureObservation, isFakeVote: false}, - {voterAddress: "zeta1ygeyr8pqfjvclxay5234gulnjzv2mkz6lph9y4", voteType: observerTypes.VoteType_FailureObservation, isFakeVote: false}, - {voterAddress: "zeta1zegyenj7xg5nck04ykkzndm2qxdzc6v83mklsy", voteType: observerTypes.VoteType_FailureObservation, isFakeVote: false}, - {voterAddress: "zeta1us2qpqdcctk6q7qv2c9d9jvjxlv88jscf68kav", voteType: observerTypes.VoteType_FailureObservation, isFakeVote: false}, - {voterAddress: "zeta1e9fyaulgntkrnqnl0es4nyxghp3petpn2ntu3t", voteType: observerTypes.VoteType_FailureObservation, isFakeVote: false}, - }, - correctBallotResult: observerTypes.BallotStatus_BallotInProgress, - cctxStatus: crosschaintypes.CctxStatus_PendingOutbound, - valueReceived: "7991636132140714751", - }, - } - for i, test := range tt { - // Buffer event index so that it does not clash with the inbound voter test - eventIndex := i + 100 - test := test - s.Run(test.name, func() { - broadcaster := s.network.Validators[0] - - // Vote the gas price - for _, val := range s.network.Validators { - out, err := clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, authcli.GetAccountCmd(), []string{val.Address.String(), "--output", "json"}) - var account authtypes.AccountI - s.NoError(val.ClientCtx.Codec.UnmarshalInterfaceJSON(out.Bytes(), &account)) - signedTx := BuildSignedGasPriceVote(s.T(), val, s.cfg.BondDenom, account) - _, err = clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, authcli.GetBroadcastCommand(), []string{signedTx.Name(), "--broadcast-mode", "sync"}) - s.Require().NoError(err) - } - s.Require().NoError(s.network.WaitForNBlocks(2)) - - // Vote the tss - for _, val := range s.network.Validators { - out, err := clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, authcli.GetAccountCmd(), []string{val.Address.String(), "--output", "json"}) - var account authtypes.AccountI - s.NoError(val.ClientCtx.Codec.UnmarshalInterfaceJSON(out.Bytes(), &account)) - signedTx := BuildSignedTssVote(s.T(), val, s.cfg.BondDenom, account) - out, err = clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, authcli.GetBroadcastCommand(), []string{signedTx.Name(), "--broadcast-mode", "sync"}) - s.Require().NoError(err) - } - s.Require().NoError(s.network.WaitForNBlocks(2)) - - // Vote the inbound tx - for _, val := range s.network.Validators { - out, err := clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, authcli.GetAccountCmd(), []string{val.Address.String(), "--output", "json"}) - var account authtypes.AccountI - s.NoError(val.ClientCtx.Codec.UnmarshalInterfaceJSON(out.Bytes(), &account)) - message := test.name - signedTx := BuildSignedInboundVote(s.T(), val, s.cfg.BondDenom, account, message, eventIndex) - out, err = clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, authcli.GetBroadcastCommand(), []string{signedTx.Name(), "--broadcast-mode", "sync"}) - s.Require().NoError(err) - } - s.Require().NoError(s.network.WaitForNBlocks(2)) - - // Get the ballot - cctxIdentifier := GetBallotIdentifier(test.name, eventIndex) - out, err := clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, crosschaincli.CmdShowSend(), []string{cctxIdentifier, "--output", "json"}) - cctx := crosschaintypes.QueryGetCctxResponse{} - s.NoError(broadcaster.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &cctx)) - s.Assert().Equal(crosschaintypes.CctxStatus_PendingOutbound.String(), cctx.CrossChainTx.CctxStatus.Status.String(), cctx.CrossChainTx.CctxStatus.StatusMessage) - nonce := cctx.CrossChainTx.GetCurrentOutTxParam().OutboundTxTssNonce - // Check the vote in the ballot and vote the outbound tx - fakeVotes := []string{} - for _, val := range s.network.Validators { - valVote := Vote{} - for _, vote := range test.votes { - if vote.voterAddress == val.Address.String() { - valVote = vote - } - } - if valVote.voteType == observerTypes.VoteType_NotYetVoted { - continue - } - out, err = clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, authcli.GetAccountCmd(), []string{val.Address.String(), "--output", "json"}) - var account authtypes.AccountI - s.NoError(val.ClientCtx.Codec.UnmarshalInterfaceJSON(out.Bytes(), &account)) - - outTxhash := test.name - if valVote.isFakeVote { - outTxhash = outTxhash + "falseVote" - fakeVotes = append(fakeVotes, val.Address.String()) - } - votestring := "" - switch valVote.voteType { - case observerTypes.VoteType_SuccessObservation: - votestring = "0" - case observerTypes.VoteType_FailureObservation: - votestring = "1" - } - - // Vote the outbound tx - signedTx := BuildSignedOutboundVote(s.T(), val, s.cfg.BondDenom, account, nonce, cctxIdentifier, outTxhash, test.valueReceived, votestring) - out, err = clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, authcli.GetBroadcastCommand(), []string{signedTx.Name(), "--broadcast-mode", "sync", "--output", "json"}) - s.Require().NoError(err) - } - s.Require().NoError(s.network.WaitForNBlocks(2)) - - // Get the cctx - out, err = clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, crosschaincli.CmdShowSend(), []string{cctxIdentifier, "--output", "json"}) - cctx = crosschaintypes.QueryGetCctxResponse{} - s.NoError(broadcaster.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &cctx)) - s.Assert().Equal(test.cctxStatus.String(), cctx.CrossChainTx.CctxStatus.Status.String(), cctx.CrossChainTx.CctxStatus.StatusMessage) - - outboundBallotIdentifier := GetBallotIdentifierOutBound(nonce, cctxIdentifier, test.name, test.valueReceived) - - out, err = clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, observercli.CmdBallotByIdentifier(), []string{outboundBallotIdentifier, "--output", "json"}) - s.Require().NoError(err) - ballot := observerTypes.QueryBallotByIdentifierResponse{} - s.NoError(broadcaster.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &ballot)) - - // Check the votes - s.Require().Equal(test.correctBallotResult.String(), ballot.BallotStatus.String()) - for _, vote := range test.votes { - for _, ballotvote := range ballot.Voters { - if vote.voterAddress == ballotvote.VoterAddress { - if !vote.isFakeVote { - s.Assert().Equal(vote.voteType.String(), ballotvote.VoteType.String()) - } else { - s.Assert().Equal(observerTypes.VoteType_NotYetVoted.String(), ballotvote.VoteType.String()) - } - break - } - } - } - if len(fakeVotes) > 0 { - outboundFakeBallotIdentifier := GetBallotIdentifierOutBound(nonce, cctxIdentifier, test.name+"falseVote", test.valueReceived) - out, err = clitestutil.ExecTestCLICmd(broadcaster.ClientCtx, observercli.CmdBallotByIdentifier(), []string{outboundFakeBallotIdentifier, "--output", "json"}) - s.Require().NoError(err) - fakeBallot := observerTypes.QueryBallotByIdentifierResponse{} - s.NoError(broadcaster.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &fakeBallot)) - for _, vote := range test.votes { - if vote.isFakeVote { - for _, ballotVote := range fakeBallot.Voters { - if vote.voterAddress == ballotVote.VoterAddress { - s.Assert().Equal(vote.voteType.String(), ballotVote.VoteType.String()) - break - } - } - } - } - } - }) - } -} diff --git a/x/crosschain/client/integrationtests/suite.go b/x/crosschain/client/integrationtests/suite.go deleted file mode 100644 index b5f16a683d..0000000000 --- a/x/crosschain/client/integrationtests/suite.go +++ /dev/null @@ -1,65 +0,0 @@ -package integrationtests - -import ( - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - ethcfg "github.com/evmos/ethermint/cmd/config" - "github.com/stretchr/testify/suite" - "github.com/zeta-chain/zetacore/app" - cmdcfg "github.com/zeta-chain/zetacore/cmd/zetacored/config" - "github.com/zeta-chain/zetacore/testutil/network" -) - -type IntegrationTestSuite struct { - suite.Suite - - cfg network.Config - network *network.Network -} - -func NewIntegrationTestSuite(cfg network.Config) *IntegrationTestSuite { - return &IntegrationTestSuite{cfg: cfg} -} - -func (s *IntegrationTestSuite) Setconfig() { - config := sdk.GetConfig() - cmdcfg.SetBech32Prefixes(config) - ethcfg.SetBip44CoinType(config) - // Make sure address is compatible with ethereum - config.SetAddressVerifier(app.VerifyAddressFormat) - config.Seal() -} -func (s *IntegrationTestSuite) SetupSuite() { - s.T().Log("setting up integration test suite") - s.Setconfig() - minOBsDel, ok := sdk.NewIntFromString("100000000000000000000") - s.Require().True(ok) - s.cfg.StakingTokens = minOBsDel.Mul(sdk.NewInt(int64(10))) - s.cfg.BondedTokens = minOBsDel - observerList := []string{"zeta13c7p3xrhd6q2rx3h235jpt8pjdwvacyw6twpax", - "zeta1f203dypqg5jh9hqfx0gfkmmnkdfuat3jr45ep2", - "zeta1szrskhdeleyt6wmn0nfxvcvt2l6f4fn06uaga4", - "zeta16h3y7s7030l4chcznwq3n6uz2m9wvmzu5vwt7c", - "zeta1xl2rfsrmx8nxryty3lsjuxwdxs59cn2q65e5ca", - "zeta1ktmprjdvc72jq0mpu8tn8sqx9xwj685qx0q6kt", - "zeta1ygeyr8pqfjvclxay5234gulnjzv2mkz6lph9y4", - "zeta1zegyenj7xg5nck04ykkzndm2qxdzc6v83mklsy", - "zeta1us2qpqdcctk6q7qv2c9d9jvjxlv88jscf68kav", - "zeta1e9fyaulgntkrnqnl0es4nyxghp3petpn2ntu3t", - } - network.SetupZetaGenesisState(s.T(), s.cfg.GenesisState, s.cfg.Codec, observerList, true) - network.AddCrosschainData(s.T(), 0, s.cfg.GenesisState, s.cfg.Codec) - network.AddObserverData(s.T(), 0, s.cfg.GenesisState, s.cfg.Codec, nil) - net, err := network.New(s.T(), app.NodeDir, s.cfg) - s.Require().NoError(err) - s.network = net - time.Sleep(3 * time.Second) - _, err = s.network.WaitForHeight(1) - s.Require().NoError(err) -} - -func (s *IntegrationTestSuite) TearDownSuite() { - s.T().Log("tearing down integration test suite") - s.network.Cleanup() -} diff --git a/x/crosschain/keeper/cctx.go b/x/crosschain/keeper/cctx.go index c689e0cc00..10613e2d6c 100644 --- a/x/crosschain/keeper/cctx.go +++ b/x/crosschain/keeper/cctx.go @@ -3,7 +3,6 @@ package keeper import ( "fmt" - "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/zeta-chain/zetacore/pkg/coin" @@ -48,7 +47,7 @@ func (k Keeper) SetCctxAndNonceToCctxAndInTxHashToCctx(ctx sdk.Context, cctx typ Tss: tss.TssPubkey, }) } - if cctx.CctxStatus.Status == types.CctxStatus_Aborted && cctx.GetCurrentOutTxParam().CoinType == coin.CoinType_Zeta { + if cctx.CctxStatus.Status == types.CctxStatus_Aborted && cctx.InboundTxParams.CoinType == coin.CoinType_Zeta { k.AddZetaAbortedAmount(ctx, GetAbortedAmount(cctx)) } } @@ -98,59 +97,3 @@ func (k Keeper) RemoveCrossChainTx(ctx sdk.Context, index string) { store := prefix.NewStore(ctx.KVStore(k.storeKey), p) store.Delete(types.KeyPrefix(index)) } - -func (k Keeper) CreateNewCCTX( - ctx sdk.Context, - msg *types.MsgVoteOnObservedInboundTx, - index string, - tssPubkey string, - s types.CctxStatus, - senderChainID, - receiverChainID int64, -) types.CrossChainTx { - if msg.TxOrigin == "" { - msg.TxOrigin = msg.Sender - } - inboundParams := &types.InboundTxParams{ - Sender: msg.Sender, - SenderChainId: senderChainID, - TxOrigin: msg.TxOrigin, - Asset: msg.Asset, - Amount: msg.Amount, - CoinType: msg.CoinType, - InboundTxObservedHash: msg.InTxHash, - InboundTxObservedExternalHeight: msg.InBlockHeight, - InboundTxFinalizedZetaHeight: 0, - InboundTxBallotIndex: index, - } - - outBoundParams := &types.OutboundTxParams{ - Receiver: msg.Receiver, - ReceiverChainId: receiverChainID, - OutboundTxHash: "", - OutboundTxTssNonce: 0, - OutboundTxGasLimit: msg.GasLimit, - OutboundTxGasPrice: "", - OutboundTxBallotIndex: "", - OutboundTxObservedExternalHeight: 0, - CoinType: msg.CoinType, // FIXME: is this correct? - Amount: sdk.NewUint(0), - TssPubkey: tssPubkey, - } - status := &types.Status{ - Status: s, - StatusMessage: "", - LastUpdateTimestamp: ctx.BlockHeader().Time.Unix(), - IsAbortRefunded: false, - } - newCctx := types.CrossChainTx{ - Creator: msg.Creator, - Index: index, - ZetaFees: math.ZeroUint(), - RelayedMessage: msg.Message, - CctxStatus: status, - InboundTxParams: inboundParams, - OutboundTxParams: []*types.OutboundTxParams{outBoundParams}, - } - return newCctx -} diff --git a/x/crosschain/keeper/cctx_test.go b/x/crosschain/keeper/cctx_test.go index 7f51dca642..3f8c3fe4d4 100644 --- a/x/crosschain/keeper/cctx_test.go +++ b/x/crosschain/keeper/cctx_test.go @@ -62,7 +62,7 @@ func createNCctx(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.CrossCha OutboundTxGasPrice: fmt.Sprintf("%d", i), OutboundTxBallotIndex: fmt.Sprintf("%d", i), OutboundTxObservedExternalHeight: uint64(i), - CoinType: 0, + CoinType: coin.CoinType_Zeta, }} items[i].CctxStatus = &types.Status{ Status: types.CctxStatus_PendingInbound, @@ -73,6 +73,7 @@ func createNCctx(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.CrossCha items[i].ZetaFees = math.OneUint() items[i].Index = fmt.Sprintf("%d", i) + keeper.SetCctxAndNonceToCctxAndInTxHashToCctx(ctx, items[i]) } return items diff --git a/x/crosschain/keeper/cctx_utils_test.go b/x/crosschain/keeper/cctx_utils_test.go index c637cc4cc9..888b3bf445 100644 --- a/x/crosschain/keeper/cctx_utils_test.go +++ b/x/crosschain/keeper/cctx_utils_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "fmt" "math/big" "testing" @@ -46,6 +47,7 @@ func TestGetRevertGasLimit(t *testing.T) { require.NoError(t, err) gasLimit, err := k.GetRevertGasLimit(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ CoinType: coin.CoinType_Gas, SenderChainId: chainID, @@ -76,6 +78,7 @@ func TestGetRevertGasLimit(t *testing.T) { require.NoError(t, err) gasLimit, err := k.GetRevertGasLimit(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ CoinType: coin.CoinType_ERC20, SenderChainId: chainID, @@ -89,6 +92,7 @@ func TestGetRevertGasLimit(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) _, err := k.GetRevertGasLimit(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ CoinType: coin.CoinType_Gas, SenderChainId: 999999, @@ -110,6 +114,7 @@ func TestGetRevertGasLimit(t *testing.T) { // no contract deployed therefore will fail _, err := k.GetRevertGasLimit(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ CoinType: coin.CoinType_Gas, SenderChainId: chainID, @@ -121,6 +126,7 @@ func TestGetRevertGasLimit(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) _, err := k.GetRevertGasLimit(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ CoinType: coin.CoinType_ERC20, SenderChainId: 999999, @@ -191,3 +197,22 @@ func TestGetAbortedAmount(t *testing.T) { require.Equal(t, sdkmath.ZeroUint(), a) }) } + +func Test_IsPending(t *testing.T) { + tt := []struct { + status types.CctxStatus + expected bool + }{ + {types.CctxStatus_PendingInbound, false}, + {types.CctxStatus_PendingOutbound, true}, + {types.CctxStatus_PendingRevert, true}, + {types.CctxStatus_Reverted, false}, + {types.CctxStatus_Aborted, false}, + {types.CctxStatus_OutboundMined, false}, + } + for _, tc := range tt { + t.Run(fmt.Sprintf("status %s", tc.status), func(t *testing.T) { + require.Equal(t, tc.expected, crosschainkeeper.IsPending(types.CrossChainTx{CctxStatus: &types.Status{Status: tc.status}})) + }) + } +} diff --git a/x/crosschain/keeper/events.go b/x/crosschain/keeper/events.go index 6943d77723..766631f156 100644 --- a/x/crosschain/keeper/events.go +++ b/x/crosschain/keeper/events.go @@ -59,11 +59,11 @@ func EmitZetaWithdrawCreated(ctx sdk.Context, cctx types.CrossChainTx) { } -func EmitOutboundSuccess(ctx sdk.Context, msg *types.MsgVoteOnObservedOutboundTx, oldStatus string, newStatus string, cctx types.CrossChainTx) { +func EmitOutboundSuccess(ctx sdk.Context, valueReceived string, oldStatus string, newStatus string, cctxIndex string) { err := ctx.EventManager().EmitTypedEvents(&types.EventOutboundSuccess{ MsgTypeUrl: sdk.MsgTypeURL(&types.MsgVoteOnObservedOutboundTx{}), - CctxIndex: cctx.Index, - ValueReceived: msg.ValueReceived.String(), + CctxIndex: cctxIndex, + ValueReceived: valueReceived, OldStatus: oldStatus, NewStatus: newStatus, }) @@ -73,11 +73,11 @@ func EmitOutboundSuccess(ctx sdk.Context, msg *types.MsgVoteOnObservedOutboundTx } -func EmitOutboundFailure(ctx sdk.Context, msg *types.MsgVoteOnObservedOutboundTx, oldStatus string, newStatus string, cctx types.CrossChainTx) { +func EmitOutboundFailure(ctx sdk.Context, valueReceived string, oldStatus string, newStatus string, cctxIndex string) { err := ctx.EventManager().EmitTypedEvents(&types.EventOutboundFailure{ MsgTypeUrl: sdk.MsgTypeURL(&types.MsgVoteOnObservedOutboundTx{}), - CctxIndex: cctx.Index, - ValueReceived: msg.ValueReceived.String(), + CctxIndex: cctxIndex, + ValueReceived: valueReceived, OldStatus: oldStatus, NewStatus: newStatus, }) diff --git a/x/crosschain/keeper/evm_deposit.go b/x/crosschain/keeper/evm_deposit.go index 96be91a5d4..0ce56a829c 100644 --- a/x/crosschain/keeper/evm_deposit.go +++ b/x/crosschain/keeper/evm_deposit.go @@ -19,14 +19,13 @@ import ( // HandleEVMDeposit handles a deposit from an inbound tx // returns (isContractReverted, err) // (true, non-nil) means CallEVM() reverted -func (k Keeper) HandleEVMDeposit( - ctx sdk.Context, - cctx *types.CrossChainTx, - msg types.MsgVoteOnObservedInboundTx, - senderChainID int64, -) (bool, error) { - to := ethcommon.HexToAddress(msg.Receiver) +func (k Keeper) HandleEVMDeposit(ctx sdk.Context, cctx *types.CrossChainTx) (bool, error) { + to := ethcommon.HexToAddress(cctx.GetCurrentOutTxParam().Receiver) var ethTxHash ethcommon.Hash + inboundAmount := cctx.GetInboundTxParams().Amount.BigInt() + inboundSender := cctx.GetInboundTxParams().Sender + inboundSenderChainID := cctx.GetInboundTxParams().SenderChainId + inboundCoinType := cctx.InboundTxParams.CoinType if len(ctx.TxBytes()) > 0 { // add event for tendermint transaction hash format hash := tmbytes.HexBytes(tmtypes.Tx(ctx.TxBytes()).Hash()) @@ -36,15 +35,15 @@ func (k Keeper) HandleEVMDeposit( cctx.GetCurrentOutTxParam().OutboundTxObservedExternalHeight = uint64(ctx.BlockHeight()) } - if msg.CoinType == coin.CoinType_Zeta { + if inboundCoinType == coin.CoinType_Zeta { // if coin type is Zeta, this is a deposit ZETA to zEVM cctx. - err := k.fungibleKeeper.DepositCoinZeta(ctx, to, msg.Amount.BigInt()) + err := k.fungibleKeeper.DepositCoinZeta(ctx, to, inboundAmount) if err != nil { return false, err } } else { // cointype is Gas or ERC20; then it could be a ZRC20 deposit/depositAndCall cctx. - parsedAddress, data, err := chains.ParseAddressAndData(msg.Message) + parsedAddress, data, err := chains.ParseAddressAndData(cctx.RelayedMessage) if err != nil { return false, errors.Wrap(types.ErrUnableToParseAddress, err.Error()) } @@ -52,7 +51,7 @@ func (k Keeper) HandleEVMDeposit( to = parsedAddress } - from, err := chains.DecodeAddressFromChainID(senderChainID, msg.Sender) + from, err := chains.DecodeAddressFromChainID(inboundSenderChainID, inboundSender) if err != nil { return false, fmt.Errorf("HandleEVMDeposit: unable to decode address: %s", err.Error()) } @@ -61,11 +60,11 @@ func (k Keeper) HandleEVMDeposit( ctx, from, to, - msg.Amount.BigInt(), - senderChainID, + inboundAmount, + inboundSenderChainID, data, - msg.CoinType, - msg.Asset, + inboundCoinType, + cctx.InboundTxParams.Asset, ) if fungibletypes.IsContractReverted(evmTxResponse, err) || errShouldRevertCctx(err) { return true, err @@ -79,9 +78,9 @@ func (k Keeper) HandleEVMDeposit( logs := evmtypes.LogsToEthereum(evmTxResponse.Logs) if len(logs) > 0 { ctx = ctx.WithValue("inCctxIndex", cctx.Index) - txOrigin := msg.TxOrigin + txOrigin := cctx.InboundTxParams.TxOrigin if txOrigin == "" { - txOrigin = msg.Sender + txOrigin = inboundSender } err = k.ProcessLogs(ctx, logs, to, txOrigin) diff --git a/x/crosschain/keeper/evm_deposit_test.go b/x/crosschain/keeper/evm_deposit_test.go index 15ca00a62a..bcb7b684e8 100644 --- a/x/crosschain/keeper/evm_deposit_test.go +++ b/x/crosschain/keeper/evm_deposit_test.go @@ -31,15 +31,14 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { fungibleMock.On("DepositCoinZeta", ctx, receiver, amount).Return(nil) // call HandleEVMDeposit + cctx := sample.CrossChainTx(t, "foo") + cctx.GetCurrentOutTxParam().Receiver = receiver.String() + cctx.GetInboundTxParams().Amount = math.NewUintFromBigInt(amount) + cctx.GetInboundTxParams().CoinType = coin.CoinType_Zeta + cctx.GetInboundTxParams().SenderChainId = 0 reverted, err := k.HandleEVMDeposit( ctx, - sample.CrossChainTx(t, "foo"), - types.MsgVoteOnObservedInboundTx{ - Receiver: receiver.String(), - Amount: math.NewUintFromBigInt(amount), - CoinType: coin.CoinType_Zeta, - }, - 0, + cctx, ) require.NoError(t, err) require.False(t, reverted) @@ -60,15 +59,15 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { fungibleMock.On("DepositCoinZeta", ctx, receiver, amount).Return(errDeposit) // call HandleEVMDeposit + + cctx := sample.CrossChainTx(t, "foo") + cctx.GetCurrentOutTxParam().Receiver = receiver.String() + cctx.GetInboundTxParams().Amount = math.NewUintFromBigInt(amount) + cctx.GetInboundTxParams().CoinType = coin.CoinType_Zeta + cctx.GetInboundTxParams().SenderChainId = 0 reverted, err := k.HandleEVMDeposit( ctx, - sample.CrossChainTx(t, "foo"), - types.MsgVoteOnObservedInboundTx{ - Receiver: receiver.String(), - Amount: math.NewUintFromBigInt(amount), - CoinType: coin.CoinType_Zeta, - }, - 0, + cctx, ) require.ErrorIs(t, err, errDeposit) require.False(t, reverted) @@ -101,18 +100,17 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { ).Return(&evmtypes.MsgEthereumTxResponse{}, false, nil) // call HandleEVMDeposit + cctx := sample.CrossChainTx(t, "foo") + cctx.GetCurrentOutTxParam().Receiver = receiver.String() + cctx.GetInboundTxParams().Amount = math.NewUintFromBigInt(amount) + cctx.GetInboundTxParams().CoinType = coin.CoinType_ERC20 + cctx.GetInboundTxParams().Sender = sample.EthAddress().String() + cctx.GetInboundTxParams().SenderChainId = senderChain + cctx.RelayedMessage = "" + cctx.GetInboundTxParams().Asset = "" reverted, err := k.HandleEVMDeposit( ctx, - sample.CrossChainTx(t, "foo"), - types.MsgVoteOnObservedInboundTx{ - Sender: sample.EthAddress().String(), - Receiver: receiver.String(), - Amount: math.NewUintFromBigInt(amount), - CoinType: coin.CoinType_ERC20, - Message: "", - Asset: "", - }, - senderChain, + cctx, ) require.NoError(t, err) require.False(t, reverted) @@ -146,18 +144,17 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { ).Return(&evmtypes.MsgEthereumTxResponse{}, false, errDeposit) // call HandleEVMDeposit + cctx := sample.CrossChainTx(t, "foo") + cctx.GetCurrentOutTxParam().Receiver = receiver.String() + cctx.GetInboundTxParams().Amount = math.NewUintFromBigInt(amount) + cctx.GetInboundTxParams().CoinType = coin.CoinType_ERC20 + cctx.GetInboundTxParams().Sender = sample.EthAddress().String() + cctx.GetInboundTxParams().SenderChainId = senderChain + cctx.RelayedMessage = "" + cctx.GetInboundTxParams().Asset = "" reverted, err := k.HandleEVMDeposit( ctx, - sample.CrossChainTx(t, "foo"), - types.MsgVoteOnObservedInboundTx{ - Sender: sample.EthAddress().String(), - Receiver: receiver.String(), - Amount: math.NewUintFromBigInt(amount), - CoinType: coin.CoinType_ERC20, - Message: "", - Asset: "", - }, - senderChain, + cctx, ) require.ErrorIs(t, err, errDeposit) require.False(t, reverted) @@ -191,18 +188,17 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { ).Return(&evmtypes.MsgEthereumTxResponse{VmError: "reverted"}, false, errDeposit) // call HandleEVMDeposit + cctx := sample.CrossChainTx(t, "foo") + cctx.GetCurrentOutTxParam().Receiver = receiver.String() + cctx.GetInboundTxParams().Amount = math.NewUintFromBigInt(amount) + cctx.InboundTxParams.CoinType = coin.CoinType_ERC20 + cctx.GetInboundTxParams().Sender = sample.EthAddress().String() + cctx.GetInboundTxParams().SenderChainId = senderChain + cctx.RelayedMessage = "" + cctx.GetInboundTxParams().Asset = "" reverted, err := k.HandleEVMDeposit( ctx, - sample.CrossChainTx(t, "foo"), - types.MsgVoteOnObservedInboundTx{ - Sender: sample.EthAddress().String(), - Receiver: receiver.String(), - Amount: math.NewUintFromBigInt(amount), - CoinType: coin.CoinType_ERC20, - Message: "", - Asset: "", - }, - senderChain, + cctx, ) require.ErrorIs(t, err, errDeposit) require.True(t, reverted) @@ -235,18 +231,17 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { ).Return(&evmtypes.MsgEthereumTxResponse{}, false, fungibletypes.ErrForeignCoinCapReached) // call HandleEVMDeposit + cctx := sample.CrossChainTx(t, "foo") + cctx.GetCurrentOutTxParam().Receiver = receiver.String() + cctx.GetInboundTxParams().Amount = math.NewUintFromBigInt(amount) + cctx.GetInboundTxParams().CoinType = coin.CoinType_ERC20 + cctx.GetInboundTxParams().Sender = sample.EthAddress().String() + cctx.GetInboundTxParams().SenderChainId = senderChain + cctx.RelayedMessage = "" + cctx.GetInboundTxParams().Asset = "" reverted, err := k.HandleEVMDeposit( ctx, - sample.CrossChainTx(t, "foo"), - types.MsgVoteOnObservedInboundTx{ - Sender: sample.EthAddress().String(), - Receiver: receiver.String(), - Amount: math.NewUintFromBigInt(amount), - CoinType: coin.CoinType_ERC20, - Message: "", - Asset: "", - }, - senderChain, + cctx, ) require.ErrorIs(t, err, fungibletypes.ErrForeignCoinCapReached) require.True(t, reverted) @@ -279,18 +274,17 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { ).Return(&evmtypes.MsgEthereumTxResponse{}, false, fungibletypes.ErrPausedZRC20) // call HandleEVMDeposit + cctx := sample.CrossChainTx(t, "foo") + cctx.GetCurrentOutTxParam().Receiver = receiver.String() + cctx.GetInboundTxParams().Amount = math.NewUintFromBigInt(amount) + cctx.GetInboundTxParams().CoinType = coin.CoinType_ERC20 + cctx.GetInboundTxParams().Sender = sample.EthAddress().String() + cctx.GetInboundTxParams().SenderChainId = senderChain + cctx.RelayedMessage = "" + cctx.GetInboundTxParams().Asset = "" reverted, err := k.HandleEVMDeposit( ctx, - sample.CrossChainTx(t, "foo"), - types.MsgVoteOnObservedInboundTx{ - Sender: sample.EthAddress().String(), - Receiver: receiver.String(), - Amount: math.NewUintFromBigInt(amount), - CoinType: coin.CoinType_ERC20, - Message: "", - Asset: "", - }, - senderChain, + cctx, ) require.ErrorIs(t, err, fungibletypes.ErrPausedZRC20) require.True(t, reverted) @@ -321,18 +315,17 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { ).Return(&evmtypes.MsgEthereumTxResponse{}, false, fungibletypes.ErrCallNonContract) // call HandleEVMDeposit + cctx := sample.CrossChainTx(t, "foo") + cctx.GetCurrentOutTxParam().Receiver = receiver.String() + cctx.GetInboundTxParams().Amount = math.NewUintFromBigInt(amount) + cctx.GetInboundTxParams().CoinType = coin.CoinType_ERC20 + cctx.GetInboundTxParams().Sender = sample.EthAddress().String() + cctx.GetInboundTxParams().SenderChainId = senderChain + cctx.RelayedMessage = "" + cctx.GetInboundTxParams().Asset = "" reverted, err := k.HandleEVMDeposit( ctx, - sample.CrossChainTx(t, "foo"), - types.MsgVoteOnObservedInboundTx{ - Sender: sample.EthAddress().String(), - Receiver: receiver.String(), - Amount: math.NewUintFromBigInt(amount), - CoinType: coin.CoinType_ERC20, - Message: "", - Asset: "", - }, - senderChain, + cctx, ) require.ErrorIs(t, err, fungibletypes.ErrCallNonContract) require.True(t, reverted) @@ -345,18 +338,17 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { }) senderChain := getValidEthChainID(t) + cctx := sample.CrossChainTx(t, "foo") + cctx.GetCurrentOutTxParam().Receiver = sample.EthAddress().String() + cctx.GetInboundTxParams().Amount = math.NewUint(42) + cctx.GetInboundTxParams().CoinType = coin.CoinType_Gas + cctx.GetInboundTxParams().Sender = sample.EthAddress().String() + cctx.GetInboundTxParams().SenderChainId = senderChain + cctx.RelayedMessage = "not_hex" + cctx.GetInboundTxParams().Asset = "" _, err := k.HandleEVMDeposit( ctx, - sample.CrossChainTx(t, "foo"), - types.MsgVoteOnObservedInboundTx{ - Sender: sample.EthAddress().String(), - Receiver: sample.EthAddress().String(), - Amount: math.NewUint(42), - CoinType: coin.CoinType_Gas, - Message: "not_hex", - Asset: "", - }, - senderChain, + cctx, ) require.ErrorIs(t, err, types.ErrUnableToParseAddress) }) @@ -386,18 +378,17 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{}, false, nil) + cctx := sample.CrossChainTx(t, "foo") + cctx.GetCurrentOutTxParam().Receiver = sample.EthAddress().String() + cctx.GetInboundTxParams().Amount = math.NewUintFromBigInt(amount) + cctx.GetInboundTxParams().CoinType = coin.CoinType_ERC20 + cctx.GetInboundTxParams().Sender = sample.EthAddress().String() + cctx.GetInboundTxParams().SenderChainId = senderChain + cctx.RelayedMessage = receiver.Hex()[2:] + "DEADBEEF" + cctx.GetInboundTxParams().Asset = "" reverted, err := k.HandleEVMDeposit( ctx, - sample.CrossChainTx(t, "foo"), - types.MsgVoteOnObservedInboundTx{ - Sender: sample.EthAddress().String(), - Receiver: sample.EthAddress().String(), - Amount: math.NewUintFromBigInt(amount), - CoinType: coin.CoinType_ERC20, - Message: receiver.Hex()[2:] + "DEADBEEF", - Asset: "", - }, - senderChain, + cctx, ) require.NoError(t, err) require.False(t, reverted) @@ -429,18 +420,17 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{}, false, nil) + cctx := sample.CrossChainTx(t, "foo") + cctx.GetCurrentOutTxParam().Receiver = receiver.String() + cctx.GetInboundTxParams().Amount = math.NewUintFromBigInt(amount) + cctx.GetInboundTxParams().CoinType = coin.CoinType_ERC20 + cctx.GetInboundTxParams().Sender = sample.EthAddress().String() + cctx.GetInboundTxParams().SenderChainId = senderChain + cctx.RelayedMessage = "DEADBEEF" + cctx.GetInboundTxParams().Asset = "" reverted, err := k.HandleEVMDeposit( ctx, - sample.CrossChainTx(t, "foo"), - types.MsgVoteOnObservedInboundTx{ - Sender: sample.EthAddress().String(), - Receiver: receiver.String(), - Amount: math.NewUintFromBigInt(amount), - CoinType: coin.CoinType_ERC20, - Message: "DEADBEEF", - Asset: "", - }, - senderChain, + cctx, ) require.NoError(t, err) require.False(t, reverted) diff --git a/x/crosschain/keeper/evm_hooks.go b/x/crosschain/keeper/evm_hooks.go index e5d85aba2a..a662352532 100644 --- a/x/crosschain/keeper/evm_hooks.go +++ b/x/crosschain/keeper/evm_hooks.go @@ -169,18 +169,13 @@ func (k Keeper) ProcessZRC20WithdrawalEvent(ctx sdk.Context, event *zrc20.ZRC20W foreignCoin.Asset, event.Raw.Index, ) - sendHash := msg.Digest() - - cctx := k.CreateNewCCTX( - ctx, - msg, - sendHash, - tss.TssPubkey, - types.CctxStatus_PendingOutbound, - senderChain.ChainId, - receiverChain.ChainId, - ) + // Create a new cctx with status as pending Inbound, this is created directly from the event without waiting for any observer votes + cctx, err := types.NewCCTX(ctx, *msg, tss.TssPubkey) + if err != nil { + return fmt.Errorf("ProcessZRC20WithdrawalEvent: failed to initialize cctx: %s", err.Error()) + } + cctx.SetPendingOutbound("ZRC20 withdrawal event setting to pending outbound directly") // Get gas price and amount gasprice, found := k.GetGasPrice(ctx, receiverChain.ChainId) if !found { @@ -206,7 +201,7 @@ func (k Keeper) ProcessZetaSentEvent(ctx sdk.Context, event *connectorzevm.ZetaC fungibletypes.ModuleName, sdk.NewCoins(sdk.NewCoin(config.BaseDenom, sdk.NewIntFromBigInt(event.ZetaValueAndGas))), ); err != nil { - fmt.Printf("burn coins failed: %s\n", err.Error()) + ctx.Logger().Error(fmt.Sprintf("ProcessZetaSentEvent: failed to burn coins from fungible: %s", err.Error())) return fmt.Errorf("ProcessZetaSentEvent: failed to burn coins from fungible: %s", err.Error()) } @@ -247,18 +242,14 @@ func (k Keeper) ProcessZetaSentEvent(ctx sdk.Context, event *connectorzevm.ZetaC "", event.Raw.Index, ) - sendHash := msg.Digest() - // Create the CCTX - cctx := k.CreateNewCCTX( - ctx, - msg, - sendHash, - tss.TssPubkey, - types.CctxStatus_PendingOutbound, - senderChain.ChainId, - receiverChain.ChainId, - ) + // create a new cctx with status as pending Inbound, + // this is created directly from the event without waiting for any observer votes + cctx, err := types.NewCCTX(ctx, *msg, tss.TssPubkey) + if err != nil { + return fmt.Errorf("ProcessZetaSentEvent: failed to initialize cctx: %s", err.Error()) + } + cctx.SetPendingOutbound("ZetaSent event setting to pending outbound directly") if err := k.PayGasAndUpdateCctx( ctx, diff --git a/x/crosschain/keeper/evm_hooks_test.go b/x/crosschain/keeper/evm_hooks_test.go index 85676457cf..c75f638d3f 100644 --- a/x/crosschain/keeper/evm_hooks_test.go +++ b/x/crosschain/keeper/evm_hooks_test.go @@ -22,7 +22,11 @@ import ( observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) -func SetupStateForProcessLogsZetaSent(t *testing.T, ctx sdk.Context, k *crosschainkeeper.Keeper, zk keepertest.ZetaKeepers, sdkk keepertest.SDKKeepers, chain chains.Chain) { +// SetupStateForProcessLogsZetaSent sets up additional state required for processing logs for ZetaSent events +// This sets up the gas coin, zrc20 contract, gas price, zrc20 pool. +// This should be used in conjunction with SetupStateForProcessLogs for processing ZetaSent events +func SetupStateForProcessLogsZetaSent(t *testing.T, ctx sdk.Context, k *crosschainkeeper.Keeper, zk keepertest.ZetaKeepers, sdkk keepertest.SDKKeepers, chain chains.Chain, admin string) { + assetAddress := sample.EthAddress().String() gasZRC20 := setupGasCoin(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper, chain.ChainId, "ethereum", "ETH") zrc20Addr := deployZRC20( @@ -38,6 +42,8 @@ func SetupStateForProcessLogsZetaSent(t *testing.T, ctx sdk.Context, k *crosscha _, err := zk.FungibleKeeper.UpdateZRC20ProtocolFlatFee(ctx, gasZRC20, big.NewInt(withdrawFee)) require.NoError(t, err) + _, err = zk.FungibleKeeper.UpdateZRC20ProtocolFlatFee(ctx, zrc20Addr, big.NewInt(withdrawFee)) + require.NoError(t, err) k.SetGasPrice(ctx, crosschaintypes.GasPrice{ ChainId: chain.ChainId, @@ -53,6 +59,8 @@ func SetupStateForProcessLogsZetaSent(t *testing.T, ctx sdk.Context, k *crosscha ) } +// SetupStateForProcessLogs sets up observer state for required for processing logs +// It deploys system contracts, sets up TSS, gas price, chain nonce's, pending nonce's.These are all required to create a cctx from a log func SetupStateForProcessLogs(t *testing.T, ctx sdk.Context, k *crosschainkeeper.Keeper, zk keepertest.ZetaKeepers, sdkk keepertest.SDKKeepers, chain chains.Chain) { deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) @@ -428,6 +436,7 @@ func TestKeeper_ParseZetaSentEvent(t *testing.T) { func TestKeeper_ProcessZetaSentEvent(t *testing.T) { t.Run("successfully process ZetaSentEvent", func(t *testing.T) { k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) chain := chains.EthChain() @@ -435,7 +444,9 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { setSupportedChain(ctx, zk, chainID) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - SetupStateForProcessLogsZetaSent(t, ctx, k, zk, sdkk, chain) + admin := keepertest.SetAdminPolices(ctx, zk.AuthorityKeeper) + SetupStateForProcessLogsZetaSent(t, ctx, k, zk, sdkk, chain, admin) + amount, ok := sdkmath.NewIntFromString("20000000000000000000000") require.True(t, ok) err := sdkk.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sdk.NewCoins(sdk.NewCoin(config.BaseDenom, amount))) @@ -465,7 +476,8 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { chainID := chain.ChainId setSupportedChain(ctx, zk, chainID) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - SetupStateForProcessLogsZetaSent(t, ctx, k, zk, sdkk, chain) + admin := keepertest.SetAdminPolices(ctx, zk.AuthorityKeeper) + SetupStateForProcessLogsZetaSent(t, ctx, k, zk, sdkk, chain, admin) event, err := crosschainkeeper.ParseZetaSentEvent(*sample.GetValidZetaSentDestinationExternal(t).Logs[4], sample.GetValidZetaSentDestinationExternal(t).Logs[4].Address) require.NoError(t, err) @@ -483,7 +495,8 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { chain := chains.EthChain() SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - SetupStateForProcessLogsZetaSent(t, ctx, k, zk, sdkk, chain) + admin := keepertest.SetAdminPolices(ctx, zk.AuthorityKeeper) + SetupStateForProcessLogsZetaSent(t, ctx, k, zk, sdkk, chain, admin) amount, ok := sdkmath.NewIntFromString("20000000000000000000000") require.True(t, ok) @@ -507,7 +520,8 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { chainID := chain.ChainId setSupportedChain(ctx, zk, chainID) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - SetupStateForProcessLogsZetaSent(t, ctx, k, zk, sdkk, chain) + admin := keepertest.SetAdminPolices(ctx, zk.AuthorityKeeper) + SetupStateForProcessLogsZetaSent(t, ctx, k, zk, sdkk, chain, admin) amount, ok := sdkmath.NewIntFromString("20000000000000000000000") require.True(t, ok) @@ -556,7 +570,8 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { setSupportedChain(ctx, zk, chainID) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - SetupStateForProcessLogsZetaSent(t, ctx, k, zk, sdkk, chain) + admin := keepertest.SetAdminPolices(ctx, zk.AuthorityKeeper) + SetupStateForProcessLogsZetaSent(t, ctx, k, zk, sdkk, chain, admin) zk.ObserverKeeper.SetChainNonces(ctx, observertypes.ChainNonces{ Index: chain.ChainName.String(), @@ -613,7 +628,8 @@ func TestKeeper_ProcessLogs(t *testing.T) { chainID := chain.ChainId setSupportedChain(ctx, zk, chainID) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - SetupStateForProcessLogsZetaSent(t, ctx, k, zk, sdkk, chain) + admin := keepertest.SetAdminPolices(ctx, zk.AuthorityKeeper) + SetupStateForProcessLogsZetaSent(t, ctx, k, zk, sdkk, chain, admin) amount, ok := sdkmath.NewIntFromString("20000000000000000000000") require.True(t, ok) diff --git a/x/crosschain/keeper/gas_payment.go b/x/crosschain/keeper/gas_payment.go index 6a9b957f16..fe51187199 100644 --- a/x/crosschain/keeper/gas_payment.go +++ b/x/crosschain/keeper/gas_payment.go @@ -14,7 +14,7 @@ import ( "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/x/crosschain/types" fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" - zetaObserverTypes "github.com/zeta-chain/zetacore/x/observer/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) // PayGasAndUpdateCctx updates the outbound tx with the new amount after paying the gas fee @@ -94,7 +94,7 @@ func (k Keeper) PayGasNativeAndUpdateCctx( return cosmoserrors.Wrapf(types.ErrInvalidCoinType, "can't pay gas in native gas with %s", cctx.InboundTxParams.CoinType.String()) } if chain := k.zetaObserverKeeper.GetSupportedChainFromChainID(ctx, chainID); chain == nil { - return zetaObserverTypes.ErrSupportedChains + return observertypes.ErrSupportedChains } // get gas params @@ -142,16 +142,14 @@ func (k Keeper) PayGasInERC20AndUpdateCctx( } if chain := k.zetaObserverKeeper.GetSupportedChainFromChainID(ctx, chainID); chain == nil { - return zetaObserverTypes.ErrSupportedChains + return observertypes.ErrSupportedChains } - // get gas params gasZRC20, gasLimit, gasPrice, protocolFlatFee, err := k.ChainGasParams(ctx, chainID) if err != nil { return cosmoserrors.Wrap(types.ErrCannotFindGasParams, err.Error()) } outTxGasFee := gasLimit.Mul(gasPrice).Add(protocolFlatFee) - // get address of the zrc20 fc, found := k.fungibleKeeper.GetForeignCoinFromAsset(ctx, cctx.InboundTxParams.Asset, chainID) if !found { @@ -269,7 +267,7 @@ func (k Keeper) PayGasInZetaAndUpdateCctx( } if chain := k.zetaObserverKeeper.GetSupportedChainFromChainID(ctx, chainID); chain == nil { - return zetaObserverTypes.ErrSupportedChains + return observertypes.ErrSupportedChains } gasZRC20, err := k.fungibleKeeper.QuerySystemContractGasCoinZRC20(ctx, big.NewInt(chainID)) diff --git a/x/crosschain/keeper/gas_payment_test.go b/x/crosschain/keeper/gas_payment_test.go index 1ccb31c937..569a360327 100644 --- a/x/crosschain/keeper/gas_payment_test.go +++ b/x/crosschain/keeper/gas_payment_test.go @@ -53,6 +53,7 @@ func TestKeeper_PayGasNativeAndUpdateCctx(t *testing.T) { OutboundTxParams: []*types.OutboundTxParams{ { ReceiverChainId: chains.ZetaPrivnetChain().ChainId, + CoinType: coin.CoinType_Gas, }, { ReceiverChainId: chainID, @@ -142,7 +143,9 @@ func TestKeeper_PayGasNativeAndUpdateCctx(t *testing.T) { cctx := types.CrossChainTx{ InboundTxParams: &types.InboundTxParams{ - CoinType: coin.CoinType_Gas, + SenderChainId: chainID, + Sender: sample.EthAddress().String(), + CoinType: coin.CoinType_Gas, }, OutboundTxParams: []*types.OutboundTxParams{ { @@ -202,9 +205,10 @@ func TestKeeper_PayGasInERC20AndUpdateCctx(t *testing.T) { // create a cctx reverted from zeta cctx := types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ - CoinType: coin.CoinType_ERC20, Asset: assetAddress, + CoinType: coin.CoinType_ERC20, }, OutboundTxParams: []*types.OutboundTxParams{ { @@ -359,8 +363,8 @@ func TestKeeper_PayGasInERC20AndUpdateCctx(t *testing.T) { // create a cctx reverted from zeta cctx := types.CrossChainTx{ InboundTxParams: &types.InboundTxParams{ - CoinType: coin.CoinType_ERC20, Asset: assetAddress, + CoinType: coin.CoinType_ERC20, }, OutboundTxParams: []*types.OutboundTxParams{ { @@ -546,7 +550,9 @@ func TestKeeper_PayGasInZetaAndUpdateCctx(t *testing.T) { // create a cctx reverted from zeta cctx := types.CrossChainTx{ InboundTxParams: &types.InboundTxParams{ - CoinType: coin.CoinType_Zeta, + SenderChainId: chainID, + Sender: sample.EthAddress().String(), + CoinType: coin.CoinType_Zeta, }, OutboundTxParams: []*types.OutboundTxParams{ { @@ -578,7 +584,9 @@ func TestKeeper_PayGasInZetaAndUpdateCctx(t *testing.T) { // create a cctx reverted from zeta cctx := types.CrossChainTx{ InboundTxParams: &types.InboundTxParams{ - CoinType: coin.CoinType_Zeta, + SenderChainId: chainID, + Sender: sample.EthAddress().String(), + CoinType: coin.CoinType_Zeta, }, OutboundTxParams: []*types.OutboundTxParams{ { diff --git a/x/crosschain/keeper/msg_server_migrate_tss_funds.go b/x/crosschain/keeper/msg_server_migrate_tss_funds.go index 02ce1bf879..7fd1228400 100644 --- a/x/crosschain/keeper/msg_server_migrate_tss_funds.go +++ b/x/crosschain/keeper/msg_server_migrate_tss_funds.go @@ -82,6 +82,9 @@ func (k Keeper) MigrateTSSFundsForChain(ctx sdk.Context, chainID int64, amount s hash := crypto.Keccak256Hash([]byte(indexString)) index := hash.Hex() + + // TODO : Use the `NewCCTX` method to create the cctx + // https://github.com/zeta-chain/node/issues/1909 cctx := types.CrossChainTx{ Creator: "", Index: index, diff --git a/x/crosschain/keeper/msg_server_tss_voter.go b/x/crosschain/keeper/msg_server_tss_voter.go deleted file mode 100644 index a3782729b1..0000000000 --- a/x/crosschain/keeper/msg_server_tss_voter.go +++ /dev/null @@ -1,120 +0,0 @@ -package keeper - -import ( - "context" - "fmt" - - errorsmod "cosmossdk.io/errors" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - math2 "github.com/ethereum/go-ethereum/common/math" - "github.com/zeta-chain/zetacore/pkg/chains" - "github.com/zeta-chain/zetacore/x/crosschain/types" - "github.com/zeta-chain/zetacore/x/observer/keeper" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" -) - -// MESSAGES - -// CreateTSSVoter votes on creating a TSS key and recording the information about it (public -// key, participant and operator addresses, finalized and keygen heights). -// -// If the vote passes, the information about the TSS key is recorded on chain -// and the status of the keygen is set to "success". -// -// Fails if the keygen does not exist, the keygen has been already -// completed, or the keygen has failed. -// -// Only node accounts are authorized to broadcast this message. -func (k msgServer) CreateTSSVoter(goCtx context.Context, msg *types.MsgCreateTSSVoter) (*types.MsgCreateTSSVoterResponse, error) { - ctx := sdk.UnwrapSDKContext(goCtx) - - if !k.IsAuthorizedNodeAccount(ctx, msg.Creator) { - return nil, errorsmod.Wrap(sdkerrors.ErrorInvalidSigner, fmt.Sprintf("signer %s does not have a node account set", msg.Creator)) - } - // No need to create a ballot if keygen does not exist - keygen, found := k.zetaObserverKeeper.GetKeygen(ctx) - if !found { - return &types.MsgCreateTSSVoterResponse{}, observertypes.ErrKeygenNotFound - } - // USE a separate transaction to update KEYGEN status to pending when trying to change the TSS address - if keygen.Status == observertypes.KeygenStatus_KeyGenSuccess { - return &types.MsgCreateTSSVoterResponse{}, observertypes.ErrKeygenCompleted - } - index := msg.Digest() - // Add votes and Set Ballot - // GetBallot checks against the supported chains list before querying for Ballot - // TODO : https://github.com/zeta-chain/node/issues/896 - ballot, found := k.zetaObserverKeeper.GetBallot(ctx, index) - if !found { - var voterList []string - - for _, nodeAccount := range k.zetaObserverKeeper.GetAllNodeAccount(ctx) { - voterList = append(voterList, nodeAccount.Operator) - } - ballot = observertypes.Ballot{ - Index: "", - BallotIdentifier: index, - VoterList: voterList, - Votes: observertypes.CreateVotes(len(voterList)), - ObservationType: observertypes.ObservationType_TSSKeyGen, - BallotThreshold: sdk.MustNewDecFromStr("1.00"), - BallotStatus: observertypes.BallotStatus_BallotInProgress, - BallotCreationHeight: ctx.BlockHeight(), - } - k.zetaObserverKeeper.AddBallotToList(ctx, ballot) - } - var err error - if msg.Status == chains.ReceiveStatus_Success { - ballot, err = k.zetaObserverKeeper.AddVoteToBallot(ctx, ballot, msg.Creator, observertypes.VoteType_SuccessObservation) - if err != nil { - return &types.MsgCreateTSSVoterResponse{}, err - } - } else if msg.Status == chains.ReceiveStatus_Failed { - ballot, err = k.zetaObserverKeeper.AddVoteToBallot(ctx, ballot, msg.Creator, observertypes.VoteType_FailureObservation) - if err != nil { - return &types.MsgCreateTSSVoterResponse{}, err - } - } - if !found { - keeper.EmitEventBallotCreated(ctx, ballot, msg.TssPubkey, "Common-TSS-For-All-Chain") - } - - ballot, isFinalized := k.zetaObserverKeeper.CheckIfFinalizingVote(ctx, ballot) - if !isFinalized { - // Return nil here to add vote to ballot and commit state - return &types.MsgCreateTSSVoterResponse{}, nil - } - // Set TSS only on success, set Keygen either way. - // Keygen block can be updated using a policy transaction if keygen fails - if ballot.BallotStatus != observertypes.BallotStatus_BallotFinalized_FailureObservation { - tss := observertypes.TSS{ - TssPubkey: msg.TssPubkey, - TssParticipantList: keygen.GetGranteePubkeys(), - OperatorAddressList: ballot.VoterList, - FinalizedZetaHeight: ctx.BlockHeight(), - KeyGenZetaHeight: msg.KeyGenZetaHeight, - } - // Set TSS history only, current TSS is updated via admin transaction - // In Case this is the first TSS address update both current and history - tssList := k.zetaObserverKeeper.GetAllTSS(ctx) - if len(tssList) == 0 { - k.GetObserverKeeper().SetTssAndUpdateNonce(ctx, tss) - } - k.zetaObserverKeeper.SetTSSHistory(ctx, tss) - keygen.Status = observertypes.KeygenStatus_KeyGenSuccess - keygen.BlockNumber = ctx.BlockHeight() - - } else if ballot.BallotStatus == observertypes.BallotStatus_BallotFinalized_FailureObservation { - keygen.Status = observertypes.KeygenStatus_KeyGenFailed - keygen.BlockNumber = math2.MaxInt64 - } - k.zetaObserverKeeper.SetKeygen(ctx, keygen) - return &types.MsgCreateTSSVoterResponse{}, nil -} - -// IsAuthorizedNodeAccount checks whether a signer is authorized to sign , by checking their address against the observer mapper which contains the observer list for the chain and type -func (k Keeper) IsAuthorizedNodeAccount(ctx sdk.Context, address string) bool { - _, found := k.zetaObserverKeeper.GetNodeAccount(ctx, address) - return found -} diff --git a/x/crosschain/keeper/msg_server_vote_inbound_tx.go b/x/crosschain/keeper/msg_server_vote_inbound_tx.go index 4fee3a2c9d..5d36965647 100644 --- a/x/crosschain/keeper/msg_server_vote_inbound_tx.go +++ b/x/crosschain/keeper/msg_server_vote_inbound_tx.go @@ -6,7 +6,6 @@ import ( cosmoserrors "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/x/crosschain/types" ) @@ -85,127 +84,44 @@ func (k msgServer) VoteOnObservedInboundTx(goCtx context.Context, msg *types.Msg } } commit() - // If the ballot is not finalized return nil here to add vote to commit state if !finalized { return &types.MsgVoteOnObservedInboundTxResponse{}, nil } - - // get the latest TSS to set the TSS public key in the CCTX - tssPub := "" tss, tssFound := k.zetaObserverKeeper.GetTSS(ctx) - if tssFound { - tssPub = tss.TssPubkey + if !tssFound { + return nil, types.ErrCannotFindTSSKeys } - - // create the CCTX - cctx := k.CreateNewCCTX( - ctx, - msg, - index, - tssPub, - types.CctxStatus_PendingInbound, - msg.SenderChainId, - msg.ReceiverChain, - ) - - defer func() { - EmitEventInboundFinalized(ctx, &cctx) - k.AddFinalizedInbound(ctx, msg.InTxHash, msg.SenderChainId, msg.EventIndex) - // #nosec G701 always positive - cctx.InboundTxParams.InboundTxFinalizedZetaHeight = uint64(ctx.BlockHeight()) - cctx.InboundTxParams.TxFinalizationStatus = types.TxFinalizationStatus_Executed - k.RemoveInTxTrackerIfExists(ctx, cctx.InboundTxParams.SenderChainId, cctx.InboundTxParams.InboundTxObservedHash) - k.SetCctxAndNonceToCctxAndInTxHashToCctx(ctx, cctx) - }() - - // FinalizeInbound updates CCTX Prices and Nonce - // Aborts is any of the updates fail - if chains.IsZetaChain(msg.ReceiverChain) { - tmpCtx, commit := ctx.CacheContext() - isContractReverted, err := k.HandleEVMDeposit(tmpCtx, &cctx, *msg, msg.SenderChainId) - - if err != nil && !isContractReverted { // exceptional case; internal error; should abort CCTX - cctx.CctxStatus.ChangeStatus(types.CctxStatus_Aborted, err.Error()) - return &types.MsgVoteOnObservedInboundTxResponse{}, nil - } else if err != nil && isContractReverted { // contract call reverted; should refund - revertMessage := err.Error() - chain := k.zetaObserverKeeper.GetSupportedChainFromChainID(ctx, cctx.InboundTxParams.SenderChainId) - if chain == nil { - cctx.CctxStatus.ChangeStatus(types.CctxStatus_Aborted, "invalid sender chain") - return &types.MsgVoteOnObservedInboundTxResponse{}, nil - } - - gasLimit, err := k.GetRevertGasLimit(ctx, cctx) - if err != nil { - cctx.CctxStatus.ChangeStatus(types.CctxStatus_Aborted, "can't get revert tx gas limit"+err.Error()) - return &types.MsgVoteOnObservedInboundTxResponse{}, nil - } - if gasLimit == 0 { - // use same gas limit of outbound as a fallback -- should not happen - gasLimit = msg.GasLimit - } - - // create new OutboundTxParams for the revert - revertTxParams := &types.OutboundTxParams{ - Receiver: cctx.InboundTxParams.Sender, - ReceiverChainId: cctx.InboundTxParams.SenderChainId, - Amount: cctx.InboundTxParams.Amount, - CoinType: cctx.InboundTxParams.CoinType, - OutboundTxGasLimit: gasLimit, - } - cctx.OutboundTxParams = append(cctx.OutboundTxParams, revertTxParams) - - // we create a new cached context, and we don't commit the previous one with EVM deposit - tmpCtx, commit := ctx.CacheContext() - err = func() error { - err := k.PayGasAndUpdateCctx( - tmpCtx, - chain.ChainId, - &cctx, - cctx.InboundTxParams.Amount, - false, - ) - if err != nil { - return err - } - return k.UpdateNonce(tmpCtx, chain.ChainId, &cctx) - }() - if err != nil { - cctx.CctxStatus.ChangeStatus(types.CctxStatus_Aborted, err.Error()+" deposit revert message: "+revertMessage) - return &types.MsgVoteOnObservedInboundTxResponse{}, nil - } - commit() - cctx.CctxStatus.ChangeStatus(types.CctxStatus_PendingRevert, revertMessage) - return &types.MsgVoteOnObservedInboundTxResponse{}, nil - } - // successful HandleEVMDeposit; - commit() - cctx.CctxStatus.ChangeStatus(types.CctxStatus_OutboundMined, "Remote omnichain contract call completed") - return &types.MsgVoteOnObservedInboundTxResponse{}, nil - } - - // Receiver is not ZetaChain: Cross Chain SWAP - tmpCtx, commit = ctx.CacheContext() - err = func() error { - err := k.PayGasAndUpdateCctx( - tmpCtx, - msg.ReceiverChain, - &cctx, - cctx.InboundTxParams.Amount, - false, - ) - if err != nil { - return err - } - return k.UpdateNonce(tmpCtx, msg.ReceiverChain, &cctx) - }() + // create a new CCTX from the inbound message.The status of the new CCTX is set to PendingInbound. + cctx, err := types.NewCCTX(ctx, *msg, tss.TssPubkey) if err != nil { - // do not commit anything here as the CCTX should be aborted - cctx.CctxStatus.ChangeStatus(types.CctxStatus_Aborted, err.Error()) - return &types.MsgVoteOnObservedInboundTxResponse{}, nil + return nil, err } - commit() - cctx.CctxStatus.ChangeStatus(types.CctxStatus_PendingOutbound, "") + // Process the inbound CCTX, the process function manages the state commit and cctx status change. + // If the process fails, the changes to the evm state are rolled back. + k.ProcessInbound(ctx, &cctx) + // Save the inbound CCTX to the store. This is called irrespective of the status of the CCTX or the outcome of the process function. + k.SaveInbound(ctx, &cctx, msg.EventIndex) return &types.MsgVoteOnObservedInboundTxResponse{}, nil } + +/* SaveInbound saves the inbound CCTX to the store.It does the following: + - Emits an event for the finalized inbound CCTX. + - Adds the inbound CCTX to the finalized inbound CCTX store.This is done to prevent double spending, using the same inbound tx hash and event index. + - Updates the CCTX with the finalized height and finalization status. + - Removes the inbound CCTX from the inbound transaction tracker store.This is only for inbounds created via InTx tracker suggestions + - Sets the CCTX and nonce to the CCTX and inbound transaction hash to CCTX store. +*/ + +func (k Keeper) SaveInbound(ctx sdk.Context, cctx *types.CrossChainTx, eventIndex uint64) { + EmitEventInboundFinalized(ctx, cctx) + k.AddFinalizedInbound(ctx, + cctx.GetInboundTxParams().InboundTxObservedHash, + cctx.GetInboundTxParams().SenderChainId, + eventIndex) + // #nosec G701 always positive + cctx.InboundTxParams.InboundTxFinalizedZetaHeight = uint64(ctx.BlockHeight()) + cctx.InboundTxParams.TxFinalizationStatus = types.TxFinalizationStatus_Executed + k.RemoveInTxTrackerIfExists(ctx, cctx.InboundTxParams.SenderChainId, cctx.InboundTxParams.InboundTxObservedHash) + k.SetCctxAndNonceToCctxAndInTxHashToCctx(ctx, *cctx) +} diff --git a/x/crosschain/keeper/msg_server_vote_inbound_tx_test.go b/x/crosschain/keeper/msg_server_vote_inbound_tx_test.go index fa51c66bfa..25d5615056 100644 --- a/x/crosschain/keeper/msg_server_vote_inbound_tx_test.go +++ b/x/crosschain/keeper/msg_server_vote_inbound_tx_test.go @@ -2,15 +2,19 @@ package keeper_test import ( "encoding/hex" + "math/big" "testing" sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" + ethcommon "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" "github.com/zeta-chain/zetacore/x/crosschain/keeper" + "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) @@ -51,6 +55,8 @@ func TestKeeper_VoteOnObservedInboundTx(t *testing.T) { to = chain.ChainId } } + zk.ObserverKeeper.SetTSS(ctx, sample.Tss()) + msg := sample.InboundVote(0, from, to) for _, validatorAddr := range validatorList { msg.Creator = validatorAddr @@ -97,6 +103,9 @@ func TestKeeper_VoteOnObservedInboundTx(t *testing.T) { ObserverList: []string{validatorAddr}, }) + // Add tss to the observer keeper + zk.ObserverKeeper.SetTSS(ctx, sample.Tss()) + // Vote on the FIRST message. msg := &types.MsgVoteOnObservedInboundTx{ Creator: validatorAddr, @@ -199,3 +208,79 @@ func TestStatus_ChangeStatus(t *testing.T) { }) } } + +func TestKeeper_SaveInbound(t *testing.T) { + t.Run("should save the cctx", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + receiver := sample.EthAddress() + amount := big.NewInt(42) + senderChain := getValidEthChain(t) + cctx := GetERC20Cctx(t, receiver, *senderChain, "", amount) + eventIndex := sample.Uint64InRange(1, 100) + k.SaveInbound(ctx, cctx, eventIndex) + require.Equal(t, types.TxFinalizationStatus_Executed, cctx.InboundTxParams.TxFinalizationStatus) + require.True(t, k.IsFinalizedInbound(ctx, cctx.GetInboundTxParams().InboundTxObservedHash, cctx.GetInboundTxParams().SenderChainId, eventIndex)) + _, found := k.GetCrossChainTx(ctx, cctx.Index) + require.True(t, found) + }) + + t.Run("should save the cctx and remove tracker", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + receiver := sample.EthAddress() + amount := big.NewInt(42) + senderChain := getValidEthChain(t) + cctx := GetERC20Cctx(t, receiver, *senderChain, "", amount) + hash := sample.Hash() + cctx.InboundTxParams.InboundTxObservedHash = hash.String() + k.SetInTxTracker(ctx, types.InTxTracker{ + ChainId: senderChain.ChainId, + TxHash: hash.String(), + CoinType: 0, + }) + eventIndex := sample.Uint64InRange(1, 100) + + k.SaveInbound(ctx, cctx, eventIndex) + require.Equal(t, types.TxFinalizationStatus_Executed, cctx.InboundTxParams.TxFinalizationStatus) + require.True(t, k.IsFinalizedInbound(ctx, cctx.GetInboundTxParams().InboundTxObservedHash, cctx.GetInboundTxParams().SenderChainId, eventIndex)) + _, found := k.GetCrossChainTx(ctx, cctx.Index) + require.True(t, found) + _, found = k.GetInTxTracker(ctx, senderChain.ChainId, hash.String()) + require.False(t, found) + }) +} + +// GetERC20Cctx returns a sample CrossChainTx with ERC20 params. This is used for testing Inbound and Outbound voting transactions +func GetERC20Cctx(t *testing.T, receiver ethcommon.Address, senderChain chains.Chain, asset string, amount *big.Int) *types.CrossChainTx { + r := sample.Rand() + cctx := &types.CrossChainTx{ + Creator: sample.AccAddress(), + Index: sample.ZetaIndex(t), + ZetaFees: sample.UintInRange(0, 100), + RelayedMessage: "", + CctxStatus: &types.Status{Status: types.CctxStatus_PendingInbound}, + InboundTxParams: sample.InboundTxParams(r), + OutboundTxParams: []*types.OutboundTxParams{sample.OutboundTxParams(r)}, + } + + cctx.GetInboundTxParams().Amount = sdkmath.NewUintFromBigInt(amount) + cctx.GetInboundTxParams().SenderChainId = senderChain.ChainId + cctx.GetInboundTxParams().InboundTxObservedHash = sample.Hash().String() + cctx.GetInboundTxParams().InboundTxBallotIndex = sample.ZetaIndex(t) + + cctx.GetCurrentOutTxParam().ReceiverChainId = senderChain.ChainId + cctx.GetCurrentOutTxParam().Receiver = receiver.String() + cctx.GetCurrentOutTxParam().OutboundTxHash = sample.Hash().String() + cctx.GetCurrentOutTxParam().OutboundTxBallotIndex = sample.ZetaIndex(t) + + cctx.InboundTxParams.CoinType = coin.CoinType_ERC20 + for _, outboundTxParam := range cctx.OutboundTxParams { + outboundTxParam.CoinType = coin.CoinType_ERC20 + } + + cctx.GetInboundTxParams().Asset = asset + cctx.GetInboundTxParams().Sender = sample.EthAddress().String() + cctx.GetCurrentOutTxParam().OutboundTxTssNonce = 42 + cctx.GetCurrentOutTxParam().OutboundTxGasUsed = 100 + cctx.GetCurrentOutTxParam().OutboundTxEffectiveGasLimit = 100 + return cctx +} diff --git a/x/crosschain/keeper/msg_server_vote_outbound_tx.go b/x/crosschain/keeper/msg_server_vote_outbound_tx.go index 9286f0865a..22afff1218 100644 --- a/x/crosschain/keeper/msg_server_vote_outbound_tx.go +++ b/x/crosschain/keeper/msg_server_vote_outbound_tx.go @@ -2,21 +2,15 @@ package keeper import ( "context" - "errors" "fmt" "math/big" cosmoserrors "cosmossdk.io/errors" "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/rs/zerolog/log" - "github.com/zeta-chain/zetacore/pkg/chains" - "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/x/crosschain/types" observerkeeper "github.com/zeta-chain/zetacore/x/observer/keeper" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) // VoteOnObservedOutboundTx casts a vote on an outbound transaction observed on a connected chain (after @@ -63,20 +57,15 @@ import ( func (k msgServer) VoteOnObservedOutboundTx(goCtx context.Context, msg *types.MsgVoteOnObservedOutboundTx) (*types.MsgVoteOnObservedOutboundTxResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - // check if CCTX exists and if the nonce matches - cctx, found := k.GetCrossChainTx(ctx, msg.CctxHash) - if !found { - return nil, cosmoserrors.Wrap(sdkerrors.ErrInvalidRequest, fmt.Sprintf("CCTX %s does not exist", msg.CctxHash)) - } - if cctx.GetCurrentOutTxParam().OutboundTxTssNonce != msg.OutTxTssNonce { - return nil, cosmoserrors.Wrap(sdkerrors.ErrInvalidRequest, fmt.Sprintf("OutTxTssNonce %d does not match CCTX OutTxTssNonce %d", msg.OutTxTssNonce, cctx.GetCurrentOutTxParam().OutboundTxTssNonce)) + // Validate the message params to verify it against an existing cctx + cctx, err := k.ValidateOutboundMessage(ctx, *msg) + if err != nil { + return nil, err } - // get ballot index ballotIndex := msg.Digest() - // vote on outbound ballot - isFinalized, isNew, ballot, observationChain, err := k.zetaObserverKeeper.VoteOnOutboundBallot( + isFinalizingVote, isNew, ballot, observationChain, err := k.zetaObserverKeeper.VoteOnOutboundBallot( ctx, ballotIndex, msg.OutTxChain, @@ -85,141 +74,41 @@ func (k msgServer) VoteOnObservedOutboundTx(goCtx context.Context, msg *types.Ms if err != nil { return nil, err } - // if the ballot is new, set the index to the CCTX if isNew { observerkeeper.EmitEventBallotCreated(ctx, ballot, msg.ObservedOutTxHash, observationChain) - // Set this the first time when the ballot is created - // The ballot might change if there are more votes in a different outbound ballot for this cctx hash - cctx.GetCurrentOutTxParam().OutboundTxBallotIndex = ballotIndex } - // if not finalized commit state here - if !isFinalized { + if !isFinalizingVote { return &types.MsgVoteOnObservedOutboundTxResponse{}, nil } // if ballot successful, the value received should be the out tx amount - if ballot.BallotStatus != observertypes.BallotStatus_BallotFinalized_FailureObservation { - if !msg.ValueReceived.Equal(cctx.GetCurrentOutTxParam().Amount) { - log.Error().Msgf("VoteOnObservedOutboundTx: Mint mismatch: %s value received vs %s cctx amount", - msg.ValueReceived, - cctx.GetCurrentOutTxParam().Amount) - return nil, cosmoserrors.Wrap(sdkerrors.ErrInvalidRequest, fmt.Sprintf("ValueReceived %s does not match sent value %s", msg.ValueReceived, cctx.GetCurrentOutTxParam().Amount)) - } + err = cctx.AddOutbound(ctx, *msg, ballot.BallotStatus) + if err != nil { + return nil, err } - - // Update CCTX values - cctx.GetCurrentOutTxParam().OutboundTxHash = msg.ObservedOutTxHash - cctx.GetCurrentOutTxParam().OutboundTxGasUsed = msg.ObservedOutTxGasUsed - cctx.GetCurrentOutTxParam().OutboundTxEffectiveGasPrice = msg.ObservedOutTxEffectiveGasPrice - cctx.GetCurrentOutTxParam().OutboundTxEffectiveGasLimit = msg.ObservedOutTxEffectiveGasLimit - cctx.CctxStatus.LastUpdateTimestamp = ctx.BlockHeader().Time.Unix() - // Fund the gas stability pool with the remaining funds - if err := k.FundGasStabilityPoolFromRemainingFees(ctx, *cctx.GetCurrentOutTxParam(), msg.OutTxChain); err != nil { - log.Error().Msgf( - "VoteOnObservedOutboundTx: CCTX: %s Can't fund the gas stability pool with remaining fees %s", cctx.Index, err.Error(), - ) - } + k.FundStabilityPool(ctx, &cctx) - tss, found := k.zetaObserverKeeper.GetTSS(ctx) - if !found { - return nil, types.ErrCannotFindTSSKeys - } - - tmpCtx, commit := ctx.CacheContext() - err = func() error { //err = FinalizeOutbound(k, ctx, &cctx, msg, ballot.BallotStatus) - cctx.GetCurrentOutTxParam().OutboundTxObservedExternalHeight = msg.ObservedOutTxBlockHeight - oldStatus := cctx.CctxStatus.Status - switch ballot.BallotStatus { - case observertypes.BallotStatus_BallotFinalized_SuccessObservation: - switch oldStatus { - case types.CctxStatus_PendingRevert: - cctx.CctxStatus.ChangeStatus(types.CctxStatus_Reverted, "") - case types.CctxStatus_PendingOutbound: - cctx.CctxStatus.ChangeStatus(types.CctxStatus_OutboundMined, "") - } - newStatus := cctx.CctxStatus.Status.String() - EmitOutboundSuccess(tmpCtx, msg, oldStatus.String(), newStatus, cctx) - case observertypes.BallotStatus_BallotFinalized_FailureObservation: - if msg.CoinType == coin.CoinType_Cmd || chains.IsZetaChain(cctx.InboundTxParams.SenderChainId) { - // if the cctx is of coin type cmd or the sender chain is zeta chain, then we do not revert, the cctx is aborted - cctx.CctxStatus.ChangeStatus(types.CctxStatus_Aborted, "") - } else { - switch oldStatus { - case types.CctxStatus_PendingOutbound: - - gasLimit, err := k.GetRevertGasLimit(ctx, cctx) - if err != nil { - return errors.New("can't get revert tx gas limit" + err.Error()) - } - if gasLimit == 0 { - // use same gas limit of outbound as a fallback -- should not happen - gasLimit = cctx.OutboundTxParams[0].OutboundTxGasLimit - } - - // create new OutboundTxParams for the revert - revertTxParams := &types.OutboundTxParams{ - Receiver: cctx.InboundTxParams.Sender, - ReceiverChainId: cctx.InboundTxParams.SenderChainId, - Amount: cctx.InboundTxParams.Amount, - CoinType: cctx.InboundTxParams.CoinType, - OutboundTxGasLimit: gasLimit, - } - cctx.OutboundTxParams = append(cctx.OutboundTxParams, revertTxParams) - - err = k.PayGasAndUpdateCctx( - tmpCtx, - cctx.InboundTxParams.SenderChainId, - &cctx, - cctx.OutboundTxParams[0].Amount, - false, - ) - if err != nil { - return err - } - err = k.UpdateNonce(tmpCtx, cctx.InboundTxParams.SenderChainId, &cctx) - if err != nil { - return err - } - cctx.CctxStatus.ChangeStatus(types.CctxStatus_PendingRevert, "Outbound failed, start revert") - case types.CctxStatus_PendingRevert: - cctx.CctxStatus.ChangeStatus(types.CctxStatus_Aborted, "Outbound failed: revert failed; abort TX") - } - } - newStatus := cctx.CctxStatus.Status.String() - EmitOutboundFailure(ctx, msg, oldStatus.String(), newStatus, cctx) - } - return nil - }() + err = k.ProcessOutbound(ctx, &cctx, ballot.BallotStatus, msg.ValueReceived.String()) if err != nil { - // do not commit tmpCtx - cctx.CctxStatus.ChangeStatus(types.CctxStatus_Aborted, err.Error()) - cctx.GetCurrentOutTxParam().TxFinalizationStatus = types.TxFinalizationStatus_Executed - ctx.Logger().Error(err.Error()) - // #nosec G701 always in range - k.GetObserverKeeper().RemoveFromPendingNonces(ctx, tss.TssPubkey, msg.OutTxChain, int64(msg.OutTxTssNonce)) - k.RemoveOutTxTracker(ctx, msg.OutTxChain, msg.OutTxTssNonce) - k.SetCctxAndNonceToCctxAndInTxHashToCctx(ctx, cctx) + k.SaveFailedOutbound(ctx, &cctx, err.Error(), ballotIndex) return &types.MsgVoteOnObservedOutboundTxResponse{}, nil } - commit() - // Set the ballot index to the finalized ballot - cctx.GetCurrentOutTxParam().OutboundTxBallotIndex = ballotIndex - cctx.GetCurrentOutTxParam().TxFinalizationStatus = types.TxFinalizationStatus_Executed - // #nosec G701 always in range - k.GetObserverKeeper().RemoveFromPendingNonces(ctx, tss.TssPubkey, msg.OutTxChain, int64(msg.OutTxTssNonce)) - k.RemoveOutTxTracker(ctx, msg.OutTxChain, msg.OutTxTssNonce) - ctx.Logger().Info(fmt.Sprintf("Remove tracker %s: , Block Height : %d ", getOutTrackerIndex(msg.OutTxChain, msg.OutTxTssNonce), ctx.BlockHeight())) - k.SetCctxAndNonceToCctxAndInTxHashToCctx(ctx, cctx) + k.SaveSuccessfulOutbound(ctx, &cctx, ballotIndex) return &types.MsgVoteOnObservedOutboundTxResponse{}, nil } -func percentOf(n *big.Int, percent int64) *big.Int { - n = n.Mul(n, big.NewInt(percent)) - n = n.Div(n, big.NewInt(100)) - return n +// FundStabilityPool funds the stability pool with the remaining fees of an outbound tx +// The funds are sent to the gas stability pool associated with the receiver chain +// This wraps the FundGasStabilityPoolFromRemainingFees function and logs an error if it fails.We do not return an error here. +// Event if the funding fails, the outbound tx is still processed. +func (k Keeper) FundStabilityPool(ctx sdk.Context, cctx *types.CrossChainTx) { + // Fund the gas stability pool with the remaining funds + if err := k.FundGasStabilityPoolFromRemainingFees(ctx, *cctx.GetCurrentOutTxParam(), cctx.GetCurrentOutTxParam().ReceiverChainId); err != nil { + ctx.Logger().Error(fmt.Sprintf("VoteOnObservedOutboundTx: CCTX: %s Can't fund the gas stability pool with remaining fees %s", cctx.Index, err.Error())) + } } // FundGasStabilityPoolFromRemainingFees funds the gas stability pool with the remaining fees of an outbound tx @@ -240,7 +129,6 @@ func (k Keeper) FundGasStabilityPoolFromRemainingFees(ctx sdk.Context, outboundT // We fund the stability pool with a portion of the remaining fees remainingFees = percentOf(remainingFees, RemainingFeesToStabilityPoolPercent) - // Fund the gas stability pool if err := k.fungibleKeeper.FundGasStabilityPool(ctx, chainID, remainingFees); err != nil { return err @@ -251,3 +139,74 @@ func (k Keeper) FundGasStabilityPoolFromRemainingFees(ctx sdk.Context, outboundT } return nil } + +// percentOf returns the percentage of a number +func percentOf(n *big.Int, percent int64) *big.Int { + n = n.Mul(n, big.NewInt(percent)) + n = n.Div(n, big.NewInt(100)) + return n +} + +/* +SaveFailedOutbound saves a failed outbound transaction.It does the following things in one function: + + 1. Change the status of the CCTX to Aborted + + 2. Save the outbound +*/ +func (k Keeper) SaveFailedOutbound(ctx sdk.Context, cctx *types.CrossChainTx, errMessage string, ballotIndex string) { + cctx.SetAbort(errMessage) + ctx.Logger().Error(errMessage) + + k.SaveOutbound(ctx, cctx, ballotIndex) +} + +// SaveSuccessfulOutbound saves a successful outbound transaction. +func (k Keeper) SaveSuccessfulOutbound(ctx sdk.Context, cctx *types.CrossChainTx, ballotIndex string) { + k.SaveOutbound(ctx, cctx, ballotIndex) +} + +/* +SaveOutbound saves the outbound transaction.It does the following things in one function: + + 1. Set the ballot index for the outbound vote to the cctx + + 2. Remove the nonce from the pending nonces + + 3. Remove the outbound tx tracker + + 4. Set the cctx and nonce to cctx and inTxHash to cctx +*/ +func (k Keeper) SaveOutbound(ctx sdk.Context, cctx *types.CrossChainTx, ballotIndex string) { + receiverChain := cctx.GetCurrentOutTxParam().ReceiverChainId + tssPubkey := cctx.GetCurrentOutTxParam().TssPubkey + outTxTssNonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce + + cctx.GetCurrentOutTxParam().OutboundTxBallotIndex = ballotIndex + // #nosec G701 always in range + k.GetObserverKeeper().RemoveFromPendingNonces(ctx, tssPubkey, receiverChain, int64(outTxTssNonce)) + k.RemoveOutTxTracker(ctx, receiverChain, outTxTssNonce) + ctx.Logger().Info(fmt.Sprintf("Remove tracker %s: , Block Height : %d ", getOutTrackerIndex(receiverChain, outTxTssNonce), ctx.BlockHeight())) + // This should set nonce to cctx only if a new revert is created. + k.SetCctxAndNonceToCctxAndInTxHashToCctx(ctx, *cctx) +} + +func (k Keeper) ValidateOutboundMessage(ctx sdk.Context, msg types.MsgVoteOnObservedOutboundTx) (types.CrossChainTx, error) { + // check if CCTX exists and if the nonce matches + cctx, found := k.GetCrossChainTx(ctx, msg.CctxHash) + if !found { + return types.CrossChainTx{}, cosmoserrors.Wrap(sdkerrors.ErrInvalidRequest, fmt.Sprintf("CCTX %s does not exist", msg.CctxHash)) + } + if cctx.GetCurrentOutTxParam().OutboundTxTssNonce != msg.OutTxTssNonce { + return types.CrossChainTx{}, cosmoserrors.Wrap(sdkerrors.ErrInvalidRequest, fmt.Sprintf("OutTxTssNonce %d does not match CCTX OutTxTssNonce %d", msg.OutTxTssNonce, cctx.GetCurrentOutTxParam().OutboundTxTssNonce)) + } + // do not process an outbound vote if TSS is not found + _, found = k.zetaObserverKeeper.GetTSS(ctx) + if !found { + return types.CrossChainTx{}, types.ErrCannotFindTSSKeys + } + if cctx.GetCurrentOutTxParam().ReceiverChainId != msg.OutTxChain { + return types.CrossChainTx{}, cosmoserrors.Wrap(sdkerrors.ErrInvalidRequest, fmt.Sprintf("OutTxChain %d does not match CCTX OutTxChain %d", msg.OutTxChain, cctx.GetCurrentOutTxParam().ReceiverChainId)) + } + return cctx, nil +} diff --git a/x/crosschain/keeper/msg_server_vote_outbound_tx_test.go b/x/crosschain/keeper/msg_server_vote_outbound_tx_test.go index b5ad6ab78d..511e703acd 100644 --- a/x/crosschain/keeper/msg_server_vote_outbound_tx_test.go +++ b/x/crosschain/keeper/msg_server_vote_outbound_tx_test.go @@ -2,15 +2,22 @@ package keeper_test import ( "errors" + "fmt" "math/big" "math/rand" "testing" "cosmossdk.io/math" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - testkeeper "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/pkg/chains" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" "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" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) func TestKeeper_FundGasStabilityPoolFromRemainingFees(t *testing.T) { @@ -85,8 +92,8 @@ func TestKeeper_FundGasStabilityPoolFromRemainingFees(t *testing.T) { for _, tc := range tt { tc := tc t.Run(tc.name, func(t *testing.T) { - k, ctx := testkeeper.CrosschainKeeperAllMocks(t) - fungibleMock := testkeeper.GetCrosschainFungibleMock(t, k) + k, ctx := keepertest.CrosschainKeeperAllMocks(t) + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) // OutboundTxParams outbound := sample.OutboundTxParams(r) @@ -111,3 +118,466 @@ func TestKeeper_FundGasStabilityPoolFromRemainingFees(t *testing.T) { }) } } + +func TestKeeper_VoteOnObservedOutboundTx(t *testing.T) { + t.Run("successfully vote on outbound tx with status pending outbound ,vote-type success", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + + // Setup mock data + observerMock := keepertest.GetCrosschainObserverMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + senderChain := getValidEthChain(t) + asset := "" + observer := sample.AccAddress() + tss := sample.Tss() + zk.ObserverKeeper.SetObserverSet(ctx, observertypes.ObserverSet{ObserverList: []string{observer}}) + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + cctx.GetCurrentOutTxParam().TssPubkey = tss.TssPubkey + cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound + k.SetCrossChainTx(ctx, *cctx) + observerMock.On("GetTSS", ctx).Return(observertypes.TSS{}, true).Once() + + // Successfully mock VoteOnOutboundBallot + keepertest.MockVoteOnOutboundSuccessBallot(observerMock, ctx, cctx, *senderChain, observer) + + // Successfully mock GetOutBound + keepertest.MockGetOutBound(observerMock, ctx) + + // Successfully mock SaveSuccessfulOutbound + keepertest.MockSaveOutBound(observerMock, ctx, cctx, tss) + + msgServer := keeper.NewMsgServerImpl(*k) + _, err := msgServer.VoteOnObservedOutboundTx(ctx, &types.MsgVoteOnObservedOutboundTx{ + CctxHash: cctx.Index, + OutTxTssNonce: cctx.GetCurrentOutTxParam().OutboundTxTssNonce, + OutTxChain: cctx.GetCurrentOutTxParam().ReceiverChainId, + Status: chains.ReceiveStatus_Success, + Creator: observer, + ObservedOutTxHash: sample.Hash().String(), + ValueReceived: cctx.GetCurrentOutTxParam().Amount, + ObservedOutTxBlockHeight: 10, + ObservedOutTxEffectiveGasPrice: math.NewInt(21), + ObservedOutTxGasUsed: 21, + CoinType: cctx.InboundTxParams.CoinType, + }) + require.NoError(t, err) + c, found := k.GetCrossChainTx(ctx, cctx.Index) + require.True(t, found) + require.Equal(t, types.CctxStatus_OutboundMined, c.CctxStatus.Status) + }) + + t.Run("successfully vote on outbound tx, vote-type failed", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + UseFungibleMock: true, + }) + + // Setup mock data + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + senderChain := getValidEthChain(t) + asset := "" + observer := sample.AccAddress() + tss := sample.Tss() + zk.ObserverKeeper.SetObserverSet(ctx, observertypes.ObserverSet{ObserverList: []string{observer}}) + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + cctx.GetCurrentOutTxParam().TssPubkey = tss.TssPubkey + cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound + k.SetCrossChainTx(ctx, *cctx) + observerMock.On("GetTSS", ctx).Return(observertypes.TSS{}, true).Once() + + // Successfully mock VoteOnOutboundBallot + keepertest.MockVoteOnOutboundFailedBallot(observerMock, ctx, cctx, *senderChain, observer) + + // Successfully mock GetOutBound + keepertest.MockGetOutBound(observerMock, ctx) + + // Successfully mock ProcessOutbound + keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain) + keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *senderChain, asset) + _ = keepertest.MockUpdateNonce(observerMock, *senderChain) + + //Successfully mock SaveOutBound + keepertest.MockSaveOutBoundNewRevertCreated(observerMock, ctx, cctx, tss) + oldParamsLen := len(cctx.OutboundTxParams) + msgServer := keeper.NewMsgServerImpl(*k) + _, err := msgServer.VoteOnObservedOutboundTx(ctx, &types.MsgVoteOnObservedOutboundTx{ + CctxHash: cctx.Index, + OutTxTssNonce: cctx.GetCurrentOutTxParam().OutboundTxTssNonce, + OutTxChain: cctx.GetCurrentOutTxParam().ReceiverChainId, + Status: chains.ReceiveStatus_Failed, + Creator: observer, + ObservedOutTxHash: sample.Hash().String(), + ValueReceived: cctx.GetCurrentOutTxParam().Amount, + ObservedOutTxBlockHeight: 10, + ObservedOutTxEffectiveGasPrice: math.NewInt(21), + ObservedOutTxGasUsed: 21, + CoinType: cctx.InboundTxParams.CoinType, + }) + require.NoError(t, err) + c, found := k.GetCrossChainTx(ctx, cctx.Index) + require.True(t, found) + require.Equal(t, types.CctxStatus_PendingRevert, c.CctxStatus.Status) + require.Equal(t, oldParamsLen+1, len(c.OutboundTxParams)) + require.Equal(t, types.TxFinalizationStatus_Executed, c.OutboundTxParams[oldParamsLen-1].TxFinalizationStatus) + require.Equal(t, types.TxFinalizationStatus_NotFinalized, cctx.GetCurrentOutTxParam().TxFinalizationStatus) + }) + + t.Run("unsuccessfully vote on outbound tx, vote-type failed", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + UseFungibleMock: true, + }) + + // Setup mock data + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + senderChain := getValidEthChain(t) + asset := "" + observer := sample.AccAddress() + tss := sample.Tss() + zk.ObserverKeeper.SetObserverSet(ctx, observertypes.ObserverSet{ObserverList: []string{observer}}) + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + cctx.GetCurrentOutTxParam().TssPubkey = tss.TssPubkey + cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound + k.SetCrossChainTx(ctx, *cctx) + observerMock.On("GetTSS", ctx).Return(observertypes.TSS{}, true).Once() + + // Successfully mock VoteOnOutboundBallot + keepertest.MockVoteOnOutboundFailedBallot(observerMock, ctx, cctx, *senderChain, observer) + + // Successfully mock GetOutBound + keepertest.MockGetOutBound(observerMock, ctx) + + // Mock Failed ProcessOutbound + keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain) + keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *senderChain, asset) + observerMock.On("GetChainNonces", mock.Anything, senderChain.ChainName.String()). + Return(observertypes.ChainNonces{}, false) + + //Successfully mock SaveOutBound + keepertest.MockSaveOutBound(observerMock, ctx, cctx, tss) + oldParamsLen := len(cctx.OutboundTxParams) + msgServer := keeper.NewMsgServerImpl(*k) + _, err := msgServer.VoteOnObservedOutboundTx(ctx, &types.MsgVoteOnObservedOutboundTx{ + CctxHash: cctx.Index, + OutTxTssNonce: cctx.GetCurrentOutTxParam().OutboundTxTssNonce, + OutTxChain: cctx.GetCurrentOutTxParam().ReceiverChainId, + Status: chains.ReceiveStatus_Failed, + Creator: observer, + ObservedOutTxHash: sample.Hash().String(), + ValueReceived: cctx.GetCurrentOutTxParam().Amount, + ObservedOutTxBlockHeight: 10, + ObservedOutTxEffectiveGasPrice: math.NewInt(21), + ObservedOutTxGasUsed: 21, + CoinType: cctx.InboundTxParams.CoinType, + }) + require.NoError(t, err) + c, found := k.GetCrossChainTx(ctx, cctx.Index) + require.True(t, found) + require.Equal(t, types.CctxStatus_Aborted, c.CctxStatus.Status) + require.Equal(t, oldParamsLen+1, len(c.OutboundTxParams)) + // The message processing fails during the creation of the revert tx + // So the original outbound tx is executed and the revert tx is not finalized. + // The cctx status is Aborted + require.Equal(t, types.TxFinalizationStatus_NotFinalized, c.GetCurrentOutTxParam().TxFinalizationStatus) + require.Equal(t, types.TxFinalizationStatus_Executed, c.OutboundTxParams[oldParamsLen-1].TxFinalizationStatus) + }) + + t.Run("failure in processing outbound tx", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + UseFungibleMock: true, + }) + + // Setup mock data + observerMock := keepertest.GetCrosschainObserverMock(t, k) + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + senderChain := getValidEthChain(t) + asset := "" + observer := sample.AccAddress() + tss := sample.Tss() + zk.ObserverKeeper.SetObserverSet(ctx, observertypes.ObserverSet{ObserverList: []string{observer}}) + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + cctx.GetCurrentOutTxParam().TssPubkey = tss.TssPubkey + cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound + k.SetCrossChainTx(ctx, *cctx) + + // Successfully mock GetTSS + observerMock.On("GetTSS", ctx).Return(observertypes.TSS{}, true).Once() + + // Successfully mock VoteOnOutboundBallot + keepertest.MockVoteOnOutboundFailedBallot(observerMock, ctx, cctx, *senderChain, observer) + + // Successfully mock GetOutBound + keepertest.MockGetOutBound(observerMock, ctx) + + // Fail ProcessOutbound so that changes are not committed to the state + fungibleMock.On("GetForeignCoinFromAsset", mock.Anything, mock.Anything, mock.Anything).Return(fungibletypes.ForeignCoins{}, false) + + //Successfully mock SaveFailedOutbound + keepertest.MockSaveOutBound(observerMock, ctx, cctx, tss) + + msgServer := keeper.NewMsgServerImpl(*k) + _, err := msgServer.VoteOnObservedOutboundTx(ctx, &types.MsgVoteOnObservedOutboundTx{ + CctxHash: cctx.Index, + OutTxTssNonce: cctx.GetCurrentOutTxParam().OutboundTxTssNonce, + OutTxChain: cctx.GetCurrentOutTxParam().ReceiverChainId, + Status: chains.ReceiveStatus_Failed, + Creator: observer, + ObservedOutTxHash: sample.Hash().String(), + ValueReceived: cctx.GetCurrentOutTxParam().Amount, + ObservedOutTxBlockHeight: 10, + ObservedOutTxEffectiveGasPrice: math.NewInt(21), + ObservedOutTxGasUsed: 21, + CoinType: cctx.InboundTxParams.CoinType, + }) + require.NoError(t, err) + c, found := k.GetCrossChainTx(ctx, cctx.Index) + require.True(t, found) + // Status would be CctxStatus_PendingRevert if process outbound did not fail + require.Equal(t, types.CctxStatus_Aborted, c.CctxStatus.Status) + }) + + t.Run("fail to finalize outbound if not a finalizing vote", func(t *testing.T) { + k, ctx, sk, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{}) + + // Setup mock data + receiver := sample.EthAddress() + amount := big.NewInt(42) + senderChain := getValidEthChain(t) + asset := "" + r := rand.New(rand.NewSource(42)) + validator := sample.Validator(t, r) + tss := sample.Tss() + + // set state to successfully vote on outbound tx + accAddress, err := observertypes.GetAccAddressFromOperatorAddress(validator.OperatorAddress) + require.NoError(t, err) + zk.ObserverKeeper.SetObserverSet(ctx, observertypes.ObserverSet{ObserverList: []string{accAddress.String(), sample.AccAddress(), sample.AccAddress()}}) + sk.StakingKeeper.SetValidator(ctx, validator) + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + cctx.GetCurrentOutTxParam().TssPubkey = tss.TssPubkey + cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound + k.SetCrossChainTx(ctx, *cctx) + zk.ObserverKeeper.SetTSS(ctx, tss) + + msgServer := keeper.NewMsgServerImpl(*k) + msg := &types.MsgVoteOnObservedOutboundTx{ + CctxHash: cctx.Index, + OutTxTssNonce: cctx.GetCurrentOutTxParam().OutboundTxTssNonce, + OutTxChain: cctx.GetCurrentOutTxParam().ReceiverChainId, + Status: chains.ReceiveStatus_Success, + Creator: accAddress.String(), + ObservedOutTxHash: sample.Hash().String(), + ValueReceived: cctx.GetCurrentOutTxParam().Amount, + ObservedOutTxBlockHeight: 10, + ObservedOutTxEffectiveGasPrice: math.NewInt(21), + ObservedOutTxGasUsed: 21, + CoinType: cctx.InboundTxParams.CoinType, + } + _, err = msgServer.VoteOnObservedOutboundTx(ctx, msg) + require.NoError(t, err) + c, found := k.GetCrossChainTx(ctx, cctx.Index) + require.True(t, found) + require.Equal(t, types.CctxStatus_PendingOutbound, c.CctxStatus.Status) + ballot, found := zk.ObserverKeeper.GetBallot(ctx, msg.Digest()) + require.True(t, found) + require.True(t, ballot.HasVoted(accAddress.String())) + }) + + t.Run("unable to add vote if tss is not present", func(t *testing.T) { + k, ctx, sk, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{}) + + // Setup mock data + receiver := sample.EthAddress() + amount := big.NewInt(42) + senderChain := getValidEthChain(t) + asset := "" + r := rand.New(rand.NewSource(42)) + validator := sample.Validator(t, r) + tss := sample.Tss() + + // set state to successfully vote on outbound tx + accAddress, err := observertypes.GetAccAddressFromOperatorAddress(validator.OperatorAddress) + require.NoError(t, err) + zk.ObserverKeeper.SetObserverSet(ctx, observertypes.ObserverSet{ObserverList: []string{accAddress.String()}}) + sk.StakingKeeper.SetValidator(ctx, validator) + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + cctx.GetCurrentOutTxParam().TssPubkey = tss.TssPubkey + cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound + k.SetCrossChainTx(ctx, *cctx) + + msgServer := keeper.NewMsgServerImpl(*k) + msg := &types.MsgVoteOnObservedOutboundTx{ + CctxHash: cctx.Index, + OutTxTssNonce: cctx.GetCurrentOutTxParam().OutboundTxTssNonce, + OutTxChain: cctx.GetCurrentOutTxParam().ReceiverChainId, + Status: chains.ReceiveStatus_Success, + Creator: accAddress.String(), + ObservedOutTxHash: sample.Hash().String(), + ValueReceived: cctx.GetCurrentOutTxParam().Amount, + ObservedOutTxBlockHeight: 10, + ObservedOutTxEffectiveGasPrice: math.NewInt(21), + ObservedOutTxGasUsed: 21, + CoinType: cctx.InboundTxParams.CoinType, + } + _, err = msgServer.VoteOnObservedOutboundTx(ctx, msg) + require.ErrorIs(t, err, types.ErrCannotFindTSSKeys) + c, found := k.GetCrossChainTx(ctx, cctx.Index) + require.True(t, found) + require.Equal(t, types.CctxStatus_PendingOutbound, c.CctxStatus.Status) + _, found = zk.ObserverKeeper.GetBallot(ctx, msg.Digest()) + require.False(t, found) + }) +} + +func TestKeeper_SaveFailedOutBound(t *testing.T) { + t.Run("successfully save failed outbound", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + cctx := sample.CrossChainTx(t, "test") + k.SetOutTxTracker(ctx, types.OutTxTracker{ + Index: "", + ChainId: cctx.GetCurrentOutTxParam().ReceiverChainId, + Nonce: cctx.GetCurrentOutTxParam().OutboundTxTssNonce, + HashList: nil, + }) + cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound + k.SaveFailedOutbound(ctx, cctx, sample.String(), sample.ZetaIndex(t)) + require.Equal(t, cctx.CctxStatus.Status, types.CctxStatus_Aborted) + _, found := k.GetOutTxTracker(ctx, cctx.GetCurrentOutTxParam().ReceiverChainId, cctx.GetCurrentOutTxParam().OutboundTxTssNonce) + require.False(t, found) + }) +} + +func TestKeeper_SaveSuccessfulOutBound(t *testing.T) { + t.Run("successfully save successful outbound", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + cctx := sample.CrossChainTx(t, "test") + k.SetOutTxTracker(ctx, types.OutTxTracker{ + Index: "", + ChainId: cctx.GetCurrentOutTxParam().ReceiverChainId, + Nonce: cctx.GetCurrentOutTxParam().OutboundTxTssNonce, + HashList: nil, + }) + cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound + k.SaveSuccessfulOutbound(ctx, cctx, sample.String()) + require.Equal(t, cctx.GetCurrentOutTxParam().OutboundTxBallotIndex, sample.String()) + _, found := k.GetOutTxTracker(ctx, cctx.GetCurrentOutTxParam().ReceiverChainId, cctx.GetCurrentOutTxParam().OutboundTxTssNonce) + require.False(t, found) + }) +} + +func TestKeeper_SaveOutbound(t *testing.T) { + t.Run("successfully save outbound", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + + // setup state for crosschain and observer modules + cctx := sample.CrossChainTx(t, "test") + cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound + ballotIndex := sample.String() + k.SetOutTxTracker(ctx, types.OutTxTracker{ + Index: "", + ChainId: cctx.GetCurrentOutTxParam().ReceiverChainId, + Nonce: cctx.GetCurrentOutTxParam().OutboundTxTssNonce, + HashList: nil, + }) + + zk.ObserverKeeper.SetPendingNonces(ctx, observertypes.PendingNonces{ + NonceLow: int64(cctx.GetCurrentOutTxParam().OutboundTxTssNonce) - 1, + NonceHigh: int64(cctx.GetCurrentOutTxParam().OutboundTxTssNonce) + 1, + ChainId: cctx.GetCurrentOutTxParam().ReceiverChainId, + Tss: cctx.GetCurrentOutTxParam().TssPubkey, + }) + zk.ObserverKeeper.SetTSS(ctx, observertypes.TSS{ + TssPubkey: cctx.GetCurrentOutTxParam().TssPubkey, + }) + + // Save outbound and assert all values are successfully saved + k.SaveOutbound(ctx, cctx, ballotIndex) + require.Equal(t, cctx.GetCurrentOutTxParam().OutboundTxBallotIndex, ballotIndex) + _, found := k.GetOutTxTracker(ctx, cctx.GetCurrentOutTxParam().ReceiverChainId, cctx.GetCurrentOutTxParam().OutboundTxTssNonce) + require.False(t, found) + pn, found := zk.ObserverKeeper.GetPendingNonces(ctx, cctx.GetCurrentOutTxParam().TssPubkey, cctx.GetCurrentOutTxParam().ReceiverChainId) + require.True(t, found) + require.Equal(t, pn.NonceLow, int64(cctx.GetCurrentOutTxParam().OutboundTxTssNonce)+1) + require.Equal(t, pn.NonceHigh, int64(cctx.GetCurrentOutTxParam().OutboundTxTssNonce)+1) + _, found = k.GetInTxHashToCctx(ctx, cctx.InboundTxParams.InboundTxObservedHash) + require.True(t, found) + _, found = zk.ObserverKeeper.GetNonceToCctx(ctx, cctx.GetCurrentOutTxParam().TssPubkey, cctx.GetCurrentOutTxParam().ReceiverChainId, int64(cctx.GetCurrentOutTxParam().OutboundTxTssNonce)) + require.True(t, found) + }) +} + +func TestKeeper_ValidateOutboundMessage(t *testing.T) { + t.Run("successfully validate outbound message", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + cctx := sample.CrossChainTx(t, "test") + k.SetCrossChainTx(ctx, *cctx) + zk.ObserverKeeper.SetTSS(ctx, sample.Tss()) + _, err := k.ValidateOutboundMessage(ctx, types.MsgVoteOnObservedOutboundTx{ + CctxHash: cctx.Index, + OutTxTssNonce: cctx.GetCurrentOutTxParam().OutboundTxTssNonce, + OutTxChain: cctx.GetCurrentOutTxParam().ReceiverChainId, + }) + require.NoError(t, err) + }) + + t.Run("failed to validate outbound message if cctx not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + msg := types.MsgVoteOnObservedOutboundTx{ + CctxHash: sample.String(), + OutTxTssNonce: 1, + } + _, err := k.ValidateOutboundMessage(ctx, msg) + require.ErrorIs(t, err, sdkerrors.ErrInvalidRequest) + require.ErrorContains(t, err, fmt.Sprintf("CCTX %s does not exist", msg.CctxHash)) + }) + + t.Run("failed to validate outbound message if nonce does not match", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + cctx := sample.CrossChainTx(t, "test") + k.SetCrossChainTx(ctx, *cctx) + msg := types.MsgVoteOnObservedOutboundTx{ + CctxHash: cctx.Index, + OutTxTssNonce: 2, + } + _, err := k.ValidateOutboundMessage(ctx, msg) + require.ErrorIs(t, err, sdkerrors.ErrInvalidRequest) + require.ErrorContains(t, err, fmt.Sprintf("OutTxTssNonce %d does not match CCTX OutTxTssNonce %d", msg.OutTxTssNonce, cctx.GetCurrentOutTxParam().OutboundTxTssNonce)) + }) + + t.Run("failed to validate outbound message if tss not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + cctx := sample.CrossChainTx(t, "test") + k.SetCrossChainTx(ctx, *cctx) + _, err := k.ValidateOutboundMessage(ctx, types.MsgVoteOnObservedOutboundTx{ + CctxHash: cctx.Index, + OutTxTssNonce: cctx.GetCurrentOutTxParam().OutboundTxTssNonce, + }) + require.ErrorIs(t, err, types.ErrCannotFindTSSKeys) + }) + + t.Run("failed to validate outbound message if chain does not match", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + cctx := sample.CrossChainTx(t, "test") + k.SetCrossChainTx(ctx, *cctx) + zk.ObserverKeeper.SetTSS(ctx, sample.Tss()) + _, err := k.ValidateOutboundMessage(ctx, types.MsgVoteOnObservedOutboundTx{ + CctxHash: cctx.Index, + OutTxTssNonce: cctx.GetCurrentOutTxParam().OutboundTxTssNonce, + OutTxChain: 2, + }) + require.ErrorIs(t, err, sdkerrors.ErrInvalidRequest) + require.ErrorContains(t, err, fmt.Sprintf("OutTxChain %d does not match CCTX OutTxChain %d", 2, cctx.GetCurrentOutTxParam().ReceiverChainId)) + }) +} diff --git a/x/crosschain/keeper/msg_server_whitelist_erc20.go b/x/crosschain/keeper/msg_server_whitelist_erc20.go index 5e61ea3e11..0687aedbb1 100644 --- a/x/crosschain/keeper/msg_server_whitelist_erc20.go +++ b/x/crosschain/keeper/msg_server_whitelist_erc20.go @@ -112,6 +112,8 @@ func (k msgServer) WhitelistERC20(goCtx context.Context, msg *types.MsgWhitelist index := hash.Hex() // create a cmd cctx to whitelist the erc20 on the external chain + // TODO : refactor this to use the `NewCCTX` function instead. + //https://github.com/zeta-chain/node/issues/1909 cctx := types.CrossChainTx{ Creator: msg.Creator, Index: index, diff --git a/x/crosschain/keeper/process_inbound.go b/x/crosschain/keeper/process_inbound.go new file mode 100644 index 0000000000..c45d65bf51 --- /dev/null +++ b/x/crosschain/keeper/process_inbound.go @@ -0,0 +1,125 @@ +package keeper + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// ProcessInbound processes the inbound CCTX. +// It does a conditional dispatch to ProcessZEVMDeposit or ProcessCrosschainMsgPassing based on the receiver chain. +// The internal functions handle the state changes and error handling. +func (k Keeper) ProcessInbound(ctx sdk.Context, cctx *types.CrossChainTx) { + if chains.IsZetaChain(cctx.GetCurrentOutTxParam().ReceiverChainId) { + k.ProcessZEVMDeposit(ctx, cctx) + } else { + k.ProcessCrosschainMsgPassing(ctx, cctx) + } +} + +/* +ProcessZEVMDeposit processes the EVM deposit CCTX. A deposit is a cctx which has Zetachain as the receiver chain.It trasnsitions state according to the following rules: + - If the deposit is successful, the CCTX status is changed to OutboundMined. + - If the deposit returns an internal error i.e if HandleEVMDeposit() returns an error, but isContractReverted is false, the CCTX status is changed to Aborted. + - If the deposit is reverted, the function tries to create a revert cctx with status PendingRevert. + - If the creation of revert tx also fails it changes the status to Aborted. + +Note : Aborted CCTXs are not refunded in this function. The refund is done using a separate refunding mechanism. +We do not return an error from this function , as all changes need to be persisted to the state. +Instead we use a temporary context to make changes and then commit the context on for the happy path ,i.e cctx is set to OutboundMined. +*/ +func (k Keeper) ProcessZEVMDeposit(ctx sdk.Context, cctx *types.CrossChainTx) { + tmpCtx, commit := ctx.CacheContext() + isContractReverted, err := k.HandleEVMDeposit(tmpCtx, cctx) + + if err != nil && !isContractReverted { // exceptional case; internal error; should abort CCTX + cctx.SetAbort(err.Error()) + return + } else if err != nil && isContractReverted { // contract call reverted; should refund + revertMessage := err.Error() + senderChain := k.zetaObserverKeeper.GetSupportedChainFromChainID(ctx, cctx.InboundTxParams.SenderChainId) + if senderChain == nil { + cctx.SetAbort(fmt.Sprintf("invalid sender chain id %d", cctx.InboundTxParams.SenderChainId)) + return + } + + gasLimit, err := k.GetRevertGasLimit(ctx, *cctx) + if err != nil { + cctx.SetAbort(fmt.Sprintf("revert gas limit error: %s", err.Error())) + return + } + if gasLimit == 0 { + // use same gas limit of outbound as a fallback -- should not happen + gasLimit = cctx.GetCurrentOutTxParam().OutboundTxGasLimit + } + + err = cctx.AddRevertOutbound(gasLimit) + if err != nil { + cctx.SetAbort(fmt.Sprintf("revert outbound error: %s", err.Error())) + return + } + + // we create a new cached context, and we don't commit the previous one with EVM deposit + tmpCtxRevert, commitRevert := ctx.CacheContext() + err = func() error { + err := k.PayGasAndUpdateCctx( + tmpCtxRevert, + senderChain.ChainId, + cctx, + cctx.InboundTxParams.Amount, + false, + ) + if err != nil { + return err + } + // Update nonce using senderchain id as this is a revert tx and would go back to the original sender + return k.UpdateNonce(tmpCtxRevert, senderChain.ChainId, cctx) + }() + if err != nil { + cctx.SetAbort(fmt.Sprintf("deposit revert message: %s err : %s", revertMessage, err.Error())) + return + } + commitRevert() + cctx.SetPendingRevert(revertMessage) + return + } + // successful HandleEVMDeposit; + commit() + cctx.SetOutBoundMined("Remote omnichain contract call completed") + return +} + +/* +ProcessCrosschainMsgPassing processes the CCTX for crosschain message passing. A crosschain message passing is a cctx which has a non-Zetachain as the receiver chain.It trasnsitions state according to the following rules: + - If the crosschain message passing is successful, the CCTX status is changed to PendingOutbound. + - If the crosschain message passing returns an error, the CCTX status is changed to Aborted. + We do not return an error from this function, as all changes need to be persisted to the state. + Instead, we use a temporary context to make changes and then commit the context on for the happy path ,i.e cctx is set to PendingOutbound. +*/ +func (k Keeper) ProcessCrosschainMsgPassing(ctx sdk.Context, cctx *types.CrossChainTx) { + tmpCtx, commit := ctx.CacheContext() + outboundReceiverChainID := cctx.GetCurrentOutTxParam().ReceiverChainId + err := func() error { + err := k.PayGasAndUpdateCctx( + tmpCtx, + outboundReceiverChainID, + cctx, + cctx.InboundTxParams.Amount, + false, + ) + if err != nil { + return err + } + return k.UpdateNonce(tmpCtx, outboundReceiverChainID, cctx) + }() + if err != nil { + // do not commit anything here as the CCTX should be aborted + cctx.SetAbort(err.Error()) + return + } + commit() + cctx.SetPendingOutbound("") + return +} diff --git a/x/crosschain/keeper/process_inbound_test.go b/x/crosschain/keeper/process_inbound_test.go new file mode 100644 index 0000000000..4b53f817de --- /dev/null +++ b/x/crosschain/keeper/process_inbound_test.go @@ -0,0 +1,355 @@ +package keeper_test + +import ( + "fmt" + "math/big" + "testing" + + sdkmath "cosmossdk.io/math" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/coin" + 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" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" +) + +func TestKeeper_ProcessZEVMDeposit(t *testing.T) { + t.Run("process zevm deposit successfully", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + }) + + // Setup mock data + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + + // expect DepositCoinZeta to be called + fungibleMock.On("DepositCoinZeta", mock.Anything, receiver, amount). + Return(nil) + + // call ProcessZEVMDeposit + cctx := sample.CrossChainTx(t, "test") + cctx.CctxStatus = &types.Status{Status: types.CctxStatus_PendingInbound} + cctx.GetCurrentOutTxParam().Receiver = receiver.String() + cctx.GetInboundTxParams().Amount = sdkmath.NewUintFromBigInt(amount) + cctx.InboundTxParams.CoinType = coin.CoinType_Zeta + cctx.GetInboundTxParams().SenderChainId = 0 + k.ProcessZEVMDeposit(ctx, cctx) + require.Equal(t, types.CctxStatus_OutboundMined, cctx.CctxStatus.Status) + }) + + t.Run("unable to process zevm deposit HandleEVMDeposit returns err without reverting", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + }) + + // Setup mock data + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + + // mock unsuccessful HandleEVMDeposit which does not revert + fungibleMock.On("DepositCoinZeta", mock.Anything, receiver, amount). + Return(fmt.Errorf("deposit error"), false) + + // call ProcessZEVMDeposit + cctx := sample.CrossChainTx(t, "test") + cctx.CctxStatus = &types.Status{Status: types.CctxStatus_PendingInbound} + cctx.GetCurrentOutTxParam().Receiver = receiver.String() + cctx.GetInboundTxParams().Amount = sdkmath.NewUintFromBigInt(amount) + cctx.InboundTxParams.CoinType = coin.CoinType_Zeta + cctx.GetInboundTxParams().SenderChainId = 0 + k.ProcessZEVMDeposit(ctx, cctx) + require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) + require.Equal(t, "deposit error", cctx.CctxStatus.StatusMessage) + }) + + t.Run("unable to process zevm deposit HandleEVMDeposit reverts fails at GetSupportedChainFromChainID", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + UseObserverMock: true, + }) + + // Setup mock data + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + senderChain := getValidEthChain(t) + errDeposit := fmt.Errorf("deposit failed") + + // Setup expected calls + // mock unsuccessful HandleEVMDeposit which reverts , i.e returns err and isContractReverted = true + keepertest.MockRevertForHandleEVMDeposit(fungibleMock, receiver, amount, senderChain.ChainId, errDeposit) + + // mock unsuccessful GetSupportedChainFromChainID + observerMock.On("GetSupportedChainFromChainID", mock.Anything, senderChain.ChainId). + Return(nil) + + // call ProcessZEVMDeposit + cctx := GetERC20Cctx(t, receiver, *senderChain, "", amount) + k.ProcessZEVMDeposit(ctx, cctx) + require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) + require.Equal(t, fmt.Sprintf("invalid sender chain id %d", cctx.InboundTxParams.SenderChainId), cctx.CctxStatus.StatusMessage) + }) + + t.Run("unable to process zevm deposit HandleEVMDeposit revert fails at and GetRevertGasLimit", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + UseObserverMock: true, + }) + + // Setup mock data + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + senderChain := getValidEthChain(t) + asset := "" + errDeposit := fmt.Errorf("deposit failed") + + // Setup expected calls + keepertest.MockRevertForHandleEVMDeposit(fungibleMock, receiver, amount, senderChain.ChainId, errDeposit) + + // Mock successful GetSupportedChainFromChainID + keepertest.MockGetSupportedChainFromChainID(observerMock, senderChain) + + // mock unsuccessful GetRevertGasLimit for ERC20 + fungibleMock.On("GetForeignCoinFromAsset", mock.Anything, asset, senderChain.ChainId). + Return(fungibletypes.ForeignCoins{}, false) + + // call ProcessZEVMDeposit + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + k.ProcessZEVMDeposit(ctx, cctx) + require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) + require.Equal(t, fmt.Sprintf("revert gas limit error: %s", types.ErrForeignCoinNotFound), cctx.CctxStatus.StatusMessage) + }) + + t.Run("unable to process zevm deposit HandleEVMDeposit revert fails at PayGasInERC20AndUpdateCctx", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + UseObserverMock: true, + }) + + // Setup mock data + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + senderChain := getValidEthChain(t) + asset := "" + + // Setup expected calls + errDeposit := fmt.Errorf("deposit failed") + keepertest.MockRevertForHandleEVMDeposit(fungibleMock, receiver, amount, senderChain.ChainId, errDeposit) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + + // Mock successful GetSupportedChainFromChainID + keepertest.MockGetSupportedChainFromChainID(observerMock, senderChain) + + // mock successful GetRevertGasLimit for ERC20 + keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain) + + // mock unsuccessful PayGasInERC20AndUpdateCctx + observerMock.On("GetSupportedChainFromChainID", mock.Anything, senderChain.ChainId). + Return(nil).Once() + + // call ProcessZEVMDeposit + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + k.ProcessZEVMDeposit(ctx, cctx) + require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) + require.Equal(t, fmt.Sprintf("deposit revert message: %s err : %s", errDeposit, observertypes.ErrSupportedChains), cctx.CctxStatus.StatusMessage) + }) + + t.Run("unable to process zevm deposit HandleEVMDeposit reverts fails at UpdateNonce", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + UseObserverMock: true, + }) + + // Setup mock data + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + senderChain := getValidEthChain(t) + asset := "" + errDeposit := fmt.Errorf("deposit failed") + + // Setup expected calls + // mock unsuccessful HandleEVMDeposit which reverts , i.e returns err and isContractReverted = true + keepertest.MockRevertForHandleEVMDeposit(fungibleMock, receiver, amount, senderChain.ChainId, errDeposit) + + // Mock successful GetSupportedChainFromChainID + keepertest.MockGetSupportedChainFromChainID(observerMock, senderChain) + + // mock successful GetRevertGasLimit for ERC20 + keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain) + + // mock successful PayGasAndUpdateCctx + keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *senderChain, asset) + + // Mock unsuccessful UpdateNonce + observerMock.On("GetChainNonces", mock.Anything, senderChain.ChainName.String()). + Return(observertypes.ChainNonces{}, false) + + // call ProcessZEVMDeposit + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + k.ProcessZEVMDeposit(ctx, cctx) + require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) + require.Contains(t, cctx.CctxStatus.StatusMessage, "cannot find receiver chain nonce") + }) + + t.Run("unable to process zevm deposit HandleEVMDeposit revert successfully", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + UseObserverMock: true, + }) + + // Setup mock data + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + senderChain := getValidEthChain(t) + asset := "" + errDeposit := fmt.Errorf("deposit failed") + + // Setup expected calls + // mock unsuccessful HandleEVMDeposit which reverts , i.e returns err and isContractReverted = true + keepertest.MockRevertForHandleEVMDeposit(fungibleMock, receiver, amount, senderChain.ChainId, errDeposit) + + // Mock successful GetSupportedChainFromChainID + keepertest.MockGetSupportedChainFromChainID(observerMock, senderChain) + + // mock successful GetRevertGasLimit for ERC20 + keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain) + + // mock successful PayGasAndUpdateCctx + keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *senderChain, asset) + // mock successful UpdateNonce + updatedNonce := keepertest.MockUpdateNonce(observerMock, *senderChain) + + // call ProcessZEVMDeposit + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + k.ProcessZEVMDeposit(ctx, cctx) + require.Equal(t, types.CctxStatus_PendingRevert, cctx.CctxStatus.Status) + require.Equal(t, errDeposit.Error(), cctx.CctxStatus.StatusMessage) + require.Equal(t, updatedNonce, cctx.GetCurrentOutTxParam().OutboundTxTssNonce) + }) + + t.Run("unable to process zevm deposit HandleEVMDeposit revert fails as the cctx has already been reverted", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + UseObserverMock: true, + }) + + // Setup mock data + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + senderChain := getValidEthChain(t) + asset := "" + errDeposit := fmt.Errorf("deposit failed") + + // Setup expected calls + // mock unsuccessful HandleEVMDeposit which reverts , i.e returns err and isContractReverted = true + keepertest.MockRevertForHandleEVMDeposit(fungibleMock, receiver, amount, senderChain.ChainId, errDeposit) + + // Mock successful GetSupportedChainFromChainID + keepertest.MockGetSupportedChainFromChainID(observerMock, senderChain) + + // mock successful GetRevertGasLimit for ERC20 + keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain) + + // call ProcessZEVMDeposit + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + cctx.OutboundTxParams = append(cctx.OutboundTxParams, cctx.GetCurrentOutTxParam()) + k.ProcessZEVMDeposit(ctx, cctx) + require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) + require.Contains(t, cctx.CctxStatus.StatusMessage, fmt.Sprintf("revert outbound error: %s", "cannot revert a revert tx")) + }) +} + +func TestKeeper_ProcessCrosschainMsgPassing(t *testing.T) { + t.Run("process crosschain msg passing successfully", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + UseObserverMock: true, + }) + + // Setup mock data + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + receiverChain := getValidEthChain(t) + + // mock successful PayGasAndUpdateCctx + keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *receiverChain, "") + + // mock successful UpdateNonce + updatedNonce := keepertest.MockUpdateNonce(observerMock, *receiverChain) + + // call ProcessCrosschainMsgPassing + cctx := GetERC20Cctx(t, receiver, *receiverChain, "", amount) + k.ProcessCrosschainMsgPassing(ctx, cctx) + require.Equal(t, types.CctxStatus_PendingOutbound, cctx.CctxStatus.Status) + require.Equal(t, updatedNonce, cctx.GetCurrentOutTxParam().OutboundTxTssNonce) + }) + + t.Run("unable to process crosschain msg passing PayGasAndUpdateCctx fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + UseObserverMock: true, + }) + + // Setup mock data + observerMock := keepertest.GetCrosschainObserverMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + receiverChain := getValidEthChain(t) + + // mock unsuccessful PayGasAndUpdateCctx + observerMock.On("GetSupportedChainFromChainID", mock.Anything, receiverChain.ChainId). + Return(nil).Once() + + // call ProcessCrosschainMsgPassing + cctx := GetERC20Cctx(t, receiver, *receiverChain, "", amount) + k.ProcessCrosschainMsgPassing(ctx, cctx) + require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) + require.Equal(t, observertypes.ErrSupportedChains.Error(), cctx.CctxStatus.StatusMessage) + }) + + t.Run("unable to process crosschain msg passing UpdateNonce fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + UseObserverMock: true, + }) + + // Setup mock data + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + receiverChain := getValidEthChain(t) + + // mock successful PayGasAndUpdateCctx + keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *receiverChain, "") + + // mock unsuccessful UpdateNonce + observerMock.On("GetChainNonces", mock.Anything, receiverChain.ChainName.String()). + Return(observertypes.ChainNonces{}, false) + + // call ProcessCrosschainMsgPassing + cctx := GetERC20Cctx(t, receiver, *receiverChain, "", amount) + k.ProcessCrosschainMsgPassing(ctx, cctx) + require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) + require.Contains(t, cctx.CctxStatus.StatusMessage, "cannot find receiver chain nonce") + }) +} diff --git a/x/crosschain/keeper/process_outbound_test.go b/x/crosschain/keeper/process_outbound_test.go new file mode 100644 index 0000000000..23877e14f7 --- /dev/null +++ b/x/crosschain/keeper/process_outbound_test.go @@ -0,0 +1,289 @@ +package keeper_test + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" + 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" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" +) + +func TestKeeper_ProcessSuccessfulOutbound(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + cctx := sample.CrossChainTx(t, "test") + // transition to reverted if pending revert + cctx.CctxStatus.Status = types.CctxStatus_PendingRevert + k.ProcessSuccessfulOutbound(ctx, cctx, sample.String()) + require.Equal(t, cctx.CctxStatus.Status, types.CctxStatus_Reverted) + // transition to outbound mined if pending outbound + cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound + k.ProcessSuccessfulOutbound(ctx, cctx, sample.String()) + require.Equal(t, cctx.CctxStatus.Status, types.CctxStatus_OutboundMined) + // do nothing if it's in any other state + k.ProcessSuccessfulOutbound(ctx, cctx, sample.String()) + require.Equal(t, cctx.CctxStatus.Status, types.CctxStatus_OutboundMined) +} + +func TestKeeper_ProcessFailedOutbound(t *testing.T) { + t.Run("successfully process failed outbound set to aborted for type cmd", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + cctx := sample.CrossChainTx(t, "test") + cctx.InboundTxParams.CoinType = coin.CoinType_Cmd + err := k.ProcessFailedOutbound(ctx, cctx, sample.String()) + require.NoError(t, err) + require.Equal(t, cctx.CctxStatus.Status, types.CctxStatus_Aborted) + require.Equal(t, cctx.GetCurrentOutTxParam().TxFinalizationStatus, types.TxFinalizationStatus_Executed) + }) + + t.Run("successfully process failed outbound set to aborted for withdraw tx", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + cctx := sample.CrossChainTx(t, "test") + cctx.InboundTxParams.SenderChainId = chains.ZetaChainMainnet().ChainId + err := k.ProcessFailedOutbound(ctx, cctx, sample.String()) + require.NoError(t, err) + require.Equal(t, cctx.CctxStatus.Status, types.CctxStatus_Aborted) + require.Equal(t, cctx.GetCurrentOutTxParam().TxFinalizationStatus, types.TxFinalizationStatus_Executed) + }) + + t.Run("successfully process failed outbound set to pending revert", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + UseObserverMock: true, + }) + + // Setup mock data + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + senderChain := getValidEthChain(t) + asset := "" + + // mock successful GetRevertGasLimit for ERC20 + keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain) + + // mock successful PayGasAndUpdateCctx + keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *senderChain, asset) + + // mock successful UpdateNonce + _ = keepertest.MockUpdateNonce(observerMock, *senderChain) + + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound + err := k.ProcessFailedOutbound(ctx, cctx, sample.String()) + require.NoError(t, err) + require.Equal(t, cctx.CctxStatus.Status, types.CctxStatus_PendingRevert) + require.Equal(t, types.TxFinalizationStatus_NotFinalized, cctx.GetCurrentOutTxParam().TxFinalizationStatus) + require.Equal(t, types.TxFinalizationStatus_Executed, cctx.OutboundTxParams[0].TxFinalizationStatus) + + }) + + t.Run("unable to process revert when update nonce fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + UseObserverMock: true, + }) + + // Setup mock data + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + senderChain := getValidEthChain(t) + asset := "" + + // mock successful GetRevertGasLimit for ERC20 + keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain) + + // mock successful PayGasAndUpdateCctx + keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *senderChain, asset) + + // mock failed UpdateNonce + observerMock.On("GetChainNonces", mock.Anything, senderChain.ChainName.String()). + Return(observertypes.ChainNonces{}, false) + + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound + err := k.ProcessFailedOutbound(ctx, cctx, sample.String()) + require.ErrorIs(t, err, types.ErrCannotFindReceiverNonce) + require.Equal(t, cctx.CctxStatus.Status, types.CctxStatus_PendingOutbound) + }) + + t.Run("unable to process revert when PayGasAndUpdateCctx fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + UseObserverMock: true, + }) + + // Setup mock data + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + senderChain := getValidEthChain(t) + asset := "" + + // mock successful GetRevertGasLimit for ERC20 + keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain) + + // mock successful PayGasAndUpdateCctx + observerMock.On("GetSupportedChainFromChainID", mock.Anything, senderChain.ChainId). + Return(nil).Once() + + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound + err := k.ProcessFailedOutbound(ctx, cctx, sample.String()) + require.ErrorIs(t, err, observertypes.ErrSupportedChains) + require.Equal(t, cctx.CctxStatus.Status, types.CctxStatus_PendingOutbound) + }) + + t.Run("unable to process revert when GetRevertGasLimit fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + }) + + // Setup mock data + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + senderChain := getValidEthChain(t) + asset := "" + + // mock failed GetRevertGasLimit for ERC20 + fungibleMock.On("GetForeignCoinFromAsset", mock.Anything, asset, senderChain.ChainId). + Return(fungibletypes.ForeignCoins{ + Zrc20ContractAddress: sample.EthAddress().String(), + }, false).Once() + + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound + err := k.ProcessFailedOutbound(ctx, cctx, sample.String()) + require.ErrorIs(t, err, types.ErrForeignCoinNotFound) + require.Equal(t, cctx.CctxStatus.Status, types.CctxStatus_PendingOutbound) + }) +} + +func TestKeeper_ProcessOutbound(t *testing.T) { + t.Run("successfully process outbound with ballot finalized to success", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + cctx := GetERC20Cctx(t, sample.EthAddress(), chains.GoerliChain(), "", big.NewInt(42)) + cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound + err := k.ProcessOutbound(ctx, cctx, observertypes.BallotStatus_BallotFinalized_SuccessObservation, sample.String()) + require.NoError(t, err) + require.Equal(t, cctx.CctxStatus.Status, types.CctxStatus_OutboundMined) + }) + + t.Run("successfully process outbound with ballot finalized to failed and old status is Pending Revert", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + cctx := GetERC20Cctx(t, sample.EthAddress(), chains.GoerliChain(), "", big.NewInt(42)) + cctx.CctxStatus.Status = types.CctxStatus_PendingRevert + err := k.ProcessOutbound(ctx, cctx, observertypes.BallotStatus_BallotFinalized_FailureObservation, sample.String()) + require.NoError(t, err) + require.Equal(t, cctx.CctxStatus.Status, types.CctxStatus_Aborted) + require.Equal(t, cctx.GetCurrentOutTxParam().TxFinalizationStatus, types.TxFinalizationStatus_Executed) + }) + + t.Run("successfully process outbound with ballot finalized to failed and coin-type is CMD", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + cctx := GetERC20Cctx(t, sample.EthAddress(), chains.GoerliChain(), "", big.NewInt(42)) + cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound + cctx.InboundTxParams.CoinType = coin.CoinType_Cmd + err := k.ProcessOutbound(ctx, cctx, observertypes.BallotStatus_BallotFinalized_FailureObservation, sample.String()) + require.NoError(t, err) + require.Equal(t, cctx.CctxStatus.Status, types.CctxStatus_Aborted) + require.Equal(t, cctx.GetCurrentOutTxParam().TxFinalizationStatus, types.TxFinalizationStatus_Executed) + }) + + t.Run("do not process outbound on error, no new outbound created", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + }) + + // Setup mock data + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + senderChain := getValidEthChain(t) + asset := "" + + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound + oldOutTxParamsLen := len(cctx.OutboundTxParams) + // mock failed GetRevertGasLimit for ERC20 + fungibleMock.On("GetForeignCoinFromAsset", mock.Anything, asset, senderChain.ChainId). + Return(fungibletypes.ForeignCoins{ + Zrc20ContractAddress: sample.EthAddress().String(), + }, false).Once() + + err := k.ProcessOutbound(ctx, cctx, observertypes.BallotStatus_BallotFinalized_FailureObservation, sample.String()) + require.ErrorIs(t, err, types.ErrForeignCoinNotFound) + require.Equal(t, cctx.CctxStatus.Status, types.CctxStatus_PendingOutbound) + // New outbound not added and the old outbound is not finalized + require.Len(t, cctx.OutboundTxParams, oldOutTxParamsLen) + require.Equal(t, cctx.GetCurrentOutTxParam().TxFinalizationStatus, types.TxFinalizationStatus_NotFinalized) + }) + + t.Run("do not process outbound if the cctx has already been reverted once", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + }) + + // Setup mock data + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + senderChain := getValidEthChain(t) + asset := "" + + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + cctx.OutboundTxParams = append(cctx.OutboundTxParams, sample.OutboundTxParams(sample.Rand())) + cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound + // mock successful GetRevertGasLimit for ERC20 + keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain) + + err := k.ProcessOutbound(ctx, cctx, observertypes.BallotStatus_BallotFinalized_FailureObservation, sample.String()) + require.ErrorContains(t, err, "cannot revert a revert") + }) + + t.Run("successfully revert a outbound and create a new revert tx", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + UseObserverMock: true, + }) + + // Setup mock data + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + senderChain := getValidEthChain(t) + asset := "" + + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound + oldOutTxParamsLen := len(cctx.OutboundTxParams) + // mock successful GetRevertGasLimit for ERC20 + keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain) + + // mock successful PayGasAndUpdateCctx + keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *senderChain, asset) + + // mock successful UpdateNonce + _ = keepertest.MockUpdateNonce(observerMock, *senderChain) + + err := k.ProcessOutbound(ctx, cctx, observertypes.BallotStatus_BallotFinalized_FailureObservation, sample.String()) + require.NoError(t, err) + require.Equal(t, cctx.CctxStatus.Status, types.CctxStatus_PendingRevert) + // New outbound added for revert and the old outbound is finalized + require.Len(t, cctx.OutboundTxParams, oldOutTxParamsLen+1) + require.Equal(t, cctx.GetCurrentOutTxParam().TxFinalizationStatus, types.TxFinalizationStatus_NotFinalized) + require.Equal(t, cctx.OutboundTxParams[oldOutTxParamsLen-1].TxFinalizationStatus, types.TxFinalizationStatus_Executed) + }) +} diff --git a/x/crosschain/keeper/processs_outbound.go b/x/crosschain/keeper/processs_outbound.go new file mode 100644 index 0000000000..b10df821cc --- /dev/null +++ b/x/crosschain/keeper/processs_outbound.go @@ -0,0 +1,127 @@ +package keeper + +import ( + cosmoserrors "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" + "github.com/zeta-chain/zetacore/x/crosschain/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" +) + +/* ProcessSuccessfulOutbound processes a successful outbound transaction. It does the following things in one function: + + 1. Change the status of the CCTX from + - PendingRevert to Reverted + - PendingOutbound to OutboundMined + + 2. Set the finalization status of the current outbound tx to executed + + 3. Emit an event for the successful outbound transaction +*/ + +func (k Keeper) ProcessSuccessfulOutbound(ctx sdk.Context, cctx *types.CrossChainTx, valueReceived string) { + oldStatus := cctx.CctxStatus.Status + switch oldStatus { + case types.CctxStatus_PendingRevert: + cctx.SetReverted("Outbound succeeded, revert executed") + case types.CctxStatus_PendingOutbound: + cctx.SetOutBoundMined("Outbound succeeded, mined") + default: + return + } + cctx.GetCurrentOutTxParam().TxFinalizationStatus = types.TxFinalizationStatus_Executed + newStatus := cctx.CctxStatus.Status.String() + EmitOutboundSuccess(ctx, valueReceived, oldStatus.String(), newStatus, cctx.Index) +} + +/* +ProcessFailedOutbound processes a failed outbound transaction. It does the following things in one function: + + 1. For Admin Tx or a withdrawal from Zeta chain, it aborts the CCTX + + 2. For other CCTX + - If the CCTX is in PendingOutbound, it creates a revert tx and sets the finalization status of the current outbound tx to executed + - If the CCTX is in PendingRevert, it sets the Status to Aborted + + 3. Emit an event for the failed outbound transaction + + 4. Set the finalization status of the current outbound tx to executed. If a revert tx is is created, the finalization status is not set, it would get set when the revert is processed via a subsequent transaction +*/ +func (k Keeper) ProcessFailedOutbound(ctx sdk.Context, cctx *types.CrossChainTx, valueReceived string) error { + oldStatus := cctx.CctxStatus.Status + if cctx.InboundTxParams.CoinType == coin.CoinType_Cmd || chains.IsZetaChain(cctx.InboundTxParams.SenderChainId) { + // if the cctx is of coin type cmd or the sender chain is zeta chain, then we do not revert, the cctx is aborted + cctx.GetCurrentOutTxParam().TxFinalizationStatus = types.TxFinalizationStatus_Executed + cctx.CctxStatus.ChangeStatus(types.CctxStatus_Aborted, "") + } else { + switch oldStatus { + case types.CctxStatus_PendingOutbound: + + gasLimit, err := k.GetRevertGasLimit(ctx, *cctx) + if err != nil { + return cosmoserrors.Wrap(err, "GetRevertGasLimit") + } + if gasLimit == 0 { + // use same gas limit of outbound as a fallback -- should not happen + gasLimit = cctx.OutboundTxParams[0].OutboundTxGasLimit + } + + // create new OutboundTxParams for the revert + err = cctx.AddRevertOutbound(gasLimit) + if err != nil { + return cosmoserrors.Wrap(err, "AddRevertOutbound") + } + + err = k.PayGasAndUpdateCctx( + ctx, + cctx.InboundTxParams.SenderChainId, + cctx, + cctx.OutboundTxParams[0].Amount, + false, + ) + if err != nil { + return err + } + err = k.UpdateNonce(ctx, cctx.InboundTxParams.SenderChainId, cctx) + if err != nil { + return err + } + // Not setting the finalization status here, the required changes have been made while creating the revert tx + cctx.SetPendingRevert("Outbound failed, start revert") + case types.CctxStatus_PendingRevert: + cctx.GetCurrentOutTxParam().TxFinalizationStatus = types.TxFinalizationStatus_Executed + cctx.SetAbort("Outbound failed: revert failed; abort TX") + } + } + newStatus := cctx.CctxStatus.Status.String() + EmitOutboundFailure(ctx, valueReceived, oldStatus.String(), newStatus, cctx.Index) + return nil +} + +// ProcessOutbound processes the finalization of an outbound transaction based on the ballot status +// The state is committed only if the individual steps are successful +func (k Keeper) ProcessOutbound(ctx sdk.Context, cctx *types.CrossChainTx, ballotStatus observertypes.BallotStatus, valueReceived string) error { + tmpCtx, commit := ctx.CacheContext() + err := func() error { + switch ballotStatus { + case observertypes.BallotStatus_BallotFinalized_SuccessObservation: + k.ProcessSuccessfulOutbound(tmpCtx, cctx, valueReceived) + case observertypes.BallotStatus_BallotFinalized_FailureObservation: + err := k.ProcessFailedOutbound(tmpCtx, cctx, valueReceived) + if err != nil { + return err + } + } + return nil + }() + if err != nil { + return err + } + err = cctx.Validate() + if err != nil { + return err + } + commit() + return nil +} diff --git a/x/crosschain/keeper/refund_test.go b/x/crosschain/keeper/refund_test.go index 933359008f..b1b893f71e 100644 --- a/x/crosschain/keeper/refund_test.go +++ b/x/crosschain/keeper/refund_test.go @@ -139,7 +139,7 @@ func TestKeeper_RefundAmountOnZetaChainZeta(t *testing.T) { sender, ) require.NoError(t, err) - coin := sdkk.BankKeeper.GetBalance(ctx, sdk.AccAddress(sender.Bytes()), config.BaseDenom) + coin := sdkk.BankKeeper.GetBalance(ctx, sender.Bytes(), config.BaseDenom) fmt.Println(coin.Amount.String()) require.Equal(t, "42", coin.Amount.String()) }) @@ -210,6 +210,7 @@ func TestKeeper_RefundAmountOnZetaChainERC20(t *testing.T) { ) err := k.RefundAmountOnZetaChainERC20(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ CoinType: coin.CoinType_ERC20, SenderChainId: chainID, @@ -232,6 +233,7 @@ func TestKeeper_RefundAmountOnZetaChainERC20(t *testing.T) { // can refund again err = k.RefundAmountOnZetaChainERC20(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ CoinType: coin.CoinType_ERC20, SenderChainId: chainID, @@ -251,6 +253,7 @@ func TestKeeper_RefundAmountOnZetaChainERC20(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) err := k.RefundAmountOnZetaChainERC20(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ CoinType: coin.CoinType_Zeta, Amount: math.NewUint(42), diff --git a/x/crosschain/migrations/v4/migrate.go b/x/crosschain/migrations/v4/migrate.go index da289e062e..8682042fcb 100644 --- a/x/crosschain/migrations/v4/migrate.go +++ b/x/crosschain/migrations/v4/migrate.go @@ -55,7 +55,7 @@ func SetZetaAccounting( for ; iterator.Valid(); iterator.Next() { var val types.CrossChainTx cdc.MustUnmarshal(iterator.Value(), &val) - if val.CctxStatus.Status == types.CctxStatus_Aborted && val.GetCurrentOutTxParam().CoinType == coin.CoinType_Zeta { + if val.CctxStatus.Status == types.CctxStatus_Aborted && val.InboundTxParams.CoinType == coin.CoinType_Zeta { abortedAmountZeta = abortedAmountZeta.Add(val.GetCurrentOutTxParam().Amount) } } diff --git a/x/crosschain/migrations/v4/migrate_test.go b/x/crosschain/migrations/v4/migrate_test.go index c4c68d93a5..4ba61e399a 100644 --- a/x/crosschain/migrations/v4/migrate_test.go +++ b/x/crosschain/migrations/v4/migrate_test.go @@ -169,6 +169,9 @@ func SetRandomCctx(ctx sdk.Context, k keeper.Keeper) sdkmath.Uint { k.SetCrossChainTx(ctx, types.CrossChainTx{ Index: fmt.Sprintf("%d", i), CctxStatus: &types.Status{Status: types.CctxStatus_Aborted}, + InboundTxParams: &types.InboundTxParams{ + CoinType: coin.CoinType_Zeta, + }, OutboundTxParams: []*types.OutboundTxParams{{ Amount: amount, CoinType: coin.CoinType_Zeta, diff --git a/x/crosschain/migrations/v5/migrate_test.go b/x/crosschain/migrations/v5/migrate_test.go index 3e9e535fda..3bed70bcb1 100644 --- a/x/crosschain/migrations/v5/migrate_test.go +++ b/x/crosschain/migrations/v5/migrate_test.go @@ -25,7 +25,7 @@ func TestMigrateStore(t *testing.T) { v4ZetaAccountingAmount := math.ZeroUint() for _, cctx := range cctxList { k.SetCrossChainTx(ctx, cctx) - if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_Aborted || cctx.GetCurrentOutTxParam().CoinType != coin.CoinType_Zeta { + if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_Aborted || cctx.InboundTxParams.CoinType != coin.CoinType_Zeta { continue } v5ZetaAccountingAmount = v5ZetaAccountingAmount.Add(crosschainkeeper.GetAbortedAmount(cctx)) diff --git a/x/crosschain/module.go b/x/crosschain/module.go index 7c81f220e8..45d23053da 100644 --- a/x/crosschain/module.go +++ b/x/crosschain/module.go @@ -154,6 +154,7 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { 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/authz.go b/x/crosschain/types/authz.go new file mode 100644 index 0000000000..6ee58171ff --- /dev/null +++ b/x/crosschain/types/authz.go @@ -0,0 +1,19 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" +) + +// GetAllAuthzZetaclientTxTypes returns all the authz types for required for zetaclient +func GetAllAuthzZetaclientTxTypes() []string { + return []string{ + sdk.MsgTypeURL(&MsgGasPriceVoter{}), + sdk.MsgTypeURL(&MsgVoteOnObservedInboundTx{}), + sdk.MsgTypeURL(&MsgVoteOnObservedOutboundTx{}), + sdk.MsgTypeURL(&MsgAddToOutTxTracker{}), + sdk.MsgTypeURL(&observertypes.MsgVoteTSS{}), + sdk.MsgTypeURL(&observertypes.MsgAddBlameVote{}), + sdk.MsgTypeURL(&observertypes.MsgAddBlockHeader{}), + } +} diff --git a/x/crosschain/types/authz_test.go b/x/crosschain/types/authz_test.go new file mode 100644 index 0000000000..7c615d7c6b --- /dev/null +++ b/x/crosschain/types/authz_test.go @@ -0,0 +1,19 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func TestGetAllAuthzZetaclientTxTypes(t *testing.T) { + require.Equal(t, []string{"/zetachain.zetacore.crosschain.MsgGasPriceVoter", + "/zetachain.zetacore.crosschain.MsgVoteOnObservedInboundTx", + "/zetachain.zetacore.crosschain.MsgVoteOnObservedOutboundTx", + "/zetachain.zetacore.crosschain.MsgAddToOutTxTracker", + "/zetachain.zetacore.observer.MsgVoteTSS", + "/zetachain.zetacore.observer.MsgAddBlameVote", + "/zetachain.zetacore.observer.MsgAddBlockHeader"}, + crosschaintypes.GetAllAuthzZetaclientTxTypes()) +} diff --git a/x/crosschain/types/cctx.go b/x/crosschain/types/cctx.go new file mode 100644 index 0000000000..45138232fc --- /dev/null +++ b/x/crosschain/types/cctx.go @@ -0,0 +1,197 @@ +package types + +import ( + "fmt" + + cosmoserrors "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" +) + +// GetCurrentOutTxParam returns the current outbound tx params. +// There can only be one active outtx. +// OutboundTxParams[0] is the original outtx, if it reverts, then +// OutboundTxParams[1] is the new outtx. +func (m CrossChainTx) GetCurrentOutTxParam() *OutboundTxParams { + if len(m.OutboundTxParams) == 0 { + return &OutboundTxParams{} + } + return m.OutboundTxParams[len(m.OutboundTxParams)-1] +} + +// IsCurrentOutTxRevert returns true if the current outbound tx is the revert tx. +func (m CrossChainTx) IsCurrentOutTxRevert() bool { + return len(m.OutboundTxParams) >= 2 +} + +// OriginalDestinationChainID returns the original destination of the outbound tx, reverted or not +// If there is no outbound tx, return -1 +func (m CrossChainTx) OriginalDestinationChainID() int64 { + if len(m.OutboundTxParams) == 0 { + return -1 + } + return m.OutboundTxParams[0].ReceiverChainId +} + +// Validate checks if the CCTX is valid. +func (m CrossChainTx) Validate() error { + if m.InboundTxParams == nil { + return fmt.Errorf("inbound tx params cannot be nil") + } + if m.OutboundTxParams == nil { + return fmt.Errorf("outbound tx params cannot be nil") + } + if m.CctxStatus == nil { + return fmt.Errorf("cctx status cannot be nil") + } + if len(m.OutboundTxParams) > 2 { + return fmt.Errorf("outbound tx params cannot be more than 2") + } + if m.Index != "" { + err := ValidateZetaIndex(m.Index) + if err != nil { + return err + } + } + err := m.InboundTxParams.Validate() + if err != nil { + return err + } + for _, outboundTxParam := range m.OutboundTxParams { + err = outboundTxParam.Validate() + if err != nil { + return err + } + } + return nil +} + +/* +AddRevertOutbound does the following things in one function: + + 1. create a new OutboundTxParams for the revert + + 2. append the new OutboundTxParams to the current OutboundTxParams + + 3. update the TxFinalizationStatus of the current OutboundTxParams to Executed. +*/ + +func (m *CrossChainTx) AddRevertOutbound(gasLimit uint64) error { + if m.IsCurrentOutTxRevert() { + return fmt.Errorf("cannot revert a revert tx") + } + revertTxParams := &OutboundTxParams{ + Receiver: m.InboundTxParams.Sender, + ReceiverChainId: m.InboundTxParams.SenderChainId, + Amount: m.InboundTxParams.Amount, + OutboundTxGasLimit: gasLimit, + TssPubkey: m.GetCurrentOutTxParam().TssPubkey, + } + // The original outbound has been finalized, the new outbound is pending + m.GetCurrentOutTxParam().TxFinalizationStatus = TxFinalizationStatus_Executed + m.OutboundTxParams = append(m.OutboundTxParams, revertTxParams) + return nil +} + +// AddOutbound adds a new outbound tx to the CCTX. +func (m *CrossChainTx) AddOutbound(ctx sdk.Context, msg MsgVoteOnObservedOutboundTx, ballotStatus observertypes.BallotStatus) error { + if ballotStatus != observertypes.BallotStatus_BallotFinalized_FailureObservation { + if !msg.ValueReceived.Equal(m.GetCurrentOutTxParam().Amount) { + ctx.Logger().Error(fmt.Sprintf("VoteOnObservedOutboundTx: Mint mismatch: %s value received vs %s cctx amount", + msg.ValueReceived, + m.GetCurrentOutTxParam().Amount)) + return cosmoserrors.Wrap(sdkerrors.ErrInvalidRequest, fmt.Sprintf("ValueReceived %s does not match sent value %s", msg.ValueReceived, m.GetCurrentOutTxParam().Amount)) + } + } + // Update CCTX values + m.GetCurrentOutTxParam().OutboundTxHash = msg.ObservedOutTxHash + m.GetCurrentOutTxParam().OutboundTxGasUsed = msg.ObservedOutTxGasUsed + m.GetCurrentOutTxParam().OutboundTxEffectiveGasPrice = msg.ObservedOutTxEffectiveGasPrice + m.GetCurrentOutTxParam().OutboundTxEffectiveGasLimit = msg.ObservedOutTxEffectiveGasLimit + m.GetCurrentOutTxParam().OutboundTxObservedExternalHeight = msg.ObservedOutTxBlockHeight + m.CctxStatus.LastUpdateTimestamp = ctx.BlockHeader().Time.Unix() + return nil +} + +// SetAbort sets the CCTX status to Aborted with the given error message. +func (m CrossChainTx) SetAbort(message string) { + m.CctxStatus.ChangeStatus(CctxStatus_Aborted, message) +} + +// SetPendingRevert sets the CCTX status to PendingRevert with the given error message. +func (m CrossChainTx) SetPendingRevert(message string) { + m.CctxStatus.ChangeStatus(CctxStatus_PendingRevert, message) +} + +// SetPendingOutbound sets the CCTX status to PendingOutbound with the given error message. +func (m CrossChainTx) SetPendingOutbound(message string) { + m.CctxStatus.ChangeStatus(CctxStatus_PendingOutbound, message) +} + +// SetOutBoundMined sets the CCTX status to OutboundMined with the given error message. +func (m CrossChainTx) SetOutBoundMined(message string) { + m.CctxStatus.ChangeStatus(CctxStatus_OutboundMined, message) +} + +// SetReverted sets the CCTX status to Reverted with the given error message. +func (m CrossChainTx) SetReverted(message string) { + m.CctxStatus.ChangeStatus(CctxStatus_Reverted, message) +} + +// NewCCTX creates a new CCTX.From a MsgVoteOnObservedInboundTx message and a TSS pubkey. +// It also validates the created cctx +func NewCCTX(ctx sdk.Context, msg MsgVoteOnObservedInboundTx, tssPubkey string) (CrossChainTx, error) { + index := msg.Digest() + + if msg.TxOrigin == "" { + msg.TxOrigin = msg.Sender + } + inboundParams := &InboundTxParams{ + Sender: msg.Sender, + SenderChainId: msg.SenderChainId, + TxOrigin: msg.TxOrigin, + Asset: msg.Asset, + Amount: msg.Amount, + InboundTxObservedHash: msg.InTxHash, + InboundTxObservedExternalHeight: msg.InBlockHeight, + InboundTxFinalizedZetaHeight: 0, + InboundTxBallotIndex: index, + CoinType: msg.CoinType, + } + + outBoundParams := &OutboundTxParams{ + Receiver: msg.Receiver, + ReceiverChainId: msg.ReceiverChain, + OutboundTxHash: "", + OutboundTxTssNonce: 0, + OutboundTxGasLimit: msg.GasLimit, + OutboundTxGasPrice: "", + OutboundTxBallotIndex: "", + OutboundTxObservedExternalHeight: 0, + Amount: sdkmath.ZeroUint(), + TssPubkey: tssPubkey, + CoinType: msg.CoinType, + } + status := &Status{ + Status: CctxStatus_PendingInbound, + StatusMessage: "", + LastUpdateTimestamp: ctx.BlockHeader().Time.Unix(), + IsAbortRefunded: false, + } + cctx := CrossChainTx{ + Creator: msg.Creator, + Index: index, + ZetaFees: sdkmath.ZeroUint(), + RelayedMessage: msg.Message, + CctxStatus: status, + InboundTxParams: inboundParams, + OutboundTxParams: []*OutboundTxParams{outBoundParams}, + } + err := cctx.Validate() + if err != nil { + return CrossChainTx{}, err + } + return cctx, nil +} diff --git a/x/crosschain/types/cctx_test.go b/x/crosschain/types/cctx_test.go new file mode 100644 index 0000000000..15df60238e --- /dev/null +++ b/x/crosschain/types/cctx_test.go @@ -0,0 +1,298 @@ +package types_test + +import ( + "math/rand" + "testing" + + sdkmath "cosmossdk.io/math" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/crosschain/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" +) + +func Test_InitializeCCTX(t *testing.T) { + t.Run("should return a cctx with correct values", func(t *testing.T) { + _, ctx, _, _ := keepertest.CrosschainKeeper(t) + senderChain := chains.GoerliChain() + sender := sample.EthAddress() + receiverChain := chains.GoerliChain() + receiver := sample.EthAddress() + creator := sample.AccAddress() + amount := sdkmath.NewUint(42) + message := "test" + intxBlockHeight := uint64(420) + intxHash := sample.Hash() + gasLimit := uint64(100) + asset := "test-asset" + eventIndex := uint64(1) + cointType := coin.CoinType_ERC20 + tss := sample.Tss() + msg := types.MsgVoteOnObservedInboundTx{ + Creator: creator, + Sender: sender.String(), + SenderChainId: senderChain.ChainId, + Receiver: receiver.String(), + ReceiverChain: receiverChain.ChainId, + Amount: amount, + Message: message, + InTxHash: intxHash.String(), + InBlockHeight: intxBlockHeight, + GasLimit: gasLimit, + CoinType: cointType, + TxOrigin: sender.String(), + Asset: asset, + EventIndex: eventIndex, + } + cctx, err := types.NewCCTX(ctx, msg, tss.TssPubkey) + require.NoError(t, err) + require.Equal(t, receiver.String(), cctx.GetCurrentOutTxParam().Receiver) + require.Equal(t, receiverChain.ChainId, cctx.GetCurrentOutTxParam().ReceiverChainId) + require.Equal(t, sender.String(), cctx.GetInboundTxParams().Sender) + require.Equal(t, senderChain.ChainId, cctx.GetInboundTxParams().SenderChainId) + require.Equal(t, amount, cctx.GetInboundTxParams().Amount) + require.Equal(t, message, cctx.RelayedMessage) + require.Equal(t, intxHash.String(), cctx.GetInboundTxParams().InboundTxObservedHash) + require.Equal(t, intxBlockHeight, cctx.GetInboundTxParams().InboundTxObservedExternalHeight) + require.Equal(t, gasLimit, cctx.GetCurrentOutTxParam().OutboundTxGasLimit) + require.Equal(t, asset, cctx.GetInboundTxParams().Asset) + require.Equal(t, cointType, cctx.InboundTxParams.CoinType) + require.Equal(t, uint64(0), cctx.GetCurrentOutTxParam().OutboundTxTssNonce) + require.Equal(t, sdkmath.ZeroUint(), cctx.GetCurrentOutTxParam().Amount) + require.Equal(t, types.CctxStatus_PendingInbound, cctx.CctxStatus.Status) + require.Equal(t, false, cctx.CctxStatus.IsAbortRefunded) + }) + t.Run("should return an error if the cctx is invalid", func(t *testing.T) { + _, ctx, _, _ := keepertest.CrosschainKeeper(t) + senderChain := chains.GoerliChain() + sender := sample.EthAddress() + receiverChain := chains.GoerliChain() + receiver := sample.EthAddress() + creator := sample.AccAddress() + amount := sdkmath.NewUint(42) + message := "test" + intxBlockHeight := uint64(420) + intxHash := sample.Hash() + gasLimit := uint64(100) + asset := "test-asset" + eventIndex := uint64(1) + cointType := coin.CoinType_ERC20 + tss := sample.Tss() + msg := types.MsgVoteOnObservedInboundTx{ + Creator: creator, + Sender: "invalid", + SenderChainId: senderChain.ChainId, + Receiver: receiver.String(), + ReceiverChain: receiverChain.ChainId, + Amount: amount, + Message: message, + InTxHash: intxHash.String(), + InBlockHeight: intxBlockHeight, + GasLimit: gasLimit, + CoinType: cointType, + TxOrigin: sender.String(), + Asset: asset, + EventIndex: eventIndex, + } + _, err := types.NewCCTX(ctx, msg, tss.TssPubkey) + require.ErrorContains(t, err, "invalid address") + }) +} + +func TestCrossChainTx_Validate(t *testing.T) { + cctx := sample.CrossChainTx(t, "foo") + cctx.InboundTxParams = nil + require.ErrorContains(t, cctx.Validate(), "inbound tx params cannot be nil") + cctx = sample.CrossChainTx(t, "foo") + cctx.OutboundTxParams = nil + require.ErrorContains(t, cctx.Validate(), "outbound tx params cannot be nil") + cctx = sample.CrossChainTx(t, "foo") + cctx.CctxStatus = nil + require.ErrorContains(t, cctx.Validate(), "cctx status cannot be nil") + cctx = sample.CrossChainTx(t, "foo") + cctx.OutboundTxParams = make([]*types.OutboundTxParams, 3) + require.ErrorContains(t, cctx.Validate(), "outbound tx params cannot be more than 2") + cctx = sample.CrossChainTx(t, "foo") + cctx.Index = "0" + require.ErrorContains(t, cctx.Validate(), "invalid index length 1") + cctx = sample.CrossChainTx(t, "foo") + cctx.InboundTxParams = sample.InboundTxParamsValidChainID(rand.New(rand.NewSource(42))) + cctx.InboundTxParams.SenderChainId = 1000 + require.ErrorContains(t, cctx.Validate(), "invalid sender chain id 1000") + cctx = sample.CrossChainTx(t, "foo") + cctx.OutboundTxParams = []*types.OutboundTxParams{sample.OutboundTxParamsValidChainID(rand.New(rand.NewSource(42)))} + cctx.InboundTxParams = sample.InboundTxParamsValidChainID(rand.New(rand.NewSource(42))) + cctx.InboundTxParams.InboundTxObservedHash = sample.Hash().String() + cctx.InboundTxParams.InboundTxBallotIndex = sample.ZetaIndex(t) + cctx.OutboundTxParams[0].ReceiverChainId = 1000 + require.ErrorContains(t, cctx.Validate(), "invalid receiver chain id 1000") +} + +func TestCrossChainTx_GetCurrentOutTxParam(t *testing.T) { + r := rand.New(rand.NewSource(42)) + cctx := sample.CrossChainTx(t, "foo") + + cctx.OutboundTxParams = []*types.OutboundTxParams{} + require.Equal(t, &types.OutboundTxParams{}, cctx.GetCurrentOutTxParam()) + + cctx.OutboundTxParams = []*types.OutboundTxParams{sample.OutboundTxParams(r)} + require.Equal(t, cctx.OutboundTxParams[0], cctx.GetCurrentOutTxParam()) + + cctx.OutboundTxParams = []*types.OutboundTxParams{sample.OutboundTxParams(r), sample.OutboundTxParams(r)} + require.Equal(t, cctx.OutboundTxParams[1], cctx.GetCurrentOutTxParam()) +} + +func TestCrossChainTx_IsCurrentOutTxRevert(t *testing.T) { + r := rand.New(rand.NewSource(42)) + cctx := sample.CrossChainTx(t, "foo") + + cctx.OutboundTxParams = []*types.OutboundTxParams{} + require.False(t, cctx.IsCurrentOutTxRevert()) + + cctx.OutboundTxParams = []*types.OutboundTxParams{sample.OutboundTxParams(r)} + require.False(t, cctx.IsCurrentOutTxRevert()) + + cctx.OutboundTxParams = []*types.OutboundTxParams{sample.OutboundTxParams(r), sample.OutboundTxParams(r)} + require.True(t, cctx.IsCurrentOutTxRevert()) +} + +func TestCrossChainTx_OriginalDestinationChainID(t *testing.T) { + r := rand.New(rand.NewSource(42)) + cctx := sample.CrossChainTx(t, "foo") + + cctx.OutboundTxParams = []*types.OutboundTxParams{} + require.Equal(t, int64(-1), cctx.OriginalDestinationChainID()) + + cctx.OutboundTxParams = []*types.OutboundTxParams{sample.OutboundTxParams(r)} + require.Equal(t, cctx.OutboundTxParams[0].ReceiverChainId, cctx.OriginalDestinationChainID()) + + cctx.OutboundTxParams = []*types.OutboundTxParams{sample.OutboundTxParams(r), sample.OutboundTxParams(r)} + require.Equal(t, cctx.OutboundTxParams[0].ReceiverChainId, cctx.OriginalDestinationChainID()) +} + +func TestCrossChainTx_AddOutbound(t *testing.T) { + t.Run("successfully get outbound tx", func(t *testing.T) { + _, ctx, _, _ := keepertest.CrosschainKeeper(t) + cctx := sample.CrossChainTx(t, "test") + hash := sample.Hash().String() + + err := cctx.AddOutbound(ctx, types.MsgVoteOnObservedOutboundTx{ + ValueReceived: cctx.GetCurrentOutTxParam().Amount, + ObservedOutTxHash: hash, + ObservedOutTxBlockHeight: 10, + ObservedOutTxGasUsed: 100, + ObservedOutTxEffectiveGasPrice: sdkmath.NewInt(100), + ObservedOutTxEffectiveGasLimit: 20, + }, observertypes.BallotStatus_BallotFinalized_SuccessObservation) + require.NoError(t, err) + require.Equal(t, cctx.GetCurrentOutTxParam().OutboundTxHash, hash) + require.Equal(t, cctx.GetCurrentOutTxParam().OutboundTxGasUsed, uint64(100)) + require.Equal(t, cctx.GetCurrentOutTxParam().OutboundTxEffectiveGasPrice, sdkmath.NewInt(100)) + require.Equal(t, cctx.GetCurrentOutTxParam().OutboundTxEffectiveGasLimit, uint64(20)) + require.Equal(t, cctx.GetCurrentOutTxParam().OutboundTxObservedExternalHeight, uint64(10)) + require.Equal(t, cctx.CctxStatus.LastUpdateTimestamp, ctx.BlockHeader().Time.Unix()) + }) + + t.Run("successfully get outbound tx for failed ballot without amount check", func(t *testing.T) { + _, ctx, _, _ := keepertest.CrosschainKeeper(t) + cctx := sample.CrossChainTx(t, "test") + hash := sample.Hash().String() + + err := cctx.AddOutbound(ctx, types.MsgVoteOnObservedOutboundTx{ + ObservedOutTxHash: hash, + ObservedOutTxBlockHeight: 10, + ObservedOutTxGasUsed: 100, + ObservedOutTxEffectiveGasPrice: sdkmath.NewInt(100), + ObservedOutTxEffectiveGasLimit: 20, + }, observertypes.BallotStatus_BallotFinalized_FailureObservation) + require.NoError(t, err) + require.Equal(t, cctx.GetCurrentOutTxParam().OutboundTxHash, hash) + require.Equal(t, cctx.GetCurrentOutTxParam().OutboundTxGasUsed, uint64(100)) + require.Equal(t, cctx.GetCurrentOutTxParam().OutboundTxEffectiveGasPrice, sdkmath.NewInt(100)) + require.Equal(t, cctx.GetCurrentOutTxParam().OutboundTxEffectiveGasLimit, uint64(20)) + require.Equal(t, cctx.GetCurrentOutTxParam().OutboundTxObservedExternalHeight, uint64(10)) + require.Equal(t, cctx.CctxStatus.LastUpdateTimestamp, ctx.BlockHeader().Time.Unix()) + }) + + t.Run("failed to get outbound tx if amount does not match value received", func(t *testing.T) { + _, ctx, _, _ := keepertest.CrosschainKeeper(t) + + cctx := sample.CrossChainTx(t, "test") + hash := sample.Hash().String() + + err := cctx.AddOutbound(ctx, types.MsgVoteOnObservedOutboundTx{ + ValueReceived: sdkmath.NewUint(100), + ObservedOutTxHash: hash, + ObservedOutTxBlockHeight: 10, + ObservedOutTxGasUsed: 100, + ObservedOutTxEffectiveGasPrice: sdkmath.NewInt(100), + ObservedOutTxEffectiveGasLimit: 20, + }, observertypes.BallotStatus_BallotFinalized_SuccessObservation) + require.ErrorIs(t, err, sdkerrors.ErrInvalidRequest) + }) +} + +func Test_SetRevertOutboundValues(t *testing.T) { + t.Run("successfully set revert outbound values", func(t *testing.T) { + cctx := sample.CrossChainTx(t, "test") + cctx.OutboundTxParams = cctx.OutboundTxParams[:1] + err := cctx.AddRevertOutbound(100) + require.NoError(t, err) + require.Len(t, cctx.OutboundTxParams, 2) + require.Equal(t, cctx.GetCurrentOutTxParam().Receiver, cctx.InboundTxParams.Sender) + require.Equal(t, cctx.GetCurrentOutTxParam().ReceiverChainId, cctx.InboundTxParams.SenderChainId) + require.Equal(t, cctx.GetCurrentOutTxParam().Amount, cctx.InboundTxParams.Amount) + require.Equal(t, cctx.GetCurrentOutTxParam().OutboundTxGasLimit, uint64(100)) + require.Equal(t, cctx.GetCurrentOutTxParam().TssPubkey, cctx.OutboundTxParams[0].TssPubkey) + require.Equal(t, types.TxFinalizationStatus_Executed, cctx.OutboundTxParams[0].TxFinalizationStatus) + }) + + t.Run("failed to set revert outbound values if revert outbound already exists", func(t *testing.T) { + cctx := sample.CrossChainTx(t, "test") + err := cctx.AddRevertOutbound(100) + require.ErrorContains(t, err, "cannot revert a revert tx") + }) +} + +func TestCrossChainTx_SetAbort(t *testing.T) { + cctx := sample.CrossChainTx(t, "test") + cctx.SetAbort("test") + require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) + require.Equal(t, "test", "test") +} + +func TestCrossChainTx_SetPendingRevert(t *testing.T) { + cctx := sample.CrossChainTx(t, "test") + cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound + cctx.SetPendingRevert("test") + require.Equal(t, types.CctxStatus_PendingRevert, cctx.CctxStatus.Status) + require.Contains(t, cctx.CctxStatus.StatusMessage, "test") +} + +func TestCrossChainTx_SetPendingOutbound(t *testing.T) { + cctx := sample.CrossChainTx(t, "test") + cctx.CctxStatus.Status = types.CctxStatus_PendingInbound + cctx.SetPendingOutbound("test") + require.Equal(t, types.CctxStatus_PendingOutbound, cctx.CctxStatus.Status) + require.Contains(t, cctx.CctxStatus.StatusMessage, "test") +} + +func TestCrossChainTx_SetOutBoundMined(t *testing.T) { + cctx := sample.CrossChainTx(t, "test") + cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound + cctx.SetOutBoundMined("test") + require.Equal(t, types.CctxStatus_OutboundMined, cctx.CctxStatus.Status) + require.Contains(t, cctx.CctxStatus.StatusMessage, "test") +} + +func TestCrossChainTx_SetReverted(t *testing.T) { + cctx := sample.CrossChainTx(t, "test") + cctx.CctxStatus.Status = types.CctxStatus_PendingRevert + cctx.SetReverted("test") + require.Equal(t, types.CctxStatus_Reverted, cctx.CctxStatus.Status) + require.Contains(t, cctx.CctxStatus.StatusMessage, "test") +} diff --git a/x/crosschain/types/cctx_utils.go b/x/crosschain/types/cctx_utils.go deleted file mode 100644 index bf3357c709..0000000000 --- a/x/crosschain/types/cctx_utils.go +++ /dev/null @@ -1,57 +0,0 @@ -package types - -import ( - "fmt" - "strconv" - - sdk "github.com/cosmos/cosmos-sdk/types" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" -) - -// GetCurrentOutTxParam returns the current outbound tx params. -// There can only be one active outtx. -// OutboundTxParams[0] is the original outtx, if it reverts, then -// OutboundTxParams[1] is the new outtx. -func (m CrossChainTx) GetCurrentOutTxParam() *OutboundTxParams { - if len(m.OutboundTxParams) == 0 { - return &OutboundTxParams{} - } - return m.OutboundTxParams[len(m.OutboundTxParams)-1] -} - -// IsCurrentOutTxRevert returns true if the current outbound tx is the revert tx. -func (m CrossChainTx) IsCurrentOutTxRevert() bool { - return len(m.OutboundTxParams) == 2 -} - -// OriginalDestinationChainID returns the original destination of the outbound tx, reverted or not -// If there is no outbound tx, return -1 -func (m CrossChainTx) OriginalDestinationChainID() int64 { - if len(m.OutboundTxParams) == 0 { - return -1 - } - return m.OutboundTxParams[0].ReceiverChainId -} - -// GetAllAuthzZetaclientTxTypes returns all the authz types for zetaclient -func GetAllAuthzZetaclientTxTypes() []string { - return []string{ - sdk.MsgTypeURL(&MsgGasPriceVoter{}), - sdk.MsgTypeURL(&MsgVoteOnObservedInboundTx{}), - sdk.MsgTypeURL(&MsgVoteOnObservedOutboundTx{}), - sdk.MsgTypeURL(&MsgCreateTSSVoter{}), - sdk.MsgTypeURL(&MsgAddToOutTxTracker{}), - sdk.MsgTypeURL(&observertypes.MsgAddBlameVote{}), - sdk.MsgTypeURL(&observertypes.MsgAddBlockHeader{}), - } -} - -// GetGasPrice returns the gas price of the outbound tx -func (m OutboundTxParams) GetGasPrice() (uint64, error) { - gasPrice, err := strconv.ParseUint(m.OutboundTxGasPrice, 10, 64) - if err != nil { - return 0, fmt.Errorf("unable to parse cctx gas price %s: %s", m.OutboundTxGasPrice, err.Error()) - } - - return gasPrice, nil -} diff --git a/x/crosschain/types/cctx_utils_test.go b/x/crosschain/types/cctx_utils_test.go deleted file mode 100644 index 381ef47f6c..0000000000 --- a/x/crosschain/types/cctx_utils_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package types_test - -import ( - "math/rand" - "testing" - - "github.com/stretchr/testify/require" - "github.com/zeta-chain/zetacore/testutil/sample" - "github.com/zeta-chain/zetacore/x/crosschain/types" -) - -func TestCrossChainTx_GetCurrentOutTxParam(t *testing.T) { - r := rand.New(rand.NewSource(42)) - cctx := sample.CrossChainTx(t, "foo") - - cctx.OutboundTxParams = []*types.OutboundTxParams{} - require.Equal(t, &types.OutboundTxParams{}, cctx.GetCurrentOutTxParam()) - - cctx.OutboundTxParams = []*types.OutboundTxParams{sample.OutboundTxParams(r)} - require.Equal(t, cctx.OutboundTxParams[0], cctx.GetCurrentOutTxParam()) - - cctx.OutboundTxParams = []*types.OutboundTxParams{sample.OutboundTxParams(r), sample.OutboundTxParams(r)} - require.Equal(t, cctx.OutboundTxParams[1], cctx.GetCurrentOutTxParam()) -} - -func TestCrossChainTx_IsCurrentOutTxRevert(t *testing.T) { - r := rand.New(rand.NewSource(42)) - cctx := sample.CrossChainTx(t, "foo") - - cctx.OutboundTxParams = []*types.OutboundTxParams{} - require.False(t, cctx.IsCurrentOutTxRevert()) - - cctx.OutboundTxParams = []*types.OutboundTxParams{sample.OutboundTxParams(r)} - require.False(t, cctx.IsCurrentOutTxRevert()) - - cctx.OutboundTxParams = []*types.OutboundTxParams{sample.OutboundTxParams(r), sample.OutboundTxParams(r)} - require.True(t, cctx.IsCurrentOutTxRevert()) -} - -func TestCrossChainTx_OriginalDestinationChainID(t *testing.T) { - r := rand.New(rand.NewSource(42)) - cctx := sample.CrossChainTx(t, "foo") - - cctx.OutboundTxParams = []*types.OutboundTxParams{} - require.Equal(t, int64(-1), cctx.OriginalDestinationChainID()) - - cctx.OutboundTxParams = []*types.OutboundTxParams{sample.OutboundTxParams(r)} - require.Equal(t, cctx.OutboundTxParams[0].ReceiverChainId, cctx.OriginalDestinationChainID()) - - cctx.OutboundTxParams = []*types.OutboundTxParams{sample.OutboundTxParams(r), sample.OutboundTxParams(r)} - require.Equal(t, cctx.OutboundTxParams[0].ReceiverChainId, cctx.OriginalDestinationChainID()) -} - -func TestOutboundTxParams_GetGasPrice(t *testing.T) { - // #nosec G404 - random seed is not used for security purposes - r := rand.New(rand.NewSource(42)) - outTxParams := sample.OutboundTxParams(r) - - outTxParams.OutboundTxGasPrice = "42" - gasPrice, err := outTxParams.GetGasPrice() - require.NoError(t, err) - require.EqualValues(t, uint64(42), gasPrice) - - outTxParams.OutboundTxGasPrice = "invalid" - _, err = outTxParams.GetGasPrice() - require.Error(t, err) -} diff --git a/x/crosschain/types/codec.go b/x/crosschain/types/codec.go index fb3d51db0a..7fcbc0cc0c 100644 --- a/x/crosschain/types/codec.go +++ b/x/crosschain/types/codec.go @@ -11,7 +11,6 @@ func RegisterCodec(cdc *codec.LegacyAmino) { cdc.RegisterConcrete(&MsgAddToOutTxTracker{}, "crosschain/AddToOutTxTracker", nil) cdc.RegisterConcrete(&MsgAddToInTxTracker{}, "crosschain/AddToInTxTracker", nil) cdc.RegisterConcrete(&MsgRemoveFromOutTxTracker{}, "crosschain/RemoveFromOutTxTracker", nil) - cdc.RegisterConcrete(&MsgCreateTSSVoter{}, "crosschain/CreateTSSVoter", nil) cdc.RegisterConcrete(&MsgGasPriceVoter{}, "crosschain/GasPriceVoter", nil) cdc.RegisterConcrete(&MsgVoteOnObservedOutboundTx{}, "crosschain/VoteOnObservedOutboundTx", nil) cdc.RegisterConcrete(&MsgVoteOnObservedInboundTx{}, "crosschain/VoteOnObservedInboundTx", nil) @@ -26,7 +25,6 @@ func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { &MsgAddToOutTxTracker{}, &MsgAddToInTxTracker{}, &MsgRemoveFromOutTxTracker{}, - &MsgCreateTSSVoter{}, &MsgGasPriceVoter{}, &MsgVoteOnObservedOutboundTx{}, &MsgVoteOnObservedInboundTx{}, diff --git a/x/crosschain/types/errors.go b/x/crosschain/types/errors.go index 2c5a595eb5..62720aaf66 100644 --- a/x/crosschain/types/errors.go +++ b/x/crosschain/types/errors.go @@ -36,7 +36,7 @@ var ( 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") + ErrInvalidIndexValue = errorsmod.Register(ModuleName, 1146, "invalid index hash") 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") diff --git a/x/crosschain/types/inbound_params.go b/x/crosschain/types/inbound_params.go new file mode 100644 index 0000000000..1cc3264eae --- /dev/null +++ b/x/crosschain/types/inbound_params.go @@ -0,0 +1,42 @@ +package types + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/zeta-chain/zetacore/pkg/chains" +) + +func (m InboundTxParams) Validate() error { + if m.Sender == "" { + return fmt.Errorf("sender cannot be empty") + } + if chains.GetChainFromChainID(m.SenderChainId) == nil { + return fmt.Errorf("invalid sender chain id %d", m.SenderChainId) + } + err := ValidateAddressForChain(m.Sender, m.SenderChainId) + if err != nil { + return err + } + + if m.TxOrigin != "" { + errTxOrigin := ValidateAddressForChain(m.TxOrigin, m.SenderChainId) + if errTxOrigin != nil { + return errTxOrigin + } + } + if m.Amount.IsNil() { + return fmt.Errorf("amount cannot be nil") + } + err = ValidateHashForChain(m.InboundTxObservedHash, m.SenderChainId) + if err != nil { + return errors.Wrap(err, "invalid inbound tx observed hash") + } + if m.InboundTxBallotIndex != "" { + err = ValidateZetaIndex(m.InboundTxBallotIndex) + if err != nil { + return errors.Wrap(err, "invalid inbound tx ballot index") + } + } + return nil +} diff --git a/x/crosschain/types/inbound_params_test.go b/x/crosschain/types/inbound_params_test.go new file mode 100644 index 0000000000..6b6cdf2293 --- /dev/null +++ b/x/crosschain/types/inbound_params_test.go @@ -0,0 +1,43 @@ +package types_test + +import ( + "math/rand" + "testing" + + sdkmath "cosmossdk.io/math" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/testutil/sample" +) + +func TestInboundTxParams_Validate(t *testing.T) { + r := rand.New(rand.NewSource(42)) + inTxParams := sample.InboundTxParamsValidChainID(r) + inTxParams.Sender = "" + require.ErrorContains(t, inTxParams.Validate(), "sender cannot be empty") + inTxParams = sample.InboundTxParamsValidChainID(r) + inTxParams.SenderChainId = 1000 + require.ErrorContains(t, inTxParams.Validate(), "invalid sender chain id 1000") + inTxParams = sample.InboundTxParamsValidChainID(r) + inTxParams.SenderChainId = chains.GoerliChain().ChainId + inTxParams.Sender = "0x123" + require.ErrorContains(t, inTxParams.Validate(), "invalid address 0x123") + inTxParams = sample.InboundTxParamsValidChainID(r) + inTxParams.SenderChainId = chains.GoerliChain().ChainId + inTxParams.TxOrigin = "0x123" + require.ErrorContains(t, inTxParams.Validate(), "invalid address 0x123") + inTxParams = sample.InboundTxParamsValidChainID(r) + inTxParams.Amount = sdkmath.Uint{} + require.ErrorContains(t, inTxParams.Validate(), "amount cannot be nil") + inTxParams = sample.InboundTxParamsValidChainID(r) + inTxParams.InboundTxObservedHash = "12" + require.ErrorContains(t, inTxParams.Validate(), "hash must be a valid ethereum hash 12") + inTxParams = sample.InboundTxParamsValidChainID(r) + inTxParams.InboundTxObservedHash = sample.Hash().String() + inTxParams.InboundTxBallotIndex = "12" + require.ErrorContains(t, inTxParams.Validate(), "invalid index length 2") + inTxParams = sample.InboundTxParamsValidChainID(r) + inTxParams.InboundTxObservedHash = sample.Hash().String() + inTxParams.InboundTxBallotIndex = sample.ZetaIndex(t) + require.NoError(t, inTxParams.Validate()) +} diff --git a/x/crosschain/types/keys.go b/x/crosschain/types/keys.go index 4adac4d73f..719f54687f 100644 --- a/x/crosschain/types/keys.go +++ b/x/crosschain/types/keys.go @@ -27,6 +27,7 @@ const ( ProtocolFee = 2000000000000000000 //TssMigrationGasMultiplierEVM is multiplied to the median gas price to get the gas price for the tss migration . This is done to avoid the tss migration tx getting stuck in the mempool TssMigrationGasMultiplierEVM = "2.5" + ZetaIndexLength = 66 ) func GetProtocolFee() sdk.Uint { @@ -75,7 +76,6 @@ func (m CrossChainTx) LogIdentifierForCCTX() string { i := len(m.OutboundTxParams) - 1 outTx := m.OutboundTxParams[i] return fmt.Sprintf("%s-%d-%d-%d", m.InboundTxParams.Sender, m.InboundTxParams.SenderChainId, outTx.ReceiverChainId, outTx.OutboundTxTssNonce) - } func FinalizedInboundKey(intxHash string, chainID int64, eventIndex uint64) string { diff --git a/x/crosschain/types/message_abort_stuck_cctx.go b/x/crosschain/types/message_abort_stuck_cctx.go index 47c383276b..86a1f31ec6 100644 --- a/x/crosschain/types/message_abort_stuck_cctx.go +++ b/x/crosschain/types/message_abort_stuck_cctx.go @@ -43,8 +43,8 @@ func (msg *MsgAbortStuckCCTX) ValidateBasic() error { if err != nil { return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err) } - if len(msg.CctxIndex) != 66 { - return ErrInvalidCCTXIndex + if len(msg.CctxIndex) != ZetaIndexLength { + return ErrInvalidIndexValue } return nil } diff --git a/x/crosschain/types/message_abort_stuck_cctx_test.go b/x/crosschain/types/message_abort_stuck_cctx_test.go index 14b1e82d78..5b0d539c0f 100644 --- a/x/crosschain/types/message_abort_stuck_cctx_test.go +++ b/x/crosschain/types/message_abort_stuck_cctx_test.go @@ -24,7 +24,7 @@ func TestMsgAbortStuckCCTX_ValidateBasic(t *testing.T) { { name: "invalid cctx index", msg: types.NewMsgAbortStuckCCTX(sample.AccAddress(), "cctx_index"), - err: types.ErrInvalidCCTXIndex, + err: types.ErrInvalidIndexValue, }, { name: "valid", diff --git a/x/crosschain/types/message_refund_aborted.go b/x/crosschain/types/message_refund_aborted.go index 698d499b09..d019b8ea31 100644 --- a/x/crosschain/types/message_refund_aborted.go +++ b/x/crosschain/types/message_refund_aborted.go @@ -45,8 +45,8 @@ func (msg *MsgRefundAbortedCCTX) ValidateBasic() error { if err != nil { return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err) } - if len(msg.CctxIndex) != 66 { - return ErrInvalidCCTXIndex + if len(msg.CctxIndex) != ZetaIndexLength { + return ErrInvalidIndexValue } if msg.RefundAddress != "" && !ethcommon.IsHexAddress(msg.RefundAddress) { return ErrInvalidAddress diff --git a/x/crosschain/types/message_refund_aborted_test.go b/x/crosschain/types/message_refund_aborted_test.go index b02b3dab56..2884b8b7c1 100644 --- a/x/crosschain/types/message_refund_aborted_test.go +++ b/x/crosschain/types/message_refund_aborted_test.go @@ -22,7 +22,7 @@ func TestMsgRefundAbortedCCTX_ValidateBasic(t *testing.T) { }) t.Run("invalid cctx index", func(t *testing.T) { msg := types.NewMsgRefundAbortedCCTX(sample.AccAddress(), "invalid", "") - require.ErrorContains(t, msg.ValidateBasic(), "invalid cctx index") + require.ErrorContains(t, msg.ValidateBasic(), "invalid index hash") }) t.Run("invalid refund address", func(t *testing.T) { cctx := sample.CrossChainTx(t, "test") diff --git a/x/crosschain/types/message_tss_voter.go b/x/crosschain/types/message_tss_voter.go deleted file mode 100644 index dc9abc4ccb..0000000000 --- a/x/crosschain/types/message_tss_voter.go +++ /dev/null @@ -1,57 +0,0 @@ -package types - -import ( - "fmt" - - cosmoserrors "cosmossdk.io/errors" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/zeta-chain/zetacore/pkg/chains" -) - -const TypeMsgCreateTSSVoter = "CreateTSSVoter" - -var _ sdk.Msg = &MsgCreateTSSVoter{} - -func NewMsgCreateTSSVoter(creator string, pubkey string, keygenZetaHeight int64, status chains.ReceiveStatus) *MsgCreateTSSVoter { - return &MsgCreateTSSVoter{ - Creator: creator, - TssPubkey: pubkey, - KeyGenZetaHeight: keygenZetaHeight, - Status: status, - } -} - -func (msg *MsgCreateTSSVoter) Route() string { - return RouterKey -} - -func (msg *MsgCreateTSSVoter) Type() string { - return TypeMsgCreateTSSVoter -} - -func (msg *MsgCreateTSSVoter) GetSigners() []sdk.AccAddress { - creator, err := sdk.AccAddressFromBech32(msg.Creator) - if err != nil { - panic(err) - } - return []sdk.AccAddress{creator} -} - -func (msg *MsgCreateTSSVoter) GetSignBytes() []byte { - bz := ModuleCdc.MustMarshalJSON(msg) - return sdk.MustSortJSON(bz) -} - -func (msg *MsgCreateTSSVoter) ValidateBasic() error { - _, err := sdk.AccAddressFromBech32(msg.Creator) - if err != nil { - return cosmoserrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err) - } - return nil -} - -func (msg *MsgCreateTSSVoter) Digest() string { - // We support only 1 keygen at a particular height - return fmt.Sprintf("%d-%s", msg.KeyGenZetaHeight, "tss-keygen") -} diff --git a/x/crosschain/types/message_tss_voter_test.go b/x/crosschain/types/message_tss_voter_test.go deleted file mode 100644 index 8c87c0e552..0000000000 --- a/x/crosschain/types/message_tss_voter_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package types_test - -import ( - "testing" - - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/stretchr/testify/require" - "github.com/zeta-chain/zetacore/pkg/chains" - "github.com/zeta-chain/zetacore/testutil/sample" - - "github.com/zeta-chain/zetacore/x/crosschain/types" -) - -func TestMsgCreateTSSVoter_ValidateBasic(t *testing.T) { - tests := []struct { - name string - msg *types.MsgCreateTSSVoter - err error - }{ - { - name: "valid message", - msg: types.NewMsgCreateTSSVoter(sample.AccAddress(), "pubkey", 1, chains.ReceiveStatus_Created), - }, - { - name: "invalid creator address", - msg: types.NewMsgCreateTSSVoter("invalid", "pubkey", 1, chains.ReceiveStatus_Created), - err: sdkerrors.ErrInvalidAddress, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := tt.msg.ValidateBasic() - if tt.err != nil { - require.ErrorIs(t, err, tt.err) - } else { - require.NoError(t, err) - } - }) - } -} - -func TestMsgCreateTSSVoter_GetSigners(t *testing.T) { - signer := sample.AccAddress() - tests := []struct { - name string - msg *types.MsgCreateTSSVoter - panics bool - }{ - { - name: "valid signer", - msg: types.NewMsgCreateTSSVoter(signer, "pubkey", 1, chains.ReceiveStatus_Created), - panics: false, - }, - { - name: "invalid signer", - msg: types.NewMsgCreateTSSVoter("invalid", "pubkey", 1, chains.ReceiveStatus_Created), - panics: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if !tt.panics { - signers := tt.msg.GetSigners() - require.Equal(t, []sdk.AccAddress{sdk.MustAccAddressFromBech32(signer)}, signers) - } else { - require.Panics(t, func() { - tt.msg.GetSigners() - }) - } - }) - } -} - -func TestMsgCreateTSSVoter_Type(t *testing.T) { - msg := types.NewMsgCreateTSSVoter(sample.AccAddress(), "pubkey", 1, chains.ReceiveStatus_Created) - require.Equal(t, types.TypeMsgCreateTSSVoter, msg.Type()) -} - -func TestMsgCreateTSSVoter_Route(t *testing.T) { - msg := types.NewMsgCreateTSSVoter(sample.AccAddress(), "pubkey", 1, chains.ReceiveStatus_Created) - require.Equal(t, types.RouterKey, msg.Route()) -} - -func TestMsgCreateTSSVoter_GetSignBytes(t *testing.T) { - msg := types.NewMsgCreateTSSVoter(sample.AccAddress(), "pubkey", 1, chains.ReceiveStatus_Created) - require.NotPanics(t, func() { - msg.GetSignBytes() - }) -} - -func TestMsgCreateTSSVoter_Digest(t *testing.T) { - msg := types.NewMsgCreateTSSVoter(sample.AccAddress(), "pubkey", 1, chains.ReceiveStatus_Created) - require.Equal(t, "1-tss-keygen", msg.Digest()) -} diff --git a/x/crosschain/types/outbound_params.go b/x/crosschain/types/outbound_params.go new file mode 100644 index 0000000000..10adf6527d --- /dev/null +++ b/x/crosschain/types/outbound_params.go @@ -0,0 +1,47 @@ +package types + +import ( + "fmt" + "strconv" + + "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/zeta-chain/zetacore/pkg/chains" +) + +func (m OutboundTxParams) GetGasPrice() (uint64, error) { + gasPrice, err := strconv.ParseUint(m.OutboundTxGasPrice, 10, 64) + if err != nil { + return 0, fmt.Errorf("unable to parse cctx gas price %s: %s", m.OutboundTxGasPrice, err.Error()) + } + + return gasPrice, nil +} + +func (m OutboundTxParams) Validate() error { + if m.Receiver == "" { + return fmt.Errorf("receiver cannot be empty") + } + if chains.GetChainFromChainID(m.ReceiverChainId) == nil { + return fmt.Errorf("invalid receiver chain id %d", m.ReceiverChainId) + } + err := ValidateAddressForChain(m.Receiver, m.ReceiverChainId) + if err != nil { + return err + } + if m.Amount.IsNil() { + return fmt.Errorf("amount cannot be nil") + } + if m.OutboundTxBallotIndex != "" { + err = ValidateZetaIndex(m.OutboundTxBallotIndex) + if err != nil { + return errors.Wrap(err, "invalid outbound tx ballot index") + } + } + if m.OutboundTxHash != "" { + err = ValidateHashForChain(m.OutboundTxHash, m.ReceiverChainId) + if err != nil { + return errors.Wrap(err, "invalid outbound tx hash") + } + } + return nil +} diff --git a/x/crosschain/types/outbound_params_test.go b/x/crosschain/types/outbound_params_test.go new file mode 100644 index 0000000000..b0075ccde7 --- /dev/null +++ b/x/crosschain/types/outbound_params_test.go @@ -0,0 +1,48 @@ +package types_test + +import ( + "math/rand" + "testing" + + sdkmath "cosmossdk.io/math" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/testutil/sample" +) + +func TestOutboundTxParams_Validate(t *testing.T) { + r := rand.New(rand.NewSource(42)) + outTxParams := sample.OutboundTxParamsValidChainID(r) + outTxParams.Receiver = "" + require.ErrorContains(t, outTxParams.Validate(), "receiver cannot be empty") + outTxParams = sample.OutboundTxParamsValidChainID(r) + outTxParams.ReceiverChainId = 1000 + require.ErrorContains(t, outTxParams.Validate(), "invalid receiver chain id 1000") + outTxParams = sample.OutboundTxParamsValidChainID(r) + outTxParams.Receiver = "0x123" + require.ErrorContains(t, outTxParams.Validate(), "invalid address 0x123") + outTxParams = sample.OutboundTxParamsValidChainID(r) + outTxParams.Amount = sdkmath.Uint{} + require.ErrorContains(t, outTxParams.Validate(), "amount cannot be nil") + outTxParams = sample.OutboundTxParamsValidChainID(r) + outTxParams.OutboundTxBallotIndex = "12" + require.ErrorContains(t, outTxParams.Validate(), "invalid index length 2") + outTxParams = sample.OutboundTxParamsValidChainID(r) + outTxParams.OutboundTxBallotIndex = sample.ZetaIndex(t) + outTxParams.OutboundTxHash = sample.Hash().String() + require.NoError(t, outTxParams.Validate()) +} + +func TestOutboundTxParams_GetGasPrice(t *testing.T) { + // #nosec G404 - random seed is not used for security purposes + r := rand.New(rand.NewSource(42)) + outTxParams := sample.OutboundTxParams(r) + + outTxParams.OutboundTxGasPrice = "42" + gasPrice, err := outTxParams.GetGasPrice() + require.NoError(t, err) + require.EqualValues(t, uint64(42), gasPrice) + + outTxParams.OutboundTxGasPrice = "invalid" + _, err = outTxParams.GetGasPrice() + require.Error(t, err) +} diff --git a/x/crosschain/types/status.go b/x/crosschain/types/status.go index be2b4af62e..729b0b9bcb 100644 --- a/x/crosschain/types/status.go +++ b/x/crosschain/types/status.go @@ -13,7 +13,11 @@ func (m *Status) AbortRefunded(timeStamp int64) { // empty msg does not overwrite old status message func (m *Status) ChangeStatus(newStatus CctxStatus, msg string) { if len(msg) > 0 { - m.StatusMessage = msg + if m.StatusMessage != "" { + m.StatusMessage = fmt.Sprintf("%s : %s", m.StatusMessage, msg) + } else { + m.StatusMessage = msg + } } if !m.ValidateTransition(newStatus) { m.StatusMessage = fmt.Sprintf("Failed to transition : OldStatus %s , NewStatus %s , MSG : %s :", m.Status.String(), newStatus.String(), msg) diff --git a/x/crosschain/types/tx.pb.go b/x/crosschain/types/tx.pb.go index c8338b386b..076bcecaec 100644 --- a/x/crosschain/types/tx.pb.go +++ b/x/crosschain/types/tx.pb.go @@ -33,110 +33,6 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package -type MsgCreateTSSVoter struct { - Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` - TssPubkey string `protobuf:"bytes,2,opt,name=tss_pubkey,json=tssPubkey,proto3" json:"tss_pubkey,omitempty"` - KeyGenZetaHeight int64 `protobuf:"varint,3,opt,name=keyGenZetaHeight,proto3" json:"keyGenZetaHeight,omitempty"` - Status chains.ReceiveStatus `protobuf:"varint,4,opt,name=status,proto3,enum=chains.ReceiveStatus" json:"status,omitempty"` -} - -func (m *MsgCreateTSSVoter) Reset() { *m = MsgCreateTSSVoter{} } -func (m *MsgCreateTSSVoter) String() string { return proto.CompactTextString(m) } -func (*MsgCreateTSSVoter) ProtoMessage() {} -func (*MsgCreateTSSVoter) Descriptor() ([]byte, []int) { - return fileDescriptor_81d6d611190b7635, []int{0} -} -func (m *MsgCreateTSSVoter) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *MsgCreateTSSVoter) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_MsgCreateTSSVoter.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 *MsgCreateTSSVoter) XXX_Merge(src proto.Message) { - xxx_messageInfo_MsgCreateTSSVoter.Merge(m, src) -} -func (m *MsgCreateTSSVoter) XXX_Size() int { - return m.Size() -} -func (m *MsgCreateTSSVoter) XXX_DiscardUnknown() { - xxx_messageInfo_MsgCreateTSSVoter.DiscardUnknown(m) -} - -var xxx_messageInfo_MsgCreateTSSVoter proto.InternalMessageInfo - -func (m *MsgCreateTSSVoter) GetCreator() string { - if m != nil { - return m.Creator - } - return "" -} - -func (m *MsgCreateTSSVoter) GetTssPubkey() string { - if m != nil { - return m.TssPubkey - } - return "" -} - -func (m *MsgCreateTSSVoter) GetKeyGenZetaHeight() int64 { - if m != nil { - return m.KeyGenZetaHeight - } - return 0 -} - -func (m *MsgCreateTSSVoter) GetStatus() chains.ReceiveStatus { - if m != nil { - return m.Status - } - return chains.ReceiveStatus_Created -} - -type MsgCreateTSSVoterResponse struct { -} - -func (m *MsgCreateTSSVoterResponse) Reset() { *m = MsgCreateTSSVoterResponse{} } -func (m *MsgCreateTSSVoterResponse) String() string { return proto.CompactTextString(m) } -func (*MsgCreateTSSVoterResponse) ProtoMessage() {} -func (*MsgCreateTSSVoterResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_81d6d611190b7635, []int{1} -} -func (m *MsgCreateTSSVoterResponse) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *MsgCreateTSSVoterResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_MsgCreateTSSVoterResponse.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 *MsgCreateTSSVoterResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_MsgCreateTSSVoterResponse.Merge(m, src) -} -func (m *MsgCreateTSSVoterResponse) XXX_Size() int { - return m.Size() -} -func (m *MsgCreateTSSVoterResponse) XXX_DiscardUnknown() { - xxx_messageInfo_MsgCreateTSSVoterResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_MsgCreateTSSVoterResponse proto.InternalMessageInfo - type MsgMigrateTssFunds struct { Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` ChainId int64 `protobuf:"varint,2,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` @@ -147,7 +43,7 @@ func (m *MsgMigrateTssFunds) Reset() { *m = MsgMigrateTssFunds{} } func (m *MsgMigrateTssFunds) String() string { return proto.CompactTextString(m) } func (*MsgMigrateTssFunds) ProtoMessage() {} func (*MsgMigrateTssFunds) Descriptor() ([]byte, []int) { - return fileDescriptor_81d6d611190b7635, []int{2} + return fileDescriptor_81d6d611190b7635, []int{0} } func (m *MsgMigrateTssFunds) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -197,7 +93,7 @@ func (m *MsgMigrateTssFundsResponse) Reset() { *m = MsgMigrateTssFundsRe func (m *MsgMigrateTssFundsResponse) String() string { return proto.CompactTextString(m) } func (*MsgMigrateTssFundsResponse) ProtoMessage() {} func (*MsgMigrateTssFundsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_81d6d611190b7635, []int{3} + return fileDescriptor_81d6d611190b7635, []int{1} } func (m *MsgMigrateTssFundsResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -235,7 +131,7 @@ func (m *MsgUpdateTssAddress) Reset() { *m = MsgUpdateTssAddress{} } func (m *MsgUpdateTssAddress) String() string { return proto.CompactTextString(m) } func (*MsgUpdateTssAddress) ProtoMessage() {} func (*MsgUpdateTssAddress) Descriptor() ([]byte, []int) { - return fileDescriptor_81d6d611190b7635, []int{4} + return fileDescriptor_81d6d611190b7635, []int{2} } func (m *MsgUpdateTssAddress) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -285,7 +181,7 @@ func (m *MsgUpdateTssAddressResponse) Reset() { *m = MsgUpdateTssAddress func (m *MsgUpdateTssAddressResponse) String() string { return proto.CompactTextString(m) } func (*MsgUpdateTssAddressResponse) ProtoMessage() {} func (*MsgUpdateTssAddressResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_81d6d611190b7635, []int{5} + return fileDescriptor_81d6d611190b7635, []int{3} } func (m *MsgUpdateTssAddressResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -328,7 +224,7 @@ func (m *MsgAddToInTxTracker) Reset() { *m = MsgAddToInTxTracker{} } func (m *MsgAddToInTxTracker) String() string { return proto.CompactTextString(m) } func (*MsgAddToInTxTracker) ProtoMessage() {} func (*MsgAddToInTxTracker) Descriptor() ([]byte, []int) { - return fileDescriptor_81d6d611190b7635, []int{6} + return fileDescriptor_81d6d611190b7635, []int{4} } func (m *MsgAddToInTxTracker) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -413,7 +309,7 @@ func (m *MsgAddToInTxTrackerResponse) Reset() { *m = MsgAddToInTxTracker func (m *MsgAddToInTxTrackerResponse) String() string { return proto.CompactTextString(m) } func (*MsgAddToInTxTrackerResponse) ProtoMessage() {} func (*MsgAddToInTxTrackerResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_81d6d611190b7635, []int{7} + return fileDescriptor_81d6d611190b7635, []int{5} } func (m *MsgAddToInTxTrackerResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -456,7 +352,7 @@ func (m *MsgWhitelistERC20) Reset() { *m = MsgWhitelistERC20{} } func (m *MsgWhitelistERC20) String() string { return proto.CompactTextString(m) } func (*MsgWhitelistERC20) ProtoMessage() {} func (*MsgWhitelistERC20) Descriptor() ([]byte, []int) { - return fileDescriptor_81d6d611190b7635, []int{8} + return fileDescriptor_81d6d611190b7635, []int{6} } func (m *MsgWhitelistERC20) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -543,7 +439,7 @@ func (m *MsgWhitelistERC20Response) Reset() { *m = MsgWhitelistERC20Resp func (m *MsgWhitelistERC20Response) String() string { return proto.CompactTextString(m) } func (*MsgWhitelistERC20Response) ProtoMessage() {} func (*MsgWhitelistERC20Response) Descriptor() ([]byte, []int) { - return fileDescriptor_81d6d611190b7635, []int{9} + return fileDescriptor_81d6d611190b7635, []int{7} } func (m *MsgWhitelistERC20Response) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -600,7 +496,7 @@ func (m *MsgAddToOutTxTracker) Reset() { *m = MsgAddToOutTxTracker{} } func (m *MsgAddToOutTxTracker) String() string { return proto.CompactTextString(m) } func (*MsgAddToOutTxTracker) ProtoMessage() {} func (*MsgAddToOutTxTracker) Descriptor() ([]byte, []int) { - return fileDescriptor_81d6d611190b7635, []int{10} + return fileDescriptor_81d6d611190b7635, []int{8} } func (m *MsgAddToOutTxTracker) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -686,7 +582,7 @@ func (m *MsgAddToOutTxTrackerResponse) Reset() { *m = MsgAddToOutTxTrack func (m *MsgAddToOutTxTrackerResponse) String() string { return proto.CompactTextString(m) } func (*MsgAddToOutTxTrackerResponse) ProtoMessage() {} func (*MsgAddToOutTxTrackerResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_81d6d611190b7635, []int{11} + return fileDescriptor_81d6d611190b7635, []int{9} } func (m *MsgAddToOutTxTrackerResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -732,7 +628,7 @@ func (m *MsgRemoveFromOutTxTracker) Reset() { *m = MsgRemoveFromOutTxTra func (m *MsgRemoveFromOutTxTracker) String() string { return proto.CompactTextString(m) } func (*MsgRemoveFromOutTxTracker) ProtoMessage() {} func (*MsgRemoveFromOutTxTracker) Descriptor() ([]byte, []int) { - return fileDescriptor_81d6d611190b7635, []int{12} + return fileDescriptor_81d6d611190b7635, []int{10} } func (m *MsgRemoveFromOutTxTracker) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -789,7 +685,7 @@ func (m *MsgRemoveFromOutTxTrackerResponse) Reset() { *m = MsgRemoveFrom func (m *MsgRemoveFromOutTxTrackerResponse) String() string { return proto.CompactTextString(m) } func (*MsgRemoveFromOutTxTrackerResponse) ProtoMessage() {} func (*MsgRemoveFromOutTxTrackerResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_81d6d611190b7635, []int{13} + return fileDescriptor_81d6d611190b7635, []int{11} } func (m *MsgRemoveFromOutTxTrackerResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -830,7 +726,7 @@ func (m *MsgGasPriceVoter) Reset() { *m = MsgGasPriceVoter{} } func (m *MsgGasPriceVoter) String() string { return proto.CompactTextString(m) } func (*MsgGasPriceVoter) ProtoMessage() {} func (*MsgGasPriceVoter) Descriptor() ([]byte, []int) { - return fileDescriptor_81d6d611190b7635, []int{14} + return fileDescriptor_81d6d611190b7635, []int{12} } func (m *MsgGasPriceVoter) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -901,7 +797,7 @@ func (m *MsgGasPriceVoterResponse) Reset() { *m = MsgGasPriceVoterRespon func (m *MsgGasPriceVoterResponse) String() string { return proto.CompactTextString(m) } func (*MsgGasPriceVoterResponse) ProtoMessage() {} func (*MsgGasPriceVoterResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_81d6d611190b7635, []int{15} + return fileDescriptor_81d6d611190b7635, []int{13} } func (m *MsgGasPriceVoterResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -949,7 +845,7 @@ func (m *MsgVoteOnObservedOutboundTx) Reset() { *m = MsgVoteOnObservedOu func (m *MsgVoteOnObservedOutboundTx) String() string { return proto.CompactTextString(m) } func (*MsgVoteOnObservedOutboundTx) ProtoMessage() {} func (*MsgVoteOnObservedOutboundTx) Descriptor() ([]byte, []int) { - return fileDescriptor_81d6d611190b7635, []int{16} + return fileDescriptor_81d6d611190b7635, []int{14} } func (m *MsgVoteOnObservedOutboundTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1055,7 +951,7 @@ func (m *MsgVoteOnObservedOutboundTxResponse) Reset() { *m = MsgVoteOnOb func (m *MsgVoteOnObservedOutboundTxResponse) String() string { return proto.CompactTextString(m) } func (*MsgVoteOnObservedOutboundTxResponse) ProtoMessage() {} func (*MsgVoteOnObservedOutboundTxResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_81d6d611190b7635, []int{17} + return fileDescriptor_81d6d611190b7635, []int{15} } func (m *MsgVoteOnObservedOutboundTxResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1108,7 +1004,7 @@ func (m *MsgVoteOnObservedInboundTx) Reset() { *m = MsgVoteOnObservedInb func (m *MsgVoteOnObservedInboundTx) String() string { return proto.CompactTextString(m) } func (*MsgVoteOnObservedInboundTx) ProtoMessage() {} func (*MsgVoteOnObservedInboundTx) Descriptor() ([]byte, []int) { - return fileDescriptor_81d6d611190b7635, []int{18} + return fileDescriptor_81d6d611190b7635, []int{16} } func (m *MsgVoteOnObservedInboundTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1235,7 +1131,7 @@ func (m *MsgVoteOnObservedInboundTxResponse) Reset() { *m = MsgVoteOnObs func (m *MsgVoteOnObservedInboundTxResponse) String() string { return proto.CompactTextString(m) } func (*MsgVoteOnObservedInboundTxResponse) ProtoMessage() {} func (*MsgVoteOnObservedInboundTxResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_81d6d611190b7635, []int{19} + return fileDescriptor_81d6d611190b7635, []int{17} } func (m *MsgVoteOnObservedInboundTxResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1273,7 +1169,7 @@ func (m *MsgAbortStuckCCTX) Reset() { *m = MsgAbortStuckCCTX{} } func (m *MsgAbortStuckCCTX) String() string { return proto.CompactTextString(m) } func (*MsgAbortStuckCCTX) ProtoMessage() {} func (*MsgAbortStuckCCTX) Descriptor() ([]byte, []int) { - return fileDescriptor_81d6d611190b7635, []int{20} + return fileDescriptor_81d6d611190b7635, []int{18} } func (m *MsgAbortStuckCCTX) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1323,7 +1219,7 @@ func (m *MsgAbortStuckCCTXResponse) Reset() { *m = MsgAbortStuckCCTXResp func (m *MsgAbortStuckCCTXResponse) String() string { return proto.CompactTextString(m) } func (*MsgAbortStuckCCTXResponse) ProtoMessage() {} func (*MsgAbortStuckCCTXResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_81d6d611190b7635, []int{21} + return fileDescriptor_81d6d611190b7635, []int{19} } func (m *MsgAbortStuckCCTXResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1362,7 +1258,7 @@ 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} + return fileDescriptor_81d6d611190b7635, []int{20} } func (m *MsgRefundAbortedCCTX) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1419,7 +1315,7 @@ func (m *MsgRefundAbortedCCTXResponse) Reset() { *m = MsgRefundAbortedCC func (m *MsgRefundAbortedCCTXResponse) String() string { return proto.CompactTextString(m) } func (*MsgRefundAbortedCCTXResponse) ProtoMessage() {} func (*MsgRefundAbortedCCTXResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_81d6d611190b7635, []int{23} + return fileDescriptor_81d6d611190b7635, []int{21} } func (m *MsgRefundAbortedCCTXResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1449,8 +1345,6 @@ func (m *MsgRefundAbortedCCTXResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgRefundAbortedCCTXResponse proto.InternalMessageInfo func init() { - proto.RegisterType((*MsgCreateTSSVoter)(nil), "zetachain.zetacore.crosschain.MsgCreateTSSVoter") - proto.RegisterType((*MsgCreateTSSVoterResponse)(nil), "zetachain.zetacore.crosschain.MsgCreateTSSVoterResponse") proto.RegisterType((*MsgMigrateTssFunds)(nil), "zetachain.zetacore.crosschain.MsgMigrateTssFunds") proto.RegisterType((*MsgMigrateTssFundsResponse)(nil), "zetachain.zetacore.crosschain.MsgMigrateTssFundsResponse") proto.RegisterType((*MsgUpdateTssAddress)(nil), "zetachain.zetacore.crosschain.MsgUpdateTssAddress") @@ -1478,102 +1372,98 @@ func init() { func init() { proto.RegisterFile("crosschain/tx.proto", fileDescriptor_81d6d611190b7635) } var fileDescriptor_81d6d611190b7635 = []byte{ - // 1517 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x18, 0xdd, 0x4e, 0x1b, 0x47, - 0x97, 0xfd, 0x00, 0x63, 0x1f, 0x30, 0x24, 0x0b, 0x09, 0xce, 0x12, 0x0c, 0x59, 0xbe, 0xa4, 0xa8, - 0x55, 0xec, 0x84, 0xa8, 0x6a, 0x92, 0xb6, 0x52, 0x01, 0x25, 0x84, 0x36, 0x84, 0x68, 0x71, 0xda, - 0x2a, 0x37, 0xd6, 0x7a, 0x77, 0x58, 0x56, 0xd8, 0x3b, 0xd6, 0xce, 0xd8, 0xb2, 0x51, 0xa5, 0x56, - 0x95, 0x7a, 0x5f, 0x55, 0x95, 0x5a, 0xf5, 0x05, 0xfa, 0x2a, 0xb9, 0x8c, 0x7a, 0xd3, 0x9f, 0x8b, - 0xa8, 0x0a, 0x0f, 0x50, 0xa9, 0x4f, 0x50, 0xcd, 0x99, 0xf1, 0xe2, 0xb5, 0xf1, 0x0f, 0x44, 0xb9, - 0xb1, 0xf7, 0x9c, 0x99, 0xf3, 0xff, 0xbb, 0x0b, 0xb3, 0x4e, 0x48, 0x19, 0x73, 0x0e, 0x6c, 0x3f, - 0xc8, 0xf3, 0x46, 0xae, 0x1a, 0x52, 0x4e, 0xf5, 0xc5, 0x23, 0xc2, 0x6d, 0xc4, 0xe5, 0xf0, 0x89, - 0x86, 0x24, 0x77, 0x72, 0xcf, 0x98, 0xf3, 0xa8, 0x47, 0xf1, 0x66, 0x5e, 0x3c, 0x49, 0x22, 0x63, - 0xbe, 0x7a, 0xe8, 0xe5, 0xf1, 0x02, 0x53, 0x7f, 0xea, 0x60, 0x16, 0x0f, 0xa8, 0x1f, 0xe0, 0x4f, - 0xfb, 0xed, 0x6a, 0x48, 0xe9, 0x3e, 0x53, 0x7f, 0xf2, 0xc0, 0xfc, 0x55, 0x83, 0x8b, 0x3b, 0xcc, - 0xdb, 0x0c, 0x89, 0xcd, 0x49, 0x61, 0x6f, 0xef, 0x73, 0xca, 0x49, 0xa8, 0x67, 0x60, 0xc2, 0x11, - 0x18, 0x1a, 0x66, 0xb4, 0x65, 0x6d, 0x35, 0x65, 0xb5, 0x40, 0x7d, 0x11, 0x80, 0x33, 0x56, 0xac, - 0xd6, 0x4a, 0x87, 0xa4, 0x99, 0xf9, 0x1f, 0x1e, 0xa6, 0x38, 0x63, 0x4f, 0x11, 0xa1, 0xbf, 0x0b, - 0x17, 0x0e, 0x49, 0x73, 0x8b, 0x04, 0xcf, 0x09, 0xb7, 0x1f, 0x11, 0xdf, 0x3b, 0xe0, 0x99, 0xd1, - 0x65, 0x6d, 0x75, 0xd4, 0xea, 0xc2, 0xeb, 0x37, 0x21, 0xc1, 0xb8, 0xcd, 0x6b, 0x2c, 0x33, 0xb6, - 0xac, 0xad, 0x4e, 0xaf, 0x5d, 0xca, 0x29, 0x3b, 0x2c, 0xe2, 0x10, 0xbf, 0x4e, 0xf6, 0xf0, 0xd0, - 0x52, 0x97, 0xcc, 0x05, 0xb8, 0xd2, 0xa5, 0xa8, 0x45, 0x58, 0x95, 0x06, 0x8c, 0x98, 0x3f, 0x68, - 0xa0, 0xef, 0x30, 0x6f, 0xc7, 0xf7, 0x42, 0x71, 0xcc, 0xd8, 0xc3, 0x5a, 0xe0, 0xb2, 0x3e, 0x76, - 0x5c, 0x81, 0x24, 0x4a, 0x2b, 0xfa, 0x2e, 0x5a, 0x31, 0x6a, 0x4d, 0x20, 0xbc, 0xed, 0xea, 0x5b, - 0x90, 0xb0, 0x2b, 0xb4, 0x16, 0x48, 0xcd, 0x53, 0x1b, 0xf9, 0x17, 0xaf, 0x96, 0x46, 0xfe, 0x7a, - 0xb5, 0xf4, 0x8e, 0xe7, 0xf3, 0x83, 0x5a, 0x29, 0xe7, 0xd0, 0x4a, 0xde, 0xa1, 0xac, 0x42, 0x99, - 0xfa, 0xbb, 0xc9, 0xdc, 0xc3, 0x3c, 0x6f, 0x56, 0x09, 0xcb, 0x3d, 0xf3, 0x03, 0x6e, 0x29, 0x72, - 0xf3, 0x2a, 0x18, 0xdd, 0x3a, 0x45, 0x2a, 0x3f, 0x81, 0xd9, 0x1d, 0xe6, 0x3d, 0xab, 0xba, 0xf2, - 0x70, 0xdd, 0x75, 0x43, 0xc2, 0xd8, 0xb9, 0x5d, 0x6f, 0x2e, 0xc2, 0xc2, 0x29, 0xfc, 0x22, 0x71, - 0xff, 0x68, 0x28, 0x6f, 0xdd, 0x75, 0x0b, 0x74, 0x3b, 0x28, 0x34, 0x0a, 0xa1, 0xed, 0x1c, 0xf6, - 0x0d, 0x75, 0x1f, 0x17, 0xcd, 0xc3, 0x04, 0x6f, 0x14, 0x0f, 0x6c, 0x76, 0x20, 0x7d, 0x64, 0x25, - 0x78, 0xe3, 0x91, 0xcd, 0x0e, 0xf4, 0xf7, 0x20, 0x25, 0xb2, 0xae, 0x28, 0xbc, 0xa1, 0xc2, 0x3a, - 0x9d, 0xc3, 0x3c, 0xdc, 0xa4, 0x7e, 0x50, 0x68, 0x56, 0x89, 0x95, 0x74, 0xd4, 0x93, 0xbe, 0x02, - 0xe3, 0x98, 0x8b, 0x99, 0xf1, 0x65, 0x6d, 0x75, 0x72, 0x2d, 0x9d, 0x53, 0x99, 0xf9, 0x54, 0xfc, - 0x59, 0xf2, 0x4c, 0x58, 0x5d, 0x2a, 0x53, 0xe7, 0x50, 0x4a, 0x4b, 0x48, 0xab, 0x11, 0x83, 0x02, - 0xaf, 0x40, 0x92, 0x37, 0x8a, 0x7e, 0xe0, 0x92, 0x46, 0x66, 0x42, 0x2a, 0xc9, 0x1b, 0xdb, 0x02, - 0x54, 0x0e, 0xe9, 0x34, 0x38, 0x72, 0xc8, 0x6f, 0x32, 0xf3, 0xbf, 0x38, 0xf0, 0x39, 0x29, 0xfb, - 0x8c, 0x3f, 0xb0, 0x36, 0xd7, 0x6e, 0xf5, 0x71, 0xc7, 0x0a, 0xa4, 0x49, 0xe8, 0xac, 0xdd, 0x2a, - 0xda, 0xd2, 0xb3, 0x2a, 0x02, 0x53, 0x88, 0x6c, 0x45, 0xaf, 0xdd, 0x67, 0xa3, 0x71, 0x9f, 0xe9, - 0x30, 0x16, 0xd8, 0x15, 0xe9, 0x95, 0x94, 0x85, 0xcf, 0xfa, 0x65, 0x48, 0xb0, 0x66, 0xa5, 0x44, - 0xcb, 0xe8, 0x82, 0x94, 0xa5, 0x20, 0xdd, 0x80, 0xa4, 0x4b, 0x1c, 0xbf, 0x62, 0x97, 0x19, 0x9a, - 0x9c, 0xb6, 0x22, 0x58, 0x5f, 0x80, 0x94, 0x67, 0xb3, 0x62, 0xd9, 0xaf, 0xf8, 0x5c, 0x99, 0x9c, - 0xf4, 0x6c, 0xf6, 0x58, 0xc0, 0x66, 0x11, 0x8b, 0x24, 0x6e, 0x53, 0xcb, 0x62, 0x61, 0xc1, 0x51, - 0xcc, 0x02, 0x69, 0xe1, 0xd4, 0x51, 0xbb, 0x05, 0x8b, 0x00, 0x8e, 0x13, 0xb9, 0x54, 0x65, 0x99, - 0xc0, 0x48, 0xa7, 0xfe, 0xa9, 0xc1, 0x5c, 0xcb, 0xab, 0xbb, 0x35, 0xfe, 0x86, 0x79, 0x34, 0x07, - 0xe3, 0x01, 0x0d, 0x1c, 0x82, 0xbe, 0x1a, 0xb3, 0x24, 0xd0, 0x9e, 0x5d, 0x63, 0xb1, 0xec, 0x7a, - 0xcb, 0x09, 0xf3, 0x31, 0x5c, 0x3d, 0xcd, 0xb4, 0xc8, 0x7f, 0x8b, 0x00, 0x3e, 0x2b, 0x86, 0xa4, - 0x42, 0xeb, 0xc4, 0x45, 0x2b, 0x93, 0x56, 0xca, 0x67, 0x96, 0x44, 0x98, 0xfb, 0xe8, 0x7b, 0x09, - 0x3d, 0x0c, 0x69, 0xe5, 0x2d, 0xb9, 0xc7, 0x5c, 0x81, 0x6b, 0x3d, 0xe5, 0x44, 0xd9, 0xfd, 0xb3, - 0x06, 0x17, 0x76, 0x98, 0xb7, 0x65, 0xb3, 0xa7, 0xa1, 0xef, 0x90, 0x41, 0x6d, 0xbd, 0xbf, 0x12, - 0x55, 0xc1, 0xa2, 0xa5, 0x04, 0x02, 0xfa, 0x35, 0x98, 0x92, 0x5e, 0x0e, 0x6a, 0x95, 0x12, 0x09, - 0x31, 0x50, 0x63, 0xd6, 0x24, 0xe2, 0x9e, 0x20, 0x0a, 0x93, 0xbb, 0x56, 0xad, 0x96, 0x9b, 0x51, - 0x72, 0x23, 0x64, 0x1a, 0x90, 0xe9, 0xd4, 0x2c, 0x52, 0xfb, 0xf7, 0x71, 0x2c, 0x5a, 0x81, 0xdc, - 0x0d, 0x76, 0x4b, 0x8c, 0x84, 0x75, 0xe2, 0xee, 0xd6, 0x78, 0x89, 0xd6, 0x02, 0xb7, 0xd0, 0xe8, - 0x63, 0xc1, 0x02, 0x60, 0x96, 0xca, 0xa8, 0xcb, 0xb4, 0x4d, 0x0a, 0x04, 0x06, 0x3d, 0x07, 0xb3, - 0x54, 0x31, 0x2b, 0x52, 0xe1, 0xae, 0xf6, 0xde, 0x75, 0x91, 0x9e, 0xc8, 0x29, 0xc8, 0xfb, 0x1f, - 0x81, 0xd1, 0x71, 0x5f, 0x26, 0x90, 0x1c, 0x68, 0xd2, 0xd6, 0x4c, 0x8c, 0x6c, 0xe3, 0xe4, 0x5c, - 0x7f, 0x1f, 0xe6, 0x3b, 0xa8, 0x45, 0xc1, 0xd6, 0x18, 0x71, 0x33, 0x80, 0xa4, 0x73, 0x31, 0xd2, - 0x2d, 0x9b, 0x3d, 0x63, 0xc4, 0xd5, 0x8f, 0xc0, 0xec, 0x20, 0x23, 0xfb, 0xfb, 0xc4, 0xe1, 0x7e, - 0x9d, 0x20, 0x03, 0x19, 0x85, 0x49, 0x9c, 0x49, 0x39, 0x35, 0x93, 0x6e, 0x0c, 0x31, 0x93, 0xb6, - 0x03, 0x6e, 0x65, 0x63, 0x12, 0x1f, 0xb4, 0xf8, 0xb6, 0x82, 0xa0, 0x7f, 0x3a, 0x40, 0xb6, 0xec, - 0x36, 0x53, 0xa8, 0x7d, 0x6f, 0x5e, 0xd8, 0x83, 0x74, 0x0a, 0xd3, 0x75, 0xbb, 0x5c, 0x23, 0xc5, - 0x50, 0xce, 0x71, 0x57, 0xc6, 0x7f, 0xe3, 0xd1, 0x19, 0xe7, 0xe8, 0xbf, 0xaf, 0x96, 0x2e, 0x35, - 0xed, 0x4a, 0xf9, 0xbe, 0x19, 0x67, 0x67, 0x5a, 0x69, 0x44, 0xa8, 0x35, 0xc1, 0x6d, 0x5b, 0x24, - 0x12, 0x43, 0x2c, 0x12, 0xfa, 0x12, 0x4c, 0x4a, 0x13, 0xf1, 0x96, 0x6a, 0x02, 0x80, 0xa8, 0x4d, - 0x81, 0xd1, 0x6f, 0xc0, 0x8c, 0xbc, 0x20, 0xc6, 0xad, 0x2c, 0xc0, 0x24, 0x5a, 0x9e, 0x46, 0x74, - 0x81, 0xb1, 0x27, 0xd8, 0xa7, 0x62, 0xc3, 0x2e, 0xd5, 0x7f, 0xd8, 0x99, 0xd7, 0x61, 0xa5, 0x4f, - 0x62, 0x47, 0x05, 0xf0, 0xcd, 0x18, 0x2e, 0x0d, 0xf1, 0x7b, 0xdb, 0xc1, 0xe0, 0xfc, 0x17, 0xd5, - 0x46, 0x02, 0x97, 0x84, 0x2a, 0xf9, 0x15, 0x24, 0x8c, 0x91, 0x4f, 0xc5, 0x8e, 0xc1, 0x94, 0x96, - 0xe8, 0x4d, 0x55, 0xe6, 0x06, 0x24, 0x95, 0x83, 0x43, 0xd5, 0x75, 0x23, 0x58, 0xbf, 0x0e, 0xd3, - 0xad, 0x67, 0xe5, 0xb4, 0x71, 0xc9, 0xa2, 0x85, 0x95, 0x7e, 0x3b, 0x59, 0x9c, 0x12, 0x6f, 0xb4, - 0x38, 0x09, 0x2b, 0x2b, 0x84, 0x31, 0xdb, 0x93, 0x8e, 0x4f, 0x59, 0x2d, 0x50, 0xbf, 0x0a, 0x20, - 0x1c, 0xae, 0xea, 0x37, 0x25, 0xf5, 0xf4, 0x03, 0x55, 0xb6, 0x37, 0x60, 0xc6, 0x0f, 0x8a, 0xaa, - 0xfb, 0xcb, 0x5a, 0x95, 0x05, 0x97, 0xf6, 0x83, 0xf6, 0x02, 0x8d, 0x8d, 0xd0, 0x49, 0xbc, 0x11, - 0x8d, 0xd0, 0x78, 0x54, 0xa7, 0x06, 0xac, 0x30, 0x0b, 0x90, 0xe2, 0x8d, 0x22, 0x0d, 0x7d, 0xcf, - 0x0f, 0x32, 0x69, 0xa9, 0x0e, 0x6f, 0xec, 0x22, 0x2c, 0x3a, 0xa7, 0xcd, 0x18, 0xe1, 0x99, 0x69, - 0x3c, 0x90, 0x80, 0x48, 0x3f, 0x52, 0x27, 0x01, 0x57, 0x33, 0x68, 0x06, 0xc5, 0x03, 0xa2, 0xe4, - 0x18, 0xfa, 0x3f, 0x98, 0xbd, 0x33, 0x20, 0x4a, 0x94, 0xc7, 0xb8, 0xbd, 0xac, 0x97, 0x68, 0xc8, - 0xf7, 0x78, 0xcd, 0x39, 0xdc, 0xdc, 0x2c, 0x7c, 0xd9, 0x7f, 0x79, 0xec, 0x37, 0xd6, 0xe5, 0x72, - 0x1d, 0xe7, 0x16, 0x89, 0xaa, 0xe3, 0xc8, 0xb7, 0xc8, 0x7e, 0x2d, 0x70, 0xf1, 0x0a, 0x71, 0xdf, - 0x48, 0x9a, 0xcc, 0x27, 0xc1, 0x2d, 0xda, 0x44, 0x64, 0x27, 0x4e, 0x4b, 0xac, 0x5a, 0x45, 0xcc, - 0x2c, 0xce, 0xe3, 0x2e, 0xb9, 0x2d, 0xbd, 0xd6, 0x8e, 0xa7, 0x60, 0x74, 0x87, 0x79, 0xfa, 0x77, - 0x1a, 0x5c, 0xec, 0x5e, 0x48, 0xee, 0xe4, 0xfa, 0xbe, 0x56, 0xe5, 0x4e, 0x1b, 0xf5, 0xc6, 0x87, - 0xe7, 0x20, 0x8a, 0xf6, 0x83, 0x6f, 0x35, 0xb8, 0xd0, 0xb5, 0x5f, 0xaf, 0x0d, 0xc9, 0xb1, 0x8d, - 0xc6, 0xb8, 0x7f, 0x76, 0x9a, 0x48, 0x89, 0x1f, 0x35, 0xb8, 0xdc, 0x63, 0x07, 0xb9, 0x3b, 0x98, - 0xed, 0xe9, 0x94, 0xc6, 0x27, 0xe7, 0xa5, 0x8c, 0xd4, 0x6a, 0x42, 0x3a, 0xbe, 0x8b, 0xe4, 0x07, - 0xb3, 0x8c, 0x11, 0x18, 0x1f, 0x9c, 0x91, 0x20, 0x12, 0xfd, 0x8b, 0x06, 0x99, 0x9e, 0x0b, 0xc5, - 0x10, 0xae, 0xee, 0x45, 0x6b, 0x6c, 0x9c, 0x9f, 0x36, 0x52, 0xee, 0x27, 0x0d, 0xe6, 0x7b, 0x35, - 0xfb, 0x7b, 0x67, 0xe5, 0x1f, 0x91, 0x1a, 0xeb, 0xe7, 0x26, 0x8d, 0x34, 0xfb, 0x0a, 0xa6, 0x3b, - 0xde, 0x8d, 0x6e, 0x0d, 0x66, 0x1a, 0xa7, 0x30, 0xee, 0x9e, 0x95, 0x22, 0x56, 0x4b, 0x5d, 0xef, - 0xc6, 0x43, 0xd4, 0x52, 0x27, 0xcd, 0x30, 0xb5, 0xd4, 0xeb, 0x9d, 0x59, 0xff, 0x1a, 0x66, 0x3a, - 0xbf, 0x28, 0xdc, 0x1e, 0xcc, 0xae, 0x83, 0xc4, 0xb8, 0x77, 0x66, 0x92, 0xf6, 0x18, 0x74, 0x7c, - 0x99, 0x19, 0x22, 0x06, 0x71, 0x8a, 0x61, 0x62, 0x70, 0xfa, 0x47, 0x15, 0x21, 0xbd, 0x63, 0xbe, - 0x0c, 0x21, 0x3d, 0x4e, 0x31, 0x8c, 0xf4, 0xd3, 0xa7, 0x0e, 0x76, 0xf5, 0xee, 0x99, 0x73, 0x67, - 0x98, 0x4e, 0xd4, 0x41, 0x34, 0x4c, 0x57, 0xef, 0x39, 0x65, 0x36, 0x3e, 0x7b, 0xf1, 0x3a, 0xab, - 0xbd, 0x7c, 0x9d, 0xd5, 0xfe, 0x7e, 0x9d, 0xd5, 0xbe, 0x3f, 0xce, 0x8e, 0xbc, 0x3c, 0xce, 0x8e, - 0xfc, 0x71, 0x9c, 0x1d, 0x79, 0x7e, 0xbb, 0x6d, 0xaf, 0x11, 0x6c, 0x6f, 0xca, 0xef, 0x7a, 0x2d, - 0x09, 0xf9, 0x46, 0xbe, 0xfd, 0x6b, 0x9f, 0x58, 0x73, 0x4a, 0x09, 0xfc, 0xea, 0x76, 0xe7, 0xbf, - 0x00, 0x00, 0x00, 0xff, 0xff, 0x08, 0xaa, 0x5d, 0x39, 0x08, 0x14, 0x00, 0x00, + // 1451 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x18, 0x5d, 0x6b, 0xdb, 0x56, + 0x3b, 0x7a, 0x93, 0x38, 0xf6, 0x93, 0xd8, 0x69, 0x95, 0xb4, 0x71, 0x95, 0xc6, 0x49, 0x95, 0xb7, + 0x7d, 0x03, 0x2f, 0xb5, 0x5b, 0x97, 0x97, 0xb7, 0xed, 0x36, 0x58, 0x12, 0xfa, 0x91, 0xad, 0x69, + 0x8a, 0xea, 0x6e, 0x63, 0x37, 0x42, 0x96, 0x4e, 0x64, 0x11, 0x5b, 0xc7, 0xe8, 0x1c, 0x05, 0x3b, + 0x0c, 0x36, 0x06, 0xbb, 0x1f, 0x63, 0xb0, 0xb1, 0x5f, 0xd4, 0xcb, 0xb2, 0x9b, 0x7d, 0x5c, 0x94, + 0xd1, 0xb2, 0xeb, 0xc1, 0x7e, 0xc1, 0xd0, 0x73, 0x8e, 0x15, 0xcb, 0x8e, 0x3f, 0x92, 0xd0, 0x1b, + 0x5b, 0xcf, 0x73, 0xce, 0xf3, 0xfd, 0x29, 0xc1, 0x82, 0x1d, 0x50, 0xc6, 0xec, 0x9a, 0xe5, 0xf9, + 0x25, 0xde, 0x2a, 0x36, 0x03, 0xca, 0xa9, 0xba, 0x72, 0x44, 0xb8, 0x85, 0xb8, 0x22, 0x3e, 0xd1, + 0x80, 0x14, 0x8f, 0xef, 0x69, 0x8b, 0x2e, 0x75, 0x29, 0xde, 0x2c, 0x45, 0x4f, 0x82, 0x48, 0x5b, + 0x6a, 0x1e, 0xb8, 0x25, 0xbc, 0xc0, 0xe4, 0x9f, 0x3c, 0x58, 0xc0, 0x03, 0xea, 0xf9, 0xf8, 0xd3, + 0x7d, 0xbb, 0x19, 0x50, 0xba, 0xcf, 0xe4, 0x9f, 0x38, 0xd0, 0xbf, 0x53, 0x40, 0xdd, 0x65, 0xee, + 0xae, 0xe7, 0x06, 0x16, 0x27, 0x15, 0xc6, 0x1e, 0x86, 0xbe, 0xc3, 0xd4, 0x3c, 0xcc, 0xd8, 0x01, + 0xb1, 0x38, 0x0d, 0xf2, 0xca, 0x9a, 0xb2, 0x91, 0x31, 0x3a, 0xa0, 0x7a, 0x05, 0xd2, 0x28, 0xce, + 0xf4, 0x9c, 0xfc, 0xbf, 0xd6, 0x94, 0x8d, 0x49, 0x63, 0x06, 0xe1, 0x1d, 0x47, 0x7d, 0x04, 0x29, + 0xab, 0x41, 0x43, 0x9f, 0xe7, 0x27, 0x23, 0x9a, 0xad, 0xd2, 0xcb, 0xd7, 0xab, 0x13, 0xbf, 0xbf, + 0x5e, 0xfd, 0x8f, 0xeb, 0xf1, 0x5a, 0x58, 0x2d, 0xda, 0xb4, 0x51, 0xb2, 0x29, 0x6b, 0x50, 0x26, + 0xff, 0x6e, 0x32, 0xe7, 0xa0, 0xc4, 0xdb, 0x4d, 0xc2, 0x8a, 0x2f, 0x3c, 0x9f, 0x1b, 0x92, 0x5c, + 0xbf, 0x0a, 0x5a, 0xbf, 0x4e, 0x06, 0x61, 0x4d, 0xea, 0x33, 0xa2, 0x3f, 0x85, 0x85, 0x5d, 0xe6, + 0xbe, 0x68, 0x3a, 0xe2, 0x70, 0xd3, 0x71, 0x02, 0xc2, 0x86, 0xa9, 0xbc, 0x02, 0xc0, 0x19, 0x33, + 0x9b, 0x61, 0xf5, 0x80, 0xb4, 0x51, 0xe9, 0x8c, 0x91, 0xe1, 0x8c, 0x3d, 0x43, 0x84, 0xbe, 0x02, + 0xcb, 0x27, 0xf0, 0x8b, 0xc5, 0xfd, 0xa5, 0xa0, 0xbc, 0x4d, 0xc7, 0xa9, 0xd0, 0x1d, 0xbf, 0xd2, + 0xaa, 0x04, 0x96, 0x7d, 0x40, 0x82, 0xb3, 0xb9, 0x68, 0x09, 0x66, 0x78, 0xcb, 0xac, 0x59, 0xac, + 0x26, 0x7c, 0x64, 0xa4, 0x78, 0xeb, 0xb1, 0xc5, 0x6a, 0xea, 0x7f, 0x21, 0x13, 0x85, 0xcb, 0x8c, + 0xbc, 0x91, 0x9f, 0x5a, 0x53, 0x36, 0x72, 0xe5, 0x5c, 0x11, 0x03, 0xb8, 0x4d, 0x3d, 0xbf, 0xd2, + 0x6e, 0x12, 0x23, 0x6d, 0xcb, 0x27, 0x75, 0x1d, 0xa6, 0x31, 0x88, 0xf9, 0xe9, 0x35, 0x65, 0x63, + 0xb6, 0x9c, 0x2d, 0xca, 0x90, 0x3e, 0x8b, 0xfe, 0x0c, 0x71, 0x16, 0x59, 0x5d, 0xad, 0x53, 0xfb, + 0x40, 0x48, 0x4b, 0x09, 0xab, 0x11, 0x83, 0x02, 0xaf, 0x40, 0x9a, 0xb7, 0x4c, 0xcf, 0x77, 0x48, + 0x2b, 0x3f, 0x23, 0x94, 0xe4, 0xad, 0x9d, 0x08, 0x94, 0x0e, 0xe9, 0x35, 0x38, 0x76, 0xc8, 0xcf, + 0x0a, 0x5c, 0xdc, 0x65, 0xee, 0xa7, 0x35, 0x8f, 0x93, 0xba, 0xc7, 0xf8, 0x03, 0x63, 0xbb, 0x7c, + 0x6b, 0x88, 0x3b, 0xd6, 0x21, 0x4b, 0x02, 0xbb, 0x7c, 0xcb, 0xb4, 0x84, 0x67, 0x65, 0x04, 0xe6, + 0x10, 0xd9, 0x89, 0x5e, 0xb7, 0xcf, 0x26, 0x93, 0x3e, 0x53, 0x61, 0xca, 0xb7, 0x1a, 0xc2, 0x2b, + 0x19, 0x03, 0x9f, 0xd5, 0xcb, 0x90, 0x62, 0xed, 0x46, 0x95, 0xd6, 0xd1, 0x05, 0x19, 0x43, 0x42, + 0xaa, 0x06, 0x69, 0x87, 0xd8, 0x5e, 0xc3, 0xaa, 0x33, 0x34, 0x39, 0x6b, 0xc4, 0xb0, 0xba, 0x0c, + 0x19, 0xd7, 0x62, 0x66, 0xdd, 0x6b, 0x78, 0x5c, 0x9a, 0x9c, 0x76, 0x2d, 0xf6, 0x24, 0x82, 0x75, + 0x13, 0xae, 0xf4, 0xd9, 0xd4, 0xb1, 0x38, 0xb2, 0xe0, 0x28, 0x61, 0x81, 0xb0, 0x70, 0xee, 0xa8, + 0xdb, 0x82, 0x15, 0x00, 0xdb, 0x8e, 0x5d, 0x2a, 0xb3, 0x2c, 0xc2, 0x08, 0xa7, 0xfe, 0xa6, 0xc0, + 0x62, 0xc7, 0xab, 0x7b, 0x21, 0x3f, 0x67, 0x1e, 0x2d, 0xc2, 0xb4, 0x4f, 0x7d, 0x9b, 0xa0, 0xaf, + 0xa6, 0x0c, 0x01, 0x74, 0x67, 0xd7, 0x54, 0x22, 0xbb, 0xde, 0x71, 0xc2, 0x7c, 0x00, 0x57, 0x4f, + 0x32, 0x2d, 0xf6, 0xdf, 0x0a, 0x80, 0xc7, 0xcc, 0x80, 0x34, 0xe8, 0x21, 0x71, 0xd0, 0xca, 0xb4, + 0x91, 0xf1, 0x98, 0x21, 0x10, 0xfa, 0x3e, 0xfa, 0x5e, 0x40, 0x0f, 0x03, 0xda, 0x78, 0x47, 0xee, + 0xd1, 0xd7, 0xe1, 0xda, 0x40, 0x39, 0x71, 0x76, 0xff, 0xa8, 0xc0, 0x85, 0x5d, 0xe6, 0x3e, 0xb2, + 0xd8, 0xb3, 0xc0, 0xb3, 0xc9, 0x27, 0x94, 0x9f, 0x43, 0x89, 0x66, 0xc4, 0xa2, 0xa3, 0x04, 0x02, + 0xea, 0x35, 0x98, 0x13, 0x5e, 0xf6, 0xc3, 0x46, 0x95, 0x04, 0x18, 0xa8, 0x29, 0x63, 0x16, 0x71, + 0x4f, 0x11, 0x85, 0xc9, 0x1d, 0x36, 0x9b, 0xf5, 0x76, 0x9c, 0xdc, 0x08, 0xe9, 0x1a, 0xe4, 0x7b, + 0x35, 0x8b, 0xd5, 0xfe, 0x65, 0x1a, 0x8b, 0x36, 0x42, 0xee, 0xf9, 0x7b, 0x55, 0x46, 0x82, 0x43, + 0xe2, 0xec, 0x85, 0xbc, 0x4a, 0x43, 0xdf, 0xa9, 0xb4, 0x86, 0x58, 0xb0, 0x0c, 0x98, 0xa5, 0x22, + 0xea, 0x22, 0x6d, 0xd3, 0x11, 0x02, 0x83, 0x5e, 0x84, 0x05, 0x2a, 0x99, 0x99, 0x34, 0x72, 0x57, + 0x77, 0xef, 0xba, 0x48, 0x8f, 0xe5, 0x54, 0xc4, 0xfd, 0xf7, 0x41, 0xeb, 0xb9, 0x2f, 0x12, 0x88, + 0x78, 0x6e, 0x8d, 0x4b, 0x5b, 0xf3, 0x09, 0xb2, 0xad, 0xe3, 0x73, 0xf5, 0x7f, 0xb0, 0xd4, 0x43, + 0x1d, 0x15, 0x6c, 0xc8, 0x88, 0x93, 0x07, 0x24, 0x5d, 0x4c, 0x90, 0x3e, 0xb2, 0xd8, 0x0b, 0x46, + 0x1c, 0xf5, 0x08, 0xf4, 0x1e, 0x32, 0xb2, 0xbf, 0x4f, 0x6c, 0xee, 0x1d, 0x12, 0x64, 0x20, 0xa2, + 0x30, 0x8b, 0x33, 0xa9, 0x28, 0x67, 0xd2, 0x8d, 0x31, 0x66, 0xd2, 0x8e, 0xcf, 0x8d, 0x42, 0x42, + 0xe2, 0x83, 0x0e, 0xdf, 0x4e, 0x10, 0xd4, 0x8f, 0x46, 0xc8, 0x16, 0xdd, 0x66, 0x0e, 0xb5, 0x1f, + 0xcc, 0x0b, 0x7b, 0x90, 0x4a, 0x21, 0x77, 0x68, 0xd5, 0x43, 0x62, 0x06, 0xc4, 0x26, 0x5e, 0x54, + 0x2a, 0x18, 0xff, 0xad, 0xc7, 0xa7, 0x9c, 0xa3, 0x7f, 0xbf, 0x5e, 0xbd, 0xd4, 0xb6, 0x1a, 0xf5, + 0xfb, 0x7a, 0x92, 0x9d, 0x6e, 0x64, 0x11, 0x61, 0x48, 0x58, 0xbd, 0x09, 0x29, 0xc6, 0x2d, 0x1e, + 0x8a, 0x5e, 0x99, 0x2b, 0x5f, 0x2a, 0xca, 0x4d, 0x42, 0xde, 0x78, 0x8e, 0x87, 0x86, 0xbc, 0xa4, + 0xae, 0xc2, 0xac, 0x30, 0x11, 0x6f, 0xc9, 0x26, 0x00, 0x88, 0xda, 0x8e, 0x30, 0xea, 0x0d, 0x98, + 0x17, 0x17, 0xa2, 0x71, 0x2b, 0x0a, 0x30, 0x8d, 0x96, 0x67, 0x11, 0x5d, 0x61, 0xec, 0x29, 0xf6, + 0xa9, 0xc4, 0xb0, 0xcb, 0x0c, 0x1f, 0x76, 0xfa, 0x75, 0x58, 0x1f, 0x92, 0xd8, 0x71, 0x01, 0x7c, + 0x35, 0x85, 0x4b, 0x43, 0xf2, 0xde, 0x8e, 0x3f, 0x3a, 0xff, 0xa3, 0x6a, 0x23, 0xbe, 0x43, 0x02, + 0x99, 0xfc, 0x12, 0x8a, 0x8c, 0x11, 0x4f, 0x66, 0xcf, 0x60, 0xca, 0x0a, 0xf4, 0xb6, 0x2c, 0x73, + 0x0d, 0xd2, 0xd2, 0xc1, 0x81, 0xec, 0xba, 0x31, 0xac, 0x5e, 0x87, 0x5c, 0xe7, 0x59, 0x3a, 0x6d, + 0x5a, 0xb0, 0xe8, 0x60, 0x85, 0xdf, 0x8e, 0x17, 0xa7, 0xd4, 0xb9, 0x16, 0xa7, 0xc8, 0xca, 0x06, + 0x61, 0xcc, 0x72, 0x85, 0xe3, 0x33, 0x46, 0x07, 0x54, 0xaf, 0x02, 0x44, 0x0e, 0x97, 0xf5, 0x9b, + 0x11, 0x7a, 0x7a, 0xbe, 0x2c, 0xdb, 0x1b, 0x30, 0xef, 0xf9, 0xa6, 0xec, 0xfe, 0xa2, 0x56, 0x45, + 0xc1, 0x65, 0x3d, 0xbf, 0xbb, 0x40, 0x13, 0x23, 0x74, 0x16, 0x6f, 0xc4, 0x23, 0x34, 0x19, 0xd5, + 0xb9, 0x11, 0x2b, 0xcc, 0x32, 0x64, 0x78, 0xcb, 0xa4, 0x81, 0xe7, 0x7a, 0x7e, 0x3e, 0x2b, 0xd4, + 0xe1, 0xad, 0x3d, 0x84, 0xa3, 0xce, 0x69, 0x31, 0x46, 0x78, 0x3e, 0x87, 0x07, 0x02, 0x88, 0xd2, + 0x8f, 0x1c, 0x12, 0x9f, 0xcb, 0x19, 0x34, 0x8f, 0xe2, 0x01, 0x51, 0x62, 0x0c, 0xfd, 0x1b, 0xf4, + 0xc1, 0x19, 0x10, 0x27, 0xca, 0x13, 0xdc, 0x5e, 0x36, 0xab, 0x34, 0xe0, 0xcf, 0x79, 0x68, 0x1f, + 0x6c, 0x6f, 0x57, 0x3e, 0x1b, 0xbe, 0x3c, 0x0e, 0x1b, 0xeb, 0xcb, 0x38, 0xbb, 0x92, 0xdc, 0x62, + 0x51, 0x87, 0x38, 0xf2, 0x0d, 0xb2, 0x1f, 0xfa, 0x0e, 0x5e, 0x21, 0xce, 0xb9, 0xa4, 0x89, 0x7c, + 0x8a, 0xb8, 0xc5, 0x9b, 0x88, 0xe8, 0xc4, 0x59, 0x81, 0x95, 0xab, 0x88, 0x5e, 0xc0, 0x79, 0xdc, + 0x27, 0xb7, 0xa3, 0x57, 0xf9, 0xcf, 0x59, 0x98, 0xdc, 0x65, 0xae, 0xfa, 0x8d, 0x02, 0x17, 0xfb, + 0x17, 0x92, 0x3b, 0xc5, 0xa1, 0xef, 0x23, 0xc5, 0x93, 0x46, 0xbd, 0xf6, 0xde, 0x19, 0x88, 0xe2, + 0xfd, 0xe0, 0x6b, 0x05, 0x2e, 0xf4, 0xed, 0xd7, 0xe5, 0x31, 0x39, 0x76, 0xd1, 0x68, 0xf7, 0x4f, + 0x4f, 0x13, 0x2b, 0xf1, 0xbd, 0x02, 0x97, 0x07, 0xec, 0x20, 0x77, 0x47, 0xb3, 0x3d, 0x99, 0x52, + 0xfb, 0xf0, 0xac, 0x94, 0xb1, 0x5a, 0x6d, 0xc8, 0x26, 0x77, 0x91, 0xd2, 0x68, 0x96, 0x09, 0x02, + 0xed, 0xff, 0xa7, 0x24, 0x88, 0x45, 0xff, 0xa4, 0x40, 0x7e, 0xe0, 0x42, 0x31, 0x86, 0xab, 0x07, + 0xd1, 0x6a, 0x5b, 0x67, 0xa7, 0x8d, 0x95, 0xfb, 0x41, 0x81, 0xa5, 0x41, 0xcd, 0xfe, 0xde, 0x69, + 0xf9, 0xc7, 0xa4, 0xda, 0xe6, 0x99, 0x49, 0x63, 0xcd, 0xbe, 0x80, 0x5c, 0xcf, 0xbb, 0xd1, 0xad, + 0xd1, 0x4c, 0x93, 0x14, 0xda, 0xdd, 0xd3, 0x52, 0x24, 0x6a, 0xa9, 0xef, 0xdd, 0x78, 0x8c, 0x5a, + 0xea, 0xa5, 0x19, 0xa7, 0x96, 0x06, 0xbd, 0x33, 0xab, 0x5f, 0xc2, 0x7c, 0xef, 0x17, 0x85, 0xdb, + 0xa3, 0xd9, 0xf5, 0x90, 0x68, 0xf7, 0x4e, 0x4d, 0xd2, 0x1d, 0x83, 0x9e, 0x0e, 0x3f, 0x46, 0x0c, + 0x92, 0x14, 0xe3, 0xc4, 0xe0, 0xe4, 0xbe, 0x8f, 0x7d, 0xb5, 0xbf, 0xeb, 0xdf, 0x19, 0xa7, 0x17, + 0xf4, 0x10, 0x8d, 0xd3, 0x57, 0x07, 0xf6, 0xf9, 0xad, 0x8f, 0x5f, 0xbe, 0x29, 0x28, 0xaf, 0xde, + 0x14, 0x94, 0x3f, 0xde, 0x14, 0x94, 0x6f, 0xdf, 0x16, 0x26, 0x5e, 0xbd, 0x2d, 0x4c, 0xfc, 0xfa, + 0xb6, 0x30, 0xf1, 0xf9, 0xed, 0xae, 0xcd, 0x22, 0x62, 0x7b, 0x53, 0x7c, 0x92, 0xea, 0x48, 0x28, + 0xb5, 0x4a, 0xdd, 0x1f, 0xaa, 0xa2, 0x45, 0xa3, 0x9a, 0xc2, 0x0f, 0x46, 0x77, 0xfe, 0x09, 0x00, + 0x00, 0xff, 0xff, 0xef, 0x61, 0xc8, 0xd2, 0xc3, 0x12, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1597,7 +1487,6 @@ type MsgClient interface { WhitelistERC20(ctx context.Context, in *MsgWhitelistERC20, opts ...grpc.CallOption) (*MsgWhitelistERC20Response, error) UpdateTssAddress(ctx context.Context, in *MsgUpdateTssAddress, opts ...grpc.CallOption) (*MsgUpdateTssAddressResponse, error) 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) } @@ -1691,15 +1580,6 @@ func (c *msgClient) MigrateTssFunds(ctx context.Context, in *MsgMigrateTssFunds, return out, nil } -func (c *msgClient) CreateTSSVoter(ctx context.Context, in *MsgCreateTSSVoter, opts ...grpc.CallOption) (*MsgCreateTSSVoterResponse, error) { - out := new(MsgCreateTSSVoterResponse) - err := c.cc.Invoke(ctx, "/zetachain.zetacore.crosschain.Msg/CreateTSSVoter", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *msgClient) AbortStuckCCTX(ctx context.Context, in *MsgAbortStuckCCTX, opts ...grpc.CallOption) (*MsgAbortStuckCCTXResponse, error) { out := new(MsgAbortStuckCCTXResponse) err := c.cc.Invoke(ctx, "/zetachain.zetacore.crosschain.Msg/AbortStuckCCTX", in, out, opts...) @@ -1729,7 +1609,6 @@ type MsgServer interface { WhitelistERC20(context.Context, *MsgWhitelistERC20) (*MsgWhitelistERC20Response, error) UpdateTssAddress(context.Context, *MsgUpdateTssAddress) (*MsgUpdateTssAddressResponse, error) MigrateTssFunds(context.Context, *MsgMigrateTssFunds) (*MsgMigrateTssFundsResponse, error) - CreateTSSVoter(context.Context, *MsgCreateTSSVoter) (*MsgCreateTSSVoterResponse, error) AbortStuckCCTX(context.Context, *MsgAbortStuckCCTX) (*MsgAbortStuckCCTXResponse, error) RefundAbortedCCTX(context.Context, *MsgRefundAbortedCCTX) (*MsgRefundAbortedCCTXResponse, error) } @@ -1765,9 +1644,6 @@ func (*UnimplementedMsgServer) UpdateTssAddress(ctx context.Context, req *MsgUpd func (*UnimplementedMsgServer) MigrateTssFunds(ctx context.Context, req *MsgMigrateTssFunds) (*MsgMigrateTssFundsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method MigrateTssFunds not implemented") } -func (*UnimplementedMsgServer) CreateTSSVoter(ctx context.Context, req *MsgCreateTSSVoter) (*MsgCreateTSSVoterResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method CreateTSSVoter not implemented") -} func (*UnimplementedMsgServer) AbortStuckCCTX(ctx context.Context, req *MsgAbortStuckCCTX) (*MsgAbortStuckCCTXResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AbortStuckCCTX not implemented") } @@ -1941,24 +1817,6 @@ func _Msg_MigrateTssFunds_Handler(srv interface{}, ctx context.Context, dec func return interceptor(ctx, in, info, handler) } -func _Msg_CreateTSSVoter_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(MsgCreateTSSVoter) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(MsgServer).CreateTSSVoter(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/zetachain.zetacore.crosschain.Msg/CreateTSSVoter", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(MsgServer).CreateTSSVoter(ctx, req.(*MsgCreateTSSVoter)) - } - return interceptor(ctx, in, info, handler) -} - func _Msg_AbortStuckCCTX_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(MsgAbortStuckCCTX) if err := dec(in); err != nil { @@ -2035,10 +1893,6 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "MigrateTssFunds", Handler: _Msg_MigrateTssFunds_Handler, }, - { - MethodName: "CreateTSSVoter", - Handler: _Msg_CreateTSSVoter_Handler, - }, { MethodName: "AbortStuckCCTX", Handler: _Msg_AbortStuckCCTX_Handler, @@ -2052,76 +1906,6 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ Metadata: "crosschain/tx.proto", } -func (m *MsgCreateTSSVoter) 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 *MsgCreateTSSVoter) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *MsgCreateTSSVoter) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Status != 0 { - i = encodeVarintTx(dAtA, i, uint64(m.Status)) - i-- - dAtA[i] = 0x20 - } - if m.KeyGenZetaHeight != 0 { - i = encodeVarintTx(dAtA, i, uint64(m.KeyGenZetaHeight)) - i-- - dAtA[i] = 0x18 - } - if len(m.TssPubkey) > 0 { - i -= len(m.TssPubkey) - copy(dAtA[i:], m.TssPubkey) - i = encodeVarintTx(dAtA, i, uint64(len(m.TssPubkey))) - 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 *MsgCreateTSSVoterResponse) 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 *MsgCreateTSSVoterResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *MsgCreateTSSVoterResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - return len(dAtA) - i, nil -} - func (m *MsgMigrateTssFunds) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -3084,38 +2868,6 @@ func encodeVarintTx(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) return base } -func (m *MsgCreateTSSVoter) 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.TssPubkey) - if l > 0 { - n += 1 + l + sovTx(uint64(l)) - } - if m.KeyGenZetaHeight != 0 { - n += 1 + sovTx(uint64(m.KeyGenZetaHeight)) - } - if m.Status != 0 { - n += 1 + sovTx(uint64(m.Status)) - } - return n -} - -func (m *MsgCreateTSSVoterResponse) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - return n -} - func (m *MsgMigrateTssFunds) Size() (n int) { if m == nil { return 0 @@ -3555,208 +3307,6 @@ func sovTx(x uint64) (n int) { func sozTx(x uint64) (n int) { return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } -func (m *MsgCreateTSSVoter) 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: MsgCreateTSSVoter: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: MsgCreateTSSVoter: 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 TssPubkey", 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.TssPubkey = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field KeyGenZetaHeight", wireType) - } - m.KeyGenZetaHeight = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.KeyGenZetaHeight |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) - } - m.Status = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Status |= chains.ReceiveStatus(b&0x7F) << shift - if b < 0x80 { - break - } - } - 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 *MsgCreateTSSVoterResponse) 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: MsgCreateTSSVoterResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: MsgCreateTSSVoterResponse: 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 (m *MsgMigrateTssFunds) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/crosschain/types/validate.go b/x/crosschain/types/validate.go new file mode 100644 index 0000000000..713eb8d432 --- /dev/null +++ b/x/crosschain/types/validate.go @@ -0,0 +1,68 @@ +package types + +import ( + "fmt" + "regexp" + + "cosmossdk.io/errors" + "github.com/btcsuite/btcutil" + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/zeta-chain/zetacore/pkg/chains" +) + +// ValidateZetaIndex validates the zeta index +func ValidateZetaIndex(index string) error { + if len(index) != ZetaIndexLength { + return errors.Wrap(ErrInvalidIndexValue, fmt.Sprintf("invalid index length %d", len(index))) + } + return nil +} + +// ValidateHashForChain validates the hash for the chain +func ValidateHashForChain(hash string, chainID int64) error { + if chains.IsEthereumChain(chainID) || chains.IsZetaChain(chainID) { + _, err := hexutil.Decode(hash) + if err != nil { + return fmt.Errorf("hash must be a valid ethereum hash %s", hash) + } + return nil + } + if chains.IsBitcoinChain(chainID) { + r, err := regexp.Compile("^[a-fA-F0-9]{64}$") + if err != nil { + return fmt.Errorf("error compiling regex") + } + if !r.MatchString(hash) { + return fmt.Errorf("hash must be a valid bitcoin hash %s", hash) + } + return nil + } + return fmt.Errorf("invalid chain id %d", chainID) +} + +// ValidateAddressForChain validates the address for the chain +func ValidateAddressForChain(address string, chainID int64) error { + // we do not validate the address for zeta chain as the address field can be btc or eth address + if chains.IsZetaChain(chainID) { + return nil + } + if chains.IsEthereumChain(chainID) { + if !ethcommon.IsHexAddress(address) { + return fmt.Errorf("invalid address %s , chain %d", address, chainID) + } + return nil + } + if chains.IsBitcoinChain(chainID) { + addr, err := chains.DecodeBtcAddress(address, chainID) + if err != nil { + return fmt.Errorf("invalid address %s , chain %d: %s", address, chainID, err) + } + _, ok := addr.(*btcutil.AddressWitnessPubKeyHash) + if !ok { + return fmt.Errorf(" invalid address %s (not P2WPKH address)", address) + } + return nil + } + return fmt.Errorf("invalid chain id %d", chainID) +} diff --git a/x/crosschain/types/validate_test.go b/x/crosschain/types/validate_test.go new file mode 100644 index 0000000000..206f99b39c --- /dev/null +++ b/x/crosschain/types/validate_test.go @@ -0,0 +1,40 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func TestValidateAddressForChain(t *testing.T) { + require.Error(t, types.ValidateAddressForChain("0x123", chains.GoerliChain().ChainId)) + require.Error(t, types.ValidateAddressForChain("", chains.GoerliChain().ChainId)) + require.Error(t, types.ValidateAddressForChain("%%%%", chains.GoerliChain().ChainId)) + require.NoError(t, types.ValidateAddressForChain("0x792c127Fa3AC1D52F904056Baf1D9257391e7D78", chains.GoerliChain().ChainId)) + require.Error(t, types.ValidateAddressForChain("1EYVvXLusCxtVuEwoYvWRyN5EZTXwPVvo3", chains.BtcMainnetChain().ChainId)) + require.Error(t, types.ValidateAddressForChain("bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw", chains.BtcMainnetChain().ChainId)) + require.Error(t, types.ValidateAddressForChain("", chains.BtcRegtestChain().ChainId)) + require.NoError(t, types.ValidateAddressForChain("bc1qysd4sp9q8my59ul9wsf5rvs9p387hf8vfwatzu", chains.BtcMainnetChain().ChainId)) + require.NoError(t, types.ValidateAddressForChain("bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw", chains.BtcRegtestChain().ChainId)) + require.NoError(t, types.ValidateAddressForChain("bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw", chains.ZetaChainMainnet().ChainId)) + require.NoError(t, types.ValidateAddressForChain("0x792c127Fa3AC1D52F904056Baf1D9257391e7D78", chains.ZetaChainMainnet().ChainId)) +} + +func TestValidateZetaIndex(t *testing.T) { + require.NoError(t, types.ValidateZetaIndex("0x84bd5c9922b63c52d8a9ca686e0a57ff978150b71be0583514d01c27aa341910")) + require.NoError(t, types.ValidateZetaIndex(sample.ZetaIndex(t))) + require.Error(t, types.ValidateZetaIndex("0")) + require.Error(t, types.ValidateZetaIndex("0x70e967acFcC17c3941E87562161406d41676FD83")) +} + +func TestValidateHashForChain(t *testing.T) { + require.NoError(t, types.ValidateHashForChain("0x84bd5c9922b63c52d8a9ca686e0a57ff978150b71be0583514d01c27aa341910", chains.GoerliChain().ChainId)) + require.Error(t, types.ValidateHashForChain("", chains.GoerliChain().ChainId)) + require.Error(t, types.ValidateHashForChain("a0fa5a82f106fb192e4c503bfa8d54b2de20a821e09338094ab825cc9b275059", chains.GoerliChain().ChainId)) + require.NoError(t, types.ValidateHashForChain("15b7880f5d236e857a5e8f043ce9d56f5ef01e1c3f2a786baf740fc0bb7a22a3", chains.BtcMainnetChain().ChainId)) + require.NoError(t, types.ValidateHashForChain("a0fa5a82f106fb192e4c503bfa8d54b2de20a821e09338094ab825cc9b275059", chains.BtcTestNetChain().ChainId)) + require.Error(t, types.ValidateHashForChain("0x84bd5c9922b63c52d8a9ca686e0a57ff978150b71be0583514d01c27aa341910", chains.BtcMainnetChain().ChainId)) +} diff --git a/x/emissions/abci.go b/x/emissions/abci.go index 6472ae9e20..1f6cdd627c 100644 --- a/x/emissions/abci.go +++ b/x/emissions/abci.go @@ -63,7 +63,7 @@ func BeginBlocker(ctx sdk.Context, keeper keeper.Keeper) { // This function uses the distribution module of cosmos-sdk , by directly sending funds to the feecollector. func DistributeValidatorRewards(ctx sdk.Context, amount sdkmath.Int, bankKeeper types.BankKeeper, feeCollector string) error { coin := sdk.NewCoins(sdk.NewCoin(config.BaseDenom, amount)) - ctx.Logger().Info(fmt.Sprintf(fmt.Sprintf("Distributing Validator Rewards Total:%s To FeeCollector : %s", amount.String(), feeCollector))) + ctx.Logger().Info(fmt.Sprintf("Distributing Validator Rewards Total:%s To FeeCollector : %s", amount.String(), feeCollector)) return bankKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, feeCollector, coin) } diff --git a/x/emissions/abci_test.go b/x/emissions/abci_test.go index 65153b377a..1f60857be4 100644 --- a/x/emissions/abci_test.go +++ b/x/emissions/abci_test.go @@ -6,6 +6,7 @@ import ( sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/cmd/zetacored/config" "github.com/zeta-chain/zetacore/pkg/coin" @@ -69,6 +70,90 @@ func TestBeginBlocker(t *testing.T) { require.True(t, sk.BankKeeper.GetBalance(ctx, feeCollectorAddress, config.BaseDenom).Amount.IsZero()) require.True(t, sk.BankKeeper.GetBalance(ctx, emissionstypes.EmissionsModuleAddress, config.BaseDenom).Amount.Equal(sdk.NewInt(1000000000000))) }) + t.Run("begin blocker returns early if validator distribution fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.EmissionKeeperWithMockOptions(t, keepertest.EmissionMockOptions{ + UseBankMock: true, + }) + // Total block rewards is the fixed amount of rewards that are distributed + totalBlockRewards, err := coin.GetAzetaDecFromAmountInZeta(emissionstypes.BlockRewardsInZeta) + totalRewardCoins := sdk.NewCoins(sdk.NewCoin(config.BaseDenom, totalBlockRewards.TruncateInt())) + require.NoError(t, err) + + bankMock := keepertest.GetEmissionsBankMock(t, k) + bankMock.On("GetBalance", + ctx, mock.Anything, config.BaseDenom). + Return(totalRewardCoins[0], nil).Once() + + // fail first distribution + bankMock.On("SendCoinsFromModuleToModule", + mock.Anything, emissionstypes.ModuleName, k.GetFeeCollector(), mock.Anything). + Return(emissionstypes.ErrUnableToWithdrawEmissions).Once() + emissionsModule.BeginBlocker(ctx, *k) + + bankMock.AssertNumberOfCalls(t, "SendCoinsFromModuleToModule", 1) + }) + + t.Run("begin blocker returns early if observer distribution fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.EmissionKeeperWithMockOptions(t, keepertest.EmissionMockOptions{ + UseBankMock: true, + }) + // Total block rewards is the fixed amount of rewards that are distributed + totalBlockRewards, err := coin.GetAzetaDecFromAmountInZeta(emissionstypes.BlockRewardsInZeta) + totalRewardCoins := sdk.NewCoins(sdk.NewCoin(config.BaseDenom, totalBlockRewards.TruncateInt())) + require.NoError(t, err) + + bankMock := keepertest.GetEmissionsBankMock(t, k) + bankMock.On("GetBalance", + ctx, mock.Anything, config.BaseDenom). + Return(totalRewardCoins[0], nil).Once() + + // allow first distribution + bankMock.On("SendCoinsFromModuleToModule", + mock.Anything, emissionstypes.ModuleName, k.GetFeeCollector(), mock.Anything). + Return(nil).Once() + + // fail second distribution + bankMock.On("SendCoinsFromModuleToModule", + mock.Anything, emissionstypes.ModuleName, emissionstypes.UndistributedObserverRewardsPool, mock.Anything). + Return(emissionstypes.ErrUnableToWithdrawEmissions).Once() + emissionsModule.BeginBlocker(ctx, *k) + + bankMock.AssertNumberOfCalls(t, "SendCoinsFromModuleToModule", 2) + }) + + t.Run("begin blocker returns early if tss distribution fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.EmissionKeeperWithMockOptions(t, keepertest.EmissionMockOptions{ + UseBankMock: true, + }) + // Total block rewards is the fixed amount of rewards that are distributed + totalBlockRewards, err := coin.GetAzetaDecFromAmountInZeta(emissionstypes.BlockRewardsInZeta) + totalRewardCoins := sdk.NewCoins(sdk.NewCoin(config.BaseDenom, totalBlockRewards.TruncateInt())) + require.NoError(t, err) + + bankMock := keepertest.GetEmissionsBankMock(t, k) + bankMock.On("GetBalance", + ctx, mock.Anything, config.BaseDenom). + Return(totalRewardCoins[0], nil).Once() + + // allow first distribution + bankMock.On("SendCoinsFromModuleToModule", + mock.Anything, emissionstypes.ModuleName, k.GetFeeCollector(), mock.Anything). + Return(nil).Once() + + // allow second distribution + bankMock.On("SendCoinsFromModuleToModule", + mock.Anything, emissionstypes.ModuleName, emissionstypes.UndistributedObserverRewardsPool, mock.Anything). + Return(nil).Once() + + // fail third distribution + bankMock.On("SendCoinsFromModuleToModule", + mock.Anything, emissionstypes.ModuleName, emissionstypes.UndistributedTssRewardsPool, mock.Anything). + Return(emissionstypes.ErrUnableToWithdrawEmissions).Once() + emissionsModule.BeginBlocker(ctx, *k) + + bankMock.AssertNumberOfCalls(t, "SendCoinsFromModuleToModule", 3) + }) + t.Run("successfully distribute rewards", func(t *testing.T) { numberOfTestBlocks := 100 k, ctx, sk, zk := keepertest.EmissionsKeeper(t) @@ -154,6 +239,7 @@ func TestBeginBlocker(t *testing.T) { func TestDistributeObserverRewards(t *testing.T) { keepertest.SetConfig(false) + k, ctx, _, _ := keepertest.EmissionsKeeper(t) observerSet := sample.ObserverSet(4) tt := []struct { @@ -240,6 +326,12 @@ func TestDistributeObserverRewards(t *testing.T) { } for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { + for _, observer := range observerSet.ObserverList { + k.SetWithdrawableEmission(ctx, emissionstypes.WithdrawableEmissions{ + Address: observer, + Amount: sdkmath.NewInt(100), + }) + } // Keeper initialization k, ctx, sk, zk := keepertest.EmissionsKeeper(t) @@ -283,7 +375,6 @@ func TestDistributeObserverRewards(t *testing.T) { Height: 0, BallotsIndexList: ballotIdentifiers, }) - ctx = ctx.WithBlockHeight(100) // Distribute the rewards and check if the rewards are distributed correctly diff --git a/x/emissions/client/tests/cli_test.go b/x/emissions/client/tests/cli_test.go deleted file mode 100644 index 4229cf2a36..0000000000 --- a/x/emissions/client/tests/cli_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package querytests - -import ( - "testing" - - "github.com/stretchr/testify/suite" - "github.com/zeta-chain/zetacore/testutil/network" -) - -func TestCLIQuerySuite(t *testing.T) { - cfg := network.DefaultConfig() - suite.Run(t, NewCLITestSuite(cfg)) -} diff --git a/x/emissions/client/tests/observer_rewards_test.go b/x/emissions/client/tests/observer_rewards_test.go deleted file mode 100644 index c8f11d2449..0000000000 --- a/x/emissions/client/tests/observer_rewards_test.go +++ /dev/null @@ -1,104 +0,0 @@ -package querytests - -import ( - "fmt" - - sdkmath "cosmossdk.io/math" - "github.com/cosmos/cosmos-sdk/client/flags" - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/bank/client/cli" - "github.com/stretchr/testify/suite" - "github.com/zeta-chain/zetacore/cmd/zetacored/config" - emissionscli "github.com/zeta-chain/zetacore/x/emissions/client/cli" - emissionskeeper "github.com/zeta-chain/zetacore/x/emissions/keeper" - emissionstypes "github.com/zeta-chain/zetacore/x/emissions/types" - observercli "github.com/zeta-chain/zetacore/x/observer/client/cli" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" -) - -func (s *CliTestSuite) TestObserverRewards() { - emissionPool := "800000000000000000000azeta" - val := s.network.Validators[0] - - out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, emissionscli.CmdListPoolAddresses(), []string{"--output", "json"}) - s.Require().NoError(err) - resPools := emissionstypes.QueryListPoolAddressesResponse{} - s.Require().NoError(err) - s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &resPools)) - txArgs := []string{ - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(config.BaseDenom, sdk.NewInt(10))).String()), - } - - // Fund the emission pool to start the emission process - sendArgs := []string{val.Address.String(), - resPools.EmissionModuleAddress, emissionPool} - args := append(sendArgs, txArgs...) - out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewSendTxCmd(), args) - s.Require().NoError(err) - s.Require().NoError(s.network.WaitForNextBlock()) - - // Collect parameter values and build assertion map for the randomised ballot set created - emissionFactors := emissionstypes.QueryGetEmissionsFactorsResponse{} - out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, emissionscli.CmdGetEmmisonsFactors(), []string{"--output", "json"}) - s.Require().NoError(err) - s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &emissionFactors)) - emissionParams := emissionstypes.QueryParamsResponse{} - out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, emissionscli.CmdQueryParams(), []string{"--output", "json"}) - s.Require().NoError(err) - s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &emissionParams)) - observerParams := observertypes.QueryParamsResponse{} - out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, observercli.CmdQueryParams(), []string{"--output", "json"}) - s.Require().NoError(err) - s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &observerParams)) - _, err = s.network.WaitForHeight(s.ballots[0].BallotCreationHeight + observerParams.Params.BallotMaturityBlocks) - s.Require().NoError(err) - out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, emissionscli.CmdGetEmmisonsFactors(), []string{"--output", "json"}) - resFactorsNewBlocks := emissionstypes.QueryGetEmissionsFactorsResponse{} - s.Require().NoError(err) - s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &resFactorsNewBlocks)) - // Duration factor is calculated in the same block,so we need to query based from the committed state at which the distribution is done - // Would be cleaner to use `--height` flag, but it is not supported by the ExecTestCLICmd function yet - emissionFactors.DurationFactor = resFactorsNewBlocks.DurationFactor - asertValues := CalculateObserverRewards(&s.Suite, s.ballots, emissionParams.Params.ObserverEmissionPercentage, emissionFactors.ReservesFactor, emissionFactors.BondFactor, emissionFactors.DurationFactor) - - // Assert withdrawable rewards for each validator - resAvailable := emissionstypes.QueryShowAvailableEmissionsResponse{} - for i := 0; i < len(s.network.Validators); i++ { - out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, emissionscli.CmdShowAvailableEmissions(), []string{s.network.Validators[i].Address.String(), "--output", "json"}) - s.Require().NoError(err) - s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &resAvailable)) - s.Require().Equal(sdk.NewCoin(config.BaseDenom, asertValues[s.network.Validators[i].Address.String()]).String(), resAvailable.Amount, "Validator %s has incorrect withdrawable rewards", s.network.Validators[i].Address.String()) - } - -} - -func CalculateObserverRewards(s *suite.Suite, ballots []*observertypes.Ballot, observerEmissionPercentage, reservesFactor, bondFactor, durationFactor string) map[string]sdkmath.Int { - calculatedDistributer := map[string]sdkmath.Int{} - //blockRewards := sdk.MustNewDecFromStr(reservesFactor).Mul(sdk.MustNewDecFromStr(bondFactor)).Mul(sdk.MustNewDecFromStr(durationFactor)) - blockRewards, err := emissionskeeper.CalculateFixedValidatorRewards(emissionstypes.AvgBlockTime) - s.Require().NoError(err) - observerRewards := sdk.MustNewDecFromStr(observerEmissionPercentage).Mul(blockRewards).TruncateInt() - rewardsDistributer := map[string]int64{} - totalRewardsUnits := int64(0) - // BuildRewardsDistribution has a separate unit test - for _, ballot := range ballots { - totalRewardsUnits = totalRewardsUnits + ballot.BuildRewardsDistribution(rewardsDistributer) - } - rewardPerUnit := observerRewards.Quo(sdk.NewInt(totalRewardsUnits)) - for address, units := range rewardsDistributer { - if units == 0 { - calculatedDistributer[address] = sdk.ZeroInt() - continue - } - if units < 0 { - calculatedDistributer[address] = sdk.ZeroInt() - continue - } - if units > 0 { - calculatedDistributer[address] = rewardPerUnit.Mul(sdkmath.NewInt(units)) - } - } - return calculatedDistributer -} diff --git a/x/emissions/client/tests/suite.go b/x/emissions/client/tests/suite.go deleted file mode 100644 index c13294ddf7..0000000000 --- a/x/emissions/client/tests/suite.go +++ /dev/null @@ -1,113 +0,0 @@ -package querytests - -import ( - "crypto/rand" - "math/big" - "strconv" - "testing" - - sdk "github.com/cosmos/cosmos-sdk/types" - ethcfg "github.com/evmos/ethermint/cmd/config" - "github.com/stretchr/testify/suite" - "github.com/zeta-chain/zetacore/app" - cmdcfg "github.com/zeta-chain/zetacore/cmd/zetacored/config" - "github.com/zeta-chain/zetacore/testutil/network" - observerTypes "github.com/zeta-chain/zetacore/x/observer/types" -) - -type CliTestSuite struct { - suite.Suite - - cfg network.Config - network *network.Network - ballots []*observerTypes.Ballot -} - -func NewCLITestSuite(cfg network.Config) *CliTestSuite { - return &CliTestSuite{cfg: cfg} -} - -func (s *CliTestSuite) Setconfig() { - config := sdk.GetConfig() - cmdcfg.SetBech32Prefixes(config) - ethcfg.SetBip44CoinType(config) - // Make sure address is compatible with ethereum - config.SetAddressVerifier(app.VerifyAddressFormat) - config.Seal() -} -func (s *CliTestSuite) SetupSuite() { - s.T().Log("setting up integration test suite") - s.Setconfig() - minOBsDel, ok := sdk.NewIntFromString("100000000000000000000") - s.Require().True(ok) - s.cfg.StakingTokens = minOBsDel.Mul(sdk.NewInt(int64(10))) - s.cfg.BondedTokens = minOBsDel - observerList := []string{"zeta13c7p3xrhd6q2rx3h235jpt8pjdwvacyw6twpax", - "zeta1f203dypqg5jh9hqfx0gfkmmnkdfuat3jr45ep2", - "zeta1szrskhdeleyt6wmn0nfxvcvt2l6f4fn06uaga4", - "zeta16h3y7s7030l4chcznwq3n6uz2m9wvmzu5vwt7c", - "zeta1xl2rfsrmx8nxryty3lsjuxwdxs59cn2q65e5ca", - "zeta1ktmprjdvc72jq0mpu8tn8sqx9xwj685qx0q6kt", - "zeta1ygeyr8pqfjvclxay5234gulnjzv2mkz6lph9y4", - "zeta1zegyenj7xg5nck04ykkzndm2qxdzc6v83mklsy", - "zeta1us2qpqdcctk6q7qv2c9d9jvjxlv88jscf68kav", - "zeta1e9fyaulgntkrnqnl0es4nyxghp3petpn2ntu3t", - } - network.SetupZetaGenesisState(s.T(), s.cfg.GenesisState, s.cfg.Codec, observerList, false) - s.ballots = RandomBallotGenerator(s.T(), 20, observerList) - network.AddObserverData(s.T(), 2, s.cfg.GenesisState, s.cfg.Codec, s.ballots) - - net, err := network.New(s.T(), app.NodeDir, s.cfg) - s.Assert().NoError(err) - s.network = net - _, err = s.network.WaitForHeight(1) - s.Require().NoError(err) - -} - -func CreateRandomVoteList(t *testing.T, numberOfVotes int) []observerTypes.VoteType { - voteOptions := []observerTypes.VoteType{observerTypes.VoteType_SuccessObservation, observerTypes.VoteType_FailureObservation, observerTypes.VoteType_NotYetVoted} - minVoterOptions := 0 - maxBoterOptions := len(voteOptions) - 1 - - randomVoteOptions, err := rand.Int(rand.Reader, big.NewInt(int64(maxBoterOptions-minVoterOptions))) - if err != nil { - t.Fatal(err) - } - - voteList := make([]observerTypes.VoteType, numberOfVotes) - for i := 0; i < numberOfVotes; i++ { - voteList[i] = voteOptions[randomVoteOptions.Int64()] - } - return voteList -} -func RandomBallotGenerator(t *testing.T, numberOfBallots int, voterList []string) []*observerTypes.Ballot { - ballots := make([]*observerTypes.Ballot, numberOfBallots) - ballotStatus := []observerTypes.BallotStatus{observerTypes.BallotStatus_BallotFinalized_FailureObservation, observerTypes.BallotStatus_BallotFinalized_SuccessObservation} - minBallotStatus := 0 - maxBallotStatus := len(ballotStatus) - 1 - - randomBallotStatus, err := rand.Int(rand.Reader, big.NewInt(int64(maxBallotStatus-minBallotStatus))) - if err != nil { - t.Fatal(err) - } - - for i := 0; i < numberOfBallots; i++ { - ballots[i] = &observerTypes.Ballot{ - Index: "", - BallotIdentifier: "TestBallot" + strconv.Itoa(i), - VoterList: voterList, - Votes: CreateRandomVoteList(t, len(voterList)), - ObservationType: observerTypes.ObservationType_InBoundTx, - BallotThreshold: sdk.MustNewDecFromStr("0.66"), - BallotStatus: ballotStatus[randomBallotStatus.Int64()], - BallotCreationHeight: 0, - } - } - return ballots -} - -func (s *CliTestSuite) TearDownSuite() { - s.T().Log("tearing down genesis test suite") - s.network.Cleanup() -} diff --git a/x/emissions/keeper/block_rewards_components.go b/x/emissions/keeper/block_rewards_components.go index e1eadf66ad..fc105842aa 100644 --- a/x/emissions/keeper/block_rewards_components.go +++ b/x/emissions/keeper/block_rewards_components.go @@ -16,6 +16,7 @@ func (k Keeper) GetBlockRewardComponents(ctx sdk.Context) (sdk.Dec, sdk.Dec, sdk durationFactor := k.GetDurationFactor(ctx) return reservesFactor, bondFactor, durationFactor } + func (k Keeper) GetBondFactor(ctx sdk.Context, stakingKeeper types.StakingKeeper) sdk.Dec { targetBondRatio := sdk.MustNewDecFromStr(k.GetParamsIfExists(ctx).TargetBondRatio) maxBondFactor := sdk.MustNewDecFromStr(k.GetParamsIfExists(ctx).MaxBondFactor) diff --git a/x/emissions/keeper/block_rewards_components_test.go b/x/emissions/keeper/block_rewards_components_test.go index 29acc7b07b..a203757a4f 100644 --- a/x/emissions/keeper/block_rewards_components_test.go +++ b/x/emissions/keeper/block_rewards_components_test.go @@ -3,9 +3,14 @@ package keeper_test import ( "testing" + "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/cmd/zetacored/config" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" emissionskeeper "github.com/zeta-chain/zetacore/x/emissions/keeper" + emissionstypes "github.com/zeta-chain/zetacore/x/emissions/types" ) func TestKeeper_CalculateFixedValidatorRewards(t *testing.T) { @@ -13,7 +18,13 @@ func TestKeeper_CalculateFixedValidatorRewards(t *testing.T) { name string blockTimeInSecs string expectedBlockRewards sdk.Dec + wantErr bool }{ + { + name: "Invalid block time", + blockTimeInSecs: "", + wantErr: true, + }, { name: "Block Time 5.7", blockTimeInSecs: "5.7", @@ -43,8 +54,134 @@ func TestKeeper_CalculateFixedValidatorRewards(t *testing.T) { for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { blockRewards, err := emissionskeeper.CalculateFixedValidatorRewards(tc.blockTimeInSecs) + if tc.wantErr { + require.Error(t, err) + return + } require.NoError(t, err) require.Equal(t, tc.expectedBlockRewards, blockRewards) }) } } + +func TestKeeper_GetFixedBlockRewards(t *testing.T) { + k, _, _, _ := keepertest.EmissionsKeeper(t) + fixedBlockRewards, err := k.GetFixedBlockRewards() + require.NoError(t, err) + require.Equal(t, emissionstypes.BlockReward, fixedBlockRewards) +} + +func TestKeeper_GetBlockRewardComponent(t *testing.T) { + t.Run("should return all 0s if reserves factor is 0", func(t *testing.T) { + k, ctx, _, _ := keepertest.EmissionKeeperWithMockOptions(t, keepertest.EmissionMockOptions{ + UseBankMock: true, + }) + + bankMock := keepertest.GetEmissionsBankMock(t, k) + bankMock.On("GetBalance", + ctx, mock.Anything, config.BaseDenom). + Return(sdk.NewCoin(config.BaseDenom, math.NewInt(0)), nil).Once() + + reservesFactor, bondFactor, durationFactor := k.GetBlockRewardComponents(ctx) + require.Equal(t, sdk.ZeroDec(), reservesFactor) + require.Equal(t, sdk.ZeroDec(), bondFactor) + require.Equal(t, sdk.ZeroDec(), durationFactor) + }) + + t.Run("should return if reserves factor is not 0", func(t *testing.T) { + k, ctx, _, _ := keepertest.EmissionKeeperWithMockOptions(t, keepertest.EmissionMockOptions{ + UseBankMock: true, + }) + + bankMock := keepertest.GetEmissionsBankMock(t, k) + bankMock.On("GetBalance", + ctx, mock.Anything, config.BaseDenom). + Return(sdk.NewCoin(config.BaseDenom, math.NewInt(1)), nil).Once() + + reservesFactor, bondFactor, durationFactor := k.GetBlockRewardComponents(ctx) + require.Equal(t, sdk.OneDec(), reservesFactor) + // bonded ratio is 0 + require.Equal(t, sdk.ZeroDec(), bondFactor) + // non 0 value returned + require.NotEqual(t, sdk.ZeroDec(), durationFactor) + require.Positive(t, durationFactor.BigInt().Int64()) + }) +} + +func TestKeeper_GetBondFactor(t *testing.T) { + t.Run("should return 0 if current bond ratio is 0", func(t *testing.T) { + k, ctx, _, _ := keepertest.EmissionsKeeper(t) + + bondFactor := k.GetBondFactor(ctx, k.GetStakingKeeper()) + require.Equal(t, sdk.ZeroDec(), bondFactor) + }) + + t.Run("should return max bond factor if bond factor exceeds max bond factor", func(t *testing.T) { + k, ctx, _, _ := keepertest.EmissionKeeperWithMockOptions(t, keepertest.EmissionMockOptions{ + UseStakingMock: true, + }) + + params := emissionstypes.DefaultParams() + params.TargetBondRatio = "0.5" + params.MaxBondFactor = "1.1" + params.MinBondFactor = "0.9" + k.SetParams(ctx, params) + + stakingMock := keepertest.GetEmissionsStakingMock(t, k) + stakingMock.On("BondedRatio", ctx).Return(sdk.MustNewDecFromStr("0.25")) + bondFactor := k.GetBondFactor(ctx, k.GetStakingKeeper()) + require.Equal(t, sdk.MustNewDecFromStr(params.MaxBondFactor), bondFactor) + }) + + t.Run("should return min bond factor if bond factor below min bond factor", func(t *testing.T) { + k, ctx, _, _ := keepertest.EmissionKeeperWithMockOptions(t, keepertest.EmissionMockOptions{ + UseStakingMock: true, + }) + + params := emissionstypes.DefaultParams() + params.TargetBondRatio = "0.5" + params.MaxBondFactor = "1.1" + params.MinBondFactor = "0.9" + k.SetParams(ctx, params) + + stakingMock := keepertest.GetEmissionsStakingMock(t, k) + stakingMock.On("BondedRatio", ctx).Return(sdk.MustNewDecFromStr("0.75")) + bondFactor := k.GetBondFactor(ctx, k.GetStakingKeeper()) + require.Equal(t, sdk.MustNewDecFromStr(params.MinBondFactor), bondFactor) + }) + + t.Run("should return calculated bond factor if bond factor in range", func(t *testing.T) { + k, ctx, _, _ := keepertest.EmissionKeeperWithMockOptions(t, keepertest.EmissionMockOptions{ + UseStakingMock: true, + }) + + params := emissionstypes.DefaultParams() + params.TargetBondRatio = "0.5" + params.MaxBondFactor = "1.1" + params.MinBondFactor = "0.9" + k.SetParams(ctx, params) + + stakingMock := keepertest.GetEmissionsStakingMock(t, k) + stakingMock.On("BondedRatio", ctx).Return(sdk.MustNewDecFromStr("0.5")) + bondFactor := k.GetBondFactor(ctx, k.GetStakingKeeper()) + require.Equal(t, sdk.OneDec(), bondFactor) + }) +} + +func TestKeeper_GetDurationFactor(t *testing.T) { + t.Run("should return duration factor 0 if duration factor constant is 0", func(t *testing.T) { + k, ctx, _, _ := keepertest.EmissionsKeeper(t) + params := emissionstypes.DefaultParams() + params.DurationFactorConstant = "0" + k.SetParams(ctx, params) + duractionFactor := k.GetDurationFactor(ctx) + require.Equal(t, sdk.ZeroDec(), duractionFactor) + }) + + t.Run("should return duration factor for default params", func(t *testing.T) { + k, ctx, _, _ := keepertest.EmissionsKeeper(t) + duractionFactor := k.GetDurationFactor(ctx) + // hardcoding actual expected value for default params, it will change if logic changes + require.Equal(t, sdk.MustNewDecFromStr("0.000000004346937374"), duractionFactor) + }) +} diff --git a/x/emissions/keeper/grpc_query_get_emmisons_factors_test.go b/x/emissions/keeper/grpc_query_get_emmisons_factors_test.go new file mode 100644 index 0000000000..f9bd288634 --- /dev/null +++ b/x/emissions/keeper/grpc_query_get_emmisons_factors_test.go @@ -0,0 +1,26 @@ +package keeper_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/x/emissions/types" +) + +func TestKeeper_GetEmissionsFactors(t *testing.T) { + k, ctx, _, _ := keepertest.EmissionsKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.GetEmissionsFactors(wctx, nil) + require.NoError(t, err) + + reservesFactor, bondFactor, durationFactor := k.GetBlockRewardComponents(ctx) + expectedRes := &types.QueryGetEmissionsFactorsResponse{ + ReservesFactor: reservesFactor.String(), + BondFactor: bondFactor.String(), + DurationFactor: durationFactor.String(), + } + require.Equal(t, expectedRes, res) +} diff --git a/x/emissions/keeper/grpc_query_list_balances.go b/x/emissions/keeper/grpc_query_list_balances.go index 1b9e9864a6..589de64573 100644 --- a/x/emissions/keeper/grpc_query_list_balances.go +++ b/x/emissions/keeper/grpc_query_list_balances.go @@ -12,6 +12,7 @@ func (k Keeper) ListPoolAddresses(_ context.Context, req *types.QueryListPoolAdd if req == nil { return nil, status.Error(codes.InvalidArgument, "invalid request") } + return &types.QueryListPoolAddressesResponse{UndistributedObserverBalancesAddress: types.UndistributedObserverRewardsPoolAddress.String(), EmissionModuleAddress: types.EmissionsModuleAddress.String(), UndistributedTssBalancesAddress: types.UndistributedTssRewardsPoolAddress.String()}, nil diff --git a/x/emissions/keeper/grpc_query_list_balances_test.go b/x/emissions/keeper/grpc_query_list_balances_test.go new file mode 100644 index 0000000000..40b24af023 --- /dev/null +++ b/x/emissions/keeper/grpc_query_list_balances_test.go @@ -0,0 +1,35 @@ +package keeper_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/x/emissions/types" +) + +func TestKeeper_ListPoolAddresses(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.EmissionsKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.ListPoolAddresses(wctx, nil) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should not error if req is not nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.EmissionsKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + expectedRes := &types.QueryListPoolAddressesResponse{ + UndistributedObserverBalancesAddress: types.UndistributedObserverRewardsPoolAddress.String(), + EmissionModuleAddress: types.EmissionsModuleAddress.String(), + UndistributedTssBalancesAddress: types.UndistributedTssRewardsPoolAddress.String(), + } + res, err := k.ListPoolAddresses(wctx, &types.QueryListPoolAddressesRequest{}) + require.NoError(t, err) + require.Equal(t, expectedRes, res) + }) +} diff --git a/x/emissions/keeper/grpc_query_show_available_emissions.go b/x/emissions/keeper/grpc_query_show_available_emissions.go index 4f33bf5f07..50b9d14dae 100644 --- a/x/emissions/keeper/grpc_query_show_available_emissions.go +++ b/x/emissions/keeper/grpc_query_show_available_emissions.go @@ -5,11 +5,17 @@ import ( "github.com/zeta-chain/zetacore/cmd/zetacored/config" "github.com/zeta-chain/zetacore/x/emissions/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" sdk "github.com/cosmos/cosmos-sdk/types" ) func (k Keeper) ShowAvailableEmissions(goCtx context.Context, req *types.QueryShowAvailableEmissionsRequest) (*types.QueryShowAvailableEmissionsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + ctx := sdk.UnwrapSDKContext(goCtx) emissions, found := k.GetWithdrawableEmission(ctx, req.Address) if !found { diff --git a/x/emissions/keeper/grpc_query_show_available_emissions_test.go b/x/emissions/keeper/grpc_query_show_available_emissions_test.go new file mode 100644 index 0000000000..e5c900a61c --- /dev/null +++ b/x/emissions/keeper/grpc_query_show_available_emissions_test.go @@ -0,0 +1,56 @@ +package keeper_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/cmd/zetacored/config" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/emissions/types" +) + +func TestKeeper_ShowAvailableEmissions(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.EmissionsKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.ShowAvailableEmissions(wctx, nil) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should return 0 if emissions not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.EmissionsKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + req := &types.QueryShowAvailableEmissionsRequest{ + Address: sample.AccAddress(), + } + res, err := k.ShowAvailableEmissions(wctx, req) + require.NoError(t, err) + expectedRes := &types.QueryShowAvailableEmissionsResponse{ + Amount: sdk.NewCoin(config.BaseDenom, sdk.ZeroInt()).String(), + } + require.Equal(t, expectedRes, res) + }) + + t.Run("should return emissions if found", func(t *testing.T) { + k, ctx, _, _ := keepertest.EmissionsKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + emissions := sample.WithdrawableEmissions(t) + k.SetWithdrawableEmission(ctx, emissions) + + req := &types.QueryShowAvailableEmissionsRequest{ + Address: emissions.Address, + } + res, err := k.ShowAvailableEmissions(wctx, req) + require.NoError(t, err) + expectedRes := &types.QueryShowAvailableEmissionsResponse{ + Amount: sdk.NewCoin(config.BaseDenom, emissions.Amount).String(), + } + require.Equal(t, expectedRes, res) + }) +} diff --git a/x/emissions/types/params.go b/x/emissions/types/params.go index 96267b0a70..2a8b4e4387 100644 --- a/x/emissions/types/params.go +++ b/x/emissions/types/params.go @@ -50,7 +50,7 @@ func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { paramtypes.NewParamSetPair(KeyPrefix(ParamTargetBondRatio), &p.TargetBondRatio, validateTargetBondRatio), paramtypes.NewParamSetPair(KeyPrefix(ParamValidatorEmissionPercentage), &p.ValidatorEmissionPercentage, validateValidatorEmissionPercentage), paramtypes.NewParamSetPair(KeyPrefix(ParamObserverEmissionPercentage), &p.ObserverEmissionPercentage, validateObserverEmissionPercentage), - paramtypes.NewParamSetPair(KeyPrefix(ParamTssSignerEmissionPercentage), &p.TssSignerEmissionPercentage, validateTssEmissonPercentage), + paramtypes.NewParamSetPair(KeyPrefix(ParamTssSignerEmissionPercentage), &p.TssSignerEmissionPercentage, validateTssEmissionPercentage), paramtypes.NewParamSetPair(KeyPrefix(ParamDurationFactorConstant), &p.DurationFactorConstant, validateDurationFactorConstant), // TODO: enable this param @@ -165,7 +165,7 @@ func validateObserverEmissionPercentage(i interface{}) error { return nil } -func validateTssEmissonPercentage(i interface{}) error { +func validateTssEmissionPercentage(i interface{}) error { v, ok := i.(string) if !ok { return fmt.Errorf("invalid parameter type: %T", i) diff --git a/x/emissions/types/params_test.go b/x/emissions/types/params_test.go new file mode 100644 index 0000000000..8dcf6053a0 --- /dev/null +++ b/x/emissions/types/params_test.go @@ -0,0 +1,138 @@ +package types + +import ( + "reflect" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v2" +) + +func TestNewParams(t *testing.T) { + params := NewParams() + + // Verifying all parameters to ensure they are set correctly. + require.Equal(t, "1.25", params.MaxBondFactor, "MaxBondFactor should be set to 1.25") + require.Equal(t, "0.75", params.MinBondFactor, "MinBondFactor should be set to 0.75") + require.Equal(t, "6.00", params.AvgBlockTime, "AvgBlockTime should be set to 6.00") + require.Equal(t, "00.67", params.TargetBondRatio, "TargetBondRatio should be set to 00.67") + require.Equal(t, "00.50", params.ValidatorEmissionPercentage, "ValidatorEmissionPercentage should be set to 00.50") + require.Equal(t, "00.25", params.ObserverEmissionPercentage, "ObserverEmissionPercentage should be set to 00.25") + require.Equal(t, "00.25", params.TssSignerEmissionPercentage, "TssSignerEmissionPercentage should be set to 00.25") + require.Equal(t, "0.001877876953694702", params.DurationFactorConstant, "DurationFactorConstant should be set to 0.001877876953694702") + + require.Equal(t, sdk.Int{}, params.ObserverSlashAmount, "ObserverSlashAmount should be initialized but is currently disabled") +} + +func TestParamKeyTable(t *testing.T) { + kt := ParamKeyTable() + + ps := Params{} + for _, psp := range ps.ParamSetPairs() { + require.PanicsWithValue(t, "duplicate parameter key", func() { + kt.RegisterType(psp) + }) + } +} + +func TestDefaultParams(t *testing.T) { + params := DefaultParams() + + // The default parameters should match those set in NewParams + require.Equal(t, NewParams(), params) +} + +func TestParamSetPairs(t *testing.T) { + params := DefaultParams() + pairs := params.ParamSetPairs() + + require.Equal(t, 8, len(pairs), "The number of param set pairs should match the expected count") + + assertParamSetPair(t, pairs, KeyPrefix(ParamMaxBondFactor), "1.25", validateMaxBondFactor) + assertParamSetPair(t, pairs, KeyPrefix(ParamMinBondFactor), "0.75", validateMinBondFactor) + assertParamSetPair(t, pairs, KeyPrefix(ParamAvgBlockTime), "6.00", validateAvgBlockTime) + assertParamSetPair(t, pairs, KeyPrefix(ParamTargetBondRatio), "00.67", validateTargetBondRatio) + assertParamSetPair(t, pairs, KeyPrefix(ParamValidatorEmissionPercentage), "00.50", validateValidatorEmissionPercentage) + assertParamSetPair(t, pairs, KeyPrefix(ParamObserverEmissionPercentage), "00.25", validateObserverEmissionPercentage) + assertParamSetPair(t, pairs, KeyPrefix(ParamTssSignerEmissionPercentage), "00.25", validateTssEmissionPercentage) + assertParamSetPair(t, pairs, KeyPrefix(ParamDurationFactorConstant), "0.001877876953694702", validateDurationFactorConstant) +} + +func assertParamSetPair(t *testing.T, pairs paramtypes.ParamSetPairs, key []byte, expectedValue string, valFunc paramtypes.ValueValidatorFn) { + for _, pair := range pairs { + if string(pair.Key) == string(key) { + actualValue, ok := pair.Value.(*string) + require.True(t, ok, "Expected value to be of type *string for key %s", string(key)) + require.Equal(t, expectedValue, *actualValue, "Value does not match for key %s", string(key)) + + actualValFunc := pair.ValidatorFn + require.Equal(t, reflect.ValueOf(valFunc).Pointer(), reflect.ValueOf(actualValFunc).Pointer(), "Val func doesnt match for key %s", string(key)) + return + } + } + + t.Errorf("Key %s not found in ParamSetPairs", string(key)) +} + +func TestValidateDurationFactorConstant(t *testing.T) { + require.NoError(t, validateDurationFactorConstant("1")) + require.Error(t, validateDurationFactorConstant(1)) +} + +func TestValidateMaxBondFactor(t *testing.T) { + require.Error(t, validateMaxBondFactor(1)) + require.NoError(t, validateMaxBondFactor("1.00")) + require.NoError(t, validateMaxBondFactor("1.25")) + require.Error(t, validateMaxBondFactor("1.30")) // Should fail as it's higher than 1.25 +} + +func TestValidateMinBondFactor(t *testing.T) { + require.Error(t, validateMinBondFactor(1)) + require.NoError(t, validateMinBondFactor("0.75")) + require.Error(t, validateMinBondFactor("0.50")) // Should fail as it's lower than 0.75 +} + +func TestValidateAvgBlockTime(t *testing.T) { + require.Error(t, validateAvgBlockTime(6)) + require.Error(t, validateAvgBlockTime("invalid")) + require.NoError(t, validateAvgBlockTime("6.00")) + require.Error(t, validateAvgBlockTime("-1")) // Negative time should fail + require.Error(t, validateAvgBlockTime("0")) // Zero should also fail +} + +func TestValidateTargetBondRatio(t *testing.T) { + require.Error(t, validateTargetBondRatio(0.5)) + require.NoError(t, validateTargetBondRatio("0.50")) + require.Error(t, validateTargetBondRatio("-0.01")) // Less than 0 percent should fail + require.Error(t, validateTargetBondRatio("1.01")) // More than 100 percent should fail +} + +func TestValidateValidatorEmissionPercentage(t *testing.T) { + require.Error(t, validateValidatorEmissionPercentage(0.5)) + require.NoError(t, validateValidatorEmissionPercentage("0.50")) + require.Error(t, validateValidatorEmissionPercentage("-0.50")) // Less than 0 percent should fail + require.Error(t, validateValidatorEmissionPercentage("1.01")) // More than 100 percent should fail +} + +func TestValidateObserverEmissionPercentage(t *testing.T) { + require.Error(t, validateObserverEmissionPercentage(0.25)) + require.NoError(t, validateObserverEmissionPercentage("0.25")) + require.Error(t, validateObserverEmissionPercentage("-0.50")) // Less than 0 percent should fail + require.Error(t, validateObserverEmissionPercentage("1.01")) // More than 100 percent should fail +} + +func TestValidateTssEmissionPercentage(t *testing.T) { + require.Error(t, validateTssEmissionPercentage(0.25)) + require.NoError(t, validateTssEmissionPercentage("0.25")) + require.Error(t, validateTssEmissionPercentage("-0.25")) // Less than 0 percent should fail + require.Error(t, validateTssEmissionPercentage("1.01")) // More than 100 percent should fail +} + +func TestParamsString(t *testing.T) { + params := DefaultParams() + out, err := yaml.Marshal(params) + require.NoError(t, err) + require.Equal(t, string(out), params.String()) +} diff --git a/x/observer/client/cli/tx.go b/x/observer/client/cli/tx.go index f581f878b9..9d65f09b70 100644 --- a/x/observer/client/cli/tx.go +++ b/x/observer/client/cli/tx.go @@ -30,6 +30,7 @@ func GetTxCmd() *cobra.Command { CmdUpdateObserver(), CmdEncode(), CmdResetChainNonces(), + CmdVoteTSS(), ) return cmd diff --git a/x/observer/client/cli/tx_vote_tss.go b/x/observer/client/cli/tx_vote_tss.go new file mode 100644 index 0000000000..2c2b837513 --- /dev/null +++ b/x/observer/client/cli/tx_vote_tss.go @@ -0,0 +1,51 @@ +package cli + +import ( + "strconv" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/spf13/cast" + "github.com/spf13/cobra" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/x/observer/types" +) + +func CmdVoteTSS() *cobra.Command { + cmd := &cobra.Command{ + Use: "vote-tss [pubkey] [keygen-block] [status]", + Short: "Vote for a new TSS creation", + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + + argsPubkey, err := cast.ToStringE(args[0]) + if err != nil { + return err + } + + keygenBlock, err := strconv.ParseInt(args[1], 10, 64) + if err != nil { + return err + } + + status, err := chains.ReceiveStatusFromString(args[2]) + if err != nil { + return err + } + + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + msg := types.NewMsgVoteTSS(clientCtx.GetFromAddress().String(), argsPubkey, keygenBlock, status) + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/observer/keeper/msg_server_remove_chain_params_test.go b/x/observer/keeper/msg_server_remove_chain_params_test.go index cdfc56b0c5..21b68f7f8e 100644 --- a/x/observer/keeper/msg_server_remove_chain_params_test.go +++ b/x/observer/keeper/msg_server_remove_chain_params_test.go @@ -39,9 +39,8 @@ func TestMsgServer_RemoveChainParams(t *testing.T) { }, }) - keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) - // remove chain params + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) _, err := srv.RemoveChainParams(sdk.WrapSDKContext(ctx), &types.MsgRemoveChainParams{ Creator: admin, ChainId: chain2, @@ -56,7 +55,6 @@ func TestMsgServer_RemoveChainParams(t *testing.T) { require.Equal(t, chain3, chainParamsList.ChainParams[1].ChainId) keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) - // remove chain params _, err = srv.RemoveChainParams(sdk.WrapSDKContext(ctx), &types.MsgRemoveChainParams{ Creator: admin, diff --git a/x/observer/keeper/msg_server_vote_tss.go b/x/observer/keeper/msg_server_vote_tss.go new file mode 100644 index 0000000000..87c7e0b31f --- /dev/null +++ b/x/observer/keeper/msg_server_vote_tss.go @@ -0,0 +1,127 @@ +package keeper + +import ( + "context" + "fmt" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/ethereum/go-ethereum/common/math" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/x/observer/types" +) + +// VoteTSS votes on creating a TSS key and recording the information about it (public +// key, participant and operator addresses, finalized and keygen heights). +// +// If the vote passes, the information about the TSS key is recorded on chain +// and the status of the keygen is set to "success". +// +// Fails if the keygen does not exist, the keygen has been already +// completed, or the keygen has failed. +// +// Only node accounts are authorized to broadcast this message. +func (k msgServer) VoteTSS(goCtx context.Context, msg *types.MsgVoteTSS) (*types.MsgVoteTSSResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // checks whether a signer is authorized to sign , by checking their address against the observer mapper which contains the observer list for the chain and type + _, found := k.GetNodeAccount(ctx, msg.Creator) + if !found { + return nil, errorsmod.Wrap(sdkerrors.ErrorInvalidSigner, fmt.Sprintf("signer %s does not have a node account set", msg.Creator)) + } + // no need to create a ballot if keygen does not exist + keygen, found := k.GetKeygen(ctx) + if !found { + return &types.MsgVoteTSSResponse{}, types.ErrKeygenNotFound + } + // use a separate transaction to update KEYGEN status to pending when trying to change the TSS address + if keygen.Status == types.KeygenStatus_KeyGenSuccess { + return &types.MsgVoteTSSResponse{}, types.ErrKeygenCompleted + } + + // add votes and set Ballot + // GetBallot checks against the supported chains list before querying for Ballot + // TODO : https://github.com/zeta-chain/node/issues/896 + ballotCreated := false + index := msg.Digest() + ballot, found := k.GetBallot(ctx, index) + if !found { + + // if ballot does not exist, create a new ballot + var voterList []string + + for _, nodeAccount := range k.GetAllNodeAccount(ctx) { + voterList = append(voterList, nodeAccount.Operator) + } + ballot = types.Ballot{ + Index: "", + BallotIdentifier: index, + VoterList: voterList, + Votes: types.CreateVotes(len(voterList)), + ObservationType: types.ObservationType_TSSKeyGen, + BallotThreshold: sdk.MustNewDecFromStr("1.00"), + BallotStatus: types.BallotStatus_BallotInProgress, + BallotCreationHeight: ctx.BlockHeight(), + } + k.AddBallotToList(ctx, ballot) + + EmitEventBallotCreated(ctx, ballot, msg.TssPubkey, "Common-TSS-For-All-Chain") + ballotCreated = true + } + + // vote the ballot + var err error + vote := types.VoteType_SuccessObservation + if msg.Status == chains.ReceiveStatus_Failed { + vote = types.VoteType_FailureObservation + } + ballot, err = k.AddVoteToBallot(ctx, ballot, msg.Creator, vote) + if err != nil { + return &types.MsgVoteTSSResponse{}, err + } + + // returns here if the ballot is not finalized + ballot, isFinalized := k.CheckIfFinalizingVote(ctx, ballot) + if !isFinalized { + return &types.MsgVoteTSSResponse{ + VoteFinalized: false, + BallotCreated: ballotCreated, + KeygenSuccess: false, + }, nil + } + + // set TSS only on success, set Keygen either way. + // keygen block can be updated using a policy transaction if keygen fails + keygenSuccess := false + if ballot.BallotStatus == types.BallotStatus_BallotFinalized_FailureObservation { + keygen.Status = types.KeygenStatus_KeyGenFailed + keygen.BlockNumber = math.MaxInt64 + } else { + tss := types.TSS{ + TssPubkey: msg.TssPubkey, + TssParticipantList: keygen.GetGranteePubkeys(), + OperatorAddressList: ballot.VoterList, + FinalizedZetaHeight: ctx.BlockHeight(), + KeyGenZetaHeight: msg.KeygenZetaHeight, + } + // set TSS history only, current TSS is updated via admin transaction + // in Case this is the first TSS address update both current and history + tssList := k.GetAllTSS(ctx) + if len(tssList) == 0 { + k.SetTssAndUpdateNonce(ctx, tss) + } + k.SetTSSHistory(ctx, tss) + keygen.Status = types.KeygenStatus_KeyGenSuccess + keygen.BlockNumber = ctx.BlockHeight() + keygenSuccess = true + } + + k.SetKeygen(ctx, keygen) + + return &types.MsgVoteTSSResponse{ + VoteFinalized: true, + BallotCreated: ballotCreated, + KeygenSuccess: keygenSuccess, + }, nil +} diff --git a/x/observer/keeper/msg_server_vote_tss_test.go b/x/observer/keeper/msg_server_vote_tss_test.go new file mode 100644 index 0000000000..f7ef851bf9 --- /dev/null +++ b/x/observer/keeper/msg_server_vote_tss_test.go @@ -0,0 +1,241 @@ +package keeper_test + +import ( + "math" + "testing" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/observer/keeper" + "github.com/zeta-chain/zetacore/x/observer/types" +) + +func TestMsgServer_VoteTSS(t *testing.T) { + t.Run("fail if node account not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + srv := keeper.NewMsgServerImpl(*k) + + _, err := srv.VoteTSS(ctx, &types.MsgVoteTSS{ + Creator: sample.AccAddress(), + TssPubkey: sample.Tss().TssPubkey, + KeygenZetaHeight: 42, + Status: chains.ReceiveStatus_Success, + }) + require.ErrorIs(t, err, sdkerrors.ErrorInvalidSigner) + }) + + t.Run("fail if keygen is not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + srv := keeper.NewMsgServerImpl(*k) + + // setup state + nodeAcc := sample.NodeAccount() + k.SetNodeAccount(ctx, *nodeAcc) + + _, err := srv.VoteTSS(ctx, &types.MsgVoteTSS{ + Creator: nodeAcc.Operator, + TssPubkey: sample.Tss().TssPubkey, + KeygenZetaHeight: 42, + Status: chains.ReceiveStatus_Success, + }) + require.ErrorIs(t, err, types.ErrKeygenNotFound) + }) + + t.Run("fail if keygen already completed ", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + srv := keeper.NewMsgServerImpl(*k) + + // setup state + nodeAcc := sample.NodeAccount() + keygen := sample.Keygen(t) + keygen.Status = types.KeygenStatus_KeyGenSuccess + k.SetNodeAccount(ctx, *nodeAcc) + k.SetKeygen(ctx, *keygen) + + _, err := srv.VoteTSS(ctx, &types.MsgVoteTSS{ + Creator: nodeAcc.Operator, + TssPubkey: sample.Tss().TssPubkey, + KeygenZetaHeight: 42, + Status: chains.ReceiveStatus_Success, + }) + require.ErrorIs(t, err, types.ErrKeygenCompleted) + }) + + t.Run("can create a new ballot, vote success and finalize", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + ctx = ctx.WithBlockHeight(42) + srv := keeper.NewMsgServerImpl(*k) + + // setup state + nodeAcc := sample.NodeAccount() + keygen := sample.Keygen(t) + keygen.Status = types.KeygenStatus_PendingKeygen + k.SetNodeAccount(ctx, *nodeAcc) + k.SetKeygen(ctx, *keygen) + + // there is a single node account, so the ballot will be created and finalized in a single vote + res, err := srv.VoteTSS(ctx, &types.MsgVoteTSS{ + Creator: nodeAcc.Operator, + TssPubkey: sample.Tss().TssPubkey, + KeygenZetaHeight: 42, + Status: chains.ReceiveStatus_Success, + }) + require.NoError(t, err) + + // check response + require.True(t, res.BallotCreated) + require.True(t, res.VoteFinalized) + require.True(t, res.KeygenSuccess) + + // check keygen updated + newKeygen, found := k.GetKeygen(ctx) + require.True(t, found) + require.EqualValues(t, types.KeygenStatus_KeyGenSuccess, newKeygen.Status) + require.EqualValues(t, ctx.BlockHeight(), newKeygen.BlockNumber) + }) + + t.Run("can create a new ballot, vote failure and finalize", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + ctx = ctx.WithBlockHeight(42) + srv := keeper.NewMsgServerImpl(*k) + + // setup state + nodeAcc := sample.NodeAccount() + keygen := sample.Keygen(t) + keygen.Status = types.KeygenStatus_PendingKeygen + k.SetNodeAccount(ctx, *nodeAcc) + k.SetKeygen(ctx, *keygen) + + // there is a single node account, so the ballot will be created and finalized in a single vote + res, err := srv.VoteTSS(ctx, &types.MsgVoteTSS{ + Creator: nodeAcc.Operator, + TssPubkey: sample.Tss().TssPubkey, + KeygenZetaHeight: 42, + Status: chains.ReceiveStatus_Failed, + }) + require.NoError(t, err) + + // check response + require.True(t, res.BallotCreated) + require.True(t, res.VoteFinalized) + require.False(t, res.KeygenSuccess) + + // check keygen updated + newKeygen, found := k.GetKeygen(ctx) + require.True(t, found) + require.EqualValues(t, types.KeygenStatus_KeyGenFailed, newKeygen.Status) + require.EqualValues(t, math.MaxInt64, newKeygen.BlockNumber) + }) + + t.Run("can create a new ballot, vote without finalizing, then add vote and finalizing", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + ctx = ctx.WithBlockHeight(42) + srv := keeper.NewMsgServerImpl(*k) + + // setup state with 3 node accounts + nodeAcc1 := sample.NodeAccount() + nodeAcc2 := sample.NodeAccount() + nodeAcc3 := sample.NodeAccount() + keygen := sample.Keygen(t) + keygen.Status = types.KeygenStatus_PendingKeygen + tss := sample.Tss() + k.SetNodeAccount(ctx, *nodeAcc1) + k.SetNodeAccount(ctx, *nodeAcc2) + k.SetNodeAccount(ctx, *nodeAcc3) + k.SetKeygen(ctx, *keygen) + + // 1st vote: created ballot, but not finalized + res, err := srv.VoteTSS(ctx, &types.MsgVoteTSS{ + Creator: nodeAcc1.Operator, + TssPubkey: tss.TssPubkey, + KeygenZetaHeight: 42, + Status: chains.ReceiveStatus_Success, + }) + require.NoError(t, err) + + // check response + require.True(t, res.BallotCreated) + require.False(t, res.VoteFinalized) + require.False(t, res.KeygenSuccess) + + // check keygen not updated + newKeygen, found := k.GetKeygen(ctx) + require.True(t, found) + require.EqualValues(t, types.KeygenStatus_PendingKeygen, newKeygen.Status) + + // 2nd vote: already created ballot, and not finalized + res, err = srv.VoteTSS(ctx, &types.MsgVoteTSS{ + Creator: nodeAcc2.Operator, + TssPubkey: tss.TssPubkey, + KeygenZetaHeight: 42, + Status: chains.ReceiveStatus_Success, + }) + require.NoError(t, err) + + // check response + require.False(t, res.BallotCreated) + require.False(t, res.VoteFinalized) + require.False(t, res.KeygenSuccess) + + // check keygen not updated + newKeygen, found = k.GetKeygen(ctx) + require.True(t, found) + require.EqualValues(t, types.KeygenStatus_PendingKeygen, newKeygen.Status) + + // 3rd vote: finalize the ballot + res, err = srv.VoteTSS(ctx, &types.MsgVoteTSS{ + Creator: nodeAcc3.Operator, + TssPubkey: tss.TssPubkey, + KeygenZetaHeight: 42, + Status: chains.ReceiveStatus_Success, + }) + require.NoError(t, err) + + // check response + require.False(t, res.BallotCreated) + require.True(t, res.VoteFinalized) + require.True(t, res.KeygenSuccess) + + // check keygen not updated + newKeygen, found = k.GetKeygen(ctx) + require.True(t, found) + require.EqualValues(t, types.KeygenStatus_KeyGenSuccess, newKeygen.Status) + require.EqualValues(t, ctx.BlockHeight(), newKeygen.BlockNumber) + }) + + t.Run("fail if voting fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + ctx = ctx.WithBlockHeight(42) + srv := keeper.NewMsgServerImpl(*k) + + // setup state with two node accounts to not finalize the ballot + nodeAcc := sample.NodeAccount() + keygen := sample.Keygen(t) + keygen.Status = types.KeygenStatus_PendingKeygen + k.SetNodeAccount(ctx, *nodeAcc) + k.SetNodeAccount(ctx, *sample.NodeAccount()) + k.SetKeygen(ctx, *keygen) + + // add a first vote + res, err := srv.VoteTSS(ctx, &types.MsgVoteTSS{ + Creator: nodeAcc.Operator, + TssPubkey: sample.Tss().TssPubkey, + KeygenZetaHeight: 42, + Status: chains.ReceiveStatus_Success, + }) + require.NoError(t, err) + require.False(t, res.VoteFinalized) + + // vote again: voting should fail + _, err = srv.VoteTSS(ctx, &types.MsgVoteTSS{ + Creator: nodeAcc.Operator, + TssPubkey: sample.Tss().TssPubkey, + KeygenZetaHeight: 42, + Status: chains.ReceiveStatus_Success, + }) + require.ErrorIs(t, err, types.ErrUnableToAddVote) + }) +} diff --git a/x/observer/keeper/tss_test.go b/x/observer/keeper/tss_test.go index 7574e05726..6408f4f49a 100644 --- a/x/observer/keeper/tss_test.go +++ b/x/observer/keeper/tss_test.go @@ -69,7 +69,7 @@ func TestTSSQuerySingle(t *testing.T) { } func TestTSSQueryHistory(t *testing.T) { - keeper, ctx, _, _ := keepertest.ObserverKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) wctx := sdk.WrapSDKContext(ctx) for _, tc := range []struct { desc string @@ -94,16 +94,16 @@ func TestTSSQueryHistory(t *testing.T) { t.Run(tc.desc, func(t *testing.T) { tssList := sample.TssList(tc.tssCount) for _, tss := range tssList { - keeper.SetTSS(ctx, tss) - keeper.SetTSSHistory(ctx, tss) + k.SetTSS(ctx, tss) + k.SetTSSHistory(ctx, tss) } request := &types.QueryTssHistoryRequest{} - response, err := keeper.TssHistory(wctx, request) + response, err := k.TssHistory(wctx, request) if tc.err != nil { require.ErrorIs(t, err, tc.err) } else { require.Equal(t, len(tssList), len(response.TssList)) - prevTss, found := keeper.GetPreviousTSS(ctx) + prevTss, found := k.GetPreviousTSS(ctx) require.Equal(t, tc.foundPrevious, found) if found { require.Equal(t, tssList[len(tssList)-2], prevTss) diff --git a/x/observer/keeper/utils.go b/x/observer/keeper/utils.go index 5209e3218d..322d45387f 100644 --- a/x/observer/keeper/utils.go +++ b/x/observer/keeper/utils.go @@ -13,9 +13,9 @@ func (k Keeper) AddVoteToBallot(ctx sdk.Context, ballot types.Ballot, address st if err != nil { return ballot, err } - ctx.Logger().Info(fmt.Sprintf("Vote Added | Voter :%s, ballot idetifier %s", address, ballot.BallotIdentifier)) + ctx.Logger().Info(fmt.Sprintf("Vote Added | Voter :%s, ballot identifier %s", address, ballot.BallotIdentifier)) k.SetBallot(ctx, &ballot) - return ballot, err + return ballot, nil } // CheckIfFinalizingVote checks if the ballot is finalized in this block and if it is, it sets the ballot in the store diff --git a/x/observer/types/ballot.go b/x/observer/types/ballot.go index 10f21119d1..811a1b7688 100644 --- a/x/observer/types/ballot.go +++ b/x/observer/types/ballot.go @@ -3,13 +3,13 @@ package types import ( "fmt" + cosmoserrors "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/errors" ) func (m Ballot) AddVote(address string, vote VoteType) (Ballot, error) { if m.HasVoted(address) { - return m, errors.Wrap(ErrUnableToAddVote, fmt.Sprintf(" Voter : %s | Ballot :%s | Already Voted", address, m.String())) + return m, cosmoserrors.Wrap(ErrUnableToAddVote, fmt.Sprintf(" Voter : %s | Ballot :%s | Already Voted", address, m.String())) } // `index` is the index of the `address` in the `VoterList` // `index` is used to set the vote in the `Votes` array diff --git a/x/observer/types/codec.go b/x/observer/types/codec.go index af6e7d87a5..c729bb3a84 100644 --- a/x/observer/types/codec.go +++ b/x/observer/types/codec.go @@ -11,12 +11,13 @@ func RegisterCodec(cdc *codec.LegacyAmino) { cdc.RegisterConcrete(&MsgAddObserver{}, "observer/AddObserver", nil) cdc.RegisterConcrete(&MsgUpdateChainParams{}, "observer/UpdateChainParams", nil) cdc.RegisterConcrete(&MsgRemoveChainParams{}, "observer/RemoveChainParams", nil) - cdc.RegisterConcrete(&MsgAddBlameVote{}, "crosschain/AddBlameVote", nil) - cdc.RegisterConcrete(&MsgUpdateCrosschainFlags{}, "crosschain/UpdateCrosschainFlags", nil) - cdc.RegisterConcrete(&MsgUpdateKeygen{}, "crosschain/UpdateKeygen", nil) - cdc.RegisterConcrete(&MsgAddBlockHeader{}, "crosschain/AddBlockHeader", nil) + cdc.RegisterConcrete(&MsgAddBlameVote{}, "observer/AddBlameVote", nil) + cdc.RegisterConcrete(&MsgUpdateCrosschainFlags{}, "observer/UpdateCrosschainFlags", nil) + cdc.RegisterConcrete(&MsgUpdateKeygen{}, "observer/UpdateKeygen", nil) + cdc.RegisterConcrete(&MsgAddBlockHeader{}, "observer/AddBlockHeader", nil) cdc.RegisterConcrete(&MsgUpdateObserver{}, "observer/UpdateObserver", nil) cdc.RegisterConcrete(&MsgResetChainNonces{}, "observer/ResetChainNonces", nil) + cdc.RegisterConcrete(&MsgVoteTSS{}, "observer/VoteTSS", nil) } func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { @@ -30,6 +31,7 @@ func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { &MsgAddBlockHeader{}, &MsgUpdateObserver{}, &MsgResetChainNonces{}, + &MsgVoteTSS{}, ) msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) diff --git a/x/observer/types/message_vote_tss.go b/x/observer/types/message_vote_tss.go new file mode 100644 index 0000000000..bbd8a73e65 --- /dev/null +++ b/x/observer/types/message_vote_tss.go @@ -0,0 +1,63 @@ +package types + +import ( + "fmt" + + cosmoserrors "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/zeta-chain/zetacore/pkg/chains" +) + +const TypeMsgVoteTSS = "VoteTSS" + +var _ sdk.Msg = &MsgVoteTSS{} + +func NewMsgVoteTSS(creator string, pubkey string, keygenZetaHeight int64, status chains.ReceiveStatus) *MsgVoteTSS { + return &MsgVoteTSS{ + Creator: creator, + TssPubkey: pubkey, + KeygenZetaHeight: keygenZetaHeight, + Status: status, + } +} + +func (msg *MsgVoteTSS) Route() string { + return RouterKey +} + +func (msg *MsgVoteTSS) Type() string { + return TypeMsgVoteTSS +} + +func (msg *MsgVoteTSS) GetSigners() []sdk.AccAddress { + creator, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + panic(err) + } + return []sdk.AccAddress{creator} +} + +func (msg *MsgVoteTSS) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +func (msg *MsgVoteTSS) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + return cosmoserrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err) + } + + // either success or observation failure + if msg.Status != chains.ReceiveStatus_Success && msg.Status != chains.ReceiveStatus_Failed { + return cosmoserrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid status: %s", msg.Status) + } + + return nil +} + +func (msg *MsgVoteTSS) Digest() string { + // We support only 1 keygen at a particular height + return fmt.Sprintf("%d-%s", msg.KeygenZetaHeight, "tss-keygen") +} diff --git a/x/observer/types/message_vote_tss_test.go b/x/observer/types/message_vote_tss_test.go new file mode 100644 index 0000000000..fb8d92323b --- /dev/null +++ b/x/observer/types/message_vote_tss_test.go @@ -0,0 +1,105 @@ +package types_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/observer/types" +) + +func TestMsgVoteTSS_ValidateBasic(t *testing.T) { + tests := []struct { + name string + msg *types.MsgVoteTSS + err error + }{ + { + name: "valid message", + msg: types.NewMsgVoteTSS(sample.AccAddress(), "pubkey", 1, chains.ReceiveStatus_Success), + }, + { + name: "valid message with receive status failed", + msg: types.NewMsgVoteTSS(sample.AccAddress(), "pubkey", 1, chains.ReceiveStatus_Failed), + }, + { + name: "invalid creator address", + msg: types.NewMsgVoteTSS("invalid", "pubkey", 1, chains.ReceiveStatus_Success), + err: sdkerrors.ErrInvalidAddress, + }, + { + name: "invalid observation status", + msg: types.NewMsgVoteTSS(sample.AccAddress(), "pubkey", 1, chains.ReceiveStatus_Created), + err: sdkerrors.ErrInvalidRequest, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.msg.ValidateBasic() + if tt.err != nil { + require.ErrorIs(t, err, tt.err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestMsgVoteTSS_GetSigners(t *testing.T) { + signer := sample.AccAddress() + tests := []struct { + name string + msg *types.MsgVoteTSS + panics bool + }{ + { + name: "valid signer", + msg: types.NewMsgVoteTSS(signer, "pubkey", 1, chains.ReceiveStatus_Success), + panics: false, + }, + { + name: "invalid signer", + msg: types.NewMsgVoteTSS("invalid", "pubkey", 1, chains.ReceiveStatus_Success), + panics: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if !tt.panics { + signers := tt.msg.GetSigners() + require.Equal(t, []sdk.AccAddress{sdk.MustAccAddressFromBech32(signer)}, signers) + } else { + require.Panics(t, func() { + tt.msg.GetSigners() + }) + } + }) + } +} + +func TestMsgVoteTSS_Type(t *testing.T) { + msg := types.NewMsgVoteTSS(sample.AccAddress(), "pubkey", 1, chains.ReceiveStatus_Success) + require.Equal(t, types.TypeMsgVoteTSS, msg.Type()) +} + +func TestMsgVoteTSS_Route(t *testing.T) { + msg := types.NewMsgVoteTSS(sample.AccAddress(), "pubkey", 1, chains.ReceiveStatus_Success) + require.Equal(t, types.RouterKey, msg.Route()) +} + +func TestMsgVoteTSS_GetSignBytes(t *testing.T) { + msg := types.NewMsgVoteTSS(sample.AccAddress(), "pubkey", 1, chains.ReceiveStatus_Success) + require.NotPanics(t, func() { + msg.GetSignBytes() + }) +} + +func TestMsgVoteTSS_Digest(t *testing.T) { + msg := types.NewMsgVoteTSS(sample.AccAddress(), "pubkey", 1, chains.ReceiveStatus_Success) + require.Equal(t, "1-tss-keygen", msg.Digest()) +} diff --git a/x/observer/types/tx.pb.go b/x/observer/types/tx.pb.go index fef0e1ec4a..3c423aba9c 100644 --- a/x/observer/types/tx.pb.go +++ b/x/observer/types/tx.pb.go @@ -13,6 +13,7 @@ import ( _ "github.com/cosmos/gogoproto/gogoproto" grpc1 "github.com/gogo/protobuf/grpc" proto "github.com/gogo/protobuf/proto" + chains "github.com/zeta-chain/zetacore/pkg/chains" proofs "github.com/zeta-chain/zetacore/pkg/proofs" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" @@ -926,6 +927,134 @@ func (m *MsgResetChainNoncesResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgResetChainNoncesResponse proto.InternalMessageInfo +type MsgVoteTSS struct { + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + TssPubkey string `protobuf:"bytes,2,opt,name=tss_pubkey,json=tssPubkey,proto3" json:"tss_pubkey,omitempty"` + KeygenZetaHeight int64 `protobuf:"varint,3,opt,name=keygen_zeta_height,json=keygenZetaHeight,proto3" json:"keygen_zeta_height,omitempty"` + Status chains.ReceiveStatus `protobuf:"varint,4,opt,name=status,proto3,enum=chains.ReceiveStatus" json:"status,omitempty"` +} + +func (m *MsgVoteTSS) Reset() { *m = MsgVoteTSS{} } +func (m *MsgVoteTSS) String() string { return proto.CompactTextString(m) } +func (*MsgVoteTSS) ProtoMessage() {} +func (*MsgVoteTSS) Descriptor() ([]byte, []int) { + return fileDescriptor_1bcd40fa296a2b1d, []int{18} +} +func (m *MsgVoteTSS) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgVoteTSS) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgVoteTSS.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 *MsgVoteTSS) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgVoteTSS.Merge(m, src) +} +func (m *MsgVoteTSS) XXX_Size() int { + return m.Size() +} +func (m *MsgVoteTSS) XXX_DiscardUnknown() { + xxx_messageInfo_MsgVoteTSS.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgVoteTSS proto.InternalMessageInfo + +func (m *MsgVoteTSS) GetCreator() string { + if m != nil { + return m.Creator + } + return "" +} + +func (m *MsgVoteTSS) GetTssPubkey() string { + if m != nil { + return m.TssPubkey + } + return "" +} + +func (m *MsgVoteTSS) GetKeygenZetaHeight() int64 { + if m != nil { + return m.KeygenZetaHeight + } + return 0 +} + +func (m *MsgVoteTSS) GetStatus() chains.ReceiveStatus { + if m != nil { + return m.Status + } + return chains.ReceiveStatus_Created +} + +type MsgVoteTSSResponse struct { + BallotCreated bool `protobuf:"varint,1,opt,name=ballot_created,json=ballotCreated,proto3" json:"ballot_created,omitempty"` + VoteFinalized bool `protobuf:"varint,2,opt,name=vote_finalized,json=voteFinalized,proto3" json:"vote_finalized,omitempty"` + KeygenSuccess bool `protobuf:"varint,3,opt,name=keygen_success,json=keygenSuccess,proto3" json:"keygen_success,omitempty"` +} + +func (m *MsgVoteTSSResponse) Reset() { *m = MsgVoteTSSResponse{} } +func (m *MsgVoteTSSResponse) String() string { return proto.CompactTextString(m) } +func (*MsgVoteTSSResponse) ProtoMessage() {} +func (*MsgVoteTSSResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1bcd40fa296a2b1d, []int{19} +} +func (m *MsgVoteTSSResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgVoteTSSResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgVoteTSSResponse.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 *MsgVoteTSSResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgVoteTSSResponse.Merge(m, src) +} +func (m *MsgVoteTSSResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgVoteTSSResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgVoteTSSResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgVoteTSSResponse proto.InternalMessageInfo + +func (m *MsgVoteTSSResponse) GetBallotCreated() bool { + if m != nil { + return m.BallotCreated + } + return false +} + +func (m *MsgVoteTSSResponse) GetVoteFinalized() bool { + if m != nil { + return m.VoteFinalized + } + return false +} + +func (m *MsgVoteTSSResponse) GetKeygenSuccess() bool { + if m != nil { + return m.KeygenSuccess + } + return false +} + func init() { proto.RegisterType((*MsgUpdateObserver)(nil), "zetachain.zetacore.observer.MsgUpdateObserver") proto.RegisterType((*MsgUpdateObserverResponse)(nil), "zetachain.zetacore.observer.MsgUpdateObserverResponse") @@ -945,76 +1074,89 @@ func init() { proto.RegisterType((*MsgUpdateKeygenResponse)(nil), "zetachain.zetacore.observer.MsgUpdateKeygenResponse") proto.RegisterType((*MsgResetChainNonces)(nil), "zetachain.zetacore.observer.MsgResetChainNonces") proto.RegisterType((*MsgResetChainNoncesResponse)(nil), "zetachain.zetacore.observer.MsgResetChainNoncesResponse") + proto.RegisterType((*MsgVoteTSS)(nil), "zetachain.zetacore.observer.MsgVoteTSS") + proto.RegisterType((*MsgVoteTSSResponse)(nil), "zetachain.zetacore.observer.MsgVoteTSSResponse") } func init() { proto.RegisterFile("observer/tx.proto", fileDescriptor_1bcd40fa296a2b1d) } var fileDescriptor_1bcd40fa296a2b1d = []byte{ - // 1024 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0x4d, 0x6f, 0xe3, 0x44, - 0x18, 0xae, 0x37, 0xfb, 0xd1, 0xbe, 0xe9, 0xf6, 0x63, 0x48, 0xb7, 0x69, 0xba, 0xcd, 0x56, 0x3e, - 0xa0, 0x00, 0x25, 0x69, 0xb3, 0x80, 0x58, 0x24, 0x0e, 0x2d, 0x1f, 0x6d, 0x58, 0x76, 0x5b, 0x59, - 0xa2, 0x07, 0x2e, 0xd6, 0xc4, 0x33, 0xb1, 0xad, 0x3a, 0x33, 0x91, 0xc7, 0xd9, 0x36, 0x08, 0x90, - 0xf8, 0x01, 0x08, 0x7e, 0x00, 0x7f, 0x82, 0xff, 0xc0, 0x61, 0x8f, 0x7b, 0xe4, 0x84, 0x50, 0x7b, - 0x82, 0x3f, 0xc0, 0x15, 0x79, 0x6c, 0x4f, 0xe2, 0x7c, 0x38, 0x49, 0x4f, 0xcd, 0xcc, 0xfb, 0xbc, - 0xcf, 0xfb, 0x31, 0xcf, 0xbc, 0xe3, 0xc2, 0x3a, 0x6f, 0x0a, 0xea, 0xbf, 0xa2, 0x7e, 0x2d, 0xb8, - 0xaa, 0x76, 0x7c, 0x1e, 0x70, 0xb4, 0xfd, 0x1d, 0x0d, 0xb0, 0xe5, 0x60, 0x97, 0x55, 0xe5, 0x2f, - 0xee, 0xd3, 0x6a, 0x82, 0x2a, 0x15, 0x6c, 0x6e, 0x73, 0x89, 0xab, 0x85, 0xbf, 0x22, 0x97, 0x52, - 0x41, 0xb1, 0x34, 0x3d, 0xdc, 0xa6, 0xf1, 0xee, 0x13, 0xb5, 0x6b, 0xf9, 0x5c, 0x08, 0x49, 0x69, - 0xb6, 0x3c, 0x6c, 0x8b, 0x18, 0xb0, 0xa9, 0x00, 0xc9, 0x8f, 0xd8, 0xb0, 0xa1, 0x0c, 0x1d, 0xec, - 0xe3, 0x76, 0x82, 0xdf, 0xe9, 0x6f, 0x53, 0x46, 0x5c, 0x66, 0x9b, 0x8c, 0x33, 0x8b, 0x26, 0x66, - 0xd4, 0xaf, 0x45, 0xa8, 0x10, 0x9d, 0x0b, 0xbb, 0xd6, 0xf1, 0x39, 0x6f, 0x89, 0xf8, 0x4f, 0x64, - 0xd0, 0xff, 0xd1, 0x60, 0xfd, 0x85, 0xb0, 0xbf, 0xe9, 0x10, 0x1c, 0xd0, 0xd3, 0xd8, 0x11, 0x15, - 0xe1, 0x81, 0xe5, 0x53, 0x1c, 0x70, 0xbf, 0xa8, 0xed, 0x6a, 0x95, 0x25, 0x23, 0x59, 0xa2, 0x7d, - 0x28, 0x70, 0x8f, 0x98, 0x49, 0x08, 0x13, 0x13, 0xe2, 0x53, 0x21, 0x8a, 0x77, 0x24, 0x0c, 0x71, - 0x8f, 0x24, 0x24, 0x87, 0x91, 0x25, 0xf4, 0x60, 0xf4, 0x72, 0xd4, 0x23, 0x17, 0x79, 0x30, 0x7a, - 0x39, 0xec, 0x71, 0x0e, 0x0f, 0xbb, 0x32, 0x1f, 0xd3, 0xa7, 0x58, 0x70, 0x56, 0xbc, 0xbb, 0xab, - 0x55, 0x56, 0xea, 0x07, 0xd5, 0x8c, 0x13, 0xa9, 0x26, 0x24, 0x51, 0x25, 0x86, 0x74, 0x34, 0x96, - 0xbb, 0x03, 0x2b, 0x7d, 0x1b, 0xb6, 0x46, 0x4a, 0x35, 0xa8, 0xe8, 0x70, 0x26, 0xa8, 0xfe, 0x7b, - 0xd4, 0x88, 0x43, 0x42, 0x8e, 0x3c, 0x6e, 0x5d, 0x9c, 0x50, 0x4c, 0x32, 0x1b, 0xb1, 0x05, 0x8b, - 0xd1, 0x49, 0xba, 0x44, 0x16, 0x9f, 0x33, 0x1e, 0xc8, 0x75, 0x83, 0xa0, 0x1d, 0x80, 0x66, 0xc8, - 0x61, 0x3a, 0x58, 0x38, 0xb2, 0xce, 0x65, 0x63, 0x49, 0xee, 0x9c, 0x60, 0xe1, 0xa0, 0x47, 0x70, - 0xdf, 0xa1, 0xae, 0xed, 0x04, 0xb2, 0xae, 0x9c, 0x11, 0xaf, 0xd0, 0x7e, 0xb8, 0x1f, 0x46, 0x2d, - 0xde, 0xdb, 0xd5, 0x2a, 0xf9, 0x3a, 0xaa, 0xc6, 0x27, 0x15, 0xe5, 0xf2, 0x39, 0x0e, 0xf0, 0xd1, - 0xdd, 0xd7, 0x7f, 0x3d, 0x59, 0x30, 0x62, 0x5c, 0x5c, 0x50, 0x3a, 0x65, 0x55, 0xd0, 0xf7, 0x50, - 0x50, 0xd5, 0x7e, 0x16, 0x66, 0x76, 0x26, 0x35, 0x94, 0x51, 0xd2, 0x57, 0x90, 0xb7, 0xfa, 0x40, - 0x59, 0x55, 0xbe, 0x5e, 0xc9, 0xec, 0xfa, 0x00, 0xb1, 0x31, 0xe8, 0xac, 0x97, 0xe1, 0xf1, 0xb8, - 0xe8, 0x2a, 0xbb, 0xe7, 0x32, 0x3b, 0x83, 0xb6, 0xf9, 0xab, 0x19, 0xb3, 0x9b, 0xdc, 0xf0, 0x38, - 0xd8, 0x08, 0x99, 0x0a, 0xf6, 0x87, 0x06, 0x2b, 0x51, 0xa3, 0x66, 0x50, 0xf8, 0x3b, 0xb0, 0x36, - 0x41, 0xdd, 0xab, 0x7c, 0x48, 0xa8, 0x9f, 0xc0, 0x96, 0x6c, 0x89, 0xe7, 0x52, 0x16, 0x98, 0xb6, - 0x8f, 0x59, 0x40, 0xa9, 0xd9, 0xe9, 0x36, 0x2f, 0x68, 0x2f, 0xd6, 0xf7, 0x66, 0x1f, 0x70, 0x1c, - 0xd9, 0xcf, 0xa4, 0x19, 0x1d, 0xc0, 0x06, 0x26, 0xc4, 0x64, 0x9c, 0x50, 0x13, 0x5b, 0x16, 0xef, - 0xb2, 0xc0, 0xe4, 0xcc, 0xeb, 0x49, 0x51, 0x2c, 0x1a, 0x08, 0x13, 0xf2, 0x92, 0x13, 0x7a, 0x18, - 0x99, 0x4e, 0x99, 0xd7, 0xd3, 0x8b, 0xf0, 0x28, 0x5d, 0x85, 0x2a, 0xf0, 0x17, 0x0d, 0x56, 0x13, - 0x25, 0xe0, 0x36, 0x3d, 0xe7, 0x01, 0xbd, 0x9d, 0x74, 0x8f, 0x43, 0xe9, 0xe2, 0x36, 0x35, 0x5d, - 0xd6, 0xe2, 0xb2, 0x84, 0x7c, 0x5d, 0xcf, 0x54, 0x80, 0x0c, 0x18, 0xeb, 0x72, 0x49, 0xfa, 0x36, - 0x58, 0x8b, 0xeb, 0x5b, 0xb0, 0x39, 0x94, 0x90, 0x4a, 0xf6, 0xbf, 0x3b, 0x50, 0xec, 0x6b, 0x43, - 0x8d, 0xc4, 0x2f, 0xc3, 0x89, 0x98, 0x91, 0xf5, 0xbb, 0xb0, 0xe6, 0x8a, 0x06, 0x6b, 0xf2, 0x2e, - 0x23, 0x5f, 0x30, 0xdc, 0xf4, 0x28, 0x91, 0x09, 0x2e, 0x1a, 0x23, 0xfb, 0x68, 0x0f, 0xd6, 0x5d, - 0x71, 0xda, 0x0d, 0x52, 0xe0, 0xa8, 0xb1, 0xa3, 0x06, 0xe4, 0xc0, 0x86, 0x8d, 0xc5, 0x99, 0xef, - 0x5a, 0xb4, 0xc1, 0xc2, 0x70, 0x82, 0xca, 0x64, 0xe2, 0x7b, 0x58, 0xcf, 0xac, 0xff, 0x78, 0x9c, - 0xa7, 0x31, 0x9e, 0x10, 0xfd, 0x00, 0x8f, 0x9b, 0xfd, 0xab, 0x7a, 0x4e, 0x7d, 0xb7, 0xe5, 0x5a, - 0x38, 0x70, 0x79, 0x54, 0x7d, 0xf1, 0xbe, 0x0c, 0xf8, 0x6c, 0x4a, 0xc3, 0x27, 0x13, 0x18, 0x99, - 0xf4, 0xba, 0x0e, 0xbb, 0x93, 0x1a, 0xaf, 0x4e, 0xe7, 0x50, 0x2a, 0x29, 0xc2, 0x3c, 0xa7, 0x3d, - 0x9b, 0xb2, 0x8c, 0x33, 0x29, 0xc0, 0x3d, 0x19, 0x30, 0x96, 0x51, 0xb4, 0x88, 0xcf, 0x7e, 0x90, - 0x42, 0xb1, 0xff, 0xa6, 0xc1, 0x5b, 0xf2, 0xaa, 0x0a, 0x1a, 0xc8, 0x9b, 0xfa, 0x52, 0xbe, 0x5c, - 0xb7, 0x13, 0xeb, 0xdb, 0xb0, 0x1a, 0x99, 0xe4, 0xf3, 0x67, 0x7a, 0xfc, 0x52, 0x0a, 0x22, 0x67, - 0x3c, 0xb4, 0x14, 0xf5, 0xd7, 0xfc, 0x12, 0x55, 0x60, 0x6d, 0x10, 0xe7, 0xb8, 0xb6, 0x13, 0x8f, - 0xde, 0x95, 0x3e, 0xf0, 0xc4, 0xb5, 0x1d, 0x7d, 0x07, 0xb6, 0xc7, 0x64, 0x97, 0x64, 0x5f, 0xff, - 0x77, 0x11, 0x72, 0x2f, 0x84, 0x8d, 0x38, 0xe4, 0x07, 0x67, 0xc9, 0x7b, 0x99, 0xe7, 0x95, 0xbe, - 0xb2, 0xa5, 0xa7, 0x73, 0x80, 0x93, 0xc0, 0xe8, 0x0a, 0x56, 0x86, 0x5e, 0xe8, 0xea, 0x34, 0x9a, - 0x34, 0xbe, 0xf4, 0xd1, 0x7c, 0x78, 0x15, 0xf9, 0x27, 0x0d, 0xd6, 0x47, 0xdf, 0x90, 0x83, 0xd9, - 0xd8, 0x06, 0x5c, 0x4a, 0xcf, 0xe6, 0x76, 0x49, 0xe5, 0x30, 0xfa, 0x52, 0x4c, 0xcd, 0x61, 0xc4, - 0x65, 0x7a, 0x0e, 0x13, 0x9f, 0x10, 0xe4, 0xc3, 0x72, 0x6a, 0xba, 0xee, 0xcd, 0x70, 0x8c, 0x0a, - 0x5d, 0xfa, 0x60, 0x1e, 0xb4, 0x8a, 0xf9, 0xb3, 0x06, 0x1b, 0xe3, 0xa7, 0xe4, 0x87, 0x33, 0x36, - 0x33, 0xed, 0x56, 0xfa, 0xf4, 0x56, 0x6e, 0x83, 0x3d, 0x48, 0xcd, 0x85, 0xbd, 0xd9, 0xe8, 0x22, - 0xf4, 0xf4, 0x1e, 0x8c, 0x1b, 0x18, 0xa1, 0xf2, 0x87, 0x3e, 0xc9, 0xaa, 0x33, 0xf5, 0x52, 0xe1, - 0xa7, 0x2b, 0x7f, 0xfc, 0xf7, 0x13, 0xfa, 0x11, 0xd6, 0x46, 0xc6, 0xd4, 0xfe, 0x74, 0x01, 0xa5, - 0x3d, 0x4a, 0x1f, 0xcf, 0xeb, 0x91, 0xc4, 0x3f, 0x6a, 0xbc, 0xbe, 0x2e, 0x6b, 0x6f, 0xae, 0xcb, - 0xda, 0xdf, 0xd7, 0x65, 0xed, 0xd7, 0x9b, 0xf2, 0xc2, 0x9b, 0x9b, 0xf2, 0xc2, 0x9f, 0x37, 0xe5, - 0x85, 0x6f, 0x6b, 0xb6, 0x1b, 0x38, 0xdd, 0x66, 0xd5, 0xe2, 0xed, 0x5a, 0xc8, 0xf9, 0xbe, 0xa4, - 0xaf, 0x25, 0xf4, 0xb5, 0xab, 0x5a, 0xff, 0x3f, 0x80, 0x5e, 0x87, 0x8a, 0xe6, 0x7d, 0xf9, 0xad, - 0xff, 0xf4, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc5, 0x1a, 0xf7, 0xc9, 0xe6, 0x0c, 0x00, 0x00, + // 1186 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0xcd, 0x72, 0xe3, 0x44, + 0x10, 0x8e, 0x36, 0xbb, 0x89, 0xd3, 0xf9, 0x17, 0xc9, 0xc6, 0x71, 0x36, 0xde, 0x94, 0xaa, 0x80, + 0x00, 0x59, 0x3b, 0xf1, 0x02, 0xc5, 0x52, 0xc5, 0x21, 0x59, 0xd8, 0x24, 0x2c, 0xd9, 0xa4, 0x14, + 0xc8, 0x61, 0x2f, 0xaa, 0xb1, 0x34, 0x96, 0x54, 0x91, 0x67, 0x5c, 0x9a, 0x71, 0x7e, 0xb6, 0x80, + 0x2a, 0x8e, 0x1c, 0x28, 0x78, 0x00, 0x4e, 0xbc, 0x01, 0xef, 0xc0, 0x61, 0x6f, 0xec, 0x91, 0x13, + 0x45, 0x25, 0x27, 0x9e, 0x80, 0x2b, 0xa5, 0x99, 0xd1, 0xd8, 0x8e, 0x1d, 0xd9, 0xc9, 0xc9, 0x52, + 0xf7, 0xd7, 0xdd, 0x5f, 0xf7, 0xf4, 0x74, 0xcb, 0x30, 0x4b, 0xab, 0x0c, 0xc7, 0x27, 0x38, 0x2e, + 0xf3, 0xb3, 0x52, 0x23, 0xa6, 0x9c, 0x9a, 0x4b, 0xaf, 0x30, 0x47, 0x6e, 0x80, 0x42, 0x52, 0x12, + 0x4f, 0x34, 0xc6, 0xa5, 0x14, 0x55, 0x98, 0xf3, 0xa9, 0x4f, 0x05, 0xae, 0x9c, 0x3c, 0x49, 0x93, + 0xc2, 0x9c, 0xf6, 0x52, 0x8d, 0x50, 0x1d, 0x2b, 0xe9, 0x43, 0x2d, 0x75, 0x63, 0xca, 0x98, 0x70, + 0xe9, 0xd4, 0x22, 0xe4, 0x33, 0x05, 0x58, 0xd0, 0x80, 0xf4, 0x41, 0x29, 0xe6, 0xb5, 0xa2, 0x81, + 0x62, 0x54, 0x4f, 0xf1, 0xcb, 0x2d, 0x31, 0x26, 0x5e, 0x48, 0x7c, 0x87, 0x50, 0xe2, 0xe2, 0x54, + 0x6d, 0xb6, 0x72, 0x61, 0x3a, 0x44, 0xe3, 0xd8, 0x2f, 0x8b, 0xc8, 0x4c, 0xfd, 0xb4, 0x2b, 0x1a, + 0x31, 0xa5, 0x35, 0xa6, 0x7e, 0xa4, 0xc2, 0xfa, 0xd7, 0x80, 0xd9, 0x3d, 0xe6, 0x7f, 0xd3, 0xf0, + 0x10, 0xc7, 0xfb, 0xca, 0xa3, 0x99, 0x87, 0x51, 0x37, 0xc6, 0x88, 0xd3, 0x38, 0x6f, 0xac, 0x18, + 0xab, 0x63, 0x76, 0xfa, 0x6a, 0xae, 0xc3, 0x1c, 0x8d, 0x3c, 0x27, 0x8d, 0xed, 0x20, 0xcf, 0x8b, + 0x31, 0x63, 0xf9, 0x3b, 0x02, 0x66, 0xd2, 0xc8, 0x4b, 0x9d, 0x6c, 0x4a, 0x4d, 0x62, 0x41, 0xf0, + 0x69, 0xb7, 0xc5, 0xb0, 0xb4, 0x20, 0xf8, 0xf4, 0xaa, 0xc5, 0x11, 0x4c, 0x36, 0x05, 0x1f, 0x27, + 0xc6, 0x88, 0x51, 0x92, 0xbf, 0xbb, 0x62, 0xac, 0x4e, 0x55, 0x36, 0x4a, 0x19, 0x47, 0x55, 0x4a, + 0x9d, 0xc8, 0x4c, 0x6c, 0x61, 0x68, 0x4f, 0x34, 0xdb, 0xde, 0xac, 0x25, 0x58, 0xec, 0x4a, 0xd5, + 0xc6, 0xac, 0x41, 0x09, 0xc3, 0xd6, 0xef, 0xb2, 0x10, 0x9b, 0x9e, 0xb7, 0x15, 0x51, 0xf7, 0x78, + 0x07, 0x23, 0x2f, 0xb3, 0x10, 0x8b, 0x90, 0x93, 0x47, 0x1c, 0x7a, 0x22, 0xf9, 0x61, 0x7b, 0x54, + 0xbc, 0xef, 0x7a, 0xe6, 0x32, 0x40, 0x35, 0xf1, 0xe1, 0x04, 0x88, 0x05, 0x22, 0xcf, 0x09, 0x7b, + 0x4c, 0x48, 0x76, 0x10, 0x0b, 0xcc, 0xfb, 0x30, 0x12, 0xe0, 0xd0, 0x0f, 0xb8, 0xc8, 0x6b, 0xd8, + 0x56, 0x6f, 0xe6, 0x7a, 0x22, 0x4f, 0xa2, 0xe6, 0xef, 0xad, 0x18, 0xab, 0xe3, 0x15, 0xb3, 0xa4, + 0x4e, 0x4a, 0x72, 0xf9, 0x1c, 0x71, 0xb4, 0x75, 0xf7, 0xf5, 0xdf, 0x0f, 0x87, 0x6c, 0x85, 0x53, + 0x09, 0x75, 0x52, 0xd6, 0x09, 0x7d, 0x0b, 0x73, 0x3a, 0xdb, 0xa7, 0x09, 0xb3, 0x03, 0xd1, 0x5c, + 0x19, 0x29, 0x7d, 0x09, 0xe3, 0x6e, 0x0b, 0x28, 0xb2, 0x1a, 0xaf, 0xac, 0x66, 0x56, 0xbd, 0xcd, + 0xb1, 0xdd, 0x6e, 0x6c, 0x15, 0xe1, 0x41, 0xaf, 0xe8, 0x9a, 0xdd, 0x73, 0xc1, 0xce, 0xc6, 0x75, + 0x7a, 0x32, 0x20, 0xbb, 0xeb, 0x0b, 0xae, 0x82, 0x75, 0x39, 0xd3, 0xc1, 0xfe, 0x30, 0x60, 0x4a, + 0x16, 0x6a, 0x80, 0x0e, 0x7f, 0x0f, 0x66, 0xae, 0xe9, 0xee, 0x69, 0x7a, 0xa5, 0x51, 0x3f, 0x85, + 0x45, 0x51, 0x92, 0x28, 0xc4, 0x84, 0x3b, 0x7e, 0x8c, 0x08, 0xc7, 0xd8, 0x69, 0x34, 0xab, 0xc7, + 0xf8, 0x5c, 0xf5, 0xf7, 0x42, 0x0b, 0xb0, 0x2d, 0xf5, 0x07, 0x42, 0x6d, 0x6e, 0xc0, 0x3c, 0xf2, + 0x3c, 0x87, 0x50, 0x0f, 0x3b, 0xc8, 0x75, 0x69, 0x93, 0x70, 0x87, 0x92, 0xe8, 0x5c, 0x34, 0x45, + 0xce, 0x36, 0x91, 0xe7, 0xbd, 0xa0, 0x1e, 0xde, 0x94, 0xaa, 0x7d, 0x12, 0x9d, 0x5b, 0x79, 0xb8, + 0xdf, 0x99, 0x85, 0x4e, 0xf0, 0x67, 0x03, 0xa6, 0xd3, 0x4e, 0x40, 0x75, 0x7c, 0x44, 0x39, 0xbe, + 0x5d, 0xeb, 0x6e, 0x27, 0xad, 0x8b, 0xea, 0xd8, 0x09, 0x49, 0x8d, 0x8a, 0x14, 0xc6, 0x2b, 0x56, + 0x66, 0x07, 0x88, 0x80, 0xaa, 0x2f, 0xc7, 0x84, 0xed, 0x2e, 0xa9, 0x51, 0x6b, 0x11, 0x16, 0xae, + 0x10, 0xd2, 0x64, 0xff, 0xbb, 0x03, 0xf9, 0x56, 0x6f, 0xe8, 0x59, 0xf9, 0x2c, 0x19, 0x95, 0x19, + 0xac, 0xdf, 0x87, 0x99, 0x90, 0xed, 0x92, 0x2a, 0x6d, 0x12, 0xef, 0x0b, 0x82, 0xaa, 0x11, 0xf6, + 0x04, 0xc1, 0x9c, 0xdd, 0x25, 0x37, 0xd7, 0x60, 0x36, 0x64, 0xfb, 0x4d, 0xde, 0x01, 0x96, 0x85, + 0xed, 0x56, 0x98, 0x01, 0xcc, 0xfb, 0x88, 0x1d, 0xc4, 0xa1, 0x8b, 0x77, 0x49, 0x12, 0x8e, 0x61, + 0x41, 0x46, 0xdd, 0xc3, 0x4a, 0x66, 0xfe, 0xdb, 0xbd, 0x2c, 0xed, 0xde, 0x0e, 0xcd, 0xef, 0xe0, + 0x41, 0xb5, 0x75, 0x55, 0x8f, 0x70, 0x1c, 0xd6, 0x42, 0x17, 0xf1, 0x90, 0xca, 0xec, 0xf3, 0x23, + 0x22, 0xe0, 0x93, 0x3e, 0x05, 0xbf, 0xde, 0x81, 0x9d, 0xe9, 0xde, 0xb2, 0x60, 0xe5, 0xba, 0xc2, + 0xeb, 0xd3, 0xd9, 0x14, 0x9d, 0x24, 0x31, 0xcf, 0xf1, 0xb9, 0x8f, 0x49, 0xc6, 0x99, 0xcc, 0xc1, + 0x3d, 0x11, 0x50, 0xb5, 0x91, 0x7c, 0x51, 0x67, 0xdf, 0xee, 0x42, 0x7b, 0xff, 0xd5, 0x80, 0xb7, + 0xc4, 0x55, 0x65, 0x98, 0x8b, 0x9b, 0xfa, 0x42, 0xac, 0xb4, 0xdb, 0x35, 0xeb, 0x3b, 0x30, 0x2d, + 0x55, 0x62, 0x2f, 0x3a, 0x11, 0x3d, 0x15, 0x0d, 0x31, 0x6c, 0x4f, 0xba, 0xda, 0xf5, 0x57, 0xf4, + 0xd4, 0x5c, 0x85, 0x99, 0x76, 0x5c, 0x10, 0xfa, 0x81, 0x1a, 0xbd, 0x53, 0x2d, 0xe0, 0x4e, 0xe8, + 0x07, 0xd6, 0x32, 0x2c, 0xf5, 0x60, 0xa7, 0xd9, 0xff, 0x66, 0x00, 0xec, 0x31, 0x3f, 0xe9, 0xe6, + 0xaf, 0x0f, 0x0f, 0x33, 0x48, 0x2f, 0x03, 0x70, 0xc6, 0xd2, 0x49, 0x20, 0xa7, 0xc7, 0x18, 0x67, + 0x4c, 0xdd, 0xfd, 0x35, 0x30, 0x8f, 0x45, 0x5d, 0x9c, 0xe4, 0x78, 0x1d, 0xb5, 0x0d, 0x24, 0xf7, + 0x19, 0xa9, 0x79, 0x89, 0x39, 0xda, 0x91, 0x7b, 0xe1, 0x11, 0x8c, 0x30, 0x8e, 0x78, 0x93, 0xa9, + 0x3d, 0x38, 0x5f, 0x52, 0xab, 0xdd, 0xc6, 0x2e, 0x0e, 0x4f, 0xf0, 0xa1, 0x50, 0xda, 0x0a, 0x64, + 0xfd, 0x68, 0x80, 0xd9, 0x22, 0x99, 0x72, 0x37, 0xdf, 0x86, 0xa9, 0x2a, 0x8a, 0x22, 0xca, 0x1d, + 0x41, 0x12, 0x7b, 0x82, 0x73, 0xce, 0x9e, 0x94, 0xd2, 0xa7, 0x52, 0x98, 0xc0, 0x4e, 0x28, 0xc7, + 0x4e, 0x2d, 0x24, 0x28, 0x0a, 0x5f, 0x61, 0x59, 0xf4, 0x9c, 0x3d, 0x99, 0x48, 0x9f, 0xa5, 0xc2, + 0x04, 0xa6, 0x32, 0x60, 0x4d, 0xd7, 0x4d, 0xd7, 0x79, 0xce, 0x9e, 0x94, 0xd2, 0x43, 0x29, 0xac, + 0xfc, 0x39, 0x06, 0xc3, 0x7b, 0xcc, 0x37, 0x29, 0x8c, 0xb7, 0x0f, 0xdf, 0x0f, 0x32, 0x1b, 0xbc, + 0x73, 0xc6, 0x15, 0x1e, 0xdf, 0x00, 0xac, 0xb3, 0x3d, 0x83, 0xa9, 0x2b, 0x9f, 0x34, 0xa5, 0x7e, + 0x6e, 0x3a, 0xf1, 0x85, 0x8f, 0x6f, 0x86, 0xd7, 0x91, 0x7f, 0x30, 0x60, 0xb6, 0x7b, 0xe9, 0x6e, + 0x0c, 0xe6, 0xad, 0xcd, 0xa4, 0xf0, 0xe4, 0xc6, 0x26, 0x1d, 0x1c, 0xba, 0x57, 0x6b, 0x5f, 0x0e, + 0x5d, 0x26, 0xfd, 0x39, 0x5c, 0xbb, 0x73, 0xcd, 0x18, 0x26, 0x3a, 0xd6, 0xd1, 0xda, 0x00, 0xc7, + 0xa8, 0xd1, 0x85, 0x0f, 0x6f, 0x82, 0xd6, 0x31, 0x7f, 0x32, 0x60, 0xbe, 0xf7, 0x5a, 0xf9, 0x68, + 0xc0, 0x62, 0x76, 0x9a, 0x15, 0x3e, 0xbb, 0x95, 0x59, 0x7b, 0x0d, 0x3a, 0x06, 0xe9, 0xda, 0x60, + 0xee, 0x24, 0xba, 0x7f, 0x0d, 0x7a, 0x4d, 0xd8, 0xa4, 0xf3, 0xaf, 0x7c, 0xc3, 0x96, 0x06, 0xaa, + 0xa5, 0xc6, 0xf7, 0xef, 0xfc, 0xde, 0x1f, 0x9c, 0xe6, 0xf7, 0x30, 0xd3, 0x35, 0xd7, 0xd7, 0xfb, + 0x37, 0x50, 0xa7, 0x45, 0xe1, 0x93, 0x9b, 0x5a, 0xe8, 0xf8, 0x2e, 0x8c, 0xa6, 0x93, 0xf9, 0xdd, + 0x7e, 0x4e, 0x14, 0xb0, 0x50, 0x1e, 0x10, 0x98, 0x06, 0xd9, 0xda, 0x7d, 0x7d, 0x51, 0x34, 0xde, + 0x5c, 0x14, 0x8d, 0x7f, 0x2e, 0x8a, 0xc6, 0x2f, 0x97, 0xc5, 0xa1, 0x37, 0x97, 0xc5, 0xa1, 0xbf, + 0x2e, 0x8b, 0x43, 0x2f, 0xcb, 0x7e, 0xc8, 0x83, 0x66, 0xb5, 0xe4, 0xd2, 0x7a, 0x39, 0x71, 0xf5, + 0x48, 0x78, 0x2d, 0xa7, 0x5e, 0xcb, 0x67, 0xe5, 0xd6, 0x1f, 0xb6, 0xf3, 0x06, 0x66, 0xd5, 0x11, + 0xf1, 0x0f, 0xec, 0xf1, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0xdb, 0x8d, 0x3c, 0x01, 0x95, 0x0e, + 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1038,6 +1180,7 @@ type MsgClient interface { UpdateKeygen(ctx context.Context, in *MsgUpdateKeygen, opts ...grpc.CallOption) (*MsgUpdateKeygenResponse, error) AddBlockHeader(ctx context.Context, in *MsgAddBlockHeader, opts ...grpc.CallOption) (*MsgAddBlockHeaderResponse, error) ResetChainNonces(ctx context.Context, in *MsgResetChainNonces, opts ...grpc.CallOption) (*MsgResetChainNoncesResponse, error) + VoteTSS(ctx context.Context, in *MsgVoteTSS, opts ...grpc.CallOption) (*MsgVoteTSSResponse, error) } type msgClient struct { @@ -1129,6 +1272,15 @@ func (c *msgClient) ResetChainNonces(ctx context.Context, in *MsgResetChainNonce return out, nil } +func (c *msgClient) VoteTSS(ctx context.Context, in *MsgVoteTSS, opts ...grpc.CallOption) (*MsgVoteTSSResponse, error) { + out := new(MsgVoteTSSResponse) + err := c.cc.Invoke(ctx, "/zetachain.zetacore.observer.Msg/VoteTSS", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // MsgServer is the server API for Msg service. type MsgServer interface { AddObserver(context.Context, *MsgAddObserver) (*MsgAddObserverResponse, error) @@ -1140,6 +1292,7 @@ type MsgServer interface { UpdateKeygen(context.Context, *MsgUpdateKeygen) (*MsgUpdateKeygenResponse, error) AddBlockHeader(context.Context, *MsgAddBlockHeader) (*MsgAddBlockHeaderResponse, error) ResetChainNonces(context.Context, *MsgResetChainNonces) (*MsgResetChainNoncesResponse, error) + VoteTSS(context.Context, *MsgVoteTSS) (*MsgVoteTSSResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. @@ -1173,6 +1326,9 @@ func (*UnimplementedMsgServer) AddBlockHeader(ctx context.Context, req *MsgAddBl func (*UnimplementedMsgServer) ResetChainNonces(ctx context.Context, req *MsgResetChainNonces) (*MsgResetChainNoncesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ResetChainNonces not implemented") } +func (*UnimplementedMsgServer) VoteTSS(ctx context.Context, req *MsgVoteTSS) (*MsgVoteTSSResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method VoteTSS not implemented") +} func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) @@ -1340,6 +1496,24 @@ func _Msg_ResetChainNonces_Handler(srv interface{}, ctx context.Context, dec fun return interceptor(ctx, in, info, handler) } +func _Msg_VoteTSS_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgVoteTSS) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).VoteTSS(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/zetachain.zetacore.observer.Msg/VoteTSS", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).VoteTSS(ctx, req.(*MsgVoteTSS)) + } + return interceptor(ctx, in, info, handler) +} + var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "zetachain.zetacore.observer.Msg", HandlerType: (*MsgServer)(nil), @@ -1380,6 +1554,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "ResetChainNonces", Handler: _Msg_ResetChainNonces_Handler, }, + { + MethodName: "VoteTSS", + Handler: _Msg_VoteTSS_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "observer/tx.proto", @@ -2028,6 +2206,106 @@ func (m *MsgResetChainNoncesResponse) MarshalToSizedBuffer(dAtA []byte) (int, er return len(dAtA) - i, nil } +func (m *MsgVoteTSS) 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 *MsgVoteTSS) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgVoteTSS) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Status != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x20 + } + if m.KeygenZetaHeight != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.KeygenZetaHeight)) + i-- + dAtA[i] = 0x18 + } + if len(m.TssPubkey) > 0 { + i -= len(m.TssPubkey) + copy(dAtA[i:], m.TssPubkey) + i = encodeVarintTx(dAtA, i, uint64(len(m.TssPubkey))) + 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 *MsgVoteTSSResponse) 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 *MsgVoteTSSResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgVoteTSSResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.KeygenSuccess { + i-- + if m.KeygenSuccess { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if m.VoteFinalized { + i-- + if m.VoteFinalized { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if m.BallotCreated { + i-- + if m.BallotCreated { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func encodeVarintTx(dAtA []byte, offset int, v uint64) int { offset -= sovTx(v) base := offset @@ -2309,6 +2587,47 @@ func (m *MsgResetChainNoncesResponse) Size() (n int) { return n } +func (m *MsgVoteTSS) 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.TssPubkey) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.KeygenZetaHeight != 0 { + n += 1 + sovTx(uint64(m.KeygenZetaHeight)) + } + if m.Status != 0 { + n += 1 + sovTx(uint64(m.Status)) + } + return n +} + +func (m *MsgVoteTSSResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BallotCreated { + n += 2 + } + if m.VoteFinalized { + n += 2 + } + if m.KeygenSuccess { + n += 2 + } + return n +} + func sovTx(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -4070,6 +4389,268 @@ func (m *MsgResetChainNoncesResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgVoteTSS) 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: MsgVoteTSS: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgVoteTSS: 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 TssPubkey", 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.TssPubkey = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field KeygenZetaHeight", wireType) + } + m.KeygenZetaHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.KeygenZetaHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Status |= chains.ReceiveStatus(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *MsgVoteTSSResponse) 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: MsgVoteTSSResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgVoteTSSResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BallotCreated", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.BallotCreated = bool(v != 0) + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field VoteFinalized", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.VoteFinalized = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field KeygenSuccess", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.KeygenSuccess = bool(v != 0) + 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 diff --git a/zetaclient/bitcoin/bitcoin_client.go b/zetaclient/bitcoin/bitcoin_client.go index c06abd0876..26acc1239c 100644 --- a/zetaclient/bitcoin/bitcoin_client.go +++ b/zetaclient/bitcoin/bitcoin_client.go @@ -13,20 +13,6 @@ import ( "sync/atomic" "time" - "github.com/zeta-chain/zetacore/pkg/chains" - "github.com/zeta-chain/zetacore/pkg/coin" - "github.com/zeta-chain/zetacore/pkg/constant" - "github.com/zeta-chain/zetacore/pkg/proofs" - corecontext "github.com/zeta-chain/zetacore/zetaclient/core_context" - - appcontext "github.com/zeta-chain/zetacore/zetaclient/app_context" - clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" - "github.com/zeta-chain/zetacore/zetaclient/compliance" - "github.com/zeta-chain/zetacore/zetaclient/config" - "github.com/zeta-chain/zetacore/zetaclient/interfaces" - "github.com/zeta-chain/zetacore/zetaclient/metrics" - "github.com/zeta-chain/zetacore/zetaclient/zetabridge" - cosmosmath "cosmossdk.io/math" "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/chaincfg" @@ -38,9 +24,21 @@ import ( lru "github.com/hashicorp/golang-lru" "github.com/pkg/errors" "github.com/rs/zerolog" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" + "github.com/zeta-chain/zetacore/pkg/constant" + "github.com/zeta-chain/zetacore/pkg/proofs" "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" + appcontext "github.com/zeta-chain/zetacore/zetaclient/app_context" + clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" + "github.com/zeta-chain/zetacore/zetaclient/compliance" + "github.com/zeta-chain/zetacore/zetaclient/config" + corecontext "github.com/zeta-chain/zetacore/zetaclient/core_context" + "github.com/zeta-chain/zetacore/zetaclient/interfaces" + "github.com/zeta-chain/zetacore/zetaclient/metrics" clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" + "github.com/zeta-chain/zetacore/zetaclient/zetabridge" "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" diff --git a/zetaclient/bitcoin/bitcoin_signer.go b/zetaclient/bitcoin/bitcoin_signer.go index 747649be06..df5502b4ae 100644 --- a/zetaclient/bitcoin/bitcoin_signer.go +++ b/zetaclient/bitcoin/bitcoin_signer.go @@ -8,11 +8,11 @@ import ( "math/rand" "time" - "github.com/zeta-chain/zetacore/pkg/chains" - "github.com/zeta-chain/zetacore/pkg/coin" corecontext "github.com/zeta-chain/zetacore/zetaclient/core_context" ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" "github.com/zeta-chain/zetacore/zetaclient/compliance" "github.com/zeta-chain/zetacore/zetaclient/interfaces" @@ -286,7 +286,8 @@ func (signer *BTCSigner) TryProcessOutTx( Logger() params := cctx.GetCurrentOutTxParam() - if params.CoinType == coin.CoinType_Zeta || params.CoinType == coin.CoinType_ERC20 { + coinType := cctx.InboundTxParams.CoinType + if coinType == coin.CoinType_Zeta || coinType == coin.CoinType_ERC20 { logger.Error().Msgf("BTC TryProcessOutTx: can only send BTC to a BTC network") return } diff --git a/zetaclient/evm/evm_client.go b/zetaclient/evm/evm_client.go index f6a70b1fe6..069545541e 100644 --- a/zetaclient/evm/evm_client.go +++ b/zetaclient/evm/evm_client.go @@ -13,20 +13,7 @@ import ( "sync/atomic" "time" - "github.com/zeta-chain/zetacore/pkg/chains" - "github.com/zeta-chain/zetacore/pkg/coin" - "github.com/zeta-chain/zetacore/pkg/proofs" - appcontext "github.com/zeta-chain/zetacore/zetaclient/app_context" - corecontext "github.com/zeta-chain/zetacore/zetaclient/core_context" - - "github.com/zeta-chain/zetacore/zetaclient/interfaces" - "github.com/zeta-chain/zetacore/zetaclient/metrics" - "github.com/zeta-chain/zetacore/zetaclient/zetabridge" - "github.com/ethereum/go-ethereum" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zeta.non-eth.sol" - zetaconnectoreth "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.eth.sol" - "github.com/ethereum/go-ethereum/accounts/abi/bind" ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" @@ -38,13 +25,23 @@ import ( "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" + "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zeta.non-eth.sol" + zetaconnectoreth "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.eth.sol" "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.non-eth.sol" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" + "github.com/zeta-chain/zetacore/pkg/proofs" crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" + appcontext "github.com/zeta-chain/zetacore/zetaclient/app_context" clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" "github.com/zeta-chain/zetacore/zetaclient/compliance" "github.com/zeta-chain/zetacore/zetaclient/config" + corecontext "github.com/zeta-chain/zetacore/zetaclient/core_context" + "github.com/zeta-chain/zetacore/zetaclient/interfaces" + "github.com/zeta-chain/zetacore/zetaclient/metrics" clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" + "github.com/zeta-chain/zetacore/zetaclient/zetabridge" "gorm.io/driver/sqlite" "gorm.io/gorm" ) @@ -319,7 +316,7 @@ func (ob *ChainClient) Stop() { // If isConfirmed, it also post to ZetaCore func (ob *ChainClient) IsSendOutTxProcessed(cctx *crosschaintypes.CrossChainTx, logger zerolog.Logger) (bool, bool, error) { sendHash := cctx.Index - cointype := cctx.GetCurrentOutTxParam().CoinType + cointype := cctx.InboundTxParams.CoinType nonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce // skip if outtx is not confirmed diff --git a/zetaclient/evm/evm_signer.go b/zetaclient/evm/evm_signer.go index a5f6114822..8301a5d267 100644 --- a/zetaclient/evm/evm_signer.go +++ b/zetaclient/evm/evm_signer.go @@ -360,7 +360,7 @@ func (signer *Signer) TryProcessOutTx( logger.Warn().Err(err).Msg(SignerErrorMsg(cctx)) return } - } else if cctx.GetCurrentOutTxParam().CoinType == coin.CoinType_Cmd { // admin command + } else if cctx.InboundTxParams.CoinType == coin.CoinType_Cmd { // admin command to := ethcommon.HexToAddress(cctx.GetCurrentOutTxParam().Receiver) if to == (ethcommon.Address{}) { logger.Error().Msgf("invalid receiver %s", cctx.GetCurrentOutTxParam().Receiver) @@ -383,7 +383,7 @@ func (signer *Signer) TryProcessOutTx( return } } else if IsSenderZetaChain(cctx, zetaBridge, &crossChainflags) { - switch cctx.GetCurrentOutTxParam().CoinType { + switch cctx.InboundTxParams.CoinType { case coin.CoinType_Gas: logger.Info().Msgf("SignWithdrawTx: %d => %s, nonce %d, gasPrice %d", cctx.InboundTxParams.SenderChainId, toChain, cctx.GetCurrentOutTxParam().OutboundTxTssNonce, txData.gasPrice) tx, err = signer.SignWithdrawTx(txData) @@ -399,7 +399,7 @@ func (signer *Signer) TryProcessOutTx( return } } else if cctx.CctxStatus.Status == types.CctxStatus_PendingRevert && cctx.OutboundTxParams[0].ReceiverChainId == zetaBridge.ZetaChain().ChainId { - switch cctx.GetCurrentOutTxParam().CoinType { + switch cctx.InboundTxParams.CoinType { case coin.CoinType_Gas: logger.Info().Msgf("SignWithdrawTx: %d => %s, nonce %d, gasPrice %d", cctx.InboundTxParams.SenderChainId, toChain, cctx.GetCurrentOutTxParam().OutboundTxTssNonce, txData.gasPrice) tx, err = signer.SignWithdrawTx(txData) diff --git a/zetaclient/evm/outbound_transaction_data.go b/zetaclient/evm/outbound_transaction_data.go index 0255c1c525..6734926f0d 100644 --- a/zetaclient/evm/outbound_transaction_data.go +++ b/zetaclient/evm/outbound_transaction_data.go @@ -170,7 +170,7 @@ func NewOutBoundTransactionData( } // Base64 decode message - if cctx.GetCurrentOutTxParam().CoinType != coin.CoinType_Cmd { + if cctx.InboundTxParams.CoinType != coin.CoinType_Cmd { txData.message, err = base64.StdEncoding.DecodeString(cctx.RelayedMessage) if err != nil { logger.Err(err).Msgf("decode CCTX.Message %s error", cctx.RelayedMessage) diff --git a/zetaclient/supplychecker/zeta_supply_checker.go b/zetaclient/supplychecker/zeta_supply_checker.go index 2168c5483e..14b4c056d2 100644 --- a/zetaclient/supplychecker/zeta_supply_checker.go +++ b/zetaclient/supplychecker/zeta_supply_checker.go @@ -247,7 +247,7 @@ func (zs *ZetaSupplyChecker) GetPendingCCTXInTransit(receivingChains []chains.Ch } nonceToCctxMap := make(map[uint64]*types.CrossChainTx) for _, c := range cctx { - if c.GetInboundTxParams().CoinType == coin.CoinType_Zeta { + if c.InboundTxParams.CoinType == coin.CoinType_Zeta { nonceToCctxMap[c.GetCurrentOutTxParam().OutboundTxTssNonce] = c } } diff --git a/zetaclient/zetabridge/tx.go b/zetaclient/zetabridge/tx.go index 1c604af4ef..90e44b16f5 100644 --- a/zetaclient/zetabridge/tx.go +++ b/zetaclient/zetabridge/tx.go @@ -7,19 +7,18 @@ import ( "time" "cosmossdk.io/math" - "github.com/zeta-chain/zetacore/pkg/coin" - "github.com/zeta-chain/zetacore/pkg/proofs" - appcontext "github.com/zeta-chain/zetacore/zetaclient/app_context" - authz2 "github.com/zeta-chain/zetacore/zetaclient/authz" - clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/authz" "github.com/pkg/errors" "github.com/zeta-chain/go-tss/blame" "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" + "github.com/zeta-chain/zetacore/pkg/proofs" "github.com/zeta-chain/zetacore/x/crosschain/types" - observerTypes "github.com/zeta-chain/zetacore/x/observer/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" + appcontext "github.com/zeta-chain/zetacore/zetaclient/app_context" + clientauthz "github.com/zeta-chain/zetacore/zetaclient/authz" + clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" ) // GasPriceMultiplier returns the gas price multiplier for the given chain @@ -32,15 +31,15 @@ func GasPriceMultiplier(chainID int64) (float64, error) { return 0, fmt.Errorf("cannot get gas price multiplier for unknown chain %d", chainID) } -func (b *ZetaCoreBridge) WrapMessageWithAuthz(msg sdk.Msg) (sdk.Msg, authz2.Signer, error) { +func (b *ZetaCoreBridge) WrapMessageWithAuthz(msg sdk.Msg) (sdk.Msg, clientauthz.Signer, error) { msgURL := sdk.MsgTypeURL(msg) // verify message validity if err := msg.ValidateBasic(); err != nil { - return nil, authz2.Signer{}, fmt.Errorf("%s invalid msg | %s", msgURL, err.Error()) + return nil, clientauthz.Signer{}, fmt.Errorf("%s invalid msg | %s", msgURL, err.Error()) } - authzSigner := authz2.GetSigner(msgURL) + authzSigner := clientauthz.GetSigner(msgURL) authzMessage := authz.NewMsgExec(authzSigner.GranteeAddress, []sdk.Msg{msg}) return &authzMessage, authzSigner, nil } @@ -107,7 +106,7 @@ func (b *ZetaCoreBridge) AddTxHashToOutTxTracker( func (b *ZetaCoreBridge) SetTSS(tssPubkey string, keyGenZetaHeight int64, status chains.ReceiveStatus) (string, error) { signerAddress := b.keys.GetOperatorAddress().String() - msg := types.NewMsgCreateTSSVoter(signerAddress, tssPubkey, keyGenZetaHeight, status) + msg := observertypes.NewMsgVoteTSS(signerAddress, tssPubkey, keyGenZetaHeight, status) authzMsg, authzSigner, err := b.WrapMessageWithAuthz(msg) if err != nil { @@ -148,12 +147,12 @@ func (b *ZetaCoreBridge) CoreContextUpdater(appContext *appcontext.AppContext) { func (b *ZetaCoreBridge) PostBlameData(blame *blame.Blame, chainID int64, index string) (string, error) { signerAddress := b.keys.GetOperatorAddress().String() - zetaBlame := observerTypes.Blame{ + zetaBlame := observertypes.Blame{ Index: index, FailureReason: blame.FailReason, - Nodes: observerTypes.ConvertNodes(blame.BlameNodes), + Nodes: observertypes.ConvertNodes(blame.BlameNodes), } - msg := observerTypes.NewMsgAddBlameVoteMsg(signerAddress, chainID, zetaBlame) + msg := observertypes.NewMsgAddBlameVoteMsg(signerAddress, chainID, zetaBlame) authzMsg, authzSigner, err := b.WrapMessageWithAuthz(msg) if err != nil { @@ -176,7 +175,7 @@ func (b *ZetaCoreBridge) PostBlameData(blame *blame.Blame, chainID int64, index func (b *ZetaCoreBridge) PostAddBlockHeader(chainID int64, blockHash []byte, height int64, header proofs.HeaderData) (string, error) { signerAddress := b.keys.GetOperatorAddress().String() - msg := observerTypes.NewMsgAddBlockHeader(signerAddress, chainID, blockHash, height, header) + msg := observertypes.NewMsgAddBlockHeader(signerAddress, chainID, blockHash, height, header) authzMsg, authzSigner, err := b.WrapMessageWithAuthz(msg) if err != nil {