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..fe9d1d9cbd 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,11 +51,15 @@ * [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 +* [1961](https://github.com/zeta-chain/node/pull/1961) - improve observer module coverage +* [1967](https://github.com/zeta-chain/node/pull/1967) - improve crosschain module coverage +* [1955](https://github.com/zeta-chain/node/pull/1955) - improve emissions module coverage ### Fixes * [1861](https://github.com/zeta-chain/node/pull/1861) - fix `ObserverSlashAmount` invalid read * [1880](https://github.com/zeta-chain/node/issues/1880) - lower the gas price multiplier for EVM chains. +* [1883](https://github.com/zeta-chain/node/issues/1883) - zetaclient should check 'IsSupported' flag to pause/unpause a specific chain * [1633](https://github.com/zeta-chain/node/issues/1633) - zetaclient should be able to pick up new connector and erc20Custody addresses * [1944](https://github.com/zeta-chain/node/pull/1944) - fix evm signer unit tests * [1888](https://github.com/zeta-chain/node/issues/1888) - zetaclient should stop inbound/outbound txs according to cross-chain flags 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/cmd/zetaclientd/utils.go b/cmd/zetaclientd/utils.go index 1ea6eb8c5b..a9fc63e1f8 100644 --- a/cmd/zetaclientd/utils.go +++ b/cmd/zetaclientd/utils.go @@ -67,9 +67,6 @@ func CreateSignerMap( loggers.Std.Error().Msgf("ChainParam not found for chain %s", evmConfig.Chain.String()) continue } - if !evmChainParams.IsSupported { - continue - } mpiAddress := ethcommon.HexToAddress(evmChainParams.ConnectorContractAddress) erc20CustodyAddress := ethcommon.HexToAddress(evmChainParams.Erc20CustodyContractAddress) signer, err := evm.NewEVMSigner( @@ -117,14 +114,11 @@ func CreateChainClientMap( if evmConfig.Chain.IsZetaChain() { continue } - evmChainParams, found := appContext.ZetaCoreContext().GetEVMChainParams(evmConfig.Chain.ChainId) + _, found := appContext.ZetaCoreContext().GetEVMChainParams(evmConfig.Chain.ChainId) if !found { loggers.Std.Error().Msgf("ChainParam not found for chain %s", evmConfig.Chain.String()) continue } - if !evmChainParams.IsSupported { - continue - } co, err := evm.NewEVMChainClient(appContext, bridge, tss, dbpath, loggers, evmConfig, ts) if err != nil { loggers.Std.Error().Err(err).Msgf("NewEVMChainClient error for chain %s", evmConfig.Chain.String()) diff --git a/codecov.yml b/codecov.yml index 93d974911b..aab4d9ab68 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" @@ -49,8 +49,10 @@ ignore: - "x/**/types/types.go" - "x/**/types/expected_keepers.go" - "x/**/module.go" + - "x/**/events.go" + - "x/**/migrator.go" - "x/**/module_simulation.go" - - "x/**/simulation/" + - "x/**/simulation/**/*" - "**/*.proto" - "**/*.md" - "**/*.yml" @@ -58,16 +60,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_query_crosschain.md b/docs/cli/zetacored/zetacored_query_crosschain.md index 0f973fe712..db80778e2a 100644 --- a/docs/cli/zetacored/zetacored_query_crosschain.md +++ b/docs/cli/zetacored/zetacored_query_crosschain.md @@ -35,7 +35,6 @@ zetacored query crosschain [flags] * [zetacored query crosschain list-in-tx-tracker](zetacored_query_crosschain_list-in-tx-tracker.md) - shows a list of in tx tracker by chainId * [zetacored query crosschain list-out-tx-tracker](zetacored_query_crosschain_list-out-tx-tracker.md) - list all OutTxTracker * [zetacored query crosschain list-pending-cctx](zetacored_query_crosschain_list-pending-cctx.md) - shows pending CCTX -* [zetacored query crosschain params](zetacored_query_crosschain_params.md) - shows the parameters of the module * [zetacored query crosschain show-cctx](zetacored_query_crosschain_show-cctx.md) - shows a CCTX * [zetacored query crosschain show-gas-price](zetacored_query_crosschain_show-gas-price.md) - shows a gasPrice * [zetacored query crosschain show-in-tx-hash-to-cctx](zetacored_query_crosschain_show-in-tx-hash-to-cctx.md) - shows a inTxHashToCctx diff --git a/docs/cli/zetacored/zetacored_query_crosschain_params.md b/docs/cli/zetacored/zetacored_query_crosschain_params.md deleted file mode 100644 index e10e8cb5b1..0000000000 --- a/docs/cli/zetacored/zetacored_query_crosschain_params.md +++ /dev/null @@ -1,33 +0,0 @@ -# query crosschain params - -shows the parameters of the module - -``` -zetacored query crosschain params [flags] -``` - -### Options - -``` - --grpc-addr string the gRPC endpoint to use for this chain - --grpc-insecure allow gRPC over insecure channels, if not TLS the server must use TLS - --height int Use a specific height to query state at (this can error if the node is pruning state) - -h, --help help for params - --node string [host]:[port] to Tendermint RPC interface for this chain - -o, --output string Output format (text|json) -``` - -### Options inherited from parent commands - -``` - --chain-id string The network chain ID - --home string directory for config and data - --log_format string The logging format (json|plain) - --log_level string The logging level (trace|debug|info|warn|error|fatal|panic) - --trace print out full stack trace on errors -``` - -### SEE ALSO - -* [zetacored query crosschain](zetacored_query_crosschain.md) - Querying commands for the crosschain module - 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..faf3e60692 100644 --- a/docs/openapi/openapi.swagger.yaml +++ b/docs/openapi/openapi.swagger.yaml @@ -27173,21 +27173,6 @@ paths: type: boolean tags: - Query - /zeta-chain/crosschain/params: - get: - summary: Parameters queries the parameters of the module. - operationId: Query_Params - responses: - "200": - description: A successful response. - schema: - $ref: '#/definitions/zetacorecrosschainQueryParamsResponse' - default: - description: An unexpected error response. - schema: - $ref: '#/definitions/googlerpcStatus' - tags: - - Query /zeta-chain/crosschain/protocolFee: get: operationId: Query_ProtocolFee @@ -53701,8 +53686,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 +54307,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: @@ -54731,19 +54723,6 @@ definitions: repeated Bar results = 1; PageResponse page = 2; } - zetacorecrosschainParams: - type: object - properties: - enabled: - type: boolean - description: Params defines the parameters for the module. - zetacorecrosschainQueryParamsResponse: - type: object - properties: - params: - $ref: '#/definitions/zetacorecrosschainParams' - description: params holds all the parameters of this module. - description: QueryParamsResponse is response type for the Query/Params RPC method. zetacorecrosschainStatus: 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/genesis.proto b/proto/crosschain/genesis.proto index 016fc38aee..87c3875b14 100644 --- a/proto/crosschain/genesis.proto +++ b/proto/crosschain/genesis.proto @@ -7,20 +7,18 @@ import "crosschain/in_tx_hash_to_cctx.proto"; import "crosschain/in_tx_tracker.proto"; import "crosschain/last_block_height.proto"; import "crosschain/out_tx_tracker.proto"; -import "crosschain/params.proto"; import "gogoproto/gogo.proto"; option go_package = "github.com/zeta-chain/zetacore/x/crosschain/types"; // GenesisState defines the metacore module's genesis state. message GenesisState { - Params params = 1 [(gogoproto.nullable) = false]; - repeated OutTxTracker outTxTrackerList = 2 [(gogoproto.nullable) = false]; - repeated GasPrice gasPriceList = 5; - repeated CrossChainTx CrossChainTxs = 7; - repeated LastBlockHeight lastBlockHeightList = 8; - repeated InTxHashToCctx inTxHashToCctxList = 9 [(gogoproto.nullable) = false]; - repeated InTxTracker in_tx_tracker_list = 11 [(gogoproto.nullable) = false]; - ZetaAccounting zeta_accounting = 12 [(gogoproto.nullable) = false]; - repeated string FinalizedInbounds = 16; + repeated OutTxTracker outTxTrackerList = 1 [(gogoproto.nullable) = false]; + repeated GasPrice gasPriceList = 2; + repeated CrossChainTx CrossChainTxs = 3; + repeated LastBlockHeight lastBlockHeightList = 4; + repeated InTxHashToCctx inTxHashToCctxList = 5 [(gogoproto.nullable) = false]; + repeated InTxTracker in_tx_tracker_list = 6 [(gogoproto.nullable) = false]; + ZetaAccounting zeta_accounting = 7 [(gogoproto.nullable) = false]; + repeated string FinalizedInbounds = 8; } diff --git a/proto/crosschain/params.proto b/proto/crosschain/params.proto deleted file mode 100644 index 5df98b9a28..0000000000 --- a/proto/crosschain/params.proto +++ /dev/null @@ -1,12 +0,0 @@ -syntax = "proto3"; -package zetachain.zetacore.crosschain; - -import "gogoproto/gogo.proto"; - -option go_package = "github.com/zeta-chain/zetacore/x/crosschain/types"; - -// Params defines the parameters for the module. -message Params { - option (gogoproto.goproto_stringer) = false; - bool enabled = 1; -} diff --git a/proto/crosschain/query.proto b/proto/crosschain/query.proto index c9eee26378..99ad6b4b72 100644 --- a/proto/crosschain/query.proto +++ b/proto/crosschain/query.proto @@ -8,7 +8,6 @@ import "crosschain/in_tx_hash_to_cctx.proto"; import "crosschain/in_tx_tracker.proto"; import "crosschain/last_block_height.proto"; import "crosschain/out_tx_tracker.proto"; -import "crosschain/params.proto"; import "gogoproto/gogo.proto"; import "google/api/annotations.proto"; @@ -16,11 +15,6 @@ option go_package = "github.com/zeta-chain/zetacore/x/crosschain/types"; // Query defines the gRPC querier service. service Query { - // Parameters queries the parameters of the module. - rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { - option (google.api.http).get = "/zeta-chain/crosschain/params"; - } - // Queries a OutTxTracker by index. rpc OutTxTracker(QueryGetOutTxTrackerRequest) returns (QueryGetOutTxTrackerResponse) { option (google.api.http).get = "/zeta-chain/crosschain/outTxTracker/{chainID}/{nonce}"; @@ -120,15 +114,6 @@ message QueryZetaAccountingResponse { string aborted_zeta_amount = 1; } -// QueryParamsRequest is request type for the Query/Params RPC method. -message QueryParamsRequest {} - -// QueryParamsResponse is response type for the Query/Params RPC method. -message QueryParamsResponse { - // params holds all the parameters of this module. - Params params = 1 [(gogoproto.nullable) = false]; -} - message QueryGetOutTxTrackerRequest { int64 chainID = 1; uint64 nonce = 2; 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/ballot.proto b/proto/observer/ballot.proto index c89b50bb83..aa871cff35 100644 --- a/proto/observer/ballot.proto +++ b/proto/observer/ballot.proto @@ -20,6 +20,7 @@ enum BallotStatus { BallotInProgress = 2; } +// https://github.com/zeta-chain/node/issues/939 message Ballot { string index = 1; string ballot_identifier = 2; 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..3fb8f81823 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, returnVal int64) { + 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(returnVal), 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/keeper/mocks/observer/staking.go b/testutil/keeper/mocks/observer/staking.go index 90007b6c35..72bf99599f 100644 --- a/testutil/keeper/mocks/observer/staking.go +++ b/testutil/keeper/mocks/observer/staking.go @@ -71,6 +71,11 @@ func (_m *ObserverStakingKeeper) GetValidator(ctx types.Context, addr types.ValA return r0, r1 } +// SetDelegation provides a mock function with given fields: ctx, delegation +func (_m *ObserverStakingKeeper) SetDelegation(ctx types.Context, delegation stakingtypes.Delegation) { + _m.Called(ctx, delegation) +} + // SetValidator provides a mock function with given fields: ctx, validator func (_m *ObserverStakingKeeper) SetValidator(ctx types.Context, validator stakingtypes.Validator) { _m.Called(ctx, validator) diff --git a/testutil/network/genesis_state.go b/testutil/network/genesis_state.go index 3fdb627b22..dfa3364cfe 100644 --- a/testutil/network/genesis_state.go +++ b/testutil/network/genesis_state.go @@ -34,7 +34,6 @@ func SetupZetaGenesisState(t *testing.T, genesisState map[string]json.RawMessage } } - crossChainGenesis.Params.Enabled = true require.NoError(t, crossChainGenesis.Validate()) crossChainGenesisBz, err := codec.MarshalJSON(&crossChainGenesis) require.NoError(t, err) diff --git a/testutil/sample/crosschain.go b/testutil/sample/crosschain.go index 3fa5d04983..0040a7fc3a 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" ) @@ -23,6 +24,16 @@ func OutTxTracker(t *testing.T, index string) types.OutTxTracker { } } +func InTxTracker(t *testing.T, index string) types.InTxTracker { + r := newRandFromStringSeed(t, index) + + return types.InTxTracker{ + ChainId: r.Int63(), + CoinType: coin.CoinType_Zeta, + TxHash: Hash().Hex(), + } +} + func GasPrice(t *testing.T, index string) *types.GasPrice { r := newRandFromStringSeed(t, index) @@ -52,6 +63,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 +94,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/genesis_pb.d.ts b/typescript/crosschain/genesis_pb.d.ts index e8c8d5dcf0..bec622e2af 100644 --- a/typescript/crosschain/genesis_pb.d.ts +++ b/typescript/crosschain/genesis_pb.d.ts @@ -5,7 +5,6 @@ import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; import { Message, proto3 } from "@bufbuild/protobuf"; -import type { Params } from "./params_pb.js"; import type { OutTxTracker } from "./out_tx_tracker_pb.js"; import type { GasPrice } from "./gas_price_pb.js"; import type { CrossChainTx, ZetaAccounting } from "./cross_chain_tx_pb.js"; @@ -20,47 +19,42 @@ import type { InTxTracker } from "./in_tx_tracker_pb.js"; */ export declare class GenesisState extends Message { /** - * @generated from field: zetachain.zetacore.crosschain.Params params = 1; - */ - params?: Params; - - /** - * @generated from field: repeated zetachain.zetacore.crosschain.OutTxTracker outTxTrackerList = 2; + * @generated from field: repeated zetachain.zetacore.crosschain.OutTxTracker outTxTrackerList = 1; */ outTxTrackerList: OutTxTracker[]; /** - * @generated from field: repeated zetachain.zetacore.crosschain.GasPrice gasPriceList = 5; + * @generated from field: repeated zetachain.zetacore.crosschain.GasPrice gasPriceList = 2; */ gasPriceList: GasPrice[]; /** - * @generated from field: repeated zetachain.zetacore.crosschain.CrossChainTx CrossChainTxs = 7; + * @generated from field: repeated zetachain.zetacore.crosschain.CrossChainTx CrossChainTxs = 3; */ CrossChainTxs: CrossChainTx[]; /** - * @generated from field: repeated zetachain.zetacore.crosschain.LastBlockHeight lastBlockHeightList = 8; + * @generated from field: repeated zetachain.zetacore.crosschain.LastBlockHeight lastBlockHeightList = 4; */ lastBlockHeightList: LastBlockHeight[]; /** - * @generated from field: repeated zetachain.zetacore.crosschain.InTxHashToCctx inTxHashToCctxList = 9; + * @generated from field: repeated zetachain.zetacore.crosschain.InTxHashToCctx inTxHashToCctxList = 5; */ inTxHashToCctxList: InTxHashToCctx[]; /** - * @generated from field: repeated zetachain.zetacore.crosschain.InTxTracker in_tx_tracker_list = 11; + * @generated from field: repeated zetachain.zetacore.crosschain.InTxTracker in_tx_tracker_list = 6; */ inTxTrackerList: InTxTracker[]; /** - * @generated from field: zetachain.zetacore.crosschain.ZetaAccounting zeta_accounting = 12; + * @generated from field: zetachain.zetacore.crosschain.ZetaAccounting zeta_accounting = 7; */ zetaAccounting?: ZetaAccounting; /** - * @generated from field: repeated string FinalizedInbounds = 16; + * @generated from field: repeated string FinalizedInbounds = 8; */ FinalizedInbounds: string[]; diff --git a/typescript/crosschain/index.d.ts b/typescript/crosschain/index.d.ts index 4c073541bc..e357b4b8b3 100644 --- a/typescript/crosschain/index.d.ts +++ b/typescript/crosschain/index.d.ts @@ -6,6 +6,5 @@ export * from "./in_tx_hash_to_cctx_pb"; export * from "./in_tx_tracker_pb"; export * from "./last_block_height_pb"; export * from "./out_tx_tracker_pb"; -export * from "./params_pb"; export * from "./query_pb"; export * from "./tx_pb"; diff --git a/typescript/crosschain/params_pb.d.ts b/typescript/crosschain/params_pb.d.ts deleted file mode 100644 index e3c135725c..0000000000 --- a/typescript/crosschain/params_pb.d.ts +++ /dev/null @@ -1,34 +0,0 @@ -// @generated by protoc-gen-es v1.3.0 with parameter "target=dts" -// @generated from file crosschain/params.proto (package zetachain.zetacore.crosschain, syntax proto3) -/* eslint-disable */ -// @ts-nocheck - -import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; -import { Message, proto3 } from "@bufbuild/protobuf"; - -/** - * Params defines the parameters for the module. - * - * @generated from message zetachain.zetacore.crosschain.Params - */ -export declare class Params extends Message { - /** - * @generated from field: bool enabled = 1; - */ - enabled: boolean; - - constructor(data?: PartialMessage); - - static readonly runtime: typeof proto3; - static readonly typeName = "zetachain.zetacore.crosschain.Params"; - static readonly fields: FieldList; - - static fromBinary(bytes: Uint8Array, options?: Partial): Params; - - static fromJson(jsonValue: JsonValue, options?: Partial): Params; - - static fromJsonString(jsonString: string, options?: Partial): Params; - - static equals(a: Params | PlainMessage | undefined, b: Params | PlainMessage | undefined): boolean; -} - diff --git a/typescript/crosschain/query_pb.d.ts b/typescript/crosschain/query_pb.d.ts index 17bb54ef27..370926427e 100644 --- a/typescript/crosschain/query_pb.d.ts +++ b/typescript/crosschain/query_pb.d.ts @@ -5,7 +5,6 @@ import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; import { Message, proto3 } from "@bufbuild/protobuf"; -import type { Params } from "./params_pb.js"; import type { OutTxTracker } from "./out_tx_tracker_pb.js"; import type { PageRequest, PageResponse } from "../cosmos/base/query/v1beta1/pagination_pb.js"; import type { InTxTracker } from "./in_tx_tracker_pb.js"; @@ -57,55 +56,6 @@ export declare class QueryZetaAccountingResponse extends Message | undefined, b: QueryZetaAccountingResponse | PlainMessage | undefined): boolean; } -/** - * QueryParamsRequest is request type for the Query/Params RPC method. - * - * @generated from message zetachain.zetacore.crosschain.QueryParamsRequest - */ -export declare class QueryParamsRequest extends Message { - constructor(data?: PartialMessage); - - static readonly runtime: typeof proto3; - static readonly typeName = "zetachain.zetacore.crosschain.QueryParamsRequest"; - static readonly fields: FieldList; - - static fromBinary(bytes: Uint8Array, options?: Partial): QueryParamsRequest; - - static fromJson(jsonValue: JsonValue, options?: Partial): QueryParamsRequest; - - static fromJsonString(jsonString: string, options?: Partial): QueryParamsRequest; - - static equals(a: QueryParamsRequest | PlainMessage | undefined, b: QueryParamsRequest | PlainMessage | undefined): boolean; -} - -/** - * QueryParamsResponse is response type for the Query/Params RPC method. - * - * @generated from message zetachain.zetacore.crosschain.QueryParamsResponse - */ -export declare class QueryParamsResponse extends Message { - /** - * params holds all the parameters of this module. - * - * @generated from field: zetachain.zetacore.crosschain.Params params = 1; - */ - params?: Params; - - constructor(data?: PartialMessage); - - static readonly runtime: typeof proto3; - static readonly typeName = "zetachain.zetacore.crosschain.QueryParamsResponse"; - static readonly fields: FieldList; - - static fromBinary(bytes: Uint8Array, options?: Partial): QueryParamsResponse; - - static fromJson(jsonValue: JsonValue, options?: Partial): QueryParamsResponse; - - static fromJsonString(jsonString: string, options?: Partial): QueryParamsResponse; - - static equals(a: QueryParamsResponse | PlainMessage | undefined, b: QueryParamsResponse | PlainMessage | undefined): boolean; -} - /** * @generated from message zetachain.zetacore.crosschain.QueryGetOutTxTrackerRequest */ 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/ballot_pb.d.ts b/typescript/observer/ballot_pb.d.ts index 100a1a0f1d..1eb7d2f01d 100644 --- a/typescript/observer/ballot_pb.d.ts +++ b/typescript/observer/ballot_pb.d.ts @@ -50,6 +50,8 @@ export declare enum BallotStatus { } /** + * https://github.com/zeta-chain/node/issues/939 + * * @generated from message zetachain.zetacore.observer.Ballot */ export declare class Ballot extends Message { 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_params.go b/x/crosschain/client/cli/cli_params.go deleted file mode 100644 index 6a42068cfa..0000000000 --- a/x/crosschain/client/cli/cli_params.go +++ /dev/null @@ -1,34 +0,0 @@ -package cli - -import ( - "context" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/spf13/cobra" - "github.com/zeta-chain/zetacore/x/crosschain/types" -) - -func CmdQueryParams() *cobra.Command { - cmd := &cobra.Command{ - Use: "params", - Short: "shows the parameters of the module", - Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - clientCtx := client.GetClientContextFromCmd(cmd) - - queryClient := types.NewQueryClient(clientCtx) - - res, err := queryClient.Params(context.Background(), &types.QueryParamsRequest{}) - if err != nil { - return err - } - - return clientCtx.PrintProto(res) - }, - } - - flags.AddQueryFlagsToCmd(cmd) - - return cmd -} 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/query.go b/x/crosschain/client/cli/query.go index 633e76e143..cd966e55af 100644 --- a/x/crosschain/client/cli/query.go +++ b/x/crosschain/client/cli/query.go @@ -37,7 +37,6 @@ func GetQueryCmd(_ string) *cobra.Command { CmdInTxHashToCctxData(), CmdListInTxHashToCctx(), CmdShowInTxHashToCctx(), - CmdQueryParams(), CmdPendingCctx(), CmdListInTxTrackerByChain(), 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/genesis.go b/x/crosschain/genesis.go index d152c32bb3..c2603532a6 100644 --- a/x/crosschain/genesis.go +++ b/x/crosschain/genesis.go @@ -9,9 +9,6 @@ import ( // InitGenesis initializes the crosschain module's state from a provided genesis // state. func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { - // Params - k.SetParams(ctx, genState.Params) - k.SetZetaAccounting(ctx, genState.ZetaAccounting) // Set all the outTxTracker for _, elem := range genState.OutTxTrackerList { @@ -59,7 +56,6 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { var genesis types.GenesisState - genesis.Params = k.GetParams(ctx) genesis.OutTxTrackerList = k.GetAllOutTxTracker(ctx) genesis.InTxHashToCctxList = k.GetAllInTxHashToCctx(ctx) genesis.InTxTrackerList = k.GetAllInTxTracker(ctx) diff --git a/x/crosschain/genesis_test.go b/x/crosschain/genesis_test.go index 3ce6a94875..470d07b09b 100644 --- a/x/crosschain/genesis_test.go +++ b/x/crosschain/genesis_test.go @@ -13,13 +13,22 @@ import ( func TestGenesis(t *testing.T) { genesisState := types.GenesisState{ - Params: types.DefaultParams(), ZetaAccounting: sample.ZetaAccounting(t, "sample"), OutTxTrackerList: []types.OutTxTracker{ sample.OutTxTracker(t, "0"), sample.OutTxTracker(t, "1"), sample.OutTxTracker(t, "2"), }, + InTxTrackerList: []types.InTxTracker{ + sample.InTxTracker(t, "0"), + sample.InTxTracker(t, "1"), + sample.InTxTracker(t, "2"), + }, + FinalizedInbounds: []string{ + sample.Hash().String(), + sample.Hash().String(), + sample.Hash().String(), + }, GasPriceList: []*types.GasPrice{ sample.GasPrice(t, "0"), sample.GasPrice(t, "1"), 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..3a7fb7fe5b 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 @@ -242,3 +243,13 @@ func TestSendQueryPaginated(t *testing.T) { require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "invalid request")) }) } + +func TestKeeper_RemoveCrossChainTx(t *testing.T) { + keeper, ctx, _, zk := keepertest.CrosschainKeeper(t) + zk.ObserverKeeper.SetTSS(ctx, sample.Tss()) + txs := createNCctx(keeper, ctx, 5) + + keeper.RemoveCrossChainTx(ctx, txs[0].Index) + txs = keeper.GetAllCrossChainTx(ctx) + require.Equal(t, 4, len(txs)) +} diff --git a/x/crosschain/keeper/cctx_utils.go b/x/crosschain/keeper/cctx_utils.go index 0fde2fb2e6..6ae826f9ed 100644 --- a/x/crosschain/keeper/cctx_utils.go +++ b/x/crosschain/keeper/cctx_utils.go @@ -18,7 +18,7 @@ import ( // UpdateNonce sets the CCTX outbound nonce to the next nonce, and updates the nonce of blockchain state. // It also updates the PendingNonces that is used to track the unfulfilled outbound txs. func (k Keeper) UpdateNonce(ctx sdk.Context, receiveChainID int64, cctx *types.CrossChainTx) error { - chain := k.zetaObserverKeeper.GetSupportedChainFromChainID(ctx, receiveChainID) + chain := k.GetObserverKeeper().GetSupportedChainFromChainID(ctx, receiveChainID) if chain == nil { return zetaObserverTypes.ErrSupportedChains } @@ -30,7 +30,7 @@ func (k Keeper) UpdateNonce(ctx sdk.Context, receiveChainID int64, cctx *types.C // SET nonce cctx.GetCurrentOutTxParam().OutboundTxTssNonce = nonce.Nonce - tss, found := k.zetaObserverKeeper.GetTSS(ctx) + tss, found := k.GetObserverKeeper().GetTSS(ctx) if !found { return cosmoserrors.Wrap(types.ErrCannotFindTSSKeys, fmt.Sprintf("Chain(%s) | Identifiers : %s ", chain.ChainName.String(), cctx.LogIdentifierForCCTX())) } diff --git a/x/crosschain/keeper/cctx_utils_test.go b/x/crosschain/keeper/cctx_utils_test.go index c637cc4cc9..1c807a97c1 100644 --- a/x/crosschain/keeper/cctx_utils_test.go +++ b/x/crosschain/keeper/cctx_utils_test.go @@ -1,17 +1,21 @@ 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/chains" "github.com/zeta-chain/zetacore/pkg/coin" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" crosschainkeeper "github.com/zeta-chain/zetacore/x/crosschain/keeper" "github.com/zeta-chain/zetacore/x/crosschain/types" fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) func TestGetRevertGasLimit(t *testing.T) { @@ -46,6 +50,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 +81,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 +95,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 +117,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 +129,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 +200,177 @@ 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}})) + }) + } +} + +func TestKeeper_UpdateNonce(t *testing.T) { + t.Run("should error if supported chain is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(nil) + + err := k.UpdateNonce(ctx, 5, nil) + require.Error(t, err) + }) + + t.Run("should error if chain nonces not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(&chains.Chain{ + ChainName: 5, + ChainId: 5, + }) + observerMock.On("GetChainNonces", mock.Anything, mock.Anything).Return(observertypes.ChainNonces{}, false) + cctx := types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + Amount: sdkmath.ZeroUint(), + }, + OutboundTxParams: []*types.OutboundTxParams{ + {Amount: sdkmath.NewUint(1)}, + }, + } + err := k.UpdateNonce(ctx, 5, &cctx) + require.Error(t, err) + }) + + t.Run("should error if tss not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(&chains.Chain{ + ChainName: 5, + ChainId: 5, + }) + observerMock.On("GetChainNonces", mock.Anything, mock.Anything).Return(observertypes.ChainNonces{ + Nonce: 100, + }, true) + observerMock.On("GetTSS", mock.Anything).Return(observertypes.TSS{}, false) + cctx := types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + Amount: sdkmath.ZeroUint(), + }, + OutboundTxParams: []*types.OutboundTxParams{ + {Amount: sdkmath.NewUint(1)}, + }, + } + err := k.UpdateNonce(ctx, 5, &cctx) + require.Error(t, err) + require.Equal(t, uint64(100), cctx.GetCurrentOutTxParam().OutboundTxTssNonce) + }) + + t.Run("should error if pending nonces not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(&chains.Chain{ + ChainName: 5, + ChainId: 5, + }) + observerMock.On("GetChainNonces", mock.Anything, mock.Anything).Return(observertypes.ChainNonces{ + Nonce: 100, + }, true) + observerMock.On("GetTSS", mock.Anything).Return(observertypes.TSS{}, true) + observerMock.On("GetPendingNonces", mock.Anything, mock.Anything, mock.Anything).Return(observertypes.PendingNonces{}, false) + + cctx := types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + Amount: sdkmath.ZeroUint(), + }, + OutboundTxParams: []*types.OutboundTxParams{ + {Amount: sdkmath.NewUint(1)}, + }, + } + err := k.UpdateNonce(ctx, 5, &cctx) + require.Error(t, err) + }) + + t.Run("should error if nonces not equal", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(&chains.Chain{ + ChainName: 5, + ChainId: 5, + }) + observerMock.On("GetChainNonces", mock.Anything, mock.Anything).Return(observertypes.ChainNonces{ + Nonce: 100, + }, true) + observerMock.On("GetTSS", mock.Anything).Return(observertypes.TSS{}, true) + observerMock.On("GetPendingNonces", mock.Anything, mock.Anything, mock.Anything).Return(observertypes.PendingNonces{ + NonceHigh: 99, + }, true) + + cctx := types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + Amount: sdkmath.ZeroUint(), + }, + OutboundTxParams: []*types.OutboundTxParams{ + {Amount: sdkmath.NewUint(1)}, + }, + } + err := k.UpdateNonce(ctx, 5, &cctx) + require.Error(t, err) + }) + + t.Run("should update nonces", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(&chains.Chain{ + ChainName: 5, + ChainId: 5, + }) + observerMock.On("GetChainNonces", mock.Anything, mock.Anything).Return(observertypes.ChainNonces{ + Nonce: 100, + }, true) + observerMock.On("GetTSS", mock.Anything).Return(observertypes.TSS{}, true) + observerMock.On("GetPendingNonces", mock.Anything, mock.Anything, mock.Anything).Return(observertypes.PendingNonces{ + NonceHigh: 100, + }, true) + + observerMock.On("SetChainNonces", mock.Anything, mock.Anything).Once() + observerMock.On("SetPendingNonces", mock.Anything, mock.Anything).Once() + + cctx := types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + Amount: sdkmath.ZeroUint(), + }, + OutboundTxParams: []*types.OutboundTxParams{ + {Amount: sdkmath.NewUint(1)}, + }, + } + err := k.UpdateNonce(ctx, 5, &cctx) + require.NoError(t, err) + }) +} 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..f82872566a 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,24 +100,169 @@ 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: "", + cctx, + ) + require.NoError(t, err) + require.False(t, reverted) + fungibleMock.AssertExpectations(t) + }) + + t.Run("should error on processing ERC20 deposit calling fungible method for contract call if process logs fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + }) + + senderChain := getValidEthChainID(t) + + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + + // expect DepositCoinZeta to be called + // ZRC20DepositAndCallContract(ctx, from, to, msg.Amount.BigInt(), senderChain, msg.Message, contract, data, msg.CoinType, msg.Asset) + fungibleMock.On( + "ZRC20DepositAndCallContract", + ctx, + mock.Anything, + receiver, + amount, + senderChain, + mock.Anything, + coin.CoinType_ERC20, + mock.Anything, + ).Return(&evmtypes.MsgEthereumTxResponse{ + Logs: []*evmtypes.Log{ + { + Address: receiver.Hex(), + Topics: []string{}, + Data: []byte{}, + BlockNumber: uint64(ctx.BlockHeight()), + TxHash: sample.Hash().Hex(), + TxIndex: 1, + BlockHash: sample.Hash().Hex(), + Index: 1, + }, }, + }, true, nil) + + fungibleMock.On("GetSystemContract", mock.Anything).Return(fungibletypes.SystemContract{}, false) + + // call HandleEVMDeposit + cctx := sample.CrossChainTx(t, "foo") + cctx.InboundTxParams.TxOrigin = "" + 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, + cctx, + ) + require.Error(t, err) + require.False(t, reverted) + fungibleMock.AssertExpectations(t) + }) + + t.Run("can process ERC20 deposit calling fungible method for contract call if process logs doesnt fail", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + }) + + senderChain := getValidEthChainID(t) + + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + receiver := sample.EthAddress() + amount := big.NewInt(42) + + // expect DepositCoinZeta to be called + // ZRC20DepositAndCallContract(ctx, from, to, msg.Amount.BigInt(), senderChain, msg.Message, contract, data, msg.CoinType, msg.Asset) + fungibleMock.On( + "ZRC20DepositAndCallContract", + ctx, + mock.Anything, + receiver, + amount, senderChain, + mock.Anything, + coin.CoinType_ERC20, + mock.Anything, + ).Return(&evmtypes.MsgEthereumTxResponse{ + Logs: []*evmtypes.Log{ + { + Address: receiver.Hex(), + Topics: []string{}, + Data: []byte{}, + BlockNumber: uint64(ctx.BlockHeight()), + TxHash: sample.Hash().Hex(), + TxIndex: 1, + BlockHash: sample.Hash().Hex(), + Index: 1, + }, + }, + }, true, nil) + + fungibleMock.On("GetSystemContract", mock.Anything).Return(fungibletypes.SystemContract{ + ConnectorZevm: sample.EthAddress().Hex(), + }, true) + + // call HandleEVMDeposit + cctx := sample.CrossChainTx(t, "foo") + cctx.InboundTxParams.TxOrigin = "" + 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, + cctx, ) require.NoError(t, err) require.False(t, reverted) fungibleMock.AssertExpectations(t) }) + t.Run("should error if invalid sender", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + }) + + receiver := sample.EthAddress() + amount := big.NewInt(42) + + // call HandleEVMDeposit + cctx := sample.CrossChainTx(t, "foo") + cctx.InboundTxParams.TxOrigin = "" + cctx.GetCurrentOutTxParam().Receiver = receiver.String() + cctx.GetInboundTxParams().Amount = math.NewUintFromBigInt(amount) + cctx.GetInboundTxParams().CoinType = coin.CoinType_ERC20 + cctx.GetInboundTxParams().Sender = "invalid" + cctx.GetInboundTxParams().SenderChainId = 987 + cctx.RelayedMessage = "" + cctx.GetInboundTxParams().Asset = "" + reverted, err := k.HandleEVMDeposit( + ctx, + cctx, + ) + require.Error(t, err) + require.False(t, reverted) + }) + t.Run("should return error with non-reverted if deposit ERC20 fails with tx non-failed", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ UseFungibleMock: true, @@ -146,18 +290,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 +334,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 +377,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 +420,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 +461,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 +484,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) }) @@ -374,6 +512,10 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { data, err := hex.DecodeString("DEADBEEF") require.NoError(t, err) + cctx := sample.CrossChainTx(t, "foo") + b, err := cctx.Marshal() + require.NoError(t, err) + ctx = ctx.WithTxBytes(b) fungibleMock.On( "ZRC20DepositAndCallContract", ctx, @@ -386,22 +528,21 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{}, false, nil) + 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) fungibleMock.AssertExpectations(t) + require.Equal(t, uint64(ctx.BlockHeight()), cctx.GetCurrentOutTxParam().OutboundTxObservedExternalHeight) }) t.Run("should deposit into receiver with specified data if no address parsed with data", func(t *testing.T) { @@ -429,18 +570,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/foreign_coins_test.go b/x/crosschain/keeper/foreign_coins_test.go new file mode 100644 index 0000000000..5d145b08f1 --- /dev/null +++ b/x/crosschain/keeper/foreign_coins_test.go @@ -0,0 +1,20 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" +) + +func TestKeeper_GetAllForeignCoins(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + fc := sample.ForeignCoins(t, sample.EthAddress().Hex()) + fc.ForeignChainId = 101 + k.GetFungibleKeeper().SetForeignCoins(ctx, fc) + + res, err := k.GetAllForeignCoins(ctx) + require.NoError(t, err) + require.Equal(t, 1, len(res)) +} 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/grpc_query_cctx_test.go b/x/crosschain/keeper/grpc_query_cctx_test.go index 6865d6d51e..59b0b68863 100644 --- a/x/crosschain/keeper/grpc_query_cctx_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_test.go @@ -64,7 +64,6 @@ func createCctxWithNonceRange( } func TestKeeper_CctxListPending(t *testing.T) { - t.Run("should fail for empty req", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) _, err := k.CctxListPending(ctx, nil) @@ -155,4 +154,133 @@ func TestKeeper_CctxListPending(t *testing.T) { // pending nonce + 2 require.EqualValues(t, uint64(1002), res.TotalPending) }) + + t.Run("error if some before low nonce are missing", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + chainID := getValidEthChainID(t) + tss := sample.Tss() + zk.ObserverKeeper.SetTSS(ctx, tss) + cctxs := createCctxWithNonceRange(t, ctx, *k, 1000, 2000, chainID, tss, zk) + + // set some cctxs as pending below nonce + cctx1, found := k.GetCrossChainTx(ctx, sample.GetCctxIndexFromString("1337-940")) + require.True(t, found) + cctx1.CctxStatus.Status = types.CctxStatus_PendingOutbound + k.SetCrossChainTx(ctx, cctx1) + + cctx2, found := k.GetCrossChainTx(ctx, sample.GetCctxIndexFromString("1337-955")) + require.True(t, found) + cctx2.CctxStatus.Status = types.CctxStatus_PendingOutbound + k.SetCrossChainTx(ctx, cctx2) + + res, err := k.CctxListPending(ctx, &types.QueryListCctxPendingRequest{ChainId: chainID, Limit: 100}) + require.NoError(t, err) + require.Equal(t, 100, len(res.CrossChainTx)) + + expectedCctxs := append([]*types.CrossChainTx{&cctx1, &cctx2}, cctxs[0:98]...) + require.EqualValues(t, expectedCctxs, res.CrossChainTx) + + // pending nonce + 2 + require.EqualValues(t, uint64(1002), res.TotalPending) + }) +} + +func TestKeeper_ZetaAccounting(t *testing.T) { + t.Run("should error if zeta accounting not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + res, err := k.ZetaAccounting(ctx, nil) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should return zeta accounting if found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + k.SetZetaAccounting(ctx, types.ZetaAccounting{ + AbortedZetaAmount: sdk.NewUint(100), + }) + + res, err := k.ZetaAccounting(ctx, nil) + require.NoError(t, err) + require.Equal(t, &types.QueryZetaAccountingResponse{ + AbortedZetaAmount: sdk.NewUint(100).String(), + }, res) + }) +} + +func TestKeeper_CctxByNonce(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + res, err := k.CctxByNonce(ctx, nil) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if tss not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + res, err := k.CctxByNonce(ctx, &types.QueryGetCctxByNonceRequest{ + ChainID: 1, + }) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if nonce to cctx not found", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + chainID := getValidEthChainID(t) + tss := sample.Tss() + zk.ObserverKeeper.SetTSS(ctx, tss) + + res, err := k.CctxByNonce(ctx, &types.QueryGetCctxByNonceRequest{ + ChainID: chainID, + }) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if crosschain tx not found", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + chainID := getValidEthChainID(t) + tss := sample.Tss() + zk.ObserverKeeper.SetTSS(ctx, tss) + nonce := 1000 + cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", chainID, nonce)) + + zk.ObserverKeeper.SetNonceToCctx(ctx, observertypes.NonceToCctx{ + ChainId: chainID, + Nonce: int64(nonce), + CctxIndex: cctx.Index, + Tss: tss.TssPubkey, + }) + + res, err := k.CctxByNonce(ctx, &types.QueryGetCctxByNonceRequest{ + ChainID: chainID, + Nonce: uint64(nonce), + }) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should return if crosschain tx found", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + chainID := getValidEthChainID(t) + tss := sample.Tss() + zk.ObserverKeeper.SetTSS(ctx, tss) + nonce := 1000 + cctx := sample.CrossChainTx(t, fmt.Sprintf("%d-%d", chainID, nonce)) + + zk.ObserverKeeper.SetNonceToCctx(ctx, observertypes.NonceToCctx{ + ChainId: chainID, + Nonce: int64(nonce), + CctxIndex: cctx.Index, + Tss: tss.TssPubkey, + }) + k.SetCrossChainTx(ctx, *cctx) + + res, err := k.CctxByNonce(ctx, &types.QueryGetCctxByNonceRequest{ + ChainID: chainID, + Nonce: uint64(nonce), + }) + require.NoError(t, err) + require.Equal(t, cctx, res.CrossChainTx) + }) } diff --git a/x/crosschain/keeper/grpc_query_gas_price_test.go b/x/crosschain/keeper/grpc_query_gas_price_test.go index cc48e2f3b0..b8e9f0606e 100644 --- a/x/crosschain/keeper/grpc_query_gas_price_test.go +++ b/x/crosschain/keeper/grpc_query_gas_price_test.go @@ -1,6 +1,7 @@ package keeper import ( + "fmt" "testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -37,15 +38,20 @@ func TestGasPriceQuerySingle(t *testing.T) { err: status.Error(codes.InvalidArgument, "not found"), }, { - desc: "InvalidRequest", + desc: "InvalidRequest nil", err: status.Error(codes.InvalidArgument, "invalid request"), }, + { + desc: "InvalidRequest index", + request: &types.QueryGetGasPriceRequest{Index: "abc"}, + err: fmt.Errorf("strconv.Atoi: parsing \"abc\": invalid syntax"), + }, } { tc := tc t.Run(tc.desc, func(t *testing.T) { response, err := keeper.GasPrice(wctx, tc.request) if tc.err != nil { - require.ErrorIs(t, err, tc.err) + require.Error(t, err) } else { require.Equal(t, tc.response, response) } diff --git a/x/crosschain/keeper/grpc_query_in_tx_tracker_test.go b/x/crosschain/keeper/grpc_query_in_tx_tracker_test.go new file mode 100644 index 0000000000..3f1d1738fa --- /dev/null +++ b/x/crosschain/keeper/grpc_query_in_tx_tracker_test.go @@ -0,0 +1,49 @@ +package keeper_test + +import ( + "testing" + + "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" +) + +func TestKeeper_InTxTrackerAllByChain(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + k.SetInTxTracker(ctx, types.InTxTracker{ + ChainId: 1, + TxHash: sample.Hash().Hex(), + CoinType: coin.CoinType_Gas, + }) + k.SetInTxTracker(ctx, types.InTxTracker{ + ChainId: 2, + TxHash: sample.Hash().Hex(), + CoinType: coin.CoinType_Gas, + }) + + res, err := k.InTxTrackerAllByChain(ctx, &types.QueryAllInTxTrackerByChainRequest{ + ChainId: 1, + }) + require.NoError(t, err) + require.Equal(t, 1, len(res.InTxTracker)) +} + +func TestKeeper_InTxTrackerAll(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + k.SetInTxTracker(ctx, types.InTxTracker{ + ChainId: 1, + TxHash: sample.Hash().Hex(), + CoinType: coin.CoinType_Gas, + }) + k.SetInTxTracker(ctx, types.InTxTracker{ + ChainId: 2, + TxHash: sample.Hash().Hex(), + CoinType: coin.CoinType_Gas, + }) + + res, err := k.InTxTrackerAll(ctx, &types.QueryAllInTxTrackersRequest{}) + require.NoError(t, err) + require.Equal(t, 2, len(res.InTxTracker)) +} diff --git a/x/crosschain/keeper/grpc_query_last_block_height.go b/x/crosschain/keeper/grpc_query_last_block_height.go index e30a113fb8..633c440dc9 100644 --- a/x/crosschain/keeper/grpc_query_last_block_height.go +++ b/x/crosschain/keeper/grpc_query_last_block_height.go @@ -2,11 +2,11 @@ package keeper import ( "context" + "math" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" - math2 "github.com/ethereum/go-ethereum/common/math" "github.com/zeta-chain/zetacore/x/crosschain/types" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -50,10 +50,10 @@ func (k Keeper) LastBlockHeight(c context.Context, req *types.QueryGetLastBlockH if !found { return nil, status.Error(codes.InvalidArgument, "not found") } - if val.LastSendHeight >= math2.MaxInt64 { + if val.LastSendHeight >= math.MaxInt64 { return nil, status.Error(codes.OutOfRange, "invalid last send height") } - if val.LastReceiveHeight >= math2.MaxInt64 { + if val.LastReceiveHeight >= math.MaxInt64 { return nil, status.Error(codes.OutOfRange, "invalid last recv height") } diff --git a/x/crosschain/keeper/grpc_query_last_block_height_test.go b/x/crosschain/keeper/grpc_query_last_block_height_test.go index f2747bf7c9..057fe120b9 100644 --- a/x/crosschain/keeper/grpc_query_last_block_height_test.go +++ b/x/crosschain/keeper/grpc_query_last_block_height_test.go @@ -1,6 +1,7 @@ package keeper import ( + "math" "testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -53,6 +54,39 @@ func TestLastBlockHeightQuerySingle(t *testing.T) { } } +func TestLastBlockHeightLimits(t *testing.T) { + t.Run("should err if last send height is max int", func(t *testing.T) { + keeper, ctx := setupKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + keeper.SetLastBlockHeight(ctx, types.LastBlockHeight{ + Index: "index", + LastSendHeight: math.MaxInt64, + }) + + res, err := keeper.LastBlockHeight(wctx, &types.QueryGetLastBlockHeightRequest{ + Index: "index", + }) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should err if last receive height is max int", func(t *testing.T) { + keeper, ctx := setupKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + keeper.SetLastBlockHeight(ctx, types.LastBlockHeight{ + Index: "index", + LastSendHeight: 10, + LastReceiveHeight: math.MaxInt64, + }) + + res, err := keeper.LastBlockHeight(wctx, &types.QueryGetLastBlockHeightRequest{ + Index: "index", + }) + require.Nil(t, res) + require.Error(t, err) + }) +} + func TestLastBlockHeightQueryPaginated(t *testing.T) { keeper, ctx := setupKeeper(t) wctx := sdk.WrapSDKContext(ctx) diff --git a/x/crosschain/keeper/grpc_query_last_zeta_height.go b/x/crosschain/keeper/grpc_query_last_zeta_height.go index 2d62202751..ff1639d8dd 100644 --- a/x/crosschain/keeper/grpc_query_last_zeta_height.go +++ b/x/crosschain/keeper/grpc_query_last_zeta_height.go @@ -2,7 +2,6 @@ package keeper import ( "context" - "math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/zeta-chain/zetacore/x/crosschain/types" @@ -17,7 +16,7 @@ func (k Keeper) LastZetaHeight(goCtx context.Context, req *types.QueryLastZetaHe ctx := sdk.UnwrapSDKContext(goCtx) height := ctx.BlockHeight() - if height >= math.MaxInt64 || height < 0 { + if height < 0 { return nil, status.Error(codes.OutOfRange, "height out of range") } return &types.QueryLastZetaHeightResponse{ diff --git a/x/crosschain/keeper/grpc_query_last_zeta_height_test.go b/x/crosschain/keeper/grpc_query_last_zeta_height_test.go new file mode 100644 index 0000000000..b34a96be20 --- /dev/null +++ b/x/crosschain/keeper/grpc_query_last_zeta_height_test.go @@ -0,0 +1,39 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func TestKeeper_LastZetaHeight(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + res, err := k.LastZetaHeight(ctx, nil) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if height less than zero", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + ctx = ctx.WithBlockHeight(-1) + res, err := k.LastZetaHeight(ctx, &types.QueryLastZetaHeightRequest{}) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should return height if gte 0", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + ctx = ctx.WithBlockHeight(0) + res, err := k.LastZetaHeight(ctx, &types.QueryLastZetaHeightRequest{}) + require.NoError(t, err) + require.Equal(t, int64(0), res.Height) + + ctx = ctx.WithBlockHeight(5) + res, err = k.LastZetaHeight(ctx, &types.QueryLastZetaHeightRequest{}) + require.NoError(t, err) + require.Equal(t, int64(5), res.Height) + }) +} diff --git a/x/crosschain/keeper/grpc_query_out_tx_tracker_test.go b/x/crosschain/keeper/grpc_query_out_tx_tracker_test.go index 39f5ad3673..c0c5db7e9b 100644 --- a/x/crosschain/keeper/grpc_query_out_tx_tracker_test.go +++ b/x/crosschain/keeper/grpc_query_out_tx_tracker_test.go @@ -1,108 +1,88 @@ package keeper_test -//func TestOutTxTrackerQuerySingle(t *testing.T) { -// keeper, ctx := keepertest.ZetacoreKeeper(t) -// wctx := sdk.WrapSDKContext(ctx) -// msgs := createNOutTxTracker(keeper, ctx, 2) -// for _, tc := range []struct { -// desc string -// request *types.QueryGetOutTxTrackerRequest -// response *types.QueryGetOutTxTrackerResponse -// err error -// }{ -// { -// desc: "First", -// request: &types.QueryGetOutTxTrackerRequest{ -// Index: msgs[0].Index, -// }, -// response: &types.QueryGetOutTxTrackerResponse{OutTxTracker: msgs[0]}, -// }, -// { -// desc: "Second", -// request: &types.QueryGetOutTxTrackerRequest{ -// Index: msgs[1].Index, -// }, -// response: &types.QueryGetOutTxTrackerResponse{OutTxTracker: msgs[1]}, -// }, -// { -// desc: "KeyNotFound", -// request: &types.QueryGetOutTxTrackerRequest{ -// Index: strconv.Itoa(100000), -// }, -// err: status.Error(codes.NotFound, "not found"), -// }, -// { -// desc: "InvalidRequest", -// err: status.Error(codes.InvalidArgument, "invalid request"), -// }, -// } { -// t.Run(tc.desc, func(t *testing.T) { -// response, err := keeper.OutTxTracker(wctx, tc.request) -// if tc.err != nil { -// require.ErrorIs(t, err, tc.err) -// } else { -// require.NoError(t, err) -// require.Equal(t, -// nullify.Fill(tc.response), -// nullify.Fill(response), -// ) -// } -// }) -// } -//} -// -//func TestOutTxTrackerQueryPaginated(t *testing.T) { -// keeper, ctx := keepertest.ZetacoreKeeper(t) -// wctx := sdk.WrapSDKContext(ctx) -// msgs := createNOutTxTracker(keeper, ctx, 5) -// -// request := func(next []byte, offset, limit uint64, total bool) *types.QueryAllOutTxTrackerRequest { -// return &types.QueryAllOutTxTrackerRequest{ -// Pagination: &query.PageRequest{ -// Key: next, -// Offset: offset, -// Limit: limit, -// CountTotal: total, -// }, -// } -// } -// t.Run("ByOffset", func(t *testing.T) { -// step := 2 -// for i := 0; i < len(msgs); i += step { -// resp, err := keeper.OutTxTrackerAll(wctx, request(nil, uint64(i), uint64(step), false)) -// require.NoError(t, err) -// require.LessOrEqual(t, len(resp.OutTxTracker), step) -// require.Subset(t, -// nullify.Fill(msgs), -// nullify.Fill(resp.OutTxTracker), -// ) -// } -// }) -// t.Run("ByKey", func(t *testing.T) { -// step := 2 -// var next []byte -// for i := 0; i < len(msgs); i += step { -// resp, err := keeper.OutTxTrackerAll(wctx, request(next, 0, uint64(step), false)) -// require.NoError(t, err) -// require.LessOrEqual(t, len(resp.OutTxTracker), step) -// require.Subset(t, -// nullify.Fill(msgs), -// nullify.Fill(resp.OutTxTracker), -// ) -// next = resp.Pagination.NextKey -// } -// }) -// t.Run("Total", func(t *testing.T) { -// resp, err := keeper.OutTxTrackerAll(wctx, request(nil, 0, 0, true)) -// require.NoError(t, err) -// require.Equal(t, len(msgs), int(resp.Pagination.Total)) -// require.ElementsMatch(t, -// nullify.Fill(msgs), -// nullify.Fill(resp.OutTxTracker), -// ) -// }) -// t.Run("InvalidRequest", func(t *testing.T) { -// _, err := keeper.OutTxTrackerAll(wctx, nil) -// require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "invalid request")) -// }) -//} +import ( + "testing" + + "github.com/stretchr/testify/require" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func TestKeeper_OutTxTrackerAllByChain(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + res, err := k.OutTxTrackerAllByChain(ctx, nil) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should return if req is not nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + k.SetOutTxTracker(ctx, types.OutTxTracker{ + ChainId: 1, + }) + k.SetOutTxTracker(ctx, types.OutTxTracker{ + ChainId: 2, + }) + + res, err := k.OutTxTrackerAllByChain(ctx, &types.QueryAllOutTxTrackerByChainRequest{ + Chain: 1, + }) + require.NoError(t, err) + require.Equal(t, 1, len(res.OutTxTracker)) + }) +} + +func TestKeeper_OutTxTrackerAll(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + res, err := k.OutTxTrackerAll(ctx, nil) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should return if req is not nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + k.SetOutTxTracker(ctx, types.OutTxTracker{ + ChainId: 1, + }) + + res, err := k.OutTxTrackerAll(ctx, &types.QueryAllOutTxTrackerRequest{}) + require.NoError(t, err) + require.Equal(t, 1, len(res.OutTxTracker)) + }) +} + +func TestKeeper_OutTxTracker(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + res, err := k.OutTxTracker(ctx, nil) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should return if not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + res, err := k.OutTxTracker(ctx, &types.QueryGetOutTxTrackerRequest{ + ChainID: 1, + Nonce: 1, + }) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should return if req is not nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + k.SetOutTxTracker(ctx, types.OutTxTracker{ + ChainId: 1, + Nonce: 1, + }) + + res, err := k.OutTxTracker(ctx, &types.QueryGetOutTxTrackerRequest{ + ChainID: 1, + Nonce: 1, + }) + require.NoError(t, err) + require.NotNil(t, res) + }) +} diff --git a/x/crosschain/keeper/grpc_query_zeta_conversion_rate_test.go b/x/crosschain/keeper/grpc_query_zeta_conversion_rate_test.go new file mode 100644 index 0000000000..f6baf0c625 --- /dev/null +++ b/x/crosschain/keeper/grpc_query_zeta_conversion_rate_test.go @@ -0,0 +1,120 @@ +package keeper_test + +import ( + "errors" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func TestKeeper_ConvertGasToZeta(t *testing.T) { + t.Run("should err if chain not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + res, err := k.ConvertGasToZeta(ctx, &types.QueryConvertGasToZetaRequest{ + ChainId: 987, + }) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should err if median price not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + res, err := k.ConvertGasToZeta(ctx, &types.QueryConvertGasToZetaRequest{ + ChainId: 5, + }) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should err if zrc20 not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + }) + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + fungibleMock.On("QuerySystemContractGasCoinZRC20", mock.Anything, mock.Anything). + Return(common.Address{}, errors.New("err")) + + k.SetGasPrice(ctx, types.GasPrice{ + ChainId: 5, + MedianIndex: 0, + Prices: []uint64{2}, + }) + + res, err := k.ConvertGasToZeta(ctx, &types.QueryConvertGasToZetaRequest{ + ChainId: 5, + GasLimit: "10", + }) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should err if uniswap2router not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + }) + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + fungibleMock.On("QuerySystemContractGasCoinZRC20", mock.Anything, mock.Anything). + Return(sample.EthAddress(), nil) + + fungibleMock.On("QueryUniswapV2RouterGetZetaAmountsIn", mock.Anything, mock.Anything, mock.Anything). + Return(big.NewInt(0), errors.New("err")) + + k.SetGasPrice(ctx, types.GasPrice{ + ChainId: 5, + MedianIndex: 0, + Prices: []uint64{2}, + }) + + res, err := k.ConvertGasToZeta(ctx, &types.QueryConvertGasToZetaRequest{ + ChainId: 5, + GasLimit: "10", + }) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should return if all is set", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + }) + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + fungibleMock.On("QuerySystemContractGasCoinZRC20", mock.Anything, mock.Anything). + Return(sample.EthAddress(), nil) + + fungibleMock.On("QueryUniswapV2RouterGetZetaAmountsIn", mock.Anything, mock.Anything, mock.Anything). + Return(big.NewInt(5), nil) + + k.SetGasPrice(ctx, types.GasPrice{ + ChainId: 5, + MedianIndex: 0, + Prices: []uint64{2}, + }) + + res, err := k.ConvertGasToZeta(ctx, &types.QueryConvertGasToZetaRequest{ + ChainId: 5, + GasLimit: "10", + }) + require.NoError(t, err) + require.Equal(t, &types.QueryConvertGasToZetaResponse{ + OutboundGasInZeta: "5", + ProtocolFeeInZeta: types.GetProtocolFee().String(), + // #nosec G701 always positive + ZetaBlockHeight: uint64(ctx.BlockHeight()), + }, res) + }) +} + +func TestKeeper_ProtocolFee(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + res, err := k.ProtocolFee(ctx, nil) + require.NoError(t, err) + require.Equal(t, &types.QueryMessagePassingProtocolFeeResponse{ + FeeInZeta: types.GetProtocolFee().String(), + }, res) +} diff --git a/x/crosschain/keeper/msg_server_add_to_intx_tracker_test.go b/x/crosschain/keeper/msg_server_add_to_intx_tracker_test.go index dc89a3534f..5ab8eba308 100644 --- a/x/crosschain/keeper/msg_server_add_to_intx_tracker_test.go +++ b/x/crosschain/keeper/msg_server_add_to_intx_tracker_test.go @@ -67,7 +67,30 @@ func TestMsgServer_AddToInTxTracker(t *testing.T) { require.False(t, found) }) - t.Run("admin add tx tracker", func(t *testing.T) { + t.Run("fail for unsupported chain id", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + tx_hash := "string" + + chainID := getValidEthChainID(t) + setSupportedChain(ctx, zk, chainID) + + msgServer := keeper.NewMsgServerImpl(*k) + + _, err := msgServer.AddToInTxTracker(ctx, &types.MsgAddToInTxTracker{ + Creator: sample.AccAddress(), + ChainId: chainID + 1, + TxHash: tx_hash, + CoinType: coin.CoinType_Zeta, + Proof: nil, + BlockHash: "", + TxIndex: 0, + }) + require.ErrorIs(t, err, observertypes.ErrSupportedChains) + _, found := k.GetInTxTracker(ctx, chainID, tx_hash) + require.False(t, found) + }) + + t.Run("admin add tx tracker", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ UseAuthorityMock: true, }) diff --git a/x/crosschain/keeper/msg_server_add_to_outtx_tracker_test.go b/x/crosschain/keeper/msg_server_add_to_outtx_tracker_test.go index 24174733b0..010486017d 100644 --- a/x/crosschain/keeper/msg_server_add_to_outtx_tracker_test.go +++ b/x/crosschain/keeper/msg_server_add_to_outtx_tracker_test.go @@ -15,7 +15,6 @@ import ( func getEthereumChainID() int64 { return 5 // Goerli - } // setEnabledChain sets the chain as enabled in chain params @@ -32,7 +31,7 @@ func setEnabledChain(ctx sdk.Context, zk keepertest.ZetaKeepers, chainID int64) } // setupTssAndNonceToCctx sets tss and nonce to cctx -func setupTssAndNonceToCctx(k *keeper.Keeper, ctx sdk.Context, chainId, nonce int64) { +func setupTssAndNonceToCctx(k *keeper.Keeper, ctx sdk.Context, chainId, nonce int64, status types.CctxStatus) { tssPubKey := "zetapub1addwnpepq28c57cvcs0a2htsem5zxr6qnlvq9mzhmm76z3jncsnzz32rclangr2g35p" k.GetObserverKeeper().SetTSS(ctx, observertypes.TSS{ TssPubkey: tssPubKey, @@ -47,7 +46,7 @@ func setupTssAndNonceToCctx(k *keeper.Keeper, ctx sdk.Context, chainId, nonce in Creator: "any", Index: "0x123", CctxStatus: &types.Status{ - Status: types.CctxStatus_PendingOutbound, + Status: status, }, } k.SetCrossChainTx(ctx, cctx) @@ -70,7 +69,7 @@ func TestMsgServer_AddToOutTxTracker(t *testing.T) { keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupEmergency, true) chainID := getEthereumChainID() - setupTssAndNonceToCctx(k, ctx, chainID, 0) + setupTssAndNonceToCctx(k, ctx, chainID, 0, types.CctxStatus_PendingOutbound) setEnabledChain(ctx, zk, chainID) msgServer := keeper.NewMsgServerImpl(*k) @@ -89,6 +88,106 @@ func TestMsgServer_AddToOutTxTracker(t *testing.T) { require.True(t, found) }) + t.Run("should return early if cctx not pending", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + + admin := sample.AccAddress() + chainID := getEthereumChainID() + setupTssAndNonceToCctx(k, ctx, chainID, 0, types.CctxStatus_OutboundMined) + setEnabledChain(ctx, zk, chainID) + + msgServer := keeper.NewMsgServerImpl(*k) + + res, err := msgServer.AddToOutTxTracker(ctx, &types.MsgAddToOutTxTracker{ + Creator: admin, + ChainId: chainID, + TxHash: sample.Hash().Hex(), + Proof: nil, + BlockHash: "", + TxIndex: 0, + Nonce: 0, + }) + require.NoError(t, err) + require.Equal(t, &types.MsgAddToOutTxTrackerResponse{IsRemoved: true}, res) + _, found := k.GetOutTxTracker(ctx, chainID, 0) + require.False(t, found) + }) + + t.Run("should error for unsupported chain", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + + admin := sample.AccAddress() + + chainID := getEthereumChainID() + setupTssAndNonceToCctx(k, ctx, chainID, 0, types.CctxStatus_PendingOutbound) + setEnabledChain(ctx, zk, chainID) + + msgServer := keeper.NewMsgServerImpl(*k) + + _, err := msgServer.AddToOutTxTracker(ctx, &types.MsgAddToOutTxTracker{ + Creator: admin, + ChainId: chainID + 1, + TxHash: sample.Hash().Hex(), + Proof: nil, + BlockHash: "", + TxIndex: 0, + Nonce: 0, + }) + require.Error(t, err) + }) + + t.Run("should error if no tss", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + + admin := sample.AccAddress() + + chainID := getEthereumChainID() + setEnabledChain(ctx, zk, chainID) + + msgServer := keeper.NewMsgServerImpl(*k) + + _, err := msgServer.AddToOutTxTracker(ctx, &types.MsgAddToOutTxTracker{ + Creator: admin, + ChainId: chainID, + TxHash: sample.Hash().Hex(), + Proof: nil, + BlockHash: "", + TxIndex: 0, + Nonce: 0, + }) + require.Error(t, err) + }) + + t.Run("should error if no cctx", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + + admin := sample.AccAddress() + + chainID := getEthereumChainID() + setEnabledChain(ctx, zk, chainID) + + msgServer := keeper.NewMsgServerImpl(*k) + + _, err := msgServer.AddToOutTxTracker(ctx, &types.MsgAddToOutTxTracker{ + Creator: admin, + ChainId: chainID, + TxHash: sample.Hash().Hex(), + Proof: nil, + BlockHash: "", + TxIndex: 0, + Nonce: 0, + }) + require.Error(t, err) + }) + t.Run("unable to add tracker admin exceeding maximum allowed length of hashlist without proof", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ UseAuthorityMock: true, @@ -99,7 +198,7 @@ func TestMsgServer_AddToOutTxTracker(t *testing.T) { keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupEmergency, true) chainID := getEthereumChainID() - setupTssAndNonceToCctx(k, ctx, chainID, 0) + setupTssAndNonceToCctx(k, ctx, chainID, 0, types.CctxStatus_PendingOutbound) setEnabledChain(ctx, zk, chainID) k.SetOutTxTracker(ctx, types.OutTxTracker{ diff --git a/x/crosschain/keeper/msg_server_gas_price_voter.go b/x/crosschain/keeper/msg_server_gas_price_voter.go index 308f895f59..52532c206f 100644 --- a/x/crosschain/keeper/msg_server_gas_price_voter.go +++ b/x/crosschain/keeper/msg_server_gas_price_voter.go @@ -25,14 +25,11 @@ func (k msgServer) GasPriceVoter(goCtx context.Context, msg *types.MsgGasPriceVo chain := k.zetaObserverKeeper.GetSupportedChainFromChainID(ctx, msg.ChainId) if chain == nil { - return nil, observertypes.ErrSupportedChains + return nil, cosmoserrors.Wrap(types.ErrUnsupportedChain, fmt.Sprintf("ChainID : %d ", msg.ChainId)) } if ok := k.zetaObserverKeeper.IsNonTombstonedObserver(ctx, msg.Creator); !ok { return nil, observertypes.ErrNotObserver } - if chain == nil { - return nil, cosmoserrors.Wrap(types.ErrUnsupportedChain, fmt.Sprintf("ChainID : %d ", msg.ChainId)) - } gasPrice, isFound := k.GetGasPrice(ctx, chain.ChainId) if !isFound { diff --git a/x/crosschain/keeper/msg_server_gas_price_voter_test.go b/x/crosschain/keeper/msg_server_gas_price_voter_test.go new file mode 100644 index 0000000000..50c11dbaa9 --- /dev/null +++ b/x/crosschain/keeper/msg_server_gas_price_voter_test.go @@ -0,0 +1,204 @@ +package keeper_test + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/mock" + "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/crosschain/keeper" + "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func TestMsgServer_GasPriceVoter(t *testing.T) { + t.Run("should error if unsupported chain", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(nil) + + msgServer := keeper.NewMsgServerImpl(*k) + + res, err := msgServer.GasPriceVoter(ctx, &types.MsgGasPriceVoter{ + ChainId: 5, + }) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if not non tombstoned observer", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(&chains.Chain{}) + observerMock.On("IsNonTombstonedObserver", mock.Anything, mock.Anything).Return(false) + + msgServer := keeper.NewMsgServerImpl(*k) + + res, err := msgServer.GasPriceVoter(ctx, &types.MsgGasPriceVoter{ + ChainId: 5, + }) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if gas price not found and set gas price in fungible keeper fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + UseFungibleMock: true, + }) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(&chains.Chain{ + ChainId: 5, + }) + observerMock.On("IsNonTombstonedObserver", mock.Anything, mock.Anything).Return(true) + + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + fungibleMock.On("SetGasPrice", mock.Anything, mock.Anything, mock.Anything).Return(uint64(0), errors.New("err")) + msgServer := keeper.NewMsgServerImpl(*k) + + res, err := msgServer.GasPriceVoter(ctx, &types.MsgGasPriceVoter{ + ChainId: 5, + }) + require.Error(t, err) + require.Nil(t, res) + _, found := k.GetGasPrice(ctx, 5) + require.True(t, found) + }) + + t.Run("should not error if gas price not found and set gas price in fungible keeper succeeds", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + UseFungibleMock: true, + }) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(&chains.Chain{ + ChainId: 5, + }) + observerMock.On("IsNonTombstonedObserver", mock.Anything, mock.Anything).Return(true) + + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + fungibleMock.On("SetGasPrice", mock.Anything, mock.Anything, mock.Anything).Return(uint64(1), nil) + msgServer := keeper.NewMsgServerImpl(*k) + creator := sample.AccAddress() + res, err := msgServer.GasPriceVoter(ctx, &types.MsgGasPriceVoter{ + Creator: creator, + ChainId: 5, + Price: 1, + BlockNumber: 1, + }) + require.NoError(t, err) + require.Empty(t, res) + gp, found := k.GetGasPrice(ctx, 5) + require.True(t, found) + require.Equal(t, types.GasPrice{ + Creator: creator, + Index: "5", + ChainId: 5, + Signers: []string{creator}, + BlockNums: []uint64{1}, + Prices: []uint64{1}, + MedianIndex: 0, + }, gp) + }) + + t.Run("should not error if gas price found and msg.creator in signers", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + UseFungibleMock: true, + }) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(&chains.Chain{ + ChainId: 5, + }) + observerMock.On("IsNonTombstonedObserver", mock.Anything, mock.Anything).Return(true) + + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + fungibleMock.On("SetGasPrice", mock.Anything, mock.Anything, mock.Anything).Return(uint64(1), nil) + msgServer := keeper.NewMsgServerImpl(*k) + + creator := sample.AccAddress() + k.SetGasPrice(ctx, types.GasPrice{ + Creator: creator, + ChainId: 5, + Signers: []string{creator}, + BlockNums: []uint64{1}, + Prices: []uint64{1}, + }) + + res, err := msgServer.GasPriceVoter(ctx, &types.MsgGasPriceVoter{ + Creator: creator, + ChainId: 5, + BlockNumber: 2, + Price: 2, + }) + require.NoError(t, err) + require.Empty(t, res) + gp, found := k.GetGasPrice(ctx, 5) + require.True(t, found) + require.Equal(t, types.GasPrice{ + Creator: creator, + Index: "", + ChainId: 5, + Signers: []string{creator}, + BlockNums: []uint64{2}, + Prices: []uint64{2}, + MedianIndex: 0, + }, gp) + }) + + t.Run("should not error if gas price found and msg.creator not in signers", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + UseFungibleMock: true, + }) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetSupportedChainFromChainID", mock.Anything, mock.Anything).Return(&chains.Chain{ + ChainId: 5, + }) + observerMock.On("IsNonTombstonedObserver", mock.Anything, mock.Anything).Return(true) + + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + fungibleMock.On("SetGasPrice", mock.Anything, mock.Anything, mock.Anything).Return(uint64(1), nil) + msgServer := keeper.NewMsgServerImpl(*k) + + creator := sample.AccAddress() + k.SetGasPrice(ctx, types.GasPrice{ + Creator: creator, + ChainId: 5, + BlockNums: []uint64{1}, + Prices: []uint64{1}, + }) + + res, err := msgServer.GasPriceVoter(ctx, &types.MsgGasPriceVoter{ + Creator: creator, + ChainId: 5, + BlockNumber: 2, + Price: 2, + }) + require.NoError(t, err) + require.Empty(t, res) + gp, found := k.GetGasPrice(ctx, 5) + require.True(t, found) + require.Equal(t, types.GasPrice{ + Creator: creator, + Index: "", + ChainId: 5, + Signers: []string{creator}, + BlockNums: []uint64{1, 2}, + Prices: []uint64{1, 2}, + MedianIndex: 1, + }, gp) + }) +} diff --git a/x/crosschain/keeper/msg_server_migrate_tss_funds.go b/x/crosschain/keeper/msg_server_migrate_tss_funds.go index 02ce1bf879..b9fddc9dae 100644 --- a/x/crosschain/keeper/msg_server_migrate_tss_funds.go +++ b/x/crosschain/keeper/msg_server_migrate_tss_funds.go @@ -41,6 +41,10 @@ func (k msgServer) MigrateTssFunds(goCtx context.Context, msg *types.MsgMigrateT } tssHistory := k.zetaObserverKeeper.GetAllTSS(ctx) + if len(tssHistory) == 0 { + return nil, errorsmod.Wrap(types.ErrCannotMigrateTssFunds, "empty TSS history") + } + sort.SliceStable(tssHistory, func(i, j int) bool { return tssHistory[i].FinalizedZetaHeight < tssHistory[j].FinalizedZetaHeight }) @@ -82,6 +86,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_migrate_tss_funds_test.go b/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go index d4a6c5206e..f13a0065ae 100644 --- a/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go +++ b/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go @@ -6,6 +6,7 @@ import ( sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/gas" @@ -78,7 +79,7 @@ func setupTssMigrationParams( } func TestKeeper_MigrateTSSFundsForChain(t *testing.T) { - t.Run("test gas price multiplier", func(t *testing.T) { + t.Run("test evm chain", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ UseAuthorityMock: true, }) @@ -106,11 +107,220 @@ func TestKeeper_MigrateTSSFundsForChain(t *testing.T) { multipliedValue, err := gas.MultiplyGasPrice(gp, crosschaintypes.TssMigrationGasMultiplierEVM) require.NoError(t, err) require.Equal(t, multipliedValue.String(), cctx.GetCurrentOutTxParam().OutboundTxGasPrice) + }) + + t.Run("test btc chain", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + admin := sample.AccAddress() + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + + msgServer := keeper.NewMsgServerImpl(*k) + chain := getValidBTCChain() + amount := sdkmath.NewUintFromString("10000000000000000000") + indexString, _ := setupTssMigrationParams(zk, k, ctx, *chain, amount, true, true) + gp, found := k.GetMedianGasPriceInUint(ctx, chain.ChainId) + require.True(t, found) + _, err := msgServer.MigrateTssFunds(ctx, &crosschaintypes.MsgMigrateTssFunds{ + Creator: admin, + ChainId: chain.ChainId, + Amount: amount, + }) + require.NoError(t, err) + hash := crypto.Keccak256Hash([]byte(indexString)) + index := hash.Hex() + cctx, found := k.GetCrossChainTx(ctx, index) + require.True(t, found) + require.Equal(t, gp.MulUint64(2).String(), cctx.GetCurrentOutTxParam().OutboundTxGasPrice) }) } func TestMsgServer_MigrateTssFunds(t *testing.T) { + t.Run("should error if not authorized", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + + admin := sample.AccAddress() + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, false) + + msgServer := keeper.NewMsgServerImpl(*k) + chain := getValidEthChain(t) + amount := sdkmath.NewUintFromString("10000000000000000000") + _, err := msgServer.MigrateTssFunds(ctx, &crosschaintypes.MsgMigrateTssFunds{ + Creator: admin, + ChainId: chain.ChainId, + Amount: amount, + }) + require.Error(t, err) + }) + + t.Run("should error if inbound enabled", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + UseObserverMock: true, + }) + + admin := sample.AccAddress() + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("IsInboundEnabled", mock.Anything).Return(true) + msgServer := keeper.NewMsgServerImpl(*k) + chain := getValidEthChain(t) + amount := sdkmath.NewUintFromString("10000000000000000000") + _, err := msgServer.MigrateTssFunds(ctx, &crosschaintypes.MsgMigrateTssFunds{ + Creator: admin, + ChainId: chain.ChainId, + Amount: amount, + }) + require.Error(t, err) + }) + + t.Run("should error if tss not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + UseObserverMock: true, + }) + + admin := sample.AccAddress() + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("IsInboundEnabled", mock.Anything).Return(false) + observerMock.On("GetTSS", mock.Anything).Return(observertypes.TSS{}, false) + + msgServer := keeper.NewMsgServerImpl(*k) + chain := getValidEthChain(t) + amount := sdkmath.NewUintFromString("10000000000000000000") + _, err := msgServer.MigrateTssFunds(ctx, &crosschaintypes.MsgMigrateTssFunds{ + Creator: admin, + ChainId: chain.ChainId, + Amount: amount, + }) + require.Error(t, err) + }) + + t.Run("should error if tss history empty", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + UseObserverMock: true, + }) + + admin := sample.AccAddress() + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("IsInboundEnabled", mock.Anything).Return(false) + observerMock.On("GetTSS", mock.Anything).Return(sample.Tss(), true) + observerMock.On("GetAllTSS", mock.Anything).Return([]observertypes.TSS{}) + + msgServer := keeper.NewMsgServerImpl(*k) + chain := getValidEthChain(t) + amount := sdkmath.NewUintFromString("10000000000000000000") + _, err := msgServer.MigrateTssFunds(ctx, &crosschaintypes.MsgMigrateTssFunds{ + Creator: admin, + ChainId: chain.ChainId, + Amount: amount, + }) + require.Error(t, err) + }) + + t.Run("should error if no new tss generated", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + UseObserverMock: true, + }) + + admin := sample.AccAddress() + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("IsInboundEnabled", mock.Anything).Return(false) + tss := sample.Tss() + observerMock.On("GetTSS", mock.Anything).Return(tss, true) + observerMock.On("GetAllTSS", mock.Anything).Return([]observertypes.TSS{tss}) + + msgServer := keeper.NewMsgServerImpl(*k) + chain := getValidEthChain(t) + amount := sdkmath.NewUintFromString("10000000000000000000") + _, err := msgServer.MigrateTssFunds(ctx, &crosschaintypes.MsgMigrateTssFunds{ + Creator: admin, + ChainId: chain.ChainId, + Amount: amount, + }) + require.Error(t, err) + }) + + t.Run("should error if current tss is the latest", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + UseObserverMock: true, + }) + + admin := sample.AccAddress() + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("IsInboundEnabled", mock.Anything).Return(false) + tss1 := sample.Tss() + tss1.FinalizedZetaHeight = 2 + tss2 := sample.Tss() + tss2.FinalizedZetaHeight = 1 + observerMock.On("GetTSS", mock.Anything).Return(tss1, true) + observerMock.On("GetAllTSS", mock.Anything).Return([]observertypes.TSS{tss2}) + + msgServer := keeper.NewMsgServerImpl(*k) + chain := getValidEthChain(t) + amount := sdkmath.NewUintFromString("10000000000000000000") + _, err := msgServer.MigrateTssFunds(ctx, &crosschaintypes.MsgMigrateTssFunds{ + Creator: admin, + ChainId: chain.ChainId, + Amount: amount, + }) + require.Error(t, err) + }) + + t.Run("should error if pending nonces not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + UseObserverMock: true, + }) + + admin := sample.AccAddress() + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("IsInboundEnabled", mock.Anything).Return(false) + tss1 := sample.Tss() + tss1.FinalizedZetaHeight = 2 + tss2 := sample.Tss() + tss2.FinalizedZetaHeight = 3 + observerMock.On("GetTSS", mock.Anything).Return(tss1, true) + observerMock.On("GetAllTSS", mock.Anything).Return([]observertypes.TSS{tss2}) + observerMock.On("GetPendingNonces", mock.Anything, mock.Anything, mock.Anything).Return(observertypes.PendingNonces{}, false) + + msgServer := keeper.NewMsgServerImpl(*k) + chain := getValidEthChain(t) + amount := sdkmath.NewUintFromString("10000000000000000000") + _, err := msgServer.MigrateTssFunds(ctx, &crosschaintypes.MsgMigrateTssFunds{ + Creator: admin, + ChainId: chain.ChainId, + Amount: amount, + }) + require.Error(t, err) + }) + t.Run("successfully create tss migration cctx", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ UseAuthorityMock: true, diff --git a/x/crosschain/keeper/msg_server_refund_aborted_tx_test.go b/x/crosschain/keeper/msg_server_refund_aborted_tx_test.go index 7d91e71b6c..f5615c8963 100644 --- a/x/crosschain/keeper/msg_server_refund_aborted_tx_test.go +++ b/x/crosschain/keeper/msg_server_refund_aborted_tx_test.go @@ -154,6 +154,70 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { require.True(t, c.CctxStatus.IsAbortRefunded) }) + t.Run("should error if aleady refunded", func(t *testing.T) { + k, ctx, sdkk, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + + admin := sample.AccAddress() + chainID := getValidEthChainID(t) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) + + msgServer := keeper.NewMsgServerImpl(*k) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + + cctx := sample.CrossChainTx(t, "sample-index") + cctx.CctxStatus.Status = crosschaintypes.CctxStatus_Aborted + cctx.CctxStatus.IsAbortRefunded = true + cctx.InboundTxParams.TxOrigin = cctx.InboundTxParams.Sender + cctx.InboundTxParams.SenderChainId = chainID + cctx.InboundTxParams.CoinType = coin.CoinType_Zeta + cctx.OutboundTxParams = nil + k.SetCrossChainTx(ctx, *cctx) + k.SetZetaAccounting(ctx, crosschaintypes.ZetaAccounting{AbortedZetaAmount: cctx.GetCurrentOutTxParam().Amount}) + deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) + + _, err := msgServer.RefundAbortedCCTX(ctx, &crosschaintypes.MsgRefundAbortedCCTX{ + Creator: admin, + CctxIndex: cctx.Index, + RefundAddress: cctx.InboundTxParams.Sender, + }) + require.Error(t, err) + }) + + t.Run("should error if refund fails", func(t *testing.T) { + k, ctx, sdkk, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + + admin := sample.AccAddress() + chainID := getValidEthChainID(t) + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) + + msgServer := keeper.NewMsgServerImpl(*k) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + + cctx := sample.CrossChainTx(t, "sample-index") + cctx.CctxStatus.Status = crosschaintypes.CctxStatus_Aborted + cctx.CctxStatus.IsAbortRefunded = false + cctx.InboundTxParams.TxOrigin = cctx.InboundTxParams.Sender + cctx.InboundTxParams.SenderChainId = chainID + cctx.InboundTxParams.CoinType = coin.CoinType_Cmd + cctx.OutboundTxParams = nil + k.SetCrossChainTx(ctx, *cctx) + k.SetZetaAccounting(ctx, crosschaintypes.ZetaAccounting{AbortedZetaAmount: cctx.GetCurrentOutTxParam().Amount}) + deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) + + _, err := msgServer.RefundAbortedCCTX(ctx, &crosschaintypes.MsgRefundAbortedCCTX{ + Creator: admin, + CctxIndex: cctx.Index, + RefundAddress: cctx.InboundTxParams.Sender, + }) + require.Error(t, err) + }) + t.Run("successfully refund to optional refund address if provided", func(t *testing.T) { k, ctx, sdkk, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ UseAuthorityMock: true, diff --git a/x/crosschain/keeper/msg_server_remove_out_tx_tracker_test.go b/x/crosschain/keeper/msg_server_remove_out_tx_tracker_test.go new file mode 100644 index 0000000000..e3b0a59b44 --- /dev/null +++ b/x/crosschain/keeper/msg_server_remove_out_tx_tracker_test.go @@ -0,0 +1,66 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + "github.com/zeta-chain/zetacore/x/crosschain/keeper" + "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func TestMsgServer_RemoveFromOutTxTracker(t *testing.T) { + t.Run("should error if not authorized", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + k.SetOutTxTracker(ctx, types.OutTxTracker{ + ChainId: 1, + Nonce: 1, + }) + + admin := sample.AccAddress() + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupEmergency, false) + + msgServer := keeper.NewMsgServerImpl(*k) + + res, err := msgServer.RemoveFromOutTxTracker(ctx, &types.MsgRemoveFromOutTxTracker{ + Creator: admin, + }) + require.Error(t, err) + require.Empty(t, res) + + _, found := k.GetOutTxTracker(ctx, 1, 1) + require.True(t, found) + }) + + t.Run("should remove if authorized", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + k.SetOutTxTracker(ctx, types.OutTxTracker{ + ChainId: 1, + Nonce: 1, + }) + + admin := sample.AccAddress() + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupEmergency, true) + + msgServer := keeper.NewMsgServerImpl(*k) + + res, err := msgServer.RemoveFromOutTxTracker(ctx, &types.MsgRemoveFromOutTxTracker{ + Creator: admin, + ChainId: 1, + Nonce: 1, + }) + require.NoError(t, err) + require.Empty(t, res) + + _, found := k.GetOutTxTracker(ctx, 1, 1) + require.False(t, found) + }) +} 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_update_tss_test.go b/x/crosschain/keeper/msg_server_update_tss_test.go index 0020cb32f8..56bfdc205f 100644 --- a/x/crosschain/keeper/msg_server_update_tss_test.go +++ b/x/crosschain/keeper/msg_server_update_tss_test.go @@ -13,6 +13,42 @@ import ( ) func TestMsgServer_UpdateTssAddress(t *testing.T) { + t.Run("should fail if not authorized", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + + admin := sample.AccAddress() + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, false) + + msgServer := keeper.NewMsgServerImpl(*k) + + _, err := msgServer.UpdateTssAddress(ctx, &crosschaintypes.MsgUpdateTssAddress{ + Creator: admin, + TssPubkey: "", + }) + require.Error(t, err) + }) + + t.Run("should fail if tss not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + + admin := sample.AccAddress() + authorityMock := keepertest.GetCrosschainAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupAdmin, true) + + msgServer := keeper.NewMsgServerImpl(*k) + + _, err := msgServer.UpdateTssAddress(ctx, &crosschaintypes.MsgUpdateTssAddress{ + Creator: admin, + TssPubkey: "", + }) + require.Error(t, err) + }) + t.Run("successfully update tss address", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ UseAuthorityMock: true, 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..87ff60e729 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,22 @@ package keeper_test import ( "encoding/hex" + "errors" + "math/big" + "math/rand" "testing" sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" + ethcommon "github.com/ethereum/go-ethereum/common" + "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/keeper" + "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) @@ -51,6 +58,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 +106,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, @@ -151,6 +163,91 @@ func TestKeeper_VoteOnObservedInboundTx(t *testing.T) { _, found = zk.ObserverKeeper.GetBallot(ctx, msg2.Digest()) require.False(t, found) }) + + t.Run("should error if vote on inbound ballot fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("VoteOnInboundBallot", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Return(true, false, errors.New("err")) + msgServer := keeper.NewMsgServerImpl(*k) + to, from := int64(1337), int64(101) + + msg := sample.InboundVote(0, from, to) + res, err := msgServer.VoteOnObservedInboundTx( + ctx, + &msg, + ) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should return if not finalized", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + msgServer := keeper.NewMsgServerImpl(*k) + validatorList := setObservers(t, k, ctx, zk) + + // add one more voter to make it not finalized + r := rand.New(rand.NewSource(42)) + valAddr := sample.ValAddress(r) + observerSet := append(validatorList, valAddr.String()) + zk.ObserverKeeper.SetObserverSet(ctx, observertypes.ObserverSet{ + ObserverList: observerSet, + }) + to, from := int64(1337), int64(101) + supportedChains := zk.ObserverKeeper.GetSupportedChains(ctx) + for _, chain := range supportedChains { + if chains.IsEVMChain(chain.ChainId) { + from = chain.ChainId + } + if chains.IsZetaChain(chain.ChainId) { + to = chain.ChainId + } + } + zk.ObserverKeeper.SetTSS(ctx, sample.Tss()) + + msg := sample.InboundVote(0, from, to) + for _, validatorAddr := range validatorList { + msg.Creator = validatorAddr + _, err := msgServer.VoteOnObservedInboundTx( + ctx, + &msg, + ) + require.NoError(t, err) + } + ballot, _, _ := zk.ObserverKeeper.FindBallot( + ctx, + msg.Digest(), + zk.ObserverKeeper.GetSupportedChainFromChainID(ctx, msg.SenderChainId), + observertypes.ObservationType_InBoundTx, + ) + require.Equal(t, ballot.BallotStatus, observertypes.BallotStatus_BallotInProgress) + require.Equal(t, ballot.Votes[0], observertypes.VoteType_SuccessObservation) + require.Equal(t, ballot.Votes[1], observertypes.VoteType_NotYetVoted) + _, found := k.GetCrossChainTx(ctx, msg.Digest()) + require.False(t, found) + }) + + t.Run("should err if tss not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("VoteOnInboundBallot", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Return(true, false, nil) + observerMock.On("GetTSS", mock.Anything).Return(observertypes.TSS{}, false) + msgServer := keeper.NewMsgServerImpl(*k) + to, from := int64(1337), int64(101) + + msg := sample.InboundVote(0, from, to) + res, err := msgServer.VoteOnObservedInboundTx( + ctx, + &msg, + ) + require.Error(t, err) + require.Nil(t, res) + }) } func TestStatus_ChangeStatus(t *testing.T) { @@ -199,3 +296,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..fa9884f25a 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, 100) + 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, 100) + 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/params.go b/x/crosschain/keeper/params.go deleted file mode 100644 index b81567feb5..0000000000 --- a/x/crosschain/keeper/params.go +++ /dev/null @@ -1,31 +0,0 @@ -package keeper - -import ( - "context" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/zeta-chain/zetacore/x/crosschain/types" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -// GetParams get all parameters as types.Params -func (k Keeper) GetParams(_ sdk.Context) types.Params { - return types.NewParams() -} - -// SetParams set the params -func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { - k.paramstore.SetParamSet(ctx, ¶ms) -} - -//Queries - -func (k Keeper) Params(c context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { - if req == nil { - return nil, status.Error(codes.InvalidArgument, "invalid request") - } - ctx := sdk.UnwrapSDKContext(c) - - return &types.QueryParamsResponse{Params: k.GetParams(ctx)}, nil -} diff --git a/x/crosschain/keeper/process_inbound.go b/x/crosschain/keeper/process_inbound.go new file mode 100644 index 0000000000..3178ee34b0 --- /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..491e362f78 --- /dev/null +++ b/x/crosschain/keeper/process_inbound_test.go @@ -0,0 +1,401 @@ +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/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_ProcessInboundZEVMDeposit(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 ProcessInbound + cctx := sample.CrossChainTx(t, "test") + cctx.CctxStatus = &types.Status{Status: types.CctxStatus_PendingInbound} + cctx.GetCurrentOutTxParam().Receiver = receiver.String() + cctx.GetCurrentOutTxParam().ReceiverChainId = chains.ZetaPrivnetChain().ChainId + cctx.GetInboundTxParams().Amount = sdkmath.NewUintFromBigInt(amount) + cctx.InboundTxParams.CoinType = coin.CoinType_Zeta + cctx.GetInboundTxParams().SenderChainId = 0 + k.ProcessInbound(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 ProcessInbound + cctx := sample.CrossChainTx(t, "test") + cctx.CctxStatus = &types.Status{Status: types.CctxStatus_PendingInbound} + cctx.GetCurrentOutTxParam().Receiver = receiver.String() + cctx.GetCurrentOutTxParam().ReceiverChainId = chains.ZetaPrivnetChain().ChainId + cctx.GetInboundTxParams().Amount = sdkmath.NewUintFromBigInt(amount) + cctx.InboundTxParams.CoinType = coin.CoinType_Zeta + cctx.GetInboundTxParams().SenderChainId = 0 + k.ProcessInbound(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 ProcessInbound + cctx := GetERC20Cctx(t, receiver, *senderChain, "", amount) + cctx.GetCurrentOutTxParam().ReceiverChainId = chains.ZetaPrivnetChain().ChainId + k.ProcessInbound(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 ProcessInbound + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + cctx.GetCurrentOutTxParam().ReceiverChainId = chains.ZetaPrivnetChain().ChainId + k.ProcessInbound(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, 100) + + // mock unsuccessful PayGasInERC20AndUpdateCctx + observerMock.On("GetSupportedChainFromChainID", mock.Anything, senderChain.ChainId). + Return(nil).Once() + + // call ProcessInbound + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + cctx.GetCurrentOutTxParam().ReceiverChainId = chains.ZetaPrivnetChain().ChainId + k.ProcessInbound(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("uunable to process zevm deposit HandleEVMDeposit revert fails at PayGasInERC20AndUpdateCctx with gas limit is 0", 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, 0) + + // mock unsuccessful PayGasInERC20AndUpdateCctx + observerMock.On("GetSupportedChainFromChainID", mock.Anything, senderChain.ChainId). + Return(nil).Once() + + // call ProcessInbound + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + cctx.GetCurrentOutTxParam().ReceiverChainId = chains.ZetaPrivnetChain().ChainId + k.ProcessInbound(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, 100) + + // 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 ProcessInbound + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + cctx.GetCurrentOutTxParam().ReceiverChainId = chains.ZetaPrivnetChain().ChainId + k.ProcessInbound(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, 100) + + // mock successful PayGasAndUpdateCctx + keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *senderChain, asset) + // mock successful UpdateNonce + updatedNonce := keepertest.MockUpdateNonce(observerMock, *senderChain) + + // call ProcessInbound + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + cctx.GetCurrentOutTxParam().ReceiverChainId = chains.ZetaPrivnetChain().ChainId + k.ProcessInbound(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, 100) + + // call ProcessInbound + cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + cctx.GetCurrentOutTxParam().ReceiverChainId = chains.ZetaPrivnetChain().ChainId + cctx.OutboundTxParams = append(cctx.OutboundTxParams, cctx.GetCurrentOutTxParam()) + k.ProcessInbound(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_ProcessInboundProcessCrosschainMsgPassing(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 ProcessInbound + cctx := GetERC20Cctx(t, receiver, *receiverChain, "", amount) + k.ProcessInbound(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 ProcessInbound + cctx := GetERC20Cctx(t, receiver, *receiverChain, "", amount) + k.ProcessInbound(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 ProcessInbound + cctx := GetERC20Cctx(t, receiver, *receiverChain, "", amount) + k.ProcessInbound(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..90837f17d8 --- /dev/null +++ b/x/crosschain/keeper/process_outbound_test.go @@ -0,0 +1,333 @@ +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, 100) + + // 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("successfully process failed outbound set to pending revert if gas limit is 0", 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, 0) + + // 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, 100) + + // 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, 100) + + // 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 if cctx invalid", 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 = nil + err := k.ProcessOutbound(ctx, cctx, observertypes.BallotStatus_BallotInProgress, sample.String()) + require.Error(t, err) + }) + + 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.OutboundTxParams[1].ReceiverChainId = 5 + cctx.OutboundTxParams[1].OutboundTxBallotIndex = "" + cctx.OutboundTxParams[1].OutboundTxHash = "" + + cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound + // mock successful GetRevertGasLimit for ERC20 + keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain, 100) + + err := k.ProcessOutbound(ctx, cctx, observertypes.BallotStatus_BallotFinalized_FailureObservation, sample.String()) + require.Error(t, err) + }) + + 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, 100) + + // 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..09732bd23a 100644 --- a/x/crosschain/keeper/refund_test.go +++ b/x/crosschain/keeper/refund_test.go @@ -1,11 +1,14 @@ package keeper_test import ( + "errors" "fmt" "testing" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "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" @@ -43,6 +46,69 @@ func TestKeeper_RefundAmountOnZetaChainGas(t *testing.T) { require.NoError(t, err) require.Equal(t, uint64(42), balance.Uint64()) }) + + t.Run("should error if zrc20 address empty", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + }) + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + sender := sample.EthAddress() + chainID := getValidEthChainID(t) + + fungibleMock.On("GetGasCoinForForeignCoin", mock.Anything, mock.Anything).Return(fungibletypes.ForeignCoins{ + Zrc20ContractAddress: "0x", + }, true) + + err := k.RefundAmountOnZetaChainGas(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + CoinType: coin.CoinType_Gas, + SenderChainId: chainID, + Sender: sender.String(), + TxOrigin: sender.String(), + Amount: math.NewUint(20), + }, + OutboundTxParams: []*types.OutboundTxParams{{ + Amount: math.NewUint(42), + }}, + }, + sender, + ) + require.Error(t, err) + }) + + t.Run("should error if deposit zrc20 fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + }) + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + sender := sample.EthAddress() + chainID := getValidEthChainID(t) + + fungibleMock.On("GetGasCoinForForeignCoin", mock.Anything, mock.Anything).Return(fungibletypes.ForeignCoins{ + Zrc20ContractAddress: sample.EthAddress().Hex(), + }, true) + + fungibleMock.On("DepositZRC20", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, errors.New("")) + + err := k.RefundAmountOnZetaChainGas(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + CoinType: coin.CoinType_Gas, + SenderChainId: chainID, + Sender: sender.String(), + TxOrigin: sender.String(), + Amount: math.NewUint(20), + }, + OutboundTxParams: []*types.OutboundTxParams{{ + Amount: math.NewUint(42), + }}, + }, + sender, + ) + require.Error(t, err) + }) + t.Run("should refund inbound amount zrc20 gas on zeta chain", func(t *testing.T) { k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) @@ -139,10 +205,60 @@ 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()) }) + + t.Run("should error if non evm chain", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + sender := sample.EthAddress() + + err := k.RefundAmountOnZetaChainZeta(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + CoinType: coin.CoinType_Gas, + SenderChainId: 101, + Sender: sender.String(), + TxOrigin: sender.String(), + Amount: math.NewUint(20), + }, + OutboundTxParams: []*types.OutboundTxParams{{ + Amount: math.NewUint(42), + }}, + }, + sender, + ) + require.Error(t, err) + }) + + t.Run("should error if deposit coin zeta fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + }) + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + fungibleMock.On("DepositCoinZeta", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("err")) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + sender := sample.EthAddress() + chainID := getValidEthChainID(t) + + err := k.RefundAmountOnZetaChainZeta(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + CoinType: coin.CoinType_Gas, + SenderChainId: chainID, + Sender: sender.String(), + TxOrigin: sender.String(), + Amount: math.NewUint(20), + }, + OutboundTxParams: []*types.OutboundTxParams{{ + Amount: math.NewUint(42), + }}, + }, + sender, + ) + require.Error(t, err) + }) + t.Run("should refund inbound amount on zeta chain if outbound is not present", func(t *testing.T) { k, ctx, sdkk, _ := keepertest.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) @@ -210,6 +326,7 @@ func TestKeeper_RefundAmountOnZetaChainERC20(t *testing.T) { ) err := k.RefundAmountOnZetaChainERC20(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ CoinType: coin.CoinType_ERC20, SenderChainId: chainID, @@ -232,6 +349,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, @@ -247,10 +365,74 @@ func TestKeeper_RefundAmountOnZetaChainERC20(t *testing.T) { require.Equal(t, uint64(84), balance.Uint64()) }) + t.Run("should error if zrc20 address empty", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + }) + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + fungibleMock.On("GetForeignCoinFromAsset", mock.Anything, mock.Anything, mock.Anything).Return(fungibletypes.ForeignCoins{ + Zrc20ContractAddress: "0x", + }, true) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + asset := sample.EthAddress().String() + sender := sample.EthAddress() + chainID := getValidEthChainID(t) + + err := k.RefundAmountOnZetaChainERC20(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + CoinType: coin.CoinType_ERC20, + SenderChainId: chainID, + Sender: sender.String(), + Asset: asset, + Amount: math.NewUint(42), + }, + OutboundTxParams: []*types.OutboundTxParams{{ + Amount: math.NewUint(42), + }}, + }, + sender, + ) + require.Error(t, err) + }) + + t.Run("should error if deposit zrc20 fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseFungibleMock: true, + }) + fungibleMock := keepertest.GetCrosschainFungibleMock(t, k) + k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) + asset := sample.EthAddress().String() + sender := sample.EthAddress() + chainID := getValidEthChainID(t) + + fungibleMock.On("GetForeignCoinFromAsset", mock.Anything, mock.Anything, mock.Anything).Return(fungibletypes.ForeignCoins{ + Zrc20ContractAddress: sample.EthAddress().Hex(), + }, true) + + fungibleMock.On("DepositZRC20", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, errors.New("")) + + err := k.RefundAmountOnZetaChainERC20(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ + CoinType: coin.CoinType_ERC20, + SenderChainId: chainID, + Sender: sender.String(), + Asset: asset, + Amount: math.NewUint(42), + }, + OutboundTxParams: []*types.OutboundTxParams{{ + Amount: math.NewUint(42), + }}, + }, + sender, + ) + require.Error(t, err) + }) + t.Run("should fail with invalid cctx", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) err := k.RefundAmountOnZetaChainERC20(ctx, types.CrossChainTx{ + InboundTxParams: &types.InboundTxParams{ CoinType: coin.CoinType_Zeta, Amount: math.NewUint(42), @@ -313,3 +495,12 @@ func TestKeeper_RefundAmountOnZetaChainERC20(t *testing.T) { require.ErrorContains(t, err, "zrc not found") }) } + +func TestKeeper_RefundAbortedAmountOnZetaChain_FailsForUnsupportedCoinType(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + + cctx := sample.CrossChainTx(t, "index") + cctx.InboundTxParams.CoinType = coin.CoinType_Cmd + err := k.RefundAbortedAmountOnZetaChain(ctx, *cctx, common.Address{}) + require.ErrorContains(t, err, "unsupported coin type for refund on ZetaChain") +} diff --git a/x/crosschain/keeper/verify_proof_test.go b/x/crosschain/keeper/verify_proof_test.go new file mode 100644 index 0000000000..62d47773b9 --- /dev/null +++ b/x/crosschain/keeper/verify_proof_test.go @@ -0,0 +1,393 @@ +package keeper_test + +import ( + "errors" + "math/big" + "testing" + + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/coin" + "github.com/zeta-chain/zetacore/pkg/proofs" + 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 TestKeeper_VerifyProof(t *testing.T) { + t.Run("should error if crosschain flags not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetCrosschainFlags", mock.Anything).Return(observertypes.CrosschainFlags{}, false) + + res, err := k.VerifyProof(ctx, &proofs.Proof{}, 5, sample.Hash().String(), 1) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if BlockHeaderVerificationFlags nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetCrosschainFlags", mock.Anything).Return(observertypes.CrosschainFlags{ + BlockHeaderVerificationFlags: nil, + }, true) + + res, err := k.VerifyProof(ctx, &proofs.Proof{}, 5, sample.Hash().String(), 1) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if verification not enabled for btc chain", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetCrosschainFlags", mock.Anything).Return(observertypes.CrosschainFlags{ + BlockHeaderVerificationFlags: &observertypes.BlockHeaderVerificationFlags{ + IsBtcTypeChainEnabled: false, + }, + }, true) + + res, err := k.VerifyProof(ctx, &proofs.Proof{}, 18444, sample.Hash().String(), 1) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if verification not enabled for evm chain", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetCrosschainFlags", mock.Anything).Return(observertypes.CrosschainFlags{ + BlockHeaderVerificationFlags: &observertypes.BlockHeaderVerificationFlags{ + IsEthTypeChainEnabled: false, + }, + }, true) + + res, err := k.VerifyProof(ctx, &proofs.Proof{}, 5, sample.Hash().String(), 1) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if block header-based verification not supported", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetCrosschainFlags", mock.Anything).Return(observertypes.CrosschainFlags{ + BlockHeaderVerificationFlags: &observertypes.BlockHeaderVerificationFlags{ + IsEthTypeChainEnabled: false, + }, + }, true) + + res, err := k.VerifyProof(ctx, &proofs.Proof{}, 101, sample.Hash().String(), 1) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if blockhash invalid", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetCrosschainFlags", mock.Anything).Return(observertypes.CrosschainFlags{ + BlockHeaderVerificationFlags: &observertypes.BlockHeaderVerificationFlags{ + IsBtcTypeChainEnabled: true, + }, + }, true) + + res, err := k.VerifyProof(ctx, &proofs.Proof{}, 18444, "invalid", 1) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if block header not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetCrosschainFlags", mock.Anything).Return(observertypes.CrosschainFlags{ + BlockHeaderVerificationFlags: &observertypes.BlockHeaderVerificationFlags{ + IsEthTypeChainEnabled: true, + }, + }, true) + + observerMock.On("GetBlockHeader", mock.Anything, mock.Anything).Return(proofs.BlockHeader{}, false) + + res, err := k.VerifyProof(ctx, &proofs.Proof{}, 5, sample.Hash().String(), 1) + require.Error(t, err) + require.Nil(t, res) + }) + // TODO: // https://github.com/zeta-chain/node/issues/1875 add more tests +} + +func TestKeeper_VerifyEVMInTxBody(t *testing.T) { + to := sample.EthAddress() + tx := ethtypes.NewTx(ðtypes.DynamicFeeTx{ + ChainID: big.NewInt(5), + Nonce: 1, + GasTipCap: nil, + GasFeeCap: nil, + Gas: 21000, + To: &to, + Value: big.NewInt(5), + Data: nil, + }) + t.Run("should error if msg tx hash not correct", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + + txBytes, err := tx.MarshalBinary() + require.NoError(t, err) + msg := &types.MsgAddToInTxTracker{ + TxHash: "0x0", + } + + err = k.VerifyEVMInTxBody(ctx, msg, txBytes) + require.Error(t, err) + }) + + t.Run("should error if msg chain id not correct", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + + txBytes, err := tx.MarshalBinary() + require.NoError(t, err) + msg := &types.MsgAddToInTxTracker{ + TxHash: tx.Hash().Hex(), + ChainId: 1, + } + + err = k.VerifyEVMInTxBody(ctx, msg, txBytes) + require.Error(t, err) + }) + + t.Run("should error if not supported coin type", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + + txBytes, err := tx.MarshalBinary() + require.NoError(t, err) + msg := &types.MsgAddToInTxTracker{ + TxHash: tx.Hash().Hex(), + ChainId: tx.ChainId().Int64(), + CoinType: coin.CoinType_Cmd, + } + + err = k.VerifyEVMInTxBody(ctx, msg, txBytes) + require.Error(t, err) + }) + + t.Run("should error for cointype_zeta if chain params not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetChainParamsByChainID", mock.Anything, mock.Anything).Return(&observertypes.ChainParams{}, false) + + txBytes, err := tx.MarshalBinary() + require.NoError(t, err) + msg := &types.MsgAddToInTxTracker{ + TxHash: tx.Hash().Hex(), + ChainId: tx.ChainId().Int64(), + CoinType: coin.CoinType_Zeta, + } + + err = k.VerifyEVMInTxBody(ctx, msg, txBytes) + require.Error(t, err) + }) + + t.Run("should error for cointype_zeta if tx.to wrong", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetChainParamsByChainID", mock.Anything, mock.Anything).Return(&observertypes.ChainParams{ + ConnectorContractAddress: sample.EthAddress().Hex(), + }, true) + + txBytes, err := tx.MarshalBinary() + require.NoError(t, err) + msg := &types.MsgAddToInTxTracker{ + TxHash: tx.Hash().Hex(), + ChainId: tx.ChainId().Int64(), + CoinType: coin.CoinType_Zeta, + } + + err = k.VerifyEVMInTxBody(ctx, msg, txBytes) + require.Error(t, err) + }) + + t.Run("should not error for cointype_zeta", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetChainParamsByChainID", mock.Anything, mock.Anything).Return(&observertypes.ChainParams{ + ConnectorContractAddress: to.Hex(), + }, true) + + txBytes, err := tx.MarshalBinary() + require.NoError(t, err) + msg := &types.MsgAddToInTxTracker{ + TxHash: tx.Hash().Hex(), + ChainId: tx.ChainId().Int64(), + CoinType: coin.CoinType_Zeta, + } + + err = k.VerifyEVMInTxBody(ctx, msg, txBytes) + require.NoError(t, err) + }) + + t.Run("should error for cointype_erc20 if chain params not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetChainParamsByChainID", mock.Anything, mock.Anything).Return(&observertypes.ChainParams{}, false) + + txBytes, err := tx.MarshalBinary() + require.NoError(t, err) + msg := &types.MsgAddToInTxTracker{ + TxHash: tx.Hash().Hex(), + ChainId: tx.ChainId().Int64(), + CoinType: coin.CoinType_ERC20, + } + + err = k.VerifyEVMInTxBody(ctx, msg, txBytes) + require.Error(t, err) + }) + + t.Run("should error for cointype_erc20 if tx.to wrong", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetChainParamsByChainID", mock.Anything, mock.Anything).Return(&observertypes.ChainParams{ + Erc20CustodyContractAddress: sample.EthAddress().Hex(), + }, true) + + txBytes, err := tx.MarshalBinary() + require.NoError(t, err) + msg := &types.MsgAddToInTxTracker{ + TxHash: tx.Hash().Hex(), + ChainId: tx.ChainId().Int64(), + CoinType: coin.CoinType_ERC20, + } + + err = k.VerifyEVMInTxBody(ctx, msg, txBytes) + require.Error(t, err) + }) + + t.Run("should not error for cointype_erc20", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetChainParamsByChainID", mock.Anything, mock.Anything).Return(&observertypes.ChainParams{ + Erc20CustodyContractAddress: to.Hex(), + }, true) + + txBytes, err := tx.MarshalBinary() + require.NoError(t, err) + msg := &types.MsgAddToInTxTracker{ + TxHash: tx.Hash().Hex(), + ChainId: tx.ChainId().Int64(), + CoinType: coin.CoinType_ERC20, + } + + err = k.VerifyEVMInTxBody(ctx, msg, txBytes) + require.NoError(t, err) + }) + + t.Run("should error for cointype_gas if tss address not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetTssAddress", mock.Anything, mock.Anything).Return(&observertypes.QueryGetTssAddressResponse{}, errors.New("err")) + + txBytes, err := tx.MarshalBinary() + require.NoError(t, err) + msg := &types.MsgAddToInTxTracker{ + TxHash: tx.Hash().Hex(), + ChainId: tx.ChainId().Int64(), + CoinType: coin.CoinType_Gas, + } + + err = k.VerifyEVMInTxBody(ctx, msg, txBytes) + require.Error(t, err) + }) + + t.Run("should error for cointype_gas if tss eth address is empty", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetTssAddress", mock.Anything, mock.Anything).Return(&observertypes.QueryGetTssAddressResponse{ + Eth: "0x", + }, nil) + + txBytes, err := tx.MarshalBinary() + require.NoError(t, err) + msg := &types.MsgAddToInTxTracker{ + TxHash: tx.Hash().Hex(), + ChainId: tx.ChainId().Int64(), + CoinType: coin.CoinType_Gas, + } + + err = k.VerifyEVMInTxBody(ctx, msg, txBytes) + require.Error(t, err) + }) + + t.Run("should error for cointype_gas if tss eth address is wrong", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetTssAddress", mock.Anything, mock.Anything).Return(&observertypes.QueryGetTssAddressResponse{ + Eth: sample.EthAddress().Hex(), + }, nil) + + txBytes, err := tx.MarshalBinary() + require.NoError(t, err) + msg := &types.MsgAddToInTxTracker{ + TxHash: tx.Hash().Hex(), + ChainId: tx.ChainId().Int64(), + CoinType: coin.CoinType_Gas, + } + + err = k.VerifyEVMInTxBody(ctx, msg, txBytes) + require.Error(t, err) + }) + + t.Run("should not error for cointype_gas", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{ + UseObserverMock: true, + }) + observerMock := keepertest.GetCrosschainObserverMock(t, k) + observerMock.On("GetTssAddress", mock.Anything, mock.Anything).Return(&observertypes.QueryGetTssAddressResponse{ + Eth: to.Hex(), + }, nil) + + txBytes, err := tx.MarshalBinary() + require.NoError(t, err) + msg := &types.MsgAddToInTxTracker{ + TxHash: tx.Hash().Hex(), + ChainId: tx.ChainId().Int64(), + CoinType: coin.CoinType_Gas, + } + + err = k.VerifyEVMInTxBody(ctx, msg, txBytes) + require.NoError(t, err) + }) +} diff --git a/x/crosschain/keeper/zeta_accounting_test.go b/x/crosschain/keeper/zeta_accounting_test.go index 8802fe7666..7a3eee1223 100644 --- a/x/crosschain/keeper/zeta_accounting_test.go +++ b/x/crosschain/keeper/zeta_accounting_test.go @@ -11,7 +11,6 @@ import ( ) func TestKeeper_AddZetaAccounting(t *testing.T) { - t.Run("should add aborted zeta amount", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) originalAmount := sdkmath.NewUint(rand.Uint64()) @@ -28,6 +27,18 @@ func TestKeeper_AddZetaAccounting(t *testing.T) { require.Equal(t, originalAmount.Add(addAmount), val.AbortedZetaAmount) }) + t.Run("should add aborted zeta amount if accounting not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + originalAmount := sdkmath.NewUint(0) + _, found := k.GetZetaAccounting(ctx) + require.False(t, found) + addAmount := sdkmath.NewUint(rand.Uint64()) + k.AddZetaAbortedAmount(ctx, addAmount) + val, found := k.GetZetaAccounting(ctx) + require.True(t, found) + require.Equal(t, originalAmount.Add(addAmount), val.AbortedZetaAmount) + }) + t.Run("cant find aborted amount", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) val, found := k.GetZetaAccounting(ctx) 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..4fe79325d6 100644 --- a/x/crosschain/module.go +++ b/x/crosschain/module.go @@ -76,12 +76,7 @@ func (AppModuleBasic) RegisterRESTRoutes(_ client.Context, _ *mux.Router) { // RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the module. func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) { - - err := types.RegisterClientCtx(clientCtx) - if err != nil { - fmt.Println("RegisterQueryHandlerClient err: %w", err) - } - err = types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) + err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) if err != nil { fmt.Println("RegisterQueryHandlerClient err: %w", err) } @@ -154,6 +149,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/module_simulation.go b/x/crosschain/module_simulation.go index ef8ecee194..e198a69462 100644 --- a/x/crosschain/module_simulation.go +++ b/x/crosschain/module_simulation.go @@ -15,9 +15,7 @@ func (AppModule) GenerateGenesisState(simState *module.SimulationState) { for i, acc := range simState.Accounts { accs[i] = acc.Address.String() } - crosschainGenesis := types.GenesisState{ - Params: types.DefaultParams(), - } + crosschainGenesis := types.GenesisState{} simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(&crosschainGenesis) } 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/client_ctx.go b/x/crosschain/types/client_ctx.go deleted file mode 100644 index b4622e1a61..0000000000 --- a/x/crosschain/types/client_ctx.go +++ /dev/null @@ -1,14 +0,0 @@ -package types - -import ( - "github.com/cosmos/cosmos-sdk/client" -) - -var ( - ClientCtx client.Context -) - -func RegisterClientCtx(clientCtx client.Context) error { - ClientCtx = clientCtx - return nil -} 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/genesis.pb.go b/x/crosschain/types/genesis.pb.go index 43a21227fb..c6358ad693 100644 --- a/x/crosschain/types/genesis.pb.go +++ b/x/crosschain/types/genesis.pb.go @@ -26,15 +26,14 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // GenesisState defines the metacore module's genesis state. type GenesisState struct { - Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` - OutTxTrackerList []OutTxTracker `protobuf:"bytes,2,rep,name=outTxTrackerList,proto3" json:"outTxTrackerList"` - GasPriceList []*GasPrice `protobuf:"bytes,5,rep,name=gasPriceList,proto3" json:"gasPriceList,omitempty"` - CrossChainTxs []*CrossChainTx `protobuf:"bytes,7,rep,name=CrossChainTxs,proto3" json:"CrossChainTxs,omitempty"` - LastBlockHeightList []*LastBlockHeight `protobuf:"bytes,8,rep,name=lastBlockHeightList,proto3" json:"lastBlockHeightList,omitempty"` - InTxHashToCctxList []InTxHashToCctx `protobuf:"bytes,9,rep,name=inTxHashToCctxList,proto3" json:"inTxHashToCctxList"` - InTxTrackerList []InTxTracker `protobuf:"bytes,11,rep,name=in_tx_tracker_list,json=inTxTrackerList,proto3" json:"in_tx_tracker_list"` - ZetaAccounting ZetaAccounting `protobuf:"bytes,12,opt,name=zeta_accounting,json=zetaAccounting,proto3" json:"zeta_accounting"` - FinalizedInbounds []string `protobuf:"bytes,16,rep,name=FinalizedInbounds,proto3" json:"FinalizedInbounds,omitempty"` + OutTxTrackerList []OutTxTracker `protobuf:"bytes,1,rep,name=outTxTrackerList,proto3" json:"outTxTrackerList"` + GasPriceList []*GasPrice `protobuf:"bytes,2,rep,name=gasPriceList,proto3" json:"gasPriceList,omitempty"` + CrossChainTxs []*CrossChainTx `protobuf:"bytes,3,rep,name=CrossChainTxs,proto3" json:"CrossChainTxs,omitempty"` + LastBlockHeightList []*LastBlockHeight `protobuf:"bytes,4,rep,name=lastBlockHeightList,proto3" json:"lastBlockHeightList,omitempty"` + InTxHashToCctxList []InTxHashToCctx `protobuf:"bytes,5,rep,name=inTxHashToCctxList,proto3" json:"inTxHashToCctxList"` + InTxTrackerList []InTxTracker `protobuf:"bytes,6,rep,name=in_tx_tracker_list,json=inTxTrackerList,proto3" json:"in_tx_tracker_list"` + ZetaAccounting ZetaAccounting `protobuf:"bytes,7,opt,name=zeta_accounting,json=zetaAccounting,proto3" json:"zeta_accounting"` + FinalizedInbounds []string `protobuf:"bytes,8,rep,name=FinalizedInbounds,proto3" json:"FinalizedInbounds,omitempty"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -70,13 +69,6 @@ func (m *GenesisState) XXX_DiscardUnknown() { var xxx_messageInfo_GenesisState proto.InternalMessageInfo -func (m *GenesisState) GetParams() Params { - if m != nil { - return m.Params - } - return Params{} -} - func (m *GenesisState) GetOutTxTrackerList() []OutTxTracker { if m != nil { return m.OutTxTrackerList @@ -140,39 +132,37 @@ func init() { func init() { proto.RegisterFile("crosschain/genesis.proto", fileDescriptor_dd51403692d571f4) } var fileDescriptor_dd51403692d571f4 = []byte{ - // 498 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0xd1, 0x6e, 0xd3, 0x3c, - 0x14, 0xc7, 0x9b, 0x6f, 0x1f, 0x85, 0xb9, 0x85, 0x0d, 0x83, 0x44, 0x54, 0x89, 0xac, 0x1a, 0x42, - 0x54, 0xc0, 0x12, 0x31, 0x9e, 0x80, 0x56, 0x62, 0x9b, 0x36, 0x89, 0x11, 0x7a, 0x35, 0x31, 0x19, - 0xd7, 0xb3, 0x12, 0x6b, 0x5d, 0x5c, 0xc5, 0x8e, 0x14, 0x7a, 0xcd, 0x03, 0xf0, 0x58, 0xbb, 0xdc, - 0x25, 0x57, 0x08, 0xb5, 0x2f, 0x82, 0x7c, 0x62, 0x86, 0xa3, 0x4e, 0x64, 0x77, 0x47, 0x3e, 0xe7, - 0xff, 0xfb, 0x1f, 0x9f, 0x63, 0x23, 0x9f, 0xe5, 0x52, 0x29, 0x96, 0x52, 0x91, 0x45, 0x09, 0xcf, - 0xb8, 0x12, 0x2a, 0x9c, 0xe5, 0x52, 0x4b, 0xfc, 0x74, 0xce, 0x35, 0x85, 0x44, 0x08, 0x91, 0xcc, - 0x79, 0xf8, 0xb7, 0xb8, 0xb7, 0xe5, 0x08, 0x21, 0x24, 0x10, 0x13, 0x5d, 0x56, 0xfa, 0x5e, 0xcf, - 0x25, 0x53, 0x45, 0x66, 0xb9, 0x60, 0xdc, 0xe6, 0x9e, 0x39, 0x39, 0xd0, 0x90, 0x94, 0xaa, 0x94, - 0x68, 0x49, 0x18, 0xbb, 0x06, 0x04, 0x2b, 0x45, 0x3a, 0xa7, 0xec, 0x9c, 0xe7, 0x36, 0xbf, 0xed, - 0xe4, 0xa7, 0x54, 0x69, 0x32, 0x99, 0x4a, 0x76, 0x4e, 0x52, 0x2e, 0x92, 0x54, 0xdb, 0x1a, 0xb7, - 0x4b, 0x59, 0xe8, 0x55, 0xc8, 0x13, 0xa7, 0x60, 0x46, 0x73, 0x7a, 0x61, 0xaf, 0xdf, 0x7b, 0x9c, - 0xc8, 0x44, 0x42, 0x18, 0x99, 0xa8, 0x3a, 0xdd, 0xfe, 0xd6, 0x46, 0xdd, 0xbd, 0x6a, 0x4c, 0x9f, - 0x34, 0xd5, 0x1c, 0x8f, 0x50, 0xbb, 0x92, 0xf9, 0x5e, 0xdf, 0x1b, 0x74, 0x76, 0x9f, 0x87, 0xff, - 0x1c, 0x5b, 0x78, 0x0c, 0xc5, 0xc3, 0xff, 0x2f, 0x7f, 0x6e, 0xb5, 0x62, 0x2b, 0xc5, 0xa7, 0x68, - 0x53, 0x16, 0x7a, 0x5c, 0x8e, 0xab, 0xd6, 0x8e, 0x84, 0xd2, 0xfe, 0x7f, 0xfd, 0xb5, 0x41, 0x67, - 0xf7, 0x55, 0x03, 0xee, 0x83, 0x23, 0xb3, 0xd0, 0x15, 0x14, 0x3e, 0x44, 0xdd, 0x84, 0xaa, 0x63, - 0x33, 0x7f, 0x40, 0xdf, 0x01, 0xf4, 0x8b, 0x06, 0xf4, 0x9e, 0x95, 0xc4, 0x35, 0x31, 0xfe, 0x88, - 0xee, 0x8f, 0x4c, 0xd1, 0xc8, 0x14, 0x8d, 0x4b, 0xe5, 0xdf, 0xbd, 0x55, 0xa3, 0xae, 0x26, 0xae, - 0x13, 0xf0, 0x17, 0xf4, 0xc8, 0xec, 0x6f, 0x68, 0xd6, 0xb7, 0x0f, 0xdb, 0x83, 0x36, 0xef, 0x01, - 0x38, 0x6c, 0x00, 0x1f, 0xd5, 0x95, 0xf1, 0x4d, 0x28, 0xcc, 0x10, 0x36, 0x56, 0xfb, 0x54, 0xa5, - 0x63, 0x39, 0x62, 0xba, 0x04, 0x83, 0x75, 0x30, 0xd8, 0x69, 0x30, 0x38, 0xa8, 0x09, 0xed, 0x90, - 0x6f, 0xc0, 0xe1, 0x53, 0x63, 0xe2, 0xbc, 0x30, 0x32, 0x35, 0x26, 0x1d, 0x30, 0x79, 0x79, 0x0b, - 0x93, 0xfa, 0x1a, 0x37, 0x44, 0x56, 0xdf, 0xe2, 0x67, 0xb4, 0x61, 0x94, 0x84, 0x32, 0x26, 0x8b, - 0x4c, 0x8b, 0x2c, 0xf1, 0xbb, 0xf0, 0xe4, 0x9a, 0x2e, 0x70, 0xc2, 0x35, 0x7d, 0x77, 0x2d, 0xb2, - 0xf8, 0x07, 0xf3, 0xda, 0x29, 0x7e, 0x8d, 0x1e, 0xbe, 0x17, 0x19, 0x9d, 0x8a, 0x39, 0x3f, 0x3b, - 0xc8, 0x26, 0xb2, 0xc8, 0xce, 0x94, 0xbf, 0xd9, 0x5f, 0x1b, 0xac, 0xc7, 0xab, 0x89, 0xe1, 0xe1, - 0xe5, 0x22, 0xf0, 0xae, 0x16, 0x81, 0xf7, 0x6b, 0x11, 0x78, 0xdf, 0x97, 0x41, 0xeb, 0x6a, 0x19, - 0xb4, 0x7e, 0x2c, 0x83, 0xd6, 0xc9, 0x9b, 0x44, 0xe8, 0xb4, 0x98, 0x84, 0x4c, 0x5e, 0x44, 0xc6, - 0x62, 0xa7, 0xfa, 0x5a, 0x7f, 0xfa, 0x8a, 0xca, 0xc8, 0xf9, 0x70, 0xfa, 0xeb, 0x8c, 0xab, 0x49, - 0x1b, 0xbe, 0xd6, 0xdb, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x69, 0x4a, 0x62, 0x7d, 0x8b, 0x04, - 0x00, 0x00, + // 465 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0x41, 0x6f, 0xd3, 0x30, + 0x14, 0xc7, 0x1b, 0xba, 0x0d, 0x30, 0x83, 0x81, 0xe1, 0x10, 0x55, 0x22, 0xab, 0xc6, 0x81, 0x09, + 0x58, 0x22, 0xe0, 0x13, 0xd0, 0x4a, 0x6c, 0xd3, 0x26, 0x01, 0xa1, 0xa7, 0x89, 0xc9, 0x38, 0x9e, + 0x95, 0x58, 0x0b, 0x76, 0x15, 0xbf, 0x48, 0xa1, 0x9f, 0x82, 0x6f, 0xc4, 0x75, 0xc7, 0x1d, 0x39, + 0x21, 0xd4, 0x7e, 0x11, 0x64, 0x27, 0x0c, 0x47, 0x99, 0xd6, 0xde, 0x9e, 0xfc, 0xde, 0xff, 0xf7, + 0x7f, 0xf9, 0x3b, 0x46, 0x3e, 0x2b, 0x94, 0xd6, 0x2c, 0xa3, 0x42, 0x46, 0x29, 0x97, 0x5c, 0x0b, + 0x1d, 0x4e, 0x0b, 0x05, 0x0a, 0x3f, 0x9d, 0x71, 0xa0, 0xb6, 0x11, 0xda, 0x4a, 0x15, 0x3c, 0xfc, + 0x3f, 0x3c, 0xd8, 0x76, 0x84, 0xb6, 0x24, 0xb6, 0x26, 0x50, 0xd5, 0xfa, 0xc1, 0xc0, 0x25, 0x53, + 0x4d, 0xa6, 0x85, 0x60, 0xbc, 0xe9, 0x3d, 0x73, 0x7a, 0x56, 0x43, 0x32, 0xaa, 0x33, 0x02, 0x8a, + 0x30, 0x76, 0x05, 0x08, 0x3a, 0x43, 0x50, 0x50, 0x76, 0xce, 0x8b, 0xa6, 0xbf, 0xe3, 0xf4, 0x73, + 0xaa, 0x81, 0x24, 0xb9, 0x62, 0xe7, 0x24, 0xe3, 0x22, 0xcd, 0xa0, 0x99, 0x71, 0xb7, 0x54, 0x25, + 0x74, 0x21, 0x4f, 0x52, 0x95, 0x2a, 0x5b, 0x46, 0xa6, 0xaa, 0x4f, 0x77, 0x7e, 0xae, 0xa3, 0xcd, + 0xfd, 0x3a, 0x8d, 0xcf, 0x40, 0x81, 0xe3, 0x53, 0xf4, 0x50, 0x95, 0x30, 0xa9, 0x26, 0xb5, 0xf8, + 0x58, 0x68, 0xf0, 0xbd, 0x61, 0x7f, 0xf7, 0xde, 0x9b, 0x97, 0xe1, 0x8d, 0x39, 0x85, 0x1f, 0x1c, + 0xd9, 0x68, 0xed, 0xe2, 0xf7, 0x76, 0x2f, 0xee, 0xa0, 0xf0, 0x11, 0xda, 0x4c, 0xa9, 0xfe, 0x68, + 0x12, 0xb2, 0xe8, 0x5b, 0x16, 0xfd, 0x7c, 0x09, 0x7a, 0xbf, 0x91, 0xc4, 0x2d, 0x31, 0xfe, 0x84, + 0xee, 0x8f, 0xcd, 0xd0, 0xd8, 0x0c, 0x4d, 0x2a, 0xed, 0xf7, 0x57, 0x5a, 0xd4, 0xd5, 0xc4, 0x6d, + 0x02, 0xfe, 0x8a, 0x1e, 0x9b, 0x84, 0x47, 0x26, 0xe0, 0x03, 0x9b, 0xaf, 0x5d, 0x73, 0xcd, 0x82, + 0xc3, 0x25, 0xe0, 0xe3, 0xb6, 0x32, 0xbe, 0x0e, 0x85, 0x19, 0xc2, 0xc6, 0xea, 0x80, 0xea, 0x6c, + 0xa2, 0xc6, 0x0c, 0x2a, 0x6b, 0xb0, 0x6e, 0x0d, 0xf6, 0x96, 0x18, 0x1c, 0xb6, 0x84, 0x4d, 0xc8, + 0xd7, 0xe0, 0xf0, 0xa9, 0x31, 0x71, 0xfe, 0x01, 0x92, 0x1b, 0x93, 0x0d, 0x6b, 0xf2, 0x62, 0x05, + 0x93, 0xf6, 0x35, 0x6e, 0x09, 0xd9, 0xbe, 0xc5, 0x2f, 0x68, 0xcb, 0x28, 0x09, 0x65, 0x4c, 0x95, + 0x12, 0x84, 0x4c, 0xfd, 0xdb, 0x43, 0x6f, 0x85, 0x0f, 0x38, 0xe1, 0x40, 0xdf, 0x5d, 0x89, 0x1a, + 0xfc, 0x83, 0x59, 0xeb, 0x14, 0xbf, 0x42, 0x8f, 0xde, 0x0b, 0x49, 0x73, 0x31, 0xe3, 0x67, 0x87, + 0x32, 0x51, 0xa5, 0x3c, 0xd3, 0xfe, 0x9d, 0x61, 0x7f, 0xf7, 0x6e, 0xdc, 0x6d, 0x8c, 0x8e, 0x2e, + 0xe6, 0x81, 0x77, 0x39, 0x0f, 0xbc, 0x3f, 0xf3, 0xc0, 0xfb, 0xb1, 0x08, 0x7a, 0x97, 0x8b, 0xa0, + 0xf7, 0x6b, 0x11, 0xf4, 0x4e, 0x5e, 0xa7, 0x02, 0xb2, 0x32, 0x09, 0x99, 0xfa, 0x16, 0x19, 0x8b, + 0xbd, 0xfa, 0x75, 0xfc, 0xdb, 0x2b, 0xaa, 0x22, 0xe7, 0xcd, 0xc0, 0xf7, 0x29, 0xd7, 0xc9, 0x86, + 0x7d, 0x15, 0x6f, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x9d, 0xe1, 0x07, 0xea, 0x2d, 0x04, 0x00, + 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -201,9 +191,7 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.FinalizedInbounds[iNdEx]) i = encodeVarintGenesis(dAtA, i, uint64(len(m.FinalizedInbounds[iNdEx]))) i-- - dAtA[i] = 0x1 - i-- - dAtA[i] = 0x82 + dAtA[i] = 0x42 } } { @@ -215,7 +203,7 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintGenesis(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x62 + dAtA[i] = 0x3a if len(m.InTxTrackerList) > 0 { for iNdEx := len(m.InTxTrackerList) - 1; iNdEx >= 0; iNdEx-- { { @@ -227,7 +215,7 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintGenesis(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x5a + dAtA[i] = 0x32 } } if len(m.InTxHashToCctxList) > 0 { @@ -241,7 +229,7 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintGenesis(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x4a + dAtA[i] = 0x2a } } if len(m.LastBlockHeightList) > 0 { @@ -255,7 +243,7 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintGenesis(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x42 + dAtA[i] = 0x22 } } if len(m.CrossChainTxs) > 0 { @@ -269,7 +257,7 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintGenesis(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x3a + dAtA[i] = 0x1a } } if len(m.GasPriceList) > 0 { @@ -283,7 +271,7 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintGenesis(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x2a + dAtA[i] = 0x12 } } if len(m.OutTxTrackerList) > 0 { @@ -297,19 +285,9 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintGenesis(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x12 + dAtA[i] = 0xa } } - { - size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintGenesis(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa return len(dAtA) - i, nil } @@ -330,8 +308,6 @@ func (m *GenesisState) Size() (n int) { } var l int _ = l - l = m.Params.Size() - n += 1 + l + sovGenesis(uint64(l)) if len(m.OutTxTrackerList) > 0 { for _, e := range m.OutTxTrackerList { l = e.Size() @@ -373,7 +349,7 @@ func (m *GenesisState) Size() (n int) { if len(m.FinalizedInbounds) > 0 { for _, s := range m.FinalizedInbounds { l = len(s) - n += 2 + l + sovGenesis(uint64(l)) + n += 1 + l + sovGenesis(uint64(l)) } } return n @@ -415,39 +391,6 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { } switch fieldNum { case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenesis - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthGenesis - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthGenesis - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field OutTxTrackerList", wireType) } @@ -481,7 +424,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 5: + case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field GasPriceList", wireType) } @@ -515,7 +458,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 7: + case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CrossChainTxs", wireType) } @@ -549,7 +492,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 8: + case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LastBlockHeightList", wireType) } @@ -583,7 +526,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 9: + case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field InTxHashToCctxList", wireType) } @@ -617,7 +560,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 11: + case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field InTxTrackerList", wireType) } @@ -651,7 +594,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 12: + case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ZetaAccounting", wireType) } @@ -684,7 +627,7 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 16: + case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field FinalizedInbounds", wireType) } diff --git a/x/crosschain/types/genesis_test.go b/x/crosschain/types/genesis_test.go index 2b8b5eb80f..d84d811bd9 100644 --- a/x/crosschain/types/genesis_test.go +++ b/x/crosschain/types/genesis_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/testutil/sample" "github.com/zeta-chain/zetacore/x/crosschain/types" ) @@ -47,6 +48,11 @@ func TestGenesisState_Validate(t *testing.T) { InTxHash: "1", }, }, + GasPriceList: []*types.GasPrice{ + sample.GasPrice(t, "0"), + sample.GasPrice(t, "1"), + sample.GasPrice(t, "2"), + }, }, valid: true, }, @@ -78,6 +84,20 @@ func TestGenesisState_Validate(t *testing.T) { }, valid: false, }, + { + desc: "duplicated gasPriceList", + genState: &types.GenesisState{ + GasPriceList: []*types.GasPrice{ + { + Index: "1", + }, + { + Index: "1", + }, + }, + }, + valid: false, + }, } { t.Run(tc.desc, func(t *testing.T) { err := tc.genState.Validate() 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/params.go b/x/crosschain/types/params.go deleted file mode 100644 index 7dda7b56d5..0000000000 --- a/x/crosschain/types/params.go +++ /dev/null @@ -1,44 +0,0 @@ -package types - -import ( - paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - "gopkg.in/yaml.v2" -) - -var _ paramtypes.ParamSet = (*Params)(nil) - -// ParamKeyTable the param key table for launch module -func ParamKeyTable() paramtypes.KeyTable { - return paramtypes.NewKeyTable().RegisterParamSet(&Params{}) -} - -// NewParams creates a new Params instance -func NewParams() Params { - return Params{ - Enabled: true, - } -} - -// DefaultParams returns a default set of parameters -func DefaultParams() Params { - return NewParams() -} - -// ParamSetPairs get the params.ParamSet -func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs { - return paramtypes.ParamSetPairs{} -} - -// Validate validates the set of params -func (p Params) Validate() error { - return nil -} - -// String implements the Stringer interface. -func (p Params) String() string { - out, err := yaml.Marshal(p) - if err != nil { - return "" - } - return string(out) -} diff --git a/x/crosschain/types/params.pb.go b/x/crosschain/types/params.pb.go deleted file mode 100644 index ad3305a765..0000000000 --- a/x/crosschain/types/params.pb.go +++ /dev/null @@ -1,307 +0,0 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: crosschain/params.proto - -package types - -import ( - fmt "fmt" - io "io" - math "math" - math_bits "math/bits" - - _ "github.com/cosmos/gogoproto/gogoproto" - proto "github.com/gogo/protobuf/proto" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package - -// Params defines the parameters for the module. -type Params struct { - Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` -} - -func (m *Params) Reset() { *m = Params{} } -func (*Params) ProtoMessage() {} -func (*Params) Descriptor() ([]byte, []int) { - return fileDescriptor_cd6915e32c251e53, []int{0} -} -func (m *Params) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_Params.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 *Params) XXX_Merge(src proto.Message) { - xxx_messageInfo_Params.Merge(m, src) -} -func (m *Params) XXX_Size() int { - return m.Size() -} -func (m *Params) XXX_DiscardUnknown() { - xxx_messageInfo_Params.DiscardUnknown(m) -} - -var xxx_messageInfo_Params proto.InternalMessageInfo - -func (m *Params) GetEnabled() bool { - if m != nil { - return m.Enabled - } - return false -} - -func init() { - proto.RegisterType((*Params)(nil), "zetachain.zetacore.crosschain.Params") -} - -func init() { proto.RegisterFile("crosschain/params.proto", fileDescriptor_cd6915e32c251e53) } - -var fileDescriptor_cd6915e32c251e53 = []byte{ - // 175 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4f, 0x2e, 0xca, 0x2f, - 0x2e, 0x4e, 0xce, 0x48, 0xcc, 0xcc, 0xd3, 0x2f, 0x48, 0x2c, 0x4a, 0xcc, 0x2d, 0xd6, 0x2b, 0x28, - 0xca, 0x2f, 0xc9, 0x17, 0x92, 0xad, 0x4a, 0x2d, 0x49, 0x04, 0x8b, 0xeb, 0x81, 0x59, 0xf9, 0x45, - 0xa9, 0x7a, 0x08, 0xb5, 0x52, 0x22, 0xe9, 0xf9, 0xe9, 0xf9, 0x60, 0x95, 0xfa, 0x20, 0x16, 0x44, - 0x93, 0x92, 0x06, 0x17, 0x5b, 0x00, 0xd8, 0x10, 0x21, 0x09, 0x2e, 0xf6, 0xd4, 0xbc, 0xc4, 0xa4, - 0x9c, 0xd4, 0x14, 0x09, 0x46, 0x05, 0x46, 0x0d, 0x8e, 0x20, 0x18, 0xd7, 0x8a, 0x65, 0xc6, 0x02, - 0x79, 0x06, 0x27, 0xef, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, - 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0x32, 0x4c, - 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x07, 0xd9, 0xac, 0x0b, 0x71, 0x1c, - 0xcc, 0x11, 0xfa, 0x15, 0xfa, 0x48, 0x4e, 0x2e, 0xa9, 0x2c, 0x48, 0x2d, 0x4e, 0x62, 0x03, 0xdb, - 0x6e, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x96, 0x4e, 0x4b, 0x16, 0xcd, 0x00, 0x00, 0x00, -} - -func (m *Params) 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 *Params) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Enabled { - i-- - if m.Enabled { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil -} - -func encodeVarintParams(dAtA []byte, offset int, v uint64) int { - offset -= sovParams(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return base -} -func (m *Params) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Enabled { - n += 2 - } - return n -} - -func sovParams(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 -} -func sozParams(x uint64) (n int) { - return sovParams(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (m *Params) 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 ErrIntOverflowParams - } - 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: Params: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Enabled", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowParams - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.Enabled = bool(v != 0) - default: - iNdEx = preIndex - skippy, err := skipParams(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthParams - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func skipParams(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - depth := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowParams - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowParams - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - case 1: - iNdEx += 8 - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowParams - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if length < 0 { - return 0, ErrInvalidLengthParams - } - iNdEx += length - case 3: - depth++ - case 4: - if depth == 0 { - return 0, ErrUnexpectedEndOfGroupParams - } - depth-- - case 5: - iNdEx += 4 - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - if iNdEx < 0 { - return 0, ErrInvalidLengthParams - } - if depth == 0 { - return iNdEx, nil - } - } - return 0, io.ErrUnexpectedEOF -} - -var ( - ErrInvalidLengthParams = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowParams = fmt.Errorf("proto: integer overflow") - ErrUnexpectedEndOfGroupParams = fmt.Errorf("proto: unexpected end of group") -) diff --git a/x/crosschain/types/query.pb.go b/x/crosschain/types/query.pb.go index 7273180766..af554543a3 100644 --- a/x/crosschain/types/query.pb.go +++ b/x/crosschain/types/query.pb.go @@ -111,89 +111,6 @@ func (m *QueryZetaAccountingResponse) GetAbortedZetaAmount() string { return "" } -// QueryParamsRequest is request type for the Query/Params RPC method. -type QueryParamsRequest struct { -} - -func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } -func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } -func (*QueryParamsRequest) ProtoMessage() {} -func (*QueryParamsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{2} -} -func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_QueryParamsRequest.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 *QueryParamsRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryParamsRequest.Merge(m, src) -} -func (m *QueryParamsRequest) XXX_Size() int { - return m.Size() -} -func (m *QueryParamsRequest) XXX_DiscardUnknown() { - xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo - -// QueryParamsResponse is response type for the Query/Params RPC method. -type QueryParamsResponse struct { - // params holds all the parameters of this module. - Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` -} - -func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } -func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } -func (*QueryParamsResponse) ProtoMessage() {} -func (*QueryParamsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{3} -} -func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_QueryParamsResponse.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 *QueryParamsResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_QueryParamsResponse.Merge(m, src) -} -func (m *QueryParamsResponse) XXX_Size() int { - return m.Size() -} -func (m *QueryParamsResponse) XXX_DiscardUnknown() { - xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo - -func (m *QueryParamsResponse) GetParams() Params { - if m != nil { - return m.Params - } - return Params{} -} - type QueryGetOutTxTrackerRequest struct { ChainID int64 `protobuf:"varint,1,opt,name=chainID,proto3" json:"chainID,omitempty"` Nonce uint64 `protobuf:"varint,2,opt,name=nonce,proto3" json:"nonce,omitempty"` @@ -203,7 +120,7 @@ func (m *QueryGetOutTxTrackerRequest) Reset() { *m = QueryGetOutTxTracke func (m *QueryGetOutTxTrackerRequest) String() string { return proto.CompactTextString(m) } func (*QueryGetOutTxTrackerRequest) ProtoMessage() {} func (*QueryGetOutTxTrackerRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{4} + return fileDescriptor_65a992045e92a606, []int{2} } func (m *QueryGetOutTxTrackerRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -254,7 +171,7 @@ func (m *QueryGetOutTxTrackerResponse) Reset() { *m = QueryGetOutTxTrack func (m *QueryGetOutTxTrackerResponse) String() string { return proto.CompactTextString(m) } func (*QueryGetOutTxTrackerResponse) ProtoMessage() {} func (*QueryGetOutTxTrackerResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{5} + return fileDescriptor_65a992045e92a606, []int{3} } func (m *QueryGetOutTxTrackerResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -298,7 +215,7 @@ func (m *QueryAllOutTxTrackerRequest) Reset() { *m = QueryAllOutTxTracke func (m *QueryAllOutTxTrackerRequest) String() string { return proto.CompactTextString(m) } func (*QueryAllOutTxTrackerRequest) ProtoMessage() {} func (*QueryAllOutTxTrackerRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{6} + return fileDescriptor_65a992045e92a606, []int{4} } func (m *QueryAllOutTxTrackerRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -343,7 +260,7 @@ func (m *QueryAllOutTxTrackerResponse) Reset() { *m = QueryAllOutTxTrack func (m *QueryAllOutTxTrackerResponse) String() string { return proto.CompactTextString(m) } func (*QueryAllOutTxTrackerResponse) ProtoMessage() {} func (*QueryAllOutTxTrackerResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{7} + return fileDescriptor_65a992045e92a606, []int{5} } func (m *QueryAllOutTxTrackerResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -395,7 +312,7 @@ func (m *QueryAllOutTxTrackerByChainRequest) Reset() { *m = QueryAllOutT func (m *QueryAllOutTxTrackerByChainRequest) String() string { return proto.CompactTextString(m) } func (*QueryAllOutTxTrackerByChainRequest) ProtoMessage() {} func (*QueryAllOutTxTrackerByChainRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{8} + return fileDescriptor_65a992045e92a606, []int{6} } func (m *QueryAllOutTxTrackerByChainRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -447,7 +364,7 @@ func (m *QueryAllOutTxTrackerByChainResponse) Reset() { *m = QueryAllOut func (m *QueryAllOutTxTrackerByChainResponse) String() string { return proto.CompactTextString(m) } func (*QueryAllOutTxTrackerByChainResponse) ProtoMessage() {} func (*QueryAllOutTxTrackerByChainResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{9} + return fileDescriptor_65a992045e92a606, []int{7} } func (m *QueryAllOutTxTrackerByChainResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -499,7 +416,7 @@ func (m *QueryAllInTxTrackerByChainRequest) Reset() { *m = QueryAllInTxT func (m *QueryAllInTxTrackerByChainRequest) String() string { return proto.CompactTextString(m) } func (*QueryAllInTxTrackerByChainRequest) ProtoMessage() {} func (*QueryAllInTxTrackerByChainRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{10} + return fileDescriptor_65a992045e92a606, []int{8} } func (m *QueryAllInTxTrackerByChainRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -551,7 +468,7 @@ func (m *QueryAllInTxTrackerByChainResponse) Reset() { *m = QueryAllInTx func (m *QueryAllInTxTrackerByChainResponse) String() string { return proto.CompactTextString(m) } func (*QueryAllInTxTrackerByChainResponse) ProtoMessage() {} func (*QueryAllInTxTrackerByChainResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{11} + return fileDescriptor_65a992045e92a606, []int{9} } func (m *QueryAllInTxTrackerByChainResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -602,7 +519,7 @@ func (m *QueryAllInTxTrackersRequest) Reset() { *m = QueryAllInTxTracker func (m *QueryAllInTxTrackersRequest) String() string { return proto.CompactTextString(m) } func (*QueryAllInTxTrackersRequest) ProtoMessage() {} func (*QueryAllInTxTrackersRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{12} + return fileDescriptor_65a992045e92a606, []int{10} } func (m *QueryAllInTxTrackersRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -647,7 +564,7 @@ func (m *QueryAllInTxTrackersResponse) Reset() { *m = QueryAllInTxTracke func (m *QueryAllInTxTrackersResponse) String() string { return proto.CompactTextString(m) } func (*QueryAllInTxTrackersResponse) ProtoMessage() {} func (*QueryAllInTxTrackersResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{13} + return fileDescriptor_65a992045e92a606, []int{11} } func (m *QueryAllInTxTrackersResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -698,7 +615,7 @@ func (m *QueryGetInTxHashToCctxRequest) Reset() { *m = QueryGetInTxHashT func (m *QueryGetInTxHashToCctxRequest) String() string { return proto.CompactTextString(m) } func (*QueryGetInTxHashToCctxRequest) ProtoMessage() {} func (*QueryGetInTxHashToCctxRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{14} + return fileDescriptor_65a992045e92a606, []int{12} } func (m *QueryGetInTxHashToCctxRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -742,7 +659,7 @@ func (m *QueryGetInTxHashToCctxResponse) Reset() { *m = QueryGetInTxHash func (m *QueryGetInTxHashToCctxResponse) String() string { return proto.CompactTextString(m) } func (*QueryGetInTxHashToCctxResponse) ProtoMessage() {} func (*QueryGetInTxHashToCctxResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{15} + return fileDescriptor_65a992045e92a606, []int{13} } func (m *QueryGetInTxHashToCctxResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -786,7 +703,7 @@ func (m *QueryInTxHashToCctxDataRequest) Reset() { *m = QueryInTxHashToC func (m *QueryInTxHashToCctxDataRequest) String() string { return proto.CompactTextString(m) } func (*QueryInTxHashToCctxDataRequest) ProtoMessage() {} func (*QueryInTxHashToCctxDataRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{16} + return fileDescriptor_65a992045e92a606, []int{14} } func (m *QueryInTxHashToCctxDataRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -830,7 +747,7 @@ func (m *QueryInTxHashToCctxDataResponse) Reset() { *m = QueryInTxHashTo func (m *QueryInTxHashToCctxDataResponse) String() string { return proto.CompactTextString(m) } func (*QueryInTxHashToCctxDataResponse) ProtoMessage() {} func (*QueryInTxHashToCctxDataResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{17} + return fileDescriptor_65a992045e92a606, []int{15} } func (m *QueryInTxHashToCctxDataResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -874,7 +791,7 @@ func (m *QueryAllInTxHashToCctxRequest) Reset() { *m = QueryAllInTxHashT func (m *QueryAllInTxHashToCctxRequest) String() string { return proto.CompactTextString(m) } func (*QueryAllInTxHashToCctxRequest) ProtoMessage() {} func (*QueryAllInTxHashToCctxRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{18} + return fileDescriptor_65a992045e92a606, []int{16} } func (m *QueryAllInTxHashToCctxRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -919,7 +836,7 @@ func (m *QueryAllInTxHashToCctxResponse) Reset() { *m = QueryAllInTxHash func (m *QueryAllInTxHashToCctxResponse) String() string { return proto.CompactTextString(m) } func (*QueryAllInTxHashToCctxResponse) ProtoMessage() {} func (*QueryAllInTxHashToCctxResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{19} + return fileDescriptor_65a992045e92a606, []int{17} } func (m *QueryAllInTxHashToCctxResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -970,7 +887,7 @@ func (m *QueryGetGasPriceRequest) Reset() { *m = QueryGetGasPriceRequest func (m *QueryGetGasPriceRequest) String() string { return proto.CompactTextString(m) } func (*QueryGetGasPriceRequest) ProtoMessage() {} func (*QueryGetGasPriceRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{20} + return fileDescriptor_65a992045e92a606, []int{18} } func (m *QueryGetGasPriceRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1014,7 +931,7 @@ func (m *QueryGetGasPriceResponse) Reset() { *m = QueryGetGasPriceRespon func (m *QueryGetGasPriceResponse) String() string { return proto.CompactTextString(m) } func (*QueryGetGasPriceResponse) ProtoMessage() {} func (*QueryGetGasPriceResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{21} + return fileDescriptor_65a992045e92a606, []int{19} } func (m *QueryGetGasPriceResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1058,7 +975,7 @@ func (m *QueryAllGasPriceRequest) Reset() { *m = QueryAllGasPriceRequest func (m *QueryAllGasPriceRequest) String() string { return proto.CompactTextString(m) } func (*QueryAllGasPriceRequest) ProtoMessage() {} func (*QueryAllGasPriceRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{22} + return fileDescriptor_65a992045e92a606, []int{20} } func (m *QueryAllGasPriceRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1103,7 +1020,7 @@ func (m *QueryAllGasPriceResponse) Reset() { *m = QueryAllGasPriceRespon func (m *QueryAllGasPriceResponse) String() string { return proto.CompactTextString(m) } func (*QueryAllGasPriceResponse) ProtoMessage() {} func (*QueryAllGasPriceResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{23} + return fileDescriptor_65a992045e92a606, []int{21} } func (m *QueryAllGasPriceResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1154,7 +1071,7 @@ func (m *QueryGetLastBlockHeightRequest) Reset() { *m = QueryGetLastBloc func (m *QueryGetLastBlockHeightRequest) String() string { return proto.CompactTextString(m) } func (*QueryGetLastBlockHeightRequest) ProtoMessage() {} func (*QueryGetLastBlockHeightRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{24} + return fileDescriptor_65a992045e92a606, []int{22} } func (m *QueryGetLastBlockHeightRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1198,7 +1115,7 @@ func (m *QueryGetLastBlockHeightResponse) Reset() { *m = QueryGetLastBlo func (m *QueryGetLastBlockHeightResponse) String() string { return proto.CompactTextString(m) } func (*QueryGetLastBlockHeightResponse) ProtoMessage() {} func (*QueryGetLastBlockHeightResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{25} + return fileDescriptor_65a992045e92a606, []int{23} } func (m *QueryGetLastBlockHeightResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1242,7 +1159,7 @@ func (m *QueryAllLastBlockHeightRequest) Reset() { *m = QueryAllLastBloc func (m *QueryAllLastBlockHeightRequest) String() string { return proto.CompactTextString(m) } func (*QueryAllLastBlockHeightRequest) ProtoMessage() {} func (*QueryAllLastBlockHeightRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{26} + return fileDescriptor_65a992045e92a606, []int{24} } func (m *QueryAllLastBlockHeightRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1287,7 +1204,7 @@ func (m *QueryAllLastBlockHeightResponse) Reset() { *m = QueryAllLastBlo func (m *QueryAllLastBlockHeightResponse) String() string { return proto.CompactTextString(m) } func (*QueryAllLastBlockHeightResponse) ProtoMessage() {} func (*QueryAllLastBlockHeightResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{27} + return fileDescriptor_65a992045e92a606, []int{25} } func (m *QueryAllLastBlockHeightResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1338,7 +1255,7 @@ func (m *QueryGetCctxRequest) Reset() { *m = QueryGetCctxRequest{} } func (m *QueryGetCctxRequest) String() string { return proto.CompactTextString(m) } func (*QueryGetCctxRequest) ProtoMessage() {} func (*QueryGetCctxRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{28} + return fileDescriptor_65a992045e92a606, []int{26} } func (m *QueryGetCctxRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1383,7 +1300,7 @@ func (m *QueryGetCctxByNonceRequest) Reset() { *m = QueryGetCctxByNonceR func (m *QueryGetCctxByNonceRequest) String() string { return proto.CompactTextString(m) } func (*QueryGetCctxByNonceRequest) ProtoMessage() {} func (*QueryGetCctxByNonceRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{29} + return fileDescriptor_65a992045e92a606, []int{27} } func (m *QueryGetCctxByNonceRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1434,7 +1351,7 @@ func (m *QueryGetCctxResponse) Reset() { *m = QueryGetCctxResponse{} } func (m *QueryGetCctxResponse) String() string { return proto.CompactTextString(m) } func (*QueryGetCctxResponse) ProtoMessage() {} func (*QueryGetCctxResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{30} + return fileDescriptor_65a992045e92a606, []int{28} } func (m *QueryGetCctxResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1478,7 +1395,7 @@ func (m *QueryAllCctxRequest) Reset() { *m = QueryAllCctxRequest{} } func (m *QueryAllCctxRequest) String() string { return proto.CompactTextString(m) } func (*QueryAllCctxRequest) ProtoMessage() {} func (*QueryAllCctxRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{31} + return fileDescriptor_65a992045e92a606, []int{29} } func (m *QueryAllCctxRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1523,7 +1440,7 @@ func (m *QueryAllCctxResponse) Reset() { *m = QueryAllCctxResponse{} } func (m *QueryAllCctxResponse) String() string { return proto.CompactTextString(m) } func (*QueryAllCctxResponse) ProtoMessage() {} func (*QueryAllCctxResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{32} + return fileDescriptor_65a992045e92a606, []int{30} } func (m *QueryAllCctxResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1575,7 +1492,7 @@ func (m *QueryListCctxPendingRequest) Reset() { *m = QueryListCctxPendin func (m *QueryListCctxPendingRequest) String() string { return proto.CompactTextString(m) } func (*QueryListCctxPendingRequest) ProtoMessage() {} func (*QueryListCctxPendingRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{33} + return fileDescriptor_65a992045e92a606, []int{31} } func (m *QueryListCctxPendingRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1627,7 +1544,7 @@ func (m *QueryListCctxPendingResponse) Reset() { *m = QueryListCctxPendi func (m *QueryListCctxPendingResponse) String() string { return proto.CompactTextString(m) } func (*QueryListCctxPendingResponse) ProtoMessage() {} func (*QueryListCctxPendingResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{34} + return fileDescriptor_65a992045e92a606, []int{32} } func (m *QueryListCctxPendingResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1677,7 +1594,7 @@ func (m *QueryLastZetaHeightRequest) Reset() { *m = QueryLastZetaHeightR func (m *QueryLastZetaHeightRequest) String() string { return proto.CompactTextString(m) } func (*QueryLastZetaHeightRequest) ProtoMessage() {} func (*QueryLastZetaHeightRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{35} + return fileDescriptor_65a992045e92a606, []int{33} } func (m *QueryLastZetaHeightRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1714,7 +1631,7 @@ func (m *QueryLastZetaHeightResponse) Reset() { *m = QueryLastZetaHeight func (m *QueryLastZetaHeightResponse) String() string { return proto.CompactTextString(m) } func (*QueryLastZetaHeightResponse) ProtoMessage() {} func (*QueryLastZetaHeightResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{36} + return fileDescriptor_65a992045e92a606, []int{34} } func (m *QueryLastZetaHeightResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1759,7 +1676,7 @@ func (m *QueryConvertGasToZetaRequest) Reset() { *m = QueryConvertGasToZ func (m *QueryConvertGasToZetaRequest) String() string { return proto.CompactTextString(m) } func (*QueryConvertGasToZetaRequest) ProtoMessage() {} func (*QueryConvertGasToZetaRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{37} + return fileDescriptor_65a992045e92a606, []int{35} } func (m *QueryConvertGasToZetaRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1812,7 +1729,7 @@ func (m *QueryConvertGasToZetaResponse) Reset() { *m = QueryConvertGasTo func (m *QueryConvertGasToZetaResponse) String() string { return proto.CompactTextString(m) } func (*QueryConvertGasToZetaResponse) ProtoMessage() {} func (*QueryConvertGasToZetaResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{38} + return fileDescriptor_65a992045e92a606, []int{36} } func (m *QueryConvertGasToZetaResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1869,7 +1786,7 @@ func (m *QueryMessagePassingProtocolFeeRequest) Reset() { *m = QueryMess func (m *QueryMessagePassingProtocolFeeRequest) String() string { return proto.CompactTextString(m) } func (*QueryMessagePassingProtocolFeeRequest) ProtoMessage() {} func (*QueryMessagePassingProtocolFeeRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{39} + return fileDescriptor_65a992045e92a606, []int{37} } func (m *QueryMessagePassingProtocolFeeRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1908,7 +1825,7 @@ func (m *QueryMessagePassingProtocolFeeResponse) Reset() { func (m *QueryMessagePassingProtocolFeeResponse) String() string { return proto.CompactTextString(m) } func (*QueryMessagePassingProtocolFeeResponse) ProtoMessage() {} func (*QueryMessagePassingProtocolFeeResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{40} + return fileDescriptor_65a992045e92a606, []int{38} } func (m *QueryMessagePassingProtocolFeeResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1947,8 +1864,6 @@ func (m *QueryMessagePassingProtocolFeeResponse) GetFeeInZeta() string { func init() { proto.RegisterType((*QueryZetaAccountingRequest)(nil), "zetachain.zetacore.crosschain.QueryZetaAccountingRequest") proto.RegisterType((*QueryZetaAccountingResponse)(nil), "zetachain.zetacore.crosschain.QueryZetaAccountingResponse") - proto.RegisterType((*QueryParamsRequest)(nil), "zetachain.zetacore.crosschain.QueryParamsRequest") - proto.RegisterType((*QueryParamsResponse)(nil), "zetachain.zetacore.crosschain.QueryParamsResponse") proto.RegisterType((*QueryGetOutTxTrackerRequest)(nil), "zetachain.zetacore.crosschain.QueryGetOutTxTrackerRequest") proto.RegisterType((*QueryGetOutTxTrackerResponse)(nil), "zetachain.zetacore.crosschain.QueryGetOutTxTrackerResponse") proto.RegisterType((*QueryAllOutTxTrackerRequest)(nil), "zetachain.zetacore.crosschain.QueryAllOutTxTrackerRequest") @@ -1991,119 +1906,115 @@ func init() { func init() { proto.RegisterFile("crosschain/query.proto", fileDescriptor_65a992045e92a606) } var fileDescriptor_65a992045e92a606 = []byte{ - // 1782 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x59, 0xdf, 0x6f, 0x14, 0x55, - 0x14, 0xee, 0xed, 0xd2, 0x52, 0x6e, 0x0b, 0x95, 0x4b, 0x85, 0x3a, 0xb4, 0x5b, 0x98, 0x5a, 0x5a, - 0xc1, 0xee, 0xd0, 0x02, 0x45, 0xa0, 0x18, 0xb7, 0x45, 0x0a, 0xb1, 0x40, 0xdd, 0xd4, 0x68, 0x30, - 0x66, 0x73, 0x3b, 0x3b, 0xce, 0x4e, 0x98, 0xce, 0x94, 0x9d, 0x59, 0xd2, 0xd2, 0xf4, 0x85, 0x07, - 0x5f, 0x7c, 0x31, 0x21, 0xd1, 0x17, 0x5f, 0x8d, 0x3e, 0xf8, 0xe0, 0x83, 0xd1, 0x07, 0x13, 0x8c, - 0x51, 0x91, 0x47, 0x12, 0x13, 0x63, 0x34, 0x31, 0x06, 0xfc, 0x43, 0xcc, 0xdc, 0x39, 0xb3, 0x7b, - 0xe7, 0xd7, 0xee, 0xed, 0x76, 0x79, 0xe0, 0xa9, 0x3b, 0x73, 0xef, 0x39, 0xe7, 0xfb, 0xbe, 0xfb, - 0x63, 0xce, 0x39, 0xc5, 0x07, 0xd5, 0x8a, 0xed, 0x38, 0x6a, 0x99, 0x1a, 0x96, 0x72, 0xbb, 0xaa, - 0x55, 0x36, 0x72, 0x6b, 0x15, 0xdb, 0xb5, 0xc9, 0xf0, 0x5d, 0xcd, 0xa5, 0xec, 0x75, 0x8e, 0xfd, - 0xb2, 0x2b, 0x5a, 0xae, 0x3e, 0x55, 0x3a, 0xae, 0xda, 0xce, 0xaa, 0xed, 0x28, 0x2b, 0xd4, 0xd1, - 0x7c, 0x3b, 0xe5, 0xce, 0xd4, 0x8a, 0xe6, 0xd2, 0x29, 0x65, 0x8d, 0xea, 0x86, 0x45, 0x5d, 0xc3, - 0xb6, 0x7c, 0x57, 0xd2, 0x08, 0x17, 0x82, 0xfd, 0x2c, 0xb2, 0xdf, 0x45, 0x77, 0x1d, 0x26, 0x48, - 0xdc, 0x04, 0x9d, 0x3a, 0xc5, 0xb5, 0x8a, 0xa1, 0x6a, 0x30, 0x36, 0xca, 0x8d, 0x31, 0x9b, 0x62, - 0x99, 0x3a, 0xe5, 0xa2, 0x6b, 0x17, 0x55, 0xb5, 0xe6, 0x20, 0x1b, 0x9b, 0xe4, 0x56, 0xa8, 0x7a, - 0x4b, 0xab, 0xc0, 0xb8, 0xcc, 0x8d, 0x9b, 0xd4, 0x71, 0x8b, 0x2b, 0xa6, 0xad, 0xde, 0x2a, 0x96, - 0x35, 0x43, 0x2f, 0xbb, 0x09, 0x28, 0xed, 0xaa, 0x1b, 0x77, 0x72, 0x88, 0x9b, 0xb0, 0x46, 0x2b, - 0x74, 0xd5, 0x81, 0x81, 0x01, 0xdd, 0xd6, 0x6d, 0xf6, 0x53, 0xf1, 0x7e, 0xc1, 0xdb, 0x21, 0xdd, - 0xb6, 0x75, 0x53, 0x53, 0xe8, 0x9a, 0xa1, 0x50, 0xcb, 0xb2, 0x5d, 0x26, 0x09, 0xd8, 0xc8, 0x43, - 0x58, 0x7a, 0xdb, 0x53, 0xed, 0xa6, 0xe6, 0xd2, 0xbc, 0xaa, 0xda, 0x55, 0xcb, 0x35, 0x2c, 0xbd, - 0xa0, 0xdd, 0xae, 0x6a, 0x8e, 0x2b, 0x5f, 0xc3, 0x87, 0x13, 0x47, 0x9d, 0x35, 0xdb, 0x72, 0x34, - 0x92, 0xc3, 0x07, 0xe8, 0x8a, 0x5d, 0x71, 0xb5, 0x52, 0xd1, 0x5b, 0x9b, 0x22, 0x5d, 0xf5, 0x66, - 0x0c, 0xa2, 0x23, 0x68, 0x62, 0x4f, 0x61, 0x3f, 0x0c, 0x31, 0x5b, 0x36, 0x20, 0x0f, 0x60, 0xc2, - 0xdc, 0x2d, 0x31, 0xd4, 0x41, 0x90, 0x9b, 0xf8, 0x40, 0xe8, 0x2d, 0x38, 0x9f, 0xc7, 0xdd, 0x3e, - 0x3b, 0xe6, 0xaf, 0x77, 0x7a, 0x2c, 0xd7, 0x70, 0x27, 0xe4, 0x7c, 0xf3, 0xb9, 0x5d, 0x8f, 0xfe, - 0x19, 0xe9, 0x28, 0x80, 0x69, 0x8d, 0xc0, 0x82, 0xe6, 0xde, 0xa8, 0xba, 0xcb, 0xeb, 0xcb, 0xbe, - 0x92, 0x10, 0x9a, 0x0c, 0xe2, 0xdd, 0xcc, 0xf8, 0xea, 0x25, 0x16, 0x24, 0x53, 0x08, 0x1e, 0xc9, - 0x00, 0xee, 0xb2, 0x6c, 0x4b, 0xd5, 0x06, 0x3b, 0x8f, 0xa0, 0x89, 0x5d, 0x05, 0xff, 0x41, 0xae, - 0xe2, 0xa1, 0x64, 0x77, 0x80, 0xf9, 0x1d, 0xdc, 0x67, 0x73, 0xef, 0x01, 0xf9, 0x89, 0x26, 0xc8, - 0x79, 0x57, 0x80, 0x3f, 0xe4, 0x46, 0xd6, 0x80, 0x45, 0xde, 0x34, 0x93, 0x58, 0x5c, 0xc6, 0xb8, - 0xbe, 0xd7, 0x21, 0xe6, 0xb1, 0x9c, 0x7f, 0x30, 0x72, 0xde, 0xc1, 0xc8, 0xf9, 0x07, 0x0a, 0x0e, - 0x46, 0x6e, 0x89, 0xea, 0x1a, 0xd8, 0x16, 0x38, 0x4b, 0xf9, 0x01, 0x02, 0x7a, 0xb1, 0x38, 0xa9, - 0xf4, 0x32, 0x6d, 0xa0, 0x47, 0x16, 0x42, 0xf8, 0x3b, 0x19, 0xfe, 0xf1, 0xa6, 0xf8, 0x7d, 0x4c, - 0x21, 0x02, 0xf7, 0x10, 0x96, 0x93, 0x08, 0xcc, 0x6d, 0xcc, 0x7b, 0x48, 0x02, 0xbd, 0x06, 0x70, - 0x17, 0x43, 0x06, 0x6b, 0xee, 0x3f, 0x44, 0x54, 0xec, 0x6c, 0x59, 0xc5, 0x5f, 0x11, 0x1e, 0x6d, - 0x08, 0xe2, 0x39, 0x11, 0xf3, 0x23, 0x84, 0x8f, 0x06, 0x3c, 0xae, 0x5a, 0x69, 0x5a, 0xbe, 0x84, - 0x7b, 0xfc, 0x4b, 0xd4, 0x28, 0x85, 0x8f, 0x50, 0xa9, 0x6d, 0x82, 0xfe, 0xc4, 0xad, 0x6a, 0x12, - 0x10, 0xd0, 0xb3, 0x80, 0x7b, 0x0d, 0x2b, 0x2a, 0xe7, 0xf1, 0x26, 0x72, 0xf2, 0xfe, 0x7c, 0x35, - 0x79, 0x27, 0xed, 0x13, 0x93, 0x3b, 0xc1, 0x5c, 0x48, 0xa7, 0xdd, 0x27, 0xf8, 0x07, 0xee, 0x04, - 0x87, 0xe3, 0x3c, 0x0f, 0x22, 0x5d, 0xc0, 0xc3, 0xc1, 0xed, 0xea, 0x85, 0xbc, 0x42, 0x9d, 0xf2, - 0xb2, 0x3d, 0xaf, 0xba, 0xeb, 0x81, 0x4c, 0x12, 0xee, 0x31, 0x60, 0x00, 0x3e, 0x32, 0xb5, 0x67, - 0x79, 0x0b, 0x67, 0xd3, 0x8c, 0x81, 0xfb, 0xfb, 0x78, 0x9f, 0x11, 0x1a, 0x01, 0xa1, 0x27, 0x05, - 0xe8, 0xd7, 0x8d, 0x40, 0x81, 0x88, 0x2b, 0x79, 0x16, 0xc2, 0x87, 0x27, 0x5f, 0xa2, 0x2e, 0x15, - 0x01, 0x7f, 0x17, 0x8f, 0xa4, 0x5a, 0x03, 0xfa, 0x77, 0xf1, 0xde, 0x79, 0x0f, 0x13, 0xdb, 0xf4, - 0xcb, 0xeb, 0x8e, 0xe0, 0x7d, 0xc1, 0xdb, 0x00, 0xf4, 0xb0, 0x1f, 0x59, 0x07, 0xd5, 0x61, 0xcb, - 0xc4, 0x55, 0x6f, 0xd7, 0xe6, 0x7c, 0x88, 0x40, 0xa3, 0x84, 0x48, 0x0d, 0x96, 0x28, 0xd3, 0xa6, - 0x25, 0x6a, 0xdf, 0x3e, 0x55, 0xf0, 0xa1, 0x60, 0xab, 0x2d, 0x50, 0x67, 0xc9, 0x4b, 0x12, 0xb9, - 0x4f, 0x8b, 0x61, 0x95, 0xb4, 0x75, 0x58, 0x61, 0xff, 0x41, 0x2e, 0xe2, 0xc1, 0xb8, 0x41, 0x2d, - 0xcd, 0xe9, 0x09, 0xde, 0x81, 0xb6, 0xe3, 0x4d, 0xc8, 0xd6, 0x5c, 0xd4, 0x0c, 0x65, 0x0a, 0x88, - 0xf2, 0xa6, 0x19, 0x45, 0xd4, 0xae, 0xd5, 0xfb, 0x0a, 0x01, 0x89, 0x50, 0x8c, 0x44, 0x12, 0x99, - 0x96, 0x48, 0xb4, 0x6f, 0x7d, 0x66, 0xea, 0x57, 0xc1, 0x22, 0x75, 0xdc, 0x39, 0x2f, 0xc7, 0xbe, - 0xc2, 0x52, 0xec, 0xc6, 0xcb, 0xb4, 0x09, 0xa7, 0x30, 0xc9, 0x0e, 0x88, 0xbe, 0x87, 0xfb, 0x23, - 0x43, 0x20, 0x69, 0xae, 0x09, 0xdf, 0xa8, 0xc3, 0xa8, 0x1b, 0xb9, 0x5c, 0x3f, 0x1c, 0x29, 0xa0, - 0xdb, 0xb5, 0x92, 0xbf, 0x20, 0xe0, 0x99, 0x14, 0xaa, 0x11, 0xcf, 0x4c, 0x1b, 0x78, 0xb6, 0x6f, - 0x95, 0x4f, 0x40, 0xd9, 0xb0, 0xa0, 0xb9, 0xfc, 0x6d, 0x95, 0xbc, 0xb4, 0x8b, 0x50, 0xe6, 0xc0, - 0xe4, 0xb9, 0x8d, 0xeb, 0x5e, 0x3e, 0xdf, 0x6a, 0x19, 0xa0, 0xe3, 0x81, 0x70, 0x68, 0x50, 0xed, - 0x06, 0xee, 0xe3, 0xef, 0x56, 0xc1, 0xf4, 0x9f, 0x37, 0x29, 0x84, 0x1c, 0xc8, 0x1f, 0x00, 0xc7, - 0xbc, 0x69, 0x3e, 0x8b, 0x1b, 0xf9, 0x1b, 0x04, 0x44, 0x6a, 0xfe, 0x53, 0x89, 0x64, 0x76, 0x44, - 0xa4, 0x7d, 0xab, 0x7e, 0x1d, 0x12, 0xa9, 0x45, 0xc3, 0x61, 0xda, 0x2f, 0x69, 0x56, 0xa9, 0x5e, - 0xb0, 0x36, 0x4a, 0x47, 0x07, 0x70, 0x97, 0x69, 0xac, 0x1a, 0x2e, 0x8b, 0xbe, 0xb7, 0xe0, 0x3f, - 0xc8, 0xf7, 0x83, 0x8c, 0x29, 0xe6, 0xf0, 0x59, 0x49, 0x21, 0xe3, 0x3e, 0xd7, 0x76, 0xa9, 0x09, - 0x81, 0x60, 0x67, 0x85, 0xde, 0xd5, 0xaa, 0x72, 0xef, 0xf0, 0x78, 0xf5, 0x73, 0xe8, 0x22, 0x90, - 0xcf, 0x04, 0x1a, 0x44, 0x46, 0x01, 0xf1, 0x41, 0xdc, 0xcd, 0x5d, 0x4d, 0x99, 0x02, 0x3c, 0xc9, - 0xcb, 0xc0, 0x74, 0xde, 0xb6, 0xee, 0x68, 0x15, 0xef, 0x4b, 0xb4, 0x6c, 0x7b, 0xe6, 0xb1, 0x53, - 0x10, 0x93, 0x4e, 0xc2, 0x3d, 0x3a, 0x75, 0x16, 0x6b, 0xea, 0xed, 0x29, 0xd4, 0x9e, 0xe5, 0x2f, - 0x10, 0xe4, 0x0f, 0x71, 0xb7, 0x80, 0xe7, 0x55, 0xbc, 0xdf, 0xae, 0xba, 0x2b, 0x76, 0xd5, 0x2a, - 0x2d, 0x50, 0xe7, 0xaa, 0xe5, 0x0d, 0x06, 0x3d, 0x82, 0xd8, 0x80, 0x37, 0x9b, 0x75, 0x26, 0x54, - 0xdb, 0xbc, 0xac, 0x69, 0x30, 0xdb, 0x0f, 0x1a, 0x1f, 0x20, 0x13, 0xb8, 0xdf, 0xfb, 0xcb, 0xdf, - 0x53, 0x19, 0xa6, 0x67, 0xf4, 0xb5, 0x3c, 0x8e, 0xc7, 0x18, 0xcc, 0x6b, 0x9a, 0xe3, 0x50, 0x5d, - 0x5b, 0xa2, 0x8e, 0x63, 0x58, 0xfa, 0x52, 0xdd, 0x63, 0xa0, 0xee, 0x65, 0x7c, 0xac, 0xd9, 0x44, - 0x20, 0x36, 0x84, 0xf7, 0x7c, 0x58, 0x83, 0xe8, 0x13, 0xaa, 0xbf, 0x98, 0xfe, 0x78, 0x04, 0x77, - 0x31, 0x47, 0xe4, 0x53, 0x84, 0xbb, 0xfd, 0xee, 0x04, 0x99, 0x6a, 0xb2, 0x6f, 0xe2, 0xed, 0x11, - 0x69, 0x7a, 0x3b, 0x26, 0x3e, 0x32, 0x79, 0xec, 0xde, 0xef, 0xff, 0xdd, 0xef, 0x1c, 0x21, 0xc3, - 0x8a, 0x67, 0x31, 0xc9, 0xb5, 0xbc, 0xf8, 0xb6, 0x11, 0x79, 0x88, 0x70, 0x1f, 0x5f, 0x50, 0x92, - 0xf3, 0x22, 0xb1, 0x92, 0x7b, 0x29, 0xd2, 0x85, 0x96, 0x6c, 0x01, 0xf0, 0x45, 0x06, 0xf8, 0x2c, - 0x39, 0x93, 0x02, 0x98, 0x2f, 0x71, 0x95, 0x4d, 0xb8, 0x9d, 0xb7, 0x94, 0x4d, 0x76, 0x1f, 0x6f, - 0x91, 0xef, 0x11, 0xee, 0xe7, 0xfd, 0xe6, 0x4d, 0x53, 0x8c, 0x4b, 0x72, 0x47, 0x45, 0x8c, 0x4b, - 0x4a, 0x97, 0x44, 0x3e, 0xc1, 0xb8, 0x8c, 0x91, 0x51, 0x01, 0x2e, 0xe4, 0x6f, 0x84, 0x0f, 0x46, - 0x90, 0x43, 0x61, 0x4b, 0xf2, 0x2d, 0x80, 0x08, 0x57, 0xe7, 0xd2, 0xdc, 0x4e, 0x5c, 0x00, 0x9d, - 0xf3, 0x8c, 0xce, 0x69, 0x32, 0x2d, 0x40, 0x07, 0x6c, 0x61, 0x85, 0xb6, 0xc8, 0x5f, 0x08, 0xbf, - 0xc8, 0x55, 0x8f, 0x1c, 0xb9, 0x37, 0x04, 0x91, 0xa5, 0x76, 0x1e, 0xa4, 0xfc, 0x0e, 0x3c, 0x00, - 0xb5, 0x59, 0x46, 0x6d, 0x86, 0x9c, 0x4e, 0xa1, 0x66, 0x58, 0x29, 0xcc, 0x8a, 0x46, 0x69, 0x8b, - 0x7c, 0x87, 0xf0, 0xbe, 0x30, 0x39, 0xe1, 0x3d, 0x97, 0xd0, 0x03, 0x10, 0xde, 0x73, 0x49, 0x75, - 0x7d, 0xd3, 0x3d, 0xc7, 0x31, 0x71, 0xc8, 0x6f, 0x00, 0x9c, 0xab, 0x8d, 0x66, 0x05, 0x0f, 0x6f, - 0x62, 0x85, 0x28, 0x5d, 0x6c, 0xd1, 0x1a, 0xc0, 0xbf, 0xc6, 0xc0, 0x4f, 0x93, 0x93, 0x0d, 0xc0, - 0xd7, 0xcd, 0x94, 0xcd, 0xe0, 0x79, 0x8b, 0xfc, 0x81, 0x30, 0x89, 0xd7, 0xcc, 0x44, 0x08, 0x4f, - 0x6a, 0xa5, 0x2e, 0xbd, 0xde, 0xaa, 0x39, 0xf0, 0xc9, 0x33, 0x3e, 0x17, 0xc8, 0xb9, 0x54, 0x3e, - 0xd1, 0x7f, 0x1f, 0x14, 0x4b, 0xd4, 0xa5, 0x3c, 0xb1, 0x1f, 0x11, 0xde, 0x1f, 0x8e, 0xe0, 0x6d, - 0xaf, 0xd9, 0x6d, 0x6c, 0x91, 0x16, 0x57, 0x29, 0xb5, 0x36, 0x97, 0x27, 0x19, 0xab, 0x71, 0x32, - 0x26, 0xb4, 0x4a, 0xe4, 0x6b, 0x54, 0xaf, 0x09, 0xc9, 0x8c, 0xe0, 0x06, 0x89, 0x14, 0xaf, 0xd2, - 0xd9, 0x6d, 0xdb, 0x01, 0x58, 0x85, 0x81, 0x7d, 0x85, 0x8c, 0xa7, 0x80, 0xd5, 0xc1, 0xc0, 0xd3, - 0xbc, 0xa4, 0xad, 0x6f, 0x91, 0x2f, 0x11, 0xee, 0x0d, 0xbc, 0x78, 0x52, 0xcf, 0x08, 0x8a, 0xd5, - 0x12, 0xe2, 0x84, 0x12, 0x5a, 0x1e, 0x67, 0x88, 0x8f, 0x92, 0x91, 0x26, 0x88, 0xc9, 0x03, 0x84, - 0x5f, 0x88, 0xe6, 0x5a, 0x44, 0xe8, 0xf2, 0x48, 0x49, 0xfc, 0xa4, 0xd9, 0xd6, 0x8c, 0x05, 0xa5, - 0x56, 0xa3, 0x58, 0x1f, 0x22, 0xdc, 0xcb, 0xa5, 0x53, 0xe4, 0x92, 0x48, 0xf8, 0x66, 0x69, 0x9b, - 0xf4, 0xe6, 0x0e, 0xbd, 0x00, 0x9b, 0xe3, 0x8c, 0xcd, 0xcb, 0x44, 0x4e, 0xcb, 0x9c, 0x38, 0xe0, - 0x8f, 0x50, 0xac, 0x4a, 0x26, 0xa2, 0x57, 0x61, 0x72, 0x8d, 0x2f, 0x76, 0xf5, 0xa4, 0xf7, 0x27, - 0xe4, 0x19, 0x06, 0xff, 0x24, 0xc9, 0xa5, 0xc0, 0x37, 0xc3, 0x76, 0xb5, 0xed, 0xff, 0x33, 0xc2, - 0x24, 0xe2, 0xd3, 0x3b, 0x05, 0xa2, 0x57, 0xc6, 0x4e, 0xd8, 0xa4, 0x77, 0x21, 0xe4, 0x1c, 0x63, - 0x33, 0x41, 0x8e, 0x89, 0xb1, 0x21, 0x9f, 0x23, 0xbc, 0x8b, 0x5d, 0x3e, 0xd3, 0x82, 0x32, 0xf2, - 0xd7, 0xe3, 0xa9, 0x6d, 0xd9, 0x08, 0x7e, 0x77, 0x55, 0xf8, 0x60, 0x31, 0x91, 0xbf, 0x45, 0xb8, - 0x97, 0xeb, 0x3e, 0x90, 0x73, 0xdb, 0x88, 0x18, 0xee, 0x58, 0xb4, 0x06, 0xf6, 0x0c, 0x03, 0xab, - 0x90, 0xc9, 0x86, 0x60, 0x63, 0xc9, 0xf5, 0x67, 0x08, 0xef, 0x0e, 0xbe, 0x40, 0xd3, 0x82, 0x2b, - 0xba, 0x6d, 0x61, 0x23, 0x1d, 0x08, 0x79, 0x94, 0x61, 0x1d, 0x26, 0x87, 0x1b, 0x60, 0xf5, 0x32, - 0xb0, 0x7e, 0xcf, 0xca, 0xab, 0xdd, 0xa1, 0x74, 0x16, 0x4b, 0xc1, 0x92, 0xbb, 0x07, 0x62, 0x29, - 0x58, 0x4a, 0xa3, 0xa0, 0xe9, 0xcd, 0xa1, 0xd6, 0x6d, 0x58, 0xea, 0x18, 0xfe, 0x9f, 0xba, 0xd8, - 0x66, 0x48, 0xfc, 0x2f, 0xbd, 0x74, 0xbe, 0x15, 0x53, 0xc1, 0xaf, 0xfa, 0xdd, 0x30, 0x4a, 0x0f, - 0x78, 0xb8, 0xed, 0x20, 0x06, 0x3c, 0xb1, 0x91, 0x21, 0x06, 0x3c, 0xb9, 0xcb, 0xd1, 0x14, 0xb8, - 0x19, 0x32, 0x9b, 0x7b, 0xeb, 0xd1, 0x93, 0x2c, 0x7a, 0xfc, 0x24, 0x8b, 0xfe, 0x7d, 0x92, 0x45, - 0x9f, 0x3c, 0xcd, 0x76, 0x3c, 0x7e, 0x9a, 0xed, 0xf8, 0xf3, 0x69, 0xb6, 0xe3, 0xe6, 0x94, 0x6e, - 0xb8, 0xe5, 0xea, 0x4a, 0x4e, 0xb5, 0x57, 0x79, 0x57, 0x01, 0x1e, 0x65, 0x9d, 0xf7, 0xea, 0x6e, - 0xac, 0x69, 0xce, 0x4a, 0x37, 0xfb, 0x0a, 0x9c, 0xfa, 0x3f, 0x00, 0x00, 0xff, 0xff, 0xf5, 0x77, - 0x46, 0x98, 0xb4, 0x22, 0x00, 0x00, + // 1715 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x59, 0xd1, 0x6f, 0x14, 0x45, + 0x18, 0xef, 0xf4, 0x28, 0x94, 0x69, 0xa1, 0x32, 0x54, 0xac, 0x4b, 0x7b, 0x85, 0xad, 0xd0, 0x0a, + 0xf6, 0x16, 0x0a, 0x14, 0x81, 0x62, 0xbc, 0x16, 0x29, 0xc4, 0x02, 0xf5, 0x52, 0xa3, 0xc1, 0x98, + 0xcb, 0x74, 0x6f, 0xdd, 0xdb, 0xb0, 0xdd, 0x29, 0xb7, 0x7b, 0xa4, 0xa5, 0xe9, 0x0b, 0x0f, 0x3e, + 0x9b, 0xf0, 0xe0, 0x8b, 0xaf, 0x46, 0x1f, 0x7c, 0xf0, 0xc1, 0xe8, 0x83, 0x09, 0xc6, 0xa8, 0xc8, + 0x23, 0x89, 0x89, 0x31, 0x9a, 0x18, 0x03, 0xfe, 0x05, 0xfe, 0x05, 0x66, 0x67, 0xbf, 0xbd, 0x9b, + 0xdd, 0xdb, 0xbd, 0x9b, 0x5e, 0x8f, 0x07, 0x9e, 0x7a, 0xbb, 0x33, 0xdf, 0x37, 0xbf, 0xdf, 0x6f, + 0xbe, 0xf9, 0xf6, 0xfb, 0xa6, 0xf8, 0x80, 0x5e, 0x61, 0xae, 0xab, 0x97, 0xa9, 0xe5, 0x68, 0xb7, + 0xab, 0x46, 0x65, 0x3d, 0xb7, 0x5a, 0x61, 0x1e, 0x23, 0x23, 0x77, 0x0d, 0x8f, 0xf2, 0xd7, 0x39, + 0xfe, 0x8b, 0x55, 0x8c, 0x5c, 0x7d, 0xaa, 0x72, 0x4c, 0x67, 0xee, 0x0a, 0x73, 0xb5, 0x65, 0xea, + 0x1a, 0x81, 0x9d, 0x76, 0xe7, 0xe4, 0xb2, 0xe1, 0xd1, 0x93, 0xda, 0x2a, 0x35, 0x2d, 0x87, 0x7a, + 0x16, 0x73, 0x02, 0x57, 0xca, 0xa8, 0xb0, 0x04, 0xff, 0x59, 0xe4, 0xbf, 0x8b, 0xde, 0x1a, 0x4c, + 0x50, 0x84, 0x09, 0x26, 0x75, 0x8b, 0xab, 0x15, 0x4b, 0x37, 0x60, 0x6c, 0x4c, 0x18, 0xe3, 0x36, + 0xc5, 0x32, 0x75, 0xcb, 0x45, 0x8f, 0x15, 0x75, 0xbd, 0xe6, 0x20, 0xdb, 0x30, 0xc9, 0xab, 0x50, + 0xfd, 0x96, 0x51, 0x81, 0x71, 0x55, 0x18, 0xb7, 0xa9, 0xeb, 0x15, 0x97, 0x6d, 0xa6, 0xdf, 0x2a, + 0x96, 0x0d, 0xcb, 0x2c, 0x7b, 0x09, 0x28, 0x59, 0xd5, 0x6b, 0x74, 0x32, 0x68, 0x32, 0x93, 0xf1, + 0x9f, 0x9a, 0xff, 0x0b, 0xde, 0x0e, 0x9b, 0x8c, 0x99, 0xb6, 0xa1, 0xd1, 0x55, 0x4b, 0xa3, 0x8e, + 0xc3, 0x3c, 0xce, 0xdc, 0x0d, 0x46, 0xd5, 0x61, 0xac, 0xbc, 0xe3, 0x8b, 0x73, 0xd3, 0xf0, 0x68, + 0x5e, 0xd7, 0x59, 0xd5, 0xf1, 0x2c, 0xc7, 0x2c, 0x18, 0xb7, 0xab, 0x86, 0xeb, 0xa9, 0xd7, 0xf0, + 0xc1, 0xc4, 0x51, 0x77, 0x95, 0x39, 0xae, 0x41, 0x72, 0x78, 0x3f, 0x5d, 0x66, 0x15, 0xcf, 0x28, + 0x15, 0xfd, 0x2d, 0x28, 0xd2, 0x15, 0x7f, 0xc6, 0x10, 0x3a, 0x84, 0x26, 0x76, 0x17, 0xf6, 0xc1, + 0x10, 0xb7, 0xe5, 0x03, 0x35, 0x77, 0xf3, 0x86, 0x77, 0xa3, 0xea, 0x2d, 0xad, 0x2d, 0x05, 0xf0, + 0x61, 0x35, 0x32, 0x84, 0x77, 0x71, 0x76, 0x57, 0x2f, 0x71, 0x17, 0x99, 0x42, 0xf8, 0x48, 0x06, + 0x71, 0x8f, 0xc3, 0x1c, 0xdd, 0x18, 0xea, 0x3e, 0x84, 0x26, 0x76, 0x14, 0x82, 0x07, 0xb5, 0x8a, + 0x87, 0x93, 0xdd, 0x01, 0xbc, 0x77, 0x71, 0x3f, 0x13, 0xde, 0x73, 0xa7, 0x7d, 0x53, 0xc7, 0x73, + 0x4d, 0x03, 0x27, 0x27, 0xba, 0x9a, 0xdd, 0xf1, 0xe8, 0xef, 0xd1, 0xae, 0x42, 0xc4, 0x8d, 0x6a, + 0x00, 0x8b, 0xbc, 0x6d, 0x27, 0xb1, 0xb8, 0x8c, 0x71, 0x3d, 0xc0, 0x60, 0xcd, 0xa3, 0xb9, 0x20, + 0x1a, 0x73, 0x7e, 0x34, 0xe6, 0x82, 0x28, 0x86, 0x68, 0xcc, 0x2d, 0x52, 0xd3, 0x00, 0xdb, 0x82, + 0x60, 0xa9, 0x3e, 0x40, 0x40, 0xaf, 0x61, 0x9d, 0x54, 0x7a, 0x99, 0x0e, 0xd0, 0x23, 0xf3, 0x11, + 0xfc, 0xdd, 0x1c, 0xff, 0x78, 0x4b, 0xfc, 0x01, 0xa6, 0x08, 0x81, 0x7b, 0x08, 0xab, 0x49, 0x04, + 0x66, 0xd7, 0xe7, 0x7c, 0x24, 0xa1, 0x5e, 0x83, 0xb8, 0x87, 0x23, 0x83, 0x3d, 0x0f, 0x1e, 0x62, + 0x2a, 0x76, 0xb7, 0xad, 0xe2, 0x2f, 0x08, 0x8f, 0x35, 0x05, 0xf1, 0x9c, 0x88, 0xf9, 0x31, 0xc2, + 0x87, 0x43, 0x1e, 0x57, 0x9d, 0x34, 0x2d, 0x5f, 0xc6, 0xbd, 0x41, 0xe6, 0xb2, 0x4a, 0xd1, 0x23, + 0x54, 0xea, 0x98, 0xa0, 0x3f, 0x0a, 0xbb, 0x9a, 0x04, 0x04, 0xf4, 0x2c, 0xe0, 0x3e, 0xcb, 0x89, + 0xcb, 0x79, 0xac, 0x85, 0x9c, 0xa2, 0xbf, 0x40, 0x4d, 0xd1, 0x49, 0xe7, 0xc4, 0x14, 0x4e, 0xb0, + 0xb0, 0xa4, 0xdb, 0xe9, 0x13, 0xfc, 0xbd, 0x70, 0x82, 0xa3, 0xeb, 0x3c, 0x0f, 0x22, 0x5d, 0xc0, + 0x23, 0x61, 0x76, 0xf5, 0x97, 0xbc, 0x42, 0xdd, 0xf2, 0x12, 0x9b, 0xd3, 0xbd, 0xb5, 0x50, 0x26, + 0x05, 0xf7, 0x5a, 0x30, 0x00, 0x29, 0xbf, 0xf6, 0xac, 0x6e, 0xe2, 0x6c, 0x9a, 0x31, 0x70, 0xff, + 0x00, 0xef, 0xb5, 0x22, 0x23, 0x20, 0xf4, 0xa4, 0x04, 0xfd, 0xba, 0x11, 0x28, 0x10, 0x73, 0xa5, + 0xce, 0xc0, 0xf2, 0xd1, 0xc9, 0x97, 0xa8, 0x47, 0x65, 0xc0, 0xdf, 0xc5, 0xa3, 0xa9, 0xd6, 0x80, + 0xfe, 0x3d, 0xbc, 0x67, 0xce, 0xc7, 0xc4, 0x83, 0x7e, 0x69, 0xcd, 0x95, 0xcc, 0x17, 0xa2, 0x0d, + 0x40, 0x8f, 0xfa, 0x51, 0x4d, 0x50, 0x1d, 0x42, 0xa6, 0x51, 0xf5, 0x4e, 0x05, 0xe7, 0x43, 0x04, + 0x1a, 0x25, 0xac, 0xd4, 0x64, 0x8b, 0x32, 0x1d, 0xda, 0xa2, 0xce, 0xc5, 0xa9, 0x86, 0x5f, 0x0a, + 0x43, 0x6d, 0x9e, 0xba, 0x8b, 0x7e, 0x65, 0x26, 0x7c, 0x5a, 0x2c, 0xa7, 0x64, 0xac, 0xc1, 0x0e, + 0x07, 0x0f, 0x6a, 0x11, 0x0f, 0x35, 0x1a, 0x00, 0xe5, 0x39, 0xdc, 0x1b, 0xbe, 0x03, 0x6d, 0xc7, + 0x5b, 0x90, 0xad, 0xb9, 0xa8, 0x19, 0xaa, 0x14, 0x10, 0xe5, 0x6d, 0x3b, 0x8e, 0xa8, 0x53, 0xbb, + 0xf7, 0x25, 0x02, 0x12, 0x91, 0x35, 0x12, 0x49, 0x64, 0xda, 0x22, 0xd1, 0xb9, 0xfd, 0x99, 0xae, + 0xa7, 0x82, 0x05, 0xea, 0x7a, 0xb3, 0x7e, 0x61, 0x7b, 0x85, 0xd7, 0xb5, 0xcd, 0xb7, 0x69, 0x03, + 0x4e, 0x61, 0x92, 0x1d, 0x10, 0x7d, 0x1f, 0x0f, 0xc4, 0x86, 0x40, 0xd2, 0x5c, 0x0b, 0xbe, 0x71, + 0x87, 0x71, 0x37, 0x6a, 0xb9, 0x7e, 0x38, 0x52, 0x40, 0x77, 0x6a, 0x27, 0x7f, 0x46, 0xc0, 0x33, + 0x69, 0xa9, 0x66, 0x3c, 0x33, 0x1d, 0xe0, 0xd9, 0xb9, 0x5d, 0x3e, 0x8e, 0xf7, 0x87, 0xbb, 0x25, + 0x66, 0xab, 0xe4, 0xad, 0x5d, 0x80, 0xa6, 0x03, 0x26, 0xcf, 0xae, 0x5f, 0xf7, 0xeb, 0xf9, 0x76, + 0xdb, 0x00, 0x13, 0x0f, 0x46, 0x97, 0x06, 0xd5, 0x6e, 0xe0, 0x7e, 0x31, 0xb7, 0x4a, 0x96, 0xff, + 0xa2, 0x49, 0x21, 0xe2, 0x40, 0xfd, 0x10, 0x38, 0xe6, 0x6d, 0xfb, 0x59, 0x64, 0xe4, 0xaf, 0x11, + 0x10, 0xa9, 0xf9, 0x4f, 0x25, 0x92, 0xd9, 0x16, 0x91, 0xce, 0xed, 0xfa, 0x75, 0x28, 0xa4, 0x16, + 0x2c, 0x97, 0x6b, 0xbf, 0x68, 0x38, 0xa5, 0x7a, 0xfb, 0xd8, 0xac, 0x1c, 0x1d, 0xc4, 0x3d, 0xb6, + 0xb5, 0x62, 0x79, 0x7c, 0xf5, 0x3d, 0x85, 0xe0, 0x41, 0xbd, 0x1f, 0x56, 0x4c, 0x0d, 0x0e, 0x9f, + 0x95, 0x14, 0x2a, 0xee, 0xf7, 0x98, 0x47, 0x6d, 0x58, 0x08, 0x22, 0x2b, 0xf2, 0xae, 0xd6, 0x23, + 0xfb, 0x87, 0xc7, 0xef, 0x66, 0x23, 0x89, 0x40, 0x3d, 0x13, 0x6a, 0x10, 0x1b, 0x05, 0xc4, 0x07, + 0xf0, 0x4e, 0x21, 0x35, 0x65, 0x0a, 0xf0, 0xa4, 0x2e, 0x01, 0xd3, 0x39, 0xe6, 0xdc, 0x31, 0x2a, + 0xfe, 0x97, 0x68, 0x89, 0xf9, 0xe6, 0x0d, 0xa7, 0xa0, 0x41, 0x3a, 0x05, 0xf7, 0x9a, 0xd4, 0x5d, + 0xa8, 0xa9, 0xb7, 0xbb, 0x50, 0x7b, 0x56, 0x3f, 0x47, 0x50, 0x3f, 0x34, 0xba, 0x05, 0x3c, 0xaf, + 0xe1, 0x7d, 0xac, 0xea, 0x2d, 0xb3, 0xaa, 0x53, 0x9a, 0xa7, 0xee, 0x55, 0xc7, 0x1f, 0x0c, 0x3b, + 0xf6, 0x86, 0x01, 0x7f, 0x36, 0xbf, 0x27, 0xd0, 0x99, 0x7d, 0xd9, 0x30, 0x60, 0x76, 0xb0, 0x68, + 0xe3, 0x00, 0x99, 0xc0, 0x03, 0xfe, 0x5f, 0x31, 0x4f, 0x65, 0xb8, 0x9e, 0xf1, 0xd7, 0xea, 0x38, + 0x3e, 0xc2, 0x61, 0x5e, 0x33, 0x5c, 0x97, 0x9a, 0xc6, 0x22, 0x75, 0x5d, 0xcb, 0x31, 0x17, 0xeb, + 0x1e, 0x43, 0x75, 0x2f, 0xe3, 0xa3, 0xad, 0x26, 0x02, 0xb1, 0x61, 0xbc, 0xfb, 0xa3, 0x1a, 0xc4, + 0x80, 0x50, 0xfd, 0xc5, 0xd4, 0x7f, 0x23, 0xb8, 0x87, 0x3b, 0x22, 0x0f, 0x11, 0xee, 0x17, 0xfb, + 0x36, 0x72, 0xbe, 0x45, 0xf4, 0x34, 0xb9, 0xb2, 0x50, 0x2e, 0xb4, 0x65, 0x1b, 0x20, 0x56, 0x2f, + 0xde, 0xfb, 0xed, 0xdf, 0xfb, 0xdd, 0x67, 0xc9, 0x19, 0xcd, 0x37, 0x9d, 0x14, 0xee, 0x9f, 0x6a, + 0x97, 0x3c, 0x35, 0x23, 0x6d, 0x03, 0x92, 0xe0, 0xa6, 0xb6, 0xc1, 0xd3, 0xde, 0x26, 0xf9, 0x0e, + 0xe1, 0x01, 0xd1, 0x6f, 0xde, 0xb6, 0xe5, 0xb8, 0x24, 0x5f, 0x5c, 0xc8, 0x71, 0x49, 0xb9, 0x8c, + 0x50, 0x8f, 0x73, 0x2e, 0x47, 0xc8, 0x98, 0x04, 0x17, 0xf2, 0x17, 0xc2, 0x07, 0x62, 0xc8, 0xa1, + 0x7f, 0x24, 0xf9, 0x36, 0x40, 0x44, 0x9b, 0x60, 0x65, 0x76, 0x3b, 0x2e, 0x80, 0xce, 0x79, 0x4e, + 0xe7, 0x34, 0x99, 0x92, 0xa0, 0x03, 0xb6, 0xb0, 0x43, 0x9b, 0xe4, 0x4f, 0x84, 0x5f, 0x14, 0x9a, + 0x34, 0x81, 0xdc, 0x9b, 0x92, 0xc8, 0x52, 0x1b, 0x7c, 0x25, 0xbf, 0x0d, 0x0f, 0x40, 0x6d, 0x86, + 0x53, 0x9b, 0x26, 0xa7, 0x53, 0xa8, 0x59, 0x4e, 0x0a, 0xb3, 0xa2, 0x55, 0xda, 0x24, 0xdf, 0x22, + 0xbc, 0x37, 0x4a, 0x4e, 0x3a, 0xe6, 0x12, 0x5a, 0x6d, 0xe9, 0x98, 0x4b, 0x6a, 0x9f, 0x5b, 0xc6, + 0x9c, 0xc0, 0xc4, 0x25, 0xbf, 0x02, 0x70, 0xa1, 0x05, 0x99, 0x91, 0x3c, 0xbc, 0x89, 0x8d, 0x98, + 0x72, 0xb1, 0x4d, 0x6b, 0x00, 0xff, 0x3a, 0x07, 0x3f, 0x45, 0x4e, 0x34, 0x01, 0x5f, 0x37, 0xd3, + 0x36, 0xc2, 0xe7, 0x4d, 0xf2, 0x3b, 0xc2, 0xa4, 0xb1, 0x35, 0x25, 0x52, 0x78, 0x52, 0x1b, 0x62, + 0xe5, 0x8d, 0x76, 0xcd, 0x81, 0x4f, 0x9e, 0xf3, 0xb9, 0x40, 0xce, 0xa5, 0xf2, 0x89, 0x5f, 0x8d, + 0x17, 0x4b, 0xd4, 0xa3, 0x22, 0xb1, 0x1f, 0x10, 0xde, 0x17, 0x5d, 0xc1, 0x0f, 0xaf, 0x99, 0x2d, + 0x84, 0x48, 0x9b, 0xbb, 0x94, 0xda, 0x02, 0xab, 0x93, 0x9c, 0xd5, 0x38, 0x39, 0x22, 0xb5, 0x4b, + 0xe4, 0x2b, 0x54, 0x6f, 0xbd, 0xc8, 0xb4, 0x64, 0x80, 0xc4, 0x7a, 0x44, 0xe5, 0xec, 0x96, 0xed, + 0x00, 0xac, 0xc6, 0xc1, 0xbe, 0x4a, 0xc6, 0x53, 0xc0, 0x9a, 0x60, 0xe0, 0x6b, 0x5e, 0x32, 0xd6, + 0x36, 0xc9, 0x17, 0x08, 0xf7, 0x85, 0x5e, 0x7c, 0xa9, 0xa7, 0x25, 0xc5, 0x6a, 0x0b, 0x71, 0x42, + 0xa7, 0xaa, 0x8e, 0x73, 0xc4, 0x87, 0xc9, 0x68, 0x0b, 0xc4, 0xe4, 0x01, 0xc2, 0x2f, 0xc4, 0x4b, + 0x1a, 0x22, 0x95, 0x3c, 0x52, 0xea, 0x2b, 0x65, 0xa6, 0x3d, 0x63, 0x49, 0xa9, 0xf5, 0x38, 0xd6, + 0x87, 0x08, 0xf7, 0x09, 0x55, 0x0b, 0xb9, 0x24, 0xb3, 0x7c, 0xab, 0xea, 0x48, 0x79, 0x6b, 0x9b, + 0x5e, 0x80, 0xcd, 0x31, 0xce, 0xe6, 0x15, 0xa2, 0xa6, 0xb0, 0x11, 0x2a, 0x3d, 0xf2, 0x08, 0x35, + 0x34, 0xa3, 0x44, 0x36, 0x15, 0x26, 0xb7, 0xd2, 0x72, 0xa9, 0x27, 0xfd, 0x1a, 0x40, 0x9d, 0xe6, + 0xf0, 0x4f, 0x90, 0x5c, 0x0a, 0x7c, 0x3b, 0x6a, 0x57, 0x0b, 0xff, 0x9f, 0x10, 0x26, 0x31, 0x9f, + 0xfe, 0x29, 0x90, 0x4d, 0x19, 0xdb, 0x61, 0x93, 0xde, 0xec, 0xab, 0x39, 0xce, 0x66, 0x82, 0x1c, + 0x95, 0x63, 0x43, 0x3e, 0x43, 0x78, 0x07, 0x4f, 0x3e, 0x53, 0x92, 0x32, 0x8a, 0xe9, 0xf1, 0xd4, + 0x96, 0x6c, 0x24, 0xbf, 0xbb, 0x3a, 0x7c, 0xb0, 0xb8, 0xc8, 0xdf, 0x20, 0xdc, 0x27, 0x34, 0xf9, + 0xe4, 0xdc, 0x16, 0x56, 0x8c, 0x5e, 0x0c, 0xb4, 0x07, 0xf6, 0x0c, 0x07, 0xab, 0x91, 0xc9, 0xa6, + 0x60, 0x1b, 0x8a, 0xeb, 0x4f, 0x11, 0xde, 0x15, 0x7e, 0x81, 0xa6, 0x24, 0x77, 0x74, 0xcb, 0xc2, + 0xc6, 0x1a, 0x7d, 0x75, 0x8c, 0x63, 0x1d, 0x21, 0x07, 0x9b, 0x60, 0xf5, 0x2b, 0xb0, 0x01, 0xdf, + 0xca, 0x6f, 0x91, 0xa1, 0x43, 0x95, 0x2b, 0xc1, 0x92, 0x9b, 0x74, 0xb9, 0x12, 0x2c, 0xa5, 0x1f, + 0x6f, 0x99, 0x39, 0xf4, 0xba, 0x0d, 0x2f, 0x1d, 0xa3, 0xff, 0x48, 0x96, 0x0b, 0x86, 0xc4, 0x7f, + 0x4d, 0x2b, 0xe7, 0xdb, 0x31, 0x95, 0xfc, 0xaa, 0xdf, 0x8d, 0xa2, 0xf4, 0x81, 0x47, 0xbb, 0x7b, + 0x39, 0xe0, 0x89, 0xf7, 0x05, 0x72, 0xc0, 0x93, 0x2f, 0x13, 0x5a, 0x02, 0xb7, 0x23, 0x66, 0xb3, + 0x6f, 0x3f, 0x7a, 0x92, 0x45, 0x8f, 0x9f, 0x64, 0xd1, 0x3f, 0x4f, 0xb2, 0xe8, 0x93, 0xa7, 0xd9, + 0xae, 0xc7, 0x4f, 0xb3, 0x5d, 0x7f, 0x3c, 0xcd, 0x76, 0xdd, 0x3c, 0x69, 0x5a, 0x5e, 0xb9, 0xba, + 0x9c, 0xd3, 0xd9, 0x8a, 0xe8, 0x2a, 0xc4, 0xa3, 0xad, 0x89, 0x5e, 0xbd, 0xf5, 0x55, 0xc3, 0x5d, + 0xde, 0xc9, 0xbf, 0x02, 0xa7, 0xfe, 0x0f, 0x00, 0x00, 0xff, 0xff, 0xda, 0xf6, 0xfb, 0xb3, 0x90, + 0x21, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2118,8 +2029,6 @@ const _ = grpc.SupportPackageIsVersion4 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type QueryClient interface { - // Parameters queries the parameters of the module. - Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) // Queries a OutTxTracker by index. OutTxTracker(ctx context.Context, in *QueryGetOutTxTrackerRequest, opts ...grpc.CallOption) (*QueryGetOutTxTrackerResponse, error) // Queries a list of OutTxTracker items. @@ -2164,15 +2073,6 @@ func NewQueryClient(cc grpc1.ClientConn) QueryClient { return &queryClient{cc} } -func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { - out := new(QueryParamsResponse) - err := c.cc.Invoke(ctx, "/zetachain.zetacore.crosschain.Query/Params", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *queryClient) OutTxTracker(ctx context.Context, in *QueryGetOutTxTrackerRequest, opts ...grpc.CallOption) (*QueryGetOutTxTrackerResponse, error) { out := new(QueryGetOutTxTrackerResponse) err := c.cc.Invoke(ctx, "/zetachain.zetacore.crosschain.Query/OutTxTracker", in, out, opts...) @@ -2355,8 +2255,6 @@ func (c *queryClient) LastZetaHeight(ctx context.Context, in *QueryLastZetaHeigh // QueryServer is the server API for Query service. type QueryServer interface { - // Parameters queries the parameters of the module. - Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) // Queries a OutTxTracker by index. OutTxTracker(context.Context, *QueryGetOutTxTrackerRequest) (*QueryGetOutTxTrackerResponse, error) // Queries a list of OutTxTracker items. @@ -2397,9 +2295,6 @@ type QueryServer interface { type UnimplementedQueryServer struct { } -func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") -} func (*UnimplementedQueryServer) OutTxTracker(ctx context.Context, req *QueryGetOutTxTrackerRequest) (*QueryGetOutTxTrackerResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method OutTxTracker not implemented") } @@ -2465,24 +2360,6 @@ func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) } -func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(QueryParamsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(QueryServer).Params(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/zetachain.zetacore.crosschain.Query/Params", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _Query_OutTxTracker_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryGetOutTxTrackerRequest) if err := dec(in); err != nil { @@ -2847,10 +2724,6 @@ var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "zetachain.zetacore.crosschain.Query", HandlerType: (*QueryServer)(nil), Methods: []grpc.MethodDesc{ - { - MethodName: "Params", - Handler: _Query_Params_Handler, - }, { MethodName: "OutTxTracker", Handler: _Query_OutTxTracker_Handler, @@ -2989,62 +2862,6 @@ func (m *QueryZetaAccountingResponse) MarshalToSizedBuffer(dAtA []byte) (int, er return len(dAtA) - i, nil } -func (m *QueryParamsRequest) 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 *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - return len(dAtA) - i, nil -} - -func (m *QueryParamsResponse) 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 *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - { - size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintQuery(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - return len(dAtA) - i, nil -} - func (m *QueryGetOutTxTrackerRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -4440,26 +4257,6 @@ func (m *QueryZetaAccountingResponse) Size() (n int) { return n } -func (m *QueryParamsRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - return n -} - -func (m *QueryParamsResponse) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = m.Params.Size() - n += 1 + l + sovQuery(uint64(l)) - return n -} - func (m *QueryGetOutTxTrackerRequest) Size() (n int) { if m == nil { return 0 @@ -5143,139 +4940,6 @@ func (m *QueryZetaAccountingResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *QueryParamsRequest) 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 ErrIntOverflowQuery - } - 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: QueryParamsRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipQuery(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *QueryParamsResponse) 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 ErrIntOverflowQuery - } - 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: QueryParamsResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: QueryParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowQuery - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthQuery - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthQuery - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipQuery(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthQuery - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} func (m *QueryGetOutTxTrackerRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/crosschain/types/query.pb.gw.go b/x/crosschain/types/query.pb.gw.go index 3e7f8b97c6..6234a732ea 100644 --- a/x/crosschain/types/query.pb.gw.go +++ b/x/crosschain/types/query.pb.gw.go @@ -33,24 +33,6 @@ var _ = utilities.NewDoubleArray var _ = descriptor.ForMessage var _ = metadata.Join -func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QueryParamsRequest - var metadata runtime.ServerMetadata - - msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq QueryParamsRequest - var metadata runtime.ServerMetadata - - msg, err := server.Params(ctx, &protoReq) - return msg, metadata, err - -} - func request_Query_OutTxTracker_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryGetOutTxTrackerRequest var metadata runtime.ServerMetadata @@ -965,29 +947,6 @@ func local_request_Query_LastZetaHeight_0(ctx context.Context, marshaler runtime // Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { - mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - var stream runtime.ServerTransportStream - ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_Query_Params_0(rctx, inboundMarshaler, server, req, pathParams) - md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - mux.Handle("GET", pattern_Query_OutTxTracker_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -1489,26 +1448,6 @@ func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc // "QueryClient" to call the correct interceptors. func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error { - mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_Query_Params_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - mux.Handle("GET", pattern_Query_OutTxTracker_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -1913,8 +1852,6 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"zeta-chain", "crosschain", "params"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_OutTxTracker_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 1, 0, 4, 1, 5, 4}, []string{"zeta-chain", "crosschain", "outTxTracker", "chainID", "nonce"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_OutTxTrackerAll_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"zeta-chain", "crosschain", "outTxTracker"}, "", runtime.AssumeColonVerbOpt(false))) @@ -1957,8 +1894,6 @@ var ( ) var ( - forward_Query_Params_0 = runtime.ForwardResponseMessage - forward_Query_OutTxTracker_0 = runtime.ForwardResponseMessage forward_Query_OutTxTrackerAll_0 = runtime.ForwardResponseMessage diff --git a/x/crosschain/types/status.go b/x/crosschain/types/status.go index be2b4af62e..b1c6fcdffb 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) @@ -60,5 +64,4 @@ func stateTransitionMap() map[CctxStatus][]CctxStatus { CctxStatus_Reverted, } return stateTransitionMap - } diff --git a/x/crosschain/types/status_test.go b/x/crosschain/types/status_test.go index cadf64490e..ad5298d383 100644 --- a/x/crosschain/types/status_test.go +++ b/x/crosschain/types/status_test.go @@ -1,9 +1,11 @@ package types_test import ( + "fmt" "testing" "time" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/x/crosschain/types" ) @@ -23,3 +25,76 @@ func TestStatus_AbortRefunded(t *testing.T) { require.Equal(t, status.LastUpdateTimestamp, timestamp) }) } + +func TestStatus_ValidateTransition(t *testing.T) { + tests := []struct { + name string + oldStatus types.CctxStatus + newStatus types.CctxStatus + expectedValid bool + }{ + {"Valid - PendingInbound to PendingOutbound", types.CctxStatus_PendingInbound, types.CctxStatus_PendingOutbound, true}, + {"Valid - PendingInbound to Aborted", types.CctxStatus_PendingInbound, types.CctxStatus_Aborted, true}, + {"Valid - PendingInbound to OutboundMined", types.CctxStatus_PendingInbound, types.CctxStatus_OutboundMined, true}, + {"Valid - PendingInbound to PendingRevert", types.CctxStatus_PendingInbound, types.CctxStatus_PendingRevert, true}, + + {"Valid - PendingOutbound to Aborted", types.CctxStatus_PendingOutbound, types.CctxStatus_Aborted, true}, + {"Valid - PendingOutbound to PendingRevert", types.CctxStatus_PendingOutbound, types.CctxStatus_PendingRevert, true}, + {"Valid - PendingOutbound to OutboundMined", types.CctxStatus_PendingOutbound, types.CctxStatus_OutboundMined, true}, + {"Valid - PendingOutbound to Reverted", types.CctxStatus_PendingOutbound, types.CctxStatus_Reverted, true}, + + {"Valid - PendingRevert to Aborted", types.CctxStatus_PendingRevert, types.CctxStatus_Aborted, true}, + {"Valid - PendingRevert to OutboundMined", types.CctxStatus_PendingRevert, types.CctxStatus_OutboundMined, true}, + {"Valid - PendingRevert to Reverted", types.CctxStatus_PendingRevert, types.CctxStatus_Reverted, true}, + + {"Invalid - PendingInbound to Reverted", types.CctxStatus_PendingInbound, types.CctxStatus_Reverted, false}, + {"Invalid - PendingInbound to PendingInbound", types.CctxStatus_PendingInbound, types.CctxStatus_PendingInbound, false}, + + {"Invalid - PendingOutbound to PendingInbound", types.CctxStatus_PendingOutbound, types.CctxStatus_PendingInbound, false}, + {"Invalid - PendingOutbound to PendingOutbound", types.CctxStatus_PendingOutbound, types.CctxStatus_PendingOutbound, false}, + + {"Invalid - PendingRevert to PendingInbound", types.CctxStatus_PendingRevert, types.CctxStatus_PendingInbound, false}, + {"Invalid - PendingRevert to PendingOutbound", types.CctxStatus_PendingRevert, types.CctxStatus_PendingOutbound, false}, + {"Invalid - PendingRevert to PendingRevert", types.CctxStatus_PendingRevert, types.CctxStatus_PendingRevert, false}, + + {"Invalid old status - CctxStatus_Aborted", types.CctxStatus_Aborted, types.CctxStatus_PendingRevert, false}, + {"Invalid old status - CctxStatus_Reverted", types.CctxStatus_Reverted, types.CctxStatus_PendingRevert, false}, + {"Invalid old status - CctxStatus_OutboundMined", types.CctxStatus_OutboundMined, types.CctxStatus_PendingRevert, false}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + m := types.Status{Status: tc.oldStatus} + valid := m.ValidateTransition(tc.newStatus) + if valid != tc.expectedValid { + t.Errorf("expected %v, got %v", tc.expectedValid, valid) + } + }) + } +} + +func TestStatus_ChangeStatus(t *testing.T) { + t.Run("should change status and msg if transition is valid", func(t *testing.T) { + s := types.Status{Status: types.CctxStatus_PendingInbound} + + s.ChangeStatus(types.CctxStatus_PendingOutbound, "msg") + assert.Equal(t, s.Status, types.CctxStatus_PendingOutbound) + assert.Equal(t, s.StatusMessage, "msg") + }) + + t.Run("should change status if transition is valid", func(t *testing.T) { + s := types.Status{Status: types.CctxStatus_PendingInbound} + + s.ChangeStatus(types.CctxStatus_PendingOutbound, "") + assert.Equal(t, s.Status, types.CctxStatus_PendingOutbound) + assert.Equal(t, s.StatusMessage, "") + }) + + t.Run("should change status to aborted and msg if transition is invalid", func(t *testing.T) { + s := types.Status{Status: types.CctxStatus_PendingOutbound} + + s.ChangeStatus(types.CctxStatus_PendingInbound, "msg") + assert.Equal(t, s.Status, types.CctxStatus_Aborted) + assert.Equal(t, fmt.Sprintf("Failed to transition : OldStatus %s , NewStatus %s , MSG : %s :", types.CctxStatus_PendingOutbound.String(), types.CctxStatus_PendingInbound.String(), "msg"), s.StatusMessage) + }) +} 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/abci.go b/x/observer/abci.go index f219bb833d..41121ced19 100644 --- a/x/observer/abci.go +++ b/x/observer/abci.go @@ -21,10 +21,6 @@ func BeginBlocker(ctx sdk.Context, k keeper.Keeper) { return } totalObserverCountCurrentBlock := allObservers.LenUint() - if totalObserverCountCurrentBlock < 0 { - ctx.Logger().Error("TotalObserverCount is negative at height", ctx.BlockHeight()) - return - } // #nosec G701 always in range if totalObserverCountCurrentBlock == lastBlockObserverCount.Count { return diff --git a/x/observer/abci_test.go b/x/observer/abci_test.go new file mode 100644 index 0000000000..02a161d6b6 --- /dev/null +++ b/x/observer/abci_test.go @@ -0,0 +1,98 @@ +package observer_test + +import ( + "math" + "testing" + + "github.com/stretchr/testify/require" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/observer" + "github.com/zeta-chain/zetacore/x/observer/types" +) + +func TestBeginBlocker(t *testing.T) { + t.Run("should not update LastObserverCount if not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + observer.BeginBlocker(ctx, *k) + + _, found := k.GetLastObserverCount(ctx) + require.False(t, found) + + _, found = k.GetKeygen(ctx) + require.False(t, found) + }) + + t.Run("should not update LastObserverCount if observer set not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + count := 1 + k.SetLastObserverCount(ctx, &types.LastObserverCount{ + Count: uint64(count), + }) + + observer.BeginBlocker(ctx, *k) + + lastObserverCount, found := k.GetLastObserverCount(ctx) + require.True(t, found) + require.Equal(t, uint64(count), lastObserverCount.Count) + require.Equal(t, int64(0), lastObserverCount.LastChangeHeight) + + _, found = k.GetKeygen(ctx) + require.False(t, found) + }) + + t.Run("should not update LastObserverCount if observer set count equal last observed count", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + count := 1 + os := sample.ObserverSet(count) + k.SetObserverSet(ctx, os) + k.SetLastObserverCount(ctx, &types.LastObserverCount{ + Count: uint64(count), + }) + + observer.BeginBlocker(ctx, *k) + + lastObserverCount, found := k.GetLastObserverCount(ctx) + require.True(t, found) + require.Equal(t, uint64(count), lastObserverCount.Count) + require.Equal(t, int64(0), lastObserverCount.LastChangeHeight) + + _, found = k.GetKeygen(ctx) + require.False(t, found) + }) + + t.Run("should update LastObserverCount", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + observeSetLen := 10 + count := 1 + os := sample.ObserverSet(observeSetLen) + k.SetObserverSet(ctx, os) + k.SetLastObserverCount(ctx, &types.LastObserverCount{ + Count: uint64(count), + }) + + keygen, found := k.GetKeygen(ctx) + require.False(t, found) + require.Equal(t, types.Keygen{}, keygen) + + observer.BeginBlocker(ctx, *k) + + keygen, found = k.GetKeygen(ctx) + require.True(t, found) + require.Empty(t, keygen.GranteePubkeys) + require.Equal(t, types.KeygenStatus_PendingKeygen, keygen.Status) + require.Equal(t, int64(math.MaxInt64), keygen.BlockNumber) + + inboundEnabled := k.IsInboundEnabled(ctx) + require.False(t, inboundEnabled) + + lastObserverCount, found := k.GetLastObserverCount(ctx) + require.True(t, found) + require.Equal(t, uint64(observeSetLen), lastObserverCount.Count) + require.Equal(t, ctx.BlockHeight(), lastObserverCount.LastChangeHeight) + }) +} 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/genesis.go b/x/observer/genesis.go index 7e3ff44311..b6d85757ff 100644 --- a/x/observer/genesis.go +++ b/x/observer/genesis.go @@ -16,7 +16,7 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) observerCount = uint64(len(genState.Observers.ObserverList)) } - // if chian params are defined set them + // if chain params are defined set them if len(genState.ChainParamsList.ChainParams) > 0 { k.SetChainParamsList(ctx, genState.ChainParamsList) } else { diff --git a/x/observer/genesis_test.go b/x/observer/genesis_test.go index 7482f24f95..62e442e9ba 100644 --- a/x/observer/genesis_test.go +++ b/x/observer/genesis_test.go @@ -12,45 +12,151 @@ import ( ) func TestGenesis(t *testing.T) { - params := types.DefaultParams() - tss := sample.Tss() - genesisState := types.GenesisState{ - Params: ¶ms, - Tss: &tss, - BlameList: sample.BlameRecordsList(t, 10), - Ballots: []*types.Ballot{ - sample.Ballot(t, "0"), - sample.Ballot(t, "1"), - sample.Ballot(t, "2"), - }, - Observers: sample.ObserverSet(3), - NodeAccountList: []*types.NodeAccount{ - sample.NodeAccount(), - sample.NodeAccount(), - sample.NodeAccount(), - }, - CrosschainFlags: types.DefaultCrosschainFlags(), - Keygen: sample.Keygen(t), - ChainParamsList: sample.ChainParamsList(), - LastObserverCount: sample.LastObserverCount(10), - TssFundMigrators: []types.TssFundMigratorInfo{sample.TssFundsMigrator(1), sample.TssFundsMigrator(2)}, - ChainNonces: []types.ChainNonces{ - sample.ChainNonces(t, "0"), - sample.ChainNonces(t, "1"), - sample.ChainNonces(t, "2"), - }, - PendingNonces: sample.PendingNoncesList(t, "sample", 20), - NonceToCctx: sample.NonceToCctxList(t, "sample", 20), - } - - // Init and export - k, ctx, _, _ := keepertest.ObserverKeeper(t) - observer.InitGenesis(ctx, *k, genesisState) - got := observer.ExportGenesis(ctx, *k) - require.NotNil(t, got) - - // Compare genesis after init and export - nullify.Fill(&genesisState) - nullify.Fill(got) - require.Equal(t, genesisState, *got) + t.Run("genState fields defined", func(t *testing.T) { + params := types.DefaultParams() + tss := sample.Tss() + genesisState := types.GenesisState{ + Params: ¶ms, + Tss: &tss, + BlameList: sample.BlameRecordsList(t, 10), + Ballots: []*types.Ballot{ + sample.Ballot(t, "0"), + sample.Ballot(t, "1"), + sample.Ballot(t, "2"), + }, + Observers: sample.ObserverSet(3), + NodeAccountList: []*types.NodeAccount{ + sample.NodeAccount(), + sample.NodeAccount(), + sample.NodeAccount(), + }, + CrosschainFlags: types.DefaultCrosschainFlags(), + Keygen: sample.Keygen(t), + ChainParamsList: sample.ChainParamsList(), + LastObserverCount: sample.LastObserverCount(10), + TssFundMigrators: []types.TssFundMigratorInfo{sample.TssFundsMigrator(1), sample.TssFundsMigrator(2)}, + ChainNonces: []types.ChainNonces{ + sample.ChainNonces(t, "0"), + sample.ChainNonces(t, "1"), + sample.ChainNonces(t, "2"), + }, + PendingNonces: sample.PendingNoncesList(t, "sample", 20), + NonceToCctx: sample.NonceToCctxList(t, "sample", 20), + TssHistory: []types.TSS{sample.Tss()}, + } + + // Init and export + k, ctx, _, _ := keepertest.ObserverKeeper(t) + observer.InitGenesis(ctx, *k, genesisState) + got := observer.ExportGenesis(ctx, *k) + require.NotNil(t, got) + + // Compare genesis after init and export + nullify.Fill(&genesisState) + nullify.Fill(got) + require.Equal(t, genesisState, *got) + }) + + t.Run("genState fields not defined", func(t *testing.T) { + genesisState := types.GenesisState{} + + k, ctx, _, _ := keepertest.ObserverKeeper(t) + observer.InitGenesis(ctx, *k, genesisState) + got := observer.ExportGenesis(ctx, *k) + require.NotNil(t, got) + + defaultParams := types.DefaultParams() + btcChainParams := types.GetDefaultBtcRegtestChainParams() + btcChainParams.IsSupported = true + goerliChainParams := types.GetDefaultGoerliLocalnetChainParams() + goerliChainParams.IsSupported = true + zetaPrivnetChainParams := types.GetDefaultZetaPrivnetChainParams() + zetaPrivnetChainParams.IsSupported = true + localnetChainParams := types.ChainParamsList{ + ChainParams: []*types.ChainParams{ + btcChainParams, + goerliChainParams, + zetaPrivnetChainParams, + }, + } + expectedGenesisState := types.GenesisState{ + Params: &defaultParams, + CrosschainFlags: types.DefaultCrosschainFlags(), + ChainParamsList: localnetChainParams, + Tss: &types.TSS{}, + Keygen: &types.Keygen{}, + LastObserverCount: &types.LastObserverCount{}, + NodeAccountList: []*types.NodeAccount{}, + } + + require.Equal(t, expectedGenesisState, *got) + }) + + t.Run("genState fields not defined except tss", func(t *testing.T) { + tss := sample.Tss() + genesisState := types.GenesisState{ + Tss: &tss, + } + + k, ctx, _, _ := keepertest.ObserverKeeper(t) + observer.InitGenesis(ctx, *k, genesisState) + got := observer.ExportGenesis(ctx, *k) + require.NotNil(t, got) + + defaultParams := types.DefaultParams() + btcChainParams := types.GetDefaultBtcRegtestChainParams() + btcChainParams.IsSupported = true + goerliChainParams := types.GetDefaultGoerliLocalnetChainParams() + goerliChainParams.IsSupported = true + zetaPrivnetChainParams := types.GetDefaultZetaPrivnetChainParams() + zetaPrivnetChainParams.IsSupported = true + localnetChainParams := types.ChainParamsList{ + ChainParams: []*types.ChainParams{ + btcChainParams, + goerliChainParams, + zetaPrivnetChainParams, + }, + } + pendingNonces, err := k.GetAllPendingNonces(ctx) + require.NoError(t, err) + require.NotEmpty(t, pendingNonces) + expectedGenesisState := types.GenesisState{ + Params: &defaultParams, + CrosschainFlags: types.DefaultCrosschainFlags(), + ChainParamsList: localnetChainParams, + Tss: &tss, + Keygen: &types.Keygen{}, + LastObserverCount: &types.LastObserverCount{}, + NodeAccountList: []*types.NodeAccount{}, + PendingNonces: pendingNonces, + } + + require.Equal(t, expectedGenesisState, *got) + }) + + t.Run("export without init", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + got := observer.ExportGenesis(ctx, *k) + require.NotNil(t, got) + + params := k.GetParamsIfExists(ctx) + expectedGenesisState := types.GenesisState{ + Params: ¶ms, + CrosschainFlags: types.DefaultCrosschainFlags(), + ChainParamsList: types.ChainParamsList{}, + Tss: &types.TSS{}, + Keygen: &types.Keygen{}, + LastObserverCount: &types.LastObserverCount{}, + NodeAccountList: []*types.NodeAccount{}, + Ballots: k.GetAllBallots(ctx), + TssHistory: k.GetAllTSS(ctx), + TssFundMigrators: k.GetAllTssFundMigrators(ctx), + BlameList: k.GetAllBlame(ctx), + ChainNonces: k.GetAllChainNonces(ctx), + NonceToCctx: k.GetAllNonceToCctx(ctx), + } + + require.Equal(t, expectedGenesisState, *got) + }) } diff --git a/x/observer/keeper/ballot_test.go b/x/observer/keeper/ballot_test.go index 13415cdaac..8d71aaa720 100644 --- a/x/observer/keeper/ballot_test.go +++ b/x/observer/keeper/ballot_test.go @@ -1,23 +1,162 @@ -package keeper +package keeper_test import ( + "math" "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/testutil/sample" "github.com/zeta-chain/zetacore/x/observer/types" ) func TestKeeper_GetBallot(t *testing.T) { - k, ctx := SetupKeeper(t) - identifier := "0x9ea007f0f60e32d58577a8cf25678942d2b10791c2a34f48e237b76a7e998e4d" - k.SetBallot(ctx, &types.Ballot{ - Index: "", - BallotIdentifier: identifier, - VoterList: nil, - ObservationType: 0, - BallotThreshold: sdk.Dec{}, - BallotStatus: 0, + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + identifier := sample.ZetaIndex(t) + b := &types.Ballot{ + Index: "123", + BallotIdentifier: identifier, + VoterList: nil, + ObservationType: 0, + BallotThreshold: sdk.Dec{}, + BallotStatus: 0, + BallotCreationHeight: 1, + } + _, found := k.GetBallot(ctx, identifier) + require.False(t, found) + + k.SetBallot(ctx, b) + + ballot, found := k.GetBallot(ctx, identifier) + require.True(t, found) + require.Equal(t, *b, ballot) + + // overwrite existing ballot + b = &types.Ballot{ + Index: "123", + BallotIdentifier: identifier, + VoterList: nil, + ObservationType: 1, + BallotThreshold: sdk.Dec{}, + BallotStatus: 1, + BallotCreationHeight: 2, + } + _, found = k.GetBallot(ctx, identifier) + require.True(t, found) + + k.SetBallot(ctx, b) + + ballot, found = k.GetBallot(ctx, identifier) + require.True(t, found) + require.Equal(t, *b, ballot) +} + +func TestKeeper_GetBallotList(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + identifier := sample.ZetaIndex(t) + b := &types.Ballot{ + Index: "", + BallotIdentifier: identifier, + VoterList: nil, + ObservationType: 0, + BallotThreshold: sdk.Dec{}, + BallotStatus: 0, + BallotCreationHeight: 1, + } + _, found := k.GetBallotList(ctx, 1) + require.False(t, found) + + k.AddBallotToList(ctx, *b) + list, found := k.GetBallotList(ctx, 1) + require.True(t, found) + require.Equal(t, 1, len(list.BallotsIndexList)) + require.Equal(t, identifier, list.BallotsIndexList[0]) +} + +func TestKeeper_GetAllBallots(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + identifier := sample.ZetaIndex(t) + b := &types.Ballot{ + Index: "", + BallotIdentifier: identifier, + VoterList: nil, + ObservationType: 0, + BallotThreshold: sdk.Dec{}, + BallotStatus: 0, + BallotCreationHeight: 1, + } + ballots := k.GetAllBallots(ctx) + require.Empty(t, ballots) + + k.SetBallot(ctx, b) + ballots = k.GetAllBallots(ctx) + require.Equal(t, 1, len(ballots)) + require.Equal(t, b, ballots[0]) +} + +func TestKeeper_GetMaturedBallotList(t *testing.T) { + t.Run("should return if maturity blocks less than height", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + identifier := sample.ZetaIndex(t) + b := &types.Ballot{ + Index: "", + BallotIdentifier: identifier, + VoterList: nil, + ObservationType: 0, + BallotThreshold: sdk.Dec{}, + BallotStatus: 0, + BallotCreationHeight: 1, + } + list := k.GetMaturedBallotList(ctx) + require.Empty(t, list) + ctx = ctx.WithBlockHeight(101) + k.AddBallotToList(ctx, *b) + list = k.GetMaturedBallotList(ctx) + require.Equal(t, 1, len(list)) + require.Equal(t, identifier, list[0]) + }) + + t.Run("should return empty for max maturity blocks", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + identifier := sample.ZetaIndex(t) + b := &types.Ballot{ + Index: "", + BallotIdentifier: identifier, + VoterList: nil, + ObservationType: 0, + BallotThreshold: sdk.Dec{}, + BallotStatus: 0, + BallotCreationHeight: 1, + } + k.SetParams(ctx, types.Params{ + BallotMaturityBlocks: math.MaxInt64, + }) + list := k.GetMaturedBallotList(ctx) + require.Empty(t, list) + k.AddBallotToList(ctx, *b) + list = k.GetMaturedBallotList(ctx) + require.Empty(t, list) }) - k.GetBallot(ctx, identifier) + t.Run("should return empty if maturity blocks greater than height", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + identifier := sample.ZetaIndex(t) + b := &types.Ballot{ + Index: "", + BallotIdentifier: identifier, + VoterList: nil, + ObservationType: 0, + BallotThreshold: sdk.Dec{}, + BallotStatus: 0, + BallotCreationHeight: 1, + } + list := k.GetMaturedBallotList(ctx) + require.Empty(t, list) + ctx = ctx.WithBlockHeight(1) + k.AddBallotToList(ctx, *b) + list = k.GetMaturedBallotList(ctx) + require.Empty(t, list) + }) } diff --git a/x/observer/keeper/blame_test.go b/x/observer/keeper/blame_test.go new file mode 100644 index 0000000000..420e6f7d8f --- /dev/null +++ b/x/observer/keeper/blame_test.go @@ -0,0 +1,110 @@ +package keeper_test + +import ( + "sort" + "testing" + + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/stretchr/testify/require" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/observer/types" +) + +func TestKeeper_GetBlame(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + var chainId int64 = 97 + var nonce uint64 = 101 + digest := sample.ZetaIndex(t) + + index := types.GetBlameIndex(chainId, nonce, digest, 123) + + k.SetBlame(ctx, types.Blame{ + Index: index, + FailureReason: "failed to join party", + Nodes: nil, + }) + + blameRecords, found := k.GetBlame(ctx, index) + require.True(t, found) + require.Equal(t, index, blameRecords.Index) +} + +func TestKeeper_GetBlameByChainAndNonce(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + var chainId int64 = 97 + var nonce uint64 = 101 + digest := sample.ZetaIndex(t) + + index := types.GetBlameIndex(chainId, nonce, digest, 123) + + k.SetBlame(ctx, types.Blame{ + Index: index, + FailureReason: "failed to join party", + Nodes: nil, + }) + + blameRecords, found := k.GetBlamesByChainAndNonce(ctx, chainId, int64(nonce)) + require.True(t, found) + require.Equal(t, 1, len(blameRecords)) + require.Equal(t, index, blameRecords[0].Index) +} + +func TestKeeper_BlameAll(t *testing.T) { + t.Run("GetBlameRecord by limit ", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + blameList := sample.BlameRecordsList(t, 10) + for _, record := range blameList { + k.SetBlame(ctx, record) + } + sort.Slice(blameList, func(i, j int) bool { + return blameList[i].Index < blameList[j].Index + }) + rst, pageRes, err := k.GetAllBlamePaginated(ctx, &query.PageRequest{Limit: 10, CountTotal: true}) + require.NoError(t, err) + sort.Slice(rst, func(i, j int) bool { + return rst[i].Index < rst[j].Index + }) + require.Equal(t, blameList, rst) + require.Equal(t, len(blameList), int(pageRes.Total)) + }) + t.Run("GetBlameRecord by offset ", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + blameList := sample.BlameRecordsList(t, 20) + offset := 10 + for _, record := range blameList { + k.SetBlame(ctx, record) + } + sort.Slice(blameList, func(i, j int) bool { + return blameList[i].Index < blameList[j].Index + }) + rst, pageRes, err := k.GetAllBlamePaginated(ctx, &query.PageRequest{Offset: uint64(offset), CountTotal: true}) + require.NoError(t, err) + sort.Slice(rst, func(i, j int) bool { + return rst[i].Index < rst[j].Index + }) + require.Subset(t, blameList, rst) + require.Equal(t, len(blameList)-offset, len(rst)) + require.Equal(t, len(blameList), int(pageRes.Total)) + }) + t.Run("GetAllBlameRecord", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + blameList := sample.BlameRecordsList(t, 100) + for _, record := range blameList { + k.SetBlame(ctx, record) + } + rst := k.GetAllBlame(ctx) + sort.Slice(rst, func(i, j int) bool { + return rst[i].Index < rst[j].Index + }) + sort.Slice(blameList, func(i, j int) bool { + return blameList[i].Index < blameList[j].Index + }) + require.Equal(t, blameList, rst) + }) + t.Run("Get no records if nothing is set", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + rst := k.GetAllBlame(ctx) + require.Len(t, rst, 0) + }) +} diff --git a/x/observer/keeper/block_header_test.go b/x/observer/keeper/block_header_test.go new file mode 100644 index 0000000000..5ab61ed20f --- /dev/null +++ b/x/observer/keeper/block_header_test.go @@ -0,0 +1,32 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/proofs" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" +) + +func TestKeeper_GetBlockHeader(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + blockHash := sample.Hash().Bytes() + _, found := k.GetBlockHeader(ctx, blockHash) + require.False(t, found) + + bh := proofs.BlockHeader{ + Height: 1, + Hash: blockHash, + ParentHash: sample.Hash().Bytes(), + ChainId: 1, + Header: proofs.HeaderData{}, + } + k.SetBlockHeader(ctx, bh) + _, found = k.GetBlockHeader(ctx, blockHash) + require.True(t, found) + + k.RemoveBlockHeader(ctx, blockHash) + _, found = k.GetBlockHeader(ctx, blockHash) + require.False(t, found) +} diff --git a/x/observer/keeper/chain_params_test.go b/x/observer/keeper/chain_params_test.go index 3464c950fa..84f28e5cff 100644 --- a/x/observer/keeper/chain_params_test.go +++ b/x/observer/keeper/chain_params_test.go @@ -23,7 +23,6 @@ func TestKeeper_GetSupportedChainFromChainID(t *testing.T) { // chain params list but chain not supported chainParams := sample.ChainParams(getValidEthChainIDWithIndex(t, 0)) - chainParams.IsSupported = false k.SetChainParamsList(ctx, types.ChainParamsList{ ChainParams: []*types.ChainParams{chainParams}, }) @@ -40,6 +39,35 @@ func TestKeeper_GetSupportedChainFromChainID(t *testing.T) { }) } +func TestKeeper_GetChainParamsByChainID(t *testing.T) { + t.Run("return false if chain params not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + _, found := k.GetChainParamsByChainID(ctx, getValidEthChainIDWithIndex(t, 0)) + require.False(t, found) + }) + + t.Run("return true if found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + chainParams := sample.ChainParams(getValidEthChainIDWithIndex(t, 0)) + k.SetChainParamsList(ctx, types.ChainParamsList{ + ChainParams: []*types.ChainParams{chainParams}, + }) + res, found := k.GetChainParamsByChainID(ctx, getValidEthChainIDWithIndex(t, 0)) + require.True(t, found) + require.Equal(t, chainParams, res) + }) + + t.Run("return false if chain not found in params", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + chainParams := sample.ChainParams(getValidEthChainIDWithIndex(t, 0)) + k.SetChainParamsList(ctx, types.ChainParamsList{ + ChainParams: []*types.ChainParams{chainParams}, + }) + _, found := k.GetChainParamsByChainID(ctx, getValidEthChainIDWithIndex(t, 1)) + require.False(t, found) + }) +} func TestKeeper_GetSupportedChains(t *testing.T) { t.Run("return empty list if no core params list", func(t *testing.T) { k, ctx, _, _ := keepertest.ObserverKeeper(t) diff --git a/x/observer/keeper/crosschain_flags_test.go b/x/observer/keeper/crosschain_flags_test.go new file mode 100644 index 0000000000..4d3f003b92 --- /dev/null +++ b/x/observer/keeper/crosschain_flags_test.go @@ -0,0 +1,83 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/x/observer/types" +) + +func TestKeeper_IsInboundEnabled(t *testing.T) { + t.Run("should return false if flags not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + enabled := k.IsInboundEnabled(ctx) + require.False(t, enabled) + }) + + t.Run("should return if flags found and set", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + k.SetCrosschainFlags(ctx, types.CrosschainFlags{ + IsInboundEnabled: false, + }) + enabled := k.IsInboundEnabled(ctx) + require.False(t, enabled) + + k.SetCrosschainFlags(ctx, types.CrosschainFlags{ + IsInboundEnabled: true, + }) + + enabled = k.IsInboundEnabled(ctx) + require.True(t, enabled) + }) +} + +func TestKeeper_IsOutboundEnabled(t *testing.T) { + t.Run("should return false if flags not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + enabled := k.IsOutboundEnabled(ctx) + require.False(t, enabled) + }) + + t.Run("should return if flags found and set", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + k.SetCrosschainFlags(ctx, types.CrosschainFlags{ + IsOutboundEnabled: false, + }) + enabled := k.IsOutboundEnabled(ctx) + require.False(t, enabled) + + k.SetCrosschainFlags(ctx, types.CrosschainFlags{ + IsOutboundEnabled: true, + }) + + enabled = k.IsOutboundEnabled(ctx) + require.True(t, enabled) + }) +} + +func TestKeeper_DisableInboundOnly(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + k.DisableInboundOnly(ctx) + enabled := k.IsOutboundEnabled(ctx) + require.True(t, enabled) + + enabled = k.IsInboundEnabled(ctx) + require.False(t, enabled) + + k.SetCrosschainFlags(ctx, types.CrosschainFlags{ + IsOutboundEnabled: false, + }) + + k.DisableInboundOnly(ctx) + enabled = k.IsOutboundEnabled(ctx) + require.False(t, enabled) + + enabled = k.IsInboundEnabled(ctx) + require.False(t, enabled) +} diff --git a/x/observer/keeper/grpc_query_ballot_test.go b/x/observer/keeper/grpc_query_ballot_test.go new file mode 100644 index 0000000000..32bbe6265d --- /dev/null +++ b/x/observer/keeper/grpc_query_ballot_test.go @@ -0,0 +1,136 @@ +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/testutil/sample" + "github.com/zeta-chain/zetacore/x/observer/types" +) + +func TestKeeper_HasVoted(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.HasVoted(wctx, nil) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should return false if ballot not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.HasVoted(wctx, &types.QueryHasVotedRequest{ + BallotIdentifier: "test", + }) + require.NoError(t, err) + require.Equal(t, &types.QueryHasVotedResponse{ + HasVoted: false, + }, res) + }) + + t.Run("should return true if ballot found and voted", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + voter := sample.AccAddress() + ballot := types.Ballot{ + Index: "index", + BallotIdentifier: "index", + VoterList: []string{voter}, + Votes: []types.VoteType{types.VoteType_SuccessObservation}, + BallotStatus: types.BallotStatus_BallotInProgress, + } + k.SetBallot(ctx, &ballot) + + res, err := k.HasVoted(wctx, &types.QueryHasVotedRequest{ + BallotIdentifier: "index", + VoterAddress: voter, + }) + require.NoError(t, err) + require.Equal(t, &types.QueryHasVotedResponse{ + HasVoted: true, + }, res) + }) + + t.Run("should return false if ballot found and not voted", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + voter := sample.AccAddress() + ballot := types.Ballot{ + Index: "index", + BallotIdentifier: "index", + VoterList: []string{voter}, + Votes: []types.VoteType{types.VoteType_SuccessObservation}, + BallotStatus: types.BallotStatus_BallotInProgress, + } + k.SetBallot(ctx, &ballot) + + res, err := k.HasVoted(wctx, &types.QueryHasVotedRequest{ + BallotIdentifier: "index", + VoterAddress: sample.AccAddress(), + }) + require.NoError(t, err) + require.Equal(t, &types.QueryHasVotedResponse{ + HasVoted: false, + }, res) + }) +} + +func TestKeeper_BallotByIdentifier(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.BallotByIdentifier(wctx, nil) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should return nil if ballot not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.BallotByIdentifier(wctx, &types.QueryBallotByIdentifierRequest{ + BallotIdentifier: "test", + }) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should return ballot if exists", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + voter := sample.AccAddress() + ballot := types.Ballot{ + Index: "index", + BallotIdentifier: "index", + VoterList: []string{voter}, + Votes: []types.VoteType{types.VoteType_SuccessObservation}, + BallotStatus: types.BallotStatus_BallotInProgress, + } + k.SetBallot(ctx, &ballot) + + res, err := k.BallotByIdentifier(wctx, &types.QueryBallotByIdentifierRequest{ + BallotIdentifier: "index", + }) + require.NoError(t, err) + require.Equal(t, &types.QueryBallotByIdentifierResponse{ + BallotIdentifier: ballot.BallotIdentifier, + Voters: []*types.VoterList{ + { + VoterAddress: voter, + VoteType: types.VoteType_SuccessObservation, + }, + }, + ObservationType: ballot.ObservationType, + BallotStatus: ballot.BallotStatus, + }, res) + }) +} diff --git a/x/observer/keeper/grpc_query_blame_test.go b/x/observer/keeper/grpc_query_blame_test.go index 36d95ed23e..728d1748d0 100644 --- a/x/observer/keeper/grpc_query_blame_test.go +++ b/x/observer/keeper/grpc_query_blame_test.go @@ -1,10 +1,9 @@ package keeper_test import ( - "sort" "testing" - "github.com/cosmos/cosmos-sdk/types/query" + 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/testutil/sample" @@ -12,99 +11,126 @@ import ( ) func TestKeeper_BlameByIdentifier(t *testing.T) { - k, ctx, _, _ := keepertest.ObserverKeeper(t) - var chainId int64 = 97 - var nonce uint64 = 101 - digest := "85f5e10431f69bc2a14046a13aabaefc660103b6de7a84f75c4b96181d03f0b5" - - index := types.GetBlameIndex(chainId, nonce, digest, 123) + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) - k.SetBlame(ctx, types.Blame{ - Index: index, - FailureReason: "failed to join party", - Nodes: nil, + res, err := k.BlameByIdentifier(wctx, nil) + require.Nil(t, res) + require.Error(t, err) }) - blameRecords, found := k.GetBlame(ctx, index) - require.True(t, found) - require.Equal(t, index, blameRecords.Index) -} - -func TestKeeper_BlameByChainAndNonce(t *testing.T) { - k, ctx, _, _ := keepertest.ObserverKeeper(t) - var chainId int64 = 97 - var nonce uint64 = 101 - digest := "85f5e10431f69bc2a14046a13aabaefc660103b6de7a84f75c4b96181d03f0b5" - - index := types.GetBlameIndex(chainId, nonce, digest, 123) + t.Run("should error if blame not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) - k.SetBlame(ctx, types.Blame{ - Index: index, - FailureReason: "failed to join party", - Nodes: nil, + res, err := k.BlameByIdentifier(wctx, &types.QueryBlameByIdentifierRequest{ + BlameIdentifier: "test", + }) + require.Error(t, err) + require.Nil(t, res) }) - blameRecords, found := k.GetBlamesByChainAndNonce(ctx, chainId, int64(nonce)) - require.True(t, found) - require.Equal(t, 1, len(blameRecords)) - require.Equal(t, index, blameRecords[0].Index) -} - -func TestKeeper_BlameAll(t *testing.T) { - t.Run("GetBlameRecord by limit ", func(t *testing.T) { + t.Run("should return blame info if found", func(t *testing.T) { k, ctx, _, _ := keepertest.ObserverKeeper(t) - blameList := sample.BlameRecordsList(t, 10) - for _, record := range blameList { - k.SetBlame(ctx, record) + wctx := sdk.WrapSDKContext(ctx) + var chainId int64 = 97 + var nonce uint64 = 101 + digest := sample.ZetaIndex(t) + + index := types.GetBlameIndex(chainId, nonce, digest, 123) + blame := types.Blame{ + Index: index, + FailureReason: "failed to join party", + Nodes: nil, } - sort.Slice(blameList, func(i, j int) bool { - return blameList[i].Index < blameList[j].Index + k.SetBlame(ctx, blame) + + res, err := k.BlameByIdentifier(wctx, &types.QueryBlameByIdentifierRequest{ + BlameIdentifier: index, }) - rst, pageRes, err := k.GetAllBlamePaginated(ctx, &query.PageRequest{Limit: 10, CountTotal: true}) require.NoError(t, err) - sort.Slice(rst, func(i, j int) bool { - return rst[i].Index < rst[j].Index - }) - require.Equal(t, blameList, rst) - require.Equal(t, len(blameList), int(pageRes.Total)) + require.Equal(t, &types.QueryBlameByIdentifierResponse{ + BlameInfo: &blame, + }, res) }) - t.Run("GetBlameRecord by offset ", func(t *testing.T) { +} + +func TestKeeper_GetAllBlameRecords(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { k, ctx, _, _ := keepertest.ObserverKeeper(t) - blameList := sample.BlameRecordsList(t, 20) - offset := 10 - for _, record := range blameList { - k.SetBlame(ctx, record) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.GetAllBlameRecords(wctx, nil) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should return all if found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + var chainId int64 = 97 + var nonce uint64 = 101 + digest := sample.ZetaIndex(t) + + index := types.GetBlameIndex(chainId, nonce, digest, 123) + blame := types.Blame{ + Index: index, + FailureReason: "failed to join party", + Nodes: nil, } - sort.Slice(blameList, func(i, j int) bool { - return blameList[i].Index < blameList[j].Index - }) - rst, pageRes, err := k.GetAllBlamePaginated(ctx, &query.PageRequest{Offset: uint64(offset), CountTotal: true}) + k.SetBlame(ctx, blame) + + res, err := k.GetAllBlameRecords(wctx, &types.QueryAllBlameRecordsRequest{}) require.NoError(t, err) - sort.Slice(rst, func(i, j int) bool { - return rst[i].Index < rst[j].Index - }) - require.Subset(t, blameList, rst) - require.Equal(t, len(blameList)-offset, len(rst)) - require.Equal(t, len(blameList), int(pageRes.Total)) + require.Equal(t, []types.Blame{blame}, res.BlameInfo) }) - t.Run("GetAllBlameRecord", func(t *testing.T) { +} + +func TestKeeper_BlamesByChainAndNonce(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { k, ctx, _, _ := keepertest.ObserverKeeper(t) - blameList := sample.BlameRecordsList(t, 100) - for _, record := range blameList { - k.SetBlame(ctx, record) - } - rst := k.GetAllBlame(ctx) - sort.Slice(rst, func(i, j int) bool { - return rst[i].Index < rst[j].Index - }) - sort.Slice(blameList, func(i, j int) bool { - return blameList[i].Index < blameList[j].Index + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.BlamesByChainAndNonce(wctx, nil) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should error if blame not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.BlamesByChainAndNonce(wctx, &types.QueryBlameByChainAndNonceRequest{ + ChainId: 1, + Nonce: 1, }) - require.Equal(t, blameList, rst) + require.Error(t, err) + require.Nil(t, res) }) - t.Run("Get no records if nothing is set", func(t *testing.T) { + + t.Run("should return blame info if found", func(t *testing.T) { k, ctx, _, _ := keepertest.ObserverKeeper(t) - rst := k.GetAllBlame(ctx) - require.Len(t, rst, 0) + wctx := sdk.WrapSDKContext(ctx) + var chainId int64 = 97 + var nonce uint64 = 101 + digest := sample.ZetaIndex(t) + + index := types.GetBlameIndex(chainId, nonce, digest, 123) + blame := types.Blame{ + Index: index, + FailureReason: "failed to join party", + Nodes: nil, + } + k.SetBlame(ctx, blame) + + res, err := k.BlamesByChainAndNonce(wctx, &types.QueryBlameByChainAndNonceRequest{ + ChainId: chainId, + Nonce: int64(nonce), + }) + require.NoError(t, err) + require.Equal(t, &types.QueryBlameByChainAndNonceResponse{ + BlameInfo: []*types.Blame{&blame}, + }, res) }) } diff --git a/x/observer/keeper/grpc_query_block_header_test.go b/x/observer/keeper/grpc_query_block_header_test.go new file mode 100644 index 0000000000..9cf50f07a4 --- /dev/null +++ b/x/observer/keeper/grpc_query_block_header_test.go @@ -0,0 +1,119 @@ +package keeper_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/proofs" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/observer/types" +) + +func TestKeeper_GetAllBlockHeaders(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.GetAllBlockHeaders(wctx, nil) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should return if block header is found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + bh := proofs.BlockHeader{ + Height: 1, + Hash: sample.Hash().Bytes(), + ParentHash: sample.Hash().Bytes(), + ChainId: 1, + Header: proofs.HeaderData{}, + } + k.SetBlockHeader(ctx, bh) + + res, err := k.GetAllBlockHeaders(wctx, &types.QueryAllBlockHeaderRequest{}) + require.NoError(t, err) + require.Equal(t, &bh, res.BlockHeaders[0]) + }) +} + +func TestKeeper_GetBlockHeaderByHash(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.GetBlockHeaderByHash(wctx, nil) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should error if not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.GetBlockHeaderByHash(wctx, &types.QueryGetBlockHeaderByHashRequest{ + BlockHash: sample.Hash().Bytes(), + }) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should return if block header is found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + bh := proofs.BlockHeader{ + Height: 1, + Hash: sample.Hash().Bytes(), + ParentHash: sample.Hash().Bytes(), + ChainId: 1, + Header: proofs.HeaderData{}, + } + k.SetBlockHeader(ctx, bh) + + res, err := k.GetBlockHeaderByHash(wctx, &types.QueryGetBlockHeaderByHashRequest{ + BlockHash: bh.Hash, + }) + require.NoError(t, err) + require.Equal(t, &bh, res.BlockHeader) + }) +} + +func TestKeeper_GetBlockHeaderStateByChain(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.GetBlockHeaderStateByChain(wctx, nil) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should error if not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.GetBlockHeaderStateByChain(wctx, &types.QueryGetBlockHeaderStateRequest{ + ChainId: 1, + }) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should return if block header state is found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + bhs := types.BlockHeaderState{ + ChainId: 1, + } + k.SetBlockHeaderState(ctx, bhs) + + res, err := k.GetBlockHeaderStateByChain(wctx, &types.QueryGetBlockHeaderStateRequest{ + ChainId: 1, + }) + require.NoError(t, err) + require.Equal(t, &bhs, res.BlockHeaderState) + }) +} diff --git a/x/observer/keeper/grpc_query_chain_params_test.go b/x/observer/keeper/grpc_query_chain_params_test.go new file mode 100644 index 0000000000..0e7145e5cd --- /dev/null +++ b/x/observer/keeper/grpc_query_chain_params_test.go @@ -0,0 +1,97 @@ +package keeper_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "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/x/observer/types" +) + +func TestKeeper_GetChainParamsForChain(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.GetChainParamsForChain(wctx, nil) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should error if chain params not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.GetChainParamsForChain(wctx, &types.QueryGetChainParamsForChainRequest{ + ChainId: 987, + }) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should return if chain params found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + list := types.ChainParamsList{ + ChainParams: []*types.ChainParams{ + { + ChainId: chains.ZetaPrivnetChain().ChainId, + IsSupported: false, + }, + }, + } + k.SetChainParamsList(ctx, list) + + res, err := k.GetChainParamsForChain(wctx, &types.QueryGetChainParamsForChainRequest{ + ChainId: chains.ZetaPrivnetChain().ChainId, + }) + require.NoError(t, err) + require.Equal(t, &types.QueryGetChainParamsForChainResponse{ + ChainParams: list.ChainParams[0], + }, res) + }) +} + +func TestKeeper_GetChainParams(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.GetChainParams(wctx, nil) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should error if chain params not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.GetChainParams(wctx, &types.QueryGetChainParamsRequest{}) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should return if chain params found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + list := types.ChainParamsList{ + ChainParams: []*types.ChainParams{ + { + ChainId: chains.ZetaPrivnetChain().ChainId, + IsSupported: false, + }, + }, + } + k.SetChainParamsList(ctx, list) + + res, err := k.GetChainParams(wctx, &types.QueryGetChainParamsRequest{}) + require.NoError(t, err) + require.Equal(t, &types.QueryGetChainParamsResponse{ + ChainParams: &list, + }, res) + }) +} diff --git a/x/observer/keeper/grpc_query_crosschain_flags_test.go b/x/observer/keeper/grpc_query_crosschain_flags_test.go new file mode 100644 index 0000000000..172df715d9 --- /dev/null +++ b/x/observer/keeper/grpc_query_crosschain_flags_test.go @@ -0,0 +1,47 @@ +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/observer/types" +) + +func TestKeeper_CrosschainFlags(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.CrosschainFlags(wctx, nil) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should error if crosschain flags not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.CrosschainFlags(wctx, &types.QueryGetCrosschainFlagsRequest{}) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should return if crosschain flags found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + flags := types.CrosschainFlags{ + IsInboundEnabled: false, + } + k.SetCrosschainFlags(ctx, flags) + + res, err := k.CrosschainFlags(wctx, &types.QueryGetCrosschainFlagsRequest{}) + + require.NoError(t, err) + require.Equal(t, &types.QueryGetCrosschainFlagsResponse{ + CrosschainFlags: flags, + }, res) + }) +} diff --git a/x/observer/keeper/grpc_query_keygen_test.go b/x/observer/keeper/grpc_query_keygen_test.go index f4c61aeabd..f081382452 100644 --- a/x/observer/keeper/grpc_query_keygen_test.go +++ b/x/observer/keeper/grpc_query_keygen_test.go @@ -1,4 +1,4 @@ -package keeper +package keeper_test import ( "github.com/stretchr/testify/require" @@ -6,33 +6,32 @@ import ( "testing" sdk "github.com/cosmos/cosmos-sdk/types" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/x/observer/types" ) -func TestKeygenQuery(t *testing.T) { - keeper, ctx := SetupKeeper(t) - wctx := sdk.WrapSDKContext(ctx) - item := createTestKeygen(keeper, ctx) - for _, tc := range []struct { - desc string - request *types.QueryGetKeygenRequest - response *types.QueryGetKeygenResponse - err error - }{ - { - desc: "First", - request: &types.QueryGetKeygenRequest{}, - response: &types.QueryGetKeygenResponse{Keygen: &item}, - }, - } { - tc := tc - t.Run(tc.desc, func(t *testing.T) { - response, err := keeper.Keygen(wctx, tc.request) - if tc.err != nil { - require.ErrorIs(t, err, tc.err) - } else { - require.Equal(t, tc.response, response) - } - }) - } +func TestKeeper_Keygen(t *testing.T) { + t.Run("should error if keygen not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.Keygen(wctx, &types.QueryGetKeygenRequest{}) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should return if keygen found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + keygen := types.Keygen{ + BlockNumber: 10, + } + k.SetKeygen(ctx, keygen) + + res, err := k.Keygen(wctx, &types.QueryGetKeygenRequest{}) + require.NoError(t, err) + require.Equal(t, &types.QueryGetKeygenResponse{ + Keygen: &keygen, + }, res) + }) } diff --git a/x/observer/keeper/grpc_query_node_account_test.go b/x/observer/keeper/grpc_query_node_account_test.go index 03facb08b4..8645357a0c 100644 --- a/x/observer/keeper/grpc_query_node_account_test.go +++ b/x/observer/keeper/grpc_query_node_account_test.go @@ -1,4 +1,4 @@ -package keeper +package keeper_test import ( "testing" @@ -6,15 +6,17 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" "github.com/stretchr/testify/require" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/x/observer/types" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) func TestNodeAccountQuerySingle(t *testing.T) { - keeper, ctx := SetupKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) - msgs := createNNodeAccount(keeper, ctx, 2) + msgs := createNNodeAccount(k, ctx, 2) for _, tc := range []struct { desc string request *types.QueryGetNodeAccountRequest @@ -43,7 +45,7 @@ func TestNodeAccountQuerySingle(t *testing.T) { } { tc := tc t.Run(tc.desc, func(t *testing.T) { - response, err := keeper.NodeAccount(wctx, tc.request) + response, err := k.NodeAccount(wctx, tc.request) if tc.err != nil { require.ErrorIs(t, err, tc.err) } else { @@ -54,9 +56,9 @@ func TestNodeAccountQuerySingle(t *testing.T) { } func TestNodeAccountQueryPaginated(t *testing.T) { - keeper, ctx := SetupKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) wctx := sdk.WrapSDKContext(ctx) - msgs := createNNodeAccount(keeper, ctx, 5) + msgs := createNNodeAccount(k, ctx, 5) request := func(next []byte, offset, limit uint64, total bool) *types.QueryAllNodeAccountRequest { return &types.QueryAllNodeAccountRequest{ @@ -71,7 +73,7 @@ func TestNodeAccountQueryPaginated(t *testing.T) { t.Run("ByOffset", func(t *testing.T) { step := 2 for i := 0; i < len(msgs); i += step { - resp, err := keeper.NodeAccountAll(wctx, request(nil, uint64(i), uint64(step), false)) + resp, err := k.NodeAccountAll(wctx, request(nil, uint64(i), uint64(step), false)) require.NoError(t, err) for j := i; j < len(msgs) && j < i+step; j++ { require.Equal(t, &msgs[j], resp.NodeAccount[j-i]) @@ -82,7 +84,7 @@ func TestNodeAccountQueryPaginated(t *testing.T) { step := 2 var next []byte for i := 0; i < len(msgs); i += step { - resp, err := keeper.NodeAccountAll(wctx, request(next, 0, uint64(step), false)) + resp, err := k.NodeAccountAll(wctx, request(next, 0, uint64(step), false)) require.NoError(t, err) for j := i; j < len(msgs) && j < i+step; j++ { require.Equal(t, &msgs[j], resp.NodeAccount[j-i]) @@ -91,12 +93,12 @@ func TestNodeAccountQueryPaginated(t *testing.T) { } }) t.Run("Total", func(t *testing.T) { - resp, err := keeper.NodeAccountAll(wctx, request(nil, 0, 0, true)) + resp, err := k.NodeAccountAll(wctx, request(nil, 0, 0, true)) require.NoError(t, err) require.Equal(t, len(msgs), int(resp.Pagination.Total)) }) t.Run("InvalidRequest", func(t *testing.T) { - _, err := keeper.NodeAccountAll(wctx, nil) + _, err := k.NodeAccountAll(wctx, nil) require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "invalid request")) }) } diff --git a/x/observer/keeper/grpc_query_nonces_test.go b/x/observer/keeper/grpc_query_nonces_test.go index 657eb59008..5a66eebfbf 100644 --- a/x/observer/keeper/grpc_query_nonces_test.go +++ b/x/observer/keeper/grpc_query_nonces_test.go @@ -108,3 +108,84 @@ func TestChainNoncesQueryPaginated(t *testing.T) { require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "invalid request")) }) } + +func TestPendingNoncesQuerySingle(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.PendingNoncesByChain(wctx, nil) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if tss not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.PendingNoncesByChain(wctx, &types.QueryPendingNoncesByChainRequest{ + ChainId: 1, + }) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + tss := sample.Tss() + k.SetTSS(ctx, tss) + res, err := k.PendingNoncesByChain(wctx, &types.QueryPendingNoncesByChainRequest{ + ChainId: 1, + }) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should return if found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + tss := sample.Tss() + k.SetTSS(ctx, tss) + pendingNonces := sample.PendingNoncesList(t, "sample", 5) + pendingNonces[1].Tss = tss.TssPubkey + for _, nonce := range pendingNonces { + k.SetPendingNonces(ctx, nonce) + } + res, err := k.PendingNoncesByChain(wctx, &types.QueryPendingNoncesByChainRequest{ + ChainId: 1, + }) + require.NoError(t, err) + require.Equal(t, pendingNonces[1], res.PendingNonces) + }) +} + +func TestPendingNoncesQueryPaginated(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + pendingNonces := sample.PendingNoncesList(t, "sample", 5) + for _, nonce := range pendingNonces { + k.SetPendingNonces(ctx, nonce) + } + + request := func(next []byte, offset, limit uint64, total bool) *types.QueryAllPendingNoncesRequest { + return &types.QueryAllPendingNoncesRequest{ + Pagination: &query.PageRequest{ + Key: next, + Offset: offset, + Limit: limit, + CountTotal: total, + }, + } + } + + t.Run("Total", func(t *testing.T) { + resp, err := k.PendingNoncesAll(wctx, request(nil, 0, 0, true)) + require.NoError(t, err) + require.Equal(t, len(pendingNonces), int(resp.Pagination.Total)) + }) + t.Run("InvalidRequest", func(t *testing.T) { + _, err := k.PendingNoncesAll(wctx, nil) + require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "invalid request")) + }) +} diff --git a/x/observer/keeper/grpc_query_observer_test.go b/x/observer/keeper/grpc_query_observer_test.go new file mode 100644 index 0000000000..73675fae0d --- /dev/null +++ b/x/observer/keeper/grpc_query_observer_test.go @@ -0,0 +1,78 @@ +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/testutil/sample" + "github.com/zeta-chain/zetacore/x/observer/types" +) + +func TestKeeper_ShowObserverCount(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.ShowObserverCount(wctx, nil) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.ShowObserverCount(wctx, &types.QueryShowObserverCountRequest{}) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should return if found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + count := 1 + loc := &types.LastObserverCount{ + Count: uint64(count), + } + k.SetLastObserverCount(ctx, loc) + + res, err := k.ShowObserverCount(wctx, &types.QueryShowObserverCountRequest{}) + require.NoError(t, err) + require.Equal(t, loc, res.LastObserverCount) + }) +} + +func TestKeeper_ObserverSet(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.ObserverSet(wctx, nil) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.ObserverSet(wctx, &types.QueryObserverSet{}) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should return if found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + os := sample.ObserverSet(10) + k.SetObserverSet(ctx, os) + + res, err := k.ObserverSet(wctx, &types.QueryObserverSet{}) + require.NoError(t, err) + require.Equal(t, os.ObserverList, res.Observers) + }) +} diff --git a/x/observer/keeper/grpc_query_params_test.go b/x/observer/keeper/grpc_query_params_test.go index 4cd534fa59..a1de6c63b2 100644 --- a/x/observer/keeper/grpc_query_params_test.go +++ b/x/observer/keeper/grpc_query_params_test.go @@ -1,20 +1,21 @@ -package keeper +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/observer/types" ) func TestParamsQuery(t *testing.T) { - keeper, ctx := SetupKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) wctx := sdk.WrapSDKContext(ctx) params := types.DefaultParams() - keeper.SetParams(ctx, params) + k.SetParams(ctx, params) - response, err := keeper.Params(wctx, &types.QueryParamsRequest{}) + response, err := k.Params(wctx, &types.QueryParamsRequest{}) require.NoError(t, err) require.Equal(t, &types.QueryParamsResponse{Params: params}, response) } diff --git a/x/observer/keeper/grpc_query_prove_test.go b/x/observer/keeper/grpc_query_prove_test.go new file mode 100644 index 0000000000..7eb11b00f5 --- /dev/null +++ b/x/observer/keeper/grpc_query_prove_test.go @@ -0,0 +1,72 @@ +package keeper_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/proofs" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/observer/types" +) + +func TestKeeper_Prove(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.Prove(wctx, nil) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should error if invalid hash", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.Prove(wctx, &types.QueryProveRequest{ + ChainId: 987, + BlockHash: sample.Hash().String(), + }) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should error if header not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.Prove(wctx, &types.QueryProveRequest{ + ChainId: 5, + BlockHash: sample.Hash().String(), + }) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should error if proof not valid", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + hash := sample.Hash() + bh := proofs.BlockHeader{ + Height: 1, + Hash: hash.Bytes(), + ParentHash: sample.Hash().Bytes(), + ChainId: 1, + Header: proofs.HeaderData{}, + } + k.SetBlockHeader(ctx, bh) + + res, err := k.Prove(wctx, &types.QueryProveRequest{ + ChainId: 5, + BlockHash: hash.String(), + Proof: &proofs.Proof{}, + }) + require.Nil(t, res) + require.Error(t, err) + }) + + // TODO: // https://github.com/zeta-chain/node/issues/1875 add more tests +} diff --git a/x/observer/keeper/grpc_query_supported_chain_test.go b/x/observer/keeper/grpc_query_supported_chain_test.go new file mode 100644 index 0000000000..50acd5703a --- /dev/null +++ b/x/observer/keeper/grpc_query_supported_chain_test.go @@ -0,0 +1,21 @@ +package keeper_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" +) + +func TestKeeper_SupportedChains(t *testing.T) { + t.Run("should return supported chains", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.SupportedChains(wctx, nil) + require.NoError(t, err) + require.Equal(t, []*chains.Chain{}, res.Chains) + }) +} diff --git a/x/observer/keeper/grpc_query_tss_test.go b/x/observer/keeper/grpc_query_tss_test.go new file mode 100644 index 0000000000..105d7c1cf9 --- /dev/null +++ b/x/observer/keeper/grpc_query_tss_test.go @@ -0,0 +1,230 @@ +package keeper_test + +import ( + "math/rand" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/crypto" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/observer/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func TestTSSQuerySingle(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + tss := sample.Tss() + wctx := sdk.WrapSDKContext(ctx) + + for _, tc := range []struct { + desc string + request *types.QueryGetTSSRequest + response *types.QueryGetTSSResponse + skipSettingTss bool + err error + }{ + { + desc: "Skip setting tss", + request: &types.QueryGetTSSRequest{}, + skipSettingTss: true, + err: status.Error(codes.InvalidArgument, "not found"), + }, + { + desc: "InvalidRequest", + err: status.Error(codes.InvalidArgument, "invalid request"), + }, + { + desc: "Should return tss", + request: &types.QueryGetTSSRequest{}, + response: &types.QueryGetTSSResponse{TSS: tss}, + }, + } { + tc := tc + t.Run(tc.desc, func(t *testing.T) { + if !tc.skipSettingTss { + k.SetTSS(ctx, tss) + } + response, err := k.TSS(wctx, tc.request) + if tc.err != nil { + require.ErrorIs(t, err, tc.err) + } else { + require.Equal(t, tc.response, response) + } + }) + } +} + +func TestTSSQueryHistory(t *testing.T) { + keeper, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + for _, tc := range []struct { + desc string + tssCount int + foundPrevious bool + err error + }{ + { + desc: "1 Tss addresses", + tssCount: 1, + foundPrevious: false, + err: nil, + }, + { + desc: "10 Tss addresses", + tssCount: 10, + foundPrevious: true, + err: nil, + }, + } { + tc := tc + 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) + } + request := &types.QueryTssHistoryRequest{} + response, err := keeper.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) + require.Equal(t, tc.foundPrevious, found) + if found { + require.Equal(t, tssList[len(tssList)-2], prevTss) + } + } + }) + } +} + +func TestKeeper_GetTssAddress(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.GetTssAddress(wctx, nil) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should error if not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.GetTssAddress(wctx, &types.QueryGetTssAddressRequest{ + BitcoinChainId: 1, + }) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should error if invalid chain id", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + tss := sample.Tss() + k.SetTSS(ctx, tss) + + res, err := k.GetTssAddress(wctx, &types.QueryGetTssAddressRequest{ + BitcoinChainId: 987, + }) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should return if valid chain id", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + tss := sample.Tss() + k.SetTSS(ctx, tss) + + res, err := k.GetTssAddress(wctx, &types.QueryGetTssAddressRequest{ + BitcoinChainId: chains.BtcRegtestChain().ChainId, + }) + require.NoError(t, err) + expectedBitcoinParams, err := chains.BitcoinNetParamsFromChainID(chains.BtcRegtestChain().ChainId) + require.NoError(t, err) + expectedBtcAddress, err := crypto.GetTssAddrBTC(tss.TssPubkey, expectedBitcoinParams) + require.NoError(t, err) + expectedEthAddress, err := crypto.GetTssAddrEVM(tss.TssPubkey) + require.NoError(t, err) + require.NotNil(t, &types.QueryGetTssAddressByFinalizedHeightResponse{ + Eth: expectedEthAddress.String(), + Btc: expectedBtcAddress, + }, res) + }) +} + +func TestKeeper_GetTssAddressByFinalizedHeight(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.GetTssAddressByFinalizedHeight(wctx, nil) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should error if not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + res, err := k.GetTssAddressByFinalizedHeight(wctx, &types.QueryGetTssAddressByFinalizedHeightRequest{ + BitcoinChainId: 1, + }) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should error if invalid chain id", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + tssList := sample.TssList(100) + r := rand.Intn((len(tssList)-1)-0) + 0 + for _, tss := range tssList { + k.SetTSSHistory(ctx, tss) + } + + res, err := k.GetTssAddressByFinalizedHeight(wctx, &types.QueryGetTssAddressByFinalizedHeightRequest{ + BitcoinChainId: 987, + FinalizedZetaHeight: tssList[r].FinalizedZetaHeight, + }) + require.Nil(t, res) + require.Error(t, err) + }) + + t.Run("should return if valid chain id", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + wctx := sdk.WrapSDKContext(ctx) + + tssList := sample.TssList(100) + r := rand.Intn((len(tssList)-1)-0) + 0 + for _, tss := range tssList { + k.SetTSSHistory(ctx, tss) + } + + res, err := k.GetTssAddressByFinalizedHeight(wctx, &types.QueryGetTssAddressByFinalizedHeightRequest{ + BitcoinChainId: chains.BtcRegtestChain().ChainId, + FinalizedZetaHeight: tssList[r].FinalizedZetaHeight, + }) + require.NoError(t, err) + expectedBitcoinParams, err := chains.BitcoinNetParamsFromChainID(chains.BtcRegtestChain().ChainId) + require.NoError(t, err) + expectedBtcAddress, err := crypto.GetTssAddrBTC(tssList[r].TssPubkey, expectedBitcoinParams) + require.NoError(t, err) + expectedEthAddress, err := crypto.GetTssAddrEVM(tssList[r].TssPubkey) + require.NoError(t, err) + require.NotNil(t, &types.QueryGetTssAddressByFinalizedHeightResponse{ + Eth: expectedEthAddress.String(), + Btc: expectedBtcAddress, + }, res) + }) +} diff --git a/x/observer/keeper/hooks_test.go b/x/observer/keeper/hooks_test.go new file mode 100644 index 0000000000..ec782663ee --- /dev/null +++ b/x/observer/keeper/hooks_test.go @@ -0,0 +1,213 @@ +package keeper_test + +import ( + "math/rand" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/stretchr/testify/require" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/observer/types" +) + +func TestKeeper_NotImplementedHooks(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + hooks := k.Hooks() + require.Nil(t, hooks.AfterValidatorCreated(ctx, nil)) + require.Nil(t, hooks.BeforeValidatorModified(ctx, nil)) + require.Nil(t, hooks.AfterValidatorBonded(ctx, nil, nil)) + require.Nil(t, hooks.BeforeDelegationCreated(ctx, nil, nil)) + require.Nil(t, hooks.BeforeDelegationSharesModified(ctx, nil, nil)) + require.Nil(t, hooks.BeforeDelegationRemoved(ctx, nil, nil)) +} + +func TestKeeper_AfterValidatorRemoved(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + // #nosec G404 test purpose - weak randomness is not an issue here + r := rand.New(rand.NewSource(1)) + valAddr := sample.ValAddress(r) + accAddress, err := types.GetAccAddressFromOperatorAddress(valAddr.String()) + require.NoError(t, err) + os := types.ObserverSet{ + ObserverList: []string{accAddress.String()}, + } + k.SetObserverSet(ctx, os) + + hooks := k.Hooks() + err = hooks.AfterValidatorRemoved(ctx, nil, valAddr) + require.NoError(t, err) + + os, found := k.GetObserverSet(ctx) + require.True(t, found) + // observer for validator is removed from set + require.Empty(t, os.ObserverList) +} + +func TestKeeper_AfterValidatorBeginUnbonding(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + r := rand.New(rand.NewSource(9)) + validator := sample.Validator(t, r) + validator.DelegatorShares = sdk.NewDec(100) + k.GetStakingKeeper().SetValidator(ctx, validator) + accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) + require.NoError(t, err) + + k.GetStakingKeeper().SetDelegation(ctx, stakingtypes.Delegation{ + DelegatorAddress: accAddressOfValidator.String(), + ValidatorAddress: validator.GetOperator().String(), + Shares: sdk.NewDec(10), + }) + + k.SetObserverSet(ctx, types.ObserverSet{ + ObserverList: []string{accAddressOfValidator.String()}, + }) + + hooks := k.Hooks() + err = hooks.AfterValidatorBeginUnbonding(ctx, nil, validator.GetOperator()) + require.NoError(t, err) + + os, found := k.GetObserverSet(ctx) + require.True(t, found) + require.Empty(t, os.ObserverList) +} + +func TestKeeper_AfterDelegationModified(t *testing.T) { + t.Run("should not clean observer if not self delegation", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + r := rand.New(rand.NewSource(9)) + validator := sample.Validator(t, r) + validator.DelegatorShares = sdk.NewDec(100) + k.GetStakingKeeper().SetValidator(ctx, validator) + accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) + require.NoError(t, err) + + k.GetStakingKeeper().SetDelegation(ctx, stakingtypes.Delegation{ + DelegatorAddress: accAddressOfValidator.String(), + ValidatorAddress: validator.GetOperator().String(), + Shares: sdk.NewDec(10), + }) + + k.SetObserverSet(ctx, types.ObserverSet{ + ObserverList: []string{accAddressOfValidator.String()}, + }) + + hooks := k.Hooks() + err = hooks.AfterDelegationModified(ctx, sdk.AccAddress(sample.AccAddress()), validator.GetOperator()) + require.NoError(t, err) + + os, found := k.GetObserverSet(ctx) + require.True(t, found) + require.Equal(t, 1, len(os.ObserverList)) + require.Equal(t, accAddressOfValidator.String(), os.ObserverList[0]) + }) + + t.Run("should clean observer if self delegation", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + r := rand.New(rand.NewSource(9)) + validator := sample.Validator(t, r) + validator.DelegatorShares = sdk.NewDec(100) + k.GetStakingKeeper().SetValidator(ctx, validator) + accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) + require.NoError(t, err) + + k.GetStakingKeeper().SetDelegation(ctx, stakingtypes.Delegation{ + DelegatorAddress: accAddressOfValidator.String(), + ValidatorAddress: validator.GetOperator().String(), + Shares: sdk.NewDec(10), + }) + + k.SetObserverSet(ctx, types.ObserverSet{ + ObserverList: []string{accAddressOfValidator.String()}, + }) + + hooks := k.Hooks() + err = hooks.AfterDelegationModified(ctx, accAddressOfValidator, validator.GetOperator()) + require.NoError(t, err) + + os, found := k.GetObserverSet(ctx) + require.True(t, found) + require.Empty(t, os.ObserverList) + }) +} + +func TestKeeper_BeforeValidatorSlashed(t *testing.T) { + t.Run("should error if validator not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + r := rand.New(rand.NewSource(9)) + validator := sample.Validator(t, r) + + hooks := k.Hooks() + err := hooks.BeforeValidatorSlashed(ctx, validator.GetOperator(), sdk.NewDec(1)) + require.Error(t, err) + }) + + t.Run("should not error if observer set not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + r := rand.New(rand.NewSource(9)) + validator := sample.Validator(t, r) + validator.DelegatorShares = sdk.NewDec(100) + k.GetStakingKeeper().SetValidator(ctx, validator) + + hooks := k.Hooks() + err := hooks.BeforeValidatorSlashed(ctx, validator.GetOperator(), sdk.NewDec(1)) + require.NoError(t, err) + }) + + t.Run("should remove from observer set", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + r := rand.New(rand.NewSource(9)) + validator := sample.Validator(t, r) + validator.DelegatorShares = sdk.NewDec(100) + validator.Tokens = sdk.NewInt(100) + k.GetStakingKeeper().SetValidator(ctx, validator) + accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) + require.NoError(t, err) + + k.SetObserverSet(ctx, types.ObserverSet{ + ObserverList: []string{accAddressOfValidator.String()}, + }) + + hooks := k.Hooks() + err = hooks.BeforeValidatorSlashed(ctx, validator.GetOperator(), sdk.MustNewDecFromStr("0.1")) + require.NoError(t, err) + + os, found := k.GetObserverSet(ctx) + require.True(t, found) + require.Empty(t, os.ObserverList) + }) + + t.Run("should not remove from observer set", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + r := rand.New(rand.NewSource(9)) + validator := sample.Validator(t, r) + validator.DelegatorShares = sdk.NewDec(100) + validator.Tokens = sdk.NewInt(1000000000000000000) + k.GetStakingKeeper().SetValidator(ctx, validator) + accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) + require.NoError(t, err) + + k.SetObserverSet(ctx, types.ObserverSet{ + ObserverList: []string{accAddressOfValidator.String()}, + }) + + hooks := k.Hooks() + err = hooks.BeforeValidatorSlashed(ctx, validator.GetOperator(), sdk.MustNewDecFromStr("0")) + require.NoError(t, err) + + os, found := k.GetObserverSet(ctx) + require.True(t, found) + require.Equal(t, 1, len(os.ObserverList)) + require.Equal(t, accAddressOfValidator.String(), os.ObserverList[0]) + }) +} diff --git a/x/observer/keeper/keeper_test.go b/x/observer/keeper/keeper_test.go deleted file mode 100644 index c34cdb5392..0000000000 --- a/x/observer/keeper/keeper_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package keeper - -import ( - "testing" - - "github.com/cosmos/cosmos-sdk/codec" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/store" - storetypes "github.com/cosmos/cosmos-sdk/store/types" - sdk "github.com/cosmos/cosmos-sdk/types" - typesparams "github.com/cosmos/cosmos-sdk/x/params/types" - slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" - stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" - "github.com/stretchr/testify/require" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - tmdb "github.com/tendermint/tm-db" - authoritykeeper "github.com/zeta-chain/zetacore/x/authority/keeper" - "github.com/zeta-chain/zetacore/x/observer/types" -) - -func SetupKeeper(t testing.TB) (*Keeper, sdk.Context) { - storeKey := sdk.NewKVStoreKey(types.StoreKey) - memStoreKey := storetypes.NewMemoryStoreKey(types.MemStoreKey) - - db := tmdb.NewMemDB() - stateStore := store.NewCommitMultiStore(db) - stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db) - stateStore.MountStoreWithDB(memStoreKey, storetypes.StoreTypeMemory, nil) - require.NoError(t, stateStore.LoadLatestVersion()) - - registry := codectypes.NewInterfaceRegistry() - cdc := codec.NewProtoCodec(registry) - - paramsSubspace := typesparams.NewSubspace(cdc, - types.Amino, - storeKey, - memStoreKey, - "ZetaObsParams", - ) - - k := NewKeeper( - codec.NewProtoCodec(registry), - storeKey, - memStoreKey, - paramsSubspace, - stakingkeeper.Keeper{}, - slashingkeeper.Keeper{}, - authoritykeeper.Keeper{}, - ) - - ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, nil) - return k, ctx -} diff --git a/x/observer/keeper/keygen_test.go b/x/observer/keeper/keygen_test.go index f14fa3aeef..ef9a5e6991 100644 --- a/x/observer/keeper/keygen_test.go +++ b/x/observer/keeper/keygen_test.go @@ -1,4 +1,4 @@ -package keeper +package keeper_test import ( "testing" @@ -6,11 +6,13 @@ import ( 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/observer/keeper" "github.com/zeta-chain/zetacore/x/observer/types" ) // Keeper Tests -func createTestKeygen(keeper *Keeper, ctx sdk.Context) types.Keygen { +func createTestKeygen(keeper *keeper.Keeper, ctx sdk.Context) types.Keygen { item := types.Keygen{ BlockNumber: 10, } @@ -19,16 +21,16 @@ func createTestKeygen(keeper *Keeper, ctx sdk.Context) types.Keygen { } func TestKeygenGet(t *testing.T) { - keeper, ctx := SetupKeeper(t) - item := createTestKeygen(keeper, ctx) - rst, found := keeper.GetKeygen(ctx) + k, ctx, _, _ := keepertest.ObserverKeeper(t) + item := createTestKeygen(k, ctx) + rst, found := k.GetKeygen(ctx) require.True(t, found) require.Equal(t, item, rst) } func TestKeygenRemove(t *testing.T) { - keeper, ctx := SetupKeeper(t) - createTestKeygen(keeper, ctx) - keeper.RemoveKeygen(ctx) - _, found := keeper.GetKeygen(ctx) + k, ctx, _, _ := keepertest.ObserverKeeper(t) + createTestKeygen(k, ctx) + k.RemoveKeygen(ctx) + _, found := k.GetKeygen(ctx) require.False(t, found) } diff --git a/x/observer/keeper/msg_server_add_blame_vote_test.go b/x/observer/keeper/msg_server_add_blame_vote_test.go new file mode 100644 index 0000000000..28ada869b0 --- /dev/null +++ b/x/observer/keeper/msg_server_add_blame_vote_test.go @@ -0,0 +1,182 @@ +package keeper_test + +import ( + "math/rand" + "testing" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + "github.com/stretchr/testify/require" + 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_AddBlameVote(t *testing.T) { + t.Run("should error if supported chain not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + srv := keeper.NewMsgServerImpl(*k) + + res, err := srv.AddBlameVote(ctx, &types.MsgAddBlameVote{ + ChainId: 1, + }) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if not tombstoned observer", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + srv := keeper.NewMsgServerImpl(*k) + + chainId := getValidEthChainIDWithIndex(t, 0) + setSupportedChain(ctx, *k, chainId) + + res, err := srv.AddBlameVote(ctx, &types.MsgAddBlameVote{ + ChainId: chainId, + }) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should return response and set blame if finalizing vote", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + srv := keeper.NewMsgServerImpl(*k) + + chainId := getValidEthChainIDWithIndex(t, 0) + setSupportedChain(ctx, *k, chainId) + + r := rand.New(rand.NewSource(9)) + // Set validator in the store + validator := sample.Validator(t, r) + k.GetStakingKeeper().SetValidator(ctx, validator) + consAddress, err := validator.GetConsAddr() + require.NoError(t, err) + k.GetSlashingKeeper().SetValidatorSigningInfo(ctx, consAddress, slashingtypes.ValidatorSigningInfo{ + Address: consAddress.String(), + StartHeight: 0, + JailedUntil: ctx.BlockHeader().Time.Add(1000000 * time.Second), + Tombstoned: false, + MissedBlocksCounter: 1, + }) + + accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) + require.NoError(t, err) + + k.SetObserverSet(ctx, types.ObserverSet{ + ObserverList: []string{accAddressOfValidator.String()}, + }) + + blameInfo := sample.BlameRecord(t, "index") + res, err := srv.AddBlameVote(ctx, &types.MsgAddBlameVote{ + Creator: accAddressOfValidator.String(), + ChainId: chainId, + BlameInfo: blameInfo, + }) + require.NoError(t, err) + require.Equal(t, &types.MsgAddBlameVoteResponse{}, res) + + blame, found := k.GetBlame(ctx, blameInfo.Index) + require.True(t, found) + require.Equal(t, blameInfo, blame) + }) + + t.Run("should error if add vote fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + srv := keeper.NewMsgServerImpl(*k) + + chainId := getValidEthChainIDWithIndex(t, 1) + setSupportedChain(ctx, *k, chainId) + + r := rand.New(rand.NewSource(9)) + // Set validator in the store + validator := sample.Validator(t, r) + k.GetStakingKeeper().SetValidator(ctx, validator) + consAddress, err := validator.GetConsAddr() + require.NoError(t, err) + k.GetSlashingKeeper().SetValidatorSigningInfo(ctx, consAddress, slashingtypes.ValidatorSigningInfo{ + Address: consAddress.String(), + StartHeight: 0, + JailedUntil: ctx.BlockHeader().Time.Add(1000000 * time.Second), + Tombstoned: false, + MissedBlocksCounter: 1, + }) + + accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) + require.NoError(t, err) + + k.SetObserverSet(ctx, types.ObserverSet{ + ObserverList: []string{accAddressOfValidator.String(), "Observer2"}, + }) + blameInfo := sample.BlameRecord(t, "index") + vote := &types.MsgAddBlameVote{ + Creator: accAddressOfValidator.String(), + ChainId: chainId, + BlameInfo: blameInfo, + } + ballot := types.Ballot{ + Index: vote.Digest(), + BallotIdentifier: vote.Digest(), + VoterList: []string{accAddressOfValidator.String()}, + Votes: []types.VoteType{types.VoteType_SuccessObservation}, + BallotStatus: types.BallotStatus_BallotInProgress, + BallotThreshold: sdk.NewDec(2), + } + k.SetBallot(ctx, &ballot) + + _, err = srv.AddBlameVote(ctx, vote) + require.Error(t, err) + }) + + t.Run("should return response and not set blame if not finalizing vote", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + srv := keeper.NewMsgServerImpl(*k) + + chainId := getValidEthChainIDWithIndex(t, 1) + setSupportedChain(ctx, *k, chainId) + + r := rand.New(rand.NewSource(9)) + // Set validator in the store + validator := sample.Validator(t, r) + k.GetStakingKeeper().SetValidator(ctx, validator) + consAddress, err := validator.GetConsAddr() + require.NoError(t, err) + k.GetSlashingKeeper().SetValidatorSigningInfo(ctx, consAddress, slashingtypes.ValidatorSigningInfo{ + Address: consAddress.String(), + StartHeight: 0, + JailedUntil: ctx.BlockHeader().Time.Add(1000000 * time.Second), + Tombstoned: false, + MissedBlocksCounter: 1, + }) + + accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) + require.NoError(t, err) + + k.SetObserverSet(ctx, types.ObserverSet{ + ObserverList: []string{accAddressOfValidator.String(), "Observer2"}, + }) + blameInfo := sample.BlameRecord(t, "index") + vote := &types.MsgAddBlameVote{ + Creator: accAddressOfValidator.String(), + ChainId: chainId, + BlameInfo: blameInfo, + } + ballot := types.Ballot{ + Index: vote.Digest(), + BallotIdentifier: vote.Digest(), + VoterList: []string{accAddressOfValidator.String()}, + Votes: []types.VoteType{types.VoteType_NotYetVoted}, + BallotStatus: types.BallotStatus_BallotInProgress, + BallotThreshold: sdk.NewDec(2), + } + k.SetBallot(ctx, &ballot) + + res, err := srv.AddBlameVote(ctx, vote) + require.NoError(t, err) + require.Equal(t, &types.MsgAddBlameVoteResponse{}, res) + + _, found := k.GetBlame(ctx, blameInfo.Index) + require.False(t, found) + }) +} diff --git a/x/observer/keeper/msg_server_add_observer_test.go b/x/observer/keeper/msg_server_add_observer_test.go new file mode 100644 index 0000000000..0cbb9df514 --- /dev/null +++ b/x/observer/keeper/msg_server_add_observer_test.go @@ -0,0 +1,117 @@ +package keeper_test + +import ( + "math" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + "github.com/zeta-chain/zetacore/x/observer/keeper" + "github.com/zeta-chain/zetacore/x/observer/types" +) + +func TestMsgServer_AddObserver(t *testing.T) { + t.Run("should error if not authorized", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMockOptions{ + UseAuthorityMock: true, + }) + authorityMock := keepertest.GetObserverAuthorityMock(t, k) + admin := sample.AccAddress() + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, false) + wctx := sdk.WrapSDKContext(ctx) + + srv := keeper.NewMsgServerImpl(*k) + res, err := srv.AddObserver(wctx, &types.MsgAddObserver{ + Creator: admin, + }) + require.Error(t, err) + require.Equal(t, &types.MsgAddObserverResponse{}, res) + }) + + t.Run("should error if pub key not valid", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMockOptions{ + UseAuthorityMock: true, + }) + authorityMock := keepertest.GetObserverAuthorityMock(t, k) + admin := sample.AccAddress() + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) + wctx := sdk.WrapSDKContext(ctx) + + srv := keeper.NewMsgServerImpl(*k) + res, err := srv.AddObserver(wctx, &types.MsgAddObserver{ + Creator: admin, + ZetaclientGranteePubkey: "invalid", + }) + require.Error(t, err) + require.Equal(t, &types.MsgAddObserverResponse{}, res) + }) + + t.Run("should add if add node account only false", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMockOptions{ + UseAuthorityMock: true, + }) + authorityMock := keepertest.GetObserverAuthorityMock(t, k) + admin := sample.AccAddress() + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) + wctx := sdk.WrapSDKContext(ctx) + + _, found := k.GetLastObserverCount(ctx) + require.False(t, found) + srv := keeper.NewMsgServerImpl(*k) + observerAddress := sdk.AccAddress(crypto.AddressHash([]byte("ObserverAddress"))) + res, err := srv.AddObserver(wctx, &types.MsgAddObserver{ + Creator: admin, + ZetaclientGranteePubkey: sample.PubKeyString(), + AddNodeAccountOnly: false, + ObserverAddress: observerAddress.String(), + }) + require.NoError(t, err) + require.Equal(t, &types.MsgAddObserverResponse{}, res) + + loc, found := k.GetLastObserverCount(ctx) + require.True(t, found) + require.Equal(t, uint64(1), loc.Count) + }) + + t.Run("should add to node account if add node account only true", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMockOptions{ + UseAuthorityMock: true, + }) + authorityMock := keepertest.GetObserverAuthorityMock(t, k) + admin := sample.AccAddress() + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) + wctx := sdk.WrapSDKContext(ctx) + + _, found := k.GetLastObserverCount(ctx) + require.False(t, found) + srv := keeper.NewMsgServerImpl(*k) + observerAddress := sdk.AccAddress(crypto.AddressHash([]byte("ObserverAddress"))) + _, found = k.GetKeygen(ctx) + require.False(t, found) + _, found = k.GetNodeAccount(ctx, observerAddress.String()) + require.False(t, found) + + res, err := srv.AddObserver(wctx, &types.MsgAddObserver{ + Creator: admin, + ZetaclientGranteePubkey: sample.PubKeyString(), + AddNodeAccountOnly: true, + ObserverAddress: observerAddress.String(), + }) + require.NoError(t, err) + require.Equal(t, &types.MsgAddObserverResponse{}, res) + + _, found = k.GetLastObserverCount(ctx) + require.False(t, found) + + keygen, found := k.GetKeygen(ctx) + require.True(t, found) + require.Equal(t, types.Keygen{BlockNumber: math.MaxInt64}, keygen) + + _, found = k.GetNodeAccount(ctx, observerAddress.String()) + require.True(t, found) + }) +} 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_update_keygen_test.go b/x/observer/keeper/msg_server_update_keygen_test.go new file mode 100644 index 0000000000..8b3201c5ed --- /dev/null +++ b/x/observer/keeper/msg_server_update_keygen_test.go @@ -0,0 +1,105 @@ +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/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + "github.com/zeta-chain/zetacore/x/observer/keeper" + "github.com/zeta-chain/zetacore/x/observer/types" +) + +func TestMsgServer_UpdateKeygen(t *testing.T) { + t.Run("should error if not authorized", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMockOptions{ + UseAuthorityMock: true, + }) + authorityMock := keepertest.GetObserverAuthorityMock(t, k) + admin := sample.AccAddress() + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupEmergency, false) + wctx := sdk.WrapSDKContext(ctx) + + srv := keeper.NewMsgServerImpl(*k) + res, err := srv.UpdateKeygen(wctx, &types.MsgUpdateKeygen{ + Creator: admin, + }) + require.Error(t, err) + require.Equal(t, &types.MsgUpdateKeygenResponse{}, res) + }) + + t.Run("should error if keygen not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMockOptions{ + UseAuthorityMock: true, + }) + authorityMock := keepertest.GetObserverAuthorityMock(t, k) + admin := sample.AccAddress() + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupEmergency, true) + wctx := sdk.WrapSDKContext(ctx) + + srv := keeper.NewMsgServerImpl(*k) + res, err := srv.UpdateKeygen(wctx, &types.MsgUpdateKeygen{ + Creator: admin, + }) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if msg block too low", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMockOptions{ + UseAuthorityMock: true, + }) + authorityMock := keepertest.GetObserverAuthorityMock(t, k) + admin := sample.AccAddress() + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupEmergency, true) + wctx := sdk.WrapSDKContext(ctx) + item := types.Keygen{ + BlockNumber: 10, + } + k.SetKeygen(ctx, item) + srv := keeper.NewMsgServerImpl(*k) + res, err := srv.UpdateKeygen(wctx, &types.MsgUpdateKeygen{ + Creator: admin, + Block: 2, + }) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should update", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMockOptions{ + UseAuthorityMock: true, + }) + authorityMock := keepertest.GetObserverAuthorityMock(t, k) + admin := sample.AccAddress() + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupEmergency, true) + wctx := sdk.WrapSDKContext(ctx) + item := types.Keygen{ + BlockNumber: 10, + } + k.SetKeygen(ctx, item) + srv := keeper.NewMsgServerImpl(*k) + + granteePubKey := sample.PubKeySet() + k.SetNodeAccount(ctx, types.NodeAccount{ + Operator: "operator", + GranteePubkey: granteePubKey, + }) + + res, err := srv.UpdateKeygen(wctx, &types.MsgUpdateKeygen{ + Creator: admin, + Block: ctx.BlockHeight() + 30, + }) + require.NoError(t, err) + require.Equal(t, &types.MsgUpdateKeygenResponse{}, res) + + keygen, found := k.GetKeygen(ctx) + require.True(t, found) + require.Equal(t, 1, len(keygen.GranteePubkeys)) + require.Equal(t, granteePubKey.Secp256k1.String(), keygen.GranteePubkeys[0]) + require.Equal(t, ctx.BlockHeight()+30, keygen.BlockNumber) + require.Equal(t, types.KeygenStatus_PendingKeygen, keygen.Status) + }) +} 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/node_account_test.go b/x/observer/keeper/node_account_test.go index 26a5c47d1c..c20501f8b2 100644 --- a/x/observer/keeper/node_account_test.go +++ b/x/observer/keeper/node_account_test.go @@ -1,4 +1,4 @@ -package keeper +package keeper_test import ( "fmt" @@ -6,12 +6,14 @@ import ( 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/observer/keeper" "github.com/zeta-chain/zetacore/x/observer/types" ) // Keeper Tests -func createNNodeAccount(keeper *Keeper, ctx sdk.Context, n int) []types.NodeAccount { +func createNNodeAccount(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.NodeAccount { items := make([]types.NodeAccount, n) for i := range items { items[i].Operator = fmt.Sprintf("%d", i) @@ -21,26 +23,28 @@ func createNNodeAccount(keeper *Keeper, ctx sdk.Context, n int) []types.NodeAcco } func TestNodeAccountGet(t *testing.T) { - keeper, ctx := SetupKeeper(t) - items := createNNodeAccount(keeper, ctx, 10) + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + items := createNNodeAccount(k, ctx, 10) for _, item := range items { - rst, found := keeper.GetNodeAccount(ctx, item.Operator) + rst, found := k.GetNodeAccount(ctx, item.Operator) require.True(t, found) require.Equal(t, item, rst) } } func TestNodeAccountRemove(t *testing.T) { - keeper, ctx := SetupKeeper(t) - items := createNNodeAccount(keeper, ctx, 10) + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + items := createNNodeAccount(k, ctx, 10) for _, item := range items { - keeper.RemoveNodeAccount(ctx, item.Operator) - _, found := keeper.GetNodeAccount(ctx, item.Operator) + k.RemoveNodeAccount(ctx, item.Operator) + _, found := k.GetNodeAccount(ctx, item.Operator) require.False(t, found) } } func TestNodeAccountGetAll(t *testing.T) { - keeper, ctx := SetupKeeper(t) - items := createNNodeAccount(keeper, ctx, 10) - require.Equal(t, items, keeper.GetAllNodeAccount(ctx)) + k, ctx, _, _ := keepertest.ObserverKeeper(t) + items := createNNodeAccount(k, ctx, 10) + require.Equal(t, items, k.GetAllNodeAccount(ctx)) } diff --git a/x/observer/keeper/nonce_to_cctx_test.go b/x/observer/keeper/nonce_to_cctx_test.go index 760e4f7111..a16a21f5ec 100644 --- a/x/observer/keeper/nonce_to_cctx_test.go +++ b/x/observer/keeper/nonce_to_cctx_test.go @@ -20,6 +20,14 @@ func TestKeeper_GetNonceToCctx(t *testing.T) { require.True(t, found) require.Equal(t, n, rst) } + + for _, n := range nonceToCctxList { + k.RemoveNonceToCctx(ctx, n) + } + for _, n := range nonceToCctxList { + _, found := k.GetNonceToCctx(ctx, n.Tss, n.ChainId, n.Nonce) + require.False(t, found) + } }) t.Run("Get nonce to cctx not found", func(t *testing.T) { k, ctx, _, _ := keepertest.ObserverKeeper(t) diff --git a/x/observer/keeper/observer_set_test.go b/x/observer/keeper/observer_set_test.go index cd4a4239a7..d17e496fae 100644 --- a/x/observer/keeper/observer_set_test.go +++ b/x/observer/keeper/observer_set_test.go @@ -12,6 +12,8 @@ func TestKeeper_GetObserverSet(t *testing.T) { t.Run("get observer set", func(t *testing.T) { k, ctx, _, _ := keepertest.ObserverKeeper(t) os := sample.ObserverSet(10) + _, found := k.GetObserverSet(ctx) + require.False(t, found) k.SetObserverSet(ctx, os) tfm, found := k.GetObserverSet(ctx) require.True(t, found) @@ -23,6 +25,7 @@ func TestKeeper_IsAddressPartOfObserverSet(t *testing.T) { t.Run("address is part of observer set", func(t *testing.T) { k, ctx, _, _ := keepertest.ObserverKeeper(t) os := sample.ObserverSet(10) + require.False(t, k.IsAddressPartOfObserverSet(ctx, os.ObserverList[0])) k.SetObserverSet(ctx, os) require.True(t, k.IsAddressPartOfObserverSet(ctx, os.ObserverList[0])) require.False(t, k.IsAddressPartOfObserverSet(ctx, sample.AccAddress())) @@ -42,12 +45,30 @@ func TestKeeper_AddObserverToSet(t *testing.T) { require.True(t, found) require.Len(t, osNew.ObserverList, len(os.ObserverList)+1) }) + + t.Run("add observer to set if set doesn't exist", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + newObserver := sample.AccAddress() + k.AddObserverToSet(ctx, newObserver) + require.True(t, k.IsAddressPartOfObserverSet(ctx, newObserver)) + osNew, found := k.GetObserverSet(ctx) + require.True(t, found) + require.Len(t, osNew.ObserverList, 1) + + // add same address again, len doesn't change + k.AddObserverToSet(ctx, newObserver) + require.True(t, k.IsAddressPartOfObserverSet(ctx, newObserver)) + osNew, found = k.GetObserverSet(ctx) + require.True(t, found) + require.Len(t, osNew.ObserverList, 1) + }) } func TestKeeper_RemoveObserverFromSet(t *testing.T) { t.Run("remove observer from set", func(t *testing.T) { k, ctx, _, _ := keepertest.ObserverKeeper(t) os := sample.ObserverSet(10) + k.RemoveObserverFromSet(ctx, os.ObserverList[0]) k.SetObserverSet(ctx, os) k.RemoveObserverFromSet(ctx, os.ObserverList[0]) require.False(t, k.IsAddressPartOfObserverSet(ctx, os.ObserverList[0])) @@ -64,13 +85,25 @@ func TestKeeper_UpdateObserverAddress(t *testing.T) { newObserverAddress := sample.AccAddress() observerSet := sample.ObserverSet(10) observerSet.ObserverList = append(observerSet.ObserverList, oldObserverAddress) - k.SetObserverSet(ctx, observerSet) err := k.UpdateObserverAddress(ctx, oldObserverAddress, newObserverAddress) + require.Error(t, err) + k.SetObserverSet(ctx, observerSet) + err = k.UpdateObserverAddress(ctx, oldObserverAddress, newObserverAddress) require.NoError(t, err) observerSet, found := k.GetObserverSet(ctx) require.True(t, found) require.Equal(t, newObserverAddress, observerSet.ObserverList[len(observerSet.ObserverList)-1]) }) + t.Run("should error if observer address not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + oldObserverAddress := sample.AccAddress() + newObserverAddress := sample.AccAddress() + observerSet := sample.ObserverSet(10) + observerSet.ObserverList = append(observerSet.ObserverList, oldObserverAddress) + k.SetObserverSet(ctx, observerSet) + err := k.UpdateObserverAddress(ctx, sample.AccAddress(), newObserverAddress) + require.Error(t, err) + }) t.Run("update observer address long observerList", func(t *testing.T) { k, ctx, _, _ := keepertest.ObserverKeeper(t) oldObserverAddress := sample.AccAddress() diff --git a/x/observer/keeper/params_test.go b/x/observer/keeper/params_test.go index c487ea0f73..309d66df9b 100644 --- a/x/observer/keeper/params_test.go +++ b/x/observer/keeper/params_test.go @@ -1,4 +1,4 @@ -package keeper +package keeper_test import ( "fmt" @@ -9,11 +9,12 @@ import ( "github.com/tendermint/tendermint/crypto" "github.com/stretchr/testify/require" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/x/observer/types" ) func TestGetParams(t *testing.T) { - k, ctx := SetupKeeper(t) + k, ctx, _, _ := keepertest.ObserverKeeper(t) params := types.DefaultParams() k.SetParams(ctx, params) @@ -22,7 +23,6 @@ func TestGetParams(t *testing.T) { } func TestGenerateAddress(t *testing.T) { - types.SetConfig(false) addr := sdk.AccAddress(crypto.AddressHash([]byte("Output1" + strconv.Itoa(1)))) addrString := addr.String() fmt.Println(addrString) diff --git a/x/observer/keeper/pending_nonces_test.go b/x/observer/keeper/pending_nonces_test.go index 6185e80bf1..37ff74fb22 100644 --- a/x/observer/keeper/pending_nonces_test.go +++ b/x/observer/keeper/pending_nonces_test.go @@ -62,5 +62,74 @@ func TestKeeper_PendingNoncesAll(t *testing.T) { return rst[i].ChainId < rst[j].ChainId }) require.Equal(t, nonces, rst) + + k.RemovePendingNonces(ctx, nonces[0]) + rst, err = k.GetAllPendingNonces(ctx) + require.NoError(t, err) + sort.SliceStable(rst, func(i, j int) bool { + return rst[i].ChainId < rst[j].ChainId + }) + require.Equal(t, nonces[1:], rst) + }) +} + +func TestKeeper_SetTssAndUpdateNonce(t *testing.T) { + t.Run("should set tss and update nonces", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + _, found := k.GetTSS(ctx) + require.False(t, found) + pendingNonces, err := k.GetAllPendingNonces(ctx) + require.NoError(t, err) + require.Empty(t, pendingNonces) + chainNonces := k.GetAllChainNonces(ctx) + require.NoError(t, err) + require.Empty(t, chainNonces) + + tss := sample.Tss() + // core params list but chain not in list + setSupportedChain(ctx, *k, getValidEthChainIDWithIndex(t, 0)) + k.SetTssAndUpdateNonce(ctx, tss) + + _, found = k.GetTSS(ctx) + require.True(t, found) + pendingNonces, err = k.GetAllPendingNonces(ctx) + require.NoError(t, err) + require.Equal(t, 1, len(pendingNonces)) + chainNonces = k.GetAllChainNonces(ctx) + require.NoError(t, err) + require.Equal(t, 1, len(chainNonces)) + }) +} + +func TestKeeper_RemoveFromPendingNonces(t *testing.T) { + t.Run("should remove from pending nonces", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + nonces := sample.PendingNoncesList(t, "sample", 10) + tss := sample.Tss() + // make nonces and pubkey deterministic for test + for i := range nonces { + nonces[i].NonceLow = int64(i) + nonces[i].NonceHigh = nonces[i].NonceLow + 3 + nonces[i].Tss = tss.TssPubkey + } + sort.SliceStable(nonces, func(i, j int) bool { + return nonces[i].ChainId < nonces[j].ChainId + }) + for _, nonce := range nonces { + k.SetPendingNonces(ctx, nonce) + } + + k.RemoveFromPendingNonces(ctx, tss.TssPubkey, 1, 1) + pendingNonces, err := k.GetAllPendingNonces(ctx) + require.NoError(t, err) + nonceUpdated := false + for _, pn := range pendingNonces { + if pn.ChainId == 1 { + require.Equal(t, int64(2), pn.NonceLow) + nonceUpdated = true + } + } + require.True(t, nonceUpdated) }) } diff --git a/x/observer/keeper/tss_funds_migrator_test.go b/x/observer/keeper/tss_funds_migrator_test.go index 1d32f19f92..620fa2ca06 100644 --- a/x/observer/keeper/tss_funds_migrator_test.go +++ b/x/observer/keeper/tss_funds_migrator_test.go @@ -12,10 +12,16 @@ func TestKeeper_GetTssFundMigrator(t *testing.T) { t.Run("Successfully set funds migrator for chain", func(t *testing.T) { k, ctx, _, _ := keepertest.ObserverKeeper(t) chain := sample.TssFundsMigrator(1) + _, found := k.GetFundMigrator(ctx, chain.ChainId) + require.False(t, found) k.SetFundMigrator(ctx, chain) tfm, found := k.GetFundMigrator(ctx, chain.ChainId) require.True(t, found) require.Equal(t, chain, tfm) + + k.RemoveAllExistingMigrators(ctx) + _, found = k.GetFundMigrator(ctx, chain.ChainId) + require.False(t, found) }) t.Run("Verify only one migrator can be created for a chain", func(t *testing.T) { k, ctx, _, _ := keepertest.ObserverKeeper(t) @@ -28,5 +34,4 @@ func TestKeeper_GetTssFundMigrator(t *testing.T) { require.Equal(t, 1, len(migratorList)) require.Equal(t, tfm2, migratorList[0]) }) - } diff --git a/x/observer/keeper/tss_test.go b/x/observer/keeper/tss_test.go index 7574e05726..a1f1ca6ddc 100644 --- a/x/observer/keeper/tss_test.go +++ b/x/observer/keeper/tss_test.go @@ -9,14 +9,10 @@ import ( "github.com/stretchr/testify/require" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/zeta-chain/zetacore/x/observer/types" ) -func TestTSSGet(t *testing.T) { +func TestKeeper_GetTSS(t *testing.T) { k, ctx, _, _ := keepertest.ObserverKeeper(t) tss := sample.Tss() k.SetTSS(ctx, tss) @@ -25,7 +21,7 @@ func TestTSSGet(t *testing.T) { require.Equal(t, tss, tssQueried) } -func TestTSSRemove(t *testing.T) { +func TestKeeper_RemoveTSS(t *testing.T) { k, ctx, _, _ := keepertest.ObserverKeeper(t) tss := sample.Tss() k.SetTSS(ctx, tss) @@ -34,83 +30,34 @@ func TestTSSRemove(t *testing.T) { require.False(t, found) } -func TestTSSQuerySingle(t *testing.T) { +func TestKeeper_CheckIfTssPubkeyHasBeenGenerated(t *testing.T) { k, ctx, _, _ := keepertest.ObserverKeeper(t) - wctx := sdk.WrapSDKContext(ctx) - //msgs := createTSS(keeper, ctx, 1) tss := sample.Tss() - k.SetTSS(ctx, tss) - for _, tc := range []struct { - desc string - request *types.QueryGetTSSRequest - response *types.QueryGetTSSResponse - err error - }{ - { - desc: "First", - request: &types.QueryGetTSSRequest{}, - response: &types.QueryGetTSSResponse{TSS: tss}, - }, - { - desc: "InvalidRequest", - err: status.Error(codes.InvalidArgument, "invalid request"), - }, - } { - tc := tc - t.Run(tc.desc, func(t *testing.T) { - response, err := k.TSS(wctx, tc.request) - if tc.err != nil { - require.ErrorIs(t, err, tc.err) - } else { - require.Equal(t, tc.response, response) - } - }) - } + + generated, found := k.CheckIfTssPubkeyHasBeenGenerated(ctx, tss.TssPubkey) + require.False(t, found) + require.Equal(t, types.TSS{}, generated) + + k.AppendTss(ctx, tss) + + generated, found = k.CheckIfTssPubkeyHasBeenGenerated(ctx, tss.TssPubkey) + require.True(t, found) + require.Equal(t, tss, generated) } -func TestTSSQueryHistory(t *testing.T) { - keeper, ctx, _, _ := keepertest.ObserverKeeper(t) - wctx := sdk.WrapSDKContext(ctx) - for _, tc := range []struct { - desc string - tssCount int - foundPrevious bool - err error - }{ - { - desc: "1 Tss addresses", - tssCount: 1, - foundPrevious: false, - err: nil, - }, - { - desc: "10 Tss addresses", - tssCount: 10, - foundPrevious: true, - err: nil, - }, - } { - tc := tc - 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) - } - request := &types.QueryTssHistoryRequest{} - response, err := keeper.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) - require.Equal(t, tc.foundPrevious, found) - if found { - require.Equal(t, tssList[len(tssList)-2], prevTss) - } - } - }) +func TestKeeper_GetHistoricalTssByFinalizedHeight(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + tssList := sample.TssList(100) + r := rand.Intn((len(tssList)-1)-0) + 0 + _, found := k.GetHistoricalTssByFinalizedHeight(ctx, tssList[r].FinalizedZetaHeight) + require.False(t, found) + + for _, tss := range tssList { + k.SetTSSHistory(ctx, tss) } + tss, found := k.GetHistoricalTssByFinalizedHeight(ctx, tssList[r].FinalizedZetaHeight) + require.True(t, found) + require.Equal(t, tssList[r], tss) } func TestKeeper_TssHistory(t *testing.T) { @@ -165,15 +112,4 @@ func TestKeeper_TssHistory(t *testing.T) { }) require.Equal(t, tssList, rst) }) - t.Run("Get historical TSS", func(t *testing.T) { - k, ctx, _, _ := keepertest.ObserverKeeper(t) - tssList := sample.TssList(100) - for _, tss := range tssList { - k.SetTSSHistory(ctx, tss) - } - r := rand.Intn((len(tssList)-1)-0) + 0 - tss, found := k.GetHistoricalTssByFinalizedHeight(ctx, tssList[r].FinalizedZetaHeight) - require.True(t, found) - require.Equal(t, tssList[r], tss) - }) } diff --git a/x/observer/keeper/utils.go b/x/observer/keeper/utils.go index 5209e3218d..566b559630 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 @@ -88,7 +88,7 @@ func (k Keeper) IsValidator(ctx sdk.Context, creator string) error { return types.ErrNotValidator } - if validator.Jailed == true || validator.IsBonded() == false { + if validator.Jailed || !validator.IsBonded() { return types.ErrValidatorStatus } return nil @@ -135,7 +135,6 @@ func (k Keeper) CheckObserverSelfDelegation(ctx sdk.Context, accAddress string) return err } tokens := validator.TokensFromShares(delegation.Shares) - if tokens.LT(minDelegation) { k.RemoveObserverFromSet(ctx, accAddress) } diff --git a/x/observer/keeper/utils_test.go b/x/observer/keeper/utils_test.go index 9d4b8c54e2..4aedd8ad13 100644 --- a/x/observer/keeper/utils_test.go +++ b/x/observer/keeper/utils_test.go @@ -7,6 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/pkg/chains" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" @@ -61,13 +62,14 @@ func TestKeeper_IsAuthorized(t *testing.T) { }) accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) + require.NoError(t, err) k.SetObserverSet(ctx, types.ObserverSet{ ObserverList: []string{accAddressOfValidator.String()}, }) require.True(t, k.IsNonTombstonedObserver(ctx, accAddressOfValidator.String())) - }) + t.Run("not authorized for tombstoned observer", func(t *testing.T) { k, ctx, _, _ := keepertest.ObserverKeeper(t) @@ -87,13 +89,15 @@ func TestKeeper_IsAuthorized(t *testing.T) { }) accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) + require.NoError(t, err) + k.SetObserverSet(ctx, types.ObserverSet{ ObserverList: []string{accAddressOfValidator.String()}, }) require.False(t, k.IsNonTombstonedObserver(ctx, accAddressOfValidator.String())) - }) + t.Run("not authorized for non-validator observer", func(t *testing.T) { k, ctx, _, _ := keepertest.ObserverKeeper(t) @@ -113,11 +117,205 @@ func TestKeeper_IsAuthorized(t *testing.T) { }) accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) + require.NoError(t, err) + k.SetObserverSet(ctx, types.ObserverSet{ ObserverList: []string{accAddressOfValidator.String()}, }) require.False(t, k.IsNonTombstonedObserver(ctx, accAddressOfValidator.String())) + }) +} + +func TestKeeper_CheckObserverSelfDelegation(t *testing.T) { + t.Run("should error if accAddress invalid", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + err := k.CheckObserverSelfDelegation(ctx, "invalid") + require.Error(t, err) + }) + + t.Run("should error if validator not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + accAddress := sample.AccAddress() + err := k.CheckObserverSelfDelegation(ctx, accAddress) + require.Error(t, err) + }) + + t.Run("should error if delegation not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + r := rand.New(rand.NewSource(9)) + validator := sample.Validator(t, r) + k.GetStakingKeeper().SetValidator(ctx, validator) + accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) + require.NoError(t, err) + + err = k.CheckObserverSelfDelegation(ctx, accAddressOfValidator.String()) + require.Error(t, err) + }) + + t.Run("should remove from observer list if tokens less than min del", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + r := rand.New(rand.NewSource(9)) + validator := sample.Validator(t, r) + validator.DelegatorShares = sdk.NewDec(100) + k.GetStakingKeeper().SetValidator(ctx, validator) + accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) + require.NoError(t, err) + + k.GetStakingKeeper().SetDelegation(ctx, stakingtypes.Delegation{ + DelegatorAddress: accAddressOfValidator.String(), + ValidatorAddress: validator.GetOperator().String(), + Shares: sdk.NewDec(10), + }) + + k.SetObserverSet(ctx, types.ObserverSet{ + ObserverList: []string{accAddressOfValidator.String()}, + }) + err = k.CheckObserverSelfDelegation(ctx, accAddressOfValidator.String()) + require.NoError(t, err) + + os, found := k.GetObserverSet(ctx) + require.True(t, found) + require.Empty(t, os.ObserverList) + }) + + t.Run("should not remove from observer list if tokens gte than min del", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + r := rand.New(rand.NewSource(9)) + validator := sample.Validator(t, r) + + validator.DelegatorShares = sdk.NewDec(1) + validator.Tokens = sdk.NewInt(1) + k.GetStakingKeeper().SetValidator(ctx, validator) + accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) + require.NoError(t, err) + + minDelegation, err := types.GetMinObserverDelegationDec() + require.NoError(t, err) + k.GetStakingKeeper().SetDelegation(ctx, stakingtypes.Delegation{ + DelegatorAddress: accAddressOfValidator.String(), + ValidatorAddress: validator.GetOperator().String(), + Shares: minDelegation, + }) + + k.SetObserverSet(ctx, types.ObserverSet{ + ObserverList: []string{accAddressOfValidator.String()}, + }) + err = k.CheckObserverSelfDelegation(ctx, accAddressOfValidator.String()) + require.NoError(t, err) + + os, found := k.GetObserverSet(ctx) + require.True(t, found) + require.Equal(t, 1, len(os.ObserverList)) + require.Equal(t, accAddressOfValidator.String(), os.ObserverList[0]) + }) +} + +func TestKeeper_IsOpeartorTombstoned(t *testing.T) { + t.Run("should err if invalid addr", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + res, err := k.IsOperatorTombstoned(ctx, "invalid") + require.Error(t, err) + require.False(t, res) + }) + + t.Run("should error if validator not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + accAddress := sample.AccAddress() + res, err := k.IsOperatorTombstoned(ctx, accAddress) + require.Error(t, err) + require.False(t, res) + }) + + t.Run("should not error if validator found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + r := rand.New(rand.NewSource(9)) + validator := sample.Validator(t, r) + k.GetStakingKeeper().SetValidator(ctx, validator) + accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) + require.NoError(t, err) + + res, err := k.IsOperatorTombstoned(ctx, accAddressOfValidator.String()) + require.NoError(t, err) + require.False(t, res) + }) +} + +func TestKeeper_IsValidator(t *testing.T) { + t.Run("should err if invalid addr", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + err := k.IsValidator(ctx, "invalid") + require.Error(t, err) + }) + + t.Run("should error if validator not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + accAddress := sample.AccAddress() + err := k.IsValidator(ctx, accAddress) + require.Error(t, err) + }) + + t.Run("should err if validator not bonded", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + r := rand.New(rand.NewSource(9)) + validator := sample.Validator(t, r) + k.GetStakingKeeper().SetValidator(ctx, validator) + accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) + require.NoError(t, err) + + err = k.IsValidator(ctx, accAddressOfValidator.String()) + require.Error(t, err) + }) + + t.Run("should err if validator jailed", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + r := rand.New(rand.NewSource(9)) + validator := sample.Validator(t, r) + validator.Status = stakingtypes.Bonded + validator.Jailed = true + k.GetStakingKeeper().SetValidator(ctx, validator) + accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) + require.NoError(t, err) + + err = k.IsValidator(ctx, accAddressOfValidator.String()) + require.Error(t, err) + }) + + t.Run("should not err if validator not jailed and bonded", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + + r := rand.New(rand.NewSource(9)) + validator := sample.Validator(t, r) + validator.Status = stakingtypes.Bonded + validator.Jailed = false + k.GetStakingKeeper().SetValidator(ctx, validator) + accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) + require.NoError(t, err) + + err = k.IsValidator(ctx, accAddressOfValidator.String()) + require.NoError(t, err) + }) +} + +func TestKeeper_FindBallot(t *testing.T) { + t.Run("should err if chain params not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeper(t) + _, _, err := k.FindBallot(ctx, "index", &chains.Chain{ + ChainId: 987, + }, types.ObservationType_InBoundTx) + require.Error(t, err) }) } diff --git a/x/observer/keeper/vote_inbound_test.go b/x/observer/keeper/vote_inbound_test.go index 41a3999b75..5b1c09e81b 100644 --- a/x/observer/keeper/vote_inbound_test.go +++ b/x/observer/keeper/vote_inbound_test.go @@ -258,6 +258,57 @@ func TestKeeper_VoteOnInboundBallot(t *testing.T) { require.True(t, isNew) }) + t.Run("fail if can not add vote", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) + + observer := sample.AccAddress() + stakingMock := keepertest.GetObserverStakingMock(t, k) + slashingMock := keepertest.GetObserverSlashingMock(t, k) + + k.SetCrosschainFlags(ctx, types.CrosschainFlags{ + IsInboundEnabled: true, + }) + k.SetChainParamsList(ctx, types.ChainParamsList{ + ChainParams: []*types.ChainParams{ + { + ChainId: getValidEthChainIDWithIndex(t, 0), + IsSupported: true, + }, + { + ChainId: getValidEthChainIDWithIndex(t, 1), + IsSupported: true, + }, + }, + }) + k.SetObserverSet(ctx, types.ObserverSet{ + ObserverList: []string{observer}, + }) + stakingMock.MockGetValidator(sample.Validator(t, sample.Rand())) + slashingMock.MockIsTombstoned(false) + ballot := types.Ballot{ + Index: "index", + BallotIdentifier: "index", + VoterList: []string{observer}, + // already voted + Votes: []types.VoteType{types.VoteType_SuccessObservation}, + BallotStatus: types.BallotStatus_BallotInProgress, + BallotThreshold: sdk.NewDec(2), + } + k.SetBallot(ctx, &ballot) + isFinalized, isNew, err := k.VoteOnInboundBallot( + ctx, + getValidEthChainIDWithIndex(t, 0), + getValidEthChainIDWithIndex(t, 1), + coin.CoinType_ERC20, + observer, + "index", + "inTxHash", + ) + require.Error(t, err) + require.False(t, isFinalized) + require.False(t, isNew) + }) + t.Run("can add vote and create ballot without finalizing ballot", func(t *testing.T) { k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) diff --git a/x/observer/keeper/vote_outbound_test.go b/x/observer/keeper/vote_outbound_test.go index fa34a11ca6..c92fec5927 100644 --- a/x/observer/keeper/vote_outbound_test.go +++ b/x/observer/keeper/vote_outbound_test.go @@ -133,6 +133,48 @@ func TestKeeper_VoteOnOutboundBallot(t *testing.T) { require.Equal(t, expectedBallot, ballot) }) + t.Run("fail if can not add vote", func(t *testing.T) { + k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) + + observer := sample.AccAddress() + stakingMock := keepertest.GetObserverStakingMock(t, k) + slashingMock := keepertest.GetObserverSlashingMock(t, k) + + k.SetChainParamsList(ctx, types.ChainParamsList{ + ChainParams: []*types.ChainParams{ + { + ChainId: getValidEthChainIDWithIndex(t, 0), + IsSupported: true, + }, + }, + }) + k.SetObserverSet(ctx, types.ObserverSet{ + ObserverList: []string{observer}, + }) + stakingMock.MockGetValidator(sample.Validator(t, sample.Rand())) + slashingMock.MockIsTombstoned(false) + ballot := types.Ballot{ + Index: "index", + BallotIdentifier: "index", + VoterList: []string{observer}, + // already voted + Votes: []types.VoteType{types.VoteType_SuccessObservation}, + BallotStatus: types.BallotStatus_BallotInProgress, + BallotThreshold: sdk.NewDec(2), + } + k.SetBallot(ctx, &ballot) + isFinalized, isNew, ballot, _, err := k.VoteOnOutboundBallot( + ctx, + "index", + getValidEthChainIDWithIndex(t, 0), + chains.ReceiveStatus_Success, + observer, + ) + require.Error(t, err) + require.False(t, isFinalized) + require.False(t, isNew) + }) + t.Run("can add vote and create ballot without finalizing ballot", func(t *testing.T) { k, ctx, _, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) diff --git a/x/observer/types/ballot.go b/x/observer/types/ballot.go index 10f21119d1..71512c9f11 100644 --- a/x/observer/types/ballot.go +++ b/x/observer/types/ballot.go @@ -3,23 +3,29 @@ 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 index := m.GetVoterIndex(address) + if index == -1 { + return m, cosmoserrors.Wrap(ErrUnableToAddVote, fmt.Sprintf("Voter %s not in voter list", address)) + } m.Votes[index] = vote return m, nil } func (m Ballot) HasVoted(address string) bool { index := m.GetVoterIndex(address) + if index == -1 { + return false + } return m.Votes[index] != VoteType_NotYetVoted } diff --git a/x/observer/types/ballot.pb.go b/x/observer/types/ballot.pb.go index 7e8c40afeb..8e9313d1dd 100644 --- a/x/observer/types/ballot.pb.go +++ b/x/observer/types/ballot.pb.go @@ -81,6 +81,7 @@ func (BallotStatus) EnumDescriptor() ([]byte, []int) { return fileDescriptor_9eac86b249c97b5b, []int{1} } +// https://github.com/zeta-chain/node/issues/939 type Ballot struct { Index string `protobuf:"bytes,1,opt,name=index,proto3" json:"index,omitempty"` BallotIdentifier string `protobuf:"bytes,2,opt,name=ballot_identifier,json=ballotIdentifier,proto3" json:"ballot_identifier,omitempty"` diff --git a/x/observer/types/ballot_test.go b/x/observer/types/ballot_test.go index 2bd6441c48..322affd728 100644 --- a/x/observer/types/ballot_test.go +++ b/x/observer/types/ballot_test.go @@ -21,6 +21,7 @@ func TestBallot_AddVote(t *testing.T) { finalVotes []VoteType finalStatus BallotStatus isFinalized bool + wantErr bool }{ { name: "All success", @@ -188,6 +189,18 @@ func TestBallot_AddVote(t *testing.T) { finalStatus: BallotStatus_BallotInProgress, isFinalized: false, }, + { + name: "Voter not in voter list", + threshold: sdk.MustNewDecFromStr("0.66"), + voterList: []string{}, + votes: []votes{ + {"Observer5", VoteType_SuccessObservation}, + }, + wantErr: true, + finalVotes: []VoteType{}, + finalStatus: BallotStatus_BallotInProgress, + isFinalized: false, + }, } for _, test := range tt { test := test @@ -202,7 +215,11 @@ func TestBallot_AddVote(t *testing.T) { BallotStatus: BallotStatus_BallotInProgress, } for _, vote := range test.votes { - ballot, _ = ballot.AddVote(vote.address, vote.vote) + b, err := ballot.AddVote(vote.address, vote.vote) + if test.wantErr { + require.Error(t, err) + } + ballot = b } finalBallot, isFinalized := ballot.IsFinalizingVote() diff --git a/x/observer/types/chain_params_test.go b/x/observer/types/chain_params_test.go index 0bdc9d814d..2100fe2071 100644 --- a/x/observer/types/chain_params_test.go +++ b/x/observer/types/chain_params_test.go @@ -3,6 +3,7 @@ package types_test import ( "testing" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/zeta-chain/zetacore/x/observer/types" @@ -196,4 +197,23 @@ func (s *UpdateChainParamsSuite) Validate(params *types.ChainParams) { copy.OutboundTxScheduleLookahead = 501 err = types.ValidateChainParams(©) require.NotNil(s.T(), err) + + copy = *params + copy.BallotThreshold = sdk.Dec{} + err = types.ValidateChainParams(©) + require.NotNil(s.T(), err) + copy.BallotThreshold = sdk.MustNewDecFromStr("1.2") + err = types.ValidateChainParams(©) + require.NotNil(s.T(), err) + copy.BallotThreshold = sdk.MustNewDecFromStr("0.9") + err = types.ValidateChainParams(©) + require.Nil(s.T(), err) + + copy = *params + copy.MinObserverDelegation = sdk.Dec{} + err = types.ValidateChainParams(©) + require.NotNil(s.T(), err) + copy.MinObserverDelegation = sdk.MustNewDecFromStr("0.9") + err = types.ValidateChainParams(©) + require.Nil(s.T(), err) } 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/crosschain_flags_test.go b/x/observer/types/crosschain_flags_test.go new file mode 100644 index 0000000000..9939f8b909 --- /dev/null +++ b/x/observer/types/crosschain_flags_test.go @@ -0,0 +1,19 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/x/observer/types" +) + +func TestDefaultDefaultCrosschainFlags(t *testing.T) { + defaultCrosschainFlags := types.DefaultCrosschainFlags() + + require.Equal(t, &types.CrosschainFlags{ + IsInboundEnabled: true, + IsOutboundEnabled: true, + GasPriceIncreaseFlags: &types.DefaultGasPriceIncreaseFlags, + BlockHeaderVerificationFlags: &types.DefaultBlockHeaderVerificationFlags, + }, defaultCrosschainFlags) +} diff --git a/x/observer/types/expected_keepers.go b/x/observer/types/expected_keepers.go index ba1c9ab9c2..5f402c2689 100644 --- a/x/observer/types/expected_keepers.go +++ b/x/observer/types/expected_keepers.go @@ -11,6 +11,7 @@ type StakingKeeper interface { GetValidator(ctx sdk.Context, addr sdk.ValAddress) (validator stakingtypes.Validator, found bool) GetDelegation(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (delegation stakingtypes.Delegation, found bool) SetValidator(ctx sdk.Context, validator stakingtypes.Validator) + SetDelegation(ctx sdk.Context, delegation stakingtypes.Delegation) } type SlashingKeeper interface { diff --git a/x/observer/types/genesis_test.go b/x/observer/types/genesis_test.go index 1ba8212a35..676bca7d62 100644 --- a/x/observer/types/genesis_test.go +++ b/x/observer/types/genesis_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/testutil/sample" "github.com/zeta-chain/zetacore/x/observer/types" ) @@ -12,6 +13,14 @@ func TestGenesisState_Validate(t *testing.T) { chainParams := types.GetDefaultChainParams().ChainParams invalidChainParamsGen.ChainParamsList.ChainParams = append(chainParams, chainParams[0]) + gsWithDuplicateNodeAccountList := types.DefaultGenesis() + nodeAccount := sample.NodeAccount() + gsWithDuplicateNodeAccountList.NodeAccountList = []*types.NodeAccount{nodeAccount, nodeAccount} + + gsWithDuplicateChainNonces := types.DefaultGenesis() + chainNonce := sample.ChainNonces(t, "0") + gsWithDuplicateChainNonces.ChainNonces = []types.ChainNonces{chainNonce, chainNonce} + for _, tc := range []struct { desc string genState *types.GenesisState @@ -32,6 +41,16 @@ func TestGenesisState_Validate(t *testing.T) { genState: invalidChainParamsGen, valid: false, }, + { + desc: "invalid genesis state duplicate node account list", + genState: gsWithDuplicateNodeAccountList, + valid: false, + }, + { + desc: "invalid genesis state duplicate chain nonces", + genState: gsWithDuplicateChainNonces, + valid: false, + }, } { t.Run(tc.desc, func(t *testing.T) { err := tc.genState.Validate() 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/observer_set_test.go b/x/observer/types/observer_set_test.go new file mode 100644 index 0000000000..5efce2cd81 --- /dev/null +++ b/x/observer/types/observer_set_test.go @@ -0,0 +1,32 @@ +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/observer/types" +) + +func TestObserverSet(t *testing.T) { + observerSet := sample.ObserverSet(4) + + require.Equal(t, int(4), observerSet.Len()) + require.Equal(t, uint64(4), observerSet.LenUint()) + err := observerSet.Validate() + require.NoError(t, err) + + observerSet.ObserverList[0] = "invalid" + err = observerSet.Validate() + require.Error(t, err) +} + +func TestCheckReceiveStatus(t *testing.T) { + err := types.CheckReceiveStatus(chains.ReceiveStatus_Success) + require.NoError(t, err) + err = types.CheckReceiveStatus(chains.ReceiveStatus_Failed) + require.NoError(t, err) + err = types.CheckReceiveStatus(chains.ReceiveStatus_Created) + require.Error(t, err) +} diff --git a/x/observer/types/params.go b/x/observer/types/params.go index 7fc72c1e89..ea1db66657 100644 --- a/x/observer/types/params.go +++ b/x/observer/types/params.go @@ -100,6 +100,7 @@ func validateAdminPolicy(i interface{}) error { return nil } +// https://github.com/zeta-chain/node/issues/1983 func validateBallotMaturityBlocks(i interface{}) error { _, ok := i.(int64) if !ok { @@ -108,12 +109,3 @@ func validateBallotMaturityBlocks(i interface{}) error { return nil } - -func (p Params) GetAdminPolicyAccount(policyType Policy_Type) string { - for _, admin := range p.AdminPolicy { - if admin.PolicyType == policyType { - return admin.Address - } - } - return "" -} diff --git a/x/observer/types/params_test.go b/x/observer/types/params_test.go new file mode 100644 index 0000000000..3b7b177565 --- /dev/null +++ b/x/observer/types/params_test.go @@ -0,0 +1,74 @@ +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 TestParamKeyTable(t *testing.T) { + kt := ParamKeyTable() + + ps := Params{} + for _, psp := range ps.ParamSetPairs() { + require.PanicsWithValue(t, "duplicate parameter key", func() { + kt.RegisterType(psp) + }) + } +} + +func TestParamSetPairs(t *testing.T) { + params := DefaultParams() + pairs := params.ParamSetPairs() + + require.Equal(t, 3, len(pairs), "The number of param set pairs should match the expected count") + + assertParamSetPair(t, pairs, KeyPrefix(ObserverParamsKey), ¶ms.ObserverParams, validateVotingThresholds) + assertParamSetPair(t, pairs, KeyPrefix(AdminPolicyParamsKey), ¶ms.AdminPolicy, validateAdminPolicy) + assertParamSetPair(t, pairs, KeyPrefix(BallotMaturityBlocksParamsKey), ¶ms.BallotMaturityBlocks, validateBallotMaturityBlocks) +} + +func assertParamSetPair(t *testing.T, pairs paramtypes.ParamSetPairs, key []byte, expectedValue interface{}, valFunc paramtypes.ValueValidatorFn) { + for _, pair := range pairs { + if string(pair.Key) == string(key) { + require.Equal(t, expectedValue, pair.Value, "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 TestParamsString(t *testing.T) { + params := DefaultParams() + out, err := yaml.Marshal(params) + require.NoError(t, err) + require.Equal(t, string(out), params.String()) +} + +func TestValidateVotingThresholds(t *testing.T) { + require.Error(t, validateVotingThresholds("invalid")) + + params := DefaultParams() + require.NoError(t, validateVotingThresholds(params.ObserverParams)) + + params.ObserverParams[0].BallotThreshold = sdk.MustNewDecFromStr("1.1") + require.Error(t, validateVotingThresholds(params.ObserverParams)) +} + +func TestValidateAdminPolicy(t *testing.T) { + require.Error(t, validateAdminPolicy("invalid")) + require.NoError(t, validateAdminPolicy([]*Admin_Policy{})) +} + +func TestValidateBallotMaturityBlocks(t *testing.T) { + require.Error(t, validateBallotMaturityBlocks("invalid")) + require.NoError(t, validateBallotMaturityBlocks(int64(1))) +} diff --git a/x/observer/types/parsers_test.go b/x/observer/types/parsers_test.go new file mode 100644 index 0000000000..1c108fcee8 --- /dev/null +++ b/x/observer/types/parsers_test.go @@ -0,0 +1,98 @@ +package types_test + +import ( + "math/rand" + "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/observer/types" +) + +func TestConvertReceiveStatusToVoteType(t *testing.T) { + tests := []struct { + name string + status chains.ReceiveStatus + expected types.VoteType + }{ + {"TestSuccessStatus", chains.ReceiveStatus_Success, types.VoteType_SuccessObservation}, + {"TestFailedStatus", chains.ReceiveStatus_Failed, types.VoteType_FailureObservation}, + {"TestDefaultStatus", chains.ReceiveStatus_Created, types.VoteType_NotYetVoted}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := types.ConvertReceiveStatusToVoteType(tt.status) + require.Equal(t, tt.expected, result) + }) + } +} + +func TestParseStringToObservationType(t *testing.T) { + tests := []struct { + name string + observationType string + expected types.ObservationType + }{ + {"TestValidObservationType1", "EmptyObserverType", types.ObservationType(0)}, + {"TestValidObservationType1", "InBoundTx", types.ObservationType(1)}, + {"TestValidObservationType1", "OutBoundTx", types.ObservationType(2)}, + {"TestValidObservationType1", "TSSKeyGen", types.ObservationType(3)}, + {"TestValidObservationType1", "TSSKeySign", types.ObservationType(4)}, + {"TestInvalidObservationType", "InvalidType", types.ObservationType(0)}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := types.ParseStringToObservationType(tt.observationType) + require.Equal(t, tt.expected, result) + }) + } +} + +func TestGetOperatorAddressFromAccAddress(t *testing.T) { + tests := []struct { + name string + accAddr string + wantErr bool + }{ + {"TestValidAccAddress", sample.AccAddress(), false}, + {"TestInvalidAccAddress", "invalid", true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := types.GetOperatorAddressFromAccAddress(tt.accAddr) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestGetAccAddressFromOperatorAddress(t *testing.T) { + // #nosec G404 test purpose - weak randomness is not an issue here + r := rand.New(rand.NewSource(1)) + tests := []struct { + name string + valAddress string + wantErr bool + }{ + {"TestValidValAddress", sample.ValAddress(r).String(), false}, + {"TestInvalidValAddress", "invalid", true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := types.GetAccAddressFromOperatorAddress(tt.valAddress) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/x/observer/types/test_data.go b/x/observer/types/test_data.go deleted file mode 100644 index 3a43ca4306..0000000000 --- a/x/observer/types/test_data.go +++ /dev/null @@ -1,27 +0,0 @@ -package types - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -const ( - AccountAddressPrefix = "zeta" -) - -var ( - AccountPubKeyPrefix = AccountAddressPrefix + "pub" - ValidatorAddressPrefix = AccountAddressPrefix + "valoper" - ValidatorPubKeyPrefix = AccountAddressPrefix + "valoperpub" - ConsNodeAddressPrefix = AccountAddressPrefix + "valcons" - ConsNodePubKeyPrefix = AccountAddressPrefix + "valconspub" -) - -func SetConfig(seal bool) { - config := sdk.GetConfig() - config.SetBech32PrefixForAccount(AccountAddressPrefix, AccountPubKeyPrefix) - config.SetBech32PrefixForValidator(ValidatorAddressPrefix, ValidatorPubKeyPrefix) - config.SetBech32PrefixForConsensusNode(ConsNodeAddressPrefix, ConsNodePubKeyPrefix) - if seal { - config.Seal() - } -} 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/x/observer/types/utils_test.go b/x/observer/types/utils_test.go new file mode 100644 index 0000000000..ac42ea127c --- /dev/null +++ b/x/observer/types/utils_test.go @@ -0,0 +1,55 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/go-tss/blame" + "github.com/zeta-chain/zetacore/x/observer/types" +) + +func TestConvertNodes(t *testing.T) { + tests := []struct { + name string + input []blame.Node + expected []*types.Node + }{ + { + name: "TestEmptyInput", + input: []blame.Node{}, + expected: nil, + }, + { + name: "TestNilInput", + input: nil, + expected: nil, + }, + { + name: "TestSingleInput", + input: []blame.Node{ + {Pubkey: "pubkey1", BlameSignature: []byte("signature1"), BlameData: []byte("data1")}, + }, + expected: []*types.Node{ + {PubKey: "pubkey1", BlameSignature: []byte("signature1"), BlameData: []byte("data1")}, + }, + }, + { + name: "TestMultipleInputs", + input: []blame.Node{ + {Pubkey: "pubkey1", BlameSignature: []byte("signature1"), BlameData: []byte("data1")}, + {Pubkey: "pubkey2", BlameSignature: []byte("signature2"), BlameData: []byte("data2")}, + }, + expected: []*types.Node{ + {PubKey: "pubkey1", BlameSignature: []byte("signature1"), BlameData: []byte("data1")}, + {PubKey: "pubkey2", BlameSignature: []byte("signature2"), BlameData: []byte("data2")}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := types.ConvertNodes(tt.input) + require.Equal(t, tt.expected, result) + }) + } +} diff --git a/zetaclient/bitcoin/bitcoin_client.go b/zetaclient/bitcoin/bitcoin_client.go index c06abd0876..859f5e7c49 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,28 +24,43 @@ 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" ) const ( - // The starting height (Bitcoin mainnet) from which dynamic depositor fee will take effect - DynamicDepositorFeeHeight = 834500 + DynamicDepositorFeeHeight = 834500 // The starting height (Bitcoin mainnet) from which dynamic depositor fee will take effect + maxHeightDiff = 10000 // in case the last block is too old when the observer starts + btcBlocksPerDay = 144 // for LRU block cache size + bigValueSats = 200000000 // 2 BTC + bigValueConfirmationCount = 6 // 6 confirmations for value >= 2 BTC ) var _ interfaces.ChainClient = &BTCChainClient{} type BTCLog struct { - ChainLogger zerolog.Logger - WatchInTx zerolog.Logger - ObserveOutTx zerolog.Logger - WatchUTXOS zerolog.Logger - WatchGasPrice zerolog.Logger - Compliance zerolog.Logger + Chain zerolog.Logger // The parent logger for the chain + InTx zerolog.Logger // The logger for incoming transactions + OutTx zerolog.Logger // The logger for outgoing transactions + UTXOS zerolog.Logger // The logger for UTXOs management + GasPrice zerolog.Logger // The logger for gas price + Compliance zerolog.Logger // The logger for compliance checks } // BTCChainClient represents a chain configuration for Bitcoin @@ -91,27 +92,21 @@ type BTCChainClient struct { BlockCache *lru.Cache } -const ( - maxHeightDiff = 10000 // in case the last block is too old when the observer starts - btcBlocksPerDay = 144 // for LRU block cache size - bigValueSats = 200000000 // 2 BTC - bigValueConfirmationCount = 6 // 6 confirmations for value >= 2 BTC -) - func (ob *BTCChainClient) WithZetaClient(bridge *zetabridge.ZetaCoreBridge) { ob.Mu.Lock() defer ob.Mu.Unlock() ob.zetaClient = bridge } + func (ob *BTCChainClient) WithLogger(logger zerolog.Logger) { ob.Mu.Lock() defer ob.Mu.Unlock() ob.logger = BTCLog{ - ChainLogger: logger, - WatchInTx: logger.With().Str("module", "WatchInTx").Logger(), - ObserveOutTx: logger.With().Str("module", "observeOutTx").Logger(), - WatchUTXOS: logger.With().Str("module", "WatchUTXOS").Logger(), - WatchGasPrice: logger.With().Str("module", "WatchGasPrice").Logger(), + Chain: logger, + InTx: logger.With().Str("module", "WatchInTx").Logger(), + OutTx: logger.With().Str("module", "WatchOutTx").Logger(), + UTXOS: logger.With().Str("module", "WatchUTXOS").Logger(), + GasPrice: logger.With().Str("module", "WatchGasPrice").Logger(), } } @@ -163,12 +158,12 @@ func NewBitcoinClient( ob.Mu = &sync.Mutex{} chainLogger := loggers.Std.With().Str("chain", chain.ChainName.String()).Logger() ob.logger = BTCLog{ - ChainLogger: chainLogger, - WatchInTx: chainLogger.With().Str("module", "WatchInTx").Logger(), - ObserveOutTx: chainLogger.With().Str("module", "observeOutTx").Logger(), - WatchUTXOS: chainLogger.With().Str("module", "WatchUTXOS").Logger(), - WatchGasPrice: chainLogger.With().Str("module", "WatchGasPrice").Logger(), - Compliance: loggers.Compliance, + Chain: chainLogger, + InTx: chainLogger.With().Str("module", "WatchInTx").Logger(), + OutTx: chainLogger.With().Str("module", "WatchOutTx").Logger(), + UTXOS: chainLogger.With().Str("module", "WatchUTXOS").Logger(), + GasPrice: chainLogger.With().Str("module", "WatchGasPrice").Logger(), + Compliance: loggers.Compliance, } ob.zetaClient = bridge @@ -183,7 +178,7 @@ func NewBitcoinClient( } ob.params = *chainParams // initialize the Client - ob.logger.ChainLogger.Info().Msgf("Chain %s endpoint %s", ob.chain.String(), btcCfg.RPCHost) + ob.logger.Chain.Info().Msgf("Chain %s endpoint %s", ob.chain.String(), btcCfg.RPCHost) connCfg := &rpcclient.ConnConfig{ Host: btcCfg.RPCHost, User: btcCfg.RPCUsername, @@ -204,7 +199,7 @@ func NewBitcoinClient( ob.BlockCache, err = lru.New(btcBlocksPerDay) if err != nil { - ob.logger.ChainLogger.Error().Err(err).Msg("failed to create bitcoin block cache") + ob.logger.Chain.Error().Err(err).Msg("failed to create bitcoin block cache") return nil, err } @@ -218,55 +213,58 @@ func NewBitcoinClient( } func (ob *BTCChainClient) Start() { - ob.logger.ChainLogger.Info().Msgf("BitcoinChainClient is starting") - go ob.WatchInTx() - go ob.observeOutTx() - go ob.WatchUTXOS() - go ob.WatchGasPrice() - go ob.ExternalChainWatcherForNewInboundTrackerSuggestions() - go ob.RPCStatus() + ob.logger.Chain.Info().Msgf("BitcoinChainClient is starting") + go ob.WatchInTx() // watch bitcoin chain for incoming txs and post votes to zetacore + go ob.WatchOutTx() // watch bitcoin chain for outgoing txs status + go ob.WatchUTXOS() // watch bitcoin chain for UTXOs owned by the TSS address + go ob.WatchGasPrice() // watch bitcoin chain for gas rate and post to zetacore + go ob.WatchIntxTracker() // watch zetacore for bitcoin intx trackers + go ob.WatchRPCStatus() // watch the RPC status of the bitcoin chain } -func (ob *BTCChainClient) RPCStatus() { - ob.logger.ChainLogger.Info().Msgf("RPCStatus is starting") +// WatchRPCStatus watches the RPC status of the Bitcoin chain +func (ob *BTCChainClient) WatchRPCStatus() { + ob.logger.Chain.Info().Msgf("RPCStatus is starting") ticker := time.NewTicker(60 * time.Second) for { select { case <-ticker.C: - //ob.logger.ChainLogger.Info().Msgf("RPCStatus is running") + if !ob.GetChainParams().IsSupported { + continue + } bn, err := ob.rpcClient.GetBlockCount() if err != nil { - ob.logger.ChainLogger.Error().Err(err).Msg("RPC status check: RPC down? ") + ob.logger.Chain.Error().Err(err).Msg("RPC status check: RPC down? ") continue } hash, err := ob.rpcClient.GetBlockHash(bn) if err != nil { - ob.logger.ChainLogger.Error().Err(err).Msg("RPC status check: RPC down? ") + ob.logger.Chain.Error().Err(err).Msg("RPC status check: RPC down? ") continue } header, err := ob.rpcClient.GetBlockHeader(hash) if err != nil { - ob.logger.ChainLogger.Error().Err(err).Msg("RPC status check: RPC down? ") + ob.logger.Chain.Error().Err(err).Msg("RPC status check: RPC down? ") continue } blockTime := header.Timestamp elapsedSeconds := time.Since(blockTime).Seconds() if elapsedSeconds > 1200 { - ob.logger.ChainLogger.Error().Err(err).Msg("RPC status check: RPC down? ") + ob.logger.Chain.Error().Err(err).Msg("RPC status check: RPC down? ") continue } tssAddr := ob.Tss.BTCAddressWitnessPubkeyHash() res, err := ob.rpcClient.ListUnspentMinMaxAddresses(0, 1000000, []btcutil.Address{tssAddr}) if err != nil { - ob.logger.ChainLogger.Error().Err(err).Msg("RPC status check: can't list utxos of TSS address; wallet or loaded? TSS address is not imported? ") + ob.logger.Chain.Error().Err(err).Msg("RPC status check: can't list utxos of TSS address; wallet or loaded? TSS address is not imported? ") continue } if len(res) == 0 { - ob.logger.ChainLogger.Error().Err(err).Msg("RPC status check: TSS address has no utxos; TSS address is not imported? ") + ob.logger.Chain.Error().Err(err).Msg("RPC status check: TSS address has no utxos; TSS address is not imported? ") continue } - ob.logger.ChainLogger.Info().Msgf("[OK] RPC status check: latest block number %d, timestamp %s (%.fs ago), tss addr %s, #utxos: %d", bn, blockTime, elapsedSeconds, tssAddr, len(res)) + ob.logger.Chain.Info().Msgf("[OK] RPC status check: latest block number %d, timestamp %s (%.fs ago), tss addr %s, #utxos: %d", bn, blockTime, elapsedSeconds, tssAddr, len(res)) case <-ob.stop: return @@ -275,9 +273,9 @@ func (ob *BTCChainClient) RPCStatus() { } func (ob *BTCChainClient) Stop() { - ob.logger.ChainLogger.Info().Msgf("ob %s is stopping", ob.chain.String()) + ob.logger.Chain.Info().Msgf("ob %s is stopping", ob.chain.String()) close(ob.stop) // this notifies all goroutines to stop - ob.logger.ChainLogger.Info().Msgf("%s observer stopped", ob.chain.String()) + ob.logger.Chain.Info().Msgf("%s observer stopped", ob.chain.String()) } func (ob *BTCChainClient) SetLastBlockHeight(height int64) { @@ -324,34 +322,39 @@ func (ob *BTCChainClient) GetBaseGasPrice() *big.Int { return big.NewInt(0) } +// WatchInTx watches Bitcoin chain for incoming txs and post votes to zetacore func (ob *BTCChainClient) WatchInTx() { ticker, err := clienttypes.NewDynamicTicker("Bitcoin_WatchInTx", ob.GetChainParams().InTxTicker) if err != nil { - ob.logger.WatchInTx.Error().Err(err).Msg("WatchInTx error") + ob.logger.InTx.Error().Err(err).Msg("error creating ticker") return } defer ticker.Stop() + ob.logger.InTx.Info().Msgf("WatchInTx started for chain %d", ob.chain.ChainId) for { select { case <-ticker.C(): if flags := ob.coreContext.GetCrossChainFlags(); !flags.IsInboundEnabled { continue } + if !ob.GetChainParams().IsSupported { + continue + } err := ob.ObserveInTx() if err != nil { - ob.logger.WatchInTx.Error().Err(err).Msg("WatchInTx error observing in tx") + ob.logger.InTx.Error().Err(err).Msg("WatchInTx error observing in tx") } - ticker.UpdateInterval(ob.GetChainParams().InTxTicker, ob.logger.WatchInTx) + ticker.UpdateInterval(ob.GetChainParams().InTxTicker, ob.logger.InTx) case <-ob.stop: - ob.logger.WatchInTx.Info().Msg("WatchInTx stopped") + ob.logger.InTx.Info().Msgf("WatchInTx stopped for chain %d", ob.chain.ChainId) return } } } func (ob *BTCChainClient) postBlockHeader(tip int64) error { - ob.logger.WatchInTx.Info().Msgf("postBlockHeader: tip %d", tip) + ob.logger.InTx.Info().Msgf("postBlockHeader: tip %d", tip) bn := tip res, err := ob.zetaClient.GetBlockHeaderStateByChain(ob.chain.ChainId) if err == nil && res.BlockHeaderState != nil && res.BlockHeaderState.EarliestHeight > 0 { @@ -368,7 +371,7 @@ func (ob *BTCChainClient) postBlockHeader(tip int64) error { var headerBuf bytes.Buffer err = res2.Header.Serialize(&headerBuf) if err != nil { // should never happen - ob.logger.WatchInTx.Error().Err(err).Msgf("error serializing bitcoin block header: %d", bn) + ob.logger.InTx.Error().Err(err).Msgf("error serializing bitcoin block header: %d", bn) return err } blockHash := res2.Header.BlockHash() @@ -378,9 +381,9 @@ func (ob *BTCChainClient) postBlockHeader(tip int64) error { res2.Block.Height, proofs.NewBitcoinHeader(headerBuf.Bytes()), ) - ob.logger.WatchInTx.Info().Msgf("posted block header %d: %s", bn, blockHash) + ob.logger.InTx.Info().Msgf("posted block header %d: %s", bn, blockHash) if err != nil { // error shouldn't block the process - ob.logger.WatchInTx.Error().Err(err).Msgf("error posting bitcoin block header: %d", bn) + ob.logger.InTx.Error().Err(err).Msgf("error posting bitcoin block header: %d", bn) } return err } @@ -417,18 +420,18 @@ func (ob *BTCChainClient) ObserveInTx() error { bn := lastScanned + 1 res, err := ob.GetBlockByNumberCached(bn) if err != nil { - ob.logger.WatchInTx.Error().Err(err).Msgf("observeInTxBTC: error getting bitcoin block %d", bn) + ob.logger.InTx.Error().Err(err).Msgf("observeInTxBTC: error getting bitcoin block %d", bn) return err } - ob.logger.WatchInTx.Info().Msgf("observeInTxBTC: block %d has %d txs, current block %d, last block %d", + ob.logger.InTx.Info().Msgf("observeInTxBTC: block %d has %d txs, current block %d, last block %d", bn, len(res.Block.Tx), cnt, lastScanned) // print some debug information if len(res.Block.Tx) > 1 { for idx, tx := range res.Block.Tx { - ob.logger.WatchInTx.Debug().Msgf("BTC InTX | %d: %s\n", idx, tx.Txid) + ob.logger.InTx.Debug().Msgf("BTC InTX | %d: %s\n", idx, tx.Txid) for vidx, vout := range tx.Vout { - ob.logger.WatchInTx.Debug().Msgf("vout %d \n value: %v\n scriptPubKey: %v\n", vidx, vout.Value, vout.ScriptPubKey.Hex) + ob.logger.InTx.Debug().Msgf("vout %d \n value: %v\n scriptPubKey: %v\n", vidx, vout.Value, vout.ScriptPubKey.Hex) } } } @@ -438,13 +441,13 @@ func (ob *BTCChainClient) ObserveInTx() error { if flags.BlockHeaderVerificationFlags != nil && flags.BlockHeaderVerificationFlags.IsBtcTypeChainEnabled { err = ob.postBlockHeader(bn) if err != nil { - ob.logger.WatchInTx.Warn().Err(err).Msgf("observeInTxBTC: error posting block header %d", bn) + ob.logger.InTx.Warn().Err(err).Msgf("observeInTxBTC: error posting block header %d", bn) } } if len(res.Block.Tx) > 1 { // get depositor fee - depositorFee := CalcDepositorFee(res.Block, ob.chain.ChainId, ob.netParams, ob.logger.WatchInTx) + depositorFee := CalcDepositorFee(res.Block, ob.chain.ChainId, ob.netParams, ob.logger.InTx) // filter incoming txs to TSS address tssAddress := ob.Tss.BTCAddress() @@ -453,7 +456,7 @@ func (ob *BTCChainClient) ObserveInTx() error { res.Block.Tx, uint64(res.Block.Height), tssAddress, - &ob.logger.WatchInTx, + &ob.logger.InTx, ob.netParams, depositorFee, ) @@ -464,10 +467,10 @@ func (ob *BTCChainClient) ObserveInTx() error { if msg != nil { zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(zetabridge.PostVoteInboundGasLimit, zetabridge.PostVoteInboundExecutionGasLimit, msg) if err != nil { - ob.logger.WatchInTx.Error().Err(err).Msgf("observeInTxBTC: error posting to zeta core for tx %s", inTx.TxHash) + ob.logger.InTx.Error().Err(err).Msgf("observeInTxBTC: error posting to zeta core for tx %s", inTx.TxHash) return err // we have to re-scan this block next time } else if zetaHash != "" { - ob.logger.WatchInTx.Info().Msgf("observeInTxBTC: PostVoteInbound zeta tx hash: %s inTx %s ballot %s fee %v", + ob.logger.InTx.Info().Msgf("observeInTxBTC: PostVoteInbound zeta tx hash: %s inTx %s ballot %s fee %v", zetaHash, inTx.TxHash, ballot, depositorFee) } } @@ -478,7 +481,7 @@ func (ob *BTCChainClient) ObserveInTx() error { ob.SetLastBlockHeightScanned(bn) // #nosec G701 always positive if err := ob.db.Save(clienttypes.ToLastBlockSQLType(uint64(bn))).Error; err != nil { - ob.logger.WatchInTx.Error().Err(err).Msgf("observeInTxBTC: error writing last scanned block %d to db", bn) + ob.logger.InTx.Error().Err(err).Msgf("observeInTxBTC: error writing last scanned block %d to db", bn) } } @@ -530,7 +533,7 @@ func (ob *BTCChainClient) IsSendOutTxProcessed(cctx *types.CrossChainTx, logger if txResult == nil { // check failed, try again next time return false, false, nil } else if inMempool { // still in mempool (should avoid unnecessary Tss keysign) - ob.logger.ObserveOutTx.Info().Msgf("IsSendOutTxProcessed: outTx %s is still in mempool", outTxID) + ob.logger.OutTx.Info().Msgf("IsSendOutTxProcessed: outTx %s is still in mempool", outTxID) return true, false, nil } // included @@ -541,7 +544,7 @@ func (ob *BTCChainClient) IsSendOutTxProcessed(cctx *types.CrossChainTx, logger if res == nil { return false, false, nil } - ob.logger.ObserveOutTx.Info().Msgf("IsSendOutTxProcessed: setIncludedTx succeeded for outTx %s", outTxID) + ob.logger.OutTx.Info().Msgf("IsSendOutTxProcessed: setIncludedTx succeeded for outTx %s", outTxID) } // It's safe to use cctx's amount to post confirmation because it has already been verified in observeOutTx() @@ -573,33 +576,37 @@ func (ob *BTCChainClient) IsSendOutTxProcessed(cctx *types.CrossChainTx, logger return true, true, nil } +// WatchGasPrice watches Bitcoin chain for gas rate and post to zetacore func (ob *BTCChainClient) WatchGasPrice() { // report gas price right away as the ticker takes time to kick in err := ob.PostGasPrice() if err != nil { - ob.logger.WatchGasPrice.Error().Err(err).Msgf("PostGasPrice error for chain %d", ob.chain.ChainId) + ob.logger.GasPrice.Error().Err(err).Msgf("PostGasPrice error for chain %d", ob.chain.ChainId) } // start gas price ticker ticker, err := clienttypes.NewDynamicTicker("Bitcoin_WatchGasPrice", ob.GetChainParams().GasPriceTicker) if err != nil { - ob.logger.WatchGasPrice.Error().Err(err).Msg("WatchGasPrice error") + ob.logger.GasPrice.Error().Err(err).Msg("error creating ticker") return } - ob.logger.WatchGasPrice.Info().Msgf("WatchGasPrice started for chain %d with interval %d", + ob.logger.GasPrice.Info().Msgf("WatchGasPrice started for chain %d with interval %d", ob.chain.ChainId, ob.GetChainParams().GasPriceTicker) defer ticker.Stop() for { select { case <-ticker.C(): + if !ob.GetChainParams().IsSupported { + continue + } err := ob.PostGasPrice() if err != nil { - ob.logger.WatchGasPrice.Error().Err(err).Msgf("PostGasPrice error for chain %d", ob.chain.ChainId) + ob.logger.GasPrice.Error().Err(err).Msgf("PostGasPrice error for chain %d", ob.chain.ChainId) } - ticker.UpdateInterval(ob.GetChainParams().GasPriceTicker, ob.logger.WatchGasPrice) + ticker.UpdateInterval(ob.GetChainParams().GasPriceTicker, ob.logger.GasPrice) case <-ob.stop: - ob.logger.WatchGasPrice.Info().Msg("WatchGasPrice stopped") + ob.logger.GasPrice.Info().Msgf("WatchGasPrice stopped for chain %d", ob.chain.ChainId) return } } @@ -614,7 +621,7 @@ func (ob *BTCChainClient) PostGasPrice() error { // #nosec G701 always in range zetaHash, err := ob.zetaClient.PostGasPrice(ob.chain, 1, "100", uint64(bn)) if err != nil { - ob.logger.WatchGasPrice.Err(err).Msg("PostGasPrice:") + ob.logger.GasPrice.Err(err).Msg("PostGasPrice:") return err } _ = zetaHash @@ -640,7 +647,7 @@ func (ob *BTCChainClient) PostGasPrice() error { // #nosec G701 always positive zetaHash, err := ob.zetaClient.PostGasPrice(ob.chain, feeRatePerByte.Uint64(), "100", uint64(bn)) if err != nil { - ob.logger.WatchGasPrice.Err(err).Msg("PostGasPrice:") + ob.logger.GasPrice.Err(err).Msg("PostGasPrice:") return err } _ = zetaHash @@ -687,7 +694,7 @@ func FilterAndParseIncomingTx( } func (ob *BTCChainClient) GetInboundVoteMessageFromBtcEvent(inTx *BTCInTxEvnet) *types.MsgVoteOnObservedInboundTx { - ob.logger.WatchInTx.Debug().Msgf("Processing inTx: %s", inTx.TxHash) + ob.logger.InTx.Debug().Msgf("Processing inTx: %s", inTx.TxHash) amount := big.NewFloat(inTx.Value) amount = amount.Mul(amount, big.NewFloat(1e8)) amountInt, _ := amount.Int(nil) @@ -724,7 +731,7 @@ func (ob *BTCChainClient) IsInTxRestricted(inTx *BTCInTxEvnet) bool { receiver = parsedAddress.Hex() } if config.ContainRestrictedAddress(inTx.FromAddress, receiver) { - compliance.PrintComplianceLog(ob.logger.WatchInTx, ob.logger.Compliance, + compliance.PrintComplianceLog(ob.logger.InTx, ob.logger.Compliance, false, ob.chain.ChainId, inTx.TxHash, inTx.FromAddress, receiver, "BTC") return true } @@ -820,10 +827,11 @@ func GetBtcEvent( return nil, nil } +// WatchUTXOS watches bitcoin chain for UTXOs owned by the TSS address func (ob *BTCChainClient) WatchUTXOS() { ticker, err := clienttypes.NewDynamicTicker("Bitcoin_WatchUTXOS", ob.GetChainParams().WatchUtxoTicker) if err != nil { - ob.logger.WatchUTXOS.Error().Err(err).Msg("WatchUTXOS error") + ob.logger.UTXOS.Error().Err(err).Msg("error creating ticker") return } @@ -831,13 +839,16 @@ func (ob *BTCChainClient) WatchUTXOS() { for { select { case <-ticker.C(): + if !ob.GetChainParams().IsSupported { + continue + } err := ob.FetchUTXOS() if err != nil { - ob.logger.WatchUTXOS.Error().Err(err).Msg("error fetching btc utxos") + ob.logger.UTXOS.Error().Err(err).Msg("error fetching btc utxos") } - ticker.UpdateInterval(ob.GetChainParams().WatchUtxoTicker, ob.logger.WatchUTXOS) + ticker.UpdateInterval(ob.GetChainParams().WatchUtxoTicker, ob.logger.UTXOS) case <-ob.stop: - ob.logger.WatchUTXOS.Info().Msg("WatchUTXOS stopped") + ob.logger.UTXOS.Info().Msgf("WatchUTXOS stopped for chain %d", ob.chain.ChainId) return } } @@ -846,7 +857,7 @@ func (ob *BTCChainClient) WatchUTXOS() { func (ob *BTCChainClient) FetchUTXOS() error { defer func() { if err := recover(); err != nil { - ob.logger.WatchUTXOS.Error().Msgf("BTC fetchUTXOS: caught panic error: %v", err) + ob.logger.UTXOS.Error().Msgf("BTC fetchUTXOS: caught panic error: %v", err) } }() @@ -920,7 +931,7 @@ func (ob *BTCChainClient) refreshPendingNonce() { // get pending nonces from zetabridge p, err := ob.zetaClient.GetPendingNoncesByChain(ob.chain.ChainId) if err != nil { - ob.logger.ChainLogger.Error().Err(err).Msg("refreshPendingNonce: error getting pending nonces") + ob.logger.Chain.Error().Err(err).Msg("refreshPendingNonce: error getting pending nonces") } // increase pending nonce if lagged behind @@ -934,14 +945,14 @@ func (ob *BTCChainClient) refreshPendingNonce() { // get the last included outTx hash txid, err := ob.getOutTxidByNonce(nonceLow-1, false) if err != nil { - ob.logger.ChainLogger.Error().Err(err).Msg("refreshPendingNonce: error getting last outTx txid") + ob.logger.Chain.Error().Err(err).Msg("refreshPendingNonce: error getting last outTx txid") } // set 'NonceLow' as the new pending nonce ob.Mu.Lock() defer ob.Mu.Unlock() ob.pendingNonce = nonceLow - ob.logger.ChainLogger.Info().Msgf("refreshPendingNonce: increase pending nonce to %d with txid %s", ob.pendingNonce, txid) + ob.logger.Chain.Info().Msgf("refreshPendingNonce: increase pending nonce to %d with txid %s", ob.pendingNonce, txid) } } @@ -981,10 +992,10 @@ func (ob *BTCChainClient) findNonceMarkUTXO(nonce uint64, txid string) (int, err for i, utxo := range ob.utxos { sats, err := GetSatoshis(utxo.Amount) if err != nil { - ob.logger.ObserveOutTx.Error().Err(err).Msgf("findNonceMarkUTXO: error getting satoshis for utxo %v", utxo) + ob.logger.OutTx.Error().Err(err).Msgf("findNonceMarkUTXO: error getting satoshis for utxo %v", utxo) } if utxo.Address == tssAddress && sats == amount && utxo.TxID == txid { - ob.logger.ObserveOutTx.Info().Msgf("findNonceMarkUTXO: found nonce-mark utxo with txid %s, amount %d satoshi", utxo.TxID, sats) + ob.logger.OutTx.Info().Msgf("findNonceMarkUTXO: found nonce-mark utxo with txid %s, amount %d satoshi", utxo.TxID, sats) return i, nil } } @@ -1086,15 +1097,16 @@ func (ob *BTCChainClient) SaveBroadcastedTx(txHash string, nonce uint64) { broadcastEntry := clienttypes.ToOutTxHashSQLType(txHash, outTxID) if err := ob.db.Save(&broadcastEntry).Error; err != nil { - ob.logger.ObserveOutTx.Error().Err(err).Msgf("SaveBroadcastedTx: error saving broadcasted txHash %s for outTx %s", txHash, outTxID) + ob.logger.OutTx.Error().Err(err).Msgf("SaveBroadcastedTx: error saving broadcasted txHash %s for outTx %s", txHash, outTxID) } - ob.logger.ObserveOutTx.Info().Msgf("SaveBroadcastedTx: saved broadcasted txHash %s for outTx %s", txHash, outTxID) + ob.logger.OutTx.Info().Msgf("SaveBroadcastedTx: saved broadcasted txHash %s for outTx %s", txHash, outTxID) } -func (ob *BTCChainClient) observeOutTx() { - ticker, err := clienttypes.NewDynamicTicker("Bitcoin_observeOutTx", ob.GetChainParams().OutTxTicker) +// WatchOutTx watches Bitcoin chain for outgoing txs status +func (ob *BTCChainClient) WatchOutTx() { + ticker, err := clienttypes.NewDynamicTicker("Bitcoin_WatchOutTx", ob.GetChainParams().OutTxTicker) if err != nil { - ob.logger.ObserveOutTx.Error().Err(err).Msg("observeOutTx: error creating ticker") + ob.logger.OutTx.Error().Err(err).Msg("error creating ticker ") return } @@ -1105,9 +1117,12 @@ func (ob *BTCChainClient) observeOutTx() { if flags := ob.coreContext.GetCrossChainFlags(); !flags.IsOutboundEnabled { continue } + if !ob.GetChainParams().IsSupported { + continue + } trackers, err := ob.zetaClient.GetAllOutTxTrackerByChain(ob.chain.ChainId, interfaces.Ascending) if err != nil { - ob.logger.ObserveOutTx.Error().Err(err).Msg("observeOutTx: error GetAllOutTxTrackerByChain") + ob.logger.OutTx.Error().Err(err).Msgf("WatchOutTx: error GetAllOutTxTrackerByChain for chain %d", ob.chain.ChainId) continue } for _, tracker := range trackers { @@ -1115,16 +1130,16 @@ func (ob *BTCChainClient) observeOutTx() { outTxID := ob.GetTxID(tracker.Nonce) cctx, err := ob.zetaClient.GetCctxByNonce(ob.chain.ChainId, tracker.Nonce) if err != nil { - ob.logger.ObserveOutTx.Info().Err(err).Msgf("observeOutTx: can't find cctx for nonce %d", tracker.Nonce) + ob.logger.OutTx.Info().Err(err).Msgf("WatchOutTx: can't find cctx for chain %d nonce %d", ob.chain.ChainId, tracker.Nonce) break } nonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce if tracker.Nonce != nonce { // Tanmay: it doesn't hurt to check - ob.logger.ObserveOutTx.Error().Msgf("observeOutTx: tracker nonce %d not match cctx nonce %d", tracker.Nonce, nonce) + ob.logger.OutTx.Error().Msgf("WatchOutTx: tracker nonce %d not match cctx nonce %d", tracker.Nonce, nonce) break } if len(tracker.HashList) > 1 { - ob.logger.ObserveOutTx.Warn().Msgf("observeOutTx: oops, outTxID %s got multiple (%d) outTx hashes", outTxID, len(tracker.HashList)) + ob.logger.OutTx.Warn().Msgf("WatchOutTx: oops, outTxID %s got multiple (%d) outTx hashes", outTxID, len(tracker.HashList)) } // iterate over all txHashes to find the truly included one. // we do it this (inefficient) way because we don't rely on the first one as it may be a false positive (for unknown reason). @@ -1135,10 +1150,10 @@ func (ob *BTCChainClient) observeOutTx() { if result != nil && !inMempool { // included txCount++ txResult = result - ob.logger.ObserveOutTx.Info().Msgf("observeOutTx: included outTx %s for chain %d nonce %d", txHash.TxHash, ob.chain.ChainId, tracker.Nonce) + ob.logger.OutTx.Info().Msgf("WatchOutTx: included outTx %s for chain %d nonce %d", txHash.TxHash, ob.chain.ChainId, tracker.Nonce) if txCount > 1 { - ob.logger.ObserveOutTx.Error().Msgf( - "observeOutTx: checkIncludedTx passed, txCount %d chain %d nonce %d result %v", txCount, ob.chain.ChainId, tracker.Nonce, result) + ob.logger.OutTx.Error().Msgf( + "WatchOutTx: checkIncludedTx passed, txCount %d chain %d nonce %d result %v", txCount, ob.chain.ChainId, tracker.Nonce, result) } } } @@ -1146,12 +1161,12 @@ func (ob *BTCChainClient) observeOutTx() { ob.setIncludedTx(tracker.Nonce, txResult) } else if txCount > 1 { ob.removeIncludedTx(tracker.Nonce) // we can't tell which txHash is true, so we remove all (if any) to be safe - ob.logger.ObserveOutTx.Error().Msgf("observeOutTx: included multiple (%d) outTx for chain %d nonce %d", txCount, ob.chain.ChainId, tracker.Nonce) + ob.logger.OutTx.Error().Msgf("WatchOutTx: included multiple (%d) outTx for chain %d nonce %d", txCount, ob.chain.ChainId, tracker.Nonce) } } - ticker.UpdateInterval(ob.GetChainParams().OutTxTicker, ob.logger.ObserveOutTx) + ticker.UpdateInterval(ob.GetChainParams().OutTxTicker, ob.logger.OutTx) case <-ob.stop: - ob.logger.ObserveOutTx.Info().Msg("observeOutTx stopped") + ob.logger.OutTx.Info().Msgf("WatchOutTx stopped for chain %d", ob.chain.ChainId) return } } @@ -1163,17 +1178,17 @@ func (ob *BTCChainClient) checkIncludedTx(cctx *types.CrossChainTx, txHash strin outTxID := ob.GetTxID(cctx.GetCurrentOutTxParam().OutboundTxTssNonce) hash, getTxResult, err := ob.GetTxResultByHash(txHash) if err != nil { - ob.logger.ObserveOutTx.Error().Err(err).Msgf("checkIncludedTx: error GetTxResultByHash: %s", txHash) + ob.logger.OutTx.Error().Err(err).Msgf("checkIncludedTx: error GetTxResultByHash: %s", txHash) return nil, false } if txHash != getTxResult.TxID { // just in case, we'll use getTxResult.TxID later - ob.logger.ObserveOutTx.Error().Msgf("checkIncludedTx: inconsistent txHash %s and getTxResult.TxID %s", txHash, getTxResult.TxID) + ob.logger.OutTx.Error().Msgf("checkIncludedTx: inconsistent txHash %s and getTxResult.TxID %s", txHash, getTxResult.TxID) return nil, false } if getTxResult.Confirmations >= 0 { // check included tx only err = ob.checkTssOutTxResult(cctx, hash, getTxResult) if err != nil { - ob.logger.ObserveOutTx.Error().Err(err).Msgf("checkIncludedTx: error verify bitcoin outTx %s outTxID %s", txHash, outTxID) + ob.logger.OutTx.Error().Err(err).Msgf("checkIncludedTx: error verify bitcoin outTx %s outTxID %s", txHash, outTxID) return nil, false } return getTxResult, false // included @@ -1196,16 +1211,16 @@ func (ob *BTCChainClient) setIncludedTx(nonce uint64, getTxResult *btcjson.GetTr if nonce >= ob.pendingNonce { // try increasing pending nonce on every newly included outTx ob.pendingNonce = nonce + 1 } - ob.logger.ObserveOutTx.Info().Msgf("setIncludedTx: included new bitcoin outTx %s outTxID %s pending nonce %d", txHash, outTxID, ob.pendingNonce) + ob.logger.OutTx.Info().Msgf("setIncludedTx: included new bitcoin outTx %s outTxID %s pending nonce %d", txHash, outTxID, ob.pendingNonce) } else if txHash == res.TxID { // found same hash. ob.includedTxResults[outTxID] = getTxResult // update tx result as confirmations may increase if getTxResult.Confirmations > res.Confirmations { - ob.logger.ObserveOutTx.Info().Msgf("setIncludedTx: bitcoin outTx %s got confirmations %d", txHash, getTxResult.Confirmations) + ob.logger.OutTx.Info().Msgf("setIncludedTx: bitcoin outTx %s got confirmations %d", txHash, getTxResult.Confirmations) } } else { // found other hash. // be alert for duplicate payment!!! As we got a new hash paying same cctx (for whatever reason). delete(ob.includedTxResults, outTxID) // we can't tell which txHash is true, so we remove all to be safe - ob.logger.ObserveOutTx.Error().Msgf("setIncludedTx: duplicate payment by bitcoin outTx %s outTxID %s, prior outTx %s", txHash, outTxID, res.TxID) + ob.logger.OutTx.Error().Msgf("setIncludedTx: duplicate payment by bitcoin outTx %s outTxID %s, prior outTx %s", txHash, outTxID, res.TxID) } } @@ -1415,7 +1430,7 @@ func (ob *BTCChainClient) checkTSSVoutCancelled(params *types.OutboundTxParams, func (ob *BTCChainClient) BuildBroadcastedTxMap() error { var broadcastedTransactions []clienttypes.OutTxHashSQLType if err := ob.db.Find(&broadcastedTransactions).Error; err != nil { - ob.logger.ChainLogger.Error().Err(err).Msg("error iterating over db") + ob.logger.Chain.Error().Err(err).Msg("error iterating over db") return err } for _, entry := range broadcastedTransactions { @@ -1433,7 +1448,7 @@ func (ob *BTCChainClient) LoadLastBlock() error { //Load persisted block number var lastBlockNum clienttypes.LastBlockSQLType if err := ob.db.First(&lastBlockNum, clienttypes.LastBlockNumID).Error; err != nil { - ob.logger.ChainLogger.Info().Msg("LastBlockNum not found in DB, scan from latest") + ob.logger.Chain.Info().Msg("LastBlockNum not found in DB, scan from latest") ob.SetLastBlockHeightScanned(bn) } else { // #nosec G701 always in range @@ -1442,7 +1457,7 @@ func (ob *BTCChainClient) LoadLastBlock() error { //If persisted block number is too low, use the latest height if (bn - lastBN) > maxHeightDiff { - ob.logger.ChainLogger.Info().Msgf("LastBlockNum too low: %d, scan from latest", lastBlockNum.Num) + ob.logger.Chain.Info().Msgf("LastBlockNum too low: %d, scan from latest", lastBlockNum.Num) ob.SetLastBlockHeightScanned(bn) } } @@ -1450,7 +1465,7 @@ func (ob *BTCChainClient) LoadLastBlock() error { if ob.chain.ChainId == 18444 { // bitcoin regtest: start from block 100 ob.SetLastBlockHeightScanned(100) } - ob.logger.ChainLogger.Info().Msgf("%s: start scanning from block %d", ob.chain.String(), ob.GetLastBlockHeightScanned()) + ob.logger.Chain.Info().Msgf("%s: start scanning from block %d", ob.chain.String(), ob.GetLastBlockHeightScanned()) return nil } 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/bitcoin/inbound_tracker.go b/zetaclient/bitcoin/inbound_tracker.go index bcabf0c1a8..95a15a0bbe 100644 --- a/zetaclient/bitcoin/inbound_tracker.go +++ b/zetaclient/bitcoin/inbound_tracker.go @@ -10,10 +10,11 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/zetabridge" ) -func (ob *BTCChainClient) ExternalChainWatcherForNewInboundTrackerSuggestions() { - ticker, err := types.NewDynamicTicker("Bitcoin_WatchInTx_InboundTrackerSuggestions", ob.GetChainParams().InTxTicker) +// WatchIntxTracker watches zetacore for bitcoin intx trackers +func (ob *BTCChainClient) WatchIntxTracker() { + ticker, err := types.NewDynamicTicker("Bitcoin_WatchIntxTracker", ob.GetChainParams().InTxTicker) if err != nil { - ob.logger.WatchInTx.Err(err).Msg("error creating ticker") + ob.logger.InTx.Err(err).Msg("error creating ticker") return } @@ -24,13 +25,16 @@ func (ob *BTCChainClient) ExternalChainWatcherForNewInboundTrackerSuggestions() if flags := ob.coreContext.GetCrossChainFlags(); !flags.IsInboundEnabled { continue } + if !ob.GetChainParams().IsSupported { + continue + } err := ob.ObserveTrackerSuggestions() if err != nil { - ob.logger.WatchInTx.Error().Err(err).Msg("error observing in tx") + ob.logger.InTx.Error().Err(err).Msgf("error observing intx tracker for chain %d", ob.chain.ChainId) } - ticker.UpdateInterval(ob.GetChainParams().InTxTicker, ob.logger.WatchInTx) + ticker.UpdateInterval(ob.GetChainParams().InTxTicker, ob.logger.InTx) case <-ob.stop: - ob.logger.WatchInTx.Info().Msg("ExternalChainWatcher for BTC inboundTrackerSuggestions stopped") + ob.logger.InTx.Info().Msgf("WatchIntxTracker stopped for chain %d", ob.chain.ChainId) return } } @@ -42,12 +46,12 @@ func (ob *BTCChainClient) ObserveTrackerSuggestions() error { return err } for _, tracker := range trackers { - ob.logger.WatchInTx.Info().Msgf("checking tracker with hash :%s and coin-type :%s ", tracker.TxHash, tracker.CoinType) + ob.logger.InTx.Info().Msgf("checking tracker with hash :%s and coin-type :%s ", tracker.TxHash, tracker.CoinType) ballotIdentifier, err := ob.CheckReceiptForBtcTxHash(tracker.TxHash, true) if err != nil { return err } - ob.logger.WatchInTx.Info().Msgf("Vote submitted for inbound Tracker,Chain : %s,Ballot Identifier : %s, coin-type %s", ob.chain.ChainName, ballotIdentifier, coin.CoinType_Gas.String()) + ob.logger.InTx.Info().Msgf("Vote submitted for inbound Tracker,Chain : %s,Ballot Identifier : %s, coin-type %s", ob.chain.ChainName, ballotIdentifier, coin.CoinType_Gas.String()) } return nil } @@ -72,13 +76,13 @@ func (ob *BTCChainClient) CheckReceiptForBtcTxHash(txHash string, vote bool) (st if len(blockVb.Tx) <= 1 { return "", fmt.Errorf("block %d has no transactions", blockVb.Height) } - depositorFee := CalcDepositorFee(blockVb, ob.chain.ChainId, ob.netParams, ob.logger.WatchInTx) + depositorFee := CalcDepositorFee(blockVb, ob.chain.ChainId, ob.netParams, ob.logger.InTx) tss, err := ob.zetaClient.GetBtcTssAddress(ob.chain.ChainId) if err != nil { return "", err } // #nosec G701 always positive - event, err := GetBtcEvent(*tx, tss, uint64(blockVb.Height), &ob.logger.WatchInTx, ob.netParams, depositorFee) + event, err := GetBtcEvent(*tx, tss, uint64(blockVb.Height), &ob.logger.InTx, ob.netParams, depositorFee) if err != nil { return "", err } @@ -94,10 +98,10 @@ func (ob *BTCChainClient) CheckReceiptForBtcTxHash(txHash string, vote bool) (st } zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(zetabridge.PostVoteInboundGasLimit, zetabridge.PostVoteInboundExecutionGasLimit, msg) if err != nil { - ob.logger.WatchInTx.Error().Err(err).Msg("error posting to zeta core") + ob.logger.InTx.Error().Err(err).Msg("error posting to zeta core") return "", err } else if zetaHash != "" { - ob.logger.WatchInTx.Info().Msgf("BTC deposit detected and reported: PostVoteInbound zeta tx hash: %s inTx %s ballot %s fee %v", + ob.logger.InTx.Info().Msgf("BTC deposit detected and reported: PostVoteInbound zeta tx hash: %s inTx %s ballot %s fee %v", zetaHash, txHash, ballot, depositorFee) } return msg.Digest(), nil diff --git a/zetaclient/evm/evm_client.go b/zetaclient/evm/evm_client.go index f6a70b1fe6..817ee66754 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" ) @@ -60,11 +57,11 @@ type OutTx struct { Nonce int64 } type Log struct { - ChainLogger zerolog.Logger // Parent logger - ExternalChainWatcher zerolog.Logger // Observes external Chains for incoming trasnactions - WatchGasPrice zerolog.Logger // Observes external Chains for Gas prices and posts to core - ObserveOutTx zerolog.Logger // Observes external Chains for outgoing transactions - Compliance zerolog.Logger // Compliance logger + Chain zerolog.Logger // The parent logger for the chain + InTx zerolog.Logger // Logger for incoming trasnactions + OutTx zerolog.Logger // Logger for outgoing transactions + GasPrice zerolog.Logger // Logger for gas prices + Compliance zerolog.Logger // Logger for compliance checks } var _ interfaces.ChainClient = &ChainClient{} @@ -111,11 +108,11 @@ func NewEVMChainClient( } chainLogger := loggers.Std.With().Str("chain", evmCfg.Chain.ChainName.String()).Logger() ob.logger = Log{ - ChainLogger: chainLogger, - ExternalChainWatcher: chainLogger.With().Str("module", "ExternalChainWatcher").Logger(), - WatchGasPrice: chainLogger.With().Str("module", "WatchGasPrice").Logger(), - ObserveOutTx: chainLogger.With().Str("module", "ObserveOutTx").Logger(), - Compliance: loggers.Compliance, + Chain: chainLogger, + InTx: chainLogger.With().Str("module", "WatchInTx").Logger(), + OutTx: chainLogger.With().Str("module", "WatchOutTx").Logger(), + GasPrice: chainLogger.With().Str("module", "WatchGasPrice").Logger(), + Compliance: loggers.Compliance, } ob.coreContext = appContext.ZetaCoreContext() chainParams, found := ob.coreContext.GetEVMChainParams(evmCfg.Chain.ChainId) @@ -133,10 +130,10 @@ func NewEVMChainClient( ob.outTXConfirmedReceipts = make(map[string]*ethtypes.Receipt) ob.outTXConfirmedTransactions = make(map[string]*ethtypes.Transaction) - ob.logger.ChainLogger.Info().Msgf("Chain %s endpoint %s", ob.chain.ChainName.String(), evmCfg.Endpoint) + ob.logger.Chain.Info().Msgf("Chain %s endpoint %s", ob.chain.ChainName.String(), evmCfg.Endpoint) client, err := ethclient.Dial(evmCfg.Endpoint) if err != nil { - ob.logger.ChainLogger.Error().Err(err).Msg("eth Client Dial") + ob.logger.Chain.Error().Err(err).Msg("eth Client Dial") return nil, err } ob.evmClient = client @@ -145,12 +142,12 @@ func NewEVMChainClient( // create block header and block caches ob.blockCache, err = lru.New(1000) if err != nil { - ob.logger.ChainLogger.Error().Err(err).Msg("failed to create block cache") + ob.logger.Chain.Error().Err(err).Msg("failed to create block cache") return nil, err } ob.headerCache, err = lru.New(1000) if err != nil { - ob.logger.ChainLogger.Error().Err(err).Msg("failed to create header cache") + ob.logger.Chain.Error().Err(err).Msg("failed to create header cache") return nil, err } @@ -159,7 +156,7 @@ func NewEVMChainClient( return nil, err } - ob.logger.ChainLogger.Info().Msgf("%s: start scanning from block %d", ob.chain.String(), ob.GetLastBlockHeightScanned()) + ob.logger.Chain.Info().Msgf("%s: start scanning from block %d", ob.chain.String(), ob.GetLastBlockHeightScanned()) return &ob, nil } @@ -172,10 +169,10 @@ func (ob *ChainClient) WithLogger(logger zerolog.Logger) { ob.Mu.Lock() defer ob.Mu.Unlock() ob.logger = Log{ - ChainLogger: logger, - ExternalChainWatcher: logger.With().Str("module", "ExternalChainWatcher").Logger(), - WatchGasPrice: logger.With().Str("module", "WatchGasPrice").Logger(), - ObserveOutTx: logger.With().Str("module", "ObserveOutTx").Logger(), + Chain: logger, + InTx: logger.With().Str("module", "WatchInTx").Logger(), + OutTx: logger.With().Str("module", "WatchOutTx").Logger(), + GasPrice: logger.With().Str("module", "WatchGasPrice").Logger(), } } @@ -255,43 +252,48 @@ func FetchERC20CustodyContract(addr ethcommon.Address, client interfaces.EVMRPCC return erc20custody.NewERC20Custody(addr, client) } +// Start all observation routines for the evm chain func (ob *ChainClient) Start() { - go ob.ExternalChainWatcherForNewInboundTrackerSuggestions() - go ob.ExternalChainWatcher() // Observes external Chains for incoming trasnactions - go ob.WatchGasPrice() // Observes external Chains for Gas prices and posts to core - go ob.observeOutTx() // Populates receipts and confirmed outbound transactions - go ob.ExternalChainRPCStatus() + go ob.WatchInTx() // watch evm chain for incoming txs and post votes to zetacore + go ob.WatchOutTx() // watch evm chain for outgoing txs status + go ob.WatchGasPrice() // watch evm chain for gas prices and post to zetacore + go ob.WatchIntxTracker() // watch zetacore for intx trackers + go ob.WatchRPCStatus() // watch the RPC status of the evm chain } -func (ob *ChainClient) ExternalChainRPCStatus() { - ob.logger.ChainLogger.Info().Msgf("Starting RPC status check for chain %s", ob.chain.String()) +// WatchRPCStatus watches the RPC status of the evm chain +func (ob *ChainClient) WatchRPCStatus() { + ob.logger.Chain.Info().Msgf("Starting RPC status check for chain %s", ob.chain.String()) ticker := time.NewTicker(60 * time.Second) for { select { case <-ticker.C: + if !ob.GetChainParams().IsSupported { + continue + } bn, err := ob.evmClient.BlockNumber(context.Background()) if err != nil { - ob.logger.ChainLogger.Error().Err(err).Msg("RPC Status Check error: RPC down?") + ob.logger.Chain.Error().Err(err).Msg("RPC Status Check error: RPC down?") continue } gasPrice, err := ob.evmClient.SuggestGasPrice(context.Background()) if err != nil { - ob.logger.ChainLogger.Error().Err(err).Msg("RPC Status Check error: RPC down?") + ob.logger.Chain.Error().Err(err).Msg("RPC Status Check error: RPC down?") continue } header, err := ob.evmClient.HeaderByNumber(context.Background(), new(big.Int).SetUint64(bn)) if err != nil { - ob.logger.ChainLogger.Error().Err(err).Msg("RPC Status Check error: RPC down?") + ob.logger.Chain.Error().Err(err).Msg("RPC Status Check error: RPC down?") continue } // #nosec G701 always in range blockTime := time.Unix(int64(header.Time), 0).UTC() elapsedSeconds := time.Since(blockTime).Seconds() if elapsedSeconds > 100 { - ob.logger.ChainLogger.Warn().Msgf("RPC Status Check warning: RPC stale or chain stuck (check explorer)? Latest block %d timestamp is %.0fs ago", bn, elapsedSeconds) + ob.logger.Chain.Warn().Msgf("RPC Status Check warning: RPC stale or chain stuck (check explorer)? Latest block %d timestamp is %.0fs ago", bn, elapsedSeconds) continue } - ob.logger.ChainLogger.Info().Msgf("[OK] RPC status: latest block num %d, timestamp %s ( %.0fs ago), suggested gas price %d", header.Number, blockTime.String(), elapsedSeconds, gasPrice.Uint64()) + ob.logger.Chain.Info().Msgf("[OK] RPC status: latest block num %d, timestamp %s ( %.0fs ago), suggested gas price %d", header.Number, blockTime.String(), elapsedSeconds, gasPrice.Uint64()) case <-ob.stop: return } @@ -299,27 +301,27 @@ func (ob *ChainClient) ExternalChainRPCStatus() { } func (ob *ChainClient) Stop() { - ob.logger.ChainLogger.Info().Msgf("ob %s is stopping", ob.chain.String()) + ob.logger.Chain.Info().Msgf("ob %s is stopping", ob.chain.String()) close(ob.stop) // this notifies all goroutines to stop - ob.logger.ChainLogger.Info().Msg("closing ob.db") + ob.logger.Chain.Info().Msg("closing ob.db") dbInst, err := ob.db.DB() if err != nil { - ob.logger.ChainLogger.Info().Msg("error getting database instance") + ob.logger.Chain.Info().Msg("error getting database instance") } err = dbInst.Close() if err != nil { - ob.logger.ChainLogger.Error().Err(err).Msg("error closing database") + ob.logger.Chain.Error().Err(err).Msg("error closing database") } - ob.logger.ChainLogger.Info().Msgf("%s observer stopped", ob.chain.String()) + ob.logger.Chain.Info().Msgf("%s observer stopped", ob.chain.String()) } // returns: isIncluded, isConfirmed, Error // 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 @@ -609,12 +611,11 @@ func (ob *ChainClient) IsSendOutTxProcessed(cctx *crosschaintypes.CrossChainTx, return false, false, nil } -// FIXME: there's a chance that a txhash in OutTxChan may not deliver when Stop() is called -// observeOutTx periodically checks all the txhash in potential outbound txs -func (ob *ChainClient) observeOutTx() { - ticker, err := clienttypes.NewDynamicTicker(fmt.Sprintf("EVM_observeOutTx_%d", ob.chain.ChainId), ob.GetChainParams().OutTxTicker) +// WatchOutTx watches evm chain for outgoing txs status +func (ob *ChainClient) WatchOutTx() { + ticker, err := clienttypes.NewDynamicTicker(fmt.Sprintf("EVM_WatchOutTx_%d", ob.chain.ChainId), ob.GetChainParams().OutTxTicker) if err != nil { - ob.logger.ObserveOutTx.Error().Err(err).Msg("failed to create ticker") + ob.logger.OutTx.Error().Err(err).Msg("error creating ticker") return } @@ -625,6 +626,9 @@ func (ob *ChainClient) observeOutTx() { if flags := ob.coreContext.GetCrossChainFlags(); !flags.IsOutboundEnabled { continue } + if !ob.GetChainParams().IsSupported { + continue + } trackers, err := ob.zetaClient.GetAllOutTxTrackerByChain(ob.chain.ChainId, interfaces.Ascending) if err != nil { continue @@ -642,22 +646,22 @@ func (ob *ChainClient) observeOutTx() { txCount++ receipt = recpt transaction = tx - ob.logger.ObserveOutTx.Info().Msgf("observeOutTx: confirmed outTx %s for chain %d nonce %d", txHash.TxHash, ob.chain.ChainId, nonceInt) + ob.logger.OutTx.Info().Msgf("WatchOutTx: confirmed outTx %s for chain %d nonce %d", txHash.TxHash, ob.chain.ChainId, nonceInt) if txCount > 1 { - ob.logger.ObserveOutTx.Error().Msgf( - "observeOutTx: checkConfirmedTx passed, txCount %d chain %d nonce %d receipt %v transaction %v", txCount, ob.chain.ChainId, nonceInt, receipt, transaction) + ob.logger.OutTx.Error().Msgf( + "WatchOutTx: checkConfirmedTx passed, txCount %d chain %d nonce %d receipt %v transaction %v", txCount, ob.chain.ChainId, nonceInt, receipt, transaction) } } } if txCount == 1 { // should be only one txHash confirmed for each nonce. ob.SetTxNReceipt(nonceInt, receipt, transaction) } else if txCount > 1 { // should not happen. We can't tell which txHash is true. It might happen (e.g. glitchy/hacked endpoint) - ob.logger.ObserveOutTx.Error().Msgf("observeOutTx: confirmed multiple (%d) outTx for chain %d nonce %d", txCount, ob.chain.ChainId, nonceInt) + ob.logger.OutTx.Error().Msgf("WatchOutTx: confirmed multiple (%d) outTx for chain %d nonce %d", txCount, ob.chain.ChainId, nonceInt) } } - ticker.UpdateInterval(ob.GetChainParams().OutTxTicker, ob.logger.ObserveOutTx) + ticker.UpdateInterval(ob.GetChainParams().OutTxTicker, ob.logger.OutTx) case <-ob.stop: - ob.logger.ObserveOutTx.Info().Msg("observeOutTx: stopped") + ob.logger.OutTx.Info().Msg("WatchOutTx: stopped") return } } @@ -823,29 +827,34 @@ func (ob *ChainClient) GetLastBlockHeight() uint64 { return height } -func (ob *ChainClient) ExternalChainWatcher() { - ticker, err := clienttypes.NewDynamicTicker(fmt.Sprintf("EVM_ExternalChainWatcher_%d", ob.chain.ChainId), ob.GetChainParams().InTxTicker) +// WatchInTx watches evm chain for incoming txs and post votes to zetacore +func (ob *ChainClient) WatchInTx() { + ticker, err := clienttypes.NewDynamicTicker(fmt.Sprintf("EVM_WatchInTx_%d", ob.chain.ChainId), ob.GetChainParams().InTxTicker) if err != nil { - ob.logger.ExternalChainWatcher.Error().Err(err).Msg("NewDynamicTicker error") + ob.logger.InTx.Error().Err(err).Msg("error creating ticker") return } defer ticker.Stop() - ob.logger.ExternalChainWatcher.Info().Msg("ExternalChainWatcher started") - sampledLogger := ob.logger.ExternalChainWatcher.Sample(&zerolog.BasicSampler{N: 10}) + ob.logger.InTx.Info().Msgf("WatchInTx started for chain %d", ob.chain.ChainId) + sampledLogger := ob.logger.InTx.Sample(&zerolog.BasicSampler{N: 10}) for { select { case <-ticker.C(): if flags := ob.coreContext.GetCrossChainFlags(); !flags.IsInboundEnabled { continue } + if !ob.GetChainParams().IsSupported { + sampledLogger.Info().Msgf("WatchInTx: chain %d is not supported", ob.chain.ChainId) + continue + } err := ob.observeInTX(sampledLogger) if err != nil { - ob.logger.ExternalChainWatcher.Err(err).Msg("observeInTX error") + ob.logger.InTx.Err(err).Msg("WatchInTx: observeInTX error") } - ticker.UpdateInterval(ob.GetChainParams().InTxTicker, ob.logger.ExternalChainWatcher) + ticker.UpdateInterval(ob.GetChainParams().InTxTicker, ob.logger.InTx) case <-ob.stop: - ob.logger.ExternalChainWatcher.Info().Msg("ExternalChainWatcher stopped") + ob.logger.InTx.Info().Msgf("WatchInTx stopped for chain %d", ob.chain.ChainId) return } } @@ -876,12 +885,12 @@ func (ob *ChainClient) postBlockHeader(tip uint64) error { header, err := ob.GetBlockHeaderCached(bn) if err != nil { - ob.logger.ExternalChainWatcher.Error().Err(err).Msgf("postBlockHeader: error getting block: %d", bn) + ob.logger.InTx.Error().Err(err).Msgf("postBlockHeader: error getting block: %d", bn) return err } headerRLP, err := rlp.EncodeToBytes(header) if err != nil { - ob.logger.ExternalChainWatcher.Error().Err(err).Msgf("postBlockHeader: error encoding block header: %d", bn) + ob.logger.InTx.Error().Err(err).Msgf("postBlockHeader: error encoding block header: %d", bn) return err } @@ -892,7 +901,7 @@ func (ob *ChainClient) postBlockHeader(tip uint64) error { proofs.NewEthereumHeader(headerRLP), ) if err != nil { - ob.logger.ExternalChainWatcher.Error().Err(err).Msgf("postBlockHeader: error posting block header: %d", bn) + ob.logger.InTx.Error().Err(err).Msgf("postBlockHeader: error posting block header: %d", bn) return err } return nil @@ -953,7 +962,7 @@ func (ob *ChainClient) observeInTX(sampledLogger zerolog.Logger) error { ob.chain.ChainId, lastScannedZetaSent, lastScannedDeposited, lastScannedTssRecvd) ob.SetLastBlockHeightScanned(lastScannedLowest) if err := ob.db.Save(clienttypes.ToLastBlockSQLType(lastScannedLowest)).Error; err != nil { - ob.logger.ExternalChainWatcher.Error().Err(err).Msgf("observeInTX: error writing lastScannedLowest %d to db", lastScannedLowest) + ob.logger.InTx.Error().Err(err).Msgf("observeInTX: error writing lastScannedLowest %d to db", lastScannedLowest) } } return nil @@ -965,7 +974,7 @@ func (ob *ChainClient) ObserveZetaSent(startBlock, toBlock uint64) uint64 { // filter ZetaSent logs addrConnector, connector, err := ob.GetConnectorContract() if err != nil { - ob.logger.ChainLogger.Warn().Err(err).Msgf("ObserveZetaSent: GetConnectorContract error:") + ob.logger.Chain.Warn().Err(err).Msgf("ObserveZetaSent: GetConnectorContract error:") return startBlock - 1 // lastScanned } iter, err := connector.FilterZetaSent(&bind.FilterOpts{ @@ -974,7 +983,7 @@ func (ob *ChainClient) ObserveZetaSent(startBlock, toBlock uint64) uint64 { Context: context.TODO(), }, []ethcommon.Address{}, []*big.Int{}) if err != nil { - ob.logger.ChainLogger.Warn().Err(err).Msgf( + ob.logger.Chain.Warn().Err(err).Msgf( "ObserveZetaSent: FilterZetaSent error from block %d to %d for chain %d", startBlock, toBlock, ob.chain.ChainId) return startBlock - 1 // lastScanned } @@ -988,7 +997,7 @@ func (ob *ChainClient) ObserveZetaSent(startBlock, toBlock uint64) uint64 { events = append(events, iter.Event) continue } - ob.logger.ExternalChainWatcher.Warn().Err(err).Msgf("ObserveZetaSent: invalid ZetaSent event in tx %s on chain %d at height %d", + ob.logger.InTx.Warn().Err(err).Msgf("ObserveZetaSent: invalid ZetaSent event in tx %s on chain %d at height %d", iter.Event.Raw.TxHash.Hex(), ob.chain.ChainId, iter.Event.Raw.BlockNumber) } sort.SliceStable(events, func(i, j int) bool { @@ -1014,7 +1023,7 @@ func (ob *ChainClient) ObserveZetaSent(startBlock, toBlock uint64) uint64 { } // guard against multiple events in the same tx if guard[event.Raw.TxHash.Hex()] { - ob.logger.ExternalChainWatcher.Warn().Msgf("ObserveZetaSent: multiple remote call events detected in tx %s", event.Raw.TxHash) + ob.logger.InTx.Warn().Msgf("ObserveZetaSent: multiple remote call events detected in tx %s", event.Raw.TxHash) continue } guard[event.Raw.TxHash.Hex()] = true @@ -1037,7 +1046,7 @@ func (ob *ChainClient) ObserveERC20Deposited(startBlock, toBlock uint64) uint64 // filter ERC20CustodyDeposited logs addrCustody, erc20custodyContract, err := ob.GetERC20CustodyContract() if err != nil { - ob.logger.ExternalChainWatcher.Warn().Err(err).Msgf("ObserveERC20Deposited: GetERC20CustodyContract error:") + ob.logger.InTx.Warn().Err(err).Msgf("ObserveERC20Deposited: GetERC20CustodyContract error:") return startBlock - 1 // lastScanned } iter, err := erc20custodyContract.FilterDeposited(&bind.FilterOpts{ @@ -1046,7 +1055,7 @@ func (ob *ChainClient) ObserveERC20Deposited(startBlock, toBlock uint64) uint64 Context: context.TODO(), }, []ethcommon.Address{}) if err != nil { - ob.logger.ExternalChainWatcher.Warn().Err(err).Msgf( + ob.logger.InTx.Warn().Err(err).Msgf( "ObserveERC20Deposited: FilterDeposited error from block %d to %d for chain %d", startBlock, toBlock, ob.chain.ChainId) return startBlock - 1 // lastScanned } @@ -1060,7 +1069,7 @@ func (ob *ChainClient) ObserveERC20Deposited(startBlock, toBlock uint64) uint64 events = append(events, iter.Event) continue } - ob.logger.ExternalChainWatcher.Warn().Err(err).Msgf("ObserveERC20Deposited: invalid Deposited event in tx %s on chain %d at height %d", + ob.logger.InTx.Warn().Err(err).Msgf("ObserveERC20Deposited: invalid Deposited event in tx %s on chain %d at height %d", iter.Event.Raw.TxHash.Hex(), ob.chain.ChainId, iter.Event.Raw.BlockNumber) } sort.SliceStable(events, func(i, j int) bool { @@ -1086,7 +1095,7 @@ func (ob *ChainClient) ObserveERC20Deposited(startBlock, toBlock uint64) uint64 } tx, _, err := ob.TransactionByHash(event.Raw.TxHash.Hex()) if err != nil { - ob.logger.ExternalChainWatcher.Error().Err(err).Msgf( + ob.logger.InTx.Error().Err(err).Msgf( "ObserveERC20Deposited: error getting transaction for intx %s chain %d", event.Raw.TxHash, ob.chain.ChainId) return beingScanned - 1 // we have to re-scan from this block next time } @@ -1094,7 +1103,7 @@ func (ob *ChainClient) ObserveERC20Deposited(startBlock, toBlock uint64) uint64 // guard against multiple events in the same tx if guard[event.Raw.TxHash.Hex()] { - ob.logger.ExternalChainWatcher.Warn().Msgf("ObserveERC20Deposited: multiple remote call events detected in tx %s", event.Raw.TxHash) + ob.logger.InTx.Warn().Msgf("ObserveERC20Deposited: multiple remote call events detected in tx %s", event.Raw.TxHash) continue } guard[event.Raw.TxHash.Hex()] = true @@ -1114,10 +1123,6 @@ func (ob *ChainClient) ObserveERC20Deposited(startBlock, toBlock uint64) uint64 // ObserverTSSReceive queries the incoming gas asset to TSS address and posts to zetabridge // returns the last block successfully scanned func (ob *ChainClient) ObserverTSSReceive(startBlock, toBlock uint64) uint64 { - if !ob.GetChainParams().IsSupported { - return startBlock - 1 // lastScanned - } - // query incoming gas asset for bn := startBlock; bn <= toBlock; bn++ { // post new block header (if any) to zetabridge and ignore error @@ -1128,14 +1133,14 @@ func (ob *ChainClient) ObserverTSSReceive(startBlock, toBlock uint64) uint64 { chains.IsHeaderSupportedEvmChain(ob.chain.ChainId) { // post block header for supported chains err := ob.postBlockHeader(toBlock) if err != nil { - ob.logger.ExternalChainWatcher.Error().Err(err).Msg("error posting block header") + ob.logger.InTx.Error().Err(err).Msg("error posting block header") } } // observe TSS received gas token in block 'bn' err := ob.ObserveTSSReceiveInBlock(bn) if err != nil { - ob.logger.ExternalChainWatcher.Error().Err(err).Msgf("ObserverTSSReceive: error observing TSS received token in block %d for chain %d", bn, ob.chain.ChainId) + ob.logger.InTx.Error().Err(err).Msgf("ObserverTSSReceive: error observing TSS received token in block %d for chain %d", bn, ob.chain.ChainId) return bn - 1 // we have to re-scan from this block next time } } @@ -1143,33 +1148,37 @@ func (ob *ChainClient) ObserverTSSReceive(startBlock, toBlock uint64) uint64 { return toBlock } +// WatchGasPrice watches evm chain for gas prices and post to zetacore func (ob *ChainClient) WatchGasPrice() { // report gas price right away as the ticker takes time to kick in err := ob.PostGasPrice() if err != nil { - ob.logger.WatchGasPrice.Error().Err(err).Msgf("PostGasPrice error for chain %d", ob.chain.ChainId) + ob.logger.GasPrice.Error().Err(err).Msgf("PostGasPrice error for chain %d", ob.chain.ChainId) } // start gas price ticker ticker, err := clienttypes.NewDynamicTicker(fmt.Sprintf("EVM_WatchGasPrice_%d", ob.chain.ChainId), ob.GetChainParams().GasPriceTicker) if err != nil { - ob.logger.WatchGasPrice.Error().Err(err).Msg("NewDynamicTicker error") + ob.logger.GasPrice.Error().Err(err).Msg("NewDynamicTicker error") return } - ob.logger.WatchGasPrice.Info().Msgf("WatchGasPrice started for chain %d with interval %d", + ob.logger.GasPrice.Info().Msgf("WatchGasPrice started for chain %d with interval %d", ob.chain.ChainId, ob.GetChainParams().GasPriceTicker) defer ticker.Stop() for { select { case <-ticker.C(): + if !ob.GetChainParams().IsSupported { + continue + } err = ob.PostGasPrice() if err != nil { - ob.logger.WatchGasPrice.Error().Err(err).Msgf("PostGasPrice error for chain %d", ob.chain.ChainId) + ob.logger.GasPrice.Error().Err(err).Msgf("PostGasPrice error for chain %d", ob.chain.ChainId) } - ticker.UpdateInterval(ob.GetChainParams().GasPriceTicker, ob.logger.WatchGasPrice) + ticker.UpdateInterval(ob.GetChainParams().GasPriceTicker, ob.logger.GasPrice) case <-ob.stop: - ob.logger.WatchGasPrice.Info().Msg("WatchGasPrice stopped") + ob.logger.GasPrice.Info().Msg("WatchGasPrice stopped") return } } @@ -1180,12 +1189,12 @@ func (ob *ChainClient) PostGasPrice() error { // GAS PRICE gasPrice, err := ob.evmClient.SuggestGasPrice(context.TODO()) if err != nil { - ob.logger.WatchGasPrice.Err(err).Msg("Err SuggestGasPrice:") + ob.logger.GasPrice.Err(err).Msg("Err SuggestGasPrice:") return err } blockNum, err := ob.evmClient.BlockNumber(context.TODO()) if err != nil { - ob.logger.WatchGasPrice.Err(err).Msg("Err Fetching Most recent Block : ") + ob.logger.GasPrice.Err(err).Msg("Err Fetching Most recent Block : ") return err } @@ -1194,7 +1203,7 @@ func (ob *ChainClient) PostGasPrice() error { zetaHash, err := ob.zetaClient.PostGasPrice(ob.chain, gasPrice.Uint64(), supply, blockNum) if err != nil { - ob.logger.WatchGasPrice.Err(err).Msg("PostGasPrice to zetabridge failed") + ob.logger.GasPrice.Err(err).Msg("PostGasPrice to zetabridge failed") return err } _ = zetaHash @@ -1203,7 +1212,7 @@ func (ob *ChainClient) PostGasPrice() error { } func (ob *ChainClient) BuildLastBlock() error { - logger := ob.logger.ChainLogger.With().Str("module", "BuildBlockIndex").Logger() + logger := ob.logger.Chain.With().Str("module", "BuildBlockIndex").Logger() envvar := ob.chain.ChainName.String() + "_SCAN_FROM" scanFromBlock := os.Getenv(envvar) if scanFromBlock != "" { @@ -1244,7 +1253,7 @@ func (ob *ChainClient) BuildReceiptsMap() error { logger := ob.logger var receipts []clienttypes.ReceiptSQLType if err := ob.db.Find(&receipts).Error; err != nil { - logger.ChainLogger.Error().Err(err).Msg("error iterating over db") + logger.Chain.Error().Err(err).Msg("error iterating over db") return err } for _, receipt := range receipts { @@ -1277,7 +1286,7 @@ func (ob *ChainClient) LoadDB(dbPath string, chain chains.Chain) error { &clienttypes.TransactionSQLType{}, &clienttypes.LastBlockSQLType{}) if err != nil { - ob.logger.ChainLogger.Error().Err(err).Msg("error migrating db") + ob.logger.Chain.Error().Err(err).Msg("error migrating db") return err } 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/inbounds.go b/zetaclient/evm/inbounds.go index 4459f361f5..3dbed486e9 100644 --- a/zetaclient/evm/inbounds.go +++ b/zetaclient/evm/inbounds.go @@ -8,6 +8,7 @@ import ( "strings" sdkmath "cosmossdk.io/math" + ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/onrik/ethrpc" "github.com/pkg/errors" @@ -15,44 +16,45 @@ import ( "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/constant" + "github.com/zeta-chain/zetacore/x/crosschain/types" "github.com/zeta-chain/zetacore/zetaclient/compliance" "github.com/zeta-chain/zetacore/zetaclient/config" clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" - - ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/zeta-chain/zetacore/pkg/constant" - "github.com/zeta-chain/zetacore/x/crosschain/types" "github.com/zeta-chain/zetacore/zetaclient/zetabridge" "golang.org/x/net/context" ) -// ExternalChainWatcherForNewInboundTrackerSuggestions At each tick, gets a list of Inbound tracker suggestions from zeta-core and tries to check if the in-tx was confirmed. +// WatchIntxTracker gets a list of Inbound tracker suggestions from zeta-core at each tick and tries to check if the in-tx was confirmed. // If it was, it tries to broadcast the confirmation vote. If this zeta client has previously broadcast the vote, the tx would be rejected -func (ob *ChainClient) ExternalChainWatcherForNewInboundTrackerSuggestions() { +func (ob *ChainClient) WatchIntxTracker() { ticker, err := clienttypes.NewDynamicTicker( - fmt.Sprintf("EVM_ExternalChainWatcher_InboundTrackerSuggestions_%d", ob.chain.ChainId), + fmt.Sprintf("EVM_WatchIntxTracker_%d", ob.chain.ChainId), ob.GetChainParams().InTxTicker, ) if err != nil { - ob.logger.ExternalChainWatcher.Err(err).Msg("error creating ticker") + ob.logger.InTx.Err(err).Msg("error creating ticker") return } defer ticker.Stop() - ob.logger.ExternalChainWatcher.Info().Msg("ExternalChainWatcher for inboundTrackerSuggestions started") + ob.logger.InTx.Info().Msgf("Intx tracker watcher started for chain %d", ob.chain.ChainId) for { select { case <-ticker.C(): if flags := ob.coreContext.GetCrossChainFlags(); !flags.IsInboundEnabled { continue } + if !ob.GetChainParams().IsSupported { + continue + } err := ob.ObserveIntxTrackers() if err != nil { - ob.logger.ExternalChainWatcher.Err(err).Msg("ObserveTrackerSuggestions error") + ob.logger.InTx.Err(err).Msg("ObserveTrackerSuggestions error") } - ticker.UpdateInterval(ob.GetChainParams().InTxTicker, ob.logger.ExternalChainWatcher) + ticker.UpdateInterval(ob.GetChainParams().InTxTicker, ob.logger.InTx) case <-ob.stop: - ob.logger.ExternalChainWatcher.Info().Msg("ExternalChainWatcher for inboundTrackerSuggestions stopped") + ob.logger.InTx.Info().Msg("ExternalChainWatcher for inboundTrackerSuggestions stopped") return } } @@ -74,7 +76,7 @@ func (ob *ChainClient) ObserveIntxTrackers() error { if err != nil { return errors.Wrapf(err, "error getting receipt for intx %s chain %d", tracker.TxHash, ob.chain.ChainId) } - ob.logger.ExternalChainWatcher.Info().Msgf("checking tracker for intx %s chain %d", tracker.TxHash, ob.chain.ChainId) + ob.logger.InTx.Info().Msgf("checking tracker for intx %s chain %d", tracker.TxHash, ob.chain.ChainId) // check and vote on inbound tx switch tracker.CoinType { @@ -116,7 +118,7 @@ func (ob *ChainClient) CheckAndVoteInboundTokenZeta(tx *ethrpc.Transaction, rece if err == nil { msg = ob.BuildInboundVoteMsgForZetaSentEvent(event) } else { - ob.logger.ExternalChainWatcher.Error().Err(err).Msgf("CheckEvmTxLog error on intx %s chain %d", tx.Hash, ob.chain.ChainId) + ob.logger.InTx.Error().Err(err).Msgf("CheckEvmTxLog error on intx %s chain %d", tx.Hash, ob.chain.ChainId) return "", err } break // only one event is allowed per tx @@ -124,7 +126,7 @@ func (ob *ChainClient) CheckAndVoteInboundTokenZeta(tx *ethrpc.Transaction, rece } if msg == nil { // no event, restricted tx, etc. - ob.logger.ExternalChainWatcher.Info().Msgf("no ZetaSent event found for intx %s chain %d", tx.Hash, ob.chain.ChainId) + ob.logger.InTx.Info().Msgf("no ZetaSent event found for intx %s chain %d", tx.Hash, ob.chain.ChainId) return "", nil } if vote { @@ -157,7 +159,7 @@ func (ob *ChainClient) CheckAndVoteInboundTokenERC20(tx *ethrpc.Transaction, rec if err == nil { msg = ob.BuildInboundVoteMsgForDepositedEvent(zetaDeposited, sender) } else { - ob.logger.ExternalChainWatcher.Error().Err(err).Msgf("CheckEvmTxLog error on intx %s chain %d", tx.Hash, ob.chain.ChainId) + ob.logger.InTx.Error().Err(err).Msgf("CheckEvmTxLog error on intx %s chain %d", tx.Hash, ob.chain.ChainId) return "", err } break // only one event is allowed per tx @@ -165,7 +167,7 @@ func (ob *ChainClient) CheckAndVoteInboundTokenERC20(tx *ethrpc.Transaction, rec } if msg == nil { // no event, donation, restricted tx, etc. - ob.logger.ExternalChainWatcher.Info().Msgf("no Deposited event found for intx %s chain %d", tx.Hash, ob.chain.ChainId) + ob.logger.InTx.Info().Msgf("no Deposited event found for intx %s chain %d", tx.Hash, ob.chain.ChainId) return "", nil } if vote { @@ -193,7 +195,7 @@ func (ob *ChainClient) CheckAndVoteInboundTokenGas(tx *ethrpc.Transaction, recei msg := ob.BuildInboundVoteMsgForTokenSentToTSS(tx, sender, receipt.BlockNumber.Uint64()) if msg == nil { // donation, restricted tx, etc. - ob.logger.ExternalChainWatcher.Info().Msgf("no vote message built for intx %s chain %d", tx.Hash, ob.chain.ChainId) + ob.logger.InTx.Info().Msgf("no vote message built for intx %s chain %d", tx.Hash, ob.chain.ChainId) return "", nil } if vote { @@ -208,12 +210,12 @@ func (ob *ChainClient) PostVoteInbound(msg *types.MsgVoteOnObservedInboundTx, co chainID := ob.chain.ChainId zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(zetabridge.PostVoteInboundGasLimit, retryGasLimit, msg) if err != nil { - ob.logger.ExternalChainWatcher.Err(err).Msgf("intx detected: error posting vote for chain %d token %s intx %s", chainID, coinType, txHash) + ob.logger.InTx.Err(err).Msgf("intx detected: error posting vote for chain %d token %s intx %s", chainID, coinType, txHash) return "", err } else if zetaHash != "" { - ob.logger.ExternalChainWatcher.Info().Msgf("intx detected: chain %d token %s intx %s vote %s ballot %s", chainID, coinType, txHash, zetaHash, ballot) + ob.logger.InTx.Info().Msgf("intx detected: chain %d token %s intx %s vote %s ballot %s", chainID, coinType, txHash, zetaHash, ballot) } else { - ob.logger.ExternalChainWatcher.Info().Msgf("intx detected: chain %d token %s intx %s already voted on ballot %s", chainID, coinType, txHash, ballot) + ob.logger.InTx.Info().Msgf("intx detected: chain %d token %s intx %s already voted on ballot %s", chainID, coinType, txHash, ballot) } return ballot, err } @@ -233,18 +235,18 @@ func (ob *ChainClient) BuildInboundVoteMsgForDepositedEvent(event *erc20custody. maybeReceiver = parsedAddress.Hex() } if config.ContainRestrictedAddress(sender.Hex(), clienttypes.BytesToEthHex(event.Recipient), maybeReceiver) { - compliance.PrintComplianceLog(ob.logger.ExternalChainWatcher, ob.logger.Compliance, + compliance.PrintComplianceLog(ob.logger.InTx, ob.logger.Compliance, false, ob.chain.ChainId, event.Raw.TxHash.Hex(), sender.Hex(), clienttypes.BytesToEthHex(event.Recipient), "ERC20") return nil } // donation check if bytes.Equal(event.Message, []byte(constant.DonationMessage)) { - ob.logger.ExternalChainWatcher.Info().Msgf("thank you rich folk for your donation! tx %s chain %d", event.Raw.TxHash.Hex(), ob.chain.ChainId) + ob.logger.InTx.Info().Msgf("thank you rich folk for your donation! tx %s chain %d", event.Raw.TxHash.Hex(), ob.chain.ChainId) return nil } message := hex.EncodeToString(event.Message) - ob.logger.ExternalChainWatcher.Info().Msgf("ERC20CustodyDeposited inTx detected on chain %d tx %s block %d from %s value %s message %s", + ob.logger.InTx.Info().Msgf("ERC20CustodyDeposited inTx detected on chain %d tx %s block %d from %s value %s message %s", ob.chain.ChainId, event.Raw.TxHash.Hex(), event.Raw.BlockNumber, sender.Hex(), event.Amount.String(), message) return zetabridge.GetInBoundVoteMessage( @@ -269,7 +271,7 @@ func (ob *ChainClient) BuildInboundVoteMsgForDepositedEvent(event *erc20custody. func (ob *ChainClient) BuildInboundVoteMsgForZetaSentEvent(event *zetaconnector.ZetaConnectorNonEthZetaSent) *types.MsgVoteOnObservedInboundTx { destChain := chains.GetChainFromChainID(event.DestinationChainId.Int64()) if destChain == nil { - ob.logger.ExternalChainWatcher.Warn().Msgf("chain id not supported %d", event.DestinationChainId.Int64()) + ob.logger.InTx.Warn().Msgf("chain id not supported %d", event.DestinationChainId.Int64()) return nil } destAddr := clienttypes.BytesToEthHex(event.DestinationAddress) @@ -277,7 +279,7 @@ func (ob *ChainClient) BuildInboundVoteMsgForZetaSentEvent(event *zetaconnector. // compliance check sender := event.ZetaTxSenderAddress.Hex() if config.ContainRestrictedAddress(sender, destAddr, event.SourceTxOriginAddress.Hex()) { - compliance.PrintComplianceLog(ob.logger.ExternalChainWatcher, ob.logger.Compliance, + compliance.PrintComplianceLog(ob.logger.InTx, ob.logger.Compliance, false, ob.chain.ChainId, event.Raw.TxHash.Hex(), sender, destAddr, "Zeta") return nil } @@ -285,17 +287,17 @@ func (ob *ChainClient) BuildInboundVoteMsgForZetaSentEvent(event *zetaconnector. if !destChain.IsZetaChain() { paramsDest, found := ob.coreContext.GetEVMChainParams(destChain.ChainId) if !found { - ob.logger.ExternalChainWatcher.Warn().Msgf("chain id not present in EVMChainParams %d", event.DestinationChainId.Int64()) + ob.logger.InTx.Warn().Msgf("chain id not present in EVMChainParams %d", event.DestinationChainId.Int64()) return nil } if strings.EqualFold(destAddr, paramsDest.ZetaTokenContractAddress) { - ob.logger.ExternalChainWatcher.Warn().Msgf("potential attack attempt: %s destination address is ZETA token contract address %s", destChain, destAddr) + ob.logger.InTx.Warn().Msgf("potential attack attempt: %s destination address is ZETA token contract address %s", destChain, destAddr) return nil } } message := base64.StdEncoding.EncodeToString(event.Message) - ob.logger.ExternalChainWatcher.Info().Msgf("ZetaSent inTx detected on chain %d tx %s block %d from %s value %s message %s", + ob.logger.InTx.Info().Msgf("ZetaSent inTx detected on chain %d tx %s block %d from %s value %s message %s", ob.chain.ChainId, event.Raw.TxHash.Hex(), event.Raw.BlockNumber, sender, event.ZetaValueAndGas.String(), message) return zetabridge.GetInBoundVoteMessage( @@ -327,7 +329,7 @@ func (ob *ChainClient) BuildInboundVoteMsgForTokenSentToTSS(tx *ethrpc.Transacti maybeReceiver = parsedAddress.Hex() } if config.ContainRestrictedAddress(sender.Hex(), maybeReceiver) { - compliance.PrintComplianceLog(ob.logger.ExternalChainWatcher, ob.logger.Compliance, + compliance.PrintComplianceLog(ob.logger.InTx, ob.logger.Compliance, false, ob.chain.ChainId, tx.Hash, sender.Hex(), sender.Hex(), "Gas") return nil } @@ -336,10 +338,10 @@ func (ob *ChainClient) BuildInboundVoteMsgForTokenSentToTSS(tx *ethrpc.Transacti // #nosec G703 err is already checked data, _ := hex.DecodeString(message) if bytes.Equal(data, []byte(constant.DonationMessage)) { - ob.logger.ExternalChainWatcher.Info().Msgf("thank you rich folk for your donation! tx %s chain %d", tx.Hash, ob.chain.ChainId) + ob.logger.InTx.Info().Msgf("thank you rich folk for your donation! tx %s chain %d", tx.Hash, ob.chain.ChainId) return nil } - ob.logger.ExternalChainWatcher.Info().Msgf("TSS inTx detected on chain %d tx %s block %d from %s value %s message %s", + ob.logger.InTx.Info().Msgf("TSS inTx detected on chain %d tx %s block %d from %s value %s message %s", ob.chain.ChainId, tx.Hash, blockNumber, sender.Hex(), tx.Value.String(), message) return zetabridge.GetInBoundVoteMessage( 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/interfaces/interfaces.go b/zetaclient/interfaces/interfaces.go index 75242ab17d..1d48fc149c 100644 --- a/zetaclient/interfaces/interfaces.go +++ b/zetaclient/interfaces/interfaces.go @@ -43,7 +43,7 @@ type ChainClient interface { SetChainParams(observertypes.ChainParams) GetChainParams() observertypes.ChainParams GetTxID(nonce uint64) string - ExternalChainWatcherForNewInboundTrackerSuggestions() + WatchIntxTracker() } // ChainSigner is the interface to sign transactions for a chain 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/testutils/stub/chain_client.go b/zetaclient/testutils/stub/chain_client.go index 642de62792..f5a5368511 100644 --- a/zetaclient/testutils/stub/chain_client.go +++ b/zetaclient/testutils/stub/chain_client.go @@ -45,7 +45,7 @@ func (s *EVMClient) GetTxID(_ uint64) string { return "" } -func (s *EVMClient) ExternalChainWatcherForNewInboundTrackerSuggestions() { +func (s *EVMClient) WatchIntxTracker() { } // ---------------------------------------------------------------------------- @@ -86,5 +86,5 @@ func (s *BTCClient) GetTxID(_ uint64) string { return "" } -func (s *BTCClient) ExternalChainWatcherForNewInboundTrackerSuggestions() { +func (s *BTCClient) WatchIntxTracker() { } 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 { diff --git a/zetaclient/zetabridge/zetacore_bridge.go b/zetaclient/zetabridge/zetacore_bridge.go index 684092c744..d8a29fa665 100644 --- a/zetaclient/zetabridge/zetacore_bridge.go +++ b/zetaclient/zetabridge/zetacore_bridge.go @@ -220,14 +220,15 @@ func (b *ZetaCoreBridge) UpdateZetaCoreContext(coreContext *corecontext.ZetaCore var newBTCParams *observertypes.ChainParams // check and update chain params for each chain + sampledLogger := b.logger.Sample(&zerolog.BasicSampler{N: 10}) for _, chainParam := range chainParams { - err := observertypes.ValidateChainParams(chainParam) - if err != nil { - b.logger.Warn().Err(err).Msgf("Invalid chain params for chain %d", chainParam.ChainId) + if !chainParam.GetIsSupported() { + sampledLogger.Info().Msgf("Chain %d is not supported yet", chainParam.ChainId) continue } - if !chainParam.GetIsSupported() { - b.logger.Info().Msgf("Chain %d is not supported yet", chainParam.ChainId) + err := observertypes.ValidateChainParams(chainParam) + if err != nil { + sampledLogger.Warn().Err(err).Msgf("Invalid chain params for chain %d", chainParam.ChainId) continue } if chains.IsBitcoinChain(chainParam.ChainId) { @@ -237,12 +238,12 @@ func (b *ZetaCoreBridge) UpdateZetaCoreContext(coreContext *corecontext.ZetaCore } } - supporteChains, err := b.GetSupportedChains() + supportedChains, err := b.GetSupportedChains() if err != nil { return err } - newChains := make([]chains.Chain, len(supporteChains)) - for i, chain := range supporteChains { + newChains := make([]chains.Chain, len(supportedChains)) + for i, chain := range supportedChains { newChains[i] = *chain } keyGen, err := b.GetKeyGen() diff --git a/zetaclient/zetacore_observer.go b/zetaclient/zetacore_observer.go index e31766b3be..3d9006110f 100644 --- a/zetaclient/zetacore_observer.go +++ b/zetaclient/zetacore_observer.go @@ -5,21 +5,18 @@ import ( "math" "time" + sdkmath "cosmossdk.io/math" ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/rs/zerolog" "github.com/zeta-chain/zetacore/pkg/chains" + "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" "github.com/zeta-chain/zetacore/zetaclient/bitcoin" corecontext "github.com/zeta-chain/zetacore/zetaclient/core_context" "github.com/zeta-chain/zetacore/zetaclient/interfaces" - "github.com/zeta-chain/zetacore/zetaclient/outtxprocessor" - - observertypes "github.com/zeta-chain/zetacore/x/observer/types" - - sdkmath "cosmossdk.io/math" - - "github.com/rs/zerolog" - "github.com/zeta-chain/zetacore/x/crosschain/types" "github.com/zeta-chain/zetacore/zetaclient/metrics" + "github.com/zeta-chain/zetacore/zetaclient/outtxprocessor" ) const ( @@ -155,6 +152,10 @@ func (co *CoreObserver) startCctxScheduler(appContext *appcontext.AppContext) { co.logger.ZetaChainWatcher.Error().Err(err).Msgf("startCctxScheduler: getTargetChainOb failed for chain %d", c.ChainId) continue } + if !ob.GetChainParams().IsSupported { + co.logger.ZetaChainWatcher.Info().Msgf("startCctxScheduler: chain %d is not supported", c.ChainId) + continue + } cctxList, totalPending, err := co.bridge.ListPendingCctx(c.ChainId) if err != nil {