From 64b898cd021c805b8a5e7ee41bee0c0c836afa7b Mon Sep 17 00:00:00 2001 From: Lucas Bertrand Date: Wed, 3 Apr 2024 15:07:07 +0200 Subject: [PATCH 01/10] refactor: move TSS vote message from `crosschain` to `observer` (#1966) * initialize proto * initialize message * fix event emit * remove crosschain vote tss * add codec registration * fix client * changelog * add tests to tss vote * add status check in message type * fix ante tests * some fixes in authz * use parser for status * regen docs * fix tests * update codecov file * remove slashes in codecov * codecov --- app/ante/ante.go | 2 +- app/ante/ante_test.go | 22 +- changelog.md | 5 +- cmd/zetaclientd/debug.go | 17 +- codecov.yml | 30 +- docs/cli/zetacored/zetacored_tx_crosschain.md | 1 - docs/cli/zetacored/zetacored_tx_observer.md | 1 + ...r.md => zetacored_tx_observer_vote-tss.md} | 10 +- docs/openapi/openapi.swagger.yaml | 11 +- docs/spec/crosschain/messages.md | 22 - docs/spec/crosschain/overview.md | 4 +- docs/spec/observer/messages.md | 22 + pkg/chains/status.go | 18 + pkg/chains/status_test.go | 47 ++ proto/crosschain/tx.proto | 11 +- proto/observer/tx.proto | 15 + testutil/sample/observer.go | 4 +- typescript/crosschain/tx_pb.d.ts | 60 +- typescript/observer/tx_pb.d.ts | 74 ++ x/crosschain/client/cli/cli_cctx.go | 10 +- x/crosschain/client/cli/cli_tss.go | 41 - x/crosschain/client/cli/tx.go | 1 - x/crosschain/keeper/msg_server_tss_voter.go | 120 --- x/crosschain/types/authz.go | 2 +- x/crosschain/types/authz_test.go | 2 +- x/crosschain/types/codec.go | 2 - x/crosschain/types/message_tss_voter.go | 57 -- x/crosschain/types/message_tss_voter_test.go | 97 --- x/crosschain/types/tx.pb.go | 678 +++-------------- x/observer/client/cli/tx.go | 1 + x/observer/client/cli/tx_vote_tss.go | 51 ++ x/observer/keeper/msg_server_vote_tss.go | 127 ++++ x/observer/keeper/msg_server_vote_tss_test.go | 241 ++++++ x/observer/keeper/utils.go | 4 +- x/observer/types/ballot.go | 4 +- x/observer/types/codec.go | 10 +- x/observer/types/message_vote_tss.go | 63 ++ x/observer/types/message_vote_tss_test.go | 105 +++ x/observer/types/tx.pb.go | 711 ++++++++++++++++-- zetaclient/bitcoin/bitcoin_client.go | 26 +- zetaclient/evm/evm_client.go | 23 +- zetaclient/zetabridge/tx.go | 29 +- 42 files changed, 1634 insertions(+), 1147 deletions(-) rename docs/cli/zetacored/{zetacored_tx_crosschain_create-tss-voter.md => zetacored_tx_observer_vote-tss.md} (91%) create mode 100644 pkg/chains/status.go create mode 100644 pkg/chains/status_test.go delete mode 100644 x/crosschain/keeper/msg_server_tss_voter.go delete mode 100644 x/crosschain/types/message_tss_voter.go delete mode 100644 x/crosschain/types/message_tss_voter_test.go create mode 100644 x/observer/client/cli/tx_vote_tss.go create mode 100644 x/observer/keeper/msg_server_vote_tss.go create mode 100644 x/observer/keeper/msg_server_vote_tss_test.go create mode 100644 x/observer/types/message_vote_tss.go create mode 100644 x/observer/types/message_vote_tss_test.go 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 04181421f9..055e9f9283 100644 --- a/changelog.md +++ b/changelog.md @@ -8,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` @@ -21,9 +24,9 @@ * [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 * [1789](https://github.com/zeta-chain/node/issues/1789) - block cross-chain transactions that involve restricted addresses diff --git a/cmd/zetaclientd/debug.go b/cmd/zetaclientd/debug.go index 76450875cb..32cb32c772 100644 --- a/cmd/zetaclientd/debug.go +++ b/cmd/zetaclientd/debug.go @@ -8,25 +8,24 @@ import ( "strings" "sync" + "github.com/btcsuite/btcd/rpcclient" + sdk "github.com/cosmos/cosmos-sdk/types" + ethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" "github.com/onrik/ethrpc" + "github.com/rs/zerolog" + "github.com/spf13/cobra" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" + "github.com/zeta-chain/zetacore/testutil/sample" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/bitcoin" + "github.com/zeta-chain/zetacore/zetaclient/config" corecontext "github.com/zeta-chain/zetacore/zetaclient/core_context" "github.com/zeta-chain/zetacore/zetaclient/evm" "github.com/zeta-chain/zetacore/zetaclient/keys" "github.com/zeta-chain/zetacore/zetaclient/metrics" "github.com/zeta-chain/zetacore/zetaclient/zetabridge" - - "github.com/btcsuite/btcd/rpcclient" - sdk "github.com/cosmos/cosmos-sdk/types" - ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/rs/zerolog" - "github.com/spf13/cobra" - "github.com/zeta-chain/zetacore/testutil/sample" - observertypes "github.com/zeta-chain/zetacore/x/observer/types" - "github.com/zeta-chain/zetacore/zetaclient/config" ) var debugArgs = debugArguments{} diff --git a/codecov.yml b/codecov.yml index 93d974911b..1a9ba2063d 100644 --- a/codecov.yml +++ b/codecov.yml @@ -38,7 +38,7 @@ flags: - "common/" ignore: - - "x/**/client/" + - "x/**/client/**/*" - "x/**/keeper/keeper.go" - "x/**/keeper/msg_server.go" - "x/**/keeper/grpc_query_params.go" @@ -50,7 +50,7 @@ ignore: - "x/**/types/expected_keepers.go" - "x/**/module.go" - "x/**/module_simulation.go" - - "x/**/simulation/" + - "x/**/simulation/**/*" - "**/*.proto" - "**/*.md" - "**/*.yml" @@ -58,16 +58,16 @@ ignore: - "**/*.pb.go" - "**/*.pb.gw.go" - "**/*.json" - - ".github/" - - "app/" - - "cmd/" - - "contrib/" - - "docs/" - - "rpc/" - - "proto/" - - "scripts/" - - "server/" - - "testutil/" - - "testutils/" - - "errors/" - - "typescript/" \ No newline at end of file + - ".github/**/*" + - "app/**/*" + - "cmd/**/*" + - "contrib/**/*" + - "docs/**/*" + - "rpc/**/*" + - "proto/**/*" + - "scripts/**/*" + - "server/**/*" + - "testutil/**/*" + - "testutils/**/*" + - "errors/**/*" + - "typescript/**/*" \ No newline at end of file diff --git a/docs/cli/zetacored/zetacored_tx_crosschain.md b/docs/cli/zetacored/zetacored_tx_crosschain.md index 5e61146b17..40582875aa 100644 --- a/docs/cli/zetacored/zetacored_tx_crosschain.md +++ b/docs/cli/zetacored/zetacored_tx_crosschain.md @@ -29,7 +29,6 @@ zetacored tx crosschain [flags] * [zetacored tx crosschain add-to-in-tx-tracker](zetacored_tx_crosschain_add-to-in-tx-tracker.md) - Add a in-tx-tracker Use 0:Zeta,1:Gas,2:ERC20 * [zetacored tx crosschain add-to-out-tx-tracker](zetacored_tx_crosschain_add-to-out-tx-tracker.md) - Add a out-tx-tracker -* [zetacored tx crosschain create-tss-voter](zetacored_tx_crosschain_create-tss-voter.md) - Create a new TSSVoter * [zetacored tx crosschain gas-price-voter](zetacored_tx_crosschain_gas-price-voter.md) - Broadcast message gasPriceVoter * [zetacored tx crosschain inbound-voter](zetacored_tx_crosschain_inbound-voter.md) - Broadcast message sendVoter * [zetacored tx crosschain migrate-tss-funds](zetacored_tx_crosschain_migrate-tss-funds.md) - Migrate TSS funds to the latest TSS address diff --git a/docs/cli/zetacored/zetacored_tx_observer.md b/docs/cli/zetacored/zetacored_tx_observer.md index de1145c512..ec0e245fe0 100644 --- a/docs/cli/zetacored/zetacored_tx_observer.md +++ b/docs/cli/zetacored/zetacored_tx_observer.md @@ -34,4 +34,5 @@ zetacored tx observer [flags] * [zetacored tx observer update-crosschain-flags](zetacored_tx_observer_update-crosschain-flags.md) - Update crosschain flags * [zetacored tx observer update-keygen](zetacored_tx_observer_update-keygen.md) - command to update the keygen block via a group proposal * [zetacored tx observer update-observer](zetacored_tx_observer_update-observer.md) - Broadcast message add-observer +* [zetacored tx observer vote-tss](zetacored_tx_observer_vote-tss.md) - Vote for a new TSS creation diff --git a/docs/cli/zetacored/zetacored_tx_crosschain_create-tss-voter.md b/docs/cli/zetacored/zetacored_tx_observer_vote-tss.md similarity index 91% rename from docs/cli/zetacored/zetacored_tx_crosschain_create-tss-voter.md rename to docs/cli/zetacored/zetacored_tx_observer_vote-tss.md index b6932a3cd8..388c8c3793 100644 --- a/docs/cli/zetacored/zetacored_tx_crosschain_create-tss-voter.md +++ b/docs/cli/zetacored/zetacored_tx_observer_vote-tss.md @@ -1,9 +1,9 @@ -# tx crosschain create-tss-voter +# tx observer vote-tss -Create a new TSSVoter +Vote for a new TSS creation ``` -zetacored tx crosschain create-tss-voter [pubkey] [keygenBlock] [status] [flags] +zetacored tx observer vote-tss [pubkey] [keygen-block] [status] [flags] ``` ### Options @@ -21,7 +21,7 @@ zetacored tx crosschain create-tss-voter [pubkey] [keygenBlock] [status] [flags] --gas-adjustment float adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored (default 1) --gas-prices string Gas prices in decimal format to determine the transaction fee (e.g. 0.1uatom) --generate-only Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase only accessed when providing a key name) - -h, --help help for create-tss-voter + -h, --help help for vote-tss --keyring-backend string Select keyring's backend (os|file|kwallet|pass|test|memory) --keyring-dir string The client Keyring directory; if omitted, the default 'home' directory will be used --ledger Use a connected Ledger device @@ -48,5 +48,5 @@ zetacored tx crosschain create-tss-voter [pubkey] [keygenBlock] [status] [flags] ### SEE ALSO -* [zetacored tx crosschain](zetacored_tx_crosschain.md) - crosschain transactions subcommands +* [zetacored tx observer](zetacored_tx_observer.md) - observer transactions subcommands diff --git a/docs/openapi/openapi.swagger.yaml b/docs/openapi/openapi.swagger.yaml index 1c59768854..2dd9812b64 100644 --- a/docs/openapi/openapi.swagger.yaml +++ b/docs/openapi/openapi.swagger.yaml @@ -53701,8 +53701,6 @@ definitions: is_removed: type: boolean title: if the tx was removed from the tracker due to no pending cctx - crosschainMsgCreateTSSVoterResponse: - type: object crosschainMsgGasPriceVoterResponse: type: object crosschainMsgMigrateTssFundsResponse: @@ -54324,6 +54322,15 @@ definitions: type: object observerMsgUpdateObserverResponse: type: object + observerMsgVoteTSSResponse: + type: object + properties: + ballot_created: + type: boolean + vote_finalized: + type: boolean + keygen_success: + type: boolean observerNode: type: object properties: diff --git a/docs/spec/crosschain/messages.md b/docs/spec/crosschain/messages.md index 506ca0f94c..e64f4bb99a 100644 --- a/docs/spec/crosschain/messages.md +++ b/docs/spec/crosschain/messages.md @@ -236,28 +236,6 @@ message MsgMigrateTssFunds { } ``` -## MsgCreateTSSVoter - -CreateTSSVoter votes on creating a TSS key and recording the information about it (public -key, participant and operator addresses, finalized and keygen heights). - -If the vote passes, the information about the TSS key is recorded on chain -and the status of the keygen is set to "success". - -Fails if the keygen does not exist, the keygen has been already -completed, or the keygen has failed. - -Only node accounts are authorized to broadcast this message. - -```proto -message MsgCreateTSSVoter { - string creator = 1; - string tss_pubkey = 2; - int64 keyGenZetaHeight = 3; - chains.ReceiveStatus status = 4; -} -``` - ## MsgAbortStuckCCTX AbortStuckCCTX aborts a stuck CCTX diff --git a/docs/spec/crosschain/overview.md b/docs/spec/crosschain/overview.md index 32e1035e41..e2c1780922 100644 --- a/docs/spec/crosschain/overview.md +++ b/docs/spec/crosschain/overview.md @@ -69,8 +69,8 @@ status is changed to final. ## Permissions | Message | Admin policy account | Observer validator | -| --------------------------- | -------------------- | ------------------ | -| MsgCreateTSSVoter | | ✅ | +|-----------------------------| -------------------- | ------------------ | +| MsgVoteTSS | | ✅ | | MsgGasPriceVoter | | ✅ | | MsgVoteOnObservedOutboundTx | | ✅ | | MsgVoteOnObservedInboundTx | | ✅ | diff --git a/docs/spec/observer/messages.md b/docs/spec/observer/messages.md index c2a6039dbe..82c6423757 100644 --- a/docs/spec/observer/messages.md +++ b/docs/spec/observer/messages.md @@ -122,3 +122,25 @@ message MsgResetChainNonces { } ``` +## MsgVoteTSS + +VoteTSS votes on creating a TSS key and recording the information about it (public +key, participant and operator addresses, finalized and keygen heights). + +If the vote passes, the information about the TSS key is recorded on chain +and the status of the keygen is set to "success". + +Fails if the keygen does not exist, the keygen has been already +completed, or the keygen has failed. + +Only node accounts are authorized to broadcast this message. + +```proto +message MsgVoteTSS { + string creator = 1; + string tss_pubkey = 2; + int64 keygen_zeta_height = 3; + chains.ReceiveStatus status = 4; +} +``` + diff --git a/pkg/chains/status.go b/pkg/chains/status.go new file mode 100644 index 0000000000..25140267f7 --- /dev/null +++ b/pkg/chains/status.go @@ -0,0 +1,18 @@ +package chains + +import "errors" + +// ReceiveStatusFromString returns a ReceiveStatus from a string using in CLI +// 0 for success, 1 for failed +// TODO: remove "receive" naming ans use outbound +// https://github.com/zeta-chain/node/issues/1797 +func ReceiveStatusFromString(str string) (ReceiveStatus, error) { + switch str { + case "0": + return ReceiveStatus_Success, nil + case "1": + return ReceiveStatus_Failed, nil + default: + return ReceiveStatus(0), errors.New("wrong status, must be 0 for success or 1 for failed") + } +} diff --git a/pkg/chains/status_test.go b/pkg/chains/status_test.go new file mode 100644 index 0000000000..65f5e7e319 --- /dev/null +++ b/pkg/chains/status_test.go @@ -0,0 +1,47 @@ +package chains_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" +) + +func TestReceiveStatusFromString(t *testing.T) { + tests := []struct { + name string + str string + want chains.ReceiveStatus + wantErr bool + }{ + { + name: "success", + str: "0", + want: chains.ReceiveStatus_Success, + wantErr: false, + }, + { + name: "failed", + str: "1", + want: chains.ReceiveStatus_Failed, + wantErr: false, + }, + { + name: "wrong status", + str: "2", + want: chains.ReceiveStatus(0), + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := chains.ReceiveStatusFromString(tt.str) + if tt.wantErr { + require.Error(t, err) + } else if got != tt.want { + require.NoError(t, err) + require.Equal(t, tt.want, got) + } + }) + } +} diff --git a/proto/crosschain/tx.proto b/proto/crosschain/tx.proto index d6e4fc093b..7e668d6325 100644 --- a/proto/crosschain/tx.proto +++ b/proto/crosschain/tx.proto @@ -20,21 +20,11 @@ service Msg { rpc WhitelistERC20(MsgWhitelistERC20) returns (MsgWhitelistERC20Response); rpc UpdateTssAddress(MsgUpdateTssAddress) returns (MsgUpdateTssAddressResponse); rpc MigrateTssFunds(MsgMigrateTssFunds) returns (MsgMigrateTssFundsResponse); - rpc CreateTSSVoter(MsgCreateTSSVoter) returns (MsgCreateTSSVoterResponse); rpc AbortStuckCCTX(MsgAbortStuckCCTX) returns (MsgAbortStuckCCTXResponse); rpc RefundAbortedCCTX(MsgRefundAbortedCCTX) returns (MsgRefundAbortedCCTXResponse); } -message MsgCreateTSSVoter { - string creator = 1; - string tss_pubkey = 2; - int64 keyGenZetaHeight = 3; - chains.ReceiveStatus status = 4; -} - -message MsgCreateTSSVoterResponse {} - message MsgMigrateTssFunds { string creator = 1; int64 chain_id = 2; @@ -43,6 +33,7 @@ message MsgMigrateTssFunds { (gogoproto.nullable) = false ]; } + message MsgMigrateTssFundsResponse {} message MsgUpdateTssAddress { diff --git a/proto/observer/tx.proto b/proto/observer/tx.proto index 8245827f16..f6b9424b97 100644 --- a/proto/observer/tx.proto +++ b/proto/observer/tx.proto @@ -8,6 +8,7 @@ import "observer/observer.proto"; import "observer/params.proto"; import "observer/pending_nonces.proto"; import "observer/tss.proto"; +import "pkg/chains/chains.proto"; import "pkg/proofs/proofs.proto"; option go_package = "github.com/zeta-chain/zetacore/x/observer/types"; @@ -23,6 +24,7 @@ service Msg { rpc UpdateKeygen(MsgUpdateKeygen) returns (MsgUpdateKeygenResponse); rpc AddBlockHeader(MsgAddBlockHeader) returns (MsgAddBlockHeaderResponse); rpc ResetChainNonces(MsgResetChainNonces) returns (MsgResetChainNoncesResponse); + rpc VoteTSS(MsgVoteTSS) returns (MsgVoteTSSResponse); } message MsgUpdateObserver { @@ -98,3 +100,16 @@ message MsgResetChainNonces { } message MsgResetChainNoncesResponse {} + +message MsgVoteTSS { + string creator = 1; + string tss_pubkey = 2; + int64 keygen_zeta_height = 3; + chains.ReceiveStatus status = 4; +} + +message MsgVoteTSSResponse { + bool ballot_created = 1; + bool vote_finalized = 2; + bool keygen_success = 3; +} diff --git a/testutil/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/typescript/crosschain/tx_pb.d.ts b/typescript/crosschain/tx_pb.d.ts index b1900d7c66..eec35c2dc8 100644 --- a/typescript/crosschain/tx_pb.d.ts +++ b/typescript/crosschain/tx_pb.d.ts @@ -5,67 +5,9 @@ import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; import { Message, proto3 } from "@bufbuild/protobuf"; -import type { ReceiveStatus } from "../pkg/chains/chains_pb.js"; import type { CoinType } from "../pkg/coin/coin_pb.js"; import type { Proof } from "../pkg/proofs/proofs_pb.js"; - -/** - * @generated from message zetachain.zetacore.crosschain.MsgCreateTSSVoter - */ -export declare class MsgCreateTSSVoter extends Message { - /** - * @generated from field: string creator = 1; - */ - creator: string; - - /** - * @generated from field: string tss_pubkey = 2; - */ - tssPubkey: string; - - /** - * @generated from field: int64 keyGenZetaHeight = 3; - */ - keyGenZetaHeight: bigint; - - /** - * @generated from field: chains.ReceiveStatus status = 4; - */ - status: ReceiveStatus; - - constructor(data?: PartialMessage); - - static readonly runtime: typeof proto3; - static readonly typeName = "zetachain.zetacore.crosschain.MsgCreateTSSVoter"; - static readonly fields: FieldList; - - static fromBinary(bytes: Uint8Array, options?: Partial): MsgCreateTSSVoter; - - static fromJson(jsonValue: JsonValue, options?: Partial): MsgCreateTSSVoter; - - static fromJsonString(jsonString: string, options?: Partial): MsgCreateTSSVoter; - - static equals(a: MsgCreateTSSVoter | PlainMessage | undefined, b: MsgCreateTSSVoter | PlainMessage | undefined): boolean; -} - -/** - * @generated from message zetachain.zetacore.crosschain.MsgCreateTSSVoterResponse - */ -export declare class MsgCreateTSSVoterResponse extends Message { - constructor(data?: PartialMessage); - - static readonly runtime: typeof proto3; - static readonly typeName = "zetachain.zetacore.crosschain.MsgCreateTSSVoterResponse"; - static readonly fields: FieldList; - - static fromBinary(bytes: Uint8Array, options?: Partial): MsgCreateTSSVoterResponse; - - static fromJson(jsonValue: JsonValue, options?: Partial): MsgCreateTSSVoterResponse; - - static fromJsonString(jsonString: string, options?: Partial): MsgCreateTSSVoterResponse; - - static equals(a: MsgCreateTSSVoterResponse | PlainMessage | undefined, b: MsgCreateTSSVoterResponse | PlainMessage | undefined): boolean; -} +import type { ReceiveStatus } from "../pkg/chains/chains_pb.js"; /** * @generated from message zetachain.zetacore.crosschain.MsgMigrateTssFunds diff --git a/typescript/observer/tx_pb.d.ts b/typescript/observer/tx_pb.d.ts index 4418e5569e..21fe737462 100644 --- a/typescript/observer/tx_pb.d.ts +++ b/typescript/observer/tx_pb.d.ts @@ -10,6 +10,7 @@ import type { HeaderData } from "../pkg/proofs/proofs_pb.js"; import type { ChainParams } from "./params_pb.js"; import type { Blame } from "./blame_pb.js"; import type { BlockHeaderVerificationFlags, GasPriceIncreaseFlags } from "./crosschain_flags_pb.js"; +import type { ReceiveStatus } from "../pkg/chains/chains_pb.js"; /** * @generated from message zetachain.zetacore.observer.MsgUpdateObserver @@ -508,3 +509,76 @@ export declare class MsgResetChainNoncesResponse extends Message | undefined, b: MsgResetChainNoncesResponse | PlainMessage | undefined): boolean; } +/** + * @generated from message zetachain.zetacore.observer.MsgVoteTSS + */ +export declare class MsgVoteTSS extends Message { + /** + * @generated from field: string creator = 1; + */ + creator: string; + + /** + * @generated from field: string tss_pubkey = 2; + */ + tssPubkey: string; + + /** + * @generated from field: int64 keygen_zeta_height = 3; + */ + keygenZetaHeight: bigint; + + /** + * @generated from field: chains.ReceiveStatus status = 4; + */ + status: ReceiveStatus; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.observer.MsgVoteTSS"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): MsgVoteTSS; + + static fromJson(jsonValue: JsonValue, options?: Partial): MsgVoteTSS; + + static fromJsonString(jsonString: string, options?: Partial): MsgVoteTSS; + + static equals(a: MsgVoteTSS | PlainMessage | undefined, b: MsgVoteTSS | PlainMessage | undefined): boolean; +} + +/** + * @generated from message zetachain.zetacore.observer.MsgVoteTSSResponse + */ +export declare class MsgVoteTSSResponse extends Message { + /** + * @generated from field: bool ballot_created = 1; + */ + ballotCreated: boolean; + + /** + * @generated from field: bool vote_finalized = 2; + */ + voteFinalized: boolean; + + /** + * @generated from field: bool keygen_success = 3; + */ + keygenSuccess: boolean; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.observer.MsgVoteTSSResponse"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): MsgVoteTSSResponse; + + static fromJson(jsonValue: JsonValue, options?: Partial): MsgVoteTSSResponse; + + static fromJsonString(jsonString: string, options?: Partial): MsgVoteTSSResponse; + + static equals(a: MsgVoteTSSResponse | PlainMessage | undefined, b: MsgVoteTSSResponse | PlainMessage | undefined): boolean; +} + diff --git a/x/crosschain/client/cli/cli_cctx.go b/x/crosschain/client/cli/cli_cctx.go index 92fed899d3..87991442d1 100644 --- a/x/crosschain/client/cli/cli_cctx.go +++ b/x/crosschain/client/cli/cli_cctx.go @@ -225,13 +225,9 @@ func CmdCCTXOutboundVoter() *cobra.Command { argsMMint := args[6] - var status chains.ReceiveStatus - if args[7] == "0" { - status = chains.ReceiveStatus_Success - } else if args[7] == "1" { - status = chains.ReceiveStatus_Failed - } else { - return fmt.Errorf("wrong status") + status, err := chains.ReceiveStatusFromString(args[7]) + if err != nil { + return err } chain, err := strconv.ParseInt(args[8], 10, 64) diff --git a/x/crosschain/client/cli/cli_tss.go b/x/crosschain/client/cli/cli_tss.go index 7c85ea8b72..9b9d551ca8 100644 --- a/x/crosschain/client/cli/cli_tss.go +++ b/x/crosschain/client/cli/cli_tss.go @@ -1,7 +1,6 @@ package cli import ( - "fmt" "strconv" "cosmossdk.io/math" @@ -10,49 +9,9 @@ import ( "github.com/cosmos/cosmos-sdk/client/tx" "github.com/spf13/cast" "github.com/spf13/cobra" - "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/x/crosschain/types" ) -func CmdCreateTSSVoter() *cobra.Command { - cmd := &cobra.Command{ - Use: "create-tss-voter [pubkey] [keygenBlock] [status]", - Short: "Create a new TSSVoter", - Args: cobra.ExactArgs(3), - RunE: func(cmd *cobra.Command, args []string) error { - - argsPubkey, err := cast.ToStringE(args[0]) - if err != nil { - return err - } - keygenBlock, err := strconv.ParseInt(args[1], 10, 64) - if err != nil { - return err - } - var status chains.ReceiveStatus - if args[2] == "0" { - status = chains.ReceiveStatus_Success - } else if args[2] == "1" { - status = chains.ReceiveStatus_Failed - } else { - return fmt.Errorf("wrong status") - } - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - - msg := types.NewMsgCreateTSSVoter(clientCtx.GetFromAddress().String(), argsPubkey, keygenBlock, status) - - return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) - }, - } - - flags.AddTxFlagsToCmd(cmd) - - return cmd -} - func CmdUpdateTss() *cobra.Command { cmd := &cobra.Command{ Use: "update-tss-address [pubkey]", diff --git a/x/crosschain/client/cli/tx.go b/x/crosschain/client/cli/tx.go index bb9831fb31..c82c55f8fd 100644 --- a/x/crosschain/client/cli/tx.go +++ b/x/crosschain/client/cli/tx.go @@ -22,7 +22,6 @@ func GetTxCmd() *cobra.Command { cmd.AddCommand( CmdAddToWatchList(), - CmdCreateTSSVoter(), CmdGasPriceVoter(), CmdCCTXOutboundVoter(), CmdCCTXInboundVoter(), diff --git a/x/crosschain/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/types/authz.go b/x/crosschain/types/authz.go index 7733a34d7a..6ee58171ff 100644 --- a/x/crosschain/types/authz.go +++ b/x/crosschain/types/authz.go @@ -11,8 +11,8 @@ func GetAllAuthzZetaclientTxTypes() []string { sdk.MsgTypeURL(&MsgGasPriceVoter{}), sdk.MsgTypeURL(&MsgVoteOnObservedInboundTx{}), sdk.MsgTypeURL(&MsgVoteOnObservedOutboundTx{}), - sdk.MsgTypeURL(&MsgCreateTSSVoter{}), 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 index 9846949636..7c615d7c6b 100644 --- a/x/crosschain/types/authz_test.go +++ b/x/crosschain/types/authz_test.go @@ -11,8 +11,8 @@ func TestGetAllAuthzZetaclientTxTypes(t *testing.T) { require.Equal(t, []string{"/zetachain.zetacore.crosschain.MsgGasPriceVoter", "/zetachain.zetacore.crosschain.MsgVoteOnObservedInboundTx", "/zetachain.zetacore.crosschain.MsgVoteOnObservedOutboundTx", - "/zetachain.zetacore.crosschain.MsgCreateTSSVoter", "/zetachain.zetacore.crosschain.MsgAddToOutTxTracker", + "/zetachain.zetacore.observer.MsgVoteTSS", "/zetachain.zetacore.observer.MsgAddBlameVote", "/zetachain.zetacore.observer.MsgAddBlockHeader"}, crosschaintypes.GetAllAuthzZetaclientTxTypes()) 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/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/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/observer/client/cli/tx.go b/x/observer/client/cli/tx.go index f581f878b9..9d65f09b70 100644 --- a/x/observer/client/cli/tx.go +++ b/x/observer/client/cli/tx.go @@ -30,6 +30,7 @@ func GetTxCmd() *cobra.Command { CmdUpdateObserver(), CmdEncode(), CmdResetChainNonces(), + CmdVoteTSS(), ) return cmd diff --git a/x/observer/client/cli/tx_vote_tss.go b/x/observer/client/cli/tx_vote_tss.go new file mode 100644 index 0000000000..2c2b837513 --- /dev/null +++ b/x/observer/client/cli/tx_vote_tss.go @@ -0,0 +1,51 @@ +package cli + +import ( + "strconv" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/spf13/cast" + "github.com/spf13/cobra" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/x/observer/types" +) + +func CmdVoteTSS() *cobra.Command { + cmd := &cobra.Command{ + Use: "vote-tss [pubkey] [keygen-block] [status]", + Short: "Vote for a new TSS creation", + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + + argsPubkey, err := cast.ToStringE(args[0]) + if err != nil { + return err + } + + keygenBlock, err := strconv.ParseInt(args[1], 10, 64) + if err != nil { + return err + } + + status, err := chains.ReceiveStatusFromString(args[2]) + if err != nil { + return err + } + + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + msg := types.NewMsgVoteTSS(clientCtx.GetFromAddress().String(), argsPubkey, keygenBlock, status) + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/x/observer/keeper/msg_server_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/utils.go b/x/observer/keeper/utils.go index 5209e3218d..322d45387f 100644 --- a/x/observer/keeper/utils.go +++ b/x/observer/keeper/utils.go @@ -13,9 +13,9 @@ func (k Keeper) AddVoteToBallot(ctx sdk.Context, ballot types.Ballot, address st if err != nil { return ballot, err } - ctx.Logger().Info(fmt.Sprintf("Vote Added | Voter :%s, ballot idetifier %s", address, ballot.BallotIdentifier)) + ctx.Logger().Info(fmt.Sprintf("Vote Added | Voter :%s, ballot identifier %s", address, ballot.BallotIdentifier)) k.SetBallot(ctx, &ballot) - return ballot, err + return ballot, nil } // CheckIfFinalizingVote checks if the ballot is finalized in this block and if it is, it sets the ballot in the store diff --git a/x/observer/types/ballot.go b/x/observer/types/ballot.go index 10f21119d1..811a1b7688 100644 --- a/x/observer/types/ballot.go +++ b/x/observer/types/ballot.go @@ -3,13 +3,13 @@ package types import ( "fmt" + cosmoserrors "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/errors" ) func (m Ballot) AddVote(address string, vote VoteType) (Ballot, error) { if m.HasVoted(address) { - return m, errors.Wrap(ErrUnableToAddVote, fmt.Sprintf(" Voter : %s | Ballot :%s | Already Voted", address, m.String())) + return m, cosmoserrors.Wrap(ErrUnableToAddVote, fmt.Sprintf(" Voter : %s | Ballot :%s | Already Voted", address, m.String())) } // `index` is the index of the `address` in the `VoterList` // `index` is used to set the vote in the `Votes` array diff --git a/x/observer/types/codec.go b/x/observer/types/codec.go index af6e7d87a5..c729bb3a84 100644 --- a/x/observer/types/codec.go +++ b/x/observer/types/codec.go @@ -11,12 +11,13 @@ func RegisterCodec(cdc *codec.LegacyAmino) { cdc.RegisterConcrete(&MsgAddObserver{}, "observer/AddObserver", nil) cdc.RegisterConcrete(&MsgUpdateChainParams{}, "observer/UpdateChainParams", nil) cdc.RegisterConcrete(&MsgRemoveChainParams{}, "observer/RemoveChainParams", nil) - cdc.RegisterConcrete(&MsgAddBlameVote{}, "crosschain/AddBlameVote", nil) - cdc.RegisterConcrete(&MsgUpdateCrosschainFlags{}, "crosschain/UpdateCrosschainFlags", nil) - cdc.RegisterConcrete(&MsgUpdateKeygen{}, "crosschain/UpdateKeygen", nil) - cdc.RegisterConcrete(&MsgAddBlockHeader{}, "crosschain/AddBlockHeader", nil) + cdc.RegisterConcrete(&MsgAddBlameVote{}, "observer/AddBlameVote", nil) + cdc.RegisterConcrete(&MsgUpdateCrosschainFlags{}, "observer/UpdateCrosschainFlags", nil) + cdc.RegisterConcrete(&MsgUpdateKeygen{}, "observer/UpdateKeygen", nil) + cdc.RegisterConcrete(&MsgAddBlockHeader{}, "observer/AddBlockHeader", nil) cdc.RegisterConcrete(&MsgUpdateObserver{}, "observer/UpdateObserver", nil) cdc.RegisterConcrete(&MsgResetChainNonces{}, "observer/ResetChainNonces", nil) + cdc.RegisterConcrete(&MsgVoteTSS{}, "observer/VoteTSS", nil) } func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { @@ -30,6 +31,7 @@ func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { &MsgAddBlockHeader{}, &MsgUpdateObserver{}, &MsgResetChainNonces{}, + &MsgVoteTSS{}, ) msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) diff --git a/x/observer/types/message_vote_tss.go b/x/observer/types/message_vote_tss.go new file mode 100644 index 0000000000..bbd8a73e65 --- /dev/null +++ b/x/observer/types/message_vote_tss.go @@ -0,0 +1,63 @@ +package types + +import ( + "fmt" + + cosmoserrors "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/zeta-chain/zetacore/pkg/chains" +) + +const TypeMsgVoteTSS = "VoteTSS" + +var _ sdk.Msg = &MsgVoteTSS{} + +func NewMsgVoteTSS(creator string, pubkey string, keygenZetaHeight int64, status chains.ReceiveStatus) *MsgVoteTSS { + return &MsgVoteTSS{ + Creator: creator, + TssPubkey: pubkey, + KeygenZetaHeight: keygenZetaHeight, + Status: status, + } +} + +func (msg *MsgVoteTSS) Route() string { + return RouterKey +} + +func (msg *MsgVoteTSS) Type() string { + return TypeMsgVoteTSS +} + +func (msg *MsgVoteTSS) GetSigners() []sdk.AccAddress { + creator, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + panic(err) + } + return []sdk.AccAddress{creator} +} + +func (msg *MsgVoteTSS) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +func (msg *MsgVoteTSS) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + return cosmoserrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err) + } + + // either success or observation failure + if msg.Status != chains.ReceiveStatus_Success && msg.Status != chains.ReceiveStatus_Failed { + return cosmoserrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid status: %s", msg.Status) + } + + return nil +} + +func (msg *MsgVoteTSS) Digest() string { + // We support only 1 keygen at a particular height + return fmt.Sprintf("%d-%s", msg.KeygenZetaHeight, "tss-keygen") +} diff --git a/x/observer/types/message_vote_tss_test.go b/x/observer/types/message_vote_tss_test.go new file mode 100644 index 0000000000..fb8d92323b --- /dev/null +++ b/x/observer/types/message_vote_tss_test.go @@ -0,0 +1,105 @@ +package types_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/observer/types" +) + +func TestMsgVoteTSS_ValidateBasic(t *testing.T) { + tests := []struct { + name string + msg *types.MsgVoteTSS + err error + }{ + { + name: "valid message", + msg: types.NewMsgVoteTSS(sample.AccAddress(), "pubkey", 1, chains.ReceiveStatus_Success), + }, + { + name: "valid message with receive status failed", + msg: types.NewMsgVoteTSS(sample.AccAddress(), "pubkey", 1, chains.ReceiveStatus_Failed), + }, + { + name: "invalid creator address", + msg: types.NewMsgVoteTSS("invalid", "pubkey", 1, chains.ReceiveStatus_Success), + err: sdkerrors.ErrInvalidAddress, + }, + { + name: "invalid observation status", + msg: types.NewMsgVoteTSS(sample.AccAddress(), "pubkey", 1, chains.ReceiveStatus_Created), + err: sdkerrors.ErrInvalidRequest, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.msg.ValidateBasic() + if tt.err != nil { + require.ErrorIs(t, err, tt.err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestMsgVoteTSS_GetSigners(t *testing.T) { + signer := sample.AccAddress() + tests := []struct { + name string + msg *types.MsgVoteTSS + panics bool + }{ + { + name: "valid signer", + msg: types.NewMsgVoteTSS(signer, "pubkey", 1, chains.ReceiveStatus_Success), + panics: false, + }, + { + name: "invalid signer", + msg: types.NewMsgVoteTSS("invalid", "pubkey", 1, chains.ReceiveStatus_Success), + panics: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if !tt.panics { + signers := tt.msg.GetSigners() + require.Equal(t, []sdk.AccAddress{sdk.MustAccAddressFromBech32(signer)}, signers) + } else { + require.Panics(t, func() { + tt.msg.GetSigners() + }) + } + }) + } +} + +func TestMsgVoteTSS_Type(t *testing.T) { + msg := types.NewMsgVoteTSS(sample.AccAddress(), "pubkey", 1, chains.ReceiveStatus_Success) + require.Equal(t, types.TypeMsgVoteTSS, msg.Type()) +} + +func TestMsgVoteTSS_Route(t *testing.T) { + msg := types.NewMsgVoteTSS(sample.AccAddress(), "pubkey", 1, chains.ReceiveStatus_Success) + require.Equal(t, types.RouterKey, msg.Route()) +} + +func TestMsgVoteTSS_GetSignBytes(t *testing.T) { + msg := types.NewMsgVoteTSS(sample.AccAddress(), "pubkey", 1, chains.ReceiveStatus_Success) + require.NotPanics(t, func() { + msg.GetSignBytes() + }) +} + +func TestMsgVoteTSS_Digest(t *testing.T) { + msg := types.NewMsgVoteTSS(sample.AccAddress(), "pubkey", 1, chains.ReceiveStatus_Success) + require.Equal(t, "1-tss-keygen", msg.Digest()) +} diff --git a/x/observer/types/tx.pb.go b/x/observer/types/tx.pb.go index fef0e1ec4a..3c423aba9c 100644 --- a/x/observer/types/tx.pb.go +++ b/x/observer/types/tx.pb.go @@ -13,6 +13,7 @@ import ( _ "github.com/cosmos/gogoproto/gogoproto" grpc1 "github.com/gogo/protobuf/grpc" proto "github.com/gogo/protobuf/proto" + chains "github.com/zeta-chain/zetacore/pkg/chains" proofs "github.com/zeta-chain/zetacore/pkg/proofs" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" @@ -926,6 +927,134 @@ func (m *MsgResetChainNoncesResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgResetChainNoncesResponse proto.InternalMessageInfo +type MsgVoteTSS struct { + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + TssPubkey string `protobuf:"bytes,2,opt,name=tss_pubkey,json=tssPubkey,proto3" json:"tss_pubkey,omitempty"` + KeygenZetaHeight int64 `protobuf:"varint,3,opt,name=keygen_zeta_height,json=keygenZetaHeight,proto3" json:"keygen_zeta_height,omitempty"` + Status chains.ReceiveStatus `protobuf:"varint,4,opt,name=status,proto3,enum=chains.ReceiveStatus" json:"status,omitempty"` +} + +func (m *MsgVoteTSS) Reset() { *m = MsgVoteTSS{} } +func (m *MsgVoteTSS) String() string { return proto.CompactTextString(m) } +func (*MsgVoteTSS) ProtoMessage() {} +func (*MsgVoteTSS) Descriptor() ([]byte, []int) { + return fileDescriptor_1bcd40fa296a2b1d, []int{18} +} +func (m *MsgVoteTSS) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgVoteTSS) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgVoteTSS.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgVoteTSS) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgVoteTSS.Merge(m, src) +} +func (m *MsgVoteTSS) XXX_Size() int { + return m.Size() +} +func (m *MsgVoteTSS) XXX_DiscardUnknown() { + xxx_messageInfo_MsgVoteTSS.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgVoteTSS proto.InternalMessageInfo + +func (m *MsgVoteTSS) GetCreator() string { + if m != nil { + return m.Creator + } + return "" +} + +func (m *MsgVoteTSS) GetTssPubkey() string { + if m != nil { + return m.TssPubkey + } + return "" +} + +func (m *MsgVoteTSS) GetKeygenZetaHeight() int64 { + if m != nil { + return m.KeygenZetaHeight + } + return 0 +} + +func (m *MsgVoteTSS) GetStatus() chains.ReceiveStatus { + if m != nil { + return m.Status + } + return chains.ReceiveStatus_Created +} + +type MsgVoteTSSResponse struct { + BallotCreated bool `protobuf:"varint,1,opt,name=ballot_created,json=ballotCreated,proto3" json:"ballot_created,omitempty"` + VoteFinalized bool `protobuf:"varint,2,opt,name=vote_finalized,json=voteFinalized,proto3" json:"vote_finalized,omitempty"` + KeygenSuccess bool `protobuf:"varint,3,opt,name=keygen_success,json=keygenSuccess,proto3" json:"keygen_success,omitempty"` +} + +func (m *MsgVoteTSSResponse) Reset() { *m = MsgVoteTSSResponse{} } +func (m *MsgVoteTSSResponse) String() string { return proto.CompactTextString(m) } +func (*MsgVoteTSSResponse) ProtoMessage() {} +func (*MsgVoteTSSResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1bcd40fa296a2b1d, []int{19} +} +func (m *MsgVoteTSSResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgVoteTSSResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgVoteTSSResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgVoteTSSResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgVoteTSSResponse.Merge(m, src) +} +func (m *MsgVoteTSSResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgVoteTSSResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgVoteTSSResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgVoteTSSResponse proto.InternalMessageInfo + +func (m *MsgVoteTSSResponse) GetBallotCreated() bool { + if m != nil { + return m.BallotCreated + } + return false +} + +func (m *MsgVoteTSSResponse) GetVoteFinalized() bool { + if m != nil { + return m.VoteFinalized + } + return false +} + +func (m *MsgVoteTSSResponse) GetKeygenSuccess() bool { + if m != nil { + return m.KeygenSuccess + } + return false +} + func init() { proto.RegisterType((*MsgUpdateObserver)(nil), "zetachain.zetacore.observer.MsgUpdateObserver") proto.RegisterType((*MsgUpdateObserverResponse)(nil), "zetachain.zetacore.observer.MsgUpdateObserverResponse") @@ -945,76 +1074,89 @@ func init() { proto.RegisterType((*MsgUpdateKeygenResponse)(nil), "zetachain.zetacore.observer.MsgUpdateKeygenResponse") proto.RegisterType((*MsgResetChainNonces)(nil), "zetachain.zetacore.observer.MsgResetChainNonces") proto.RegisterType((*MsgResetChainNoncesResponse)(nil), "zetachain.zetacore.observer.MsgResetChainNoncesResponse") + proto.RegisterType((*MsgVoteTSS)(nil), "zetachain.zetacore.observer.MsgVoteTSS") + proto.RegisterType((*MsgVoteTSSResponse)(nil), "zetachain.zetacore.observer.MsgVoteTSSResponse") } func init() { proto.RegisterFile("observer/tx.proto", fileDescriptor_1bcd40fa296a2b1d) } var fileDescriptor_1bcd40fa296a2b1d = []byte{ - // 1024 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0x4d, 0x6f, 0xe3, 0x44, - 0x18, 0xae, 0x37, 0xfb, 0xd1, 0xbe, 0xe9, 0xf6, 0x63, 0x48, 0xb7, 0x69, 0xba, 0xcd, 0x56, 0x3e, - 0xa0, 0x00, 0x25, 0x69, 0xb3, 0x80, 0x58, 0x24, 0x0e, 0x2d, 0x1f, 0x6d, 0x58, 0x76, 0x5b, 0x59, - 0xa2, 0x07, 0x2e, 0xd6, 0xc4, 0x33, 0xb1, 0xad, 0x3a, 0x33, 0x91, 0xc7, 0xd9, 0x36, 0x08, 0x90, - 0xf8, 0x01, 0x08, 0x7e, 0x00, 0x7f, 0x82, 0xff, 0xc0, 0x61, 0x8f, 0x7b, 0xe4, 0x84, 0x50, 0x7b, - 0x82, 0x3f, 0xc0, 0x15, 0x79, 0x6c, 0x4f, 0xe2, 0x7c, 0x38, 0x49, 0x4f, 0xcd, 0xcc, 0xfb, 0xbc, - 0xcf, 0xfb, 0x31, 0xcf, 0xbc, 0xe3, 0xc2, 0x3a, 0x6f, 0x0a, 0xea, 0xbf, 0xa2, 0x7e, 0x2d, 0xb8, - 0xaa, 0x76, 0x7c, 0x1e, 0x70, 0xb4, 0xfd, 0x1d, 0x0d, 0xb0, 0xe5, 0x60, 0x97, 0x55, 0xe5, 0x2f, - 0xee, 0xd3, 0x6a, 0x82, 0x2a, 0x15, 0x6c, 0x6e, 0x73, 0x89, 0xab, 0x85, 0xbf, 0x22, 0x97, 0x52, - 0x41, 0xb1, 0x34, 0x3d, 0xdc, 0xa6, 0xf1, 0xee, 0x13, 0xb5, 0x6b, 0xf9, 0x5c, 0x08, 0x49, 0x69, - 0xb6, 0x3c, 0x6c, 0x8b, 0x18, 0xb0, 0xa9, 0x00, 0xc9, 0x8f, 0xd8, 0xb0, 0xa1, 0x0c, 0x1d, 0xec, - 0xe3, 0x76, 0x82, 0xdf, 0xe9, 0x6f, 0x53, 0x46, 0x5c, 0x66, 0x9b, 0x8c, 0x33, 0x8b, 0x26, 0x66, - 0xd4, 0xaf, 0x45, 0xa8, 0x10, 0x9d, 0x0b, 0xbb, 0xd6, 0xf1, 0x39, 0x6f, 0x89, 0xf8, 0x4f, 0x64, - 0xd0, 0xff, 0xd1, 0x60, 0xfd, 0x85, 0xb0, 0xbf, 0xe9, 0x10, 0x1c, 0xd0, 0xd3, 0xd8, 0x11, 0x15, - 0xe1, 0x81, 0xe5, 0x53, 0x1c, 0x70, 0xbf, 0xa8, 0xed, 0x6a, 0x95, 0x25, 0x23, 0x59, 0xa2, 0x7d, - 0x28, 0x70, 0x8f, 0x98, 0x49, 0x08, 0x13, 0x13, 0xe2, 0x53, 0x21, 0x8a, 0x77, 0x24, 0x0c, 0x71, - 0x8f, 0x24, 0x24, 0x87, 0x91, 0x25, 0xf4, 0x60, 0xf4, 0x72, 0xd4, 0x23, 0x17, 0x79, 0x30, 0x7a, - 0x39, 0xec, 0x71, 0x0e, 0x0f, 0xbb, 0x32, 0x1f, 0xd3, 0xa7, 0x58, 0x70, 0x56, 0xbc, 0xbb, 0xab, - 0x55, 0x56, 0xea, 0x07, 0xd5, 0x8c, 0x13, 0xa9, 0x26, 0x24, 0x51, 0x25, 0x86, 0x74, 0x34, 0x96, - 0xbb, 0x03, 0x2b, 0x7d, 0x1b, 0xb6, 0x46, 0x4a, 0x35, 0xa8, 0xe8, 0x70, 0x26, 0xa8, 0xfe, 0x7b, - 0xd4, 0x88, 0x43, 0x42, 0x8e, 0x3c, 0x6e, 0x5d, 0x9c, 0x50, 0x4c, 0x32, 0x1b, 0xb1, 0x05, 0x8b, - 0xd1, 0x49, 0xba, 0x44, 0x16, 0x9f, 0x33, 0x1e, 0xc8, 0x75, 0x83, 0xa0, 0x1d, 0x80, 0x66, 0xc8, - 0x61, 0x3a, 0x58, 0x38, 0xb2, 0xce, 0x65, 0x63, 0x49, 0xee, 0x9c, 0x60, 0xe1, 0xa0, 0x47, 0x70, - 0xdf, 0xa1, 0xae, 0xed, 0x04, 0xb2, 0xae, 0x9c, 0x11, 0xaf, 0xd0, 0x7e, 0xb8, 0x1f, 0x46, 0x2d, - 0xde, 0xdb, 0xd5, 0x2a, 0xf9, 0x3a, 0xaa, 0xc6, 0x27, 0x15, 0xe5, 0xf2, 0x39, 0x0e, 0xf0, 0xd1, - 0xdd, 0xd7, 0x7f, 0x3d, 0x59, 0x30, 0x62, 0x5c, 0x5c, 0x50, 0x3a, 0x65, 0x55, 0xd0, 0xf7, 0x50, - 0x50, 0xd5, 0x7e, 0x16, 0x66, 0x76, 0x26, 0x35, 0x94, 0x51, 0xd2, 0x57, 0x90, 0xb7, 0xfa, 0x40, - 0x59, 0x55, 0xbe, 0x5e, 0xc9, 0xec, 0xfa, 0x00, 0xb1, 0x31, 0xe8, 0xac, 0x97, 0xe1, 0xf1, 0xb8, - 0xe8, 0x2a, 0xbb, 0xe7, 0x32, 0x3b, 0x83, 0xb6, 0xf9, 0xab, 0x19, 0xb3, 0x9b, 0xdc, 0xf0, 0x38, - 0xd8, 0x08, 0x99, 0x0a, 0xf6, 0x87, 0x06, 0x2b, 0x51, 0xa3, 0x66, 0x50, 0xf8, 0x3b, 0xb0, 0x36, - 0x41, 0xdd, 0xab, 0x7c, 0x48, 0xa8, 0x9f, 0xc0, 0x96, 0x6c, 0x89, 0xe7, 0x52, 0x16, 0x98, 0xb6, - 0x8f, 0x59, 0x40, 0xa9, 0xd9, 0xe9, 0x36, 0x2f, 0x68, 0x2f, 0xd6, 0xf7, 0x66, 0x1f, 0x70, 0x1c, - 0xd9, 0xcf, 0xa4, 0x19, 0x1d, 0xc0, 0x06, 0x26, 0xc4, 0x64, 0x9c, 0x50, 0x13, 0x5b, 0x16, 0xef, - 0xb2, 0xc0, 0xe4, 0xcc, 0xeb, 0x49, 0x51, 0x2c, 0x1a, 0x08, 0x13, 0xf2, 0x92, 0x13, 0x7a, 0x18, - 0x99, 0x4e, 0x99, 0xd7, 0xd3, 0x8b, 0xf0, 0x28, 0x5d, 0x85, 0x2a, 0xf0, 0x17, 0x0d, 0x56, 0x13, - 0x25, 0xe0, 0x36, 0x3d, 0xe7, 0x01, 0xbd, 0x9d, 0x74, 0x8f, 0x43, 0xe9, 0xe2, 0x36, 0x35, 0x5d, - 0xd6, 0xe2, 0xb2, 0x84, 0x7c, 0x5d, 0xcf, 0x54, 0x80, 0x0c, 0x18, 0xeb, 0x72, 0x49, 0xfa, 0x36, - 0x58, 0x8b, 0xeb, 0x5b, 0xb0, 0x39, 0x94, 0x90, 0x4a, 0xf6, 0xbf, 0x3b, 0x50, 0xec, 0x6b, 0x43, - 0x8d, 0xc4, 0x2f, 0xc3, 0x89, 0x98, 0x91, 0xf5, 0xbb, 0xb0, 0xe6, 0x8a, 0x06, 0x6b, 0xf2, 0x2e, - 0x23, 0x5f, 0x30, 0xdc, 0xf4, 0x28, 0x91, 0x09, 0x2e, 0x1a, 0x23, 0xfb, 0x68, 0x0f, 0xd6, 0x5d, - 0x71, 0xda, 0x0d, 0x52, 0xe0, 0xa8, 0xb1, 0xa3, 0x06, 0xe4, 0xc0, 0x86, 0x8d, 0xc5, 0x99, 0xef, - 0x5a, 0xb4, 0xc1, 0xc2, 0x70, 0x82, 0xca, 0x64, 0xe2, 0x7b, 0x58, 0xcf, 0xac, 0xff, 0x78, 0x9c, - 0xa7, 0x31, 0x9e, 0x10, 0xfd, 0x00, 0x8f, 0x9b, 0xfd, 0xab, 0x7a, 0x4e, 0x7d, 0xb7, 0xe5, 0x5a, - 0x38, 0x70, 0x79, 0x54, 0x7d, 0xf1, 0xbe, 0x0c, 0xf8, 0x6c, 0x4a, 0xc3, 0x27, 0x13, 0x18, 0x99, - 0xf4, 0xba, 0x0e, 0xbb, 0x93, 0x1a, 0xaf, 0x4e, 0xe7, 0x50, 0x2a, 0x29, 0xc2, 0x3c, 0xa7, 0x3d, - 0x9b, 0xb2, 0x8c, 0x33, 0x29, 0xc0, 0x3d, 0x19, 0x30, 0x96, 0x51, 0xb4, 0x88, 0xcf, 0x7e, 0x90, - 0x42, 0xb1, 0xff, 0xa6, 0xc1, 0x5b, 0xf2, 0xaa, 0x0a, 0x1a, 0xc8, 0x9b, 0xfa, 0x52, 0xbe, 0x5c, - 0xb7, 0x13, 0xeb, 0xdb, 0xb0, 0x1a, 0x99, 0xe4, 0xf3, 0x67, 0x7a, 0xfc, 0x52, 0x0a, 0x22, 0x67, - 0x3c, 0xb4, 0x14, 0xf5, 0xd7, 0xfc, 0x12, 0x55, 0x60, 0x6d, 0x10, 0xe7, 0xb8, 0xb6, 0x13, 0x8f, - 0xde, 0x95, 0x3e, 0xf0, 0xc4, 0xb5, 0x1d, 0x7d, 0x07, 0xb6, 0xc7, 0x64, 0x97, 0x64, 0x5f, 0xff, - 0x77, 0x11, 0x72, 0x2f, 0x84, 0x8d, 0x38, 0xe4, 0x07, 0x67, 0xc9, 0x7b, 0x99, 0xe7, 0x95, 0xbe, - 0xb2, 0xa5, 0xa7, 0x73, 0x80, 0x93, 0xc0, 0xe8, 0x0a, 0x56, 0x86, 0x5e, 0xe8, 0xea, 0x34, 0x9a, - 0x34, 0xbe, 0xf4, 0xd1, 0x7c, 0x78, 0x15, 0xf9, 0x27, 0x0d, 0xd6, 0x47, 0xdf, 0x90, 0x83, 0xd9, - 0xd8, 0x06, 0x5c, 0x4a, 0xcf, 0xe6, 0x76, 0x49, 0xe5, 0x30, 0xfa, 0x52, 0x4c, 0xcd, 0x61, 0xc4, - 0x65, 0x7a, 0x0e, 0x13, 0x9f, 0x10, 0xe4, 0xc3, 0x72, 0x6a, 0xba, 0xee, 0xcd, 0x70, 0x8c, 0x0a, - 0x5d, 0xfa, 0x60, 0x1e, 0xb4, 0x8a, 0xf9, 0xb3, 0x06, 0x1b, 0xe3, 0xa7, 0xe4, 0x87, 0x33, 0x36, - 0x33, 0xed, 0x56, 0xfa, 0xf4, 0x56, 0x6e, 0x83, 0x3d, 0x48, 0xcd, 0x85, 0xbd, 0xd9, 0xe8, 0x22, - 0xf4, 0xf4, 0x1e, 0x8c, 0x1b, 0x18, 0xa1, 0xf2, 0x87, 0x3e, 0xc9, 0xaa, 0x33, 0xf5, 0x52, 0xe1, - 0xa7, 0x2b, 0x7f, 0xfc, 0xf7, 0x13, 0xfa, 0x11, 0xd6, 0x46, 0xc6, 0xd4, 0xfe, 0x74, 0x01, 0xa5, - 0x3d, 0x4a, 0x1f, 0xcf, 0xeb, 0x91, 0xc4, 0x3f, 0x6a, 0xbc, 0xbe, 0x2e, 0x6b, 0x6f, 0xae, 0xcb, - 0xda, 0xdf, 0xd7, 0x65, 0xed, 0xd7, 0x9b, 0xf2, 0xc2, 0x9b, 0x9b, 0xf2, 0xc2, 0x9f, 0x37, 0xe5, - 0x85, 0x6f, 0x6b, 0xb6, 0x1b, 0x38, 0xdd, 0x66, 0xd5, 0xe2, 0xed, 0x5a, 0xc8, 0xf9, 0xbe, 0xa4, - 0xaf, 0x25, 0xf4, 0xb5, 0xab, 0x5a, 0xff, 0x3f, 0x80, 0x5e, 0x87, 0x8a, 0xe6, 0x7d, 0xf9, 0xad, - 0xff, 0xf4, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc5, 0x1a, 0xf7, 0xc9, 0xe6, 0x0c, 0x00, 0x00, + // 1186 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0xcd, 0x72, 0xe3, 0x44, + 0x10, 0x8e, 0x36, 0xbb, 0x89, 0xd3, 0xf9, 0x17, 0xc9, 0xc6, 0x71, 0x36, 0xde, 0x94, 0xaa, 0x80, + 0x00, 0x59, 0x3b, 0xf1, 0x02, 0xc5, 0x52, 0xc5, 0x21, 0x59, 0xd8, 0x24, 0x2c, 0xd9, 0xa4, 0x14, + 0xc8, 0x61, 0x2f, 0xaa, 0xb1, 0x34, 0x96, 0x54, 0x91, 0x67, 0x5c, 0x9a, 0x71, 0x7e, 0xb6, 0x80, + 0x2a, 0x8e, 0x1c, 0x28, 0x78, 0x00, 0x4e, 0xbc, 0x01, 0xef, 0xc0, 0x61, 0x6f, 0xec, 0x91, 0x13, + 0x45, 0x25, 0x27, 0x9e, 0x80, 0x2b, 0xa5, 0x99, 0xd1, 0xd8, 0x8e, 0x1d, 0xd9, 0xc9, 0xc9, 0x52, + 0xf7, 0xd7, 0xdd, 0x5f, 0xf7, 0xf4, 0x74, 0xcb, 0x30, 0x4b, 0xab, 0x0c, 0xc7, 0x27, 0x38, 0x2e, + 0xf3, 0xb3, 0x52, 0x23, 0xa6, 0x9c, 0x9a, 0x4b, 0xaf, 0x30, 0x47, 0x6e, 0x80, 0x42, 0x52, 0x12, + 0x4f, 0x34, 0xc6, 0xa5, 0x14, 0x55, 0x98, 0xf3, 0xa9, 0x4f, 0x05, 0xae, 0x9c, 0x3c, 0x49, 0x93, + 0xc2, 0x9c, 0xf6, 0x52, 0x8d, 0x50, 0x1d, 0x2b, 0xe9, 0x43, 0x2d, 0x75, 0x63, 0xca, 0x98, 0x70, + 0xe9, 0xd4, 0x22, 0xe4, 0x33, 0x05, 0x58, 0xd0, 0x80, 0xf4, 0x41, 0x29, 0xe6, 0xb5, 0xa2, 0x81, + 0x62, 0x54, 0x4f, 0xf1, 0xcb, 0x2d, 0x31, 0x26, 0x5e, 0x48, 0x7c, 0x87, 0x50, 0xe2, 0xe2, 0x54, + 0x6d, 0xb6, 0x72, 0x61, 0x3a, 0x44, 0xe3, 0xd8, 0x2f, 0x8b, 0xc8, 0x4c, 0xfd, 0xb4, 0x2b, 0x1a, + 0x31, 0xa5, 0x35, 0xa6, 0x7e, 0xa4, 0xc2, 0xfa, 0xd7, 0x80, 0xd9, 0x3d, 0xe6, 0x7f, 0xd3, 0xf0, + 0x10, 0xc7, 0xfb, 0xca, 0xa3, 0x99, 0x87, 0x51, 0x37, 0xc6, 0x88, 0xd3, 0x38, 0x6f, 0xac, 0x18, + 0xab, 0x63, 0x76, 0xfa, 0x6a, 0xae, 0xc3, 0x1c, 0x8d, 0x3c, 0x27, 0x8d, 0xed, 0x20, 0xcf, 0x8b, + 0x31, 0x63, 0xf9, 0x3b, 0x02, 0x66, 0xd2, 0xc8, 0x4b, 0x9d, 0x6c, 0x4a, 0x4d, 0x62, 0x41, 0xf0, + 0x69, 0xb7, 0xc5, 0xb0, 0xb4, 0x20, 0xf8, 0xf4, 0xaa, 0xc5, 0x11, 0x4c, 0x36, 0x05, 0x1f, 0x27, + 0xc6, 0x88, 0x51, 0x92, 0xbf, 0xbb, 0x62, 0xac, 0x4e, 0x55, 0x36, 0x4a, 0x19, 0x47, 0x55, 0x4a, + 0x9d, 0xc8, 0x4c, 0x6c, 0x61, 0x68, 0x4f, 0x34, 0xdb, 0xde, 0xac, 0x25, 0x58, 0xec, 0x4a, 0xd5, + 0xc6, 0xac, 0x41, 0x09, 0xc3, 0xd6, 0xef, 0xb2, 0x10, 0x9b, 0x9e, 0xb7, 0x15, 0x51, 0xf7, 0x78, + 0x07, 0x23, 0x2f, 0xb3, 0x10, 0x8b, 0x90, 0x93, 0x47, 0x1c, 0x7a, 0x22, 0xf9, 0x61, 0x7b, 0x54, + 0xbc, 0xef, 0x7a, 0xe6, 0x32, 0x40, 0x35, 0xf1, 0xe1, 0x04, 0x88, 0x05, 0x22, 0xcf, 0x09, 0x7b, + 0x4c, 0x48, 0x76, 0x10, 0x0b, 0xcc, 0xfb, 0x30, 0x12, 0xe0, 0xd0, 0x0f, 0xb8, 0xc8, 0x6b, 0xd8, + 0x56, 0x6f, 0xe6, 0x7a, 0x22, 0x4f, 0xa2, 0xe6, 0xef, 0xad, 0x18, 0xab, 0xe3, 0x15, 0xb3, 0xa4, + 0x4e, 0x4a, 0x72, 0xf9, 0x1c, 0x71, 0xb4, 0x75, 0xf7, 0xf5, 0xdf, 0x0f, 0x87, 0x6c, 0x85, 0x53, + 0x09, 0x75, 0x52, 0xd6, 0x09, 0x7d, 0x0b, 0x73, 0x3a, 0xdb, 0xa7, 0x09, 0xb3, 0x03, 0xd1, 0x5c, + 0x19, 0x29, 0x7d, 0x09, 0xe3, 0x6e, 0x0b, 0x28, 0xb2, 0x1a, 0xaf, 0xac, 0x66, 0x56, 0xbd, 0xcd, + 0xb1, 0xdd, 0x6e, 0x6c, 0x15, 0xe1, 0x41, 0xaf, 0xe8, 0x9a, 0xdd, 0x73, 0xc1, 0xce, 0xc6, 0x75, + 0x7a, 0x32, 0x20, 0xbb, 0xeb, 0x0b, 0xae, 0x82, 0x75, 0x39, 0xd3, 0xc1, 0xfe, 0x30, 0x60, 0x4a, + 0x16, 0x6a, 0x80, 0x0e, 0x7f, 0x0f, 0x66, 0xae, 0xe9, 0xee, 0x69, 0x7a, 0xa5, 0x51, 0x3f, 0x85, + 0x45, 0x51, 0x92, 0x28, 0xc4, 0x84, 0x3b, 0x7e, 0x8c, 0x08, 0xc7, 0xd8, 0x69, 0x34, 0xab, 0xc7, + 0xf8, 0x5c, 0xf5, 0xf7, 0x42, 0x0b, 0xb0, 0x2d, 0xf5, 0x07, 0x42, 0x6d, 0x6e, 0xc0, 0x3c, 0xf2, + 0x3c, 0x87, 0x50, 0x0f, 0x3b, 0xc8, 0x75, 0x69, 0x93, 0x70, 0x87, 0x92, 0xe8, 0x5c, 0x34, 0x45, + 0xce, 0x36, 0x91, 0xe7, 0xbd, 0xa0, 0x1e, 0xde, 0x94, 0xaa, 0x7d, 0x12, 0x9d, 0x5b, 0x79, 0xb8, + 0xdf, 0x99, 0x85, 0x4e, 0xf0, 0x67, 0x03, 0xa6, 0xd3, 0x4e, 0x40, 0x75, 0x7c, 0x44, 0x39, 0xbe, + 0x5d, 0xeb, 0x6e, 0x27, 0xad, 0x8b, 0xea, 0xd8, 0x09, 0x49, 0x8d, 0x8a, 0x14, 0xc6, 0x2b, 0x56, + 0x66, 0x07, 0x88, 0x80, 0xaa, 0x2f, 0xc7, 0x84, 0xed, 0x2e, 0xa9, 0x51, 0x6b, 0x11, 0x16, 0xae, + 0x10, 0xd2, 0x64, 0xff, 0xbb, 0x03, 0xf9, 0x56, 0x6f, 0xe8, 0x59, 0xf9, 0x2c, 0x19, 0x95, 0x19, + 0xac, 0xdf, 0x87, 0x99, 0x90, 0xed, 0x92, 0x2a, 0x6d, 0x12, 0xef, 0x0b, 0x82, 0xaa, 0x11, 0xf6, + 0x04, 0xc1, 0x9c, 0xdd, 0x25, 0x37, 0xd7, 0x60, 0x36, 0x64, 0xfb, 0x4d, 0xde, 0x01, 0x96, 0x85, + 0xed, 0x56, 0x98, 0x01, 0xcc, 0xfb, 0x88, 0x1d, 0xc4, 0xa1, 0x8b, 0x77, 0x49, 0x12, 0x8e, 0x61, + 0x41, 0x46, 0xdd, 0xc3, 0x4a, 0x66, 0xfe, 0xdb, 0xbd, 0x2c, 0xed, 0xde, 0x0e, 0xcd, 0xef, 0xe0, + 0x41, 0xb5, 0x75, 0x55, 0x8f, 0x70, 0x1c, 0xd6, 0x42, 0x17, 0xf1, 0x90, 0xca, 0xec, 0xf3, 0x23, + 0x22, 0xe0, 0x93, 0x3e, 0x05, 0xbf, 0xde, 0x81, 0x9d, 0xe9, 0xde, 0xb2, 0x60, 0xe5, 0xba, 0xc2, + 0xeb, 0xd3, 0xd9, 0x14, 0x9d, 0x24, 0x31, 0xcf, 0xf1, 0xb9, 0x8f, 0x49, 0xc6, 0x99, 0xcc, 0xc1, + 0x3d, 0x11, 0x50, 0xb5, 0x91, 0x7c, 0x51, 0x67, 0xdf, 0xee, 0x42, 0x7b, 0xff, 0xd5, 0x80, 0xb7, + 0xc4, 0x55, 0x65, 0x98, 0x8b, 0x9b, 0xfa, 0x42, 0xac, 0xb4, 0xdb, 0x35, 0xeb, 0x3b, 0x30, 0x2d, + 0x55, 0x62, 0x2f, 0x3a, 0x11, 0x3d, 0x15, 0x0d, 0x31, 0x6c, 0x4f, 0xba, 0xda, 0xf5, 0x57, 0xf4, + 0xd4, 0x5c, 0x85, 0x99, 0x76, 0x5c, 0x10, 0xfa, 0x81, 0x1a, 0xbd, 0x53, 0x2d, 0xe0, 0x4e, 0xe8, + 0x07, 0xd6, 0x32, 0x2c, 0xf5, 0x60, 0xa7, 0xd9, 0xff, 0x66, 0x00, 0xec, 0x31, 0x3f, 0xe9, 0xe6, + 0xaf, 0x0f, 0x0f, 0x33, 0x48, 0x2f, 0x03, 0x70, 0xc6, 0xd2, 0x49, 0x20, 0xa7, 0xc7, 0x18, 0x67, + 0x4c, 0xdd, 0xfd, 0x35, 0x30, 0x8f, 0x45, 0x5d, 0x9c, 0xe4, 0x78, 0x1d, 0xb5, 0x0d, 0x24, 0xf7, + 0x19, 0xa9, 0x79, 0x89, 0x39, 0xda, 0x91, 0x7b, 0xe1, 0x11, 0x8c, 0x30, 0x8e, 0x78, 0x93, 0xa9, + 0x3d, 0x38, 0x5f, 0x52, 0xab, 0xdd, 0xc6, 0x2e, 0x0e, 0x4f, 0xf0, 0xa1, 0x50, 0xda, 0x0a, 0x64, + 0xfd, 0x68, 0x80, 0xd9, 0x22, 0x99, 0x72, 0x37, 0xdf, 0x86, 0xa9, 0x2a, 0x8a, 0x22, 0xca, 0x1d, + 0x41, 0x12, 0x7b, 0x82, 0x73, 0xce, 0x9e, 0x94, 0xd2, 0xa7, 0x52, 0x98, 0xc0, 0x4e, 0x28, 0xc7, + 0x4e, 0x2d, 0x24, 0x28, 0x0a, 0x5f, 0x61, 0x59, 0xf4, 0x9c, 0x3d, 0x99, 0x48, 0x9f, 0xa5, 0xc2, + 0x04, 0xa6, 0x32, 0x60, 0x4d, 0xd7, 0x4d, 0xd7, 0x79, 0xce, 0x9e, 0x94, 0xd2, 0x43, 0x29, 0xac, + 0xfc, 0x39, 0x06, 0xc3, 0x7b, 0xcc, 0x37, 0x29, 0x8c, 0xb7, 0x0f, 0xdf, 0x0f, 0x32, 0x1b, 0xbc, + 0x73, 0xc6, 0x15, 0x1e, 0xdf, 0x00, 0xac, 0xb3, 0x3d, 0x83, 0xa9, 0x2b, 0x9f, 0x34, 0xa5, 0x7e, + 0x6e, 0x3a, 0xf1, 0x85, 0x8f, 0x6f, 0x86, 0xd7, 0x91, 0x7f, 0x30, 0x60, 0xb6, 0x7b, 0xe9, 0x6e, + 0x0c, 0xe6, 0xad, 0xcd, 0xa4, 0xf0, 0xe4, 0xc6, 0x26, 0x1d, 0x1c, 0xba, 0x57, 0x6b, 0x5f, 0x0e, + 0x5d, 0x26, 0xfd, 0x39, 0x5c, 0xbb, 0x73, 0xcd, 0x18, 0x26, 0x3a, 0xd6, 0xd1, 0xda, 0x00, 0xc7, + 0xa8, 0xd1, 0x85, 0x0f, 0x6f, 0x82, 0xd6, 0x31, 0x7f, 0x32, 0x60, 0xbe, 0xf7, 0x5a, 0xf9, 0x68, + 0xc0, 0x62, 0x76, 0x9a, 0x15, 0x3e, 0xbb, 0x95, 0x59, 0x7b, 0x0d, 0x3a, 0x06, 0xe9, 0xda, 0x60, + 0xee, 0x24, 0xba, 0x7f, 0x0d, 0x7a, 0x4d, 0xd8, 0xa4, 0xf3, 0xaf, 0x7c, 0xc3, 0x96, 0x06, 0xaa, + 0xa5, 0xc6, 0xf7, 0xef, 0xfc, 0xde, 0x1f, 0x9c, 0xe6, 0xf7, 0x30, 0xd3, 0x35, 0xd7, 0xd7, 0xfb, + 0x37, 0x50, 0xa7, 0x45, 0xe1, 0x93, 0x9b, 0x5a, 0xe8, 0xf8, 0x2e, 0x8c, 0xa6, 0x93, 0xf9, 0xdd, + 0x7e, 0x4e, 0x14, 0xb0, 0x50, 0x1e, 0x10, 0x98, 0x06, 0xd9, 0xda, 0x7d, 0x7d, 0x51, 0x34, 0xde, + 0x5c, 0x14, 0x8d, 0x7f, 0x2e, 0x8a, 0xc6, 0x2f, 0x97, 0xc5, 0xa1, 0x37, 0x97, 0xc5, 0xa1, 0xbf, + 0x2e, 0x8b, 0x43, 0x2f, 0xcb, 0x7e, 0xc8, 0x83, 0x66, 0xb5, 0xe4, 0xd2, 0x7a, 0x39, 0x71, 0xf5, + 0x48, 0x78, 0x2d, 0xa7, 0x5e, 0xcb, 0x67, 0xe5, 0xd6, 0x1f, 0xb6, 0xf3, 0x06, 0x66, 0xd5, 0x11, + 0xf1, 0x0f, 0xec, 0xf1, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0xdb, 0x8d, 0x3c, 0x01, 0x95, 0x0e, + 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1038,6 +1180,7 @@ type MsgClient interface { UpdateKeygen(ctx context.Context, in *MsgUpdateKeygen, opts ...grpc.CallOption) (*MsgUpdateKeygenResponse, error) AddBlockHeader(ctx context.Context, in *MsgAddBlockHeader, opts ...grpc.CallOption) (*MsgAddBlockHeaderResponse, error) ResetChainNonces(ctx context.Context, in *MsgResetChainNonces, opts ...grpc.CallOption) (*MsgResetChainNoncesResponse, error) + VoteTSS(ctx context.Context, in *MsgVoteTSS, opts ...grpc.CallOption) (*MsgVoteTSSResponse, error) } type msgClient struct { @@ -1129,6 +1272,15 @@ func (c *msgClient) ResetChainNonces(ctx context.Context, in *MsgResetChainNonce return out, nil } +func (c *msgClient) VoteTSS(ctx context.Context, in *MsgVoteTSS, opts ...grpc.CallOption) (*MsgVoteTSSResponse, error) { + out := new(MsgVoteTSSResponse) + err := c.cc.Invoke(ctx, "/zetachain.zetacore.observer.Msg/VoteTSS", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // MsgServer is the server API for Msg service. type MsgServer interface { AddObserver(context.Context, *MsgAddObserver) (*MsgAddObserverResponse, error) @@ -1140,6 +1292,7 @@ type MsgServer interface { UpdateKeygen(context.Context, *MsgUpdateKeygen) (*MsgUpdateKeygenResponse, error) AddBlockHeader(context.Context, *MsgAddBlockHeader) (*MsgAddBlockHeaderResponse, error) ResetChainNonces(context.Context, *MsgResetChainNonces) (*MsgResetChainNoncesResponse, error) + VoteTSS(context.Context, *MsgVoteTSS) (*MsgVoteTSSResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. @@ -1173,6 +1326,9 @@ func (*UnimplementedMsgServer) AddBlockHeader(ctx context.Context, req *MsgAddBl func (*UnimplementedMsgServer) ResetChainNonces(ctx context.Context, req *MsgResetChainNonces) (*MsgResetChainNoncesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ResetChainNonces not implemented") } +func (*UnimplementedMsgServer) VoteTSS(ctx context.Context, req *MsgVoteTSS) (*MsgVoteTSSResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method VoteTSS not implemented") +} func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) @@ -1340,6 +1496,24 @@ func _Msg_ResetChainNonces_Handler(srv interface{}, ctx context.Context, dec fun return interceptor(ctx, in, info, handler) } +func _Msg_VoteTSS_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgVoteTSS) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).VoteTSS(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/zetachain.zetacore.observer.Msg/VoteTSS", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).VoteTSS(ctx, req.(*MsgVoteTSS)) + } + return interceptor(ctx, in, info, handler) +} + var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "zetachain.zetacore.observer.Msg", HandlerType: (*MsgServer)(nil), @@ -1380,6 +1554,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "ResetChainNonces", Handler: _Msg_ResetChainNonces_Handler, }, + { + MethodName: "VoteTSS", + Handler: _Msg_VoteTSS_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "observer/tx.proto", @@ -2028,6 +2206,106 @@ func (m *MsgResetChainNoncesResponse) MarshalToSizedBuffer(dAtA []byte) (int, er return len(dAtA) - i, nil } +func (m *MsgVoteTSS) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgVoteTSS) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgVoteTSS) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Status != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x20 + } + if m.KeygenZetaHeight != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.KeygenZetaHeight)) + i-- + dAtA[i] = 0x18 + } + if len(m.TssPubkey) > 0 { + i -= len(m.TssPubkey) + copy(dAtA[i:], m.TssPubkey) + i = encodeVarintTx(dAtA, i, uint64(len(m.TssPubkey))) + i-- + dAtA[i] = 0x12 + } + if len(m.Creator) > 0 { + i -= len(m.Creator) + copy(dAtA[i:], m.Creator) + i = encodeVarintTx(dAtA, i, uint64(len(m.Creator))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgVoteTSSResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgVoteTSSResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgVoteTSSResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.KeygenSuccess { + i-- + if m.KeygenSuccess { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if m.VoteFinalized { + i-- + if m.VoteFinalized { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if m.BallotCreated { + i-- + if m.BallotCreated { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func encodeVarintTx(dAtA []byte, offset int, v uint64) int { offset -= sovTx(v) base := offset @@ -2309,6 +2587,47 @@ func (m *MsgResetChainNoncesResponse) Size() (n int) { return n } +func (m *MsgVoteTSS) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Creator) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.TssPubkey) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.KeygenZetaHeight != 0 { + n += 1 + sovTx(uint64(m.KeygenZetaHeight)) + } + if m.Status != 0 { + n += 1 + sovTx(uint64(m.Status)) + } + return n +} + +func (m *MsgVoteTSSResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BallotCreated { + n += 2 + } + if m.VoteFinalized { + n += 2 + } + if m.KeygenSuccess { + n += 2 + } + return n +} + func sovTx(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -4070,6 +4389,268 @@ func (m *MsgResetChainNoncesResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgVoteTSS) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgVoteTSS: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgVoteTSS: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Creator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Creator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TssPubkey", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TssPubkey = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field KeygenZetaHeight", wireType) + } + m.KeygenZetaHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.KeygenZetaHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Status |= chains.ReceiveStatus(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgVoteTSSResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgVoteTSSResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgVoteTSSResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BallotCreated", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.BallotCreated = bool(v != 0) + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field VoteFinalized", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.VoteFinalized = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field KeygenSuccess", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.KeygenSuccess = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTx(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/zetaclient/bitcoin/bitcoin_client.go b/zetaclient/bitcoin/bitcoin_client.go index 8f9c1f9463..2fe823811b 100644 --- a/zetaclient/bitcoin/bitcoin_client.go +++ b/zetaclient/bitcoin/bitcoin_client.go @@ -13,20 +13,6 @@ import ( "sync/atomic" "time" - "github.com/zeta-chain/zetacore/pkg/chains" - "github.com/zeta-chain/zetacore/pkg/coin" - "github.com/zeta-chain/zetacore/pkg/constant" - "github.com/zeta-chain/zetacore/pkg/proofs" - corecontext "github.com/zeta-chain/zetacore/zetaclient/core_context" - - appcontext "github.com/zeta-chain/zetacore/zetaclient/app_context" - clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" - "github.com/zeta-chain/zetacore/zetaclient/compliance" - "github.com/zeta-chain/zetacore/zetaclient/config" - "github.com/zeta-chain/zetacore/zetaclient/interfaces" - "github.com/zeta-chain/zetacore/zetaclient/metrics" - "github.com/zeta-chain/zetacore/zetaclient/zetabridge" - cosmosmath "cosmossdk.io/math" "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/chaincfg" @@ -38,9 +24,21 @@ import ( lru "github.com/hashicorp/golang-lru" "github.com/pkg/errors" "github.com/rs/zerolog" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" + "github.com/zeta-chain/zetacore/pkg/constant" + "github.com/zeta-chain/zetacore/pkg/proofs" "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" + appcontext "github.com/zeta-chain/zetacore/zetaclient/app_context" + clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" + "github.com/zeta-chain/zetacore/zetaclient/compliance" + "github.com/zeta-chain/zetacore/zetaclient/config" + corecontext "github.com/zeta-chain/zetacore/zetaclient/core_context" + "github.com/zeta-chain/zetacore/zetaclient/interfaces" + "github.com/zeta-chain/zetacore/zetaclient/metrics" clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" + "github.com/zeta-chain/zetacore/zetaclient/zetabridge" "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" diff --git a/zetaclient/evm/evm_client.go b/zetaclient/evm/evm_client.go index 672c6b65b0..84fbd19e83 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" ) 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 { From 09f2018230724c355db1cfa1735c4c3929e81cc7 Mon Sep 17 00:00:00 2001 From: skosito Date: Wed, 3 Apr 2024 17:41:53 +0100 Subject: [PATCH 02/10] test: improve crosschain module coverage (#1967) --- changelog.md | 1 + codecov.yml | 2 + .../zetacored/zetacored_query_crosschain.md | 1 - .../zetacored_query_crosschain_params.md | 33 - docs/openapi/openapi.swagger.yaml | 28 - proto/crosschain/genesis.proto | 18 +- proto/crosschain/params.proto | 12 - proto/crosschain/query.proto | 15 - testutil/keeper/crosschain.go | 4 +- testutil/network/genesis_state.go | 1 - testutil/sample/crosschain.go | 10 + typescript/crosschain/genesis_pb.d.ts | 22 +- typescript/crosschain/index.d.ts | 1 - typescript/crosschain/params_pb.d.ts | 34 - typescript/crosschain/query_pb.d.ts | 50 -- x/crosschain/client/cli/cli_params.go | 34 - x/crosschain/client/cli/query.go | 1 - x/crosschain/genesis.go | 4 - x/crosschain/genesis_test.go | 11 +- x/crosschain/keeper/cctx_test.go | 10 + x/crosschain/keeper/cctx_utils.go | 4 +- x/crosschain/keeper/cctx_utils_test.go | 158 +++++ x/crosschain/keeper/evm_deposit_test.go | 152 ++++- x/crosschain/keeper/foreign_coins_test.go | 20 + x/crosschain/keeper/grpc_query_cctx_test.go | 130 +++- .../keeper/grpc_query_gas_price_test.go | 10 +- .../keeper/grpc_query_in_tx_tracker_test.go | 49 ++ .../keeper/grpc_query_last_block_height.go | 6 +- .../grpc_query_last_block_height_test.go | 34 + .../keeper/grpc_query_last_zeta_height.go | 3 +- .../grpc_query_last_zeta_height_test.go | 39 ++ .../keeper/grpc_query_out_tx_tracker_test.go | 192 +++--- .../grpc_query_zeta_conversion_rate_test.go | 120 ++++ .../msg_server_add_to_intx_tracker_test.go | 25 +- .../msg_server_add_to_outtx_tracker_test.go | 109 ++- .../keeper/msg_server_gas_price_voter.go | 5 +- .../keeper/msg_server_gas_price_voter_test.go | 204 ++++++ .../keeper/msg_server_migrate_tss_funds.go | 4 + .../msg_server_migrate_tss_funds_test.go | 212 +++++- .../msg_server_refund_aborted_tx_test.go | 64 ++ .../msg_server_remove_out_tx_tracker_test.go | 66 ++ .../keeper/msg_server_update_tss_test.go | 36 + .../keeper/msg_server_vote_inbound_tx_test.go | 88 +++ .../msg_server_vote_outbound_tx_test.go | 4 +- x/crosschain/keeper/params.go | 31 - x/crosschain/keeper/process_inbound.go | 12 +- x/crosschain/keeper/process_inbound_test.go | 102 ++- x/crosschain/keeper/process_outbound_test.go | 56 +- x/crosschain/keeper/refund_test.go | 188 ++++++ x/crosschain/keeper/verify_proof_test.go | 393 +++++++++++ x/crosschain/keeper/zeta_accounting_test.go | 13 +- x/crosschain/module.go | 7 +- x/crosschain/module_simulation.go | 4 +- x/crosschain/types/client_ctx.go | 14 - x/crosschain/types/genesis.pb.go | 167 ++--- x/crosschain/types/genesis_test.go | 20 + x/crosschain/types/params.go | 44 -- x/crosschain/types/params.pb.go | 307 --------- x/crosschain/types/query.pb.go | 628 ++++-------------- x/crosschain/types/query.pb.gw.go | 65 -- x/crosschain/types/status.go | 1 - x/crosschain/types/status_test.go | 75 +++ 62 files changed, 2676 insertions(+), 1477 deletions(-) delete mode 100644 docs/cli/zetacored/zetacored_query_crosschain_params.md delete mode 100644 proto/crosschain/params.proto delete mode 100644 typescript/crosschain/params_pb.d.ts delete mode 100644 x/crosschain/client/cli/cli_params.go create mode 100644 x/crosschain/keeper/foreign_coins_test.go create mode 100644 x/crosschain/keeper/grpc_query_in_tx_tracker_test.go create mode 100644 x/crosschain/keeper/grpc_query_last_zeta_height_test.go create mode 100644 x/crosschain/keeper/grpc_query_zeta_conversion_rate_test.go create mode 100644 x/crosschain/keeper/msg_server_gas_price_voter_test.go create mode 100644 x/crosschain/keeper/msg_server_remove_out_tx_tracker_test.go delete mode 100644 x/crosschain/keeper/params.go create mode 100644 x/crosschain/keeper/verify_proof_test.go delete mode 100644 x/crosschain/types/client_ctx.go delete mode 100644 x/crosschain/types/params.go delete mode 100644 x/crosschain/types/params.pb.go diff --git a/changelog.md b/changelog.md index 055e9f9283..8fe144056a 100644 --- a/changelog.md +++ b/changelog.md @@ -51,6 +51,7 @@ * [1879](https://github.com/zeta-chain/node/pull/1879) - full coverage for messages in types packages * [1899](https://github.com/zeta-chain/node/pull/1899) - add empty test files so packages are included in coverage * [1903](https://github.com/zeta-chain/node/pull/1903) - common package tests +* [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 diff --git a/codecov.yml b/codecov.yml index 1a9ba2063d..aab4d9ab68 100644 --- a/codecov.yml +++ b/codecov.yml @@ -49,6 +49,8 @@ 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/**/*" - "**/*.proto" 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/openapi/openapi.swagger.yaml b/docs/openapi/openapi.swagger.yaml index 2dd9812b64..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 @@ -54738,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/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/testutil/keeper/crosschain.go b/testutil/keeper/crosschain.go index 04bbcf7ac9..3fb8f81823 100644 --- a/testutil/keeper/crosschain.go +++ b/testutil/keeper/crosschain.go @@ -198,13 +198,13 @@ func MockGetSupportedChainFromChainID(m *crosschainmocks.CrosschainObserverKeepe Return(senderChain).Once() } -func MockGetRevertGasLimitForERC20(m *crosschainmocks.CrosschainFungibleKeeper, asset string, senderChain chains.Chain) { +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(100), nil).Once() + 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) { 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 0fe91e0c2e..0040a7fc3a 100644 --- a/testutil/sample/crosschain.go +++ b/testutil/sample/crosschain.go @@ -24,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) 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/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/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/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_test.go b/x/crosschain/keeper/cctx_test.go index 3f8c3fe4d4..3a7fb7fe5b 100644 --- a/x/crosschain/keeper/cctx_test.go +++ b/x/crosschain/keeper/cctx_test.go @@ -243,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 888b3bf445..1c807a97c1 100644 --- a/x/crosschain/keeper/cctx_utils_test.go +++ b/x/crosschain/keeper/cctx_utils_test.go @@ -6,13 +6,16 @@ import ( "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) { @@ -216,3 +219,158 @@ func Test_IsPending(t *testing.T) { }) } } + +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/evm_deposit_test.go b/x/crosschain/keeper/evm_deposit_test.go index bcb7b684e8..f82872566a 100644 --- a/x/crosschain/keeper/evm_deposit_test.go +++ b/x/crosschain/keeper/evm_deposit_test.go @@ -117,6 +117,152 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { 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, @@ -366,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, @@ -378,7 +528,6 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{}, false, nil) - cctx := sample.CrossChainTx(t, "foo") cctx.GetCurrentOutTxParam().Receiver = sample.EthAddress().String() cctx.GetInboundTxParams().Amount = math.NewUintFromBigInt(amount) cctx.GetInboundTxParams().CoinType = coin.CoinType_ERC20 @@ -393,6 +542,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { 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) { 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/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 7fd1228400..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 }) 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_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_test.go b/x/crosschain/keeper/msg_server_vote_inbound_tx_test.go index 25d5615056..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,12 +2,15 @@ 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" @@ -160,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) { 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 511e703acd..fa9884f25a 100644 --- a/x/crosschain/keeper/msg_server_vote_outbound_tx_test.go +++ b/x/crosschain/keeper/msg_server_vote_outbound_tx_test.go @@ -198,7 +198,7 @@ func TestKeeper_VoteOnObservedOutboundTx(t *testing.T) { keepertest.MockGetOutBound(observerMock, ctx) // Successfully mock ProcessOutbound - keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain) + keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain, 100) keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *senderChain, asset) _ = keepertest.MockUpdateNonce(observerMock, *senderChain) @@ -257,7 +257,7 @@ func TestKeeper_VoteOnObservedOutboundTx(t *testing.T) { keepertest.MockGetOutBound(observerMock, ctx) // Mock Failed ProcessOutbound - keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain) + 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) 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 index c45d65bf51..3178ee34b0 100644 --- a/x/crosschain/keeper/process_inbound.go +++ b/x/crosschain/keeper/process_inbound.go @@ -13,14 +13,14 @@ import ( // 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) + k.processZEVMDeposit(ctx, cctx) } else { - k.ProcessCrosschainMsgPassing(ctx, cctx) + 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: +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. @@ -30,7 +30,7 @@ Note : Aborted CCTXs are not refunded in this function. The refund is done using 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) { +func (k Keeper) processZEVMDeposit(ctx sdk.Context, cctx *types.CrossChainTx) { tmpCtx, commit := ctx.CacheContext() isContractReverted, err := k.HandleEVMDeposit(tmpCtx, cctx) @@ -92,13 +92,13 @@ func (k Keeper) ProcessZEVMDeposit(ctx sdk.Context, cctx *types.CrossChainTx) { } /* -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: +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) { +func (k Keeper) processCrosschainMsgPassing(ctx sdk.Context, cctx *types.CrossChainTx) { tmpCtx, commit := ctx.CacheContext() outboundReceiverChainID := cctx.GetCurrentOutTxParam().ReceiverChainId err := func() error { diff --git a/x/crosschain/keeper/process_inbound_test.go b/x/crosschain/keeper/process_inbound_test.go index 4b53f817de..491e362f78 100644 --- a/x/crosschain/keeper/process_inbound_test.go +++ b/x/crosschain/keeper/process_inbound_test.go @@ -8,6 +8,7 @@ import ( 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" @@ -16,7 +17,7 @@ import ( observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) -func TestKeeper_ProcessZEVMDeposit(t *testing.T) { +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, @@ -31,14 +32,15 @@ func TestKeeper_ProcessZEVMDeposit(t *testing.T) { fungibleMock.On("DepositCoinZeta", mock.Anything, receiver, amount). Return(nil) - // call ProcessZEVMDeposit + // 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.ProcessZEVMDeposit(ctx, cctx) + k.ProcessInbound(ctx, cctx) require.Equal(t, types.CctxStatus_OutboundMined, cctx.CctxStatus.Status) }) @@ -56,14 +58,15 @@ func TestKeeper_ProcessZEVMDeposit(t *testing.T) { fungibleMock.On("DepositCoinZeta", mock.Anything, receiver, amount). Return(fmt.Errorf("deposit error"), false) - // call ProcessZEVMDeposit + // 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.ProcessZEVMDeposit(ctx, cctx) + k.ProcessInbound(ctx, cctx) require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) require.Equal(t, "deposit error", cctx.CctxStatus.StatusMessage) }) @@ -90,9 +93,10 @@ func TestKeeper_ProcessZEVMDeposit(t *testing.T) { observerMock.On("GetSupportedChainFromChainID", mock.Anything, senderChain.ChainId). Return(nil) - // call ProcessZEVMDeposit + // call ProcessInbound cctx := GetERC20Cctx(t, receiver, *senderChain, "", amount) - k.ProcessZEVMDeposit(ctx, cctx) + 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) }) @@ -122,9 +126,10 @@ func TestKeeper_ProcessZEVMDeposit(t *testing.T) { fungibleMock.On("GetForeignCoinFromAsset", mock.Anything, asset, senderChain.ChainId). Return(fungibletypes.ForeignCoins{}, false) - // call ProcessZEVMDeposit + // call ProcessInbound cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) - k.ProcessZEVMDeposit(ctx, cctx) + 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) }) @@ -152,15 +157,53 @@ func TestKeeper_ProcessZEVMDeposit(t *testing.T) { keepertest.MockGetSupportedChainFromChainID(observerMock, senderChain) // mock successful GetRevertGasLimit for ERC20 - keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain) + keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain, 100) // mock unsuccessful PayGasInERC20AndUpdateCctx observerMock.On("GetSupportedChainFromChainID", mock.Anything, senderChain.ChainId). Return(nil).Once() - // call ProcessZEVMDeposit + // call ProcessInbound cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) - k.ProcessZEVMDeposit(ctx, cctx) + 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) }) @@ -188,7 +231,7 @@ func TestKeeper_ProcessZEVMDeposit(t *testing.T) { keepertest.MockGetSupportedChainFromChainID(observerMock, senderChain) // mock successful GetRevertGasLimit for ERC20 - keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain) + keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain, 100) // mock successful PayGasAndUpdateCctx keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *senderChain, asset) @@ -197,9 +240,10 @@ func TestKeeper_ProcessZEVMDeposit(t *testing.T) { observerMock.On("GetChainNonces", mock.Anything, senderChain.ChainName.String()). Return(observertypes.ChainNonces{}, false) - // call ProcessZEVMDeposit + // call ProcessInbound cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) - k.ProcessZEVMDeposit(ctx, cctx) + 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") }) @@ -227,16 +271,17 @@ func TestKeeper_ProcessZEVMDeposit(t *testing.T) { keepertest.MockGetSupportedChainFromChainID(observerMock, senderChain) // mock successful GetRevertGasLimit for ERC20 - keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain) + 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 ProcessZEVMDeposit + // call ProcessInbound cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) - k.ProcessZEVMDeposit(ctx, cctx) + 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) @@ -265,18 +310,19 @@ func TestKeeper_ProcessZEVMDeposit(t *testing.T) { keepertest.MockGetSupportedChainFromChainID(observerMock, senderChain) // mock successful GetRevertGasLimit for ERC20 - keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain) + keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain, 100) - // call ProcessZEVMDeposit + // call ProcessInbound cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount) + cctx.GetCurrentOutTxParam().ReceiverChainId = chains.ZetaPrivnetChain().ChainId cctx.OutboundTxParams = append(cctx.OutboundTxParams, cctx.GetCurrentOutTxParam()) - k.ProcessZEVMDeposit(ctx, cctx) + 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_ProcessCrosschainMsgPassing(t *testing.T) { +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, @@ -296,9 +342,9 @@ func TestKeeper_ProcessCrosschainMsgPassing(t *testing.T) { // mock successful UpdateNonce updatedNonce := keepertest.MockUpdateNonce(observerMock, *receiverChain) - // call ProcessCrosschainMsgPassing + // call ProcessInbound cctx := GetERC20Cctx(t, receiver, *receiverChain, "", amount) - k.ProcessCrosschainMsgPassing(ctx, cctx) + k.ProcessInbound(ctx, cctx) require.Equal(t, types.CctxStatus_PendingOutbound, cctx.CctxStatus.Status) require.Equal(t, updatedNonce, cctx.GetCurrentOutTxParam().OutboundTxTssNonce) }) @@ -319,9 +365,9 @@ func TestKeeper_ProcessCrosschainMsgPassing(t *testing.T) { observerMock.On("GetSupportedChainFromChainID", mock.Anything, receiverChain.ChainId). Return(nil).Once() - // call ProcessCrosschainMsgPassing + // call ProcessInbound cctx := GetERC20Cctx(t, receiver, *receiverChain, "", amount) - k.ProcessCrosschainMsgPassing(ctx, cctx) + k.ProcessInbound(ctx, cctx) require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) require.Equal(t, observertypes.ErrSupportedChains.Error(), cctx.CctxStatus.StatusMessage) }) @@ -346,9 +392,9 @@ func TestKeeper_ProcessCrosschainMsgPassing(t *testing.T) { observerMock.On("GetChainNonces", mock.Anything, receiverChain.ChainName.String()). Return(observertypes.ChainNonces{}, false) - // call ProcessCrosschainMsgPassing + // call ProcessInbound cctx := GetERC20Cctx(t, receiver, *receiverChain, "", amount) - k.ProcessCrosschainMsgPassing(ctx, cctx) + 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 index 23877e14f7..90837f17d8 100644 --- a/x/crosschain/keeper/process_outbound_test.go +++ b/x/crosschain/keeper/process_outbound_test.go @@ -67,7 +67,7 @@ func TestKeeper_ProcessFailedOutbound(t *testing.T) { asset := "" // mock successful GetRevertGasLimit for ERC20 - keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain) + keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain, 100) // mock successful PayGasAndUpdateCctx keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *senderChain, asset) @@ -82,7 +82,38 @@ func TestKeeper_ProcessFailedOutbound(t *testing.T) { 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) { @@ -100,7 +131,7 @@ func TestKeeper_ProcessFailedOutbound(t *testing.T) { asset := "" // mock successful GetRevertGasLimit for ERC20 - keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain) + keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain, 100) // mock successful PayGasAndUpdateCctx keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *senderChain, asset) @@ -131,7 +162,7 @@ func TestKeeper_ProcessFailedOutbound(t *testing.T) { asset := "" // mock successful GetRevertGasLimit for ERC20 - keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain) + keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain, 100) // mock successful PayGasAndUpdateCctx observerMock.On("GetSupportedChainFromChainID", mock.Anything, senderChain.ChainId). @@ -201,6 +232,15 @@ func TestKeeper_ProcessOutbound(t *testing.T) { 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, @@ -244,12 +284,16 @@ func TestKeeper_ProcessOutbound(t *testing.T) { 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) + keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain, 100) err := k.ProcessOutbound(ctx, cctx, observertypes.BallotStatus_BallotFinalized_FailureObservation, sample.String()) - require.ErrorContains(t, err, "cannot revert a revert") + require.Error(t, err) }) t.Run("successfully revert a outbound and create a new revert tx", func(t *testing.T) { @@ -270,7 +314,7 @@ func TestKeeper_ProcessOutbound(t *testing.T) { cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound oldOutTxParamsLen := len(cctx.OutboundTxParams) // mock successful GetRevertGasLimit for ERC20 - keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain) + keepertest.MockGetRevertGasLimitForERC20(fungibleMock, asset, *senderChain, 100) // mock successful PayGasAndUpdateCctx keepertest.MockPayGasAndUpdateCCTX(fungibleMock, observerMock, ctx, *k, *senderChain, asset) diff --git a/x/crosschain/keeper/refund_test.go b/x/crosschain/keeper/refund_test.go index b1b893f71e..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) @@ -143,6 +209,56 @@ func TestKeeper_RefundAmountOnZetaChainZeta(t *testing.T) { 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) @@ -249,6 +365,69 @@ 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) @@ -316,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/module.go b/x/crosschain/module.go index 45d23053da..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) } 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/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/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/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 729b0b9bcb..b1c6fcdffb 100644 --- a/x/crosschain/types/status.go +++ b/x/crosschain/types/status.go @@ -64,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) + }) +} From fca927f538cf4cd89e74db57a262f7b8ab5a91af Mon Sep 17 00:00:00 2001 From: skosito Date: Wed, 3 Apr 2024 18:33:36 +0100 Subject: [PATCH 03/10] test: improve observer module coverage (#1961) --- changelog.md | 1 + proto/observer/ballot.proto | 1 + testutil/keeper/mocks/observer/staking.go | 5 + typescript/observer/ballot_pb.d.ts | 2 + x/observer/abci.go | 4 - x/observer/abci_test.go | 98 ++++++++ x/observer/genesis.go | 2 +- x/observer/genesis_test.go | 188 ++++++++++---- x/observer/keeper/ballot_test.go | 161 +++++++++++- x/observer/keeper/blame_test.go | 110 +++++++++ x/observer/keeper/block_header_test.go | 32 +++ x/observer/keeper/chain_params_test.go | 30 ++- x/observer/keeper/crosschain_flags_test.go | 83 +++++++ x/observer/keeper/grpc_query_ballot_test.go | 136 +++++++++++ x/observer/keeper/grpc_query_blame_test.go | 180 ++++++++------ .../keeper/grpc_query_block_header_test.go | 119 +++++++++ .../keeper/grpc_query_chain_params_test.go | 97 ++++++++ .../grpc_query_crosschain_flags_test.go | 47 ++++ x/observer/keeper/grpc_query_keygen_test.go | 53 ++-- .../keeper/grpc_query_node_account_test.go | 22 +- x/observer/keeper/grpc_query_nonces_test.go | 81 ++++++ x/observer/keeper/grpc_query_observer_test.go | 78 ++++++ x/observer/keeper/grpc_query_params_test.go | 9 +- x/observer/keeper/grpc_query_prove_test.go | 72 ++++++ .../keeper/grpc_query_supported_chain_test.go | 21 ++ x/observer/keeper/grpc_query_tss_test.go | 230 ++++++++++++++++++ x/observer/keeper/hooks_test.go | 213 ++++++++++++++++ x/observer/keeper/keeper_test.go | 53 ---- x/observer/keeper/keygen_test.go | 20 +- .../keeper/msg_server_add_blame_vote_test.go | 182 ++++++++++++++ .../keeper/msg_server_add_observer_test.go | 117 +++++++++ .../keeper/msg_server_update_keygen_test.go | 105 ++++++++ x/observer/keeper/node_account_test.go | 28 ++- x/observer/keeper/nonce_to_cctx_test.go | 8 + x/observer/keeper/observer_set_test.go | 35 ++- x/observer/keeper/params_test.go | 6 +- x/observer/keeper/pending_nonces_test.go | 69 ++++++ x/observer/keeper/tss_funds_migrator_test.go | 7 +- x/observer/keeper/tss_test.go | 112 ++------- x/observer/keeper/utils.go | 3 +- x/observer/keeper/utils_test.go | 202 ++++++++++++++- x/observer/keeper/vote_inbound_test.go | 51 ++++ x/observer/keeper/vote_outbound_test.go | 42 ++++ x/observer/types/ballot.go | 6 + x/observer/types/ballot.pb.go | 1 + x/observer/types/ballot_test.go | 19 +- x/observer/types/chain_params_test.go | 20 ++ x/observer/types/crosschain_flags_test.go | 19 ++ x/observer/types/expected_keepers.go | 1 + x/observer/types/genesis_test.go | 19 ++ x/observer/types/observer_set_test.go | 32 +++ x/observer/types/params.go | 10 +- x/observer/types/params_test.go | 74 ++++++ x/observer/types/parsers_test.go | 98 ++++++++ x/observer/types/test_data.go | 27 -- x/observer/types/utils_test.go | 55 +++++ 56 files changed, 3112 insertions(+), 384 deletions(-) create mode 100644 x/observer/abci_test.go create mode 100644 x/observer/keeper/blame_test.go create mode 100644 x/observer/keeper/block_header_test.go create mode 100644 x/observer/keeper/crosschain_flags_test.go create mode 100644 x/observer/keeper/grpc_query_ballot_test.go create mode 100644 x/observer/keeper/grpc_query_block_header_test.go create mode 100644 x/observer/keeper/grpc_query_chain_params_test.go create mode 100644 x/observer/keeper/grpc_query_crosschain_flags_test.go create mode 100644 x/observer/keeper/grpc_query_observer_test.go create mode 100644 x/observer/keeper/grpc_query_prove_test.go create mode 100644 x/observer/keeper/grpc_query_supported_chain_test.go create mode 100644 x/observer/keeper/grpc_query_tss_test.go create mode 100644 x/observer/keeper/hooks_test.go delete mode 100644 x/observer/keeper/keeper_test.go create mode 100644 x/observer/keeper/msg_server_add_blame_vote_test.go create mode 100644 x/observer/keeper/msg_server_add_observer_test.go create mode 100644 x/observer/keeper/msg_server_update_keygen_test.go create mode 100644 x/observer/types/crosschain_flags_test.go create mode 100644 x/observer/types/observer_set_test.go create mode 100644 x/observer/types/params_test.go create mode 100644 x/observer/types/parsers_test.go delete mode 100644 x/observer/types/test_data.go create mode 100644 x/observer/types/utils_test.go diff --git a/changelog.md b/changelog.md index 8fe144056a..bc3070f7e1 100644 --- a/changelog.md +++ b/changelog.md @@ -51,6 +51,7 @@ * [1879](https://github.com/zeta-chain/node/pull/1879) - full coverage for messages in types packages * [1899](https://github.com/zeta-chain/node/pull/1899) - add empty test files so packages are included in coverage * [1903](https://github.com/zeta-chain/node/pull/1903) - common package tests +* [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 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/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/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/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/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_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/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 6408f4f49a..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) { +func TestKeeper_GetHistoricalTssByFinalizedHeight(t *testing.T) { k, 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 { - k.SetTSS(ctx, tss) - k.SetTSSHistory(ctx, tss) - } - request := &types.QueryTssHistoryRequest{} - response, err := k.TssHistory(wctx, request) - if tc.err != nil { - require.ErrorIs(t, err, tc.err) - } else { - require.Equal(t, len(tssList), len(response.TssList)) - prevTss, found := k.GetPreviousTSS(ctx) - require.Equal(t, tc.foundPrevious, found) - if found { - require.Equal(t, tssList[len(tssList)-2], prevTss) - } - } - }) + 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 322d45387f..566b559630 100644 --- a/x/observer/keeper/utils.go +++ b/x/observer/keeper/utils.go @@ -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 811a1b7688..71512c9f11 100644 --- a/x/observer/types/ballot.go +++ b/x/observer/types/ballot.go @@ -14,12 +14,18 @@ func (m Ballot) AddVote(address string, vote VoteType) (Ballot, error) { // `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/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/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/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) + }) + } +} From ae29968989042a2fb5cfff183b789f7e711c51db Mon Sep 17 00:00:00 2001 From: Charlie Chen <34498985+ws4charlie@users.noreply.github.com> Date: Wed, 3 Apr 2024 16:15:27 -0500 Subject: [PATCH 04/10] fix: zetaclient should check 'IsSupported' flag to pause/unpause a specific chain (#1969) * check IsSupported flag to pause/unpause a specific chain * added changelog entry * skip keysign if chain is not supported * make log printing less verbose on unsupported chain or invalid chain params * added comments for each observation go routine --- changelog.md | 1 + cmd/zetaclientd/utils.go | 8 +- zetaclient/bitcoin/bitcoin_client.go | 237 ++++++++++++---------- zetaclient/bitcoin/inbound_tracker.go | 28 +-- zetaclient/evm/evm_client.go | 194 +++++++++--------- zetaclient/evm/inbounds.go | 59 +++--- zetaclient/interfaces/interfaces.go | 2 +- zetaclient/testutils/stub/chain_client.go | 4 +- zetaclient/zetabridge/zetacore_bridge.go | 17 +- zetaclient/zetacore_observer.go | 4 + 10 files changed, 295 insertions(+), 259 deletions(-) diff --git a/changelog.md b/changelog.md index bc3070f7e1..3a9ab8a848 100644 --- a/changelog.md +++ b/changelog.md @@ -59,6 +59,7 @@ * [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 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/zetaclient/bitcoin/bitcoin_client.go b/zetaclient/bitcoin/bitcoin_client.go index 2fe823811b..34727e204b 100644 --- a/zetaclient/bitcoin/bitcoin_client.go +++ b/zetaclient/bitcoin/bitcoin_client.go @@ -45,19 +45,22 @@ import ( ) 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 @@ -89,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(), } } @@ -161,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 @@ -181,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, @@ -202,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 } @@ -216,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 @@ -273,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) { @@ -322,31 +322,36 @@ 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 !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 { @@ -363,7 +368,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() @@ -373,9 +378,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 } @@ -418,18 +423,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 +443,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 +458,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 +469,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 +483,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 +535,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 +546,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,10 +578,11 @@ 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() { 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 } @@ -584,13 +590,16 @@ func (ob *BTCChainClient) WatchGasPrice() { for { select { case <-ticker.C(): + if !ob.GetChainParams().IsSupported { + continue + } err := ob.PostGasPrice() if err != nil { - ob.logger.WatchGasPrice.Error().Err(err).Msg("PostGasPrice error on " + ob.chain.String()) + 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 } } @@ -605,7 +614,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 @@ -631,7 +640,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 @@ -678,7 +687,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) @@ -715,7 +724,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 } @@ -811,10 +820,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 } @@ -822,13 +832,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 } } @@ -837,7 +850,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) } }() @@ -911,7 +924,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 @@ -925,14 +938,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) } } @@ -972,10 +985,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 } } @@ -1077,15 +1090,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 } @@ -1093,9 +1107,12 @@ func (ob *BTCChainClient) observeOutTx() { for { select { case <-ticker.C(): + 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 { @@ -1103,16 +1120,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). @@ -1123,10 +1140,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) } } } @@ -1134,12 +1151,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 } } @@ -1151,17 +1168,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 @@ -1184,16 +1201,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) } } @@ -1403,7 +1420,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 { @@ -1421,7 +1438,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 @@ -1430,7 +1447,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) } } @@ -1438,7 +1455,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/inbound_tracker.go b/zetaclient/bitcoin/inbound_tracker.go index 2a6b80c124..4bf2b5b8ff 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 } @@ -21,13 +22,16 @@ func (ob *BTCChainClient) ExternalChainWatcherForNewInboundTrackerSuggestions() for { select { case <-ticker.C(): + 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 } } @@ -39,12 +43,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 } @@ -69,13 +73,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 } @@ -91,10 +95,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 84fbd19e83..f94536be25 100644 --- a/zetaclient/evm/evm_client.go +++ b/zetaclient/evm/evm_client.go @@ -57,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{} @@ -108,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) @@ -130,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 @@ -142,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 } @@ -156,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 } @@ -169,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(), } } @@ -252,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 } @@ -296,20 +301,20 @@ 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 @@ -606,19 +611,18 @@ 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() { +// WatchOutTx watches evm chain for outgoing txs status +func (ob *ChainClient) WatchOutTx() { // read env variables if set timeoutNonce, err := strconv.Atoi(os.Getenv("OS_TIMEOUT_NONCE")) if err != nil || timeoutNonce <= 0 { timeoutNonce = 100 * 3 // process up to 100 hashes } - ob.logger.ObserveOutTx.Info().Msgf("observeOutTx: using timeoutNonce %d seconds", timeoutNonce) + ob.logger.OutTx.Info().Msgf("WatchOutTx: using timeoutNonce %d seconds", timeoutNonce) - ticker, err := clienttypes.NewDynamicTicker(fmt.Sprintf("EVM_observeOutTx_%d", ob.chain.ChainId), ob.GetChainParams().OutTxTicker) + 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 } @@ -626,6 +630,9 @@ func (ob *ChainClient) observeOutTx() { for { select { case <-ticker.C(): + if !ob.GetChainParams().IsSupported { + continue + } trackers, err := ob.zetaClient.GetAllOutTxTrackerByChain(ob.chain.ChainId, interfaces.Ascending) if err != nil { continue @@ -644,17 +651,17 @@ func (ob *ChainClient) observeOutTx() { for _, txHash := range tracker.HashList { select { case <-outTimeout: - ob.logger.ObserveOutTx.Warn().Msgf("observeOutTx: timeout on chain %d nonce %d", ob.chain.ChainId, nonceInt) + ob.logger.OutTx.Warn().Msgf("WatchOutTx: timeout on chain %d nonce %d", ob.chain.ChainId, nonceInt) break TRACKERLOOP default: if recpt, tx, ok := ob.checkConfirmedTx(txHash.TxHash, nonceInt); ok { 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) } } } @@ -662,12 +669,12 @@ func (ob *ChainClient) observeOutTx() { 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 } } @@ -833,26 +840,31 @@ 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 !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 } } @@ -883,12 +895,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 } @@ -899,7 +911,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 @@ -966,7 +978,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 @@ -978,7 +990,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{ @@ -987,7 +999,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 } @@ -1001,7 +1013,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 { @@ -1027,7 +1039,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 @@ -1050,7 +1062,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{ @@ -1059,7 +1071,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 } @@ -1073,7 +1085,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 { @@ -1099,7 +1111,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 } @@ -1107,7 +1119,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 @@ -1127,10 +1139,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, flags observertypes.CrosschainFlags) 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 @@ -1140,14 +1148,14 @@ func (ob *ChainClient) ObserverTSSReceive(startBlock, toBlock uint64, flags obse 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 } } @@ -1155,41 +1163,45 @@ func (ob *ChainClient) ObserverTSSReceive(startBlock, toBlock uint64, flags obse return toBlock } +// WatchGasPrice watches evm chain for gas prices and post to zetacore func (ob *ChainClient) WatchGasPrice() { - ob.logger.WatchGasPrice.Info().Msg("WatchGasPrice starting...") + ob.logger.GasPrice.Info().Msg("WatchGasPrice starting...") err := ob.PostGasPrice() if err != nil { height, err := ob.zetaClient.GetBlockHeight() if err != nil { - ob.logger.WatchGasPrice.Error().Err(err).Msg("GetBlockHeight error") + ob.logger.GasPrice.Error().Err(err).Msg("GetBlockHeight error") } else { - ob.logger.WatchGasPrice.Error().Err(err).Msgf("PostGasPrice error at zeta block : %d ", height) + ob.logger.GasPrice.Error().Err(err).Msgf("PostGasPrice error at zeta block : %d ", height) } } 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 with interval %d", ob.GetChainParams().GasPriceTicker) + ob.logger.GasPrice.Info().Msgf("WatchGasPrice started with interval %d", ob.GetChainParams().GasPriceTicker) defer ticker.Stop() for { select { case <-ticker.C(): + if !ob.GetChainParams().IsSupported { + continue + } err = ob.PostGasPrice() if err != nil { height, err := ob.zetaClient.GetBlockHeight() if err != nil { - ob.logger.WatchGasPrice.Error().Err(err).Msg("GetBlockHeight error") + ob.logger.GasPrice.Error().Err(err).Msg("GetBlockHeight error") } else { - ob.logger.WatchGasPrice.Error().Err(err).Msgf("PostGasPrice error at zeta block : %d ", height) + ob.logger.GasPrice.Error().Err(err).Msgf("PostGasPrice error at zeta block : %d ", height) } } - 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 } } @@ -1200,12 +1212,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 } @@ -1214,7 +1226,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 @@ -1223,7 +1235,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 != "" { @@ -1264,7 +1276,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 { @@ -1297,7 +1309,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/inbounds.go b/zetaclient/evm/inbounds.go index 2daa4fc0c1..d6f6fcb223 100644 --- a/zetaclient/evm/inbounds.go +++ b/zetaclient/evm/inbounds.go @@ -26,30 +26,33 @@ import ( "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 !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 } } @@ -71,7 +74,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 { @@ -113,7 +116,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 @@ -121,7 +124,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 { @@ -154,7 +157,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 @@ -162,7 +165,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 { @@ -190,7 +193,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 { @@ -205,12 +208,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 } @@ -230,18 +233,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( @@ -266,7 +269,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) @@ -274,7 +277,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 } @@ -282,17 +285,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( @@ -324,7 +327,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 } @@ -333,10 +336,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/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/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/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 8ec76013a5..34d0774507 100644 --- a/zetaclient/zetacore_observer.go +++ b/zetaclient/zetacore_observer.go @@ -154,6 +154,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 { From f845afcf46976f25d926ffb9ed371d3c0c620803 Mon Sep 17 00:00:00 2001 From: Charlie Chen <34498985+ws4charlie@users.noreply.github.com> Date: Wed, 3 Apr 2024 23:46:33 -0500 Subject: [PATCH 05/10] feat: support Bitcoin P2TR, P2WSH, P2SH, P2PKH address types (#1982) * backport/add a AddressTaproot type that satisfies btcutil.Address interface * support taproot in DecodeBtcAddress added more unit tests * minor tweak * initial commit to support different types of BTC addresses * added decoding for more intx types * added btc intx address decoding and tests * fix compile error * adjusted btc outtx size and withdrawer fee * added changelog entry * fix unit tests * added e2e tests for different types of bitcoin addresses * try retriggering CI * e2e tests all PR * resolved some PR review feedback * removed panic from test method LoadObjectFromJSONFile * improved function and added comments * feat: add metric for concurrent key signs (#1960) * added live test for intx sender address parsing * fix CI unit test failure * updated changelog * format testdata and clean up empty lines --------- Co-authored-by: brewmaster012 <88689859+brewmaster012@users.noreply.github.com> Co-authored-by: lumtis Co-authored-by: kevinssgh <79858682+kevinssgh@users.noreply.github.com> --- changelog.md | 3 + cmd/zetae2e/local/local.go | 6 +- e2e/e2etests/e2etests.go | 53 +- e2e/e2etests/test_bitcoin_withdraw.go | 136 ++++- e2e/e2etests/test_bitcoin_withdraw_invalid.go | 1 + e2e/runner/bitcoin.go | 8 +- pkg/chains/address.go | 35 +- pkg/chains/address_taproot.go | 218 ++++++++ pkg/chains/address_taproot_test.go | 75 +++ pkg/chains/address_test.go | 254 ++++++++- x/crosschain/keeper/evm_hooks.go | 6 +- x/crosschain/keeper/evm_hooks_test.go | 14 +- x/crosschain/types/validate.go | 6 +- x/crosschain/types/validate_test.go | 9 +- zetaclient/bitcoin/bitcoin_client.go | 227 ++++---- ...pc_test.go => bitcoin_client_live_test.go} | 136 ++++- zetaclient/bitcoin/bitcoin_client_test.go | 387 ++++++++++++- zetaclient/bitcoin/bitcoin_signer.go | 163 +++--- zetaclient/bitcoin/bitcoin_signer_test.go | 398 ++++++------- zetaclient/bitcoin/bitcoin_test.go | 3 +- zetaclient/bitcoin/fee.go | 226 ++++++++ zetaclient/bitcoin/fee_test.go | 463 ++++++++++++++++ zetaclient/bitcoin/inbound_tracker.go | 2 +- zetaclient/bitcoin/tx_script.go | 229 ++++++++ zetaclient/bitcoin/tx_script_test.go | 521 ++++++++++++++++++ zetaclient/bitcoin/utils.go | 205 ------- zetaclient/bitcoin/utils_test.go | 85 --- zetaclient/compliance/compliance_test.go | 3 +- zetaclient/evm/evm_signer_test.go | 40 +- .../evm/outbound_transaction_data_test.go | 18 +- zetaclient/interfaces/interfaces.go | 1 + .../btc/block_trimmed_8332_831071.json | 108 ++++ ...bf7417cfc8a4d6f277ec11f40cd87319f04aa.json | 51 ++ ...1a18b9e853dbdf265ebb1c728f9b52813455a.json | 25 + ...aa8b767b4a5d6d60b143c2c50af52b257e867.json | 29 + ...573fda52c8fdbba5e78152aeb4432286836a7.json | 23 + ...42a17b25e481a88cc9119008e8f8296652697.json | 42 ++ ...a0b0f318feaea283185c1cddb8b341c27c016.json | 28 + ...3558981a35a360e3d1262a6675892c91322ca.json | 42 ++ ...ab055490650dbdaa6c2c8e380a7e075958a21.json | 42 ++ ...f7ca10039a66a474f91d23a17896f46e677a7.json | 42 ++ ...e4029ab33a99087fd5328a2331b52ff2ebe5b.json | 42 ++ ...f3552faf77b9d5688699a480261424b4f7e53.json | 73 +++ zetaclient/testutils/mempool_client.go | 55 +- zetaclient/testutils/stub/btc_rpc.go | 121 ++++ zetaclient/testutils/stub/tss_signer.go | 19 +- zetaclient/testutils/testdata.go | 50 +- zetaclient/testutils/testdata_naming.go | 11 + 48 files changed, 3879 insertions(+), 855 deletions(-) create mode 100644 pkg/chains/address_taproot.go create mode 100644 pkg/chains/address_taproot_test.go rename zetaclient/bitcoin/{bitcoin_client_rpc_test.go => bitcoin_client_live_test.go} (76%) create mode 100644 zetaclient/bitcoin/fee.go create mode 100644 zetaclient/bitcoin/fee_test.go create mode 100644 zetaclient/bitcoin/tx_script.go create mode 100644 zetaclient/bitcoin/tx_script_test.go delete mode 100644 zetaclient/bitcoin/utils_test.go create mode 100644 zetaclient/testdata/btc/block_trimmed_8332_831071.json create mode 100644 zetaclient/testdata/btc/chain_8332_intx_raw_result_847139aa65aa4a5ee896375951cbf7417cfc8a4d6f277ec11f40cd87319f04aa.json create mode 100644 zetaclient/testdata/btc/chain_8332_msgtx_211568441340fd5e10b1a8dcb211a18b9e853dbdf265ebb1c728f9b52813455a.json create mode 100644 zetaclient/testdata/btc/chain_8332_msgtx_3618e869f9e87863c0f1cc46dbbaa8b767b4a5d6d60b143c2c50af52b257e867.json create mode 100644 zetaclient/testdata/btc/chain_8332_msgtx_781fc8d41b476dbceca283ebff9573fda52c8fdbba5e78152aeb4432286836a7.json create mode 100644 zetaclient/testdata/btc/chain_8332_msgtx_c5d224963832fc0b9a597251c2342a17b25e481a88cc9119008e8f8296652697.json create mode 100644 zetaclient/testdata/btc/chain_8332_msgtx_d13de30b0cc53b5c4702b184ae0a0b0f318feaea283185c1cddb8b341c27c016.json create mode 100644 zetaclient/testdata/btc/chain_8332_tx_raw_result_P2PKH_9c741de6e17382b7a9113fc811e3558981a35a360e3d1262a6675892c91322ca.json create mode 100644 zetaclient/testdata/btc/chain_8332_tx_raw_result_P2SH_fd68c8b4478686ca6f5ae4c28eaab055490650dbdaa6c2c8e380a7e075958a21.json create mode 100644 zetaclient/testdata/btc/chain_8332_tx_raw_result_P2TR_259fc21e63e138136c8f19270a0f7ca10039a66a474f91d23a17896f46e677a7.json create mode 100644 zetaclient/testdata/btc/chain_8332_tx_raw_result_P2WPKH_5d09d232bfe41c7cb831bf53fc2e4029ab33a99087fd5328a2331b52ff2ebe5b.json create mode 100644 zetaclient/testdata/btc/chain_8332_tx_raw_result_P2WSH_791bb9d16f7ab05f70a116d18eaf3552faf77b9d5688699a480261424b4f7e53.json create mode 100644 zetaclient/testutils/stub/btc_rpc.go diff --git a/changelog.md b/changelog.md index 3a9ab8a848..62f22f2491 100644 --- a/changelog.md +++ b/changelog.md @@ -33,6 +33,7 @@ * [1755](https://github.com/zeta-chain/node/issues/1755) - use evm JSON RPC for inbound tx (including blob tx) observation. * [1815](https://github.com/zeta-chain/node/pull/1815) - add authority module for authorized actions * [1884](https://github.com/zeta-chain/node/pull/1884) - added zetatool cmd, added subcommand to filter deposits +* [1942](https://github.com/zeta-chain/node/pull/1982) - support Bitcoin P2TR, P2WSH, P2SH, P2PKH addresses * [1935](https://github.com/zeta-chain/node/pull/1935) - add an operational authority group * [1954](https://github.com/zeta-chain/node/pull/1954) - add metric for concurrent keysigns @@ -176,6 +177,7 @@ * [1675](https://github.com/zeta-chain/node/issues/1675) - use chain param ConfirmationCount for bitcoin confirmation ## Chores + * [1694](https://github.com/zeta-chain/node/pull/1694) - remove standalone network, use require testing package for the entire node folder ## Version: v12.1.0 @@ -188,6 +190,7 @@ * [1658](https://github.com/zeta-chain/node/pull/1658) - modify emission distribution to use fixed block rewards ### Fixes + * [1535](https://github.com/zeta-chain/node/issues/1535) - Avoid voting on wrong ballots due to false blockNumber in EVM tx receipt * [1588](https://github.com/zeta-chain/node/pull/1588) - fix chain params comparison logic * [1650](https://github.com/zeta-chain/node/pull/1605) - exempt (discounted) *system txs* from min gas price check and gas fee deduction diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index 6aef91156a..fe1e3faff7 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -250,7 +250,11 @@ func localE2ETest(cmd *cobra.Command, _ []string) { e2etests.TestZetaDepositRestrictedName, } bitcoinTests := []string{ - e2etests.TestBitcoinWithdrawName, + e2etests.TestBitcoinWithdrawSegWitName, + e2etests.TestBitcoinWithdrawTaprootName, + e2etests.TestBitcoinWithdrawLegacyName, + e2etests.TestBitcoinWithdrawP2SHName, + e2etests.TestBitcoinWithdrawP2WSHName, e2etests.TestBitcoinWithdrawInvalidAddressName, e2etests.TestZetaWithdrawBTCRevertName, e2etests.TestCrosschainSwapName, diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index 70bdc6de94..b5b598ed01 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -15,7 +15,11 @@ const ( TestZetaWithdrawBTCRevertName = "zeta_withdraw_btc_revert" // #nosec G101 - not a hardcoded password TestMessagePassingName = "message_passing" TestZRC20SwapName = "zrc20_swap" - TestBitcoinWithdrawName = "bitcoin_withdraw" + TestBitcoinWithdrawSegWitName = "bitcoin_withdraw_segwit" + TestBitcoinWithdrawTaprootName = "bitcoin_withdraw_taproot" + TestBitcoinWithdrawLegacyName = "bitcoin_withdraw_legacy" + TestBitcoinWithdrawP2WSHName = "bitcoin_withdraw_p2wsh" + TestBitcoinWithdrawP2SHName = "bitcoin_withdraw_p2sh" TestBitcoinWithdrawInvalidAddressName = "bitcoin_withdraw_invalid" TestBitcoinWithdrawRestrictedName = "bitcoin_withdraw_restricted" TestCrosschainSwapName = "crosschain_swap" @@ -130,12 +134,49 @@ var AllE2ETests = []runner.E2ETest{ TestZRC20Swap, ), runner.NewE2ETest( - TestBitcoinWithdrawName, - "withdraw BTC from ZEVM", + TestBitcoinWithdrawSegWitName, + "withdraw BTC from ZEVM to a SegWit address", []runner.ArgDefinition{ - runner.ArgDefinition{Description: "amount in btc", DefaultValue: "0.01"}, + runner.ArgDefinition{Description: "receiver address", DefaultValue: ""}, + runner.ArgDefinition{Description: "amount in btc", DefaultValue: "0.001"}, + }, + TestBitcoinWithdrawSegWit, + ), + runner.NewE2ETest( + TestBitcoinWithdrawTaprootName, + "withdraw BTC from ZEVM to a Taproot address", + []runner.ArgDefinition{ + runner.ArgDefinition{Description: "receiver address", DefaultValue: ""}, + runner.ArgDefinition{Description: "amount in btc", DefaultValue: "0.001"}, + }, + TestBitcoinWithdrawTaproot, + ), + runner.NewE2ETest( + TestBitcoinWithdrawLegacyName, + "withdraw BTC from ZEVM to a legacy address", + []runner.ArgDefinition{ + runner.ArgDefinition{Description: "receiver address", DefaultValue: ""}, + runner.ArgDefinition{Description: "amount in btc", DefaultValue: "0.001"}, }, - TestBitcoinWithdraw, + TestBitcoinWithdrawLegacy, + ), + runner.NewE2ETest( + TestBitcoinWithdrawP2WSHName, + "withdraw BTC from ZEVM to a P2WSH address", + []runner.ArgDefinition{ + runner.ArgDefinition{Description: "receiver address", DefaultValue: ""}, + runner.ArgDefinition{Description: "amount in btc", DefaultValue: "0.001"}, + }, + TestBitcoinWithdrawP2WSH, + ), + runner.NewE2ETest( + TestBitcoinWithdrawP2SHName, + "withdraw BTC from ZEVM to a P2SH address", + []runner.ArgDefinition{ + runner.ArgDefinition{Description: "receiver address", DefaultValue: ""}, + runner.ArgDefinition{Description: "amount in btc", DefaultValue: "0.001"}, + }, + TestBitcoinWithdrawP2SH, ), runner.NewE2ETest( TestBitcoinWithdrawInvalidAddressName, @@ -311,7 +352,7 @@ var AllE2ETests = []runner.E2ETest{ TestBitcoinWithdrawRestrictedName, "withdraw Bitcoin from ZEVM to restricted address", []runner.ArgDefinition{ - runner.ArgDefinition{Description: "amount in btc", DefaultValue: "0.01"}, + runner.ArgDefinition{Description: "amount in btc", DefaultValue: "0.001"}, }, TestBitcoinWithdrawRestricted, ), diff --git a/e2e/e2etests/test_bitcoin_withdraw.go b/e2e/e2etests/test_bitcoin_withdraw.go index 3920b7b3ce..cfb43eb72d 100644 --- a/e2e/e2etests/test_bitcoin_withdraw.go +++ b/e2e/e2etests/test_bitcoin_withdraw.go @@ -11,28 +11,98 @@ import ( "github.com/zeta-chain/zetacore/e2e/runner" "github.com/zeta-chain/zetacore/e2e/utils" "github.com/zeta-chain/zetacore/pkg/chains" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" "github.com/zeta-chain/zetacore/zetaclient/testutils" ) -func TestBitcoinWithdraw(r *runner.E2ERunner, args []string) { - if len(args) != 1 { - panic("TestBitcoinWithdraw requires exactly one argument for the amount.") +func TestBitcoinWithdrawSegWit(r *runner.E2ERunner, args []string) { + // check length of arguments + if len(args) != 2 { + panic("TestBitcoinWithdrawSegWit requires two arguments: [receiver, amount]") } + r.SetBtcAddress(r.Name, false) - withdrawalAmount, err := strconv.ParseFloat(args[0], 64) - if err != nil { - panic("Invalid withdrawal amount specified for TestBitcoinWithdraw.") + // parse arguments + defaultReceiver := r.BTCDeployerAddress.EncodeAddress() + receiver, amount := parseBitcoinWithdrawArgs(args, defaultReceiver) + _, ok := receiver.(*btcutil.AddressWitnessPubKeyHash) + if !ok { + panic("Invalid receiver address specified for TestBitcoinWithdrawSegWit.") } - withdrawalAmountSat, err := btcutil.NewAmount(withdrawalAmount) - if err != nil { - panic(err) + withdrawBTCZRC20(r, receiver, amount) +} + +func TestBitcoinWithdrawTaproot(r *runner.E2ERunner, args []string) { + // check length of arguments + if len(args) != 2 { + panic("TestBitcoinWithdrawTaproot requires two arguments: [receiver, amount]") } - amount := big.NewInt(int64(withdrawalAmountSat)) + r.SetBtcAddress(r.Name, false) + // parse arguments and withdraw BTC + defaultReceiver := "bcrt1pqqqsyqcyq5rqwzqfpg9scrgwpugpzysnzs23v9ccrydpk8qarc0sj9hjuh" + receiver, amount := parseBitcoinWithdrawArgs(args, defaultReceiver) + _, ok := receiver.(*chains.AddressTaproot) + if !ok { + panic("Invalid receiver address specified for TestBitcoinWithdrawTaproot.") + } + + withdrawBTCZRC20(r, receiver, amount) +} + +func TestBitcoinWithdrawLegacy(r *runner.E2ERunner, args []string) { + // check length of arguments + if len(args) != 2 { + panic("TestBitcoinWithdrawLegacy requires two arguments: [receiver, amount]") + } r.SetBtcAddress(r.Name, false) - WithdrawBitcoin(r, amount) + // parse arguments and withdraw BTC + defaultReceiver := "mxpYha3UJKUgSwsAz2qYRqaDSwAkKZ3YEY" + receiver, amount := parseBitcoinWithdrawArgs(args, defaultReceiver) + _, ok := receiver.(*btcutil.AddressPubKeyHash) + if !ok { + panic("Invalid receiver address specified for TestBitcoinWithdrawLegacy.") + } + + withdrawBTCZRC20(r, receiver, amount) +} + +func TestBitcoinWithdrawP2WSH(r *runner.E2ERunner, args []string) { + // check length of arguments + if len(args) != 2 { + panic("TestBitcoinWithdrawP2WSH requires two arguments: [receiver, amount]") + } + r.SetBtcAddress(r.Name, false) + + // parse arguments and withdraw BTC + defaultReceiver := "bcrt1qm9mzhyky4w853ft2ms6dtqdyyu3z2tmrq8jg8xglhyuv0dsxzmgs2f0sqy" + receiver, amount := parseBitcoinWithdrawArgs(args, defaultReceiver) + _, ok := receiver.(*btcutil.AddressWitnessScriptHash) + if !ok { + panic("Invalid receiver address specified for TestBitcoinWithdrawP2WSH.") + } + + withdrawBTCZRC20(r, receiver, amount) +} + +func TestBitcoinWithdrawP2SH(r *runner.E2ERunner, args []string) { + // check length of arguments + if len(args) != 2 { + panic("TestBitcoinWithdrawP2SH requires two arguments: [receiver, amount]") + } + r.SetBtcAddress(r.Name, false) + + // parse arguments and withdraw BTC + defaultReceiver := "2N6AoUj3KPS7wNGZXuCckh8YEWcSYNsGbqd" + receiver, amount := parseBitcoinWithdrawArgs(args, defaultReceiver) + _, ok := receiver.(*btcutil.AddressScriptHash) + if !ok { + panic("Invalid receiver address specified for TestBitcoinWithdrawP2SH.") + } + + withdrawBTCZRC20(r, receiver, amount) } func TestBitcoinWithdrawRestricted(r *runner.E2ERunner, args []string) { @@ -53,7 +123,37 @@ func TestBitcoinWithdrawRestricted(r *runner.E2ERunner, args []string) { r.SetBtcAddress(r.Name, false) - WithdrawBitcoinRestricted(r, amount) + withdrawBitcoinRestricted(r, amount) +} + +func parseBitcoinWithdrawArgs(args []string, defaultReceiver string) (btcutil.Address, *big.Int) { + // parse receiver address + var err error + var receiver btcutil.Address + if args[0] == "" { + // use the default receiver + receiver, err = chains.DecodeBtcAddress(defaultReceiver, chains.BtcRegtestChain().ChainId) + if err != nil { + panic("Invalid default receiver address specified for TestBitcoinWithdraw.") + } + } else { + receiver, err = chains.DecodeBtcAddress(args[0], chains.BtcRegtestChain().ChainId) + if err != nil { + panic("Invalid receiver address specified for TestBitcoinWithdraw.") + } + } + // parse the withdrawal amount + withdrawalAmount, err := strconv.ParseFloat(args[1], 64) + if err != nil { + panic("Invalid withdrawal amount specified for TestBitcoinWithdraw.") + } + withdrawalAmountSat, err := btcutil.NewAmount(withdrawalAmount) + if err != nil { + panic(err) + } + amount := big.NewInt(int64(withdrawalAmountSat)) + + return receiver, amount } func withdrawBTCZRC20(r *runner.E2ERunner, to btcutil.Address, amount *big.Int) *btcjson.TxRawResult { @@ -85,7 +185,13 @@ func withdrawBTCZRC20(r *runner.E2ERunner, to btcutil.Address, amount *big.Int) panic(err) } + // get cctx and check status cctx := utils.WaitCctxMinedByInTxHash(r.Ctx, receipt.TxHash.Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_OutboundMined { + panic(fmt.Errorf("cctx status is not OutboundMined")) + } + + // get bitcoin tx according to the outTxHash in cctx outTxHash := cctx.GetCurrentOutTxParam().OutboundTxHash hash, err := chainhash.NewHashFromStr(outTxHash) if err != nil { @@ -116,11 +222,7 @@ func withdrawBTCZRC20(r *runner.E2ERunner, to btcutil.Address, amount *big.Int) return rawTx } -func WithdrawBitcoin(r *runner.E2ERunner, amount *big.Int) { - withdrawBTCZRC20(r, r.BTCDeployerAddress, amount) -} - -func WithdrawBitcoinRestricted(r *runner.E2ERunner, amount *big.Int) { +func withdrawBitcoinRestricted(r *runner.E2ERunner, amount *big.Int) { // use restricted BTC P2WPKH address addressRestricted, err := chains.DecodeBtcAddress(testutils.RestrictedBtcAddressTest, chains.BtcRegtestChain().ChainId) if err != nil { diff --git a/e2e/e2etests/test_bitcoin_withdraw_invalid.go b/e2e/e2etests/test_bitcoin_withdraw_invalid.go index d168826493..39ff5e195b 100644 --- a/e2e/e2etests/test_bitcoin_withdraw_invalid.go +++ b/e2e/e2etests/test_bitcoin_withdraw_invalid.go @@ -48,6 +48,7 @@ func WithdrawToInvalidAddress(r *runner.E2ERunner, amount *big.Int) { stop := r.MineBlocks() // withdraw amount provided as test arg BTC from ZRC20 to BTC legacy address + // the address "1EYVvXLusCxtVuEwoYvWRyN5EZTXwPVvo3" is for mainnet, not regtest tx, err = r.BTCZRC20.Withdraw(r.ZEVMAuth, []byte("1EYVvXLusCxtVuEwoYvWRyN5EZTXwPVvo3"), amount) if err != nil { panic(err) diff --git a/e2e/runner/bitcoin.go b/e2e/runner/bitcoin.go index df0c2d1a23..6462b2f1db 100644 --- a/e2e/runner/bitcoin.go +++ b/e2e/runner/bitcoin.go @@ -267,14 +267,18 @@ func (runner *E2ERunner) SendToTSSFromDeployerWithMemo( } depositorFee := zetabitcoin.DefaultDepositorFee - events := zetabitcoin.FilterAndParseIncomingTx( + events, err := zetabitcoin.FilterAndParseIncomingTx( + btcRPC, []btcjson.TxRawResult{*rawtx}, 0, runner.BTCTSSAddress.EncodeAddress(), - &log.Logger, + log.Logger, runner.BitcoinParams, depositorFee, ) + if err != nil { + panic(err) + } runner.Logger.Info("bitcoin intx events:") for _, event := range events { runner.Logger.Info(" TxHash: %s", event.TxHash) diff --git a/pkg/chains/address.go b/pkg/chains/address.go index 6ee8647f8b..5eb0040485 100644 --- a/pkg/chains/address.go +++ b/pkg/chains/address.go @@ -51,6 +51,7 @@ func ConvertRecoverToError(r interface{}) error { } } +// DecodeBtcAddress decodes a BTC address from a given string and chainID func DecodeBtcAddress(inputAddress string, chainID int64) (address btcutil.Address, err error) { defer func() { if r := recover(); r != nil { @@ -63,13 +64,43 @@ func DecodeBtcAddress(inputAddress string, chainID int64) (address btcutil.Addre if err != nil { return nil, err } + if chainParams == nil { + return nil, fmt.Errorf("chain params not found") + } + // test taproot address type + address, err = DecodeTaprootAddress(inputAddress) + if err == nil { + if address.IsForNet(chainParams) { + return address, nil + } + return nil, fmt.Errorf("address %s is not for network %s", inputAddress, chainParams.Name) + } + // test taproot address failed; continue testing other types: P2WSH, P2WPKH, P2SH, P2PKH address, err = btcutil.DecodeAddress(inputAddress, chainParams) if err != nil { - return nil, fmt.Errorf("decode address failed: %s , for input address %s", err.Error(), inputAddress) + return nil, fmt.Errorf("decode address failed: %s, for input address %s", err.Error(), inputAddress) } ok := address.IsForNet(chainParams) if !ok { - return nil, fmt.Errorf("address is not for network %s", chainParams.Name) + return nil, fmt.Errorf("address %s is not for network %s", inputAddress, chainParams.Name) } return } + +// IsBtcAddressSupported returns true if the given BTC address is supported +func IsBtcAddressSupported(addr btcutil.Address) bool { + switch addr.(type) { + // P2TR address + case *AddressTaproot, + // P2WSH address + *btcutil.AddressWitnessScriptHash, + // P2WPKH address + *btcutil.AddressWitnessPubKeyHash, + // P2SH address + *btcutil.AddressScriptHash, + // P2PKH address + *btcutil.AddressPubKeyHash: + return true + } + return false +} diff --git a/pkg/chains/address_taproot.go b/pkg/chains/address_taproot.go new file mode 100644 index 0000000000..b79118ea85 --- /dev/null +++ b/pkg/chains/address_taproot.go @@ -0,0 +1,218 @@ +package chains + +import ( + "bytes" + "errors" + "fmt" + "strings" + + "github.com/btcsuite/btcd/btcutil/bech32" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcutil" +) + +// taproot address type + +type AddressSegWit struct { + hrp string + witnessVersion byte + witnessProgram []byte +} + +type AddressTaproot struct { + AddressSegWit +} + +var _ btcutil.Address = &AddressTaproot{} + +// NewAddressTaproot returns a new AddressTaproot. +func NewAddressTaproot(witnessProg []byte, + net *chaincfg.Params) (*AddressTaproot, error) { + + return newAddressTaproot(net.Bech32HRPSegwit, witnessProg) +} + +// newAddressWitnessScriptHash is an internal helper function to create an +// AddressWitnessScriptHash with a known human-readable part, rather than +// looking it up through its parameters. +func newAddressTaproot(hrp string, witnessProg []byte) (*AddressTaproot, error) { + // Check for valid program length for witness version 1, which is 32 + // for P2TR. + if len(witnessProg) != 32 { + return nil, errors.New("witness program must be 32 bytes for " + + "p2tr") + } + + addr := &AddressTaproot{ + AddressSegWit{ + hrp: strings.ToLower(hrp), + witnessVersion: 0x01, + witnessProgram: witnessProg, + }, + } + + return addr, nil +} + +// EncodeAddress returns the bech32 (or bech32m for SegWit v1) string encoding +// of an AddressSegWit. +// +// NOTE: This method is part of the Address interface. +func (a AddressSegWit) EncodeAddress() string { + str, err := encodeSegWitAddress( + a.hrp, a.witnessVersion, a.witnessProgram[:], + ) + if err != nil { + return "" + } + return str +} + +// encodeSegWitAddress creates a bech32 (or bech32m for SegWit v1) encoded +// address string representation from witness version and witness program. +func encodeSegWitAddress(hrp string, witnessVersion byte, witnessProgram []byte) (string, error) { + // Group the address bytes into 5 bit groups, as this is what is used to + // encode each character in the address string. + converted, err := bech32.ConvertBits(witnessProgram, 8, 5, true) + if err != nil { + return "", err + } + + // Concatenate the witness version and program, and encode the resulting + // bytes using bech32 encoding. + combined := make([]byte, len(converted)+1) + combined[0] = witnessVersion + copy(combined[1:], converted) + + var bech string + switch witnessVersion { + case 0: + bech, err = bech32.Encode(hrp, combined) + + case 1: + bech, err = bech32.EncodeM(hrp, combined) + + default: + return "", fmt.Errorf("unsupported witness version %d", + witnessVersion) + } + if err != nil { + return "", err + } + + // Check validity by decoding the created address. + _, version, program, err := decodeSegWitAddress(bech) + if err != nil { + return "", fmt.Errorf("invalid segwit address: %v", err) + } + + if version != witnessVersion || !bytes.Equal(program, witnessProgram) { + return "", fmt.Errorf("invalid segwit address") + } + + return bech, nil +} + +// decodeSegWitAddress parses a bech32 encoded segwit address string and +// returns the witness version and witness program byte representation. +func decodeSegWitAddress(address string) (string, byte, []byte, error) { + // Decode the bech32 encoded address. + hrp, data, bech32version, err := bech32.DecodeGeneric(address) + if err != nil { + return "", 0, nil, err + } + + // The first byte of the decoded address is the witness version, it must + // exist. + if len(data) < 1 { + return "", 0, nil, fmt.Errorf("no witness version") + } + + // ...and be <= 16. + version := data[0] + if version > 16 { + return "", 0, nil, fmt.Errorf("invalid witness version: %v", version) + } + + // The remaining characters of the address returned are grouped into + // words of 5 bits. In order to restore the original witness program + // bytes, we'll need to regroup into 8 bit words. + regrouped, err := bech32.ConvertBits(data[1:], 5, 8, false) + if err != nil { + return "", 0, nil, err + } + + // The regrouped data must be between 2 and 40 bytes. + if len(regrouped) < 2 || len(regrouped) > 40 { + return "", 0, nil, fmt.Errorf("invalid data length") + } + + // For witness version 0, address MUST be exactly 20 or 32 bytes. + if version == 0 && len(regrouped) != 20 && len(regrouped) != 32 { + return "", 0, nil, fmt.Errorf("invalid data length for witness "+ + "version 0: %v", len(regrouped)) + } + + // For witness version 0, the bech32 encoding must be used. + if version == 0 && bech32version != bech32.Version0 { + return "", 0, nil, fmt.Errorf("invalid checksum expected bech32 " + + "encoding for address with witness version 0") + } + + // For witness version 1, the bech32m encoding must be used. + if version == 1 && bech32version != bech32.VersionM { + return "", 0, nil, fmt.Errorf("invalid checksum expected bech32m " + + "encoding for address with witness version 1") + } + + return hrp, version, regrouped, nil +} + +// ScriptAddress returns the witness program for this address. +// +// NOTE: This method is part of the Address interface. +func (a *AddressSegWit) ScriptAddress() []byte { + return a.witnessProgram[:] +} + +// IsForNet returns whether the AddressSegWit is associated with the passed +// bitcoin network. +// +// NOTE: This method is part of the Address interface. +func (a *AddressSegWit) IsForNet(net *chaincfg.Params) bool { + return a.hrp == net.Bech32HRPSegwit +} + +// String returns a human-readable string for the AddressWitnessPubKeyHash. +// This is equivalent to calling EncodeAddress, but is provided so the type +// can be used as a fmt.Stringer. +// +// NOTE: This method is part of the Address interface. +func (a *AddressSegWit) String() string { + return a.EncodeAddress() +} + +// DecodeTaprootAddress decodes taproot address only and returns error on non-taproot address +func DecodeTaprootAddress(addr string) (*AddressTaproot, error) { + hrp, version, program, err := decodeSegWitAddress(addr) + if err != nil { + return nil, err + } + if version != 1 { + return nil, errors.New("invalid witness version; taproot address must be version 1") + } + return &AddressTaproot{ + AddressSegWit{ + hrp: hrp, + witnessVersion: version, + witnessProgram: program, + }, + }, nil +} + +// PayToWitnessTaprootScript creates a new script to pay to a version 1 +// (taproot) witness program. The passed hash is expected to be valid. +func PayToWitnessTaprootScript(rawKey []byte) ([]byte, error) { + return txscript.NewScriptBuilder().AddOp(txscript.OP_1).AddData(rawKey).Script() +} diff --git a/pkg/chains/address_taproot_test.go b/pkg/chains/address_taproot_test.go new file mode 100644 index 0000000000..d29e91a67d --- /dev/null +++ b/pkg/chains/address_taproot_test.go @@ -0,0 +1,75 @@ +package chains + +import ( + "encoding/hex" + "testing" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/stretchr/testify/require" +) + +func TestAddressTaproot(t *testing.T) { + { + // should parse mainnet taproot address + addrStr := "bc1p4ur084x8y63mj5hj7eydscuc4awals7ly749x8vhyquc0twcmvhquspa5c" + addr, err := DecodeTaprootAddress(addrStr) + require.Nil(t, err) + require.Equal(t, addrStr, addr.String()) + require.Equal(t, addrStr, addr.EncodeAddress()) + require.True(t, addr.IsForNet(&chaincfg.MainNetParams)) + } + { + // should parse testnet taproot address + addrStr := "tb1pzeclkt6upu8xwuksjcz36y4q56dd6jw5r543eu8j8238yaxpvcvq7t8f33" + addr, err := DecodeTaprootAddress(addrStr) + require.Nil(t, err) + require.Equal(t, addrStr, addr.String()) + require.Equal(t, addrStr, addr.EncodeAddress()) + require.True(t, addr.IsForNet(&chaincfg.TestNet3Params)) + } + { + // should parse regtest taproot address + addrStr := "bcrt1pqqqsyqcyq5rqwzqfpg9scrgwpugpzysnzs23v9ccrydpk8qarc0sj9hjuh" + addr, err := DecodeTaprootAddress(addrStr) + require.Nil(t, err) + require.Equal(t, addrStr, addr.String()) + require.Equal(t, addrStr, addr.EncodeAddress()) + require.True(t, addr.IsForNet(&chaincfg.RegressionNetParams)) + } + + { + // should fail to parse invalid taproot address + // should parse mainnet taproot address + addrStr := "bc1qysd4sp9q8my59ul9wsf5rvs9p387hf8vfwatzu" + _, err := DecodeTaprootAddress(addrStr) + require.Error(t, err) + } + { + var witnessProg [32]byte + for i := 0; i < 32; i++ { + witnessProg[i] = byte(i) + } + _, err := newAddressTaproot("bcrt", witnessProg[:]) + require.Nil(t, err) + //t.Logf("addr: %v", addr) + } + { + // should create correct taproot address from given witness program + // these hex string comes from link + // https://mempool.space/tx/41f7cbaaf9a8d378d09ee86de32eebef455225520cb71015cc9a7318fb42e326 + witnessProg, err := hex.DecodeString("af06f3d4c726a3b952f2f648d86398af5ddfc3df27aa531d97203987add8db2e") + require.Nil(t, err) + addr, err := NewAddressTaproot(witnessProg[:], &chaincfg.MainNetParams) + require.Nil(t, err) + require.Equal(t, addr.EncodeAddress(), "bc1p4ur084x8y63mj5hj7eydscuc4awals7ly749x8vhyquc0twcmvhquspa5c") + } + { + // should give correct ScriptAddress for taproot address + // example comes from + // https://blockstream.info/tx/09298a2f32f5267f419aeaf8a58c4807dcf6cac3edb59815a3b129cd8f1219b0?expand + addrStr := "bc1p6pls9gpm24g8ntl37pajpjtuhd3y08hs5rnf9a4n0wq595hwdh9suw7m2h" + addr, err := DecodeTaprootAddress(addrStr) + require.Nil(t, err) + require.Equal(t, "d07f02a03b555079aff1f07b20c97cbb62479ef0a0e692f6b37b8142d2ee6dcb", hex.EncodeToString(addr.ScriptAddress())) + } +} diff --git a/pkg/chains/address_test.go b/pkg/chains/address_test.go index f34b1145c5..bc9e8d8171 100644 --- a/pkg/chains/address_test.go +++ b/pkg/chains/address_test.go @@ -4,6 +4,7 @@ import ( "errors" "testing" + "github.com/btcsuite/btcutil" "github.com/stretchr/testify/require" . "gopkg.in/check.v1" @@ -65,12 +66,263 @@ func TestDecodeBtcAddress(t *testing.T) { t.Run("non legacy valid address with incorrect params", func(t *testing.T) { _, err := DecodeBtcAddress("bcrt1qy9pqmk2pd9sv63g27jt8r657wy0d9uee4x2dt2", BtcMainnetChain().ChainId) - require.ErrorContains(t, err, "address is not for network mainnet") + require.ErrorContains(t, err, "not for network mainnet") }) t.Run("non legacy valid address with correct params", func(t *testing.T) { _, err := DecodeBtcAddress("bcrt1qy9pqmk2pd9sv63g27jt8r657wy0d9uee4x2dt2", BtcRegtestChain().ChainId) require.NoError(t, err) }) + + t.Run("taproot address with correct params", func(t *testing.T) { + _, err := DecodeBtcAddress("bc1p4ur084x8y63mj5hj7eydscuc4awals7ly749x8vhyquc0twcmvhquspa5c", BtcMainnetChain().ChainId) + require.NoError(t, err) + }) + t.Run("taproot address with incorrect params", func(t *testing.T) { + _, err := DecodeBtcAddress("bc1p4ur084x8y63mj5hj7eydscuc4awals7ly749x8vhyquc0twcmvhquspa5c", BtcTestNetChain().ChainId) + require.ErrorContains(t, err, "not for network testnet") + }) +} + +func Test_IsBtcAddressSupported_P2TR(t *testing.T) { + tests := []struct { + name string + addr string + chainId int64 + supported bool + }{ + { + // https://mempool.space/tx/259fc21e63e138136c8f19270a0f7ca10039a66a474f91d23a17896f46e677a7 + name: "mainnet taproot address", + addr: "bc1p4scddlkkuw9486579autxumxmkvuphm5pz4jvf7f6pdh50p2uzqstawjt9", + chainId: BtcMainnetChain().ChainId, + supported: true, + }, + { + // https://mempool.space/testnet/tx/24991bd2fdc4f744bf7bbd915d4915925eecebdae249f81e057c0a6ffb700ab9 + name: "testnet taproot address", + addr: "tb1p7qqaucx69xtwkx7vwmhz03xjmzxxpy3hk29y7q06mt3k6a8sehhsu5lacw", + chainId: BtcTestNetChain().ChainId, + supported: true, + }, + { + name: "regtest taproot address", + addr: "bcrt1pqqqsyqcyq5rqwzqfpg9scrgwpugpzysnzs23v9ccrydpk8qarc0sj9hjuh", + chainId: BtcRegtestChain().ChainId, + supported: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // it should be a taproot address + addr, err := DecodeBtcAddress(tt.addr, tt.chainId) + require.NoError(t, err) + _, ok := addr.(*AddressTaproot) + require.True(t, ok) + + // it should be supported + require.NoError(t, err) + supported := IsBtcAddressSupported(addr) + require.Equal(t, tt.supported, supported) + }) + } +} + +func Test_IsBtcAddressSupported_P2WSH(t *testing.T) { + tests := []struct { + name string + addr string + chainId int64 + supported bool + }{ + { + // https://mempool.space/tx/791bb9d16f7ab05f70a116d18eaf3552faf77b9d5688699a480261424b4f7e53 + name: "mainnet P2WSH address", + addr: "bc1qqv6pwn470vu0tssdfha4zdk89v3c8ch5lsnyy855k9hcrcv3evequdmjmc", + chainId: BtcMainnetChain().ChainId, + supported: true, + }, + { + // https://mempool.space/testnet/tx/78fac3f0d4c0174c88d21c4bb1e23a8f007e890c6d2cfa64c97389ead16c51ed + name: "testnet P2WSH address", + addr: "tb1quhassyrlj43qar0mn0k5sufyp6mazmh2q85lr6ex8ehqfhxpzsksllwrsu", + chainId: BtcTestNetChain().ChainId, + supported: true, + }, + { + name: "regtest P2WSH address", + addr: "bcrt1qm9mzhyky4w853ft2ms6dtqdyyu3z2tmrq8jg8xglhyuv0dsxzmgs2f0sqy", + chainId: BtcRegtestChain().ChainId, + supported: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // it should be a P2WSH address + addr, err := DecodeBtcAddress(tt.addr, tt.chainId) + require.NoError(t, err) + _, ok := addr.(*btcutil.AddressWitnessScriptHash) + require.True(t, ok) + + // it should be supported + supported := IsBtcAddressSupported(addr) + require.Equal(t, tt.supported, supported) + }) + } +} + +func Test_IsBtcAddressSupported_P2WPKH(t *testing.T) { + tests := []struct { + name string + addr string + chainId int64 + supported bool + }{ + { + // https://mempool.space/tx/5d09d232bfe41c7cb831bf53fc2e4029ab33a99087fd5328a2331b52ff2ebe5b + name: "mainnet P2WPKH address", + addr: "bc1qaxf82vyzy8y80v000e7t64gpten7gawewzu42y", + chainId: BtcMainnetChain().ChainId, + supported: true, + }, + { + // https://mempool.space/testnet/tx/508b4d723c754bad001eae9b7f3c12377d3307bd5b595c27fd8a90089094f0e9 + name: "testnet P2WPKH address", + addr: "tb1q6rufg6myrxurdn0h57d2qhtm9zfmjw2mzcm05q", + chainId: BtcTestNetChain().ChainId, + supported: true, + }, + { + name: "regtest P2WPKH address", + addr: "bcrt1qy9pqmk2pd9sv63g27jt8r657wy0d9uee4x2dt2", + chainId: BtcRegtestChain().ChainId, + supported: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // it should be a P2WPKH address + addr, err := DecodeBtcAddress(tt.addr, tt.chainId) + require.NoError(t, err) + _, ok := addr.(*btcutil.AddressWitnessPubKeyHash) + require.True(t, ok) + + // it should be supported + supported := IsBtcAddressSupported(addr) + require.Equal(t, tt.supported, supported) + }) + } +} + +func Test_IsBtcAddressSupported_P2SH(t *testing.T) { + tests := []struct { + name string + addr string + chainId int64 + supported bool + }{ + { + // https://mempool.space/tx/fd68c8b4478686ca6f5ae4c28eaab055490650dbdaa6c2c8e380a7e075958a21 + name: "mainnet P2SH address", + addr: "327z4GyFM8Y8DiYfasGKQWhRK4MvyMSEgE", + chainId: BtcMainnetChain().ChainId, + supported: true, + }, + { + // https://mempool.space/testnet/tx/0c8c8f94817e0288a5273f5c971adaa3cee18a895c3ec8544785dddcd96f3848 + name: "testnet P2SH address 1", + addr: "2N6AoUj3KPS7wNGZXuCckh8YEWcSYNsGbqd", + chainId: BtcTestNetChain().ChainId, + supported: true, + }, + { + // https://mempool.space/testnet/tx/b5e074c5e021fcbd91ea14b1db29dfe5d14e1a6e046039467bf6ada7f8cc01b3 + name: "testnet P2SH address 2", + addr: "2MwbFpRpZWv4zREjbdLB9jVW3Q8xonpVeyE", + chainId: BtcTestNetChain().ChainId, + supported: true, + }, + { + name: "testnet P2SH address 1 should also be supported in regtest", + addr: "2N6AoUj3KPS7wNGZXuCckh8YEWcSYNsGbqd", + chainId: BtcRegtestChain().ChainId, + supported: true, + }, + { + name: "testnet P2SH address 2 should also be supported in regtest", + addr: "2MwbFpRpZWv4zREjbdLB9jVW3Q8xonpVeyE", + chainId: BtcRegtestChain().ChainId, + supported: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // it should be a P2SH address + addr, err := DecodeBtcAddress(tt.addr, tt.chainId) + require.NoError(t, err) + _, ok := addr.(*btcutil.AddressScriptHash) + require.True(t, ok) + + // it should be supported + supported := IsBtcAddressSupported(addr) + require.Equal(t, tt.supported, supported) + }) + } +} + +func Test_IsBtcAddressSupported_P2PKH(t *testing.T) { + tests := []struct { + name string + addr string + chainId int64 + supported bool + }{ + { + // https://mempool.space/tx/9c741de6e17382b7a9113fc811e3558981a35a360e3d1262a6675892c91322ca + name: "mainnet P2PKH address 1", + addr: "1FueivsE338W2LgifJ25HhTcVJ7CRT8kte", + chainId: BtcMainnetChain().ChainId, + supported: true, + }, + { + // https://mempool.space/testnet/tx/1e3974386f071de7f65cabb57346c1a22ec9b3e211a96928a98149673f681237 + name: "testnet P2PKH address 1", + addr: "mxpYha3UJKUgSwsAz2qYRqaDSwAkKZ3YEY", + chainId: BtcTestNetChain().ChainId, + supported: true, + }, + { + // https://mempool.space/testnet/tx/e48459f372727f2253b0ea8c71ded83e8270873b8a044feb3435fc7a799a648f + name: "testnet P2PKH address 2", + addr: "n1gXcqxmzwqHmqmgobe1XXuJaweSu69tZz", + chainId: BtcTestNetChain().ChainId, + supported: true, + }, + { + name: "testnet P2PKH address should also be supported in regtest", + addr: "mxpYha3UJKUgSwsAz2qYRqaDSwAkKZ3YEY", + chainId: BtcRegtestChain().ChainId, + supported: true, + }, + { + name: "testnet P2PKH address should also be supported in regtest", + addr: "n1gXcqxmzwqHmqmgobe1XXuJaweSu69tZz", + chainId: BtcRegtestChain().ChainId, + supported: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // it should be a P2PKH address + addr, err := DecodeBtcAddress(tt.addr, tt.chainId) + require.NoError(t, err) + _, ok := addr.(*btcutil.AddressPubKeyHash) + require.True(t, ok) + + // it should be supported + supported := IsBtcAddressSupported(addr) + require.Equal(t, tt.supported, supported) + }) + } } func TestConvertRecoverToError(t *testing.T) { diff --git a/x/crosschain/keeper/evm_hooks.go b/x/crosschain/keeper/evm_hooks.go index a662352532..5ca8c8e814 100644 --- a/x/crosschain/keeper/evm_hooks.go +++ b/x/crosschain/keeper/evm_hooks.go @@ -7,7 +7,6 @@ import ( errorsmod "cosmossdk.io/errors" "cosmossdk.io/math" - "github.com/btcsuite/btcutil" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ethereum/go-ethereum/accounts/abi/bind" ethcommon "github.com/ethereum/go-ethereum/common" @@ -310,9 +309,8 @@ func ValidateZrc20WithdrawEvent(event *zrc20.ZRC20Withdrawal, chainID int64) err if err != nil { return fmt.Errorf("ParseZRC20WithdrawalEvent: invalid address %s: %s", event.To, err) } - _, ok := addr.(*btcutil.AddressWitnessPubKeyHash) - if !ok { - return fmt.Errorf("ParseZRC20WithdrawalEvent: invalid address %s (not P2WPKH address)", event.To) + if !chains.IsBtcAddressSupported(addr) { + return fmt.Errorf("ParseZRC20WithdrawalEvent: unsupported address %s", string(event.To)) } } return nil diff --git a/x/crosschain/keeper/evm_hooks_test.go b/x/crosschain/keeper/evm_hooks_test.go index c75f638d3f..bd22eab38c 100644 --- a/x/crosschain/keeper/evm_hooks_test.go +++ b/x/crosschain/keeper/evm_hooks_test.go @@ -158,15 +158,16 @@ func TestValidateZrc20WithdrawEvent(t *testing.T) { btcMainNetWithdrawalEvent, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*sample.GetValidZRC20WithdrawToBTC(t).Logs[3]) require.NoError(t, err) err = crosschainkeeper.ValidateZrc20WithdrawEvent(btcMainNetWithdrawalEvent, chains.BtcTestNetChain().ChainId) - require.ErrorContains(t, err, "address is not for network testnet3") + require.ErrorContains(t, err, "invalid address") }) - t.Run("unable to validate a event with an invalid address type", func(t *testing.T) { + t.Run("unable to validate an unsupported address type", func(t *testing.T) { btcMainNetWithdrawalEvent, err := crosschainkeeper.ParseZRC20WithdrawalEvent(*sample.GetValidZRC20WithdrawToBTC(t).Logs[3]) require.NoError(t, err) - btcMainNetWithdrawalEvent.To = []byte("1EYVvXLusCxtVuEwoYvWRyN5EZTXwPVvo3") - err = crosschainkeeper.ValidateZrc20WithdrawEvent(btcMainNetWithdrawalEvent, chains.BtcTestNetChain().ChainId) - require.ErrorContains(t, err, "decode address failed: unknown address type") + btcMainNetWithdrawalEvent.To = []byte("04b2891ba8cb491828db3ebc8a780d43b169e7b3974114e6e50f9bab6ec" + + "63c2f20f6d31b2025377d05c2a704d3bd799d0d56f3a8543d79a01ab6084a1cb204f260") + err = crosschainkeeper.ValidateZrc20WithdrawEvent(btcMainNetWithdrawalEvent, chains.BtcMainnetChain().ChainId) + require.ErrorContains(t, err, "unsupported address") }) } @@ -734,7 +735,8 @@ func TestKeeper_ProcessLogs(t *testing.T) { k, ctx, sdkk, zk := keepertest.CrosschainKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, fungibletypes.ModuleName) - chain := chains.BtcMainnetChain() + // use the wrong (testnet) chain ID to make the btc address parsing fail + chain := chains.BtcTestNetChain() chainID := chain.ChainId setSupportedChain(ctx, zk, chainID) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) diff --git a/x/crosschain/types/validate.go b/x/crosschain/types/validate.go index 713eb8d432..fb83c0d1d6 100644 --- a/x/crosschain/types/validate.go +++ b/x/crosschain/types/validate.go @@ -5,7 +5,6 @@ import ( "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" @@ -58,9 +57,8 @@ func ValidateAddressForChain(address string, chainID int64) error { 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) + if !chains.IsBtcAddressSupported(addr) { + return fmt.Errorf("unsupported address %s", address) } return nil } diff --git a/x/crosschain/types/validate_test.go b/x/crosschain/types/validate_test.go index 206f99b39c..c768a7d4ab 100644 --- a/x/crosschain/types/validate_test.go +++ b/x/crosschain/types/validate_test.go @@ -10,15 +10,22 @@ import ( ) func TestValidateAddressForChain(t *testing.T) { + // test for eth chain 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)) + + // test for btc chain + require.NoError(t, types.ValidateAddressForChain("bc1p4scddlkkuw9486579autxumxmkvuphm5pz4jvf7f6pdh50p2uzqstawjt9", chains.BtcMainnetChain().ChainId)) + require.NoError(t, types.ValidateAddressForChain("327z4GyFM8Y8DiYfasGKQWhRK4MvyMSEgE", chains.BtcMainnetChain().ChainId)) + require.NoError(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)) + + // test for zeta chain require.NoError(t, types.ValidateAddressForChain("bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw", chains.ZetaChainMainnet().ChainId)) require.NoError(t, types.ValidateAddressForChain("0x792c127Fa3AC1D52F904056Baf1D9257391e7D78", chains.ZetaChainMainnet().ChainId)) } diff --git a/zetaclient/bitcoin/bitcoin_client.go b/zetaclient/bitcoin/bitcoin_client.go index 34727e204b..51bc1ab758 100644 --- a/zetaclient/bitcoin/bitcoin_client.go +++ b/zetaclient/bitcoin/bitcoin_client.go @@ -8,7 +8,6 @@ import ( "math/big" "os" "sort" - "strconv" "sync" "sync/atomic" "time" @@ -26,7 +25,6 @@ import ( "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" @@ -454,14 +452,19 @@ func (ob *BTCChainClient) ObserveInTx() error { // filter incoming txs to TSS address tssAddress := ob.Tss.BTCAddress() // #nosec G701 always positive - inTxs := FilterAndParseIncomingTx( + inTxs, err := FilterAndParseIncomingTx( + ob.rpcClient, res.Block.Tx, uint64(res.Block.Height), tssAddress, - &ob.logger.InTx, + ob.logger.InTx, ob.netParams, depositorFee, ) + if err != nil { + ob.logger.InTx.Error().Err(err).Msgf("observeInTxBTC: error filtering incoming txs for block %d", bn) + return err // we have to re-scan this block next time + } // post inbound vote message to zetacore for _, inTx := range inTxs { @@ -661,29 +664,30 @@ type BTCInTxEvnet struct { // vout0: p2wpkh to the TSS address (targetAddress) // vout1: OP_RETURN memo, base64 encoded func FilterAndParseIncomingTx( + rpcClient interfaces.BTCRPCClient, txs []btcjson.TxRawResult, blockNumber uint64, - targetAddress string, - logger *zerolog.Logger, + tssAddress string, + logger zerolog.Logger, netParams *chaincfg.Params, depositorFee float64, -) []*BTCInTxEvnet { +) ([]*BTCInTxEvnet, error) { inTxs := make([]*BTCInTxEvnet, 0) for idx, tx := range txs { if idx == 0 { continue // the first tx is coinbase; we do not process coinbase tx } - inTx, err := GetBtcEvent(tx, targetAddress, blockNumber, logger, netParams, depositorFee) + inTx, err := GetBtcEvent(rpcClient, tx, tssAddress, blockNumber, logger, netParams, depositorFee) if err != nil { - logger.Error().Err(err).Msgf("FilterAndParseIncomingTx: error getting btc event for tx %s in block %d", tx.Txid, blockNumber) - continue + // unable to parse the tx, the caller should retry + return nil, errors.Wrapf(err, "error getting btc event for tx %s in block %d", tx.Txid, blockNumber) } if inTx != nil { inTxs = append(inTxs, inTx) logger.Info().Msgf("FilterAndParseIncomingTx: found btc event for tx %s in block %d", tx.Txid, blockNumber) } } - return inTxs + return inTxs, nil } func (ob *BTCChainClient) GetInboundVoteMessageFromBtcEvent(inTx *BTCInTxEvnet) *types.MsgVoteOnObservedInboundTx { @@ -731,11 +735,14 @@ func (ob *BTCChainClient) IsInTxRestricted(inTx *BTCInTxEvnet) bool { return false } +// GetBtcEvent either returns a valid BTCInTxEvent or nil +// Note: the caller should retry the tx on error (e.g., GetSenderAddressByVin failed) func GetBtcEvent( + rpcClient interfaces.BTCRPCClient, tx btcjson.TxRawResult, - targetAddress string, + tssAddress string, blockNumber uint64, - logger *zerolog.Logger, + logger zerolog.Logger, netParams *chaincfg.Params, depositorFee float64, ) (*BTCInTxEvnet, error) { @@ -743,74 +750,46 @@ func GetBtcEvent( var value float64 var memo []byte if len(tx.Vout) >= 2 { - // first vout must to addressed to the targetAddress with p2wpkh scriptPubKey - out := tx.Vout[0] - script := out.ScriptPubKey.Hex - if len(script) == 44 && script[:4] == "0014" { // segwit output: 0x00 + 20 bytes of pubkey hash - hash, err := hex.DecodeString(script[4:]) - if err != nil { + // 1st vout must have tss address as receiver with p2wpkh scriptPubKey + vout0 := tx.Vout[0] + script := vout0.ScriptPubKey.Hex + if len(script) == 44 && script[:4] == "0014" { // P2WPKH output: 0x00 + 20 bytes of pubkey hash + receiver, err := DecodeScriptP2WPKH(vout0.ScriptPubKey.Hex, netParams) + if err != nil { // should never happen return nil, err } - wpkhAddress, err := btcutil.NewAddressWitnessPubKeyHash(hash, netParams) - if err != nil { - return nil, err - } - if wpkhAddress.EncodeAddress() != targetAddress { - return nil, nil // irrelevant tx to us, skip + // skip irrelevant tx to us + if receiver != tssAddress { + return nil, nil } // deposit amount has to be no less than the minimum depositor fee - if out.Value < depositorFee { - return nil, fmt.Errorf("btc deposit amount %v in txid %s is less than depositor fee %v", value, tx.Txid, depositorFee) + if vout0.Value < depositorFee { + logger.Info().Msgf("GetBtcEvent: btc deposit amount %v in txid %s is less than depositor fee %v", vout0.Value, tx.Txid, depositorFee) + return nil, nil } - value = out.Value - depositorFee + value = vout0.Value - depositorFee - out = tx.Vout[1] - script = out.ScriptPubKey.Hex - if len(script) >= 4 && script[:2] == "6a" { // OP_RETURN - memoSize, err := strconv.ParseInt(script[2:4], 16, 32) - if err != nil { - return nil, errors.Wrapf(err, "error decoding pubkey hash") - } - if int(memoSize) != (len(script)-4)/2 { - return nil, fmt.Errorf("memo size mismatch: %d != %d", memoSize, (len(script)-4)/2) - } - memoBytes, err := hex.DecodeString(script[4:]) - if err != nil { - logger.Warn().Err(err).Msgf("error hex decoding memo") - return nil, fmt.Errorf("error hex decoding memo: %s", err) - } - if bytes.Equal(memoBytes, []byte(constant.DonationMessage)) { - logger.Info().Msgf("donation tx: %s; value %f", tx.Txid, value) - return nil, fmt.Errorf("donation tx: %s; value %f", tx.Txid, value) - } - memo = memoBytes - found = true + // 2nd vout must be a valid OP_RETURN memo + vout1 := tx.Vout[1] + memo, found, err = DecodeOpReturnMemo(vout1.ScriptPubKey.Hex, tx.Txid) + if err != nil { + logger.Error().Err(err).Msgf("GetBtcEvent: error decoding OP_RETURN memo: %s", vout1.ScriptPubKey.Hex) + return nil, nil } } } + // event found, get sender address if found { - logger.Info().Msgf("found bitcoin intx: %s", tx.Txid) - var fromAddress string - if len(tx.Vin) > 0 { - vin := tx.Vin[0] - //log.Info().Msgf("vin: %v", vin.Witness) - if len(vin.Witness) == 2 { - pk := vin.Witness[1] - pkBytes, err := hex.DecodeString(pk) - if err != nil { - return nil, errors.Wrapf(err, "error decoding pubkey") - } - hash := btcutil.Hash160(pkBytes) - addr, err := btcutil.NewAddressWitnessPubKeyHash(hash, netParams) - if err != nil { - return nil, errors.Wrapf(err, "error decoding pubkey hash") - } - fromAddress = addr.EncodeAddress() - } + if len(tx.Vin) == 0 { // should never happen + return nil, fmt.Errorf("GetBtcEvent: no input found for intx: %s", tx.Txid) + } + fromAddress, err := GetSenderAddressByVin(rpcClient, tx.Vin[0], netParams) + if err != nil { + return nil, errors.Wrapf(err, "error getting sender address for intx: %s", tx.Txid) } return &BTCInTxEvnet{ FromAddress: fromAddress, - ToAddress: targetAddress, + ToAddress: tssAddress, Value: value, MemoBytes: memo, BlockNumber: blockNumber, @@ -820,6 +799,45 @@ func GetBtcEvent( return nil, nil } +// GetSenderAddressByVin get the sender address from the previous transaction +func GetSenderAddressByVin(rpcClient interfaces.BTCRPCClient, vin btcjson.Vin, net *chaincfg.Params) (string, error) { + // query previous raw transaction by txid + // GetTransaction requires reconfiguring the bitcoin node (txindex=1), so we use GetRawTransaction instead + hash, err := chainhash.NewHashFromStr(vin.Txid) + if err != nil { + return "", err + } + tx, err := rpcClient.GetRawTransaction(hash) + if err != nil { + return "", errors.Wrapf(err, "error getting raw transaction %s", vin.Txid) + } + // #nosec G701 - always in range + if len(tx.MsgTx().TxOut) <= int(vin.Vout) { + return "", fmt.Errorf("vout index %d out of range for tx %s", vin.Vout, vin.Txid) + } + + // decode sender address from previous pkScript + pkScript := tx.MsgTx().TxOut[vin.Vout].PkScript + scriptHex := hex.EncodeToString(pkScript) + if IsPkScriptP2TR(pkScript) { + return DecodeScriptP2TR(scriptHex, net) + } + if IsPkScriptP2WSH(pkScript) { + return DecodeScriptP2WSH(scriptHex, net) + } + if IsPkScriptP2WPKH(pkScript) { + return DecodeScriptP2WPKH(scriptHex, net) + } + if IsPkScriptP2SH(pkScript) { + return DecodeScriptP2SH(scriptHex, net) + } + if IsPkScriptP2PKH(pkScript) { + return DecodeScriptP2PKH(scriptHex, net) + } + // sender address not found, return nil and move on to the next tx + return "", 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) @@ -967,7 +985,7 @@ func (ob *BTCChainClient) getOutTxidByNonce(nonce uint64, test bool) (string, er return "", fmt.Errorf("getOutTxidByNonce: cannot find outTx txid for nonce %d", nonce) } // make sure it's a real Bitcoin txid - _, getTxResult, err := ob.GetTxResultByHash(txid) + _, getTxResult, err := GetTxResultByHash(ob.rpcClient, txid) if err != nil { return "", errors.Wrapf(err, "getOutTxidByNonce: error getting outTx result for nonce %d hash %s", nonce, txid) } @@ -987,7 +1005,7 @@ func (ob *BTCChainClient) findNonceMarkUTXO(nonce uint64, txid string) (int, err if err != nil { ob.logger.OutTx.Error().Err(err).Msgf("findNonceMarkUTXO: error getting satoshis for utxo %v", utxo) } - if utxo.Address == tssAddress && sats == amount && utxo.TxID == txid { + if utxo.Address == tssAddress && sats == amount && utxo.TxID == txid && utxo.Vout == 0 { ob.logger.OutTx.Info().Msgf("findNonceMarkUTXO: found nonce-mark utxo with txid %s, amount %d satoshi", utxo.TxID, sats) return i, nil } @@ -1166,7 +1184,7 @@ func (ob *BTCChainClient) WatchOutTx() { // Note: if txResult is nil, then inMempool flag should be ignored. func (ob *BTCChainClient) checkIncludedTx(cctx *types.CrossChainTx, txHash string) (*btcjson.GetTransactionResult, bool) { outTxID := ob.GetTxID(cctx.GetCurrentOutTxParam().OutboundTxTssNonce) - hash, getTxResult, err := ob.GetTxResultByHash(txHash) + hash, getTxResult, err := GetTxResultByHash(ob.rpcClient, txHash) if err != nil { ob.logger.OutTx.Error().Err(err).Msgf("checkIncludedTx: error GetTxResultByHash: %s", txHash) return nil, false @@ -1240,7 +1258,7 @@ func (ob *BTCChainClient) removeIncludedTx(nonce uint64) { func (ob *BTCChainClient) checkTssOutTxResult(cctx *types.CrossChainTx, hash *chainhash.Hash, res *btcjson.GetTransactionResult) error { params := cctx.GetCurrentOutTxParam() nonce := params.OutboundTxTssNonce - rawResult, err := ob.getRawTxResult(hash, res) + rawResult, err := GetRawTxResult(ob.rpcClient, hash, res) if err != nil { return errors.Wrapf(err, "checkTssOutTxResult: error GetRawTxResultByHash %s", hash.String()) } @@ -1264,23 +1282,25 @@ func (ob *BTCChainClient) checkTssOutTxResult(cctx *types.CrossChainTx, hash *ch return nil } -func (ob *BTCChainClient) GetTxResultByHash(txID string) (*chainhash.Hash, *btcjson.GetTransactionResult, error) { +// GetTxResultByHash gets the transaction result by hash +func GetTxResultByHash(rpcClient interfaces.BTCRPCClient, txID string) (*chainhash.Hash, *btcjson.GetTransactionResult, error) { hash, err := chainhash.NewHashFromStr(txID) if err != nil { return nil, nil, errors.Wrapf(err, "GetTxResultByHash: error NewHashFromStr: %s", txID) } // The Bitcoin node has to be configured to watch TSS address - txResult, err := ob.rpcClient.GetTransaction(hash) + txResult, err := rpcClient.GetTransaction(hash) if err != nil { return nil, nil, errors.Wrapf(err, "GetOutTxByTxHash: error GetTransaction %s", hash.String()) } return hash, txResult, nil } -func (ob *BTCChainClient) getRawTxResult(hash *chainhash.Hash, res *btcjson.GetTransactionResult) (btcjson.TxRawResult, error) { +// GetRawTxResult gets the raw tx result +func GetRawTxResult(rpcClient interfaces.BTCRPCClient, hash *chainhash.Hash, res *btcjson.GetTransactionResult) (btcjson.TxRawResult, error) { if res.Confirmations == 0 { // for pending tx, we query the raw tx directly - rawResult, err := ob.rpcClient.GetRawTransactionVerbose(hash) // for pending tx, we query the raw tx + rawResult, err := rpcClient.GetRawTransactionVerbose(hash) // for pending tx, we query the raw tx if err != nil { return btcjson.TxRawResult{}, errors.Wrapf(err, "getRawTxResult: error GetRawTransactionVerbose %s", res.TxID) } @@ -1290,7 +1310,7 @@ func (ob *BTCChainClient) getRawTxResult(hash *chainhash.Hash, res *btcjson.GetT if err != nil { return btcjson.TxRawResult{}, errors.Wrapf(err, "getRawTxResult: error NewHashFromStr for block hash %s", res.BlockHash) } - block, err := ob.rpcClient.GetBlockVerboseTx(blkHash) + block, err := rpcClient.GetBlockVerboseTx(blkHash) if err != nil { return btcjson.TxRawResult{}, errors.Wrapf(err, "getRawTxResult: error GetBlockVerboseTx %s", res.BlockHash) } @@ -1349,33 +1369,35 @@ func (ob *BTCChainClient) checkTSSVout(params *types.OutboundTxParams, vouts []b nonce := params.OutboundTxTssNonce tssAddress := ob.Tss.BTCAddress() for _, vout := range vouts { - recvAddress, amount, err := DecodeP2WPKHVout(vout, ob.chain) + // decode receiver and amount from vout + receiverExpected := tssAddress + if vout.N == 1 { + // the 2nd output is the payment to recipient + receiverExpected = params.Receiver + } + receiverVout, amount, err := DecodeTSSVout(vout, receiverExpected, ob.chain) if err != nil { - return errors.Wrap(err, "checkTSSVout: error decoding P2WPKH vout") + return err } - // 1st vout: nonce-mark - if vout.N == 0 { - if recvAddress != tssAddress { - return fmt.Errorf("checkTSSVout: nonce-mark address %s not match TSS address %s", recvAddress, tssAddress) + switch vout.N { + case 0: // 1st vout: nonce-mark + if receiverVout != tssAddress { + return fmt.Errorf("checkTSSVout: nonce-mark address %s not match TSS address %s", receiverVout, tssAddress) } if amount != chains.NonceMarkAmount(nonce) { return fmt.Errorf("checkTSSVout: nonce-mark amount %d not match nonce-mark amount %d", amount, chains.NonceMarkAmount(nonce)) } - } - // 2nd vout: payment to recipient - if vout.N == 1 { - if recvAddress != params.Receiver { - return fmt.Errorf("checkTSSVout: output address %s not match params receiver %s", recvAddress, params.Receiver) + case 1: // 2nd vout: payment to recipient + if receiverVout != params.Receiver { + return fmt.Errorf("checkTSSVout: output address %s not match params receiver %s", receiverVout, params.Receiver) } // #nosec G701 always positive if uint64(amount) != params.Amount.Uint64() { return fmt.Errorf("checkTSSVout: output amount %d not match params amount %d", amount, params.Amount) } - } - // 3rd vout: change to TSS (optional) - if vout.N == 2 { - if recvAddress != tssAddress { - return fmt.Errorf("checkTSSVout: change address %s not match TSS address %s", recvAddress, tssAddress) + case 2: // 3rd vout: change to TSS (optional) + if receiverVout != tssAddress { + return fmt.Errorf("checkTSSVout: change address %s not match TSS address %s", receiverVout, tssAddress) } } } @@ -1394,23 +1416,22 @@ func (ob *BTCChainClient) checkTSSVoutCancelled(params *types.OutboundTxParams, nonce := params.OutboundTxTssNonce tssAddress := ob.Tss.BTCAddress() for _, vout := range vouts { - recvAddress, amount, err := DecodeP2WPKHVout(vout, ob.chain) + // decode receiver and amount from vout + receiverVout, amount, err := DecodeTSSVout(vout, tssAddress, ob.chain) if err != nil { return errors.Wrap(err, "checkTSSVoutCancelled: error decoding P2WPKH vout") } - // 1st vout: nonce-mark - if vout.N == 0 { - if recvAddress != tssAddress { - return fmt.Errorf("checkTSSVoutCancelled: nonce-mark address %s not match TSS address %s", recvAddress, tssAddress) + switch vout.N { + case 0: // 1st vout: nonce-mark + if receiverVout != tssAddress { + return fmt.Errorf("checkTSSVoutCancelled: nonce-mark address %s not match TSS address %s", receiverVout, tssAddress) } if amount != chains.NonceMarkAmount(nonce) { return fmt.Errorf("checkTSSVoutCancelled: nonce-mark amount %d not match nonce-mark amount %d", amount, chains.NonceMarkAmount(nonce)) } - } - // 2nd vout: change to TSS (optional) - if vout.N == 2 { - if recvAddress != tssAddress { - return fmt.Errorf("checkTSSVoutCancelled: change address %s not match TSS address %s", recvAddress, tssAddress) + case 1: // 2nd vout: change to TSS (optional) + if receiverVout != tssAddress { + return fmt.Errorf("checkTSSVoutCancelled: change address %s not match TSS address %s", receiverVout, tssAddress) } } } diff --git a/zetaclient/bitcoin/bitcoin_client_rpc_test.go b/zetaclient/bitcoin/bitcoin_client_live_test.go similarity index 76% rename from zetaclient/bitcoin/bitcoin_client_rpc_test.go rename to zetaclient/bitcoin/bitcoin_client_live_test.go index d4f65ed84a..522b604d19 100644 --- a/zetaclient/bitcoin/bitcoin_client_rpc_test.go +++ b/zetaclient/bitcoin/bitcoin_client_live_test.go @@ -9,13 +9,6 @@ import ( "testing" "time" - "github.com/zeta-chain/zetacore/pkg/chains" - appcontext "github.com/zeta-chain/zetacore/zetaclient/app_context" - clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" - "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/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" @@ -25,12 +18,18 @@ import ( "github.com/rs/zerolog/log" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" + "github.com/zeta-chain/zetacore/pkg/chains" + appcontext "github.com/zeta-chain/zetacore/zetaclient/app_context" + clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" + "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/testutils" ) type BitcoinClientTestSuite struct { suite.Suite - BitcoinChainClient *BTCChainClient + rpcClient *rpcclient.Client } func (suite *BitcoinClientTestSuite) SetupTest() { @@ -50,7 +49,8 @@ func (suite *BitcoinClientTestSuite) SetupTest() { client, err := NewBitcoinClient(appContext, chains.BtcRegtestChain(), nil, tss, tempSQLiteDbPath, clientcommon.DefaultLoggers(), config.BTCConfig{}, nil) suite.Require().NoError(err) - suite.BitcoinChainClient = client + suite.rpcClient, err = getRPCClient(18332) + suite.Require().NoError(err) skBytes, err := hex.DecodeString(skHex) suite.Require().NoError(err) suite.T().Logf("skBytes: %d", len(skBytes)) @@ -127,10 +127,10 @@ func getFeeRate(client *rpcclient.Client, confTarget int64, estimateMode *btcjso // All methods that begin with "Test" are run as tests within a // suite. func (suite *BitcoinClientTestSuite) Test1() { - feeResult, err := suite.BitcoinChainClient.rpcClient.EstimateSmartFee(1, nil) + feeResult, err := suite.rpcClient.EstimateSmartFee(1, nil) suite.Require().NoError(err) suite.T().Logf("fee result: %f", *feeResult.FeeRate) - bn, err := suite.BitcoinChainClient.rpcClient.GetBlockCount() + bn, err := suite.rpcClient.GetBlockCount() suite.Require().NoError(err) suite.T().Logf("block %d", bn) @@ -139,21 +139,21 @@ func (suite *BitcoinClientTestSuite) Test1() { err = chainhash.Decode(&hash, hashStr) suite.Require().NoError(err) - //:= suite.BitcoinChainClient.rpcClient.GetBlock(&hash) - block, err := suite.BitcoinChainClient.rpcClient.GetBlockVerboseTx(&hash) + block, err := suite.rpcClient.GetBlockVerboseTx(&hash) suite.Require().NoError(err) suite.T().Logf("block confirmation %d", block.Confirmations) suite.T().Logf("block txs len %d", len(block.Tx)) - inTxs := FilterAndParseIncomingTx( + inTxs, err := FilterAndParseIncomingTx( + suite.rpcClient, block.Tx, uint64(block.Height), "tb1qsa222mn2rhdq9cruxkz8p2teutvxuextx3ees2", - &log.Logger, + log.Logger, &chaincfg.TestNet3Params, 0.0, ) - + suite.Require().NoError(err) suite.Require().Equal(1, len(inTxs)) suite.Require().Equal(inTxs[0].Value, 0.0001) suite.Require().Equal(inTxs[0].ToAddress, "tb1qsa222mn2rhdq9cruxkz8p2teutvxuextx3ees2") @@ -175,27 +175,27 @@ func (suite *BitcoinClientTestSuite) Test2() { err := chainhash.Decode(&hash, hashStr) suite.Require().NoError(err) - //:= suite.BitcoinChainClient.rpcClient.GetBlock(&hash) - block, err := suite.BitcoinChainClient.rpcClient.GetBlockVerboseTx(&hash) + block, err := suite.rpcClient.GetBlockVerboseTx(&hash) suite.Require().NoError(err) suite.T().Logf("block confirmation %d", block.Confirmations) suite.T().Logf("block height %d", block.Height) suite.T().Logf("block txs len %d", len(block.Tx)) - inTxs := FilterAndParseIncomingTx( + inTxs, err := FilterAndParseIncomingTx( + suite.rpcClient, block.Tx, uint64(block.Height), "tb1qsa222mn2rhdq9cruxkz8p2teutvxuextx3ees2", - &log.Logger, + log.Logger, &chaincfg.TestNet3Params, 0.0, ) - + suite.Require().NoError(err) suite.Require().Equal(0, len(inTxs)) } func (suite *BitcoinClientTestSuite) Test3() { - client := suite.BitcoinChainClient.rpcClient + client := suite.rpcClient res, err := client.EstimateSmartFee(1, &btcjson.EstimateModeConservative) suite.Require().NoError(err) suite.T().Logf("fee: %f", *res.FeeRate) @@ -210,11 +210,18 @@ func (suite *BitcoinClientTestSuite) Test3() { suite.T().Logf("block number %d", bn) } -// func TestBitcoinChainClient(t *testing.T) { -// suite.Run(t, new(BitcoinClientTestSuite)) -// } +// TestBitcoinClientLive is a phony test to run each live test individually +func TestBitcoinClientLive(t *testing.T) { + // suite.Run(t, new(BitcoinClientTestSuite)) -// Remove prefix "Live" to run this live test + // LiveTestBitcoinFeeRate(t) + // LiveTestAvgFeeRateMainnetMempoolSpace(t) + // LiveTestAvgFeeRateTestnetMempoolSpace(t) + // LiveTestGetSenderByVin(t) +} + +// LiveTestBitcoinFeeRate query Bitcoin mainnet fee rate every 5 minutes +// and compares Conservative and Economical fee rates for different block targets (1 and 2) func LiveTestBitcoinFeeRate(t *testing.T) { // setup Bitcoin client client, err := getRPCClient(8332) @@ -319,7 +326,7 @@ func compareAvgFeeRate(t *testing.T, client *rpcclient.Client, startBlock int, e } } -// Remove prefix "Live" to run this live test +// LiveTestAvgFeeRateMainnetMempoolSpace compares calculated fee rate with mempool.space fee rate for mainnet func LiveTestAvgFeeRateMainnetMempoolSpace(t *testing.T) { // setup Bitcoin client client, err := getRPCClient(8332) @@ -333,7 +340,7 @@ func LiveTestAvgFeeRateMainnetMempoolSpace(t *testing.T) { compareAvgFeeRate(t, client, startBlock, endBlock, false) } -// Remove prefix "Live" to run this live test +// LiveTestAvgFeeRateTestnetMempoolSpace compares calculated fee rate with mempool.space fee rate for testnet func LiveTestAvgFeeRateTestnetMempoolSpace(t *testing.T) { // setup Bitcoin client client, err := getRPCClient(18332) @@ -346,3 +353,76 @@ func LiveTestAvgFeeRateTestnetMempoolSpace(t *testing.T) { compareAvgFeeRate(t, client, startBlock, endBlock, true) } + +// LiveTestGetSenderByVin gets sender address for each vin and compares with mempool.space sender address +func LiveTestGetSenderByVin(t *testing.T) { + // setup Bitcoin client + chainID := int64(8332) + client, err := getRPCClient(chainID) + require.NoError(t, err) + + // net params + net, err := chains.GetBTCChainParams(chainID) + require.NoError(t, err) + testnet := false + if chainID == chains.BtcTestNetChain().ChainId { + testnet = true + } + + // calculates block range to test + startBlock, err := client.GetBlockCount() + require.NoError(t, err) + endBlock := startBlock - 5000 + + // loop through mempool.space blocks in descending order +BLOCKLOOP: + for bn := startBlock; bn >= endBlock; { + // get block hash + blkHash, err := client.GetBlockHash(int64(bn)) + if err != nil { + fmt.Printf("error GetBlockHash for block %d: %s\n", bn, err) + time.Sleep(3 * time.Second) + continue + } + + // get mempool.space txs for the block + mempoolTxs, err := testutils.GetBlockTxs(context.Background(), blkHash.String(), testnet) + if err != nil { + fmt.Printf("error GetBlockTxs %d: %s\n", bn, err) + time.Sleep(10 * time.Second) + continue + } + + // loop through each tx in the block + for i, mptx := range mempoolTxs { + // sample 10 txs per block + if i >= 10 { + break + } + for _, mpvin := range mptx.Vin { + // skip coinbase tx + if mpvin.IsCoinbase { + continue + } + // get sender address for each vin + vin := btcjson.Vin{ + Txid: mpvin.TxID, + Vout: mpvin.Vout, + } + senderAddr, err := GetSenderAddressByVin(client, vin, net) + if err != nil { + fmt.Printf("error GetSenderAddressByVin for block %d, tx %s vout %d: %s\n", bn, vin.Txid, vin.Vout, err) + time.Sleep(3 * time.Second) + continue BLOCKLOOP // retry the block + } + if senderAddr != mpvin.Prevout.ScriptpubkeyAddress { + panic(fmt.Sprintf("block %d, tx %s, vout %d: want %s, got %s\n", bn, vin.Txid, vin.Vout, mpvin.Prevout.ScriptpubkeyAddress, senderAddr)) + } else { + fmt.Printf("block: %d sender address type: %s\n", bn, mpvin.Prevout.ScriptpubkeyType) + } + } + } + bn-- + time.Sleep(500 * time.Millisecond) + } +} diff --git a/zetaclient/bitcoin/bitcoin_client_test.go b/zetaclient/bitcoin/bitcoin_client_test.go index fa7a84b02f..58377cdc65 100644 --- a/zetaclient/bitcoin/bitcoin_client_test.go +++ b/zetaclient/bitcoin/bitcoin_client_test.go @@ -6,12 +6,14 @@ import ( "math" "math/big" "path" + "strings" "sync" "testing" "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/rs/zerolog/log" "github.com/stretchr/testify/require" @@ -39,6 +41,22 @@ func MockBTCClientMainnet() *BTCChainClient { } } +// createRPCClientAndLoadTx is a helper function to load raw tx and feed it to mock rpc client +func createRPCClientAndLoadTx(t *testing.T, chainId int64, txHash string) *stub.MockBTCRPCClient { + // file name for the archived MsgTx + nameMsgTx := path.Join("../", testutils.TestDataPathBTC, testutils.FileNameBTCMsgTx(chainId, txHash)) + + // load archived MsgTx + var msgTx wire.MsgTx + testutils.LoadObjectFromJSONFile(t, &msgTx, nameMsgTx) + tx := btcutil.NewTx(&msgTx) + + // feed tx to mock rpc client + rpcClient := stub.NewMockBTCRPCClient() + rpcClient.WithRawTransaction(tx) + return rpcClient +} + func TestNewBitcoinClient(t *testing.T) { t.Run("should return error because zetacore doesn't update core context", func(t *testing.T) { cfg := config.NewConfig() @@ -46,7 +64,7 @@ func TestNewBitcoinClient(t *testing.T) { appContext := appcontext.NewAppContext(coreContext, cfg) chain := chains.BtcMainnetChain() bridge := stub.NewMockZetaCoreBridge() - tss := stub.NewMockTSS(sample.EthAddress().String(), "") + tss := stub.NewMockTSS(chains.BtcTestNetChain(), sample.EthAddress().String(), "") loggers := clientcommon.ClientLogger{} btcCfg := cfg.BitcoinConfig ts := metrics.NewTelemetryServer() @@ -78,13 +96,11 @@ func TestConfirmationThreshold(t *testing.T) { func TestAvgFeeRateBlock828440(t *testing.T) { // load archived block 828440 var blockVb btcjson.GetBlockVerboseTxResult - err := testutils.LoadObjectFromJSONFile(&blockVb, path.Join("../", testutils.TestDataPathBTC, "block_trimmed_8332_828440.json")) - require.NoError(t, err) + testutils.LoadObjectFromJSONFile(t, &blockVb, path.Join("../", testutils.TestDataPathBTC, "block_trimmed_8332_828440.json")) // https://mempool.space/block/000000000000000000025ca01d2c1094b8fd3bacc5468cc3193ced6a14618c27 var blockMb testutils.MempoolBlock - err = testutils.LoadObjectFromJSONFile(&blockMb, path.Join("../", testutils.TestDataPathBTC, "block_mempool.space_8332_828440.json")) - require.NoError(t, err) + testutils.LoadObjectFromJSONFile(t, &blockMb, path.Join("../", testutils.TestDataPathBTC, "block_mempool.space_8332_828440.json")) gasRate, err := CalcBlockAvgFeeRate(&blockVb, &chaincfg.MainNetParams) require.NoError(t, err) @@ -94,8 +110,7 @@ func TestAvgFeeRateBlock828440(t *testing.T) { func TestAvgFeeRateBlock828440Errors(t *testing.T) { // load archived block 828440 var blockVb btcjson.GetBlockVerboseTxResult - err := testutils.LoadObjectFromJSONFile(&blockVb, path.Join("../", testutils.TestDataPathBTC, "block_trimmed_8332_828440.json")) - require.NoError(t, err) + testutils.LoadObjectFromJSONFile(t, &blockVb, path.Join("../", testutils.TestDataPathBTC, "block_trimmed_8332_828440.json")) t.Run("block has no transactions", func(t *testing.T) { emptyVb := btcjson.GetBlockVerboseTxResult{Tx: []btcjson.TxRawResult{}} @@ -181,8 +196,7 @@ func TestAvgFeeRateBlock828440Errors(t *testing.T) { func TestCalcDepositorFee828440(t *testing.T) { // load archived block 828440 var blockVb btcjson.GetBlockVerboseTxResult - err := testutils.LoadObjectFromJSONFile(&blockVb, path.Join("../", testutils.TestDataPathBTC, "block_trimmed_8332_828440.json")) - require.NoError(t, err) + testutils.LoadObjectFromJSONFile(t, &blockVb, path.Join("../", testutils.TestDataPathBTC, "block_trimmed_8332_828440.json")) avgGasRate := float64(32.0) // #nosec G701 test - always in range gasRate := int64(avgGasRate * clientcommon.BTCOuttxGasPriceMultiplier) @@ -212,7 +226,8 @@ func TestCalcDepositorFee828440(t *testing.T) { func TestCheckTSSVout(t *testing.T) { // the archived outtx raw result file and cctx file // https://blockstream.info/tx/030cd813443f7b70cc6d8a544d320c6d8465e4528fc0f3410b599dc0b26753a0 - chainID := int64(8332) + chain := chains.BtcMainnetChain() + chainID := chain.ChainId nonce := uint64(148) // create mainnet mock client @@ -234,6 +249,15 @@ func TestCheckTSSVout(t *testing.T) { err = btcClient.checkTSSVout(params, []btcjson.Vout{{}, {}, {}, {}}) require.ErrorContains(t, err, "invalid number of vouts") }) + t.Run("should fail on invalid TSS vout", func(t *testing.T) { + rawResult, cctx := testutils.LoadBTCTxRawResultNCctx(t, chainID, nonce) + params := cctx.GetCurrentOutTxParam() + + // invalid TSS vout + rawResult.Vout[0].ScriptPubKey.Hex = "invalid script" + err := btcClient.checkTSSVout(params, rawResult.Vout) + require.Error(t, err) + }) t.Run("should fail if vout 0 is not to the TSS address", func(t *testing.T) { rawResult, cctx := testutils.LoadBTCTxRawResultNCctx(t, chainID, nonce) params := cctx.GetCurrentOutTxParam() @@ -284,7 +308,8 @@ func TestCheckTSSVout(t *testing.T) { func TestCheckTSSVoutCancelled(t *testing.T) { // the archived outtx raw result file and cctx file // https://blockstream.info/tx/030cd813443f7b70cc6d8a544d320c6d8465e4528fc0f3410b599dc0b26753a0 - chainID := int64(8332) + chain := chains.BtcMainnetChain() + chainID := chain.ChainId nonce := uint64(148) // create mainnet mock client @@ -338,6 +363,7 @@ func TestCheckTSSVoutCancelled(t *testing.T) { // remove change vout to simulate cancelled tx rawResult, cctx := testutils.LoadBTCTxRawResultNCctx(t, chainID, nonce) rawResult.Vout[1] = rawResult.Vout[2] + rawResult.Vout[1].N = 1 // swap vout index rawResult.Vout = rawResult.Vout[:2] params := cctx.GetCurrentOutTxParam() @@ -348,6 +374,345 @@ func TestCheckTSSVoutCancelled(t *testing.T) { }) } +func TestGetSenderAddressByVin(t *testing.T) { + chain := chains.BtcMainnetChain() + net := &chaincfg.MainNetParams + + t.Run("should get sender address from P2TR tx", func(t *testing.T) { + // vin from the archived P2TR tx + // https://mempool.space/tx/3618e869f9e87863c0f1cc46dbbaa8b767b4a5d6d60b143c2c50af52b257e867 + txHash := "3618e869f9e87863c0f1cc46dbbaa8b767b4a5d6d60b143c2c50af52b257e867" + rpcClient := createRPCClientAndLoadTx(t, chain.ChainId, txHash) + + // get sender address + txVin := btcjson.Vin{Txid: txHash, Vout: 2} + sender, err := GetSenderAddressByVin(rpcClient, txVin, net) + require.NoError(t, err) + require.Equal(t, "bc1px3peqcd60hk7wqyqk36697u9hzugq0pd5lzvney93yzzrqy4fkpq6cj7m3", sender) + }) + t.Run("should get sender address from P2WSH tx", func(t *testing.T) { + // vin from the archived P2WSH tx + // https://mempool.space/tx/d13de30b0cc53b5c4702b184ae0a0b0f318feaea283185c1cddb8b341c27c016 + txHash := "d13de30b0cc53b5c4702b184ae0a0b0f318feaea283185c1cddb8b341c27c016" + rpcClient := createRPCClientAndLoadTx(t, chain.ChainId, txHash) + + // get sender address + txVin := btcjson.Vin{Txid: txHash, Vout: 0} + sender, err := GetSenderAddressByVin(rpcClient, txVin, net) + require.NoError(t, err) + require.Equal(t, "bc1q79kmcyc706d6nh7tpzhnn8lzp76rp0tepph3hqwrhacqfcy4lwxqft0ppq", sender) + }) + t.Run("should get sender address from P2WPKH tx", func(t *testing.T) { + // vin from the archived P2WPKH tx + // https://mempool.space/tx/c5d224963832fc0b9a597251c2342a17b25e481a88cc9119008e8f8296652697 + txHash := "c5d224963832fc0b9a597251c2342a17b25e481a88cc9119008e8f8296652697" + rpcClient := createRPCClientAndLoadTx(t, chain.ChainId, txHash) + + // get sender address + txVin := btcjson.Vin{Txid: txHash, Vout: 2} + sender, err := GetSenderAddressByVin(rpcClient, txVin, net) + require.NoError(t, err) + require.Equal(t, "bc1q68kxnq52ahz5vd6c8czevsawu0ux9nfrzzrh6e", sender) + }) + t.Run("should get sender address from P2SH tx", func(t *testing.T) { + // vin from the archived P2SH tx + // https://mempool.space/tx/211568441340fd5e10b1a8dcb211a18b9e853dbdf265ebb1c728f9b52813455a + txHash := "211568441340fd5e10b1a8dcb211a18b9e853dbdf265ebb1c728f9b52813455a" + rpcClient := createRPCClientAndLoadTx(t, chain.ChainId, txHash) + + // get sender address + txVin := btcjson.Vin{Txid: txHash, Vout: 0} + sender, err := GetSenderAddressByVin(rpcClient, txVin, net) + require.NoError(t, err) + require.Equal(t, "3MqRRSP76qxdVD9K4cfFnVtSLVwaaAjm3t", sender) + }) + t.Run("should get sender address from P2PKH tx", func(t *testing.T) { + // vin from the archived P2PKH tx + // https://mempool.space/tx/781fc8d41b476dbceca283ebff9573fda52c8fdbba5e78152aeb4432286836a7 + txHash := "781fc8d41b476dbceca283ebff9573fda52c8fdbba5e78152aeb4432286836a7" + rpcClient := createRPCClientAndLoadTx(t, chain.ChainId, txHash) + + // get sender address + txVin := btcjson.Vin{Txid: txHash, Vout: 1} + sender, err := GetSenderAddressByVin(rpcClient, txVin, net) + require.NoError(t, err) + require.Equal(t, "1ESQp1WQi7fzSpzCNs2oBTqaUBmNjLQLoV", sender) + }) + t.Run("should get empty sender address on unknown script", func(t *testing.T) { + // vin from the archived P2PKH tx + // https://mempool.space/tx/781fc8d41b476dbceca283ebff9573fda52c8fdbba5e78152aeb4432286836a7 + txHash := "781fc8d41b476dbceca283ebff9573fda52c8fdbba5e78152aeb4432286836a7" + nameMsgTx := path.Join("../", testutils.TestDataPathBTC, testutils.FileNameBTCMsgTx(chain.ChainId, txHash)) + var msgTx wire.MsgTx + testutils.LoadObjectFromJSONFile(t, &msgTx, nameMsgTx) + + // modify script to unknown script + msgTx.TxOut[1].PkScript = []byte{0x00, 0x01, 0x02, 0x03} // can be any invalid script bytes + tx := btcutil.NewTx(&msgTx) + + // feed tx to mock rpc client + rpcClient := stub.NewMockBTCRPCClient() + rpcClient.WithRawTransaction(tx) + + // get sender address + txVin := btcjson.Vin{Txid: txHash, Vout: 1} + sender, err := GetSenderAddressByVin(rpcClient, txVin, net) + require.NoError(t, err) + require.Empty(t, sender) + }) +} + +func TestGetSenderAddressByVinErrors(t *testing.T) { + // https://mempool.space/tx/3618e869f9e87863c0f1cc46dbbaa8b767b4a5d6d60b143c2c50af52b257e867 + txHash := "3618e869f9e87863c0f1cc46dbbaa8b767b4a5d6d60b143c2c50af52b257e867" + chain := chains.BtcMainnetChain() + net := &chaincfg.MainNetParams + + t.Run("should get sender address from P2TR tx", func(t *testing.T) { + rpcClient := stub.NewMockBTCRPCClient() + // use invalid tx hash + txVin := btcjson.Vin{Txid: "invalid tx hash", Vout: 2} + sender, err := GetSenderAddressByVin(rpcClient, txVin, net) + require.Error(t, err) + require.Empty(t, sender) + }) + t.Run("should return error when RPC client fails to get raw tx", func(t *testing.T) { + // create mock rpc client without preloaded tx + rpcClient := stub.NewMockBTCRPCClient() + txVin := btcjson.Vin{Txid: txHash, Vout: 2} + sender, err := GetSenderAddressByVin(rpcClient, txVin, net) + require.ErrorContains(t, err, "error getting raw transaction") + require.Empty(t, sender) + }) + t.Run("should return error on invalid output index", func(t *testing.T) { + // create mock rpc client with preloaded tx + rpcClient := createRPCClientAndLoadTx(t, chain.ChainId, txHash) + // invalid output index + txVin := btcjson.Vin{Txid: txHash, Vout: 3} + sender, err := GetSenderAddressByVin(rpcClient, txVin, net) + require.ErrorContains(t, err, "out of range") + require.Empty(t, sender) + }) +} + +func TestGetBtcEvent(t *testing.T) { + // load archived intx P2WPKH raw result + // https://mempool.space/tx/847139aa65aa4a5ee896375951cbf7417cfc8a4d6f277ec11f40cd87319f04aa + txHash := "847139aa65aa4a5ee896375951cbf7417cfc8a4d6f277ec11f40cd87319f04aa" + chain := chains.BtcMainnetChain() + + // GetBtcEvent arguments + tx := testutils.LoadBTCIntxRawResult(t, chain.ChainId, txHash, false) + tssAddress := testutils.TSSAddressBTCMainnet + blockNumber := uint64(835640) + net := &chaincfg.MainNetParams + // 2.992e-05, see avgFeeRate https://mempool.space/api/v1/blocks/835640 + depositorFee := DepositorFee(22 * clientcommon.BTCOuttxGasPriceMultiplier) + + // expected result + memo, err := hex.DecodeString(tx.Vout[1].ScriptPubKey.Hex[4:]) + require.NoError(t, err) + eventExpected := &BTCInTxEvnet{ + FromAddress: "bc1q68kxnq52ahz5vd6c8czevsawu0ux9nfrzzrh6e", + ToAddress: tssAddress, + Value: tx.Vout[0].Value - depositorFee, // 7008 sataoshis + MemoBytes: memo, + BlockNumber: blockNumber, + TxHash: tx.Txid, + } + + t.Run("should get BTC intx event from P2WPKH sender", func(t *testing.T) { + // https://mempool.space/tx/c5d224963832fc0b9a597251c2342a17b25e481a88cc9119008e8f8296652697 + preHash := "c5d224963832fc0b9a597251c2342a17b25e481a88cc9119008e8f8296652697" + tx.Vin[0].Txid = preHash + tx.Vin[0].Vout = 2 + eventExpected.FromAddress = "bc1q68kxnq52ahz5vd6c8czevsawu0ux9nfrzzrh6e" + // load previous raw tx so so mock rpc client can return it + rpcClient := createRPCClientAndLoadTx(t, chain.ChainId, preHash) + + // get BTC event + event, err := GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + require.NoError(t, err) + require.Equal(t, eventExpected, event) + }) + t.Run("should get BTC intx event from P2TR sender", func(t *testing.T) { + // replace vin with a P2TR vin, so the sender address will change + // https://mempool.space/tx/3618e869f9e87863c0f1cc46dbbaa8b767b4a5d6d60b143c2c50af52b257e867 + preHash := "3618e869f9e87863c0f1cc46dbbaa8b767b4a5d6d60b143c2c50af52b257e867" + tx.Vin[0].Txid = preHash + tx.Vin[0].Vout = 2 + eventExpected.FromAddress = "bc1px3peqcd60hk7wqyqk36697u9hzugq0pd5lzvney93yzzrqy4fkpq6cj7m3" + // load previous raw tx so so mock rpc client can return it + rpcClient := createRPCClientAndLoadTx(t, chain.ChainId, preHash) + + // get BTC event + event, err := GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + require.NoError(t, err) + require.Equal(t, eventExpected, event) + }) + t.Run("should get BTC intx event from P2WSH sender", func(t *testing.T) { + // replace vin with a P2WSH vin, so the sender address will change + // https://mempool.space/tx/d13de30b0cc53b5c4702b184ae0a0b0f318feaea283185c1cddb8b341c27c016 + preHash := "d13de30b0cc53b5c4702b184ae0a0b0f318feaea283185c1cddb8b341c27c016" + tx.Vin[0].Txid = preHash + tx.Vin[0].Vout = 0 + eventExpected.FromAddress = "bc1q79kmcyc706d6nh7tpzhnn8lzp76rp0tepph3hqwrhacqfcy4lwxqft0ppq" + // load previous raw tx so so mock rpc client can return it + rpcClient := createRPCClientAndLoadTx(t, chain.ChainId, preHash) + + // get BTC event + event, err := GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + require.NoError(t, err) + require.Equal(t, eventExpected, event) + }) + t.Run("should get BTC intx event from P2SH sender", func(t *testing.T) { + // replace vin with a P2SH vin, so the sender address will change + // https://mempool.space/tx/211568441340fd5e10b1a8dcb211a18b9e853dbdf265ebb1c728f9b52813455a + preHash := "211568441340fd5e10b1a8dcb211a18b9e853dbdf265ebb1c728f9b52813455a" + tx.Vin[0].Txid = preHash + tx.Vin[0].Vout = 0 + eventExpected.FromAddress = "3MqRRSP76qxdVD9K4cfFnVtSLVwaaAjm3t" + // load previous raw tx so so mock rpc client can return it + rpcClient := createRPCClientAndLoadTx(t, chain.ChainId, preHash) + + // get BTC event + event, err := GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + require.NoError(t, err) + require.Equal(t, eventExpected, event) + }) + t.Run("should get BTC intx event from P2PKH sender", func(t *testing.T) { + // replace vin with a P2PKH vin, so the sender address will change + // https://mempool.space/tx/781fc8d41b476dbceca283ebff9573fda52c8fdbba5e78152aeb4432286836a7 + preHash := "781fc8d41b476dbceca283ebff9573fda52c8fdbba5e78152aeb4432286836a7" + tx.Vin[0].Txid = preHash + tx.Vin[0].Vout = 1 + eventExpected.FromAddress = "1ESQp1WQi7fzSpzCNs2oBTqaUBmNjLQLoV" + // load previous raw tx so so mock rpc client can return it + rpcClient := createRPCClientAndLoadTx(t, chain.ChainId, preHash) + + // get BTC event + event, err := GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + require.NoError(t, err) + require.Equal(t, eventExpected, event) + }) + t.Run("should skip tx if len(tx.Vout) < 2", func(t *testing.T) { + // load tx and modify the tx to have only 1 vout + tx := testutils.LoadBTCIntxRawResult(t, chain.ChainId, txHash, false) + tx.Vout = tx.Vout[:1] + + // get BTC event + rpcClient := stub.NewMockBTCRPCClient() + event, err := GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + require.NoError(t, err) + require.Nil(t, event) + }) + t.Run("should skip tx if Vout[0] is not a P2WPKH output", func(t *testing.T) { + // load tx + rpcClient := stub.NewMockBTCRPCClient() + tx := testutils.LoadBTCIntxRawResult(t, chain.ChainId, txHash, false) + + // modify the tx to have Vout[0] a P2SH output + tx.Vout[0].ScriptPubKey.Hex = strings.Replace(tx.Vout[0].ScriptPubKey.Hex, "0014", "a914", 1) + event, err := GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + require.NoError(t, err) + require.Nil(t, event) + + // append 1 byte to script to make it longer than 22 bytes + tx.Vout[0].ScriptPubKey.Hex = tx.Vout[0].ScriptPubKey.Hex + "00" + event, err = GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + require.NoError(t, err) + require.Nil(t, event) + }) + t.Run("should skip tx if receiver address is not TSS address", func(t *testing.T) { + // load tx and modify receiver address to any non-tss address: bc1qw8wrek2m7nlqldll66ajnwr9mh64syvkt67zlu + tx := testutils.LoadBTCIntxRawResult(t, chain.ChainId, txHash, false) + tx.Vout[0].ScriptPubKey.Hex = "001471dc3cd95bf4fe0fb7ffd6bb29b865ddf5581196" + + // get BTC event + rpcClient := stub.NewMockBTCRPCClient() + event, err := GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + require.NoError(t, err) + require.Nil(t, event) + }) + t.Run("should skip tx if amount is less than depositor fee", func(t *testing.T) { + // load tx and modify amount to less than depositor fee + tx := testutils.LoadBTCIntxRawResult(t, chain.ChainId, txHash, false) + tx.Vout[0].Value = depositorFee - 1.0/1e8 // 1 satoshi less than depositor fee + + // get BTC event + rpcClient := stub.NewMockBTCRPCClient() + event, err := GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + require.NoError(t, err) + require.Nil(t, event) + }) + t.Run("should skip tx if 2nd vout is not OP_RETURN", func(t *testing.T) { + // load tx and modify memo OP_RETURN to OP_1 + tx := testutils.LoadBTCIntxRawResult(t, chain.ChainId, txHash, false) + tx.Vout[1].ScriptPubKey.Hex = strings.Replace(tx.Vout[1].ScriptPubKey.Hex, "6a", "51", 1) + + // get BTC event + rpcClient := stub.NewMockBTCRPCClient() + event, err := GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + require.NoError(t, err) + require.Nil(t, event) + }) + t.Run("should skip tx if memo decoding fails", func(t *testing.T) { + // load tx and modify memo length to be 1 byte less than actual + tx := testutils.LoadBTCIntxRawResult(t, chain.ChainId, txHash, false) + tx.Vout[1].ScriptPubKey.Hex = strings.Replace(tx.Vout[1].ScriptPubKey.Hex, "6a14", "6a13", 1) + + // get BTC event + rpcClient := stub.NewMockBTCRPCClient() + event, err := GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + require.NoError(t, err) + require.Nil(t, event) + }) +} + +func TestGetBtcEventErrors(t *testing.T) { + // load archived intx P2WPKH raw result + // https://mempool.space/tx/847139aa65aa4a5ee896375951cbf7417cfc8a4d6f277ec11f40cd87319f04aa + txHash := "847139aa65aa4a5ee896375951cbf7417cfc8a4d6f277ec11f40cd87319f04aa" + chain := chains.BtcMainnetChain() + net := &chaincfg.MainNetParams + tssAddress := testutils.TSSAddressBTCMainnet + blockNumber := uint64(835640) + depositorFee := DepositorFee(22 * clientcommon.BTCOuttxGasPriceMultiplier) + + t.Run("should return error on invalid Vout[0] script", func(t *testing.T) { + // load tx and modify Vout[0] script to invalid script + tx := testutils.LoadBTCIntxRawResult(t, chain.ChainId, txHash, false) + tx.Vout[0].ScriptPubKey.Hex = "0014invalid000000000000000000000000000000000" + + // get BTC event + rpcClient := stub.NewMockBTCRPCClient() + event, err := GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + require.Error(t, err) + require.Nil(t, event) + }) + t.Run("should return error if len(tx.Vin) < 1", func(t *testing.T) { + // load tx and remove vin + tx := testutils.LoadBTCIntxRawResult(t, chain.ChainId, txHash, false) + tx.Vin = nil + + // get BTC event + rpcClient := stub.NewMockBTCRPCClient() + event, err := GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + require.Error(t, err) + require.Nil(t, event) + }) + t.Run("should return error if RPC client fails to get raw tx", func(t *testing.T) { + // load tx and leave rpc client without preloaded tx + tx := testutils.LoadBTCIntxRawResult(t, chain.ChainId, txHash, false) + rpcClient := stub.NewMockBTCRPCClient() + + // get BTC event + event, err := GetBtcEvent(rpcClient, *tx, tssAddress, blockNumber, log.Logger, net, depositorFee) + require.Error(t, err) + require.Nil(t, event) + }) +} + func TestBTCChainClient_ObserveInTx(t *testing.T) { t.Run("should return error", func(t *testing.T) { // create mainnet mock client diff --git a/zetaclient/bitcoin/bitcoin_signer.go b/zetaclient/bitcoin/bitcoin_signer.go index df5502b4ae..a5ff22fa6a 100644 --- a/zetaclient/bitcoin/bitcoin_signer.go +++ b/zetaclient/bitcoin/bitcoin_signer.go @@ -8,35 +8,34 @@ import ( "math/rand" "time" - 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" - "github.com/zeta-chain/zetacore/zetaclient/metrics" - "github.com/zeta-chain/zetacore/zetaclient/outtxprocessor" - "github.com/zeta-chain/zetacore/zetaclient/tss" - "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" + ethcommon "github.com/ethereum/go-ethereum/common" "github.com/rs/zerolog" + "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" + 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" + "github.com/zeta-chain/zetacore/zetaclient/outtxprocessor" + "github.com/zeta-chain/zetacore/zetaclient/tss" ) const ( + // the maximum number of inputs per outtx maxNoOfInputsPerTx = 20 - consolidationRank = 10 // the rank below (or equal to) which we consolidate UTXOs - outTxBytesMin = uint64(239) // 239vB == EstimateSegWitTxSize(2, 3) - outTxBytesMax = uint64(1531) // 1531v == EstimateSegWitTxSize(21, 3) + + // the rank below (or equal to) which we consolidate UTXOs + consolidationRank = 10 ) // BTCSigner deals with signing BTC transactions and implements the ChainSigner interface @@ -98,9 +97,73 @@ func (signer *BTCSigner) GetERC20CustodyAddress() ethcommon.Address { return ethcommon.Address{} } +// AddWithdrawTxOutputs adds the 3 outputs to the withdraw tx +// 1st output: the nonce-mark btc to TSS itself +// 2nd output: the payment to the recipient +// 3rd output: the remaining btc to TSS itself +func (signer *BTCSigner) AddWithdrawTxOutputs( + tx *wire.MsgTx, + to btcutil.Address, + total float64, + amount float64, + nonceMark int64, + fees *big.Int, + cancelTx bool, +) error { + // convert withdraw amount to satoshis + amountSatoshis, err := GetSatoshis(amount) + if err != nil { + return err + } + + // calculate remaining btc (the change) to TSS self + remaining := total - amount + remainingSats, err := GetSatoshis(remaining) + if err != nil { + return err + } + remainingSats -= fees.Int64() + remainingSats -= nonceMark + if remainingSats < 0 { + return fmt.Errorf("remainder value is negative: %d", remainingSats) + } else if remainingSats == nonceMark { + signer.logger.Info().Msgf("adjust remainder value to avoid duplicate nonce-mark: %d", remainingSats) + remainingSats-- + } + + // 1st output: the nonce-mark btc to TSS self + tssAddrP2WPKH := signer.tssSigner.BTCAddressWitnessPubkeyHash() + payToSelfScript, err := PayToAddrScript(tssAddrP2WPKH) + if err != nil { + return err + } + txOut1 := wire.NewTxOut(nonceMark, payToSelfScript) + tx.AddTxOut(txOut1) + + // 2nd output: the payment to the recipient + if !cancelTx { + pkScript, err := PayToAddrScript(to) + if err != nil { + return err + } + txOut2 := wire.NewTxOut(amountSatoshis, pkScript) + tx.AddTxOut(txOut2) + } else { + // send the amount to TSS self if tx is cancelled + remainingSats += amountSatoshis + } + + // 3rd output: the remaining btc to TSS self + if remainingSats > 0 { + txOut3 := wire.NewTxOut(remainingSats, payToSelfScript) + tx.AddTxOut(txOut3) + } + return nil +} + // SignWithdrawTx receives utxos sorted by value, amount in BTC, feeRate in BTC per Kb func (signer *BTCSigner) SignWithdrawTx( - to *btcutil.AddressWitnessPubKeyHash, + to btcutil.Address, amount float64, gasPrice *big.Int, sizeLimit uint64, @@ -137,14 +200,12 @@ func (signer *BTCSigner) SignWithdrawTx( tx.AddTxIn(txIn) } - amountSatoshis, err := GetSatoshis(amount) + // size checking + // #nosec G701 always positive + txSize, err := EstimateOuttxSize(uint64(len(prevOuts)), []btcutil.Address{to}) if err != nil { return nil, err } - - // size checking - // #nosec G701 always positive - txSize := EstimateSegWitTxSize(uint64(len(prevOuts)), 3) if sizeLimit < BtcOutTxBytesWithdrawer { // ZRC20 'withdraw' charged less fee from end user signer.logger.Info().Msgf("sizeLimit %d is less than BtcOutTxBytesWithdrawer %d for nonce %d", sizeLimit, txSize, nonce) } @@ -163,45 +224,11 @@ func (signer *BTCSigner) SignWithdrawTx( signer.logger.Info().Msgf("bitcoin outTx nonce %d gasPrice %s size %d fees %s consolidated %d utxos of value %v", nonce, gasPrice.String(), txSize, fees.String(), consolidatedUtxo, consolidatedValue) - // calculate remaining btc to TSS self - tssAddrWPKH := signer.tssSigner.BTCAddressWitnessPubkeyHash() - payToSelf, err := PayToWitnessPubKeyHashScript(tssAddrWPKH.WitnessProgram()) + // add tx outputs + err = signer.AddWithdrawTxOutputs(tx, to, total, amount, nonceMark, fees, cancelTx) if err != nil { return nil, err } - remaining := total - amount - remainingSats, err := GetSatoshis(remaining) - if err != nil { - return nil, err - } - remainingSats -= fees.Int64() - remainingSats -= nonceMark - if remainingSats < 0 { - return nil, fmt.Errorf("remainder value is negative: %d", remainingSats) - } else if remainingSats == nonceMark { - signer.logger.Info().Msgf("SignWithdrawTx: adjust remainder value to avoid duplicate nonce-mark: %d", remainingSats) - remainingSats-- - } - - // 1st output: the nonce-mark btc to TSS self - txOut1 := wire.NewTxOut(nonceMark, payToSelf) - tx.AddTxOut(txOut1) - - // 2nd output: the payment to the recipient - if !cancelTx { - pkScript, err := PayToWitnessPubKeyHashScript(to.WitnessProgram()) - if err != nil { - return nil, err - } - txOut2 := wire.NewTxOut(amountSatoshis, pkScript) - tx.AddTxOut(txOut2) - } - - // 3rd output: the remaining btc to TSS self - if remainingSats > 0 { - txOut3 := wire.NewTxOut(remainingSats, payToSelf) - tx.AddTxOut(txOut3) - } // sign the tx sigHashes := txscript.NewTxSigHashes(tx) @@ -314,27 +341,13 @@ func (signer *BTCSigner) TryProcessOutTx( } // Check receiver P2WPKH address - bitcoinNetParams, err := chains.BitcoinNetParamsFromChainID(params.ReceiverChainId) - if err != nil { - logger.Error().Err(err).Msgf("cannot get bitcoin net params%v", err) - return - } - addr, err := chains.DecodeBtcAddress(params.Receiver, params.ReceiverChainId) + to, err := chains.DecodeBtcAddress(params.Receiver, params.ReceiverChainId) if err != nil { logger.Error().Err(err).Msgf("cannot decode address %s ", params.Receiver) return } - if !addr.IsForNet(bitcoinNetParams) { - logger.Error().Msgf( - "address %s is not for network %s", - params.Receiver, - bitcoinNetParams.Name, - ) - return - } - to, ok := addr.(*btcutil.AddressWitnessPubKeyHash) - if err != nil || !ok { - logger.Error().Err(err).Msgf("cannot convert address %s to P2WPKH address", params.Receiver) + if !chains.IsBtcAddressSupported(to) { + logger.Error().Msgf("unsupported address %s", params.Receiver) return } amount := float64(params.Amount.Uint64()) / 1e8 @@ -356,7 +369,7 @@ func (signer *BTCSigner) TryProcessOutTx( amount = 0.0 // zero out the amount to cancel the tx } - logger.Info().Msgf("SignWithdrawTx: to %s, value %d sats", addr.EncodeAddress(), params.Amount.Uint64()) + logger.Info().Msgf("SignWithdrawTx: to %s, value %d sats", to.EncodeAddress(), params.Amount.Uint64()) logger.Info().Msgf("using utxos: %v", btcClient.utxos) tx, err := signer.SignWithdrawTx( diff --git a/zetaclient/bitcoin/bitcoin_signer_test.go b/zetaclient/bitcoin/bitcoin_signer_test.go index 26e79ed8d6..96dc48cdff 100644 --- a/zetaclient/bitcoin/bitcoin_signer_test.go +++ b/zetaclient/bitcoin/bitcoin_signer_test.go @@ -4,12 +4,12 @@ import ( "encoding/hex" "fmt" "math" - "math/rand" + "math/big" + "reflect" "sort" "sync" "testing" - "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/chaincfg" @@ -25,6 +25,7 @@ import ( 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/testutils/stub" . "gopkg.in/check.v1" ) @@ -34,31 +35,6 @@ type BTCSignerSuite struct { var _ = Suite(&BTCSignerSuite{}) -// 21 example UTXO txids to use in the test. -var exampleTxids = []string{ - "c1729638e1c9b6bfca57d11bf93047d98b65594b0bf75d7ee68bf7dc80dc164e", - "54f9ebbd9e3ad39a297da54bf34a609b6831acbea0361cb5b7b5c8374f5046aa", - "b18a55a34319cfbedebfcfe1a80fef2b92ad8894d06caf8293a0344824c2cfbc", - "969fb309a4df7c299972700da788b5d601c0c04bab4ab46fff79d0335a7d75de", - "6c71913061246ffc20e268c1b0e65895055c36bfbf1f8faf92dcad6f8242121e", - "ba6d6e88cb5a97556684a1232719a3ffe409c5c9501061e1f59741bc412b3585", - "69b56c3c8c5d1851f9eaec256cd49f290b477a5d43e2aef42ef25d3c1d9f4b33", - "b87effd4cb46fe1a575b5b1ba0289313dc9b4bc9e615a3c6cbc0a14186921fdf", - "3135433054523f5e220621c9e3d48efbbb34a6a2df65635c2a3e7d462d3e1cda", - "8495c22a9ce6359ab53aa048c13b41c64fdf5fe141f516ba2573cc3f9313f06e", - "f31583544b475370d7b9187c9a01b92e44fb31ac5fcfa7fc55565ac64043aa9a", - "c03d55f9f717c1df978623e2e6b397b720999242f9ead7db9b5988fee3fb3933", - "ee55688439b47a5410cdc05bac46be0094f3af54d307456fdfe6ba8caf336e0b", - "61895f86c70f0bc3eef55d9a00347b509fa90f7a344606a9774be98a3ee9e02a", - "ffabb401a19d04327bd4a076671d48467dbcde95459beeab23df21686fd01525", - "b7e1c03b9b73e4e90fc06da893072c5604203c49e66699acbb2f61485d822981", - "185614d21973990138e478ce10e0a4014352df58044276d4e4c0093aa140f482", - "4a2800f13d15dc0c82308761d6fe8f6d13b65e42d7ca96a42a3a7048830e8c55", - "fb98f52e91db500735b185797cebb5848afbfe1289922d87e03b98c3da5b85ef", - "7901c5e36d9e8456ac61b29b82048650672a889596cbd30a9f8910a589ffc5b3", - "6bcd0850fd2fa1404290ed04d78d4ae718414f16d4fbfd344951add8dcf60326", -} - func (s *BTCSignerSuite) SetUpTest(c *C) { // test private key with EVM address //// EVM: 0x236C7f53a90493Bb423411fe4117Cb4c2De71DfB @@ -104,7 +80,7 @@ func (s *BTCSignerSuite) TestP2PH(c *C) { prevOut := wire.NewOutPoint(&chainhash.Hash{}, ^uint32(0)) txIn := wire.NewTxIn(prevOut, []byte{txscript.OP_0, txscript.OP_0}, nil) originTx.AddTxIn(txIn) - pkScript, err := txscript.PayToAddrScript(addr) + pkScript, err := PayToAddrScript(addr) c.Assert(err, IsNil) @@ -176,7 +152,7 @@ func (s *BTCSignerSuite) TestP2WPH(c *C) { prevOut := wire.NewOutPoint(&chainhash.Hash{}, ^uint32(0)) txIn := wire.NewTxIn(prevOut, []byte{txscript.OP_0, txscript.OP_0}, nil) originTx.AddTxIn(txIn) - pkScript, err := txscript.PayToAddrScript(addr) + pkScript, err := PayToAddrScript(addr) c.Assert(err, IsNil) txOut := wire.NewTxOut(100000000, pkScript) originTx.AddTxOut(txOut) @@ -197,7 +173,7 @@ func (s *BTCSignerSuite) TestP2WPH(c *C) { txOut = wire.NewTxOut(0, nil) redeemTx.AddTxOut(txOut) txSigHashes := txscript.NewTxSigHashes(redeemTx) - pkScript, err = PayToWitnessPubKeyHashScript(addr.WitnessProgram()) + pkScript, err = PayToAddrScript(addr) c.Assert(err, IsNil) { @@ -239,193 +215,7 @@ func (s *BTCSignerSuite) TestP2WPH(c *C) { fmt.Println("Transaction successfully signed") } -func generateKeyPair(t *testing.T, net *chaincfg.Params) (*btcec.PrivateKey, []byte) { - privateKey, err := btcec.NewPrivateKey(btcec.S256()) - require.Nil(t, err) - pubKeyHash := btcutil.Hash160(privateKey.PubKey().SerializeCompressed()) - addr, err := btcutil.NewAddressWitnessPubKeyHash(pubKeyHash, net) - require.Nil(t, err) - //fmt.Printf("New address: %s\n", addr.EncodeAddress()) - pkScript, err := PayToWitnessPubKeyHashScript(addr.WitnessProgram()) - require.Nil(t, err) - return privateKey, pkScript -} - -func addTxInputs(t *testing.T, tx *wire.MsgTx, txids []string) { - preTxSize := tx.SerializeSize() - for _, txid := range txids { - hash, err := chainhash.NewHashFromStr(txid) - require.Nil(t, err) - outpoint := wire.NewOutPoint(hash, uint32(rand.Intn(100))) - txIn := wire.NewTxIn(outpoint, nil, nil) - tx.AddTxIn(txIn) - require.Equal(t, bytesPerInput, tx.SerializeSize()-preTxSize) - //fmt.Printf("tx size: %d, input %d size: %d\n", tx.SerializeSize(), i, tx.SerializeSize()-preTxSize) - preTxSize = tx.SerializeSize() - } -} - -func addTxOutputs(t *testing.T, tx *wire.MsgTx, payerScript, payeeScript []byte) { - preTxSize := tx.SerializeSize() - - // 1st output to payer - value1 := int64(1 + rand.Intn(100000000)) - txOut1 := wire.NewTxOut(value1, payerScript) - tx.AddTxOut(txOut1) - require.Equal(t, bytesPerOutput, tx.SerializeSize()-preTxSize) - //fmt.Printf("tx size: %d, output 1: %d\n", tx.SerializeSize(), tx.SerializeSize()-preTxSize) - preTxSize = tx.SerializeSize() - - // 2nd output to payee - value2 := int64(1 + rand.Intn(100000000)) - txOut2 := wire.NewTxOut(value2, payeeScript) - tx.AddTxOut(txOut2) - require.Equal(t, bytesPerOutput, tx.SerializeSize()-preTxSize) - //fmt.Printf("tx size: %d, output 2: %d\n", tx.SerializeSize(), tx.SerializeSize()-preTxSize) - preTxSize = tx.SerializeSize() - - // 3rd output to payee - value3 := int64(1 + rand.Intn(100000000)) - txOut3 := wire.NewTxOut(value3, payeeScript) - tx.AddTxOut(txOut3) - require.Equal(t, bytesPerOutput, tx.SerializeSize()-preTxSize) - //fmt.Printf("tx size: %d, output 3: %d\n", tx.SerializeSize(), tx.SerializeSize()-preTxSize) -} - -func signTx(t *testing.T, tx *wire.MsgTx, payerScript []byte, privateKey *btcec.PrivateKey) { - preTxSize := tx.SerializeSize() - sigHashes := txscript.NewTxSigHashes(tx) - for ix := range tx.TxIn { - amount := int64(1 + rand.Intn(100000000)) - witnessHash, err := txscript.CalcWitnessSigHash(payerScript, sigHashes, txscript.SigHashAll, tx, ix, amount) - require.Nil(t, err) - sig, err := privateKey.Sign(witnessHash) - require.Nil(t, err) - - pkCompressed := privateKey.PubKey().SerializeCompressed() - txWitness := wire.TxWitness{append(sig.Serialize(), byte(txscript.SigHashAll)), pkCompressed} - tx.TxIn[ix].Witness = txWitness - - //fmt.Printf("tx size: %d, witness %d: %d\n", tx.SerializeSize(), ix+1, tx.SerializeSize()-preTxSize) - if ix == 0 { - bytesIncur := bytes1stWitness + len(tx.TxIn) - 1 // e.g., 130 bytes for a 21-input tx - require.True(t, tx.SerializeSize()-preTxSize >= bytesIncur-5) - require.True(t, tx.SerializeSize()-preTxSize <= bytesIncur+5) - } else { - require.True(t, tx.SerializeSize()-preTxSize >= bytesPerWitness-5) - require.True(t, tx.SerializeSize()-preTxSize <= bytesPerWitness+5) - } - preTxSize = tx.SerializeSize() - } -} - -func TestP2WPHSize2In3Out(t *testing.T) { - // Generate payer/payee private keys and P2WPKH addresss - privateKey, payerScript := generateKeyPair(t, &chaincfg.TestNet3Params) - _, payeeScript := generateKeyPair(t, &chaincfg.TestNet3Params) - - // 2 example UTXO txids to use in the test. - utxosTxids := []string{ - "c1729638e1c9b6bfca57d11bf93047d98b65594b0bf75d7ee68bf7dc80dc164e", - "54f9ebbd9e3ad39a297da54bf34a609b6831acbea0361cb5b7b5c8374f5046aa", - } - - // Create a new transaction and add inputs - tx := wire.NewMsgTx(wire.TxVersion) - addTxInputs(t, tx, utxosTxids) - - // Add P2WPKH outputs - addTxOutputs(t, tx, payerScript, payeeScript) - - // Payer sign the redeeming transaction. - signTx(t, tx, payerScript, privateKey) - - // Estimate the tx size in vByte - // #nosec G701 always positive - vBytes := uint64(blockchain.GetTransactionWeight(btcutil.NewTx(tx)) / blockchain.WitnessScaleFactor) - vBytesEstimated := EstimateSegWitTxSize(uint64(len(utxosTxids)), 3) - require.Equal(t, vBytes, vBytesEstimated) - require.Equal(t, vBytes, outTxBytesMin) -} - -func TestP2WPHSize21In3Out(t *testing.T) { - // Generate payer/payee private keys and P2WPKH addresss - privateKey, payerScript := generateKeyPair(t, &chaincfg.TestNet3Params) - _, payeeScript := generateKeyPair(t, &chaincfg.TestNet3Params) - - // Create a new transaction and add inputs - tx := wire.NewMsgTx(wire.TxVersion) - addTxInputs(t, tx, exampleTxids) - - // Add P2WPKH outputs - addTxOutputs(t, tx, payerScript, payeeScript) - - // Payer sign the redeeming transaction. - signTx(t, tx, payerScript, privateKey) - - // Estimate the tx size in vByte - // #nosec G701 always positive - vError := uint64(21 / 4) // 5 vBytes error tolerance - vBytes := uint64(blockchain.GetTransactionWeight(btcutil.NewTx(tx)) / blockchain.WitnessScaleFactor) - vBytesEstimated := EstimateSegWitTxSize(uint64(len(exampleTxids)), 3) - require.Equal(t, vBytesEstimated, outTxBytesMax) - if vBytes > vBytesEstimated { - require.True(t, vBytes-vBytesEstimated <= vError) - } else { - require.True(t, vBytesEstimated-vBytes <= vError) - } -} - -func TestP2WPHSizeXIn3Out(t *testing.T) { - // Generate payer/payee private keys and P2WPKH addresss - privateKey, payerScript := generateKeyPair(t, &chaincfg.TestNet3Params) - _, payeeScript := generateKeyPair(t, &chaincfg.TestNet3Params) - - // Create new transactions with X (2 <= X <= 21) inputs and 3 outputs respectively - for x := 2; x <= 21; x++ { - tx := wire.NewMsgTx(wire.TxVersion) - addTxInputs(t, tx, exampleTxids[:x]) - - // Add P2WPKH outputs - addTxOutputs(t, tx, payerScript, payeeScript) - - // Payer sign the redeeming transaction. - signTx(t, tx, payerScript, privateKey) - - // Estimate the tx size - // #nosec G701 always positive - vError := uint64(0.25 + float64(x)/4) // 1st witness incur 0.25 vByte error, other witness incur 1/4 vByte error tolerance, - vBytes := uint64(blockchain.GetTransactionWeight(btcutil.NewTx(tx)) / blockchain.WitnessScaleFactor) - vBytesEstimated := EstimateSegWitTxSize(uint64(len(exampleTxids[:x])), 3) - if vBytes > vBytesEstimated { - require.True(t, vBytes-vBytesEstimated <= vError) - //fmt.Printf("%d error percentage: %.2f%%\n", float64(vBytes-vBytesEstimated)/float64(vBytes)*100) - } else { - require.True(t, vBytesEstimated-vBytes <= vError) - //fmt.Printf("error percentage: %.2f%%\n", float64(vBytesEstimated-vBytes)/float64(vBytes)*100) - } - } -} - -func TestP2WPHSizeBreakdown(t *testing.T) { - txSize2In3Out := EstimateSegWitTxSize(2, 3) - require.Equal(t, outTxBytesMin, txSize2In3Out) - - sz := EstimateSegWitTxSize(1, 1) - fmt.Printf("1 input, 1 output: %d\n", sz) - - txSizeDepositor := SegWitTxSizeDepositor() - require.Equal(t, uint64(68), txSizeDepositor) - - txSizeWithdrawer := SegWitTxSizeWithdrawer() - require.Equal(t, uint64(171), txSizeWithdrawer) - require.Equal(t, txSize2In3Out, txSizeDepositor+txSizeWithdrawer) // 239 = 68 + 171 - - depositFee := DepositorFee(defaultDepositorFeeRate) - require.Equal(t, depositFee, 0.00001360) -} - -// helper function to create a new BitcoinChainClient +// helper function to create a test BitcoinChainClient func createTestClient(t *testing.T) *BTCChainClient { skHex := "7b8507ba117e069f4a3f456f505276084f8c92aee86ac78ae37b4d1801d35fa8" privateKey, err := crypto.HexToECDSA(skHex) @@ -433,14 +223,18 @@ func createTestClient(t *testing.T) *BTCChainClient { tss := interfaces.TestSigner{ PrivKey: privateKey, } - tssAddress := tss.BTCAddressWitnessPubkeyHash().EncodeAddress() - - // Create BitcoinChainClient - client := &BTCChainClient{ + return &BTCChainClient{ Tss: tss, Mu: &sync.Mutex{}, includedTxResults: make(map[string]*btcjson.GetTransactionResult), } +} + +// helper function to create a test BitcoinChainClient with UTXOs +func createTestClientWithUTXOs(t *testing.T) *BTCChainClient { + // Create BitcoinChainClient + client := createTestClient(t) + tssAddress := client.Tss.BTCAddressWitnessPubkeyHash().EncodeAddress() // Create 10 dummy UTXOs (22.44 BTC in total) client.utxos = make([]btcjson.ListUnspentResult, 0, 10) @@ -451,6 +245,153 @@ func createTestClient(t *testing.T) *BTCChainClient { return client } +func TestAddWithdrawTxOutputs(t *testing.T) { + // Create test signer and receiver address + signer, err := NewBTCSigner(config.BTCConfig{}, stub.NewTSSMainnet(), clientcommon.DefaultLoggers(), &metrics.TelemetryServer{}, nil) + require.NoError(t, err) + + // tss address and script + tssAddr := signer.tssSigner.BTCAddressWitnessPubkeyHash() + tssScript, err := PayToAddrScript(tssAddr) + require.NoError(t, err) + fmt.Printf("tss address: %s", tssAddr.EncodeAddress()) + + // receiver addresses + receiver := "bc1qaxf82vyzy8y80v000e7t64gpten7gawewzu42y" + to, err := chains.DecodeBtcAddress(receiver, chains.BtcMainnetChain().ChainId) + require.NoError(t, err) + toScript, err := PayToAddrScript(to) + require.NoError(t, err) + + // test cases + tests := []struct { + name string + tx *wire.MsgTx + to btcutil.Address + total float64 + amount float64 + nonce int64 + fees *big.Int + cancelTx bool + fail bool + message string + txout []*wire.TxOut + }{ + { + name: "should add outputs successfully", + tx: wire.NewMsgTx(wire.TxVersion), + to: to, + total: 1.00012000, + amount: 0.2, + nonce: 10000, + fees: big.NewInt(2000), + fail: false, + txout: []*wire.TxOut{ + {Value: 10000, PkScript: tssScript}, + {Value: 20000000, PkScript: toScript}, + {Value: 80000000, PkScript: tssScript}, + }, + }, + { + name: "should add outputs without change successfully", + tx: wire.NewMsgTx(wire.TxVersion), + to: to, + total: 0.20012000, + amount: 0.2, + nonce: 10000, + fees: big.NewInt(2000), + fail: false, + txout: []*wire.TxOut{ + {Value: 10000, PkScript: tssScript}, + {Value: 20000000, PkScript: toScript}, + }, + }, + { + name: "should cancel tx successfully", + tx: wire.NewMsgTx(wire.TxVersion), + to: to, + total: 1.00012000, + amount: 0.2, + nonce: 10000, + fees: big.NewInt(2000), + cancelTx: true, + fail: false, + txout: []*wire.TxOut{ + {Value: 10000, PkScript: tssScript}, + {Value: 100000000, PkScript: tssScript}, + }, + }, + { + name: "should fail on invalid amount", + tx: wire.NewMsgTx(wire.TxVersion), + to: to, + total: 1.00012000, + amount: -0.5, + fail: true, + }, + { + name: "should fail when total < amount", + tx: wire.NewMsgTx(wire.TxVersion), + to: to, + total: 0.00012000, + amount: 0.2, + fail: true, + }, + { + name: "should fail when total < fees + amount + nonce", + tx: wire.NewMsgTx(wire.TxVersion), + to: to, + total: 0.20011000, + amount: 0.2, + nonce: 10000, + fees: big.NewInt(2000), + fail: true, + message: "remainder value is negative", + }, + { + name: "should not produce duplicate nonce mark", + tx: wire.NewMsgTx(wire.TxVersion), + to: to, + total: 0.20022000, // 0.2 + fee + nonceMark * 2 + amount: 0.2, + nonce: 10000, + fees: big.NewInt(2000), + fail: false, + txout: []*wire.TxOut{ + {Value: 10000, PkScript: tssScript}, + {Value: 20000000, PkScript: toScript}, + {Value: 9999, PkScript: tssScript}, // nonceMark - 1 + }, + }, + { + name: "should fail on invalid to address", + tx: wire.NewMsgTx(wire.TxVersion), + to: nil, + total: 1.00012000, + amount: 0.2, + nonce: 10000, + fees: big.NewInt(2000), + fail: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := signer.AddWithdrawTxOutputs(tt.tx, tt.to, tt.total, tt.amount, tt.nonce, tt.fees, tt.cancelTx) + if tt.fail { + require.Error(t, err) + if tt.message != "" { + require.Contains(t, err.Error(), tt.message) + } + return + } else { + require.NoError(t, err) + require.True(t, reflect.DeepEqual(tt.txout, tt.tx.TxOut)) + } + }) + } +} + func mineTxNSetNonceMark(ob *BTCChainClient, nonce uint64, txid string, preMarkIndex int) { // Mine transaction outTxID := ob.GetTxID(nonce) @@ -471,7 +412,7 @@ func mineTxNSetNonceMark(ob *BTCChainClient, nonce uint64, txid string, preMarkI } func TestSelectUTXOs(t *testing.T) { - ob := createTestClient(t) + ob := createTestClientWithUTXOs(t) dummyTxID := "6e6f71d281146c1fc5c755b35908ee449f26786c84e2ae18f98b268de40b7ec4" // Case1: nonce = 0, bootstrap @@ -563,7 +504,7 @@ func TestUTXOConsolidation(t *testing.T) { dummyTxID := "6e6f71d281146c1fc5c755b35908ee449f26786c84e2ae18f98b268de40b7ec4" t.Run("should not consolidate", func(t *testing.T) { - ob := createTestClient(t) + ob := createTestClientWithUTXOs(t) mineTxNSetNonceMark(ob, 0, dummyTxID, -1) // mine a transaction and set nonce-mark utxo for nonce 0 // input: utxoCap = 10, amount = 0.01, nonce = 1, rank = 10 @@ -577,7 +518,7 @@ func TestUTXOConsolidation(t *testing.T) { }) t.Run("should consolidate 1 utxo", func(t *testing.T) { - ob := createTestClient(t) + ob := createTestClientWithUTXOs(t) mineTxNSetNonceMark(ob, 0, dummyTxID, -1) // mine a transaction and set nonce-mark utxo for nonce 0 // input: utxoCap = 9, amount = 0.01, nonce = 1, rank = 9 @@ -591,7 +532,7 @@ func TestUTXOConsolidation(t *testing.T) { }) t.Run("should consolidate 3 utxos", func(t *testing.T) { - ob := createTestClient(t) + ob := createTestClientWithUTXOs(t) mineTxNSetNonceMark(ob, 0, dummyTxID, -1) // mine a transaction and set nonce-mark utxo for nonce 0 // input: utxoCap = 5, amount = 0.01, nonce = 0, rank = 5 @@ -610,7 +551,7 @@ func TestUTXOConsolidation(t *testing.T) { }) t.Run("should consolidate all utxos using rank 1", func(t *testing.T) { - ob := createTestClient(t) + ob := createTestClientWithUTXOs(t) mineTxNSetNonceMark(ob, 0, dummyTxID, -1) // mine a transaction and set nonce-mark utxo for nonce 0 // input: utxoCap = 12, amount = 0.01, nonce = 0, rank = 1 @@ -629,7 +570,7 @@ func TestUTXOConsolidation(t *testing.T) { }) t.Run("should consolidate 3 utxos sparse", func(t *testing.T) { - ob := createTestClient(t) + ob := createTestClientWithUTXOs(t) mineTxNSetNonceMark(ob, 24105431, dummyTxID, -1) // mine a transaction and set nonce-mark utxo for nonce 24105431 // input: utxoCap = 5, amount = 0.13, nonce = 24105432, rank = 5 @@ -647,7 +588,7 @@ func TestUTXOConsolidation(t *testing.T) { }) t.Run("should consolidate all utxos sparse", func(t *testing.T) { - ob := createTestClient(t) + ob := createTestClientWithUTXOs(t) mineTxNSetNonceMark(ob, 24105431, dummyTxID, -1) // mine a transaction and set nonce-mark utxo for nonce 24105431 // input: utxoCap = 12, amount = 0.13, nonce = 24105432, rank = 1 @@ -685,5 +626,6 @@ func TestNewBTCSigner(t *testing.T) { clientcommon.DefaultLoggers(), &metrics.TelemetryServer{}, corecontext.NewZetaCoreContext(cfg)) + require.NoError(t, err) require.NotNil(t, btcSigner) } diff --git a/zetaclient/bitcoin/bitcoin_test.go b/zetaclient/bitcoin/bitcoin_test.go index 12e9e29136..4dae7f8590 100644 --- a/zetaclient/bitcoin/bitcoin_test.go +++ b/zetaclient/bitcoin/bitcoin_test.go @@ -96,7 +96,6 @@ func buildTX() (*wire.MsgTx, *txscript.TxSigHashes, int, int64, []byte, *btcec.P if err != nil { return nil, nil, 0, 0, nil, nil, false, err } - fmt.Printf("addr %v\n", addr.EncodeAddress()) hash, err := chainhash.NewHashFromStr(prevOut) if err != nil { @@ -109,7 +108,7 @@ func buildTX() (*wire.MsgTx, *txscript.TxSigHashes, int, int64, []byte, *btcec.P txIn := wire.NewTxIn(outpoint, nil, nil) tx.AddTxIn(txIn) - pkScript, err := PayToWitnessPubKeyHashScript(addr.WitnessProgram()) + pkScript, err := PayToAddrScript(addr) if err != nil { return nil, nil, 0, 0, nil, nil, false, err } diff --git a/zetaclient/bitcoin/fee.go b/zetaclient/bitcoin/fee.go new file mode 100644 index 0000000000..ab19240264 --- /dev/null +++ b/zetaclient/bitcoin/fee.go @@ -0,0 +1,226 @@ +package bitcoin + +import ( + "encoding/hex" + "fmt" + "math" + "math/big" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" + "github.com/pkg/errors" + "github.com/rs/zerolog" + "github.com/zeta-chain/zetacore/pkg/chains" + clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" +) + +const ( + bytesPerKB = 1000 + bytesPerInput = 41 // each input is 41 bytes + bytesPerOutputP2TR = 43 // each P2TR output is 43 bytes + bytesPerOutputP2WSH = 43 // each P2WSH output is 43 bytes + bytesPerOutputP2WPKH = 31 // each P2WPKH output is 31 bytes + bytesPerOutputP2SH = 32 // each P2SH output is 32 bytes + bytesPerOutputP2PKH = 34 // each P2PKH output is 34 bytes + bytesPerOutputAvg = 37 // average size of all above types of outputs (36.6 bytes) + bytes1stWitness = 110 // the 1st witness incurs about 110 bytes and it may vary + bytesPerWitness = 108 // each additional witness incurs about 108 bytes and it may vary + defaultDepositorFeeRate = 20 // 20 sat/byte is the default depositor fee rate + + outTxBytesMin = uint64(239) // 239vB == EstimateSegWitTxSize(2, 2, toP2WPKH) + outTxBytesMax = uint64(1543) // 1543v == EstimateSegWitTxSize(21, 2, toP2TR) + outTxBytesAvg = uint64(245) // 245vB is a suggested gas limit for zeta core +) + +var ( + // The outtx size incurred by the depositor: 68vB + BtcOutTxBytesDepositor = OuttxSizeDepositor() + + // The outtx size incurred by the withdrawer: 177vB + BtcOutTxBytesWithdrawer = OuttxSizeWithdrawer() + + // The default depositor fee is 0.00001360 BTC (20 * 68vB / 100000000) + // default depositor fee calculation is based on a fixed fee rate of 20 sat/byte just for simplicity. + DefaultDepositorFee = DepositorFee(defaultDepositorFeeRate) +) + +// FeeRateToSatPerByte converts a fee rate in BTC/KB to sat/byte. +func FeeRateToSatPerByte(rate float64) *big.Int { + // #nosec G701 always in range + satPerKB := new(big.Int).SetInt64(int64(rate * btcutil.SatoshiPerBitcoin)) + return new(big.Int).Div(satPerKB, big.NewInt(bytesPerKB)) +} + +// WiredTxSize calculates the wired tx size in bytes +func WiredTxSize(numInputs uint64, numOutputs uint64) uint64 { + // Version 4 bytes + LockTime 4 bytes + Serialized varint size for the + // number of transaction inputs and outputs. + // #nosec G701 always positive + return uint64(8 + wire.VarIntSerializeSize(numInputs) + wire.VarIntSerializeSize(numOutputs)) +} + +// EstimateOuttxSize estimates the size of a outtx in vBytes +func EstimateOuttxSize(numInputs uint64, payees []btcutil.Address) (uint64, error) { + if numInputs == 0 { + return 0, nil + } + // #nosec G701 always positive + numOutputs := 2 + uint64(len(payees)) + bytesWiredTx := WiredTxSize(numInputs, numOutputs) + bytesInput := numInputs * bytesPerInput + bytesOutput := uint64(2) * bytesPerOutputP2WPKH // new nonce mark, change + + // calculate the size of the outputs to payees + bytesToPayees := uint64(0) + for _, to := range payees { + sizeOutput, err := GetOutputSizeByAddress(to) + if err != nil { + return 0, err + } + bytesToPayees += sizeOutput + } + // calculate the size of the witness + bytesWitness := bytes1stWitness + (numInputs-1)*bytesPerWitness + // https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#transaction-size-calculations + // Calculation for signed SegWit tx: blockchain.GetTransactionWeight(tx) / 4 + return bytesWiredTx + bytesInput + bytesOutput + bytesToPayees + bytesWitness/blockchain.WitnessScaleFactor, nil +} + +// GetOutputSizeByAddress returns the size of a tx output in bytes by the given address +func GetOutputSizeByAddress(to btcutil.Address) (uint64, error) { + switch addr := to.(type) { + case *chains.AddressTaproot: + if addr == nil { + return 0, nil + } + return bytesPerOutputP2TR, nil + case *btcutil.AddressWitnessScriptHash: + if addr == nil { + return 0, nil + } + return bytesPerOutputP2WSH, nil + case *btcutil.AddressWitnessPubKeyHash: + if addr == nil { + return 0, nil + } + return bytesPerOutputP2WPKH, nil + case *btcutil.AddressScriptHash: + if addr == nil { + return 0, nil + } + return bytesPerOutputP2SH, nil + case *btcutil.AddressPubKeyHash: + if addr == nil { + return 0, nil + } + return bytesPerOutputP2PKH, nil + default: + return 0, fmt.Errorf("cannot get output size for address type %T", to) + } +} + +// OuttxSizeDepositor returns outtx size (68vB) incurred by the depositor +func OuttxSizeDepositor() uint64 { + return bytesPerInput + bytesPerWitness/blockchain.WitnessScaleFactor +} + +// OuttxSizeWithdrawer returns outtx size (177vB) incurred by the withdrawer (1 input, 3 outputs) +func OuttxSizeWithdrawer() uint64 { + bytesWiredTx := WiredTxSize(1, 3) + bytesInput := uint64(1) * bytesPerInput // nonce mark + bytesOutput := uint64(2) * bytesPerOutputP2WPKH // 2 P2WPKH outputs: new nonce mark, change + bytesOutput += bytesPerOutputAvg // 1 output to withdrawer's address + return bytesWiredTx + bytesInput + bytesOutput + bytes1stWitness/blockchain.WitnessScaleFactor +} + +// DepositorFee calculates the depositor fee in BTC for a given sat/byte fee rate +// Note: the depositor fee is charged in order to cover the cost of spending the deposited UTXO in the future +func DepositorFee(satPerByte int64) float64 { + return float64(satPerByte) * float64(BtcOutTxBytesDepositor) / btcutil.SatoshiPerBitcoin +} + +// CalcBlockAvgFeeRate calculates the average gas rate (in sat/vByte) for a given block +func CalcBlockAvgFeeRate(blockVb *btcjson.GetBlockVerboseTxResult, netParams *chaincfg.Params) (int64, error) { + // sanity check + if len(blockVb.Tx) == 0 { + return 0, errors.New("block has no transactions") + } + if len(blockVb.Tx) == 1 { + return 0, nil // only coinbase tx, it happens + } + txCoinbase := &blockVb.Tx[0] + if blockVb.Weight < blockchain.WitnessScaleFactor { + return 0, fmt.Errorf("block weight %d too small", blockVb.Weight) + } + if blockVb.Weight < txCoinbase.Weight { + return 0, fmt.Errorf("block weight %d less than coinbase tx weight %d", blockVb.Weight, txCoinbase.Weight) + } + if blockVb.Height <= 0 || blockVb.Height > math.MaxInt32 { + return 0, fmt.Errorf("invalid block height %d", blockVb.Height) + } + + // make sure the first tx is coinbase tx + txBytes, err := hex.DecodeString(txCoinbase.Hex) + if err != nil { + return 0, fmt.Errorf("failed to decode coinbase tx %s", txCoinbase.Txid) + } + tx, err := btcutil.NewTxFromBytes(txBytes) + if err != nil { + return 0, fmt.Errorf("failed to parse coinbase tx %s", txCoinbase.Txid) + } + if !blockchain.IsCoinBaseTx(tx.MsgTx()) { + return 0, fmt.Errorf("first tx %s is not coinbase tx", txCoinbase.Txid) + } + + // calculate fees earned by the miner + btcEarned := int64(0) + for _, out := range tx.MsgTx().TxOut { + if out.Value > 0 { + btcEarned += out.Value + } + } + // #nosec G701 checked above + subsidy := blockchain.CalcBlockSubsidy(int32(blockVb.Height), netParams) + if btcEarned < subsidy { + return 0, fmt.Errorf("miner earned %d, less than subsidy %d", btcEarned, subsidy) + } + txsFees := btcEarned - subsidy + + // sum up weight of all txs (<= 4 MWU) + txsWeight := int32(0) + for i, tx := range blockVb.Tx { + // coinbase doesn't pay fees, so we exclude it + if i > 0 && tx.Weight > 0 { + txsWeight += tx.Weight + } + } + + // calculate average fee rate. + vBytes := txsWeight / blockchain.WitnessScaleFactor + return txsFees / int64(vBytes), nil +} + +// CalcDepositorFee calculates the depositor fee for a given block +func CalcDepositorFee(blockVb *btcjson.GetBlockVerboseTxResult, chainID int64, netParams *chaincfg.Params, logger zerolog.Logger) float64 { + // use default fee for regnet + if chains.IsBitcoinRegnet(chainID) { + return DefaultDepositorFee + } + // mainnet dynamic fee takes effect only after a planned upgrade height + if chains.IsBitcoinMainnet(chainID) && blockVb.Height < DynamicDepositorFeeHeight { + return DefaultDepositorFee + } + + // calculate deposit fee rate + feeRate, err := CalcBlockAvgFeeRate(blockVb, netParams) + if err != nil { + feeRate = defaultDepositorFeeRate // use default fee rate if calculation fails, should not happen + logger.Error().Err(err).Msgf("cannot calculate fee rate for block %d", blockVb.Height) + } + // #nosec G701 always in range + feeRate = int64(float64(feeRate) * clientcommon.BTCOuttxGasPriceMultiplier) + return DepositorFee(feeRate) +} diff --git a/zetaclient/bitcoin/fee_test.go b/zetaclient/bitcoin/fee_test.go new file mode 100644 index 0000000000..c9276911cf --- /dev/null +++ b/zetaclient/bitcoin/fee_test.go @@ -0,0 +1,463 @@ +package bitcoin + +import ( + "math/rand" + "testing" + + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" +) + +const ( + // btc address script types + ScriptTypeP2TR = "witness_v1_taproot" + ScriptTypeP2WSH = "witness_v0_scripthash" + ScriptTypeP2WPKH = "witness_v0_keyhash" + ScriptTypeP2SH = "scripthash" + ScriptTypeP2PKH = "pubkeyhash" +) + +var testAddressMap = map[string]string{ + ScriptTypeP2TR: "bc1p4scddlkkuw9486579autxumxmkvuphm5pz4jvf7f6pdh50p2uzqstawjt9", + ScriptTypeP2WSH: "bc1qqv6pwn470vu0tssdfha4zdk89v3c8ch5lsnyy855k9hcrcv3evequdmjmc", + ScriptTypeP2WPKH: "bc1qaxf82vyzy8y80v000e7t64gpten7gawewzu42y", + ScriptTypeP2SH: "327z4GyFM8Y8DiYfasGKQWhRK4MvyMSEgE", + ScriptTypeP2PKH: "1FueivsE338W2LgifJ25HhTcVJ7CRT8kte", +} + +// 21 example UTXO txids to use in the test. +var exampleTxids = []string{ + "c1729638e1c9b6bfca57d11bf93047d98b65594b0bf75d7ee68bf7dc80dc164e", + "54f9ebbd9e3ad39a297da54bf34a609b6831acbea0361cb5b7b5c8374f5046aa", + "b18a55a34319cfbedebfcfe1a80fef2b92ad8894d06caf8293a0344824c2cfbc", + "969fb309a4df7c299972700da788b5d601c0c04bab4ab46fff79d0335a7d75de", + "6c71913061246ffc20e268c1b0e65895055c36bfbf1f8faf92dcad6f8242121e", + "ba6d6e88cb5a97556684a1232719a3ffe409c5c9501061e1f59741bc412b3585", + "69b56c3c8c5d1851f9eaec256cd49f290b477a5d43e2aef42ef25d3c1d9f4b33", + "b87effd4cb46fe1a575b5b1ba0289313dc9b4bc9e615a3c6cbc0a14186921fdf", + "3135433054523f5e220621c9e3d48efbbb34a6a2df65635c2a3e7d462d3e1cda", + "8495c22a9ce6359ab53aa048c13b41c64fdf5fe141f516ba2573cc3f9313f06e", + "f31583544b475370d7b9187c9a01b92e44fb31ac5fcfa7fc55565ac64043aa9a", + "c03d55f9f717c1df978623e2e6b397b720999242f9ead7db9b5988fee3fb3933", + "ee55688439b47a5410cdc05bac46be0094f3af54d307456fdfe6ba8caf336e0b", + "61895f86c70f0bc3eef55d9a00347b509fa90f7a344606a9774be98a3ee9e02a", + "ffabb401a19d04327bd4a076671d48467dbcde95459beeab23df21686fd01525", + "b7e1c03b9b73e4e90fc06da893072c5604203c49e66699acbb2f61485d822981", + "185614d21973990138e478ce10e0a4014352df58044276d4e4c0093aa140f482", + "4a2800f13d15dc0c82308761d6fe8f6d13b65e42d7ca96a42a3a7048830e8c55", + "fb98f52e91db500735b185797cebb5848afbfe1289922d87e03b98c3da5b85ef", + "7901c5e36d9e8456ac61b29b82048650672a889596cbd30a9f8910a589ffc5b3", + "6bcd0850fd2fa1404290ed04d78d4ae718414f16d4fbfd344951add8dcf60326", +} + +func generateKeyPair(t *testing.T, net *chaincfg.Params) (*btcec.PrivateKey, btcutil.Address, []byte) { + privateKey, err := btcec.NewPrivateKey(btcec.S256()) + require.Nil(t, err) + pubKeyHash := btcutil.Hash160(privateKey.PubKey().SerializeCompressed()) + addr, err := btcutil.NewAddressWitnessPubKeyHash(pubKeyHash, net) + require.Nil(t, err) + //fmt.Printf("New address: %s\n", addr.EncodeAddress()) + pkScript, err := PayToAddrScript(addr) + require.Nil(t, err) + return privateKey, addr, pkScript +} + +// getTestAddrScript returns hard coded test address scripts by script type +func getTestAddrScript(t *testing.T, scriptType string) btcutil.Address { + chain := chains.BtcMainnetChain() + if inputAddress, ok := testAddressMap[scriptType]; ok { + address, err := chains.DecodeBtcAddress(inputAddress, chain.ChainId) + require.NoError(t, err) + return address + } else { + panic("unknown script type") + } +} + +// createPkScripts creates 10 random amount of scripts to the given address 'to' +func createPkScripts(t *testing.T, to btcutil.Address, repeat int) ([]btcutil.Address, [][]byte) { + pkScript, err := PayToAddrScript(to) + require.NoError(t, err) + + addrs := []btcutil.Address{} + pkScripts := [][]byte{} + for i := 0; i < repeat; i++ { + addrs = append(addrs, to) + pkScripts = append(pkScripts, pkScript) + } + return addrs, pkScripts +} + +func addTxInputs(t *testing.T, tx *wire.MsgTx, txids []string) { + preTxSize := tx.SerializeSize() + for _, txid := range txids { + hash, err := chainhash.NewHashFromStr(txid) + require.Nil(t, err) + outpoint := wire.NewOutPoint(hash, uint32(rand.Intn(100))) + txIn := wire.NewTxIn(outpoint, nil, nil) + tx.AddTxIn(txIn) + require.Equal(t, bytesPerInput, tx.SerializeSize()-preTxSize) + //fmt.Printf("tx size: %d, input %d size: %d\n", tx.SerializeSize(), i, tx.SerializeSize()-preTxSize) + preTxSize = tx.SerializeSize() + } +} + +func addTxOutputs(t *testing.T, tx *wire.MsgTx, payerScript []byte, payeeScripts [][]byte) { + preTxSize := tx.SerializeSize() + + // 1st output to payer + value1 := int64(1 + rand.Intn(100000000)) + txOut1 := wire.NewTxOut(value1, payerScript) + tx.AddTxOut(txOut1) + require.Equal(t, bytesPerOutputP2WPKH, tx.SerializeSize()-preTxSize) + //fmt.Printf("tx size: %d, output 1: %d\n", tx.SerializeSize(), tx.SerializeSize()-preTxSize) + preTxSize = tx.SerializeSize() + + // output to payee list + for _, payeeScript := range payeeScripts { + value := int64(1 + rand.Intn(100000000)) + txOut := wire.NewTxOut(value, payeeScript) + tx.AddTxOut(txOut) + //fmt.Printf("tx size: %d, output %d: %d\n", tx.SerializeSize(), i+1, tx.SerializeSize()-preTxSize) + preTxSize = tx.SerializeSize() + } + + // 3rd output to payee + value3 := int64(1 + rand.Intn(100000000)) + txOut3 := wire.NewTxOut(value3, payerScript) + tx.AddTxOut(txOut3) + require.Equal(t, bytesPerOutputP2WPKH, tx.SerializeSize()-preTxSize) + //fmt.Printf("tx size: %d, last output: %d\n", tx.SerializeSize(), tx.SerializeSize()-preTxSize) +} + +func addTxInputsOutputsAndSignTx( + t *testing.T, tx *wire.MsgTx, + privateKey *btcec.PrivateKey, + payerScript []byte, + txids []string, + payeeScripts [][]byte) { + // Add inputs + addTxInputs(t, tx, txids) + + // Add outputs + addTxOutputs(t, tx, payerScript, payeeScripts) + + // Payer sign the redeeming transaction. + signTx(t, tx, payerScript, privateKey) +} + +func signTx(t *testing.T, tx *wire.MsgTx, payerScript []byte, privateKey *btcec.PrivateKey) { + preTxSize := tx.SerializeSize() + sigHashes := txscript.NewTxSigHashes(tx) + for ix := range tx.TxIn { + amount := int64(1 + rand.Intn(100000000)) + witnessHash, err := txscript.CalcWitnessSigHash(payerScript, sigHashes, txscript.SigHashAll, tx, ix, amount) + require.Nil(t, err) + sig, err := privateKey.Sign(witnessHash) + require.Nil(t, err) + + pkCompressed := privateKey.PubKey().SerializeCompressed() + txWitness := wire.TxWitness{append(sig.Serialize(), byte(txscript.SigHashAll)), pkCompressed} + tx.TxIn[ix].Witness = txWitness + + //fmt.Printf("tx size: %d, witness %d: %d\n", tx.SerializeSize(), ix+1, tx.SerializeSize()-preTxSize) + if ix == 0 { + bytesIncur := bytes1stWitness + len(tx.TxIn) - 1 // e.g., 130 bytes for a 21-input tx + require.True(t, tx.SerializeSize()-preTxSize >= bytesIncur-5) + require.True(t, tx.SerializeSize()-preTxSize <= bytesIncur+5) + } else { + require.True(t, tx.SerializeSize()-preTxSize >= bytesPerWitness-5) + require.True(t, tx.SerializeSize()-preTxSize <= bytesPerWitness+5) + } + preTxSize = tx.SerializeSize() + } +} + +func TestOutTxSize2In3Out(t *testing.T) { + // Generate payer/payee private keys and P2WPKH addresss + privateKey, _, payerScript := generateKeyPair(t, &chaincfg.TestNet3Params) + _, payee, payeeScript := generateKeyPair(t, &chaincfg.TestNet3Params) + + // 2 example UTXO txids to use in the test. + utxosTxids := exampleTxids[:2] + + // Create a new transaction + tx := wire.NewMsgTx(wire.TxVersion) + + // Add inputs and outputs and sign the transaction + addTxInputsOutputsAndSignTx(t, tx, privateKey, payerScript, utxosTxids, [][]byte{payeeScript}) + + // Estimate the tx size in vByte + // #nosec G701 always positive + vError := uint64(1) // 1 vByte error tolerance + vBytes := uint64(blockchain.GetTransactionWeight(btcutil.NewTx(tx)) / blockchain.WitnessScaleFactor) + vBytesEstimated, err := EstimateOuttxSize(uint64(len(utxosTxids)), []btcutil.Address{payee}) + require.NoError(t, err) + if vBytes > vBytesEstimated { + require.True(t, vBytes-vBytesEstimated <= vError) + } else { + require.True(t, vBytesEstimated-vBytes <= vError) + } +} + +func TestOutTxSize21In3Out(t *testing.T) { + // Generate payer/payee private keys and P2WPKH addresss + privateKey, _, payerScript := generateKeyPair(t, &chaincfg.TestNet3Params) + _, payee, payeeScript := generateKeyPair(t, &chaincfg.TestNet3Params) + + // Create a new transaction + tx := wire.NewMsgTx(wire.TxVersion) + + // Add inputs and outputs and sign the transaction + addTxInputsOutputsAndSignTx(t, tx, privateKey, payerScript, exampleTxids, [][]byte{payeeScript}) + + // Estimate the tx size in vByte + // #nosec G701 always positive + vError := uint64(21 / 4) // 5 vBytes error tolerance + vBytes := uint64(blockchain.GetTransactionWeight(btcutil.NewTx(tx)) / blockchain.WitnessScaleFactor) + vBytesEstimated, err := EstimateOuttxSize(uint64(len(exampleTxids)), []btcutil.Address{payee}) + require.NoError(t, err) + if vBytes > vBytesEstimated { + require.True(t, vBytes-vBytesEstimated <= vError) + } else { + require.True(t, vBytesEstimated-vBytes <= vError) + } +} + +func TestOutTxSizeXIn3Out(t *testing.T) { + // Generate payer/payee private keys and P2WPKH addresss + privateKey, _, payerScript := generateKeyPair(t, &chaincfg.TestNet3Params) + _, payee, payeeScript := generateKeyPair(t, &chaincfg.TestNet3Params) + + // Create new transactions with X (2 <= X <= 21) inputs and 3 outputs respectively + for x := 2; x <= 21; x++ { + // Create transaction. Add inputs and outputs and sign the transaction + tx := wire.NewMsgTx(wire.TxVersion) + addTxInputsOutputsAndSignTx(t, tx, privateKey, payerScript, exampleTxids[:x], [][]byte{payeeScript}) + + // Estimate the tx size + // #nosec G701 always positive + vError := uint64(0.25 + float64(x)/4) // 1st witness incurs 0.25 more vByte error than others (which incurs 1/4 vByte per witness) + vBytes := uint64(blockchain.GetTransactionWeight(btcutil.NewTx(tx)) / blockchain.WitnessScaleFactor) + vBytesEstimated, err := EstimateOuttxSize(uint64(len(exampleTxids[:x])), []btcutil.Address{payee}) + require.NoError(t, err) + if vBytes > vBytesEstimated { + require.True(t, vBytes-vBytesEstimated <= vError) + //fmt.Printf("%d error percentage: %.2f%%\n", float64(vBytes-vBytesEstimated)/float64(vBytes)*100) + } else { + require.True(t, vBytesEstimated-vBytes <= vError) + //fmt.Printf("error percentage: %.2f%%\n", float64(vBytesEstimated-vBytes)/float64(vBytes)*100) + } + } +} + +func TestGetOutputSizeByAddress(t *testing.T) { + // test nil P2TR address and non-nil P2TR address + nilP2TR := (*chains.AddressTaproot)(nil) + sizeNilP2TR, err := GetOutputSizeByAddress(nilP2TR) + require.NoError(t, err) + require.Equal(t, uint64(0), sizeNilP2TR) + + addrP2TR := getTestAddrScript(t, ScriptTypeP2TR) + sizeP2TR, err := GetOutputSizeByAddress(addrP2TR) + require.NoError(t, err) + require.Equal(t, uint64(bytesPerOutputP2TR), sizeP2TR) + + // test nil P2WSH address and non-nil P2WSH address + nilP2WSH := (*btcutil.AddressWitnessScriptHash)(nil) + sizeNilP2WSH, err := GetOutputSizeByAddress(nilP2WSH) + require.NoError(t, err) + require.Equal(t, uint64(0), sizeNilP2WSH) + + addrP2WSH := getTestAddrScript(t, ScriptTypeP2WSH) + sizeP2WSH, err := GetOutputSizeByAddress(addrP2WSH) + require.NoError(t, err) + require.Equal(t, uint64(bytesPerOutputP2WSH), sizeP2WSH) + + // test nil P2WPKH address and non-nil P2WPKH address + nilP2WPKH := (*btcutil.AddressWitnessPubKeyHash)(nil) + sizeNilP2WPKH, err := GetOutputSizeByAddress(nilP2WPKH) + require.NoError(t, err) + require.Equal(t, uint64(0), sizeNilP2WPKH) + + addrP2WPKH := getTestAddrScript(t, ScriptTypeP2WPKH) + sizeP2WPKH, err := GetOutputSizeByAddress(addrP2WPKH) + require.NoError(t, err) + require.Equal(t, uint64(bytesPerOutputP2WPKH), sizeP2WPKH) + + // test nil P2SH address and non-nil P2SH address + nilP2SH := (*btcutil.AddressScriptHash)(nil) + sizeNilP2SH, err := GetOutputSizeByAddress(nilP2SH) + require.NoError(t, err) + require.Equal(t, uint64(0), sizeNilP2SH) + + addrP2SH := getTestAddrScript(t, ScriptTypeP2SH) + sizeP2SH, err := GetOutputSizeByAddress(addrP2SH) + require.NoError(t, err) + require.Equal(t, uint64(bytesPerOutputP2SH), sizeP2SH) + + // test nil P2PKH address and non-nil P2PKH address + nilP2PKH := (*btcutil.AddressPubKeyHash)(nil) + sizeNilP2PKH, err := GetOutputSizeByAddress(nilP2PKH) + require.NoError(t, err) + require.Equal(t, uint64(0), sizeNilP2PKH) + + addrP2PKH := getTestAddrScript(t, ScriptTypeP2PKH) + sizeP2PKH, err := GetOutputSizeByAddress(addrP2PKH) + require.NoError(t, err) + require.Equal(t, uint64(bytesPerOutputP2PKH), sizeP2PKH) + + // test unsupported address type + nilP2PK := (*btcutil.AddressPubKey)(nil) + sizeP2PK, err := GetOutputSizeByAddress(nilP2PK) + require.ErrorContains(t, err, "cannot get output size for address type") + require.Equal(t, uint64(0), sizeP2PK) +} + +func TestOutputSizeP2TR(t *testing.T) { + // Generate payer/payee private keys and P2WPKH addresss + privateKey, _, payerScript := generateKeyPair(t, &chaincfg.TestNet3Params) + payee := getTestAddrScript(t, ScriptTypeP2TR) + + // Create a new transaction and 10 random amount of payee scripts + tx := wire.NewMsgTx(wire.TxVersion) + payees, payeeScripts := createPkScripts(t, payee, 10) + + // Add inputs and outputs and sign the transaction + addTxInputsOutputsAndSignTx(t, tx, privateKey, payerScript, exampleTxids[:2], payeeScripts) + + // Estimate the tx size in vByte + // #nosec G701 always positive + vBytes := uint64(blockchain.GetTransactionWeight(btcutil.NewTx(tx)) / blockchain.WitnessScaleFactor) + vBytesEstimated, err := EstimateOuttxSize(2, payees) + require.NoError(t, err) + require.Equal(t, vBytes, vBytesEstimated) +} + +func TestOutputSizeP2WSH(t *testing.T) { + // Generate payer/payee private keys and P2WPKH addresss + privateKey, _, payerScript := generateKeyPair(t, &chaincfg.TestNet3Params) + payee := getTestAddrScript(t, ScriptTypeP2WSH) + + // Create a new transaction and 10 random amount of payee scripts + tx := wire.NewMsgTx(wire.TxVersion) + payees, payeeScripts := createPkScripts(t, payee, 10) + + // Add inputs and outputs and sign the transaction + addTxInputsOutputsAndSignTx(t, tx, privateKey, payerScript, exampleTxids[:2], payeeScripts) + + // Estimate the tx size in vByte + // #nosec G701 always positive + vBytes := uint64(blockchain.GetTransactionWeight(btcutil.NewTx(tx)) / blockchain.WitnessScaleFactor) + vBytesEstimated, err := EstimateOuttxSize(2, payees) + require.NoError(t, err) + require.Equal(t, vBytes, vBytesEstimated) +} + +func TestOutputSizeP2SH(t *testing.T) { + // Generate payer/payee private keys and P2SH addresss + privateKey, _, payerScript := generateKeyPair(t, &chaincfg.TestNet3Params) + payee := getTestAddrScript(t, ScriptTypeP2SH) + + // Create a new transaction and 10 random amount of payee scripts + tx := wire.NewMsgTx(wire.TxVersion) + payees, payeeScripts := createPkScripts(t, payee, 10) + + // Add inputs and outputs and sign the transaction + addTxInputsOutputsAndSignTx(t, tx, privateKey, payerScript, exampleTxids[:2], payeeScripts) + + // Estimate the tx size in vByte + // #nosec G701 always positive + vBytes := uint64(blockchain.GetTransactionWeight(btcutil.NewTx(tx)) / blockchain.WitnessScaleFactor) + vBytesEstimated, err := EstimateOuttxSize(2, payees) + require.NoError(t, err) + require.Equal(t, vBytes, vBytesEstimated) +} + +func TestOutputSizeP2PKH(t *testing.T) { + // Generate payer/payee private keys and P2PKH addresss + privateKey, _, payerScript := generateKeyPair(t, &chaincfg.TestNet3Params) + payee := getTestAddrScript(t, ScriptTypeP2PKH) + + // Create a new transaction and 10 random amount of payee scripts + tx := wire.NewMsgTx(wire.TxVersion) + payees, payeeScripts := createPkScripts(t, payee, 10) + + // Add inputs and outputs and sign the transaction + addTxInputsOutputsAndSignTx(t, tx, privateKey, payerScript, exampleTxids[:2], payeeScripts) + + // Estimate the tx size in vByte + // #nosec G701 always positive + vBytes := uint64(blockchain.GetTransactionWeight(btcutil.NewTx(tx)) / blockchain.WitnessScaleFactor) + vBytesEstimated, err := EstimateOuttxSize(2, payees) + require.NoError(t, err) + require.Equal(t, vBytes, vBytesEstimated) +} + +func TestOuttxSizeBreakdown(t *testing.T) { + // a list of all types of addresses + payees := []btcutil.Address{ + getTestAddrScript(t, ScriptTypeP2TR), + getTestAddrScript(t, ScriptTypeP2WSH), + getTestAddrScript(t, ScriptTypeP2WPKH), + getTestAddrScript(t, ScriptTypeP2SH), + getTestAddrScript(t, ScriptTypeP2PKH), + } + + // add all outtx sizes paying to each address + txSizeTotal := uint64(0) + for _, payee := range payees { + sizeOutput, err := EstimateOuttxSize(2, []btcutil.Address{payee}) + require.NoError(t, err) + txSizeTotal += sizeOutput + } + + // calculate the average outtx size + // #nosec G701 always in range + txSizeAverage := uint64((float64(txSizeTotal))/float64(len(payees)) + 0.5) + + // get deposit fee + txSizeDepositor := OuttxSizeDepositor() + require.Equal(t, uint64(68), txSizeDepositor) + + // get withdrawer fee + txSizeWithdrawer := OuttxSizeWithdrawer() + require.Equal(t, uint64(177), txSizeWithdrawer) + + // total outtx size == (deposit fee + withdrawer fee), 245 = 68 + 177 + require.Equal(t, outTxBytesAvg, txSizeAverage) + require.Equal(t, txSizeAverage, txSizeDepositor+txSizeWithdrawer) + + // check default depositor fee + depositFee := DepositorFee(defaultDepositorFeeRate) + require.Equal(t, depositFee, 0.00001360) +} + +func TestOuttxSizeMinMaxError(t *testing.T) { + // P2TR output is the largest in size; P2WPKH is the smallest + toP2TR := getTestAddrScript(t, ScriptTypeP2TR) + toP2WPKH := getTestAddrScript(t, ScriptTypeP2WPKH) + + // Estimate the largest outtx size in vByte + sizeMax, err := EstimateOuttxSize(21, []btcutil.Address{toP2TR}) + require.NoError(t, err) + require.Equal(t, outTxBytesMax, sizeMax) + + // Estimate the smallest outtx size in vByte + sizeMin, err := EstimateOuttxSize(2, []btcutil.Address{toP2WPKH}) + require.NoError(t, err) + require.Equal(t, outTxBytesMin, sizeMin) + + // Estimate unknown address type + nilP2PK := (*btcutil.AddressPubKey)(nil) + size, err := EstimateOuttxSize(1, []btcutil.Address{nilP2PK}) + require.Error(t, err) + require.Equal(t, uint64(0), size) +} diff --git a/zetaclient/bitcoin/inbound_tracker.go b/zetaclient/bitcoin/inbound_tracker.go index 4bf2b5b8ff..b948a6c979 100644 --- a/zetaclient/bitcoin/inbound_tracker.go +++ b/zetaclient/bitcoin/inbound_tracker.go @@ -79,7 +79,7 @@ func (ob *BTCChainClient) CheckReceiptForBtcTxHash(txHash string, vote bool) (st return "", err } // #nosec G701 always positive - event, err := GetBtcEvent(*tx, tss, uint64(blockVb.Height), &ob.logger.InTx, ob.netParams, depositorFee) + event, err := GetBtcEvent(ob.rpcClient, *tx, tss, uint64(blockVb.Height), ob.logger.InTx, ob.netParams, depositorFee) if err != nil { return "", err } diff --git a/zetaclient/bitcoin/tx_script.go b/zetaclient/bitcoin/tx_script.go new file mode 100644 index 0000000000..08046b0171 --- /dev/null +++ b/zetaclient/bitcoin/tx_script.go @@ -0,0 +1,229 @@ +package bitcoin + +import ( + "bytes" + "encoding/hex" + "fmt" + "strconv" + + "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcutil" + "github.com/cosmos/btcutil/base58" + "github.com/pkg/errors" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/constant" + "golang.org/x/crypto/ripemd160" +) + +const ( + // Lenth of P2TR script [OP_1 0x20 <32-byte-hash>] + LengthScriptP2TR = 34 + + // Length of P2WSH script [OP_0 0x20 <32-byte-hash>] + LengthScriptP2WSH = 34 + + // Length of P2WPKH script [OP_0 0x14 <20-byte-hash>] + LengthScriptP2WPKH = 22 + + // Length of P2SH script [OP_HASH160 0x14 <20-byte-hash> OP_EQUAL] + LengthScriptP2SH = 23 + + // Length of P2PKH script [OP_DUP OP_HASH160 0x14 <20-byte-hash> OP_EQUALVERIFY OP_CHECKSIG] + LengthScriptP2PKH = 25 +) + +// PayToAddrScript creates a new script to pay a transaction output to a the +// specified address. +func PayToAddrScript(addr btcutil.Address) ([]byte, error) { + switch addr := addr.(type) { + case *chains.AddressTaproot: + return chains.PayToWitnessTaprootScript(addr.ScriptAddress()) + default: + return txscript.PayToAddrScript(addr) + } +} + +// IsPkScriptP2TR checks if the given script is a P2TR script +func IsPkScriptP2TR(script []byte) bool { + return len(script) == LengthScriptP2TR && script[0] == txscript.OP_1 && script[1] == 0x20 +} + +// IsPkScriptP2WSH checks if the given script is a P2WSH script +func IsPkScriptP2WSH(script []byte) bool { + return len(script) == LengthScriptP2WSH && script[0] == txscript.OP_0 && script[1] == 0x20 +} + +// IsPkScriptP2WPKH checks if the given script is a P2WPKH script +func IsPkScriptP2WPKH(script []byte) bool { + return len(script) == LengthScriptP2WPKH && script[0] == txscript.OP_0 && script[1] == 0x14 +} + +// IsPkScriptP2SH checks if the given script is a P2SH script +func IsPkScriptP2SH(script []byte) bool { + return len(script) == LengthScriptP2SH && + script[0] == txscript.OP_HASH160 && + script[1] == 0x14 && + script[22] == txscript.OP_EQUAL +} + +// IsPkScriptP2PKH checks if the given script is a P2PKH script +func IsPkScriptP2PKH(script []byte) bool { + return len(script) == LengthScriptP2PKH && + script[0] == txscript.OP_DUP && + script[1] == txscript.OP_HASH160 && + script[2] == 0x14 && + script[23] == txscript.OP_EQUALVERIFY && + script[24] == txscript.OP_CHECKSIG +} + +// DecodeScriptP2TR decodes address from P2TR script +func DecodeScriptP2TR(scriptHex string, net *chaincfg.Params) (string, error) { + script, err := hex.DecodeString(scriptHex) + if err != nil { + return "", errors.Wrapf(err, "error decoding script %s", scriptHex) + } + if !IsPkScriptP2TR(script) { + return "", fmt.Errorf("invalid P2TR script: %s", scriptHex) + } + witnessProg := script[2:] + receiverAddress, err := chains.NewAddressTaproot(witnessProg, net) + if err != nil { // should never happen + return "", errors.Wrapf(err, "error getting address from script %s", scriptHex) + } + return receiverAddress.EncodeAddress(), nil +} + +// DecodeScriptP2WSH decodes address from P2WSH script +func DecodeScriptP2WSH(scriptHex string, net *chaincfg.Params) (string, error) { + script, err := hex.DecodeString(scriptHex) + if err != nil { + return "", errors.Wrapf(err, "error decoding script: %s", scriptHex) + } + if !IsPkScriptP2WSH(script) { + return "", fmt.Errorf("invalid P2WSH script: %s", scriptHex) + } + witnessProg := script[2:] + receiverAddress, err := btcutil.NewAddressWitnessScriptHash(witnessProg, net) + if err != nil { // should never happen + return "", errors.Wrapf(err, "error getting receiver from script: %s", scriptHex) + } + return receiverAddress.EncodeAddress(), nil +} + +// DecodeScriptP2WPKH decodes address from P2WPKH script +func DecodeScriptP2WPKH(scriptHex string, net *chaincfg.Params) (string, error) { + script, err := hex.DecodeString(scriptHex) + if err != nil { + return "", errors.Wrapf(err, "error decoding script: %s", scriptHex) + } + if !IsPkScriptP2WPKH(script) { + return "", fmt.Errorf("invalid P2WPKH script: %s", scriptHex) + } + witnessProg := script[2:] + receiverAddress, err := btcutil.NewAddressWitnessPubKeyHash(witnessProg, net) + if err != nil { // should never happen + return "", errors.Wrapf(err, "error getting receiver from script: %s", scriptHex) + } + return receiverAddress.EncodeAddress(), nil +} + +// DecodeScriptP2SH decodes address from P2SH script +func DecodeScriptP2SH(scriptHex string, net *chaincfg.Params) (string, error) { + script, err := hex.DecodeString(scriptHex) + if err != nil { + return "", errors.Wrapf(err, "error decoding script: %s", scriptHex) + } + if !IsPkScriptP2SH(script) { + return "", fmt.Errorf("invalid P2SH script: %s", scriptHex) + } + scriptHash := script[2:22] + return EncodeAddress(scriptHash, net.ScriptHashAddrID), nil +} + +// DecodeScriptP2PKH decodes address from P2PKH script +func DecodeScriptP2PKH(scriptHex string, net *chaincfg.Params) (string, error) { + script, err := hex.DecodeString(scriptHex) + if err != nil { + return "", errors.Wrapf(err, "error decoding script: %s", scriptHex) + } + if !IsPkScriptP2PKH(script) { + return "", fmt.Errorf("invalid P2PKH script: %s", scriptHex) + } + pubKeyHash := script[3:23] + return EncodeAddress(pubKeyHash, net.PubKeyHashAddrID), nil +} + +// DecodeOpReturnMemo decodes memo from OP_RETURN script +// returns (memo, found, error) +func DecodeOpReturnMemo(scriptHex string, txid string) ([]byte, bool, error) { + if len(scriptHex) >= 4 && scriptHex[:2] == "6a" { // OP_RETURN + memoSize, err := strconv.ParseInt(scriptHex[2:4], 16, 32) + if err != nil { + return nil, false, errors.Wrapf(err, "error decoding memo size: %s", scriptHex) + } + if int(memoSize) != (len(scriptHex)-4)/2 { + return nil, false, fmt.Errorf("memo size mismatch: %d != %d", memoSize, (len(scriptHex)-4)/2) + } + memoBytes, err := hex.DecodeString(scriptHex[4:]) + if err != nil { + return nil, false, errors.Wrapf(err, "error hex decoding memo: %s", scriptHex) + } + if bytes.Equal(memoBytes, []byte(constant.DonationMessage)) { + return nil, false, fmt.Errorf("donation tx: %s", txid) + } + return memoBytes, true, nil + } + return nil, false, nil +} + +// EncodeAddress returns a human-readable payment address given a ripemd160 hash +// and netID which encodes the bitcoin network and address type. It is used +// in both pay-to-pubkey-hash (P2PKH) and pay-to-script-hash (P2SH) address +// encoding. +// Note: this function is a copy of the function in btcutil/address.go +func EncodeAddress(hash160 []byte, netID byte) string { + // Format is 1 byte for a network and address class (i.e. P2PKH vs + // P2SH), 20 bytes for a RIPEMD160 hash, and 4 bytes of checksum. + return base58.CheckEncode(hash160[:ripemd160.Size], netID) +} + +// DecodeTSSVout decodes receiver and amount from a given TSS vout +func DecodeTSSVout(vout btcjson.Vout, receiverExpected string, chain chains.Chain) (string, int64, error) { + // parse amount + amount, err := GetSatoshis(vout.Value) + if err != nil { + return "", 0, errors.Wrap(err, "error getting satoshis") + } + // get btc chain params + chainParams, err := chains.GetBTCChainParams(chain.ChainId) + if err != nil { + return "", 0, errors.Wrapf(err, "error GetBTCChainParams for chain %d", chain.ChainId) + } + // decode cctx receiver address + addr, err := chains.DecodeBtcAddress(receiverExpected, chain.ChainId) + if err != nil { + return "", 0, errors.Wrapf(err, "error decoding receiver %s", receiverExpected) + } + // parse receiver address from vout + var receiverVout string + switch addr.(type) { + case *chains.AddressTaproot: + receiverVout, err = DecodeScriptP2TR(vout.ScriptPubKey.Hex, chainParams) + case *btcutil.AddressWitnessScriptHash: + receiverVout, err = DecodeScriptP2WSH(vout.ScriptPubKey.Hex, chainParams) + case *btcutil.AddressWitnessPubKeyHash: + receiverVout, err = DecodeScriptP2WPKH(vout.ScriptPubKey.Hex, chainParams) + case *btcutil.AddressScriptHash: + receiverVout, err = DecodeScriptP2SH(vout.ScriptPubKey.Hex, chainParams) + case *btcutil.AddressPubKeyHash: + receiverVout, err = DecodeScriptP2PKH(vout.ScriptPubKey.Hex, chainParams) + default: + return "", 0, fmt.Errorf("unsupported receiver address type: %T", addr) + } + if err != nil { + return "", 0, errors.Wrap(err, "error decoding TSS vout") + } + return receiverVout, amount, nil +} diff --git a/zetaclient/bitcoin/tx_script_test.go b/zetaclient/bitcoin/tx_script_test.go new file mode 100644 index 0000000000..c29e22c556 --- /dev/null +++ b/zetaclient/bitcoin/tx_script_test.go @@ -0,0 +1,521 @@ +package bitcoin + +import ( + "encoding/hex" + "path" + "strings" + "testing" + + "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/chaincfg" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/constant" + "github.com/zeta-chain/zetacore/zetaclient/testutils" +) + +func TestDecodeVoutP2TR(t *testing.T) { + // load archived tx raw result + // https://mempool.space/tx/259fc21e63e138136c8f19270a0f7ca10039a66a474f91d23a17896f46e677a7 + chain := chains.BtcMainnetChain() + txHash := "259fc21e63e138136c8f19270a0f7ca10039a66a474f91d23a17896f46e677a7" + net := &chaincfg.MainNetParams + nameTx := path.Join("../", testutils.TestDataPathBTC, testutils.FileNameBTCTxByType(chain.ChainId, "P2TR", txHash)) + + var rawResult btcjson.TxRawResult + testutils.LoadObjectFromJSONFile(t, &rawResult, nameTx) + require.Len(t, rawResult.Vout, 2) + + // decode vout 0, P2TR + receiver, err := DecodeScriptP2TR(rawResult.Vout[0].ScriptPubKey.Hex, net) + require.NoError(t, err) + require.Equal(t, "bc1p4scddlkkuw9486579autxumxmkvuphm5pz4jvf7f6pdh50p2uzqstawjt9", receiver) +} + +func TestDecodeVoutP2TRErrors(t *testing.T) { + // load archived tx raw result + // https://mempool.space/tx/259fc21e63e138136c8f19270a0f7ca10039a66a474f91d23a17896f46e677a7 + chain := chains.BtcMainnetChain() + txHash := "259fc21e63e138136c8f19270a0f7ca10039a66a474f91d23a17896f46e677a7" + net := &chaincfg.MainNetParams + nameTx := path.Join("../", testutils.TestDataPathBTC, testutils.FileNameBTCTxByType(chain.ChainId, "P2TR", txHash)) + + var rawResult btcjson.TxRawResult + testutils.LoadObjectFromJSONFile(t, &rawResult, nameTx) + + t.Run("should return error on invalid script", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + invalidVout.ScriptPubKey.Hex = "invalid script" + _, err := DecodeScriptP2TR(invalidVout.ScriptPubKey.Hex, net) + require.ErrorContains(t, err, "error decoding script") + }) + t.Run("should return error on wrong script length", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + invalidVout.ScriptPubKey.Hex = "0020" // 2 bytes, should be 34 + _, err := DecodeScriptP2TR(invalidVout.ScriptPubKey.Hex, net) + require.ErrorContains(t, err, "invalid P2TR script") + }) + t.Run("should return error on invalid OP_1", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + // modify the OP_1 '51' to OP_2 '52' + invalidVout.ScriptPubKey.Hex = strings.Replace(invalidVout.ScriptPubKey.Hex, "51", "52", 1) + _, err := DecodeScriptP2TR(invalidVout.ScriptPubKey.Hex, net) + require.ErrorContains(t, err, "invalid P2TR script") + }) + t.Run("should return error on wrong hash length", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + // modify the length '20' to '19' + invalidVout.ScriptPubKey.Hex = strings.Replace(invalidVout.ScriptPubKey.Hex, "5120", "5119", 1) + _, err := DecodeScriptP2TR(invalidVout.ScriptPubKey.Hex, net) + require.ErrorContains(t, err, "invalid P2TR script") + }) +} + +func TestDecodeVoutP2WSH(t *testing.T) { + // load archived tx raw result + // https://mempool.space/tx/791bb9d16f7ab05f70a116d18eaf3552faf77b9d5688699a480261424b4f7e53 + chain := chains.BtcMainnetChain() + txHash := "791bb9d16f7ab05f70a116d18eaf3552faf77b9d5688699a480261424b4f7e53" + net := &chaincfg.MainNetParams + nameTx := path.Join("../", testutils.TestDataPathBTC, testutils.FileNameBTCTxByType(chain.ChainId, "P2WSH", txHash)) + + var rawResult btcjson.TxRawResult + testutils.LoadObjectFromJSONFile(t, &rawResult, nameTx) + require.Len(t, rawResult.Vout, 1) + + // decode vout 0, P2WSH + receiver, err := DecodeScriptP2WSH(rawResult.Vout[0].ScriptPubKey.Hex, net) + require.NoError(t, err) + require.Equal(t, "bc1qqv6pwn470vu0tssdfha4zdk89v3c8ch5lsnyy855k9hcrcv3evequdmjmc", receiver) +} + +func TestDecodeVoutP2WSHErrors(t *testing.T) { + // load archived tx raw result + // https://mempool.space/tx/791bb9d16f7ab05f70a116d18eaf3552faf77b9d5688699a480261424b4f7e53 + chain := chains.BtcMainnetChain() + txHash := "791bb9d16f7ab05f70a116d18eaf3552faf77b9d5688699a480261424b4f7e53" + net := &chaincfg.MainNetParams + nameTx := path.Join("../", testutils.TestDataPathBTC, testutils.FileNameBTCTxByType(chain.ChainId, "P2WSH", txHash)) + + var rawResult btcjson.TxRawResult + testutils.LoadObjectFromJSONFile(t, &rawResult, nameTx) + + t.Run("should return error on invalid script", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + invalidVout.ScriptPubKey.Hex = "invalid script" + _, err := DecodeScriptP2WSH(invalidVout.ScriptPubKey.Hex, net) + require.ErrorContains(t, err, "error decoding script") + }) + t.Run("should return error on wrong script length", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + invalidVout.ScriptPubKey.Hex = "0020" // 2 bytes, should be 34 + _, err := DecodeScriptP2WSH(invalidVout.ScriptPubKey.Hex, net) + require.ErrorContains(t, err, "invalid P2WSH script") + }) + t.Run("should return error on invalid OP_0", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + // modify the OP_0 '00' to OP_1 '51' + invalidVout.ScriptPubKey.Hex = strings.Replace(invalidVout.ScriptPubKey.Hex, "00", "51", 1) + _, err := DecodeScriptP2WSH(invalidVout.ScriptPubKey.Hex, net) + require.ErrorContains(t, err, "invalid P2WSH script") + }) + t.Run("should return error on wrong hash length", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + // modify the length '20' to '19' + invalidVout.ScriptPubKey.Hex = strings.Replace(invalidVout.ScriptPubKey.Hex, "0020", "0019", 1) + _, err := DecodeScriptP2WSH(invalidVout.ScriptPubKey.Hex, net) + require.ErrorContains(t, err, "invalid P2WSH script") + }) +} + +func TestDecodeP2WPKHVout(t *testing.T) { + // load archived outtx raw result + // https://mempool.space/tx/030cd813443f7b70cc6d8a544d320c6d8465e4528fc0f3410b599dc0b26753a0 + chain := chains.BtcMainnetChain() + nonce := uint64(148) + net := &chaincfg.MainNetParams + nameTx := path.Join("../", testutils.TestDataPathBTC, testutils.FileNameBTCOuttx(chain.ChainId, nonce)) + + var rawResult btcjson.TxRawResult + testutils.LoadObjectFromJSONFile(t, &rawResult, nameTx) + require.Len(t, rawResult.Vout, 3) + + // decode vout 0, nonce mark 148 + receiver, err := DecodeScriptP2WPKH(rawResult.Vout[0].ScriptPubKey.Hex, net) + require.NoError(t, err) + require.Equal(t, testutils.TSSAddressBTCMainnet, receiver) + + // decode vout 1, payment 0.00012000 BTC + receiver, err = DecodeScriptP2WPKH(rawResult.Vout[1].ScriptPubKey.Hex, net) + require.NoError(t, err) + require.Equal(t, "bc1qpsdlklfcmlcfgm77c43x65ddtrt7n0z57hsyjp", receiver) + + // decode vout 2, change 0.39041489 BTC + receiver, err = DecodeScriptP2WPKH(rawResult.Vout[2].ScriptPubKey.Hex, net) + require.NoError(t, err) + require.Equal(t, testutils.TSSAddressBTCMainnet, receiver) +} + +func TestDecodeP2WPKHVoutErrors(t *testing.T) { + // load archived outtx raw result + // https://mempool.space/tx/030cd813443f7b70cc6d8a544d320c6d8465e4528fc0f3410b599dc0b26753a0 + chain := chains.BtcMainnetChain() + nonce := uint64(148) + net := &chaincfg.MainNetParams + nameTx := path.Join("../", testutils.TestDataPathBTC, testutils.FileNameBTCOuttx(chain.ChainId, nonce)) + + var rawResult btcjson.TxRawResult + testutils.LoadObjectFromJSONFile(t, &rawResult, nameTx) + + t.Run("should return error on invalid script", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + invalidVout.ScriptPubKey.Hex = "invalid script" + _, err := DecodeScriptP2WPKH(invalidVout.ScriptPubKey.Hex, net) + require.ErrorContains(t, err, "error decoding script") + }) + t.Run("should return error on wrong script length", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + invalidVout.ScriptPubKey.Hex = "0014" // 2 bytes, should be 22 + _, err := DecodeScriptP2WPKH(invalidVout.ScriptPubKey.Hex, net) + require.ErrorContains(t, err, "invalid P2WPKH script") + }) + t.Run("should return error on wrong hash length", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + // modify the length '14' to '13' + invalidVout.ScriptPubKey.Hex = strings.Replace(invalidVout.ScriptPubKey.Hex, "0014", "0013", 1) + _, err := DecodeScriptP2WPKH(invalidVout.ScriptPubKey.Hex, net) + require.ErrorContains(t, err, "invalid P2WPKH script") + }) +} + +func TestDecodeVoutP2SH(t *testing.T) { + // load archived tx raw result + // https://mempool.space/tx/fd68c8b4478686ca6f5ae4c28eaab055490650dbdaa6c2c8e380a7e075958a21 + chain := chains.BtcMainnetChain() + txHash := "fd68c8b4478686ca6f5ae4c28eaab055490650dbdaa6c2c8e380a7e075958a21" + net := &chaincfg.MainNetParams + nameTx := path.Join("../", testutils.TestDataPathBTC, testutils.FileNameBTCTxByType(chain.ChainId, "P2SH", txHash)) + + var rawResult btcjson.TxRawResult + testutils.LoadObjectFromJSONFile(t, &rawResult, nameTx) + require.Len(t, rawResult.Vout, 2) + + // decode vout 0, P2SH + receiver, err := DecodeScriptP2SH(rawResult.Vout[0].ScriptPubKey.Hex, net) + require.NoError(t, err) + require.Equal(t, "327z4GyFM8Y8DiYfasGKQWhRK4MvyMSEgE", receiver) +} + +func TestDecodeVoutP2SHErrors(t *testing.T) { + // load archived tx raw result + // https://mempool.space/tx/fd68c8b4478686ca6f5ae4c28eaab055490650dbdaa6c2c8e380a7e075958a21 + chain := chains.BtcMainnetChain() + txHash := "fd68c8b4478686ca6f5ae4c28eaab055490650dbdaa6c2c8e380a7e075958a21" + net := &chaincfg.MainNetParams + nameTx := path.Join("../", testutils.TestDataPathBTC, testutils.FileNameBTCTxByType(chain.ChainId, "P2SH", txHash)) + + var rawResult btcjson.TxRawResult + testutils.LoadObjectFromJSONFile(t, &rawResult, nameTx) + + t.Run("should return error on invalid script", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + invalidVout.ScriptPubKey.Hex = "invalid script" + _, err := DecodeScriptP2SH(invalidVout.ScriptPubKey.Hex, net) + require.ErrorContains(t, err, "error decoding script") + }) + t.Run("should return error on wrong script length", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + invalidVout.ScriptPubKey.Hex = "0014" // 2 bytes, should be 23 + _, err := DecodeScriptP2SH(invalidVout.ScriptPubKey.Hex, net) + require.ErrorContains(t, err, "invalid P2SH script") + }) + t.Run("should return error on invalid OP_HASH160", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + // modify the OP_HASH160 'a9' to OP_HASH256 'aa' + invalidVout.ScriptPubKey.Hex = strings.Replace(invalidVout.ScriptPubKey.Hex, "a9", "aa", 1) + _, err := DecodeScriptP2SH(invalidVout.ScriptPubKey.Hex, net) + require.ErrorContains(t, err, "invalid P2SH script") + }) + t.Run("should return error on wrong data length", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + // modify the length '14' to '13' + invalidVout.ScriptPubKey.Hex = strings.Replace(invalidVout.ScriptPubKey.Hex, "a914", "a913", 1) + _, err := DecodeScriptP2SH(invalidVout.ScriptPubKey.Hex, net) + require.ErrorContains(t, err, "invalid P2SH script") + }) + t.Run("should return error on invalid OP_EQUAL", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + invalidVout.ScriptPubKey.Hex = strings.Replace(invalidVout.ScriptPubKey.Hex, "87", "88", 1) + _, err := DecodeScriptP2SH(invalidVout.ScriptPubKey.Hex, net) + require.ErrorContains(t, err, "invalid P2SH script") + }) +} + +func TestDecodeVoutP2PKH(t *testing.T) { + // load archived tx raw result + // https://mempool.space/tx/9c741de6e17382b7a9113fc811e3558981a35a360e3d1262a6675892c91322ca + chain := chains.BtcMainnetChain() + txHash := "9c741de6e17382b7a9113fc811e3558981a35a360e3d1262a6675892c91322ca" + net := &chaincfg.MainNetParams + nameTx := path.Join("../", testutils.TestDataPathBTC, testutils.FileNameBTCTxByType(chain.ChainId, "P2PKH", txHash)) + + var rawResult btcjson.TxRawResult + testutils.LoadObjectFromJSONFile(t, &rawResult, nameTx) + require.Len(t, rawResult.Vout, 2) + + // decode vout 0, P2PKH + receiver, err := DecodeScriptP2PKH(rawResult.Vout[0].ScriptPubKey.Hex, net) + require.NoError(t, err) + require.Equal(t, "1FueivsE338W2LgifJ25HhTcVJ7CRT8kte", receiver) +} + +func TestDecodeVoutP2PKHErrors(t *testing.T) { + // load archived tx raw result + // https://mempool.space/tx/9c741de6e17382b7a9113fc811e3558981a35a360e3d1262a6675892c91322ca + chain := chains.BtcMainnetChain() + txHash := "9c741de6e17382b7a9113fc811e3558981a35a360e3d1262a6675892c91322ca" + net := &chaincfg.MainNetParams + nameTx := path.Join("../", testutils.TestDataPathBTC, testutils.FileNameBTCTxByType(chain.ChainId, "P2PKH", txHash)) + + var rawResult btcjson.TxRawResult + testutils.LoadObjectFromJSONFile(t, &rawResult, nameTx) + + t.Run("should return error on invalid script", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + invalidVout.ScriptPubKey.Hex = "invalid script" + _, err := DecodeScriptP2PKH(invalidVout.ScriptPubKey.Hex, net) + require.ErrorContains(t, err, "error decoding script") + }) + t.Run("should return error on wrong script length", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + invalidVout.ScriptPubKey.Hex = "76a914" // 3 bytes, should be 25 + _, err := DecodeScriptP2PKH(invalidVout.ScriptPubKey.Hex, net) + require.ErrorContains(t, err, "invalid P2PKH script") + }) + t.Run("should return error on invalid OP_DUP", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + // modify the OP_DUP '76' to OP_NIP '77' + invalidVout.ScriptPubKey.Hex = strings.Replace(invalidVout.ScriptPubKey.Hex, "76", "77", 1) + _, err := DecodeScriptP2PKH(invalidVout.ScriptPubKey.Hex, net) + require.ErrorContains(t, err, "invalid P2PKH script") + }) + t.Run("should return error on invalid OP_HASH160", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + // modify the OP_HASH160 'a9' to OP_HASH256 'aa' + invalidVout.ScriptPubKey.Hex = strings.Replace(invalidVout.ScriptPubKey.Hex, "76a9", "76aa", 1) + _, err := DecodeScriptP2PKH(invalidVout.ScriptPubKey.Hex, net) + require.ErrorContains(t, err, "invalid P2PKH script") + }) + t.Run("should return error on wrong data length", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + // modify the length '14' to '13' + invalidVout.ScriptPubKey.Hex = strings.Replace(invalidVout.ScriptPubKey.Hex, "76a914", "76a913", 1) + _, err := DecodeScriptP2PKH(invalidVout.ScriptPubKey.Hex, net) + require.ErrorContains(t, err, "invalid P2PKH script") + }) + t.Run("should return error on invalid OP_EQUALVERIFY", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + // modify the OP_EQUALVERIFY '88' to OP_RESERVED1 '89' + invalidVout.ScriptPubKey.Hex = strings.Replace(invalidVout.ScriptPubKey.Hex, "88ac", "89ac", 1) + _, err := DecodeScriptP2PKH(invalidVout.ScriptPubKey.Hex, net) + require.ErrorContains(t, err, "invalid P2PKH script") + }) + t.Run("should return error on invalid OP_CHECKSIG", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + // modify the OP_CHECKSIG 'ac' to OP_CHECKSIGVERIFY 'ad' + invalidVout.ScriptPubKey.Hex = strings.Replace(invalidVout.ScriptPubKey.Hex, "88ac", "88ad", 1) + _, err := DecodeScriptP2PKH(invalidVout.ScriptPubKey.Hex, net) + require.ErrorContains(t, err, "invalid P2PKH script") + }) +} + +func TestDecodeOpReturnMemo(t *testing.T) { + // load archived intx raw result + // https://mempool.space/tx/847139aa65aa4a5ee896375951cbf7417cfc8a4d6f277ec11f40cd87319f04aa + chain := chains.BtcMainnetChain() + txHash := "847139aa65aa4a5ee896375951cbf7417cfc8a4d6f277ec11f40cd87319f04aa" + scriptHex := "6a1467ed0bcc4e1256bc2ce87d22e190d63a120114bf" + rawResult := testutils.LoadBTCIntxRawResult(t, chain.ChainId, txHash, false) + require.True(t, len(rawResult.Vout) >= 2) + require.Equal(t, scriptHex, rawResult.Vout[1].ScriptPubKey.Hex) + + t.Run("should decode memo from OP_RETURN output", func(t *testing.T) { + memo, found, err := DecodeOpReturnMemo(rawResult.Vout[1].ScriptPubKey.Hex, txHash) + require.NoError(t, err) + require.True(t, found) + // [OP_RETURN, 0x14,<20-byte-hash>] + require.Equal(t, scriptHex[4:], hex.EncodeToString(memo)) + }) + t.Run("should return nil memo non-OP_RETURN output", func(t *testing.T) { + // modify the OP_RETURN to OP_1 + scriptInvalid := strings.Replace(scriptHex, "6a", "51", 1) + memo, found, err := DecodeOpReturnMemo(scriptInvalid, txHash) + require.NoError(t, err) + require.False(t, found) + require.Nil(t, memo) + }) + t.Run("should return nil memo on invalid script", func(t *testing.T) { + // use known short script + scriptInvalid := "00" + memo, found, err := DecodeOpReturnMemo(scriptInvalid, txHash) + require.NoError(t, err) + require.False(t, found) + require.Nil(t, memo) + }) +} + +func TestDecodeOpReturnMemoErrors(t *testing.T) { + // https://mempool.space/tx/847139aa65aa4a5ee896375951cbf7417cfc8a4d6f277ec11f40cd87319f04aa + txHash := "847139aa65aa4a5ee896375951cbf7417cfc8a4d6f277ec11f40cd87319f04aa" + scriptHex := "6a1467ed0bcc4e1256bc2ce87d22e190d63a120114bf" + + t.Run("should return error on invalid memo size", func(t *testing.T) { + // use invalid memo size + scriptInvalid := strings.Replace(scriptHex, "6a14", "6axy", 1) + memo, found, err := DecodeOpReturnMemo(scriptInvalid, txHash) + require.ErrorContains(t, err, "error decoding memo size") + require.False(t, found) + require.Nil(t, memo) + }) + t.Run("should return error on memo size mismatch", func(t *testing.T) { + // use wrong memo size + scriptInvalid := strings.Replace(scriptHex, "6a14", "6a13", 1) + memo, found, err := DecodeOpReturnMemo(scriptInvalid, txHash) + require.ErrorContains(t, err, "memo size mismatch") + require.False(t, found) + require.Nil(t, memo) + }) + t.Run("should return error on invalid hex", func(t *testing.T) { + // use invalid hex + scriptInvalid := strings.Replace(scriptHex, "6a1467", "6a14xy", 1) + memo, found, err := DecodeOpReturnMemo(scriptInvalid, txHash) + require.ErrorContains(t, err, "error hex decoding memo") + require.False(t, found) + require.Nil(t, memo) + }) + t.Run("should return nil memo on donation tx", func(t *testing.T) { + // use donation sctipt "6a0a4920616d207269636821" + scriptDonation := "6a0a" + hex.EncodeToString([]byte(constant.DonationMessage)) + memo, found, err := DecodeOpReturnMemo(scriptDonation, txHash) + require.ErrorContains(t, err, "donation tx") + require.False(t, found) + require.Nil(t, memo) + }) +} + +func TestDecodeTSSVout(t *testing.T) { + chain := chains.BtcMainnetChain() + + t.Run("should decode P2TR vout", func(t *testing.T) { + // https://mempool.space/tx/259fc21e63e138136c8f19270a0f7ca10039a66a474f91d23a17896f46e677a7 + txHash := "259fc21e63e138136c8f19270a0f7ca10039a66a474f91d23a17896f46e677a7" + nameTx := path.Join("../", testutils.TestDataPathBTC, testutils.FileNameBTCTxByType(chain.ChainId, "P2TR", txHash)) + var rawResult btcjson.TxRawResult + testutils.LoadObjectFromJSONFile(t, &rawResult, nameTx) + + receiverExpected := "bc1p4scddlkkuw9486579autxumxmkvuphm5pz4jvf7f6pdh50p2uzqstawjt9" + receiver, amount, err := DecodeTSSVout(rawResult.Vout[0], receiverExpected, chain) + require.NoError(t, err) + require.Equal(t, receiverExpected, receiver) + require.Equal(t, int64(45000), amount) + }) + t.Run("should decode P2WSH vout", func(t *testing.T) { + // https://mempool.space/tx/791bb9d16f7ab05f70a116d18eaf3552faf77b9d5688699a480261424b4f7e53 + txHash := "791bb9d16f7ab05f70a116d18eaf3552faf77b9d5688699a480261424b4f7e53" + nameTx := path.Join("../", testutils.TestDataPathBTC, testutils.FileNameBTCTxByType(chain.ChainId, "P2WSH", txHash)) + var rawResult btcjson.TxRawResult + testutils.LoadObjectFromJSONFile(t, &rawResult, nameTx) + + receiverExpected := "bc1qqv6pwn470vu0tssdfha4zdk89v3c8ch5lsnyy855k9hcrcv3evequdmjmc" + receiver, amount, err := DecodeTSSVout(rawResult.Vout[0], receiverExpected, chain) + require.NoError(t, err) + require.Equal(t, receiverExpected, receiver) + require.Equal(t, int64(36557203), amount) + }) + t.Run("should decode P2WPKH vout", func(t *testing.T) { + // https://mempool.space/tx/5d09d232bfe41c7cb831bf53fc2e4029ab33a99087fd5328a2331b52ff2ebe5b + txHash := "5d09d232bfe41c7cb831bf53fc2e4029ab33a99087fd5328a2331b52ff2ebe5b" + nameTx := path.Join("../", testutils.TestDataPathBTC, testutils.FileNameBTCTxByType(chain.ChainId, "P2WPKH", txHash)) + var rawResult btcjson.TxRawResult + testutils.LoadObjectFromJSONFile(t, &rawResult, nameTx) + + receiverExpected := "bc1qaxf82vyzy8y80v000e7t64gpten7gawewzu42y" + receiver, amount, err := DecodeTSSVout(rawResult.Vout[0], receiverExpected, chain) + require.NoError(t, err) + require.Equal(t, receiverExpected, receiver) + require.Equal(t, int64(79938), amount) + }) + t.Run("should decode P2SH vout", func(t *testing.T) { + // https://mempool.space/tx/fd68c8b4478686ca6f5ae4c28eaab055490650dbdaa6c2c8e380a7e075958a21 + txHash := "fd68c8b4478686ca6f5ae4c28eaab055490650dbdaa6c2c8e380a7e075958a21" + nameTx := path.Join("../", testutils.TestDataPathBTC, testutils.FileNameBTCTxByType(chain.ChainId, "P2SH", txHash)) + var rawResult btcjson.TxRawResult + testutils.LoadObjectFromJSONFile(t, &rawResult, nameTx) + + receiverExpected := "327z4GyFM8Y8DiYfasGKQWhRK4MvyMSEgE" + receiver, amount, err := DecodeTSSVout(rawResult.Vout[0], receiverExpected, chain) + require.NoError(t, err) + require.Equal(t, receiverExpected, receiver) + require.Equal(t, int64(1003881), amount) + }) + t.Run("should decode P2PKH vout", func(t *testing.T) { + // https://mempool.space/tx/9c741de6e17382b7a9113fc811e3558981a35a360e3d1262a6675892c91322ca + txHash := "9c741de6e17382b7a9113fc811e3558981a35a360e3d1262a6675892c91322ca" + nameTx := path.Join("../", testutils.TestDataPathBTC, testutils.FileNameBTCTxByType(chain.ChainId, "P2PKH", txHash)) + var rawResult btcjson.TxRawResult + testutils.LoadObjectFromJSONFile(t, &rawResult, nameTx) + + receiverExpected := "1FueivsE338W2LgifJ25HhTcVJ7CRT8kte" + receiver, amount, err := DecodeTSSVout(rawResult.Vout[0], receiverExpected, chain) + require.NoError(t, err) + require.Equal(t, receiverExpected, receiver) + require.Equal(t, int64(1140000), amount) + }) +} + +func TestDecodeTSSVoutErrors(t *testing.T) { + // load archived tx raw result + // https://mempool.space/tx/259fc21e63e138136c8f19270a0f7ca10039a66a474f91d23a17896f46e677a7 + chain := chains.BtcMainnetChain() + txHash := "259fc21e63e138136c8f19270a0f7ca10039a66a474f91d23a17896f46e677a7" + nameTx := path.Join("../", testutils.TestDataPathBTC, testutils.FileNameBTCTxByType(chain.ChainId, "P2TR", txHash)) + + var rawResult btcjson.TxRawResult + testutils.LoadObjectFromJSONFile(t, &rawResult, nameTx) + receiverExpected := "bc1p4scddlkkuw9486579autxumxmkvuphm5pz4jvf7f6pdh50p2uzqstawjt9" + + t.Run("should return error on invalid amount", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + invalidVout.Value = -0.05 // use negative amount + receiver, amount, err := DecodeTSSVout(invalidVout, receiverExpected, chain) + require.ErrorContains(t, err, "error getting satoshis") + require.Empty(t, receiver) + require.Zero(t, amount) + }) + t.Run("should return error on invalid btc chain", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + // use invalid chain + invalidChain := chains.Chain{ChainId: 123} + receiver, amount, err := DecodeTSSVout(invalidVout, receiverExpected, invalidChain) + require.ErrorContains(t, err, "error GetBTCChainParams") + require.Empty(t, receiver) + require.Zero(t, amount) + }) + t.Run("should return error when invalid receiver passed", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + // use testnet params to decode mainnet receiver + wrongChain := chains.BtcTestNetChain() + receiver, amount, err := DecodeTSSVout(invalidVout, "bc1qulmx8ej27cj0xe20953cztr2excnmsqvuh0s5c", wrongChain) + require.ErrorContains(t, err, "error decoding receiver") + require.Empty(t, receiver) + require.Zero(t, amount) + }) + t.Run("should return error on decoding failure", func(t *testing.T) { + invalidVout := rawResult.Vout[0] + invalidVout.ScriptPubKey.Hex = "invalid script" + receiver, amount, err := DecodeTSSVout(invalidVout, receiverExpected, chain) + require.ErrorContains(t, err, "error decoding TSS vout") + require.Empty(t, receiver) + require.Zero(t, amount) + }) +} diff --git a/zetaclient/bitcoin/utils.go b/zetaclient/bitcoin/utils.go index 12d24a5880..fe2bb2481b 100644 --- a/zetaclient/bitcoin/utils.go +++ b/zetaclient/bitcoin/utils.go @@ -1,50 +1,13 @@ package bitcoin import ( - "encoding/hex" "encoding/json" - "fmt" "math" - "math/big" - "github.com/btcsuite/btcd/blockchain" - "github.com/btcsuite/btcd/btcjson" - "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcutil" - "github.com/rs/zerolog" - "github.com/zeta-chain/zetacore/pkg/chains" - clientcommon "github.com/zeta-chain/zetacore/zetaclient/common" - - "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/wire" "github.com/pkg/errors" ) -const ( - bytesPerKB = 1000 - bytesEmptyTx = 10 // an empty tx is about 10 bytes - bytesPerInput = 41 // each input is about 41 bytes - bytesPerOutput = 31 // each output is about 31 bytes - bytes1stWitness = 110 // the 1st witness incurs about 110 bytes and it may vary - bytesPerWitness = 108 // each additional witness incurs about 108 bytes and it may vary - defaultDepositorFeeRate = 20 // 20 sat/byte is the default depositor fee rate -) - -var ( - BtcOutTxBytesDepositor uint64 - BtcOutTxBytesWithdrawer uint64 - DefaultDepositorFee float64 -) - -func init() { - BtcOutTxBytesDepositor = SegWitTxSizeDepositor() // 68vB, the outtx size incurred by the depositor - BtcOutTxBytesWithdrawer = SegWitTxSizeWithdrawer() // 171vB, the outtx size incurred by the withdrawer - - // default depositor fee calculation is based on a fixed fee rate of 20 sat/byte just for simplicity. - // In reality, the fee rate on UTXO deposit is different from the fee rate when the UTXO is spent. - DefaultDepositorFee = DepositorFee(defaultDepositorFeeRate) // 0.00001360 (20 * 68vB / 100000000) -} - func PrettyPrintStruct(val interface{}) (string, error) { prettyStruct, err := json.MarshalIndent( val, @@ -57,143 +20,6 @@ func PrettyPrintStruct(val interface{}) (string, error) { return string(prettyStruct), nil } -// FeeRateToSatPerByte converts a fee rate in BTC/KB to sat/byte. -func FeeRateToSatPerByte(rate float64) *big.Int { - // #nosec G701 always in range - satPerKB := new(big.Int).SetInt64(int64(rate * btcutil.SatoshiPerBitcoin)) - return new(big.Int).Div(satPerKB, big.NewInt(bytesPerKB)) -} - -// WiredTxSize calculates the wired tx size in bytes -func WiredTxSize(numInputs uint64, numOutputs uint64) uint64 { - // Version 4 bytes + LockTime 4 bytes + Serialized varint size for the - // number of transaction inputs and outputs. - // #nosec G701 always positive - return uint64(8 + wire.VarIntSerializeSize(numInputs) + wire.VarIntSerializeSize(numOutputs)) -} - -// EstimateSegWitTxSize estimates SegWit tx size -func EstimateSegWitTxSize(numInputs uint64, numOutputs uint64) uint64 { - if numInputs == 0 { - return 0 - } - bytesWiredTx := WiredTxSize(numInputs, numOutputs) - bytesInput := numInputs * bytesPerInput - bytesOutput := numOutputs * bytesPerOutput - bytesWitness := bytes1stWitness + (numInputs-1)*bytesPerWitness - // https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#transaction-size-calculations - // Calculation for signed SegWit tx: blockchain.GetTransactionWeight(tx) / 4 - return bytesWiredTx + bytesInput + bytesOutput + bytesWitness/blockchain.WitnessScaleFactor -} - -// SegWitTxSizeDepositor returns SegWit tx size (68vB) incurred by the depositor -func SegWitTxSizeDepositor() uint64 { - return bytesPerInput + bytesPerWitness/blockchain.WitnessScaleFactor -} - -// SegWitTxSizeWithdrawer returns SegWit tx size (171vB) incurred by the withdrawer (1 input, 3 outputs) -func SegWitTxSizeWithdrawer() uint64 { - bytesWiredTx := WiredTxSize(1, 3) - bytesInput := uint64(1) * bytesPerInput // nonce mark - bytesOutput := uint64(3) * bytesPerOutput // 3 outputs: new nonce mark, payment, change - return bytesWiredTx + bytesInput + bytesOutput + bytes1stWitness/blockchain.WitnessScaleFactor -} - -// DepositorFee calculates the depositor fee in BTC for a given sat/byte fee rate -// Note: the depositor fee is charged in order to cover the cost of spending the deposited UTXO in the future -func DepositorFee(satPerByte int64) float64 { - return float64(satPerByte) * float64(BtcOutTxBytesDepositor) / btcutil.SatoshiPerBitcoin -} - -// CalcBlockAvgFeeRate calculates the average gas rate (in sat/vByte) for a given block -func CalcBlockAvgFeeRate(blockVb *btcjson.GetBlockVerboseTxResult, netParams *chaincfg.Params) (int64, error) { - // sanity check - if len(blockVb.Tx) == 0 { - return 0, errors.New("block has no transactions") - } - if len(blockVb.Tx) == 1 { - return 0, nil // only coinbase tx, it happens - } - txCoinbase := &blockVb.Tx[0] - if blockVb.Weight < blockchain.WitnessScaleFactor { - return 0, fmt.Errorf("block weight %d too small", blockVb.Weight) - } - if blockVb.Weight < txCoinbase.Weight { - return 0, fmt.Errorf("block weight %d less than coinbase tx weight %d", blockVb.Weight, txCoinbase.Weight) - } - if blockVb.Height <= 0 || blockVb.Height > math.MaxInt32 { - return 0, fmt.Errorf("invalid block height %d", blockVb.Height) - } - - // make sure the first tx is coinbase tx - txBytes, err := hex.DecodeString(txCoinbase.Hex) - if err != nil { - return 0, fmt.Errorf("failed to decode coinbase tx %s", txCoinbase.Txid) - } - tx, err := btcutil.NewTxFromBytes(txBytes) - if err != nil { - return 0, fmt.Errorf("failed to parse coinbase tx %s", txCoinbase.Txid) - } - if !blockchain.IsCoinBaseTx(tx.MsgTx()) { - return 0, fmt.Errorf("first tx %s is not coinbase tx", txCoinbase.Txid) - } - - // calculate fees earned by the miner - btcEarned := int64(0) - for _, out := range tx.MsgTx().TxOut { - if out.Value > 0 { - btcEarned += out.Value - } - } - // #nosec G701 checked above - subsidy := blockchain.CalcBlockSubsidy(int32(blockVb.Height), netParams) - if btcEarned < subsidy { - return 0, fmt.Errorf("miner earned %d, less than subsidy %d", btcEarned, subsidy) - } - txsFees := btcEarned - subsidy - - // sum up weight of all txs (<= 4 MWU) - txsWeight := int32(0) - for i, tx := range blockVb.Tx { - // coinbase doesn't pay fees, so we exclude it - if i > 0 && tx.Weight > 0 { - txsWeight += tx.Weight - } - } - - // calculate average fee rate. - vBytes := txsWeight / blockchain.WitnessScaleFactor - return txsFees / int64(vBytes), nil -} - -// CalcDepositorFee calculates the depositor fee for a given block -func CalcDepositorFee(blockVb *btcjson.GetBlockVerboseTxResult, chainID int64, netParams *chaincfg.Params, logger zerolog.Logger) float64 { - // use dynamic fee or default - dynamicFee := true - - // use default fee for regnet - if chains.IsBitcoinRegnet(chainID) { - dynamicFee = false - } - // mainnet dynamic fee takes effect only after a planned upgrade height - if chains.IsBitcoinMainnet(chainID) && blockVb.Height < DynamicDepositorFeeHeight { - dynamicFee = false - } - if !dynamicFee { - return DefaultDepositorFee - } - - // calculate deposit fee rate - feeRate, err := CalcBlockAvgFeeRate(blockVb, netParams) - if err != nil { - feeRate = defaultDepositorFeeRate // use default fee rate if calculation fails, should not happen - logger.Error().Err(err).Msgf("cannot calculate fee rate for block %d", blockVb.Height) - } - // #nosec G701 always in range - feeRate = int64(float64(feeRate) * clientcommon.BTCOuttxGasPriceMultiplier) - return DepositorFee(feeRate) -} - func GetSatoshis(btc float64) (int64, error) { // The amount is only considered invalid if it cannot be represented // as an integer type. This may happen if f is NaN or +-Infinity. @@ -221,34 +47,3 @@ func round(f float64) int64 { // #nosec G701 always in range return int64(f + 0.5) } - -func PayToWitnessPubKeyHashScript(pubKeyHash []byte) ([]byte, error) { - return txscript.NewScriptBuilder().AddOp(txscript.OP_0).AddData(pubKeyHash).Script() -} - -// DecodeP2WPKHVout decodes receiver and amount from P2WPKH output -func DecodeP2WPKHVout(vout btcjson.Vout, chain chains.Chain) (string, int64, error) { - amount, err := GetSatoshis(vout.Value) - if err != nil { - return "", 0, errors.Wrap(err, "error getting satoshis") - } - // decode P2WPKH scriptPubKey - scriptPubKey := vout.ScriptPubKey.Hex - decodedScriptPubKey, err := hex.DecodeString(scriptPubKey) - if err != nil { - return "", 0, errors.Wrapf(err, "error decoding scriptPubKey %s", scriptPubKey) - } - if len(decodedScriptPubKey) != 22 { // P2WPKH script - return "", 0, fmt.Errorf("unsupported scriptPubKey: %s", scriptPubKey) - } - witnessVersion := decodedScriptPubKey[0] - witnessProgram := decodedScriptPubKey[2:] - if witnessVersion != 0 { - return "", 0, fmt.Errorf("unsupported witness in scriptPubKey %s", scriptPubKey) - } - recvAddress, err := chain.BTCAddressFromWitnessProgram(witnessProgram) - if err != nil { - return "", 0, errors.Wrapf(err, "error getting receiver from witness program %s", witnessProgram) - } - return recvAddress, amount, nil -} diff --git a/zetaclient/bitcoin/utils_test.go b/zetaclient/bitcoin/utils_test.go deleted file mode 100644 index 7196460cd8..0000000000 --- a/zetaclient/bitcoin/utils_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package bitcoin - -import ( - "path" - "testing" - - "github.com/btcsuite/btcd/btcjson" - "github.com/stretchr/testify/require" - "github.com/zeta-chain/zetacore/pkg/chains" - "github.com/zeta-chain/zetacore/zetaclient/testutils" -) - -func TestDecodeP2WPKHVout(t *testing.T) { - // load archived outtx raw result - // https://blockstream.info/tx/030cd813443f7b70cc6d8a544d320c6d8465e4528fc0f3410b599dc0b26753a0 - chain := chains.BtcMainnetChain() - nonce := uint64(148) - nameTx := path.Join("../", testutils.TestDataPathBTC, testutils.FileNameBTCOuttx(chain.ChainId, nonce)) - - var rawResult btcjson.TxRawResult - err := testutils.LoadObjectFromJSONFile(&rawResult, nameTx) - require.NoError(t, err) - require.Len(t, rawResult.Vout, 3) - - // decode vout 0, nonce mark 148 - receiver, amount, err := DecodeP2WPKHVout(rawResult.Vout[0], chain) - require.NoError(t, err) - require.Equal(t, testutils.TSSAddressBTCMainnet, receiver) - require.Equal(t, chains.NonceMarkAmount(nonce), amount) - - // decode vout 1, payment 0.00012000 BTC - receiver, amount, err = DecodeP2WPKHVout(rawResult.Vout[1], chain) - require.NoError(t, err) - require.Equal(t, "bc1qpsdlklfcmlcfgm77c43x65ddtrt7n0z57hsyjp", receiver) - require.Equal(t, int64(12000), amount) - - // decode vout 2, change 0.39041489 BTC - receiver, amount, err = DecodeP2WPKHVout(rawResult.Vout[2], chain) - require.NoError(t, err) - require.Equal(t, testutils.TSSAddressBTCMainnet, receiver) - require.Equal(t, int64(39041489), amount) -} - -func TestDecodeP2WPKHVoutErrors(t *testing.T) { - // load archived outtx raw result - // https://blockstream.info/tx/030cd813443f7b70cc6d8a544d320c6d8465e4528fc0f3410b599dc0b26753a0 - chain := chains.BtcMainnetChain() - nonce := uint64(148) - nameTx := path.Join("../", testutils.TestDataPathBTC, testutils.FileNameBTCOuttx(chain.ChainId, nonce)) - - var rawResult btcjson.TxRawResult - err := testutils.LoadObjectFromJSONFile(&rawResult, nameTx) - require.NoError(t, err) - - t.Run("should return error on invalid amount", func(t *testing.T) { - invalidVout := rawResult.Vout[0] - invalidVout.Value = -0.5 // negative amount, should not happen - _, _, err := DecodeP2WPKHVout(invalidVout, chain) - require.Error(t, err) - require.ErrorContains(t, err, "error getting satoshis") - }) - t.Run("should return error on invalid script", func(t *testing.T) { - invalidVout := rawResult.Vout[0] - invalidVout.ScriptPubKey.Hex = "invalid script" - _, _, err := DecodeP2WPKHVout(invalidVout, chain) - require.Error(t, err) - require.ErrorContains(t, err, "error decoding scriptPubKey") - }) - t.Run("should return error on unsupported script", func(t *testing.T) { - invalidVout := rawResult.Vout[0] - // can use any invalid script, https://blockstream.info/tx/e95c6ff206103716129c8e3aa8def1427782af3490589d1ea35ccf0122adbc25 (P2SH) - invalidVout.ScriptPubKey.Hex = "a91413b2388e6532653a4b369b7e4ed130f7b81626cc87" - _, _, err := DecodeP2WPKHVout(invalidVout, chain) - require.Error(t, err) - require.ErrorContains(t, err, "unsupported scriptPubKey") - }) - t.Run("should return error on unsupported witness version", func(t *testing.T) { - invalidVout := rawResult.Vout[0] - // use a fake witness version 1, even if version 0 is the only witness version defined in BIP141 - invalidVout.ScriptPubKey.Hex = "01140c1bfb7d38dff0946fdec5626d51ad58d7e9bc54" - _, _, err := DecodeP2WPKHVout(invalidVout, chain) - require.Error(t, err) - require.ErrorContains(t, err, "unsupported witness in scriptPubKey") - }) -} diff --git a/zetaclient/compliance/compliance_test.go b/zetaclient/compliance/compliance_test.go index 638e6c8ba0..001e5d9e0d 100644 --- a/zetaclient/compliance/compliance_test.go +++ b/zetaclient/compliance/compliance_test.go @@ -14,8 +14,7 @@ import ( func TestCctxRestricted(t *testing.T) { // load archived cctx var cctx crosschaintypes.CrossChainTx - err := testutils.LoadObjectFromJSONFile(&cctx, path.Join("../", testutils.TestDataPathCctx, "cctx_1_6270.json")) - require.NoError(t, err) + testutils.LoadObjectFromJSONFile(t, &cctx, path.Join("../", testutils.TestDataPathCctx, "cctx_1_6270.json")) // create config cfg := config.Config{ diff --git a/zetaclient/evm/evm_signer_test.go b/zetaclient/evm/evm_signer_test.go index b78a3d1c26..eace105ff2 100644 --- a/zetaclient/evm/evm_signer_test.go +++ b/zetaclient/evm/evm_signer_test.go @@ -11,7 +11,6 @@ import ( "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/constant" "github.com/zeta-chain/zetacore/testutil/sample" - "github.com/zeta-chain/zetacore/x/crosschain/types" crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" appcontext "github.com/zeta-chain/zetacore/zetaclient/app_context" "github.com/zeta-chain/zetacore/zetaclient/common" @@ -67,16 +66,16 @@ func getNewOutTxProcessor() *outtxprocessor.Processor { return outtxprocessor.NewOutTxProcessorManager(logger) } -func getCCTX() (*types.CrossChainTx, error) { +func getCCTX(t *testing.T) *crosschaintypes.CrossChainTx { var cctx crosschaintypes.CrossChainTx - err := testutils.LoadObjectFromJSONFile(&cctx, path.Join("../", testutils.TestDataPathCctx, "cctx_56_68270.json")) - return &cctx, err + testutils.LoadObjectFromJSONFile(t, &cctx, path.Join("../", testutils.TestDataPathCctx, "cctx_56_68270.json")) + return &cctx } -func getInvalidCCTX() (*types.CrossChainTx, error) { +func getInvalidCCTX(t *testing.T) *crosschaintypes.CrossChainTx { var cctx crosschaintypes.CrossChainTx - err := testutils.LoadObjectFromJSONFile(&cctx, path.Join("../", testutils.TestDataPathCctx, "cctx_56_68270_invalidChainID.json")) - return &cctx, err + testutils.LoadObjectFromJSONFile(t, &cctx, path.Join("../", testutils.TestDataPathCctx, "cctx_56_68270_invalidChainID.json")) + return &cctx } func TestSigner_SetGetConnectorAddress(t *testing.T) { @@ -106,8 +105,7 @@ func TestSigner_SetGetERC20CustodyAddress(t *testing.T) { func TestSigner_TryProcessOutTx(t *testing.T) { evmSigner, err := getNewEvmSigner() require.NoError(t, err) - cctx, err := getCCTX() - require.NoError(t, err) + cctx := getCCTX(t) processorManager := getNewOutTxProcessor() mockChainClient, err := getNewEvmChainClient() require.NoError(t, err) @@ -126,8 +124,7 @@ func TestSigner_SignOutboundTx(t *testing.T) { // Setup txData struct - cctx, err := getCCTX() - require.NoError(t, err) + cctx := getCCTX(t) mockChainClient, err := getNewEvmChainClient() require.NoError(t, err) txData, skip, err := NewOutBoundTransactionData(cctx, mockChainClient, evmSigner.EvmClient(), zerolog.Logger{}, 123) @@ -156,8 +153,7 @@ func TestSigner_SignRevertTx(t *testing.T) { require.NoError(t, err) // Setup txData struct - cctx, err := getCCTX() - require.NoError(t, err) + cctx := getCCTX(t) mockChainClient, err := getNewEvmChainClient() require.NoError(t, err) txData, skip, err := NewOutBoundTransactionData(cctx, mockChainClient, evmSigner.EvmClient(), zerolog.Logger{}, 123) @@ -186,8 +182,7 @@ func TestSigner_SignWithdrawTx(t *testing.T) { require.NoError(t, err) // Setup txData struct - cctx, err := getCCTX() - require.NoError(t, err) + cctx := getCCTX(t) mockChainClient, err := getNewEvmChainClient() require.NoError(t, err) txData, skip, err := NewOutBoundTransactionData(cctx, mockChainClient, evmSigner.EvmClient(), zerolog.Logger{}, 123) @@ -216,8 +211,7 @@ func TestSigner_SignCommandTx(t *testing.T) { require.NoError(t, err) // Setup txData struct - cctx, err := getCCTX() - require.NoError(t, err) + cctx := getCCTX(t) mockChainClient, err := getNewEvmChainClient() require.NoError(t, err) txData, skip, err := NewOutBoundTransactionData(cctx, mockChainClient, evmSigner.EvmClient(), zerolog.Logger{}, 123) @@ -264,8 +258,7 @@ func TestSigner_SignERC20WithdrawTx(t *testing.T) { require.NoError(t, err) // Setup txData struct - cctx, err := getCCTX() - require.NoError(t, err) + cctx := getCCTX(t) mockChainClient, err := getNewEvmChainClient() require.NoError(t, err) txData, skip, err := NewOutBoundTransactionData(cctx, mockChainClient, evmSigner.EvmClient(), zerolog.Logger{}, 123) @@ -294,8 +287,7 @@ func TestSigner_BroadcastOutTx(t *testing.T) { require.NoError(t, err) // Setup txData struct - cctx, err := getCCTX() - require.NoError(t, err) + cctx := getCCTX(t) mockChainClient, err := getNewEvmChainClient() require.NoError(t, err) txData, skip, err := NewOutBoundTransactionData(cctx, mockChainClient, evmSigner.EvmClient(), zerolog.Logger{}, 123) @@ -325,8 +317,7 @@ func TestSigner_getEVMRPC(t *testing.T) { } func TestSigner_SignerErrorMsg(t *testing.T) { - cctx, err := getCCTX() - require.NoError(t, err) + cctx := getCCTX(t) msg := SignerErrorMsg(cctx) require.Contains(t, msg, "nonce 68270 chain 56") @@ -338,8 +329,7 @@ func TestSigner_SignWhitelistERC20Cmd(t *testing.T) { require.NoError(t, err) // Setup txData struct - cctx, err := getCCTX() - require.NoError(t, err) + cctx := getCCTX(t) mockChainClient, err := getNewEvmChainClient() require.NoError(t, err) txData, skip, err := NewOutBoundTransactionData(cctx, mockChainClient, evmSigner.EvmClient(), zerolog.Logger{}, 123) diff --git a/zetaclient/evm/outbound_transaction_data_test.go b/zetaclient/evm/outbound_transaction_data_test.go index 8943091157..85c812119b 100644 --- a/zetaclient/evm/outbound_transaction_data_test.go +++ b/zetaclient/evm/outbound_transaction_data_test.go @@ -13,9 +13,7 @@ import ( func TestSigner_SetChainAndSender(t *testing.T) { // setup inputs - cctx, err := getCCTX() - require.NoError(t, err) - + cctx := getCCTX(t) txData := &OutBoundTransactionData{} logger := zerolog.Logger{} @@ -45,9 +43,7 @@ func TestSigner_SetChainAndSender(t *testing.T) { } func TestSigner_SetupGas(t *testing.T) { - cctx, err := getCCTX() - require.NoError(t, err) - + cctx := getCCTX(t) evmSigner, err := getNewEvmSigner() require.NoError(t, err) @@ -77,16 +73,14 @@ func TestSigner_NewOutBoundTransactionData(t *testing.T) { require.NoError(t, err) t.Run("NewOutBoundTransactionData success", func(t *testing.T) { - cctx, err := getCCTX() - require.NoError(t, err) + cctx := getCCTX(t) _, skip, err := NewOutBoundTransactionData(cctx, mockChainClient, evmSigner.EvmClient(), zerolog.Logger{}, 123) require.False(t, skip) require.NoError(t, err) }) t.Run("NewOutBoundTransactionData skip", func(t *testing.T) { - cctx, err := getCCTX() - require.NoError(t, err) + cctx := getCCTX(t) cctx.CctxStatus.Status = types.CctxStatus_Aborted _, skip, err := NewOutBoundTransactionData(cctx, mockChainClient, evmSigner.EvmClient(), zerolog.Logger{}, 123) require.NoError(t, err) @@ -94,7 +88,7 @@ func TestSigner_NewOutBoundTransactionData(t *testing.T) { }) t.Run("NewOutBoundTransactionData unknown chain", func(t *testing.T) { - cctx, err := getInvalidCCTX() + cctx := getInvalidCCTX(t) require.NoError(t, err) _, skip, err := NewOutBoundTransactionData(cctx, mockChainClient, evmSigner.EvmClient(), zerolog.Logger{}, 123) require.ErrorContains(t, err, "unknown chain") @@ -102,7 +96,7 @@ func TestSigner_NewOutBoundTransactionData(t *testing.T) { }) t.Run("NewOutBoundTransactionData setup gas error", func(t *testing.T) { - cctx, err := getCCTX() + cctx := getCCTX(t) require.NoError(t, err) cctx.GetCurrentOutTxParam().OutboundTxGasPrice = "invalidGasPrice" _, skip, err := NewOutBoundTransactionData(cctx, mockChainClient, evmSigner.EvmClient(), zerolog.Logger{}, 123) diff --git a/zetaclient/interfaces/interfaces.go b/zetaclient/interfaces/interfaces.go index 1d48fc149c..1c01b7e486 100644 --- a/zetaclient/interfaces/interfaces.go +++ b/zetaclient/interfaces/interfaces.go @@ -124,6 +124,7 @@ type BTCRPCClient interface { ListUnspentMinMaxAddresses(minConf int, maxConf int, addrs []btcutil.Address) ([]btcjson.ListUnspentResult, error) EstimateSmartFee(confTarget int64, mode *btcjson.EstimateSmartFeeMode) (*btcjson.EstimateSmartFeeResult, error) GetTransaction(txHash *chainhash.Hash) (*btcjson.GetTransactionResult, error) + GetRawTransaction(txHash *chainhash.Hash) (*btcutil.Tx, error) GetRawTransactionVerbose(txHash *chainhash.Hash) (*btcjson.TxRawResult, error) GetBlockCount() (int64, error) GetBlockHash(blockHeight int64) (*chainhash.Hash, error) diff --git a/zetaclient/testdata/btc/block_trimmed_8332_831071.json b/zetaclient/testdata/btc/block_trimmed_8332_831071.json new file mode 100644 index 0000000000..4a662f8263 --- /dev/null +++ b/zetaclient/testdata/btc/block_trimmed_8332_831071.json @@ -0,0 +1,108 @@ +{ + "hash": "0000000000000000000157d7c042e957e5005bdce3b10cf1219c846ff88871f6", + "confirmations": 4636, + "strippedsize": 767728, + "size": 1689876, + "weight": 3993060, + "height": 831071, + "version": 536887296, + "versionHex": "20004000", + "merkleroot": "d2e93c6392c54f1877f4b2f8821af02c551afa4c30a9eac61c4d49c6aa5ad70a", + "tx": [ + { + "hex": "", + "txid": "b350eafdbf61a9f5718410ba2851a5d350d59548608b399e4fc15f4ce593a54a", + "hash": "6b400359ca1f3e33ce049ff411535815f0ebdde0de6aa425b9a8c56b470be096", + "size": 214, + "vsize": 187, + "weight": 748, + "version": 2, + "locktime": 0, + "vin": [ + { + "coinbase": "035fae0c0445ced2652f466f756e6472792055534120506f6f6c202364726f70676f6c642f23714ef33cd5000000000000", + "sequence": 4294967295, + "witness": [ + "0000000000000000000000000000000000000000000000000000000000000000" + ] + } + ], + "vout": [ + { + "value": 6.43696349, + "n": 0, + "scriptPubKey": { + "asm": "0 35f6de260c9f3bdee47524c473a6016c0c055cb9", + "hex": "001435f6de260c9f3bdee47524c473a6016c0c055cb9", + "type": "witness_v0_keyhash" + } + }, + { + "value": 0, + "n": 1, + "scriptPubKey": { + "asm": "OP_RETURN aa21a9ed4ce95738e15b620273a0ceb3f5e243e6e50b84355c1c602783d77ad11ce54c66", + "hex": "6a24aa21a9ed4ce95738e15b620273a0ceb3f5e243e6e50b84355c1c602783d77ad11ce54c66", + "type": "nulldata" + } + } + ] + }, + { + "hex": "", + "txid": "3618e869f9e87863c0f1cc46dbbaa8b767b4a5d6d60b143c2c50af52b257e867", + "hash": "39f6a6cfe5c8d3f0066b2bf3813f2cc5986dd0a425d5df2cbf9aa298433cf1c2", + "size": 226, + "vsize": 175, + "weight": 700, + "version": 2, + "locktime": 0, + "vin": [ + { + "txid": "9b5071d68ac8f3a9fd5da76bf1880915ea51f16dfa48e4404d4fe2c5ee5fc574", + "vout": 2, + "scriptSig": { "asm": "", "hex": "" }, + "txinwitness": [ + "9e622c221101926bc0134b564419e385ad1708118442a567d51483558c9035949becc91fc84386fa2b1b11268e035e2e756c7c1e6f248c7690cfad469e0c1412" + ], + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0, + "n": 0, + "scriptPubKey": { + "asm": "OP_RETURN bf9686022c1cbe7d07d8e473b6f4e1bd91811cb60000", + "hex": "6a16bf9686022c1cbe7d07d8e473b6f4e1bd91811cb60000", + "type": "nulldata" + } + }, + { + "value": 0.0001085, + "n": 1, + "scriptPubKey": { + "asm": "0 daaae0d3de9d8fdee31661e61aea828b59be7864", + "hex": "0014daaae0d3de9d8fdee31661e61aea828b59be7864", + "type": "witness_v0_keyhash" + } + }, + { + "value": 0.0008211, + "n": 2, + "scriptPubKey": { + "asm": "1 34439061ba7dede70080b475a2fb85b8b8803c2da7c4c9e48589042180954d82", + "hex": "512034439061ba7dede70080b475a2fb85b8b8803c2da7c4c9e48589042180954d82", + "type": "witness_v1_taproot" + } + } + ] + } + ], + "time": 1708314180, + "nonce": 1304717940, + "bits": "170371b1", + "difficulty": 81725299822043.22, + "previousblockhash": "00000000000000000000fa6c7c8d518d28c265e4d93618dc11476a3bd9b2c1c2", + "nextblockhash": "0000000000000000000135e541c901f610309100dd7a078ab94969006e6e20e2" +} diff --git a/zetaclient/testdata/btc/chain_8332_intx_raw_result_847139aa65aa4a5ee896375951cbf7417cfc8a4d6f277ec11f40cd87319f04aa.json b/zetaclient/testdata/btc/chain_8332_intx_raw_result_847139aa65aa4a5ee896375951cbf7417cfc8a4d6f277ec11f40cd87319f04aa.json new file mode 100644 index 0000000000..5445d50411 --- /dev/null +++ b/zetaclient/testdata/btc/chain_8332_intx_raw_result_847139aa65aa4a5ee896375951cbf7417cfc8a4d6f277ec11f40cd87319f04aa.json @@ -0,0 +1,51 @@ +{ + "hex": "0200000000010197266596828f8e001991cc881a485eb2172a34c25172599a0bfc32389624d2c50200000000ffffffff031027000000000000160014daaae0d3de9d8fdee31661e61aea828b59be78640000000000000000166a1467ed0bcc4e1256bc2ce87d22e190d63a120114bfdf24010000000000160014d1ec69828aedc54637583e059643aee3f862cd2302473044022047ecada1e409279fe2b714db2b126714b88a67032b1bd1247e935c4b7b71ff50022055a480a97d2dbdd8cf8f7585296e62b538fc735b61d672b4565b9a1df4a8225e0121035ce366bfd01fde742562f7cc5e6ae125ec2bac862d3c3a11d2d70b1b3baa9ae000000000", + "txid": "847139aa65aa4a5ee896375951cbf7417cfc8a4d6f277ec11f40cd87319f04aa", + "hash": "79988813059cd0c82f9291299d4d6324a3da56c8a6507797c0f2ebf0a13cee56", + "size": 253, + "vsize": 172, + "weight": 685, + "version": 2, + "locktime": 0, + "vin": [ + { + "txid": "c5d224963832fc0b9a597251c2342a17b25e481a88cc9119008e8f8296652697", + "vout": 2, + "scriptSig": { "asm": "", "hex": "" }, + "txinwitness": [ + "3044022047ecada1e409279fe2b714db2b126714b88a67032b1bd1247e935c4b7b71ff50022055a480a97d2dbdd8cf8f7585296e62b538fc735b61d672b4565b9a1df4a8225e01", + "035ce366bfd01fde742562f7cc5e6ae125ec2bac862d3c3a11d2d70b1b3baa9ae0" + ], + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.0001, + "n": 0, + "scriptPubKey": { + "asm": "0 daaae0d3de9d8fdee31661e61aea828b59be7864", + "hex": "0014daaae0d3de9d8fdee31661e61aea828b59be7864", + "type": "witness_v0_keyhash" + } + }, + { + "value": 0, + "n": 1, + "scriptPubKey": { + "asm": "OP_RETURN 67ed0bcc4e1256bc2ce87d22e190d63a120114bf", + "hex": "6a1467ed0bcc4e1256bc2ce87d22e190d63a120114bf", + "type": "nulldata" + } + }, + { + "value": 0.00074975, + "n": 2, + "scriptPubKey": { + "asm": "0 d1ec69828aedc54637583e059643aee3f862cd23", + "hex": "0014d1ec69828aedc54637583e059643aee3f862cd23", + "type": "witness_v0_keyhash" + } + } + ] +} diff --git a/zetaclient/testdata/btc/chain_8332_msgtx_211568441340fd5e10b1a8dcb211a18b9e853dbdf265ebb1c728f9b52813455a.json b/zetaclient/testdata/btc/chain_8332_msgtx_211568441340fd5e10b1a8dcb211a18b9e853dbdf265ebb1c728f9b52813455a.json new file mode 100644 index 0000000000..6dcd43a643 --- /dev/null +++ b/zetaclient/testdata/btc/chain_8332_msgtx_211568441340fd5e10b1a8dcb211a18b9e853dbdf265ebb1c728f9b52813455a.json @@ -0,0 +1,25 @@ +{ + "Version": 2, + "TxIn": [ + { + "PreviousOutPoint": { + "Hash": [ + 30, 42, 54, 220, 104, 77, 18, 96, 207, 100, 83, 218, 222, 30, 81, 215, + 17, 216, 241, 23, 140, 26, 84, 197, 241, 73, 15, 53, 249, 182, 38, 134 + ], + "Index": 2 + }, + "SignatureScript": "", + "Witness": [ + "MEUCIQDJuzu34uVBPrsi8DgNfQH5TwM2uX//dXWkULGhElDYzAIgYxSUnwOnyHIBR546z56in6he+zNI5xMEso/0szFIy7QB", + "Am5WKFBuzTMkLlzrX9r+TTBmtcDxWbPAWmIe9l8XfqKG" + ], + "Sequence": 4294967293 + } + ], + "TxOut": [ + { "Value": 249140, "PkScript": "qRTc+XVrIoqedsOz7aKs1GSUk2FjrYc=" }, + { "Value": 965576774, "PkScript": "ABT2CDTvFlJTxXGxHOn6dORmkvxewQ==" } + ], + "LockTime": 0 +} diff --git a/zetaclient/testdata/btc/chain_8332_msgtx_3618e869f9e87863c0f1cc46dbbaa8b767b4a5d6d60b143c2c50af52b257e867.json b/zetaclient/testdata/btc/chain_8332_msgtx_3618e869f9e87863c0f1cc46dbbaa8b767b4a5d6d60b143c2c50af52b257e867.json new file mode 100644 index 0000000000..15152341cd --- /dev/null +++ b/zetaclient/testdata/btc/chain_8332_msgtx_3618e869f9e87863c0f1cc46dbbaa8b767b4a5d6d60b143c2c50af52b257e867.json @@ -0,0 +1,29 @@ +{ + "Version": 2, + "TxIn": [ + { + "PreviousOutPoint": { + "Hash": [ + 116, 197, 95, 238, 197, 226, 79, 77, 64, 228, 72, 250, 109, 241, 81, + 234, 21, 9, 136, 241, 107, 167, 93, 253, 169, 243, 200, 138, 214, 113, + 80, 155 + ], + "Index": 2 + }, + "SignatureScript": "", + "Witness": [ + "nmIsIhEBkmvAE0tWRBnjha0XCBGEQqVn1RSDVYyQNZSb7MkfyEOG+isbESaOA14udWx8Hm8kjHaQz61GngwUEg==" + ], + "Sequence": 4294967295 + } + ], + "TxOut": [ + { "Value": 0, "PkScript": "aha/loYCLBy+fQfY5HO29OG9kYEctgAA" }, + { "Value": 10850, "PkScript": "ABTaquDT3p2P3uMWYeYa6oKLWb54ZA==" }, + { + "Value": 82110, + "PkScript": "USA0Q5Bhun3t5wCAtHWi+4W4uIA8LafEyeSFiQQhgJVNgg==" + } + ], + "LockTime": 0 +} diff --git a/zetaclient/testdata/btc/chain_8332_msgtx_781fc8d41b476dbceca283ebff9573fda52c8fdbba5e78152aeb4432286836a7.json b/zetaclient/testdata/btc/chain_8332_msgtx_781fc8d41b476dbceca283ebff9573fda52c8fdbba5e78152aeb4432286836a7.json new file mode 100644 index 0000000000..13789f13b1 --- /dev/null +++ b/zetaclient/testdata/btc/chain_8332_msgtx_781fc8d41b476dbceca283ebff9573fda52c8fdbba5e78152aeb4432286836a7.json @@ -0,0 +1,23 @@ +{ + "Version": 1, + "TxIn": [ + { + "PreviousOutPoint": { + "Hash": [ + 88, 35, 68, 215, 98, 226, 80, 135, 144, 112, 176, 41, 10, 204, 196, + 243, 171, 206, 47, 130, 190, 82, 88, 31, 234, 63, 168, 65, 219, 15, + 248, 81 + ], + "Index": 1 + }, + "SignatureScript": "SDBFAiEAtm10q/gt84o5QCkWsxouad5aqiEDgBd3h3ci/8tuDl4CIB9wKaSz2HDRhPKwkoXBbYqZwTD+8fhiZOolJcwvgV49ASED25w7Nl3+PHENu2OrUD2EmDvsdylmoWe0hSDOMtQvj04=", + "Witness": null, + "Sequence": 268435456 + } + ], + "TxOut": [ + { "Value": 1425000, "PkScript": "qRRdKyXqq2FtT8BTLay8+A2Un6cUTIc=" }, + { "Value": 19064706, "PkScript": "dqkUk2fpBSIxBUekNfA/gxxYw1RZ3KGIrA==" } + ], + "LockTime": 0 +} diff --git a/zetaclient/testdata/btc/chain_8332_msgtx_c5d224963832fc0b9a597251c2342a17b25e481a88cc9119008e8f8296652697.json b/zetaclient/testdata/btc/chain_8332_msgtx_c5d224963832fc0b9a597251c2342a17b25e481a88cc9119008e8f8296652697.json new file mode 100644 index 0000000000..1d0004422e --- /dev/null +++ b/zetaclient/testdata/btc/chain_8332_msgtx_c5d224963832fc0b9a597251c2342a17b25e481a88cc9119008e8f8296652697.json @@ -0,0 +1,42 @@ +{ + "Version": 2, + "TxIn": [ + { + "PreviousOutPoint": { + "Hash": [ + 127, 3, 175, 215, 135, 178, 52, 169, 234, 18, 205, 65, 18, 193, 182, + 123, 2, 102, 254, 107, 221, 18, 243, 92, 98, 226, 161, 209, 185, 140, + 72, 91 + ], + "Index": 2 + }, + "SignatureScript": "", + "Witness": [ + "MEUCIQCWzKD96dT53Y9o+KCtLvB5WXrJL4X2XGgpiz3rRBdJMwIgcdwc7Z870dOeiVUZdSLjRAJ8hkLz0l7TXVSLF0ZArekB", + "A1zjZr/QH950JWL3zF5q4SXsK6yGLTw6EdLXCxs7qprg" + ], + "Sequence": 4294967295 + }, + { + "PreviousOutPoint": { + "Hash": [ + 175, 165, 125, 14, 98, 13, 62, 204, 88, 160, 18, 74, 177, 75, 159, 44, + 131, 4, 151, 145, 200, 2, 177, 50, 227, 26, 2, 89, 184, 155, 179, 54 + ], + "Index": 2 + }, + "SignatureScript": "", + "Witness": [ + "MEQCIGbWmCHGFlrSKF7+tnQHhrQGDNS0PR2E4lxq/OLz68CFAiB/yHWTSaAs1GbDzngvzFkGu76N/YKJX1iy88irfDg0tAE=", + "A1zjZr/QH950JWL3zF5q4SXsK6yGLTw6EdLXCxs7qprg" + ], + "Sequence": 4294967295 + } + ], + "TxOut": [ + { "Value": 1000, "PkScript": "ABTaquDT3p2P3uMWYeYa6oKLWb54ZA==" }, + { "Value": 0, "PkScript": "ahRn7QvMThJWvCzofSLhkNY6EgEUvw==" }, + { "Value": 89858, "PkScript": "ABTR7GmCiu3FRjdYPgWWQ67j+GLNIw==" } + ], + "LockTime": 0 +} diff --git a/zetaclient/testdata/btc/chain_8332_msgtx_d13de30b0cc53b5c4702b184ae0a0b0f318feaea283185c1cddb8b341c27c016.json b/zetaclient/testdata/btc/chain_8332_msgtx_d13de30b0cc53b5c4702b184ae0a0b0f318feaea283185c1cddb8b341c27c016.json new file mode 100644 index 0000000000..e7343425a4 --- /dev/null +++ b/zetaclient/testdata/btc/chain_8332_msgtx_d13de30b0cc53b5c4702b184ae0a0b0f318feaea283185c1cddb8b341c27c016.json @@ -0,0 +1,28 @@ +{ + "Version": 2, + "TxIn": [ + { + "PreviousOutPoint": { + "Hash": [ + 121, 61, 82, 212, 158, 94, 66, 22, 92, 111, 162, 24, 255, 33, 79, 208, + 234, 255, 133, 0, 255, 110, 97, 104, 105, 49, 187, 30, 61, 168, 9, 166 + ], + "Index": 1 + }, + "SignatureScript": "", + "Witness": [ + "MEQCIDeXH61fcBuKz091+3kS2ZETW4hjkbfDIxRcnVU+TrIZAiBBg9boZh2AcAa5HIXpKS5YSfDLsJz817yDC5lPEnzYLAE=", + "AxAyc+pMvZfG+8ELyKyr2TBo0kulM0XM4QisuLyR7L3f" + ], + "Sequence": 4294967295 + } + ], + "TxOut": [ + { + "Value": 50000000, + "PkScript": "ACDxbbwTHn6bqd/LCK85n+IPtDC9eQhvG4HDv3AE4JX7jA==" + }, + { "Value": 9992996, "PkScript": "ABT8w6bf+cf2lmZLrUWPhgVJeNjEVw==" } + ], + "LockTime": 0 +} diff --git a/zetaclient/testdata/btc/chain_8332_tx_raw_result_P2PKH_9c741de6e17382b7a9113fc811e3558981a35a360e3d1262a6675892c91322ca.json b/zetaclient/testdata/btc/chain_8332_tx_raw_result_P2PKH_9c741de6e17382b7a9113fc811e3558981a35a360e3d1262a6675892c91322ca.json new file mode 100644 index 0000000000..2aa2635469 --- /dev/null +++ b/zetaclient/testdata/btc/chain_8332_tx_raw_result_P2PKH_9c741de6e17382b7a9113fc811e3558981a35a360e3d1262a6675892c91322ca.json @@ -0,0 +1,42 @@ +{ + "hex": "01000000000101f163b41b5a56ac3dd741d8695d964c50eb4acda02d08b9e0f9827a4c5d2accad0100000000ffffffff0220651100000000001976a914a386e1676ff60f1a0d1645e1c73d06ab7872467488acb778880000000000160014e7f663e64af624f3654f2d23812c6ac9b13dc00c02473044022071bdf8aeb210418daacf0cb5e4185ed7629a2db94be13746985a356bdcc067ac02203a7dc27af4959e03a5d8ee3100170aafc5710e5cdaaf5d2991a509584eb0a7040121020476f998fae688c3f412755b3b850139995781835f66a62368794f938669350600000000", + "txid": "9c741de6e17382b7a9113fc811e3558981a35a360e3d1262a6675892c91322ca", + "hash": "3de8e19acb8e05fc19aa6196ef4b10d303a107b22a035d97808867aacaea335d", + "size": 225, + "vsize": 144, + "weight": 573, + "version": 1, + "locktime": 0, + "vin": [ + { + "txid": "adcc2a5d4c7a82f9e0b9082da0cd4aeb504c965d69d841d73dac565a1bb463f1", + "vout": 1, + "scriptSig": { "asm": "", "hex": "" }, + "txinwitness": [ + "3044022071bdf8aeb210418daacf0cb5e4185ed7629a2db94be13746985a356bdcc067ac02203a7dc27af4959e03a5d8ee3100170aafc5710e5cdaaf5d2991a509584eb0a70401", + "020476f998fae688c3f412755b3b850139995781835f66a62368794f9386693506" + ], + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.0114, + "n": 0, + "scriptPubKey": { + "asm": "OP_DUP OP_HASH160 a386e1676ff60f1a0d1645e1c73d06ab78724674 OP_EQUALVERIFY OP_CHECKSIG", + "hex": "76a914a386e1676ff60f1a0d1645e1c73d06ab7872467488ac", + "type": "pubkeyhash" + } + }, + { + "value": 0.08943799, + "n": 1, + "scriptPubKey": { + "asm": "0 e7f663e64af624f3654f2d23812c6ac9b13dc00c", + "hex": "0014e7f663e64af624f3654f2d23812c6ac9b13dc00c", + "type": "witness_v0_keyhash" + } + } + ] +} diff --git a/zetaclient/testdata/btc/chain_8332_tx_raw_result_P2SH_fd68c8b4478686ca6f5ae4c28eaab055490650dbdaa6c2c8e380a7e075958a21.json b/zetaclient/testdata/btc/chain_8332_tx_raw_result_P2SH_fd68c8b4478686ca6f5ae4c28eaab055490650dbdaa6c2c8e380a7e075958a21.json new file mode 100644 index 0000000000..a388ad429c --- /dev/null +++ b/zetaclient/testdata/btc/chain_8332_tx_raw_result_P2SH_fd68c8b4478686ca6f5ae4c28eaab055490650dbdaa6c2c8e380a7e075958a21.json @@ -0,0 +1,42 @@ +{ + "hex": "020000000001019481d17b8fb50d38fa5f726b858c2300567620447c93b4d77582ebd8ee286ec70000000000000000800269510f000000000017a91404b8d73fbfeccaea8c253279811e60c5e1d4a9a887ef03160000000000160014811535e6ed76ba320d54bb8f418fcb53d527e2c40247304402200e757ee5c1400ae06a47a558e11540ea7acc75ed366b2acc3301e4a17ebeccdc02204e00c03de16b6c14850cd556c503f1258da7d9f25886b4bc4d4510d182e1ec77012102f9edaa905fbafdacddbbae0fb3cefeb092c5a0dce8a7aeaeb7bd17cf6fa116c900000000", + "txid": "fd68c8b4478686ca6f5ae4c28eaab055490650dbdaa6c2c8e380a7e075958a21", + "hash": "6e3db4ed0fcaad7daff5166615b5c432ba151fb885a603cbb33645fe6f360a86", + "size": 223, + "vsize": 142, + "weight": 565, + "version": 2, + "locktime": 0, + "vin": [ + { + "txid": "c76e28eed8eb8275d7b4937c4420765600238c856b725ffa380db58f7bd18194", + "vout": 0, + "scriptSig": { "asm": "", "hex": "" }, + "txinwitness": [ + "304402200e757ee5c1400ae06a47a558e11540ea7acc75ed366b2acc3301e4a17ebeccdc02204e00c03de16b6c14850cd556c503f1258da7d9f25886b4bc4d4510d182e1ec7701", + "02f9edaa905fbafdacddbbae0fb3cefeb092c5a0dce8a7aeaeb7bd17cf6fa116c9" + ], + "sequence": 2147483648 + } + ], + "vout": [ + { + "value": 0.01003881, + "n": 0, + "scriptPubKey": { + "asm": "OP_HASH160 04b8d73fbfeccaea8c253279811e60c5e1d4a9a8 OP_EQUAL", + "hex": "a91404b8d73fbfeccaea8c253279811e60c5e1d4a9a887", + "type": "scripthash" + } + }, + { + "value": 0.01442799, + "n": 1, + "scriptPubKey": { + "asm": "0 811535e6ed76ba320d54bb8f418fcb53d527e2c4", + "hex": "0014811535e6ed76ba320d54bb8f418fcb53d527e2c4", + "type": "witness_v0_keyhash" + } + } + ] +} diff --git a/zetaclient/testdata/btc/chain_8332_tx_raw_result_P2TR_259fc21e63e138136c8f19270a0f7ca10039a66a474f91d23a17896f46e677a7.json b/zetaclient/testdata/btc/chain_8332_tx_raw_result_P2TR_259fc21e63e138136c8f19270a0f7ca10039a66a474f91d23a17896f46e677a7.json new file mode 100644 index 0000000000..2de51e3356 --- /dev/null +++ b/zetaclient/testdata/btc/chain_8332_tx_raw_result_P2TR_259fc21e63e138136c8f19270a0f7ca10039a66a474f91d23a17896f46e677a7.json @@ -0,0 +1,42 @@ +{ + "hex": "02000000000101ab6912b3e805bb584ed67d71aea4f5504f18c279d77409001b0fe307277e23020100000000ffffffff02c8af000000000000225120ac30d6fed6e38b53ea9e2f78b37366dd99c0df7408ab2627c9d05b7a3c2ae0816efd00000000000016001433d1c36b26902298ac55fc176653854fc2f9bc6e02473044022072fee83302148a971a5e1c24e063d518665dfe76c1791f20da5ec8df4a352fdb02204f1a2ba4068b86fe3c1ca2ec77a5c45f0dcb3af8d9dbc2d38091afae6011d83f0121038bdc9021a5d81cbfc28b5b41d2465ca891ba233e9d6ca72cfef654a1ef37749200000000", + "txid": "259fc21e63e138136c8f19270a0f7ca10039a66a474f91d23a17896f46e677a7", + "hash": "253639775c305a4c28707be417bbb64ce3fa1b33ce582a60f95262f0e847d553", + "size": 234, + "vsize": 153, + "weight": 609, + "version": 2, + "locktime": 0, + "vin": [ + { + "txid": "02237e2707e30f1b000974d779c2184f50f5a4ae717dd64e58bb05e8b31269ab", + "vout": 1, + "scriptSig": { "asm": "", "hex": "" }, + "txinwitness": [ + "3044022072fee83302148a971a5e1c24e063d518665dfe76c1791f20da5ec8df4a352fdb02204f1a2ba4068b86fe3c1ca2ec77a5c45f0dcb3af8d9dbc2d38091afae6011d83f01", + "038bdc9021a5d81cbfc28b5b41d2465ca891ba233e9d6ca72cfef654a1ef377492" + ], + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.00045, + "n": 0, + "scriptPubKey": { + "asm": "1 ac30d6fed6e38b53ea9e2f78b37366dd99c0df7408ab2627c9d05b7a3c2ae081", + "hex": "5120ac30d6fed6e38b53ea9e2f78b37366dd99c0df7408ab2627c9d05b7a3c2ae081", + "type": "witness_v1_taproot" + } + }, + { + "value": 0.00064878, + "n": 1, + "scriptPubKey": { + "asm": "0 33d1c36b26902298ac55fc176653854fc2f9bc6e", + "hex": "001433d1c36b26902298ac55fc176653854fc2f9bc6e", + "type": "witness_v0_keyhash" + } + } + ] +} diff --git a/zetaclient/testdata/btc/chain_8332_tx_raw_result_P2WPKH_5d09d232bfe41c7cb831bf53fc2e4029ab33a99087fd5328a2331b52ff2ebe5b.json b/zetaclient/testdata/btc/chain_8332_tx_raw_result_P2WPKH_5d09d232bfe41c7cb831bf53fc2e4029ab33a99087fd5328a2331b52ff2ebe5b.json new file mode 100644 index 0000000000..4f68b6653c --- /dev/null +++ b/zetaclient/testdata/btc/chain_8332_tx_raw_result_P2WPKH_5d09d232bfe41c7cb831bf53fc2e4029ab33a99087fd5328a2331b52ff2ebe5b.json @@ -0,0 +1,42 @@ +{ + "hex": "0100000000010147073577cf72e6227bb0bfbcc4ada7ecb28323c32c5c3b7c415b2b6e873022e37600000000fdffffff024238010000000000160014e99275308221c877b1ef7e7cbd55015e67e475d9bee10e0000000000225120ce403c2e1563b1e05eb213b4f601c2adf26293b86532146aab241c51a742eacf024730440220126e8c6a5fb11a1c0cbafc348601d427e9126d17c7136cb99fa6e6e61259066b02206fae29f384575cfed54946794b8784bf0f68b4ea5ed6caa5a12bb7d18a226de30121031f88031b5aa6e540ac109e4b6875b1c2826f84a3a4b23f4520d65ed54947b13b00000000", + "txid": "5d09d232bfe41c7cb831bf53fc2e4029ab33a99087fd5328a2331b52ff2ebe5b", + "hash": "197248f8cae9b569f871f728b8163f1f322c145095b4df6425578e752928873e", + "size": 234, + "vsize": 153, + "weight": 609, + "version": 1, + "locktime": 0, + "vin": [ + { + "txid": "e32230876e2b5b417c3b5c2cc32383b2eca7adc4bcbfb07b22e672cf77350747", + "vout": 118, + "scriptSig": { "asm": "", "hex": "" }, + "txinwitness": [ + "30440220126e8c6a5fb11a1c0cbafc348601d427e9126d17c7136cb99fa6e6e61259066b02206fae29f384575cfed54946794b8784bf0f68b4ea5ed6caa5a12bb7d18a226de301", + "031f88031b5aa6e540ac109e4b6875b1c2826f84a3a4b23f4520d65ed54947b13b" + ], + "sequence": 4294967293 + } + ], + "vout": [ + { + "value": 0.00079938, + "n": 0, + "scriptPubKey": { + "asm": "0 e99275308221c877b1ef7e7cbd55015e67e475d9", + "hex": "0014e99275308221c877b1ef7e7cbd55015e67e475d9", + "type": "witness_v0_keyhash" + } + }, + { + "value": 0.00975294, + "n": 1, + "scriptPubKey": { + "asm": "1 ce403c2e1563b1e05eb213b4f601c2adf26293b86532146aab241c51a742eacf", + "hex": "5120ce403c2e1563b1e05eb213b4f601c2adf26293b86532146aab241c51a742eacf", + "type": "witness_v1_taproot" + } + } + ] +} diff --git a/zetaclient/testdata/btc/chain_8332_tx_raw_result_P2WSH_791bb9d16f7ab05f70a116d18eaf3552faf77b9d5688699a480261424b4f7e53.json b/zetaclient/testdata/btc/chain_8332_tx_raw_result_P2WSH_791bb9d16f7ab05f70a116d18eaf3552faf77b9d5688699a480261424b4f7e53.json new file mode 100644 index 0000000000..adc78f14c8 --- /dev/null +++ b/zetaclient/testdata/btc/chain_8332_tx_raw_result_P2WSH_791bb9d16f7ab05f70a116d18eaf3552faf77b9d5688699a480261424b4f7e53.json @@ -0,0 +1,73 @@ +{ + "hex": "0200000000010583fd136d03a9483695a252222fb2d0cb73ba8c76793ad5f876c375f58aa300920600000000ffffffff5a0e44799e3256044c7f4a89ed2c0d1e1e4341c8d9d41b08b96d2b0592d1d7c00e00000000ffffffff48245fa209bbdcbe9009a5a38eb871972ae1dcf6ffe665095fe4f142682401690c00000000ffffffff6b288db05995de2a600225624e9e3e9f59eadb8f3a9654de144d953df62a12831300000000ffffffff5354734d187ee87102beea3c59b707337281e269e869657936ef271cb40a01b90000000000ffffffff0193d12d02000000002200200334174ebe7b38f5c20d4dfb5136c72b2383e2f4fc26421e94b16f81e191cb3202473044022100c532ad6675bdd13b3c8dd3202d6d576bfb9db739f2b8f3895e5358c075c1273f021f4b201148c88cc8208f32151490e3b236c473553ffd6fe3e7a4d14fe3291b1f012103a81f3fe200eb9637c2f553ce1daad935d1cd62c3f2a0bd030817ee812395332a02483045022100d374b7f988b8aeaf8a971ff6358373e3aa015e5ab9c8a325bfa837c36ffef6360220615ce57849e5499592161f404493b6db197021e280c34d0cf30fd825d3bbddae012103a81f3fe200eb9637c2f553ce1daad935d1cd62c3f2a0bd030817ee812395332a02473044022046cc9dc40d51a6978edcb15b6b404618691f3ff0255f1afa431a973d7246c8c3022027e64ec89d09862ef8485f54352728344ca871b4f5bc536f019db07023f8d3bf012103a81f3fe200eb9637c2f553ce1daad935d1cd62c3f2a0bd030817ee812395332a02473044022037dc56be5a40c2f7f13da618aa56745c8ef6f60ae70e926bf6af55b504179dd902200227ab4ba869dd658f7956161c0d3d5df2fa6255ea064839588d9a42636b15c6012103a81f3fe200eb9637c2f553ce1daad935d1cd62c3f2a0bd030817ee812395332a02473044022033c0b3daac006e07e7537e7f821ad60ae363a1c7cf01ae4dcd1cba1f1409cadd02200844428654b8ed4f6e6e3cab7d7613666eaf667f8d8b1e0e7c7ac921838137a3012103a81f3fe200eb9637c2f553ce1daad935d1cd62c3f2a0bd030817ee812395332a00000000", + "txid": "791bb9d16f7ab05f70a116d18eaf3552faf77b9d5688699a480261424b4f7e53", + "hash": "f9610c875ed2084ea51e8c87077f4eb2d6bfc9c40a6a4ab011046b59a1a35a04", + "size": 796, + "vsize": 393, + "weight": 1570, + "version": 2, + "locktime": 0, + "vin": [ + { + "txid": "9200a38af575c376f8d53a79768cba73cbd0b22f2252a2953648a9036d13fd83", + "vout": 6, + "scriptSig": { "asm": "", "hex": "" }, + "txinwitness": [ + "3044022100c532ad6675bdd13b3c8dd3202d6d576bfb9db739f2b8f3895e5358c075c1273f021f4b201148c88cc8208f32151490e3b236c473553ffd6fe3e7a4d14fe3291b1f01", + "03a81f3fe200eb9637c2f553ce1daad935d1cd62c3f2a0bd030817ee812395332a" + ], + "sequence": 4294967295 + }, + { + "txid": "c0d7d192052b6db9081bd4d9c841431e1e0d2ced894a7f4c0456329e79440e5a", + "vout": 14, + "scriptSig": { "asm": "", "hex": "" }, + "txinwitness": [ + "3045022100d374b7f988b8aeaf8a971ff6358373e3aa015e5ab9c8a325bfa837c36ffef6360220615ce57849e5499592161f404493b6db197021e280c34d0cf30fd825d3bbddae01", + "03a81f3fe200eb9637c2f553ce1daad935d1cd62c3f2a0bd030817ee812395332a" + ], + "sequence": 4294967295 + }, + { + "txid": "6901246842f1e45f0965e6fff6dce12a9771b88ea3a50990bedcbb09a25f2448", + "vout": 12, + "scriptSig": { "asm": "", "hex": "" }, + "txinwitness": [ + "3044022046cc9dc40d51a6978edcb15b6b404618691f3ff0255f1afa431a973d7246c8c3022027e64ec89d09862ef8485f54352728344ca871b4f5bc536f019db07023f8d3bf01", + "03a81f3fe200eb9637c2f553ce1daad935d1cd62c3f2a0bd030817ee812395332a" + ], + "sequence": 4294967295 + }, + { + "txid": "83122af63d954d14de54963a8fdbea599f3e9e4e622502602ade9559b08d286b", + "vout": 19, + "scriptSig": { "asm": "", "hex": "" }, + "txinwitness": [ + "3044022037dc56be5a40c2f7f13da618aa56745c8ef6f60ae70e926bf6af55b504179dd902200227ab4ba869dd658f7956161c0d3d5df2fa6255ea064839588d9a42636b15c601", + "03a81f3fe200eb9637c2f553ce1daad935d1cd62c3f2a0bd030817ee812395332a" + ], + "sequence": 4294967295 + }, + { + "txid": "b9010ab41c27ef36796569e869e281723307b7593ceabe0271e87e184d735453", + "vout": 0, + "scriptSig": { "asm": "", "hex": "" }, + "txinwitness": [ + "3044022033c0b3daac006e07e7537e7f821ad60ae363a1c7cf01ae4dcd1cba1f1409cadd02200844428654b8ed4f6e6e3cab7d7613666eaf667f8d8b1e0e7c7ac921838137a301", + "03a81f3fe200eb9637c2f553ce1daad935d1cd62c3f2a0bd030817ee812395332a" + ], + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.36557203, + "n": 0, + "scriptPubKey": { + "asm": "0 0334174ebe7b38f5c20d4dfb5136c72b2383e2f4fc26421e94b16f81e191cb32", + "hex": "00200334174ebe7b38f5c20d4dfb5136c72b2383e2f4fc26421e94b16f81e191cb32", + "type": "witness_v0_scripthash" + } + } + ] +} diff --git a/zetaclient/testutils/mempool_client.go b/zetaclient/testutils/mempool_client.go index 09b48b8a53..03c79120dc 100644 --- a/zetaclient/testutils/mempool_client.go +++ b/zetaclient/testutils/mempool_client.go @@ -9,8 +9,10 @@ import ( ) const ( - APIURLBlocks = "https://mempool.space/api/v1/blocks" - APIUrlBlocksTestnet = "https://mempool.space/testnet/api/v1/blocks" + APIURLBlocks = "https://mempool.space/api/v1/blocks" + APIURLBlockTxs = "https://mempool.space/api/block/%s/txs" + APIURLBlocksTestnet = "https://mempool.space/testnet/api/v1/blocks" + APIURLBlockTxsTestnet = "https://mempool.space/testnet/api/block/%s/txs" ) type MempoolBlock struct { @@ -30,6 +32,39 @@ type MempoolBlock struct { Extras BlockExtra `json:"extras"` } +type Vin struct { + TxID string `json:"txid"` + Vout uint32 `json:"vout"` + Prevout struct { + Scriptpubkey string `json:"scriptpubkey"` + ScriptpubkeyAsm string `json:"scriptpubkey_asm"` + ScriptpubkeyType string `json:"scriptpubkey_type"` + ScriptpubkeyAddress string `json:"scriptpubkey_address"` + Value int64 `json:"value"` + } `json:"prevout"` + Scriptsig string `json:"scriptsig"` + IsCoinbase bool `json:"is_coinbase"` + Sequence uint32 `json:"sequence"` +} + +type Vout struct { + Scriptpubkey string `json:"scriptpubkey"` + ScriptpubkeyAsm string `json:"scriptpubkey_asm"` + ScriptpubkeyType string `json:"scriptpubkey_type"` + Value int64 `json:"value"` +} + +type MempoolTx struct { + TxID string `json:"txid"` + Version int `json:"version"` + LockTime int `json:"locktime"` + Vin []Vin `json:"vin"` + Vout []Vout `json:"vout"` + Size int `json:"size"` + Weight int `json:"weight"` + Fee int `json:"fee"` +} + type BlockExtra struct { TotalFees int `json:"totalFees"` MedianFee float64 `json:"medianFee"` @@ -91,10 +126,11 @@ func Get(ctx context.Context, path string, v interface{}) error { return json.NewDecoder(r.Body).Decode(v) } +// GetBlocks returns return 15 mempool.space blocks [n-14, n] per request func GetBlocks(ctx context.Context, n int, testnet bool) ([]MempoolBlock, error) { path := fmt.Sprintf("%s/%d", APIURLBlocks, n) if testnet { - path = fmt.Sprintf("%s/%d", APIUrlBlocksTestnet, n) + path = fmt.Sprintf("%s/%d", APIURLBlocksTestnet, n) } blocks := make([]MempoolBlock, 0) if err := Get(ctx, path, &blocks); err != nil { @@ -102,3 +138,16 @@ func GetBlocks(ctx context.Context, n int, testnet bool) ([]MempoolBlock, error) } return blocks, nil } + +// GetBlockTxs a list of transactions in the block (up to 25 transactions beginning at index 0) +func GetBlockTxs(ctx context.Context, blockHash string, testnet bool) ([]MempoolTx, error) { + path := fmt.Sprintf(APIURLBlockTxs, blockHash) + if testnet { + path = fmt.Sprintf(APIURLBlockTxsTestnet, blockHash) + } + txs := make([]MempoolTx, 0) + if err := Get(ctx, path, &txs); err != nil { + return nil, err + } + return txs, nil +} diff --git a/zetaclient/testutils/stub/btc_rpc.go b/zetaclient/testutils/stub/btc_rpc.go new file mode 100644 index 0000000000..6b8ac84c53 --- /dev/null +++ b/zetaclient/testutils/stub/btc_rpc.go @@ -0,0 +1,121 @@ +package stub + +import ( + "errors" + + "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/rpcclient" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" + "github.com/zeta-chain/zetacore/zetaclient/interfaces" +) + +// EvmClient interface +var _ interfaces.BTCRPCClient = &MockBTCRPCClient{} + +// MockBTCRPCClient is a mock implementation of the BTCRPCClient interface +type MockBTCRPCClient struct { + Txs []*btcutil.Tx +} + +// NewMockBTCRPCClient creates a new mock BTC RPC client +func NewMockBTCRPCClient() *MockBTCRPCClient { + client := &MockBTCRPCClient{} + return client.Reset() +} + +// Reset clears the mock data +func (c *MockBTCRPCClient) Reset() *MockBTCRPCClient { + c.Txs = []*btcutil.Tx{} + return c +} + +func (c *MockBTCRPCClient) GetNetworkInfo() (*btcjson.GetNetworkInfoResult, error) { + return nil, errors.New("not implemented") +} + +func (c *MockBTCRPCClient) CreateWallet(_ string, _ ...rpcclient.CreateWalletOpt) (*btcjson.CreateWalletResult, error) { + return nil, errors.New("not implemented") +} + +func (c *MockBTCRPCClient) GetNewAddress(_ string) (btcutil.Address, error) { + return nil, errors.New("not implemented") +} + +func (c *MockBTCRPCClient) GenerateToAddress(_ int64, _ btcutil.Address, _ *int64) ([]*chainhash.Hash, error) { + return nil, errors.New("not implemented") +} + +func (c *MockBTCRPCClient) GetBalance(_ string) (btcutil.Amount, error) { + return 0, errors.New("not implemented") +} + +func (c *MockBTCRPCClient) SendRawTransaction(_ *wire.MsgTx, _ bool) (*chainhash.Hash, error) { + return nil, errors.New("not implemented") +} + +func (c *MockBTCRPCClient) ListUnspent() ([]btcjson.ListUnspentResult, error) { + return nil, errors.New("not implemented") +} + +func (c *MockBTCRPCClient) ListUnspentMinMaxAddresses(_ int, _ int, _ []btcutil.Address) ([]btcjson.ListUnspentResult, error) { + return nil, errors.New("not implemented") +} + +func (c *MockBTCRPCClient) EstimateSmartFee(_ int64, _ *btcjson.EstimateSmartFeeMode) (*btcjson.EstimateSmartFeeResult, error) { + return nil, errors.New("not implemented") +} + +func (c *MockBTCRPCClient) GetTransaction(_ *chainhash.Hash) (*btcjson.GetTransactionResult, error) { + return nil, errors.New("not implemented") +} + +// GetRawTransaction returns a pre-loaded transaction or nil +func (c *MockBTCRPCClient) GetRawTransaction(_ *chainhash.Hash) (*btcutil.Tx, error) { + // pop a transaction from the list + if len(c.Txs) > 0 { + tx := c.Txs[len(c.Txs)-1] + c.Txs = c.Txs[:len(c.Txs)-1] + return tx, nil + } + return nil, errors.New("no transaction found") +} + +func (c *MockBTCRPCClient) GetRawTransactionVerbose(_ *chainhash.Hash) (*btcjson.TxRawResult, error) { + return nil, errors.New("not implemented") +} + +func (c *MockBTCRPCClient) GetBlockCount() (int64, error) { + return 0, errors.New("not implemented") +} + +func (c *MockBTCRPCClient) GetBlockHash(_ int64) (*chainhash.Hash, error) { + return nil, errors.New("not implemented") +} + +func (c *MockBTCRPCClient) GetBlockVerbose(_ *chainhash.Hash) (*btcjson.GetBlockVerboseResult, error) { + return nil, errors.New("not implemented") +} + +func (c *MockBTCRPCClient) GetBlockVerboseTx(_ *chainhash.Hash) (*btcjson.GetBlockVerboseTxResult, error) { + return nil, errors.New("not implemented") +} + +func (c *MockBTCRPCClient) GetBlockHeader(_ *chainhash.Hash) (*wire.BlockHeader, error) { + return nil, errors.New("not implemented") +} + +// ---------------------------------------------------------------------------- +// Feed data to the mock BTC RPC client for testing +// ---------------------------------------------------------------------------- + +func (c *MockBTCRPCClient) WithRawTransaction(tx *btcutil.Tx) *MockBTCRPCClient { + c.Txs = append(c.Txs, tx) + return c +} + +func (c *MockBTCRPCClient) WithRawTransactions(txs []*btcutil.Tx) *MockBTCRPCClient { + c.Txs = append(c.Txs, txs...) + return c +} diff --git a/zetaclient/testutils/stub/tss_signer.go b/zetaclient/testutils/stub/tss_signer.go index 5270618df0..0f6d9787e6 100644 --- a/zetaclient/testutils/stub/tss_signer.go +++ b/zetaclient/testutils/stub/tss_signer.go @@ -26,23 +26,25 @@ func init() { // TSS is a mock of TSS signer for testing type TSS struct { + chain chains.Chain evmAddress string btcAddress string } -func NewMockTSS(evmAddress string, btcAddress string) *TSS { +func NewMockTSS(chain chains.Chain, evmAddress string, btcAddress string) *TSS { return &TSS{ + chain: chain, evmAddress: evmAddress, btcAddress: btcAddress, } } func NewTSSMainnet() *TSS { - return NewMockTSS(testutils.TSSAddressEVMMainnet, testutils.TSSAddressBTCMainnet) + return NewMockTSS(chains.BtcMainnetChain(), testutils.TSSAddressEVMMainnet, testutils.TSSAddressBTCMainnet) } func NewTSSAthens3() *TSS { - return NewMockTSS(testutils.TSSAddressEVMAthens3, testutils.TSSAddressBTCAthens3) + return NewMockTSS(chains.BscTestnetChain(), testutils.TSSAddressEVMAthens3, testutils.TSSAddressBTCAthens3) } // Sign uses test key unrelated to any tss key in production @@ -76,7 +78,16 @@ func (s *TSS) BTCAddress() string { } func (s *TSS) BTCAddressWitnessPubkeyHash() *btcutil.AddressWitnessPubKeyHash { - return nil + net, err := chains.GetBTCChainParams(s.chain.ChainId) + if err != nil { + panic(err) + } + tssAddress := s.BTCAddress() + addr, err := btcutil.DecodeAddress(tssAddress, net) + if err != nil { + return nil + } + return addr.(*btcutil.AddressWitnessPubKeyHash) } func (s *TSS) PubKeyCompressedBytes() []byte { diff --git a/zetaclient/testutils/testdata.go b/zetaclient/testutils/testdata.go index c8d5788d8d..37b6cbab14 100644 --- a/zetaclient/testutils/testdata.go +++ b/zetaclient/testutils/testdata.go @@ -38,16 +38,15 @@ func SaveObjectToJSONFile(obj interface{}, filename string) error { } // LoadObjectFromJSONFile loads an object from a file in JSON format -func LoadObjectFromJSONFile(obj interface{}, filename string) error { +func LoadObjectFromJSONFile(t *testing.T, obj interface{}, filename string) { file, err := os.Open(filepath.Clean(filename)) - if err != nil { - return err - } + require.NoError(t, err) defer file.Close() // read the struct from the file decoder := json.NewDecoder(file) - return decoder.Decode(&obj) + err = decoder.Decode(&obj) + require.NoError(t, err) } func ComplianceConfigTest() config.ComplianceConfig { @@ -81,23 +80,28 @@ func SaveBTCBlockTrimTx(blockVb *btcjson.GetBlockVerboseTxResult, filename strin func LoadEVMBlock(t *testing.T, chainID int64, blockNumber uint64, trimmed bool) *ethrpc.Block { name := path.Join("../", TestDataPathEVM, FileNameEVMBlock(chainID, blockNumber, trimmed)) block := ðrpc.Block{} - err := LoadObjectFromJSONFile(block, name) - require.NoError(t, err) + LoadObjectFromJSONFile(t, block, name) return block } +// LoadBTCIntxRawResult loads archived Bitcoin intx raw result from file +func LoadBTCIntxRawResult(t *testing.T, chainID int64, txHash string, donation bool) *btcjson.TxRawResult { + name := path.Join("../", TestDataPathBTC, FileNameBTCIntx(chainID, txHash, donation)) + rawResult := &btcjson.TxRawResult{} + LoadObjectFromJSONFile(t, rawResult, name) + return rawResult +} + // LoadBTCTxRawResultNCctx loads archived Bitcoin outtx raw result and corresponding cctx func LoadBTCTxRawResultNCctx(t *testing.T, chainID int64, nonce uint64) (*btcjson.TxRawResult, *crosschaintypes.CrossChainTx) { //nameTx := FileNameBTCOuttx(chainID, nonce) nameTx := path.Join("../", TestDataPathBTC, FileNameBTCOuttx(chainID, nonce)) rawResult := &btcjson.TxRawResult{} - err := LoadObjectFromJSONFile(rawResult, nameTx) - require.NoError(t, err) + LoadObjectFromJSONFile(t, rawResult, nameTx) nameCctx := path.Join("../", TestDataPathCctx, FileNameCctxByNonce(chainID, nonce)) cctx := &crosschaintypes.CrossChainTx{} - err = LoadObjectFromJSONFile(cctx, nameCctx) - require.NoError(t, err) + LoadObjectFromJSONFile(t, cctx, nameCctx) return rawResult, cctx } @@ -110,8 +114,7 @@ func LoadEVMIntx( nameTx := path.Join("../", TestDataPathEVM, FileNameEVMIntx(chainID, intxHash, coinType, false)) tx := ðrpc.Transaction{} - err := LoadObjectFromJSONFile(&tx, nameTx) - require.NoError(t, err) + LoadObjectFromJSONFile(t, &tx, nameTx) return tx } @@ -124,8 +127,7 @@ func LoadEVMIntxReceipt( nameReceipt := path.Join("../", TestDataPathEVM, FileNameEVMIntxReceipt(chainID, intxHash, coinType, false)) receipt := ðtypes.Receipt{} - err := LoadObjectFromJSONFile(&receipt, nameReceipt) - require.NoError(t, err) + LoadObjectFromJSONFile(t, &receipt, nameReceipt) return receipt } @@ -138,8 +140,7 @@ func LoadEVMIntxCctx( nameCctx := path.Join("../", TestDataPathCctx, FileNameEVMIntxCctx(chainID, intxHash, coinType)) cctx := &crosschaintypes.CrossChainTx{} - err := LoadObjectFromJSONFile(&cctx, nameCctx) - require.NoError(t, err) + LoadObjectFromJSONFile(t, &cctx, nameCctx) return cctx } @@ -151,8 +152,7 @@ func LoadCctxByNonce( nameCctx := path.Join("../", TestDataPathCctx, FileNameCctxByNonce(chainID, nonce)) cctx := &crosschaintypes.CrossChainTx{} - err := LoadObjectFromJSONFile(&cctx, nameCctx) - require.NoError(t, err) + LoadObjectFromJSONFile(t, &cctx, nameCctx) return cctx } @@ -178,8 +178,7 @@ func LoadEVMIntxDonation( nameTx := path.Join("../", TestDataPathEVM, FileNameEVMIntx(chainID, intxHash, coinType, true)) tx := ðrpc.Transaction{} - err := LoadObjectFromJSONFile(&tx, nameTx) - require.NoError(t, err) + LoadObjectFromJSONFile(t, &tx, nameTx) return tx } @@ -192,8 +191,7 @@ func LoadEVMIntxReceiptDonation( nameReceipt := path.Join("../", TestDataPathEVM, FileNameEVMIntxReceipt(chainID, intxHash, coinType, true)) receipt := ðtypes.Receipt{} - err := LoadObjectFromJSONFile(&receipt, nameReceipt) - require.NoError(t, err) + LoadObjectFromJSONFile(t, &receipt, nameReceipt) return receipt } @@ -233,8 +231,7 @@ func LoadEVMOuttx( nameTx := path.Join("../", TestDataPathEVM, FileNameEVMOuttx(chainID, intxHash, coinType)) tx := ðtypes.Transaction{} - err := LoadObjectFromJSONFile(&tx, nameTx) - require.NoError(t, err) + LoadObjectFromJSONFile(t, &tx, nameTx) return tx } @@ -247,8 +244,7 @@ func LoadEVMOuttxReceipt( nameReceipt := path.Join("../", TestDataPathEVM, FileNameEVMOuttxReceipt(chainID, intxHash, coinType)) receipt := ðtypes.Receipt{} - err := LoadObjectFromJSONFile(&receipt, nameReceipt) - require.NoError(t, err) + LoadObjectFromJSONFile(t, &receipt, nameReceipt) return receipt } diff --git a/zetaclient/testutils/testdata_naming.go b/zetaclient/testutils/testdata_naming.go index bfe842310e..a920680335 100644 --- a/zetaclient/testutils/testdata_naming.go +++ b/zetaclient/testutils/testdata_naming.go @@ -48,6 +48,17 @@ func FileNameBTCOuttx(chainID int64, nonce uint64) string { return fmt.Sprintf("chain_%d_outtx_raw_result_nonce_%d.json", chainID, nonce) } +// FileNameBTCTxByType returns unified archive file name for tx by type +// txType: "P2TR", "P2WPKH", "P2WSH", "P2PKH", "P2SH +func FileNameBTCTxByType(chainID int64, txType string, txHash string) string { + return fmt.Sprintf("chain_%d_tx_raw_result_%s_%s.json", chainID, txType, txHash) +} + +// FileNameBTCMsgTx returns unified archive file name for btc MsgTx +func FileNameBTCMsgTx(chainID int64, txHash string) string { + return fmt.Sprintf("chain_%d_msgtx_%s.json", chainID, txHash) +} + // FileNameCctxByNonce returns unified archive file name for cctx by nonce func FileNameCctxByNonce(chainID int64, nonce uint64) string { return fmt.Sprintf("cctx_%d_%d.json", chainID, nonce) From 9eb55e74e05d733d83e1912e1491e48fb6298592 Mon Sep 17 00:00:00 2001 From: Lucas Bertrand Date: Thu, 4 Apr 2024 08:21:47 +0200 Subject: [PATCH 06/10] test(e2e): add testing for external chain migration (#1900) * init * add migration logic * go import * test logic v1 * temporary utility to reset nonces * complete test with restart * hack for setting nonces * use ganache * manually produce blocks for e2e test * add erc20 withdraw with lower amount of 0.01 * comment failing erc20 withdraw * use new tx format * conflict * remove produce block * automatic block mining anvil * add Dockerfile for anvil localnet * remove chain nonce hack * Reset chain nonces * add the reset chain nonce message * various fixes for Zeta * make generage * remove reset message * rename restart script files * add anvil docker image * add test erc20 whitelist * make lint * fix comment * fix some unit tests * add tests back * update changeog * remove bg * revert back legacy tx * disable chain migration test * fix test file * move timeout check location --------- Co-authored-by: Tanmay Co-authored-by: skosito Co-authored-by: kevinssgh --- Dockerfile-localnet | 9 - changelog.md | 1 + cmd/zetae2e/local/accounts.go | 5 +- cmd/zetae2e/local/admin.go | 5 +- cmd/zetae2e/local/local.go | 8 +- cmd/zetae2e/stress.go | 2 +- contrib/localnet/anvil/Dockerfile | 11 + contrib/localnet/docker-compose-admin.yml | 17 + contrib/localnet/orchestrator/Dockerfile | 1 + .../orchestrator/Dockerfile.fastbuild | 1 + .../restart-zetaclientd-at-upgrade.sh | 50 +++ .../orchestrator/restart-zetaclientd.sh | 51 +-- .../localnet/orchestrator/start-zetae2e.sh | 8 +- contrib/localnet/scripts/start-zetaclientd.sh | 24 +- docs/spec/observer/messages.md | 1 - e2e/e2etests/e2etests.go | 17 +- e2e/e2etests/test_eth_withdraw.go | 4 + e2e/e2etests/test_migrate_chain_support.go | 318 ++++++++++++++++++ e2e/runner/evm.go | 40 ++- e2e/runner/runner.go | 35 +- e2e/runner/setup_evm.go | 16 +- e2e/runner/setup_zeta.go | 5 +- e2e/runner/zeta.go | 16 + e2e/txserver/zeta_tx_server.go | 30 +- e2e/utils/zetacore.go | 64 +++- proto/crosschain/events.proto | 5 + typescript/crosschain/events_pb.d.ts | 29 ++ .../keeper/msg_server_whitelist_erc20.go | 19 +- x/crosschain/types/events.pb.go | 307 ++++++++++++++--- .../keeper/msg_server_reset_chain_nonces.go | 1 - zetaclient/config/config_chain.go | 4 + zetaclient/evm/evm_signer.go | 11 + 32 files changed, 953 insertions(+), 162 deletions(-) create mode 100644 contrib/localnet/anvil/Dockerfile create mode 100644 contrib/localnet/orchestrator/restart-zetaclientd-at-upgrade.sh create mode 100644 e2e/e2etests/test_migrate_chain_support.go diff --git a/Dockerfile-localnet b/Dockerfile-localnet index 793b555c22..65ca4188b4 100644 --- a/Dockerfile-localnet +++ b/Dockerfile-localnet @@ -11,21 +11,12 @@ RUN ssh-keygen -b 2048 -t rsa -f /root/.ssh/localtest.pem -q -N "" WORKDIR /go/delivery/zeta-node COPY go.mod . COPY go.sum . -#RUN --mount=type=cache,target=/root/.cache/go-build \ -# go mod download RUN go mod download COPY . . -#RUN --mount=type=cache,target=/root/.cache/go-build \ -# make install -#RUN --mount=type=cache,target=/root/.cache/go-build \ -# make install-zetae2e RUN make install RUN make install-zetae2e -# -#FROM golang:1.20-alpine -#RUN apk --no-cache add openssh jq tmux vim curl bash RUN ssh-keygen -A WORKDIR /root diff --git a/changelog.md b/changelog.md index 62f22f2491..4dde927265 100644 --- a/changelog.md +++ b/changelog.md @@ -51,6 +51,7 @@ * [1805](https://github.com/zeta-chain/node/pull/1805) - add admin and performance test and fix upgrade test * [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 +* [1900](https://github.com/zeta-chain/node/pull/1900) - add testing for external chain migration * [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 diff --git a/cmd/zetae2e/local/accounts.go b/cmd/zetae2e/local/accounts.go index f179e02859..bcdd741e7f 100644 --- a/cmd/zetae2e/local/accounts.go +++ b/cmd/zetae2e/local/accounts.go @@ -30,8 +30,9 @@ var ( UserMiscPrivateKey = "853c0945b8035a501b1161df65a17a0a20fc848bda8975a8b4e9222cc6f84cd4" // #nosec G101 - used for testing // UserAdminAddress is the address of the account for testing admin function features - UserAdminAddress = ethcommon.HexToAddress("0xcC8487562AAc220ea4406196Ee902C7c076966af") - UserAdminPrivateKey = "95409f1f0e974871cc26ba98ffd31f613aa1287d40c0aea6a87475fc3521d083" // #nosec G101 - used for testing + // NOTE: this is the default account using Anvil + UserAdminAddress = ethcommon.HexToAddress("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266") + UserAdminPrivateKey = "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" // #nosec G101 - used for testing FungibleAdminMnemonic = "snow grace federal cupboard arrive fancy gym lady uniform rotate exercise either leave alien grass" // #nosec G101 - used for testing ) diff --git a/cmd/zetae2e/local/admin.go b/cmd/zetae2e/local/admin.go index 50c8b2b8f7..fa2df0b75c 100644 --- a/cmd/zetae2e/local/admin.go +++ b/cmd/zetae2e/local/admin.go @@ -38,7 +38,7 @@ func adminTestRoutine( deployerRunner, UserAdminAddress, UserAdminPrivateKey, - runner.NewLogger(verbose, color.FgGreen, "admin"), + runner.NewLogger(verbose, color.FgHiGreen, "admin"), ) if err != nil { return err @@ -48,7 +48,8 @@ func adminTestRoutine( startTime := time.Now() // funding the account - txZetaSend := deployerRunner.SendZetaOnEvm(UserAdminAddress, 1000) + // we transfer around the total supply of Zeta to the admin for the chain migration test + txZetaSend := deployerRunner.SendZetaOnEvm(UserAdminAddress, 20_500_000_000) txERC20Send := deployerRunner.SendERC20OnEvm(UserAdminAddress, 1000) adminRunner.WaitForTxReceiptOnEvm(txZetaSend) adminRunner.WaitForTxReceiptOnEvm(txERC20Send) diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index fe1e3faff7..d3ddca09a7 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -188,7 +188,7 @@ func localE2ETest(cmd *cobra.Command, _ []string) { if !skipSetup { logger.Print("⚙️ setting up networks") startTime := time.Now() - deployerRunner.SetupEVM(contractsDeployed) + deployerRunner.SetupEVM(contractsDeployed, true) deployerRunner.SetZEVMContracts() // NOTE: this method return an error so we handle it and panic if it occurs unlike other method that panics directly @@ -289,6 +289,12 @@ func localE2ETest(cmd *cobra.Command, _ []string) { e2etests.TestPauseZRC20Name, e2etests.TestUpdateBytecodeName, e2etests.TestDepositEtherLiquidityCapName, + + // TestMigrateChainSupportName tests EVM chain migration. Currently this test doesn't work with Anvil because pre-EIP1559 txs are not supported + // See issue below for details + // TODO: renenable this test as per the issue below + // https://github.com/zeta-chain/node/issues/1980 + // e2etests.TestMigrateChainSupportName, )) } if testPerformance { diff --git a/cmd/zetae2e/stress.go b/cmd/zetae2e/stress.go index b85d0e78d7..793b9ec3c4 100644 --- a/cmd/zetae2e/stress.go +++ b/cmd/zetae2e/stress.go @@ -155,7 +155,7 @@ func StressTest(cmd *cobra.Command, _ []string) { panic(err) } - e2eTest.SetupEVM(stressTestArgs.contractsDeployed) + e2eTest.SetupEVM(stressTestArgs.contractsDeployed, true) // If stress test is running on local docker environment if stressTestArgs.network == "LOCAL" { diff --git a/contrib/localnet/anvil/Dockerfile b/contrib/localnet/anvil/Dockerfile new file mode 100644 index 0000000000..5426174fdf --- /dev/null +++ b/contrib/localnet/anvil/Dockerfile @@ -0,0 +1,11 @@ +# This Dockerfile is used to build a Docker image for Anvil, a localnet for testing purposes. +# Currently we directly set the chain ID to 11155111 and expose the default Anvil port specifically for the chain migration test. + +# Start from the latest Rust image as Anvil is built with Rust +FROM ghcr.io/foundry-rs/foundry:latest + +# Expose the default Anvil port +EXPOSE 8545 + +# Run Anvil with specified chain ID and a prefunded account when the container starts +ENTRYPOINT ["anvil", "--host", "0.0.0.0", "--chain-id", "11155111"] \ No newline at end of file diff --git a/contrib/localnet/docker-compose-admin.yml b/contrib/localnet/docker-compose-admin.yml index 46b24bfb24..23503cf305 100644 --- a/contrib/localnet/docker-compose-admin.yml +++ b/contrib/localnet/docker-compose-admin.yml @@ -2,8 +2,25 @@ version: "3" # This docker-compose file overrides the orchestrator service to specify the flag to test the admin functions # and skip the regular tests +# it also adds another local Ethereum network to test EVM chain migration and use the additional-evm flag services: orchestrator: entrypoint: ["/work/start-zetae2e.sh", "local --skip-regular --test-admin"] + eth2: + build: + context: ./anvil + container_name: eth2 + hostname: eth2 + ports: + - "8546:8545" + networks: + mynetwork: + ipv4_address: 172.20.0.102 + + zetaclient0: + entrypoint: [ "/root/start-zetaclientd.sh", "additional-evm" ] + + zetaclient1: + entrypoint: [ "/root/start-zetaclientd.sh", "additional-evm" ] \ No newline at end of file diff --git a/contrib/localnet/orchestrator/Dockerfile b/contrib/localnet/orchestrator/Dockerfile index e159d8ba84..e268738ef2 100644 --- a/contrib/localnet/orchestrator/Dockerfile +++ b/contrib/localnet/orchestrator/Dockerfile @@ -13,6 +13,7 @@ COPY --from=zeta /root/.ssh/localtest.pem /root/.ssh/localtest.pem COPY contrib/localnet/orchestrator/start-zetae2e.sh /work/ COPY contrib/localnet/orchestrator/restart-zetaclientd.sh /work/ +COPY contrib/localnet/orchestrator/restart-zetaclientd-at-upgrade.sh /work/ RUN chmod +x /work/*.sh ENV GOPATH /go diff --git a/contrib/localnet/orchestrator/Dockerfile.fastbuild b/contrib/localnet/orchestrator/Dockerfile.fastbuild index 64d9e9c87e..36cf07b8cc 100644 --- a/contrib/localnet/orchestrator/Dockerfile.fastbuild +++ b/contrib/localnet/orchestrator/Dockerfile.fastbuild @@ -13,6 +13,7 @@ COPY --from=zeta /root/.ssh/localtest.pem /root/.ssh/localtest.pem COPY contrib/localnet/orchestrator/start-zetae2e.sh /work/ COPY contrib/localnet/orchestrator/restart-zetaclientd.sh /work/ +COPY contrib/localnet/orchestrator/restart-zetaclientd-at-upgrade.sh /work/ RUN chmod +x /work/*.sh COPY --from=zeta /usr/local/bin/zetae2e /usr/local/bin/ diff --git a/contrib/localnet/orchestrator/restart-zetaclientd-at-upgrade.sh b/contrib/localnet/orchestrator/restart-zetaclientd-at-upgrade.sh new file mode 100644 index 0000000000..7911deca01 --- /dev/null +++ b/contrib/localnet/orchestrator/restart-zetaclientd-at-upgrade.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# This script is used to restart zetaclientd after an upgrade +# It waits for the upgrade height to be reached and then restarts the zetaclientd on all nodes in the network +# It interacts with the network using the zetaclientd binary + +clibuilder() +{ + echo "" + echo "Usage: $0 -u UPGRADE_HEIGHT" + echo -e "\t-u Height of upgrade, should match governance proposal" + echo -e "\t-n Number of clients in the network" + exit 1 # Exit script after printing help +} + +while getopts "u:n:" opt +do + case "$opt" in + u ) UPGRADE_HEIGHT="$OPTARG" ;; + n ) NUM_OF_NODES="$OPTARG" ;; + ? ) clibuilder ;; # Print cliBuilder in case parameter is non-existent + esac +done + +# generate client list +START=0 +END=$((NUM_OF_NODES-1)) +CLIENT_LIST=() +for i in $(eval echo "{$START..$END}") +do + CLIENT_LIST+=("zetaclient$i") +done + +echo "$UPGRADE_HEIGHT" + +CURRENT_HEIGHT=0 + +while [[ $CURRENT_HEIGHT -lt $UPGRADE_HEIGHT ]] +do + CURRENT_HEIGHT=$(curl -s zetacore0:26657/status | jq '.result.sync_info.latest_block_height' | tr -d '"') + echo current height is "$CURRENT_HEIGHT", waiting for "$UPGRADE_HEIGHT" + sleep 5 +done + +echo upgrade height reached, restarting zetaclients + +for NODE in "${CLIENT_LIST[@]}"; do + ssh -o "StrictHostKeyChecking no" "$NODE" -i ~/.ssh/localtest.pem killall zetaclientd + ssh -o "StrictHostKeyChecking no" "$NODE" -i ~/.ssh/localtest.pem "$GOPATH/bin/new/zetaclientd start < /root/password.file > $HOME/zetaclient.log 2>&1 &" +done diff --git a/contrib/localnet/orchestrator/restart-zetaclientd.sh b/contrib/localnet/orchestrator/restart-zetaclientd.sh index 7911deca01..6071b07570 100644 --- a/contrib/localnet/orchestrator/restart-zetaclientd.sh +++ b/contrib/localnet/orchestrator/restart-zetaclientd.sh @@ -1,50 +1,11 @@ #!/bin/bash -# This script is used to restart zetaclientd after an upgrade -# It waits for the upgrade height to be reached and then restarts the zetaclientd on all nodes in the network -# It interacts with the network using the zetaclientd binary +# This script immediately restarts the zetaclientd on zetaclient0 and zetaclient1 containers in the network -clibuilder() -{ - echo "" - echo "Usage: $0 -u UPGRADE_HEIGHT" - echo -e "\t-u Height of upgrade, should match governance proposal" - echo -e "\t-n Number of clients in the network" - exit 1 # Exit script after printing help -} +echo restarting zetaclients -while getopts "u:n:" opt -do - case "$opt" in - u ) UPGRADE_HEIGHT="$OPTARG" ;; - n ) NUM_OF_NODES="$OPTARG" ;; - ? ) clibuilder ;; # Print cliBuilder in case parameter is non-existent - esac -done +ssh -o "StrictHostKeyChecking no" "zetaclient0" -i ~/.ssh/localtest.pem killall zetaclientd +ssh -o "StrictHostKeyChecking no" "zetaclient1" -i ~/.ssh/localtest.pem killall zetaclientd +ssh -o "StrictHostKeyChecking no" "zetaclient0" -i ~/.ssh/localtest.pem "/usr/local/bin/zetaclientd start < /root/password.file > $HOME/zetaclient.log 2>&1 &" +ssh -o "StrictHostKeyChecking no" "zetaclient1" -i ~/.ssh/localtest.pem "/usr/local/bin/zetaclientd start < /root/password.file > $HOME/zetaclient.log 2>&1 &" -# generate client list -START=0 -END=$((NUM_OF_NODES-1)) -CLIENT_LIST=() -for i in $(eval echo "{$START..$END}") -do - CLIENT_LIST+=("zetaclient$i") -done - -echo "$UPGRADE_HEIGHT" - -CURRENT_HEIGHT=0 - -while [[ $CURRENT_HEIGHT -lt $UPGRADE_HEIGHT ]] -do - CURRENT_HEIGHT=$(curl -s zetacore0:26657/status | jq '.result.sync_info.latest_block_height' | tr -d '"') - echo current height is "$CURRENT_HEIGHT", waiting for "$UPGRADE_HEIGHT" - sleep 5 -done - -echo upgrade height reached, restarting zetaclients - -for NODE in "${CLIENT_LIST[@]}"; do - ssh -o "StrictHostKeyChecking no" "$NODE" -i ~/.ssh/localtest.pem killall zetaclientd - ssh -o "StrictHostKeyChecking no" "$NODE" -i ~/.ssh/localtest.pem "$GOPATH/bin/new/zetaclientd start < /root/password.file > $HOME/zetaclient.log 2>&1 &" -done diff --git a/contrib/localnet/orchestrator/start-zetae2e.sh b/contrib/localnet/orchestrator/start-zetae2e.sh index 6f75ae7cc5..a893c689ee 100644 --- a/contrib/localnet/orchestrator/start-zetae2e.sh +++ b/contrib/localnet/orchestrator/start-zetae2e.sh @@ -37,9 +37,9 @@ geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0x8D47Db7390AC4D3D449 echo "funding deployer address 0x90126d02E41c9eB2a10cfc43aAb3BD3460523Cdf with 100 Ether" geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0x90126d02E41c9eB2a10cfc43aAb3BD3460523Cdf", value: web3.toWei(100,"ether")})' attach http://eth:8545 -# unlock advanced erc20 tests accounts -echo "funding deployer address 0xcC8487562AAc220ea4406196Ee902C7c076966af with 100 Ether" -geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0xcC8487562AAc220ea4406196Ee902C7c076966af", value: web3.toWei(100,"ether")})' attach http://eth:8545 +# unlock admin erc20 tests accounts +echo "funding deployer address 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 with 100 Ether" +geth --exec 'eth.sendTransaction({from: eth.coinbase, to: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", value: web3.toWei(100,"ether")})' attach http://eth:8545 # unlock the TSS account echo "funding TSS address 0xF421292cb0d3c97b90EEEADfcD660B893592c6A2 with 100 Ether" @@ -74,7 +74,7 @@ if [ "$OPTION" == "upgrade" ]; then echo "E2E setup passed, waiting for upgrade height..." # Restart zetaclients at upgrade height - /work/restart-zetaclientd.sh -u "$UPGRADE_HEIGHT" -n 2 + /work/restart-zetaclientd-at-upgrade.sh -u "$UPGRADE_HEIGHT" -n 2 echo "waiting 10 seconds for node to restart..." diff --git a/contrib/localnet/scripts/start-zetaclientd.sh b/contrib/localnet/scripts/start-zetaclientd.sh index 761a58098d..a43e8e120f 100755 --- a/contrib/localnet/scripts/start-zetaclientd.sh +++ b/contrib/localnet/scripts/start-zetaclientd.sh @@ -9,6 +9,11 @@ HOSTNAME=$(hostname) OPTION=$1 +# sepolia is used in chain migration tests, this functions set the sepolia endpoint in the zetaclient_config.json +set_sepolia_endpoint() { + jq '.EVMChainConfigs."11155111".Endpoint = "http://eth2:8545"' /root/.zetacored/config/zetaclient_config.json > tmp.json && mv tmp.json /root/.zetacored/config/zetaclient_config.json +} + # read HOTKEY_BACKEND env var for hotkey keyring backend and set default to test BACKEND="test" if [ "$HOTKEY_BACKEND" == "file" ]; then @@ -30,6 +35,14 @@ then rm ~/.tss/* MYIP=$(/sbin/ip -o -4 addr list eth0 | awk '{print $4}' | cut -d/ -f1) zetaclientd init --zetacore-url zetacore0 --chain-id athens_101-1 --operator "$operatorAddress" --log-format=text --public-ip "$MYIP" --keyring-backend "$BACKEND" + + # check if the option is additional-evm + # in this case, the additional evm is represented with the sepolia chain, we set manually the eth2 endpoint to the sepolia chain (11155111 -> http://eth2:8545) + # in /root/.zetacored/config/zetaclient_config.json + if [ "$OPTION" == "additional-evm" ]; then + set_sepolia_endpoint + fi + zetaclientd start < /root/password.file else num=$(echo $HOSTNAME | tr -dc '0-9') @@ -42,11 +55,20 @@ else done rm ~/.tss/* zetaclientd init --peer /ip4/172.20.0.21/tcp/6668/p2p/"$SEED" --zetacore-url "$node" --chain-id athens_101-1 --operator "$operatorAddress" --log-format=text --public-ip "$MYIP" --log-level 1 --keyring-backend "$BACKEND" + + # check if the option is additional-evm + # in this case, the additional evm is represented with the sepolia chain, we set manually the eth2 endpoint to the sepolia chain (11155111 -> http://eth2:8545) + # in /root/.zetacored/config/zetaclient_config.json + if [ "$OPTION" == "additional-evm" ]; then + set_sepolia_endpoint + fi + zetaclientd start < /root/password.file fi +# check if the option is background +# in this case, we tail the zetaclientd log file if [ "$OPTION" == "background" ]; then sleep 3 tail -f $HOME/zetaclient.log fi - diff --git a/docs/spec/observer/messages.md b/docs/spec/observer/messages.md index 82c6423757..a73d3a6134 100644 --- a/docs/spec/observer/messages.md +++ b/docs/spec/observer/messages.md @@ -111,7 +111,6 @@ message MsgAddBlockHeader { ## MsgResetChainNonces ResetChainNonces handles resetting chain nonces -Authorized: admin policy group 2 (admin update) ```proto message MsgResetChainNonces { diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index b5b598ed01..404616535f 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -25,17 +25,15 @@ const ( TestCrosschainSwapName = "crosschain_swap" TestMessagePassingRevertFailName = "message_passing_revert_fail" TestMessagePassingRevertSuccessName = "message_passing_revert_success" - TestPauseZRC20Name = "pause_zrc20" TestERC20DepositAndCallRefundName = "erc20_deposit_and_call_refund" - TestUpdateBytecodeName = "update_bytecode" TestEtherDepositAndCallName = "eth_deposit_and_call" TestDepositEtherLiquidityCapName = "deposit_eth_liquidity_cap" TestMyTestName = "my_test" TestERC20WithdrawName = "erc20_withdraw" TestERC20DepositName = "erc20_deposit" - // #nosec G101: Potential hardcoded credentials (gosec), not a credential - TestERC20DepositRestrictedName = "erc20_deposit_restricted" + + TestERC20DepositRestrictedName = "erc20_deposit_restricted" // #nosec G101: Potential hardcoded credentials (gosec), not a credential TestEtherDepositName = "eth_deposit" TestEtherWithdrawName = "eth_withdraw" TestEtherWithdrawRestrictedName = "eth_withdraw_restricted" @@ -49,6 +47,11 @@ const ( TestStressBTCWithdrawName = "stress_btc_withdraw" TestStressEtherDepositName = "stress_eth_deposit" TestStressBTCDepositName = "stress_btc_deposit" + + // Admin test + TestMigrateChainSupportName = "migrate_chain_support" + TestPauseZRC20Name = "pause_zrc20" + TestUpdateBytecodeName = "update_bytecode" ) // AllE2ETests is an ordered list of all e2e tests @@ -356,4 +359,10 @@ var AllE2ETests = []runner.E2ETest{ }, TestBitcoinWithdrawRestricted, ), + runner.NewE2ETest( + TestMigrateChainSupportName, + "migrate the evm chain from goerli to sepolia", + []runner.ArgDefinition{}, + TestMigrateChainSupport, + ), } diff --git a/e2e/e2etests/test_eth_withdraw.go b/e2e/e2etests/test_eth_withdraw.go index 66e0385642..e9a4211461 100644 --- a/e2e/e2etests/test_eth_withdraw.go +++ b/e2e/e2etests/test_eth_withdraw.go @@ -12,6 +12,8 @@ import ( // TestEtherWithdraw tests the withdraw of ether func TestEtherWithdraw(r *runner.E2ERunner, args []string) { + r.Logger.Info("TestEtherWithdraw") + approvedAmount := big.NewInt(1e18) if len(args) != 1 { panic("TestEtherWithdraw requires exactly one argument for the withdrawal amount.") @@ -59,6 +61,8 @@ func TestEtherWithdraw(r *runner.E2ERunner, args []string) { if cctx.CctxStatus.Status != crosschaintypes.CctxStatus_OutboundMined { panic("cctx status is not outbound mined") } + + r.Logger.Info("TestEtherWithdraw completed") } // TestEtherWithdrawRestricted tests the withdrawal to a restricted receiver address diff --git a/e2e/e2etests/test_migrate_chain_support.go b/e2e/e2etests/test_migrate_chain_support.go new file mode 100644 index 0000000000..ae1acdde93 --- /dev/null +++ b/e2e/e2etests/test_migrate_chain_support.go @@ -0,0 +1,318 @@ +package e2etests + +import ( + "context" + "fmt" + "math/big" + "os/exec" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/fatih/color" + "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zrc20.sol" + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/txserver" + "github.com/zeta-chain/zetacore/e2e/utils" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" +) + +// EVM2RPCURL is the RPC URL for the additional EVM localnet +// Only this test currently uses a additional EVM localnet, and this test is only run locally +// Therefore, we hardcode RPC urls and addresses for simplicity +const EVM2RPCURL = "http://eth2:8545" + +// EVM2ChainID is the chain ID for the additional EVM localnet +// We set Sepolia testnet although the value is not important, only used to differentiate +var EVM2ChainID = chains.SepoliaChain().ChainId + +func TestMigrateChainSupport(r *runner.E2ERunner, _ []string) { + // deposit most of the ZETA supply on ZetaChain + zetaAmount := big.NewInt(1e18) + zetaAmount = zetaAmount.Mul(zetaAmount, big.NewInt(20_000_000_000)) // 20B Zeta + r.DepositZetaWithAmount(r.DeployerAddress, zetaAmount) + + // do an ethers withdraw on the previous chain (0.01eth) for some interaction + TestEtherWithdraw(r, []string{"10000000000000000"}) + + // create runner for the new EVM and set it up + newRunner, err := configureEVM2(r) + if err != nil { + panic(err) + } + newRunner.SetupEVM(false, false) + + // mint some ERC20 + newRunner.MintERC20OnEvm(10000) + + // we deploy connectorETH in this test to simulate a new "canonical" chain emitting ZETA + // to represent the ZETA already existing on ZetaChain we manually send the minted ZETA to the connector + newRunner.SendZetaOnEvm(newRunner.ConnectorEthAddr, 20_000_000_000) + + // update the chain params to set up the chain + chainParams := getNewEVMChainParams(newRunner) + adminAddr, err := newRunner.ZetaTxServer.GetAccountAddressFromName(utils.FungibleAdminName) + if err != nil { + panic(err) + } + _, err = newRunner.ZetaTxServer.BroadcastTx(utils.FungibleAdminName, observertypes.NewMsgUpdateChainParams( + adminAddr, + chainParams, + )) + if err != nil { + panic(err) + } + + // setup the gas token + if err != nil { + panic(err) + } + _, err = newRunner.ZetaTxServer.BroadcastTx(utils.FungibleAdminName, fungibletypes.NewMsgDeployFungibleCoinZRC20( + adminAddr, + "", + chainParams.ChainId, + 18, + "Sepolia ETH", + "sETH", + coin.CoinType_Gas, + 100000, + )) + if err != nil { + panic(err) + } + + // set the gas token in the runner + ethZRC20Addr, err := newRunner.SystemContract.GasCoinZRC20ByChainId(&bind.CallOpts{}, big.NewInt(chainParams.ChainId)) + if err != nil { + panic(err) + } + if (ethZRC20Addr == ethcommon.Address{}) { + panic("eth zrc20 not found") + } + newRunner.ETHZRC20Addr = ethZRC20Addr + ethZRC20, err := zrc20.NewZRC20(ethZRC20Addr, newRunner.ZEVMClient) + if err != nil { + panic(err) + } + newRunner.ETHZRC20 = ethZRC20 + + // set the chain nonces for the new chain + _, err = r.ZetaTxServer.BroadcastTx(utils.FungibleAdminName, observertypes.NewMsgResetChainNonces( + adminAddr, + chainParams.ChainId, + 0, + 0, + )) + if err != nil { + panic(err) + } + + // deactivate the previous chain + chainParams = observertypes.GetDefaultGoerliLocalnetChainParams() + chainParams.IsSupported = false + _, err = newRunner.ZetaTxServer.BroadcastTx(utils.FungibleAdminName, observertypes.NewMsgUpdateChainParams( + adminAddr, + chainParams, + )) + if err != nil { + panic(err) + } + + // restart ZetaClient to pick up the new chain + r.Logger.Print("🔄 restarting ZetaClient to pick up the new chain") + if err := restartZetaClient(); err != nil { + panic(err) + } + + // wait 10 set for the chain to start + time.Sleep(10 * time.Second) + + // emitting a withdraw with the previous chain should fail + txWithdraw, err := r.ETHZRC20.Withdraw(r.ZEVMAuth, r.DeployerAddress.Bytes(), big.NewInt(10000000000000000)) + if err == nil { + receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, txWithdraw, r.Logger, r.ReceiptTimeout) + if receipt.Status == 1 { + panic("withdraw should have failed on the previous chain") + } + } + + // test cross-chain functionalities on the new network + // we use a Go routine to manually mine blocks because Anvil network only mine blocks on tx by default + // we need automatic block mining to get the necessary confirmations for the cross-chain functionalities + stopMining, err := newRunner.AnvilMineBlocks(EVM2RPCURL, 3) + if err != nil { + panic(err) + } + + // deposit Ethers and ERC20 on ZetaChain + etherAmount := big.NewInt(1e18) + etherAmount = etherAmount.Mul(etherAmount, big.NewInt(10)) + txEtherDeposit := newRunner.DepositEtherWithAmount(false, etherAmount) + newRunner.WaitForMinedCCTX(txEtherDeposit) + + // perform withdrawals on the new chain + TestZetaWithdraw(newRunner, []string{"10000000000000000000"}) + TestEtherWithdraw(newRunner, []string{"50000000000000000"}) + + // finally try to deposit Zeta back + TestZetaDeposit(newRunner, []string{"100000000000000000"}) + + // ERC20 test + + // whitelist erc20 zrc20 + newRunner.Logger.Info("whitelisting ERC20 on new network") + res, err := newRunner.ZetaTxServer.BroadcastTx(utils.FungibleAdminName, crosschaintypes.NewMsgWhitelistERC20( + adminAddr, + newRunner.ERC20Addr.Hex(), + chains.SepoliaChain().ChainId, + "USDT", + "USDT", + 18, + 100000, + )) + if err != nil { + panic(err) + } + + // retrieve zrc20 and cctx from event + whitelistCCTXIndex, err := txserver.FetchAttributeFromTxResponse(res, "whitelist_cctx_index") + if err != nil { + panic(err) + } + + erc20zrc20Addr, err := txserver.FetchAttributeFromTxResponse(res, "zrc20_address") + if err != nil { + panic(err) + } + + // wait for the whitelist cctx to be mined + newRunner.WaitForMinedCCTXFromIndex(whitelistCCTXIndex) + + // set erc20 zrc20 contract address + if !ethcommon.IsHexAddress(erc20zrc20Addr) { + panic(fmt.Errorf("invalid contract address: %s", erc20zrc20Addr)) + } + erc20ZRC20, err := zrc20.NewZRC20(ethcommon.HexToAddress(erc20zrc20Addr), newRunner.ZEVMClient) + if err != nil { + panic(err) + } + newRunner.ERC20ZRC20 = erc20ZRC20 + + // deposit ERC20 on ZetaChain + txERC20Deposit := newRunner.DepositERC20() + newRunner.WaitForMinedCCTX(txERC20Deposit) + + // stop mining + stopMining() +} + +// configureEVM2 takes a runner and configures it to use the additional EVM localnet +func configureEVM2(r *runner.E2ERunner) (*runner.E2ERunner, error) { + // initialize a new runner with previous runner values + newRunner := runner.NewE2ERunner( + r.Ctx, + "admin-evm2", + r.CtxCancel, + r.DeployerAddress, + r.DeployerPrivateKey, + r.FungibleAdminMnemonic, + r.EVMClient, + r.ZEVMClient, + r.CctxClient, + r.ZetaTxServer, + r.FungibleClient, + r.AuthClient, + r.BankClient, + r.ObserverClient, + r.EVMAuth, + r.ZEVMAuth, + r.BtcRPCClient, + runner.NewLogger(true, color.FgHiYellow, "admin-evm2"), + ) + + // All existing fields of the runner are the same except for the RPC URL and client for EVM + ewvmClient, evmAuth, err := getEVMClient(newRunner.Ctx, EVM2RPCURL, r.DeployerPrivateKey) + if err != nil { + return nil, err + } + newRunner.EVMClient = ewvmClient + newRunner.EVMAuth = evmAuth + + // Copy the ZetaChain contract addresses from the original runner + if err := newRunner.CopyAddressesFrom(r); err != nil { + return nil, err + } + + // reset evm contracts to ensure they are re-initialized + newRunner.ZetaEthAddr = ethcommon.Address{} + newRunner.ZetaEth = nil + newRunner.ConnectorEthAddr = ethcommon.Address{} + newRunner.ConnectorEth = nil + newRunner.ERC20CustodyAddr = ethcommon.Address{} + newRunner.ERC20Custody = nil + newRunner.ERC20Addr = ethcommon.Address{} + newRunner.ERC20 = nil + + return newRunner, nil +} + +// getEVMClient get evm client from rpc and private key +func getEVMClient(ctx context.Context, rpc, privKey string) (*ethclient.Client, *bind.TransactOpts, error) { + evmClient, err := ethclient.Dial(rpc) + if err != nil { + return nil, nil, err + } + + chainid, err := evmClient.ChainID(ctx) + if err != nil { + return nil, nil, err + } + deployerPrivkey, err := crypto.HexToECDSA(privKey) + if err != nil { + return nil, nil, err + } + evmAuth, err := bind.NewKeyedTransactorWithChainID(deployerPrivkey, chainid) + if err != nil { + return nil, nil, err + } + + return evmClient, evmAuth, nil +} + +// getNewEVMChainParams returns the chain params for the new EVM chain +func getNewEVMChainParams(r *runner.E2ERunner) *observertypes.ChainParams { + // goerli local as base + chainParams := observertypes.GetDefaultGoerliLocalnetChainParams() + + // set the chain id to the new chain id + chainParams.ChainId = EVM2ChainID + + // set contracts + chainParams.ConnectorContractAddress = r.ConnectorEthAddr.Hex() + chainParams.Erc20CustodyContractAddress = r.ERC20CustodyAddr.Hex() + chainParams.ZetaTokenContractAddress = r.ZetaEthAddr.Hex() + + // set supported + chainParams.IsSupported = true + + return chainParams +} + +// restartZetaClient restarts the Zeta client +func restartZetaClient() error { + sshCommandFilePath := "/work/restart-zetaclientd.sh" + cmd := exec.Command("/bin/sh", sshCommandFilePath) + + // Execute the command + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("error restarting ZetaClient: %s - %s", err.Error(), output) + } + return nil +} diff --git a/e2e/runner/evm.go b/e2e/runner/evm.go index 8a931eeccd..c0253905cc 100644 --- a/e2e/runner/evm.go +++ b/e2e/runner/evm.go @@ -1,9 +1,12 @@ package runner import ( + "log" "math/big" "time" + "github.com/ethereum/go-ethereum/rpc" + ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" @@ -77,7 +80,7 @@ func (runner *E2ERunner) DepositERC20() ethcommon.Hash { } func (runner *E2ERunner) DepositERC20WithAmountAndMessage(to ethcommon.Address, amount *big.Int, msg []byte) ethcommon.Hash { - // reset allowance, necessary for ERC20 + // reset allowance, necessary for USDT tx, err := runner.ERC20.Approve(runner.EVMAuth, runner.ERC20CustodyAddr, big.NewInt(0)) if err != nil { panic(err) @@ -169,7 +172,7 @@ func (runner *E2ERunner) SendEther(_ ethcommon.Address, value *big.Int, data []b } tx := ethtypes.NewTransaction(nonce, runner.TSSAddress, value, gasLimit, gasPrice, data) - chainID, err := evmClient.NetworkID(runner.Ctx) + chainID, err := evmClient.ChainID(runner.Ctx) if err != nil { return nil, err } @@ -256,3 +259,36 @@ func (runner *E2ERunner) ProveEthTransaction(receipt *ethtypes.Receipt) { } runner.Logger.Info("OK: txProof verified") } + +// AnvilMineBlocks mines blocks on Anvil localnet +// the block time is provided in seconds +// the method returns a function to stop the mining +func (runner *E2ERunner) AnvilMineBlocks(url string, blockTime int) (func(), error) { + stop := make(chan struct{}) + + client, err := rpc.Dial(url) + if err != nil { + return nil, err + } + defer client.Close() + + go func() { + for { + select { + case <-stop: + return + default: + time.Sleep(time.Duration(blockTime) * time.Second) + + var result interface{} + err = client.CallContext(runner.Ctx, &result, "evm_mine") + if err != nil { + log.Fatalf("Failed to mine a new block: %v", err) + } + } + } + }() + return func() { + close(stop) + }, nil +} diff --git a/e2e/runner/runner.go b/e2e/runner/runner.go index 22f549b296..f66af62ff0 100644 --- a/e2e/runner/runner.go +++ b/e2e/runner/runner.go @@ -62,15 +62,17 @@ type E2ERunner struct { EVMAuth *bind.TransactOpts ZEVMAuth *bind.TransactOpts - // contracts - ZetaEthAddr ethcommon.Address - ZetaEth *zetaeth.ZetaEth - ConnectorEthAddr ethcommon.Address - ConnectorEth *zetaconnectoreth.ZetaConnectorEth - ERC20CustodyAddr ethcommon.Address - ERC20Custody *erc20custody.ERC20Custody - ERC20Addr ethcommon.Address - ERC20 *erc20.ERC20 + // contracts evm + ZetaEthAddr ethcommon.Address + ZetaEth *zetaeth.ZetaEth + ConnectorEthAddr ethcommon.Address + ConnectorEth *zetaconnectoreth.ZetaConnectorEth + ERC20CustodyAddr ethcommon.Address + ERC20Custody *erc20custody.ERC20Custody + ERC20Addr ethcommon.Address + ERC20 *erc20.ERC20 + + // contracts zevm ERC20ZRC20Addr ethcommon.Address ERC20ZRC20 *zrc20.ZRC20 ETHZRC20Addr ethcommon.Address @@ -85,14 +87,13 @@ type E2ERunner struct { ConnectorZEVM *connectorzevm.ZetaConnectorZEVM WZetaAddr ethcommon.Address WZeta *wzeta.WETH9 - - TestDAppAddr ethcommon.Address - ZEVMSwapAppAddr ethcommon.Address - ZEVMSwapApp *zevmswap.ZEVMSwapApp - ContextAppAddr ethcommon.Address - ContextApp *contextapp.ContextApp - SystemContractAddr ethcommon.Address - SystemContract *systemcontract.SystemContract + TestDAppAddr ethcommon.Address + ZEVMSwapAppAddr ethcommon.Address + ZEVMSwapApp *zevmswap.ZEVMSwapApp + ContextAppAddr ethcommon.Address + ContextApp *contextapp.ContextApp + SystemContractAddr ethcommon.Address + SystemContract *systemcontract.SystemContract // config CctxTimeout time.Duration diff --git a/e2e/runner/setup_evm.go b/e2e/runner/setup_evm.go index e8ff909169..8e76ac7409 100644 --- a/e2e/runner/setup_evm.go +++ b/e2e/runner/setup_evm.go @@ -42,7 +42,7 @@ func (runner *E2ERunner) SetEVMContractsFromConfig() { } // SetupEVM setup contracts on EVM for e2e test -func (runner *E2ERunner) SetupEVM(contractsDeployed bool) { +func (runner *E2ERunner) SetupEVM(contractsDeployed bool, whitelistERC20 bool) { runner.Logger.Print("⚙️ setting up EVM network") startTime := time.Now() defer func() { @@ -157,12 +157,14 @@ func (runner *E2ERunner) SetupEVM(contractsDeployed bool) { // initialize custody contract runner.Logger.Info("Whitelist ERC20") - txWhitelist, err := ERC20Custody.Whitelist(runner.EVMAuth, erc20Addr) - if err != nil { - panic(err) - } - if receipt := utils.MustWaitForTxReceipt(runner.Ctx, runner.EVMClient, txWhitelist, runner.Logger, runner.ReceiptTimeout); receipt.Status != 1 { - panic("ERC20 whitelist failed") + if whitelistERC20 { + txWhitelist, err := ERC20Custody.Whitelist(runner.EVMAuth, erc20Addr) + if err != nil { + panic(err) + } + if receipt := utils.MustWaitForTxReceipt(runner.Ctx, runner.EVMClient, txWhitelist, runner.Logger, runner.ReceiptTimeout); receipt.Status != 1 { + panic("ERC20 whitelist failed") + } } runner.Logger.Info("Set TSS address") diff --git a/e2e/runner/setup_zeta.go b/e2e/runner/setup_zeta.go index 28d9f25658..3789babf06 100644 --- a/e2e/runner/setup_zeta.go +++ b/e2e/runner/setup_zeta.go @@ -4,9 +4,6 @@ import ( "math/big" "time" - "github.com/zeta-chain/zetacore/e2e/txserver" - "github.com/zeta-chain/zetacore/pkg/chains" - "github.com/btcsuite/btcutil" "github.com/ethereum/go-ethereum/accounts/abi/bind" ethcommon "github.com/ethereum/go-ethereum/common" @@ -18,7 +15,9 @@ import ( uniswapv2router "github.com/zeta-chain/protocol-contracts/pkg/uniswap/v2-periphery/contracts/uniswapv2router02.sol" "github.com/zeta-chain/zetacore/e2e/contracts/contextapp" "github.com/zeta-chain/zetacore/e2e/contracts/zevmswap" + "github.com/zeta-chain/zetacore/e2e/txserver" e2eutils "github.com/zeta-chain/zetacore/e2e/utils" + "github.com/zeta-chain/zetacore/pkg/chains" fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) diff --git a/e2e/runner/zeta.go b/e2e/runner/zeta.go index 046e14d706..d7fdbdcdf4 100644 --- a/e2e/runner/zeta.go +++ b/e2e/runner/zeta.go @@ -40,6 +40,22 @@ func (runner *E2ERunner) WaitForMinedCCTX(txHash ethcommon.Hash) { } } +// WaitForMinedCCTXFromIndex waits for a cctx to be mined from its index +func (runner *E2ERunner) WaitForMinedCCTXFromIndex(index string) { + defer func() { + runner.Unlock() + }() + runner.Lock() + + cctx := utils.WaitCCTXMinedByIndex(runner.Ctx, index, runner.CctxClient, runner.Logger, runner.CctxTimeout) + if cctx.CctxStatus.Status != types.CctxStatus_OutboundMined { + panic(fmt.Sprintf("expected cctx status to be mined; got %s, message: %s", + cctx.CctxStatus.Status.String(), + cctx.CctxStatus.StatusMessage), + ) + } +} + // SendZetaOnEvm sends ZETA to an address on EVM // this allows the ZETA contract deployer to funds other accounts on EVM func (runner *E2ERunner) SendZetaOnEvm(address ethcommon.Address, zetaAmount int64) *ethtypes.Transaction { diff --git a/e2e/txserver/zeta_tx_server.go b/e2e/txserver/zeta_tx_server.go index f610b8b2bf..f3d946ff5c 100644 --- a/e2e/txserver/zeta_tx_server.go +++ b/e2e/txserver/zeta_tx_server.go @@ -128,6 +128,20 @@ func (zts ZetaTxServer) GetAccountAddress(index int) string { return zts.address[index] } +// GetAccountAddressFromName returns the account address from the given name +func (zts ZetaTxServer) GetAccountAddressFromName(name string) (string, error) { + acc, err := zts.clientCtx.Keyring.Key(name) + if err != nil { + return "", err + } + addr, err := acc.GetAddress() + if err != nil { + return "", err + } + return addr.String(), nil +} + +// GetAllAccountAddress returns all account addresses func (zts ZetaTxServer) GetAllAccountAddress() []string { return zts.address @@ -197,7 +211,7 @@ func (zts ZetaTxServer) DeploySystemContractsAndZRC20(account, erc20Addr string) return "", "", "", "", "", fmt.Errorf("failed to deploy system contracts: %s", err.Error()) } - systemContractAddress, err := fetchAttribute(res, "system_contract") + systemContractAddress, err := FetchAttributeFromTxResponse(res, "system_contract") if err != nil { return "", "", "", "", "", fmt.Errorf("failed to fetch system contract address: %s; rawlog %s", err.Error(), res.RawLog) } @@ -209,23 +223,23 @@ func (zts ZetaTxServer) DeploySystemContractsAndZRC20(account, erc20Addr string) } // get uniswap contract addresses - uniswapV2FactoryAddr, err := fetchAttribute(res, "uniswap_v2_factory") + uniswapV2FactoryAddr, err := FetchAttributeFromTxResponse(res, "uniswap_v2_factory") if err != nil { return "", "", "", "", "", fmt.Errorf("failed to fetch uniswap v2 factory address: %s", err.Error()) } - uniswapV2RouterAddr, err := fetchAttribute(res, "uniswap_v2_router") + uniswapV2RouterAddr, err := FetchAttributeFromTxResponse(res, "uniswap_v2_router") if err != nil { return "", "", "", "", "", fmt.Errorf("failed to fetch uniswap v2 router address: %s", err.Error()) } // get zevm connector address - zevmConnectorAddr, err := fetchAttribute(res, "connector_zevm") + zevmConnectorAddr, err := FetchAttributeFromTxResponse(res, "connector_zevm") if err != nil { return "", "", "", "", "", fmt.Errorf("failed to fetch zevm connector address: %s, txResponse: %s", err.Error(), res.String()) } // get wzeta address - wzetaAddr, err := fetchAttribute(res, "wzeta") + wzetaAddr, err := FetchAttributeFromTxResponse(res, "wzeta") if err != nil { return "", "", "", "", "", fmt.Errorf("failed to fetch wzeta address: %s, txResponse: %s", err.Error(), res.String()) } @@ -276,7 +290,7 @@ func (zts ZetaTxServer) DeploySystemContractsAndZRC20(account, erc20Addr string) } // fetch the erc20 zrc20 contract address and remove the quotes - erc20zrc20Addr, err := fetchAttribute(res, "Contract") + erc20zrc20Addr, err := FetchAttributeFromTxResponse(res, "Contract") if err != nil { return "", "", "", "", "", fmt.Errorf("failed to fetch erc20 zrc20 contract address: %s", err.Error()) } @@ -395,8 +409,8 @@ type attribute struct { Value string `json:"value"` } -// fetchAttribute fetches the attribute from the tx response -func fetchAttribute(res *sdktypes.TxResponse, key string) (string, error) { +// FetchAttributeFromTxResponse fetches the attribute from the tx response +func FetchAttributeFromTxResponse(res *sdktypes.TxResponse, key string) (string, error) { var logs []messageLog err := json.Unmarshal([]byte(res.RawLog), &logs) if err != nil { diff --git a/e2e/utils/zetacore.go b/e2e/utils/zetacore.go index 6a491d1ebd..4aeac71ba3 100644 --- a/e2e/utils/zetacore.go +++ b/e2e/utils/zetacore.go @@ -49,7 +49,11 @@ func WaitCctxsMinedByInTxHash( // fetch cctxs by inTxHash for i := 0; ; i++ { + if time.Since(startTime) > timeout { + panic(fmt.Sprintf("waiting cctx timeout, cctx not mined, inTxHash: %s", inTxHash)) + } time.Sleep(1 * time.Second) + res, err := cctxClient.InTxHashToCctxData(ctx, &crosschaintypes.QueryInTxHashToCctxDataRequest{ InTxHash: inTxHash, }) @@ -93,19 +97,65 @@ func WaitCctxsMinedByInTxHash( cctxs = append(cctxs, &cctx) } if !allFound { - if time.Since(startTime) > timeout { - panic(fmt.Sprintf( - "waiting cctx timeout, cctx not mined, inTxHash: %s, current cctxs: %v", - inTxHash, - cctxs, - )) - } continue } return cctxs } } +// WaitCCTXMinedByIndex waits until cctx is mined; returns the cctxIndex +func WaitCCTXMinedByIndex( + ctx context.Context, + cctxIndex string, + cctxClient crosschaintypes.QueryClient, + logger infoLogger, + cctxTimeout time.Duration, +) *crosschaintypes.CrossChainTx { + startTime := time.Now() + + timeout := DefaultCctxTimeout + if cctxTimeout != 0 { + timeout = cctxTimeout + } + + for i := 0; ; i++ { + if time.Since(startTime) > timeout { + panic(fmt.Sprintf( + "waiting cctx timeout, cctx not mined, cctx: %s", + cctxIndex, + )) + } + time.Sleep(1 * time.Second) + + // fetch cctx by index + res, err := cctxClient.Cctx(ctx, &crosschaintypes.QueryGetCctxRequest{ + Index: cctxIndex, + }) + if err != nil { + // prevent spamming logs + if i%10 == 0 { + logger.Info("Error getting cctx by inTxHash: %s", err.Error()) + } + continue + } + cctx := res.CrossChainTx + if !IsTerminalStatus(cctx.CctxStatus.Status) { + // prevent spamming logs + if i%10 == 0 { + logger.Info( + "waiting for cctx to be mined from index: %s, cctx status: %s, message: %s", + cctxIndex, + cctx.CctxStatus.Status.String(), + cctx.CctxStatus.StatusMessage, + ) + } + continue + } + + return cctx + } +} + func IsTerminalStatus(status crosschaintypes.CctxStatus) bool { return status == crosschaintypes.CctxStatus_OutboundMined || status == crosschaintypes.CctxStatus_Aborted || diff --git a/proto/crosschain/events.proto b/proto/crosschain/events.proto index 05786446ed..3c955bf0f7 100644 --- a/proto/crosschain/events.proto +++ b/proto/crosschain/events.proto @@ -63,3 +63,8 @@ message EventCCTXGasPriceIncreased { string gas_price_increase = 2; string additional_fees = 3; } + +message EventERC20Whitelist { + string whitelist_cctx_index = 1; + string zrc20_address = 2; +} diff --git a/typescript/crosschain/events_pb.d.ts b/typescript/crosschain/events_pb.d.ts index 285f476b30..37a8e7cf2a 100644 --- a/typescript/crosschain/events_pb.d.ts +++ b/typescript/crosschain/events_pb.d.ts @@ -325,3 +325,32 @@ export declare class EventCCTXGasPriceIncreased extends Message | undefined, b: EventCCTXGasPriceIncreased | PlainMessage | undefined): boolean; } +/** + * @generated from message zetachain.zetacore.crosschain.EventERC20Whitelist + */ +export declare class EventERC20Whitelist extends Message { + /** + * @generated from field: string whitelist_cctx_index = 1; + */ + whitelistCctxIndex: string; + + /** + * @generated from field: string zrc20_address = 2; + */ + zrc20Address: string; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.crosschain.EventERC20Whitelist"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): EventERC20Whitelist; + + static fromJson(jsonValue: JsonValue, options?: Partial): EventERC20Whitelist; + + static fromJsonString(jsonString: string, options?: Partial): EventERC20Whitelist; + + static equals(a: EventERC20Whitelist | PlainMessage | undefined, b: EventERC20Whitelist | PlainMessage | undefined): boolean; +} + diff --git a/x/crosschain/keeper/msg_server_whitelist_erc20.go b/x/crosschain/keeper/msg_server_whitelist_erc20.go index 0687aedbb1..fb2de5e45e 100644 --- a/x/crosschain/keeper/msg_server_whitelist_erc20.go +++ b/x/crosschain/keeper/msg_server_whitelist_erc20.go @@ -5,18 +5,15 @@ import ( "fmt" "math/big" - "github.com/zeta-chain/zetacore/pkg/coin" - "github.com/zeta-chain/zetacore/pkg/constant" - authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" - errorsmod "cosmossdk.io/errors" "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - + "github.com/zeta-chain/zetacore/pkg/coin" + "github.com/zeta-chain/zetacore/pkg/constant" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/crosschain/types" fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" ) @@ -174,6 +171,16 @@ func (k msgServer) WhitelistERC20(goCtx context.Context, msg *types.MsgWhitelist commit() + err = ctx.EventManager().EmitTypedEvent( + &types.EventERC20Whitelist{ + Zrc20Address: zrc20Addr.Hex(), + WhitelistCctxIndex: index, + }, + ) + if err != nil { + return nil, errorsmod.Wrapf(err, "failed to emit event") + } + return &types.MsgWhitelistERC20Response{ Zrc20Address: zrc20Addr.Hex(), CctxIndex: index, diff --git a/x/crosschain/types/events.pb.go b/x/crosschain/types/events.pb.go index 39d521d4a7..6fd40fd641 100644 --- a/x/crosschain/types/events.pb.go +++ b/x/crosschain/types/events.pb.go @@ -568,6 +568,58 @@ func (m *EventCCTXGasPriceIncreased) GetAdditionalFees() string { return "" } +type EventERC20Whitelist struct { + WhitelistCctxIndex string `protobuf:"bytes,1,opt,name=whitelist_cctx_index,json=whitelistCctxIndex,proto3" json:"whitelist_cctx_index,omitempty"` + Zrc20Address string `protobuf:"bytes,2,opt,name=zrc20_address,json=zrc20Address,proto3" json:"zrc20_address,omitempty"` +} + +func (m *EventERC20Whitelist) Reset() { *m = EventERC20Whitelist{} } +func (m *EventERC20Whitelist) String() string { return proto.CompactTextString(m) } +func (*EventERC20Whitelist) ProtoMessage() {} +func (*EventERC20Whitelist) Descriptor() ([]byte, []int) { + return fileDescriptor_7398db8b12b87b9e, []int{6} +} +func (m *EventERC20Whitelist) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventERC20Whitelist) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventERC20Whitelist.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 *EventERC20Whitelist) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventERC20Whitelist.Merge(m, src) +} +func (m *EventERC20Whitelist) XXX_Size() int { + return m.Size() +} +func (m *EventERC20Whitelist) XXX_DiscardUnknown() { + xxx_messageInfo_EventERC20Whitelist.DiscardUnknown(m) +} + +var xxx_messageInfo_EventERC20Whitelist proto.InternalMessageInfo + +func (m *EventERC20Whitelist) GetWhitelistCctxIndex() string { + if m != nil { + return m.WhitelistCctxIndex + } + return "" +} + +func (m *EventERC20Whitelist) GetZrc20Address() string { + if m != nil { + return m.Zrc20Address + } + return "" +} + func init() { proto.RegisterType((*EventInboundFinalized)(nil), "zetachain.zetacore.crosschain.EventInboundFinalized") proto.RegisterType((*EventZrcWithdrawCreated)(nil), "zetachain.zetacore.crosschain.EventZrcWithdrawCreated") @@ -575,52 +627,57 @@ func init() { proto.RegisterType((*EventOutboundFailure)(nil), "zetachain.zetacore.crosschain.EventOutboundFailure") proto.RegisterType((*EventOutboundSuccess)(nil), "zetachain.zetacore.crosschain.EventOutboundSuccess") proto.RegisterType((*EventCCTXGasPriceIncreased)(nil), "zetachain.zetacore.crosschain.EventCCTXGasPriceIncreased") + proto.RegisterType((*EventERC20Whitelist)(nil), "zetachain.zetacore.crosschain.EventERC20Whitelist") } func init() { proto.RegisterFile("crosschain/events.proto", fileDescriptor_7398db8b12b87b9e) } var fileDescriptor_7398db8b12b87b9e = []byte{ - // 640 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x95, 0xdf, 0x4e, 0x14, 0x31, - 0x14, 0xc6, 0x19, 0xd8, 0x5d, 0x76, 0x0b, 0x2c, 0x66, 0x82, 0x52, 0x89, 0x4c, 0x90, 0xc4, 0x3f, - 0x17, 0xba, 0x1b, 0xe3, 0x1b, 0xb0, 0x11, 0x21, 0xc6, 0x60, 0x00, 0xa3, 0xe1, 0xa6, 0xe9, 0x4e, - 0x8f, 0x33, 0x8d, 0xb3, 0xed, 0xa6, 0xed, 0xc0, 0xc0, 0x53, 0x18, 0xdf, 0xc3, 0xc4, 0x07, 0xf0, - 0x01, 0xbc, 0xe4, 0xc2, 0x0b, 0x2f, 0x0d, 0xbc, 0x88, 0x69, 0x3b, 0x23, 0xec, 0x60, 0xf4, 0xc2, - 0x68, 0xe2, 0x5d, 0xcf, 0x77, 0x4e, 0x0f, 0xbf, 0x7e, 0x67, 0xd8, 0x83, 0x96, 0x63, 0x25, 0xb5, - 0x8e, 0x53, 0xca, 0x45, 0x1f, 0x0e, 0x41, 0x18, 0xdd, 0x1b, 0x2b, 0x69, 0x64, 0xb8, 0x7a, 0x02, - 0x86, 0x3a, 0xbd, 0xe7, 0x4e, 0x52, 0x41, 0xef, 0xa2, 0x76, 0x65, 0x29, 0x91, 0x89, 0x74, 0x95, - 0x7d, 0x7b, 0xf2, 0x97, 0xd6, 0xbf, 0xcc, 0xa0, 0xeb, 0x4f, 0x6c, 0x97, 0x6d, 0x31, 0x94, 0xb9, - 0x60, 0x9b, 0x5c, 0xd0, 0x8c, 0x9f, 0x00, 0x0b, 0xd7, 0xd0, 0xfc, 0x48, 0x27, 0xc4, 0x1c, 0x8f, - 0x81, 0xe4, 0x2a, 0xc3, 0xc1, 0x5a, 0x70, 0xbf, 0xb3, 0x8b, 0x46, 0x3a, 0xd9, 0x3f, 0x1e, 0xc3, - 0x4b, 0x95, 0x85, 0xab, 0x08, 0xc5, 0xb1, 0x29, 0x08, 0x17, 0x0c, 0x0a, 0x3c, 0xed, 0xf2, 0x1d, - 0xab, 0x6c, 0x5b, 0x21, 0xbc, 0x81, 0x5a, 0x1a, 0x04, 0x03, 0x85, 0x67, 0x5c, 0xaa, 0x8c, 0xc2, - 0x9b, 0xa8, 0x6d, 0x0a, 0x22, 0x55, 0xc2, 0x05, 0x6e, 0xb8, 0xcc, 0xac, 0x29, 0x76, 0x6c, 0x18, - 0x2e, 0xa1, 0x26, 0xd5, 0x1a, 0x0c, 0x6e, 0x3a, 0xdd, 0x07, 0xe1, 0x2d, 0x84, 0xb8, 0x20, 0xa6, - 0x20, 0x29, 0xd5, 0x29, 0x6e, 0xb9, 0x54, 0x9b, 0x8b, 0xfd, 0x62, 0x8b, 0xea, 0x34, 0xbc, 0x8b, - 0x16, 0xb9, 0x20, 0xc3, 0x4c, 0xc6, 0x6f, 0x49, 0x0a, 0x3c, 0x49, 0x0d, 0x9e, 0x75, 0x25, 0x0b, - 0x5c, 0x6c, 0x58, 0x75, 0xcb, 0x89, 0xe1, 0x0a, 0x6a, 0x2b, 0x88, 0x81, 0x1f, 0x82, 0xc2, 0x6d, - 0xdf, 0xa3, 0x8a, 0xc3, 0x3b, 0xa8, 0x5b, 0x9d, 0x89, 0x73, 0x0b, 0x77, 0x7c, 0x8b, 0x4a, 0x1d, - 0x58, 0xd1, 0xbe, 0x88, 0x8e, 0x64, 0x2e, 0x0c, 0x46, 0xfe, 0x45, 0x3e, 0x0a, 0xef, 0xa1, 0x45, - 0x05, 0x19, 0x3d, 0x06, 0x46, 0x46, 0xa0, 0x35, 0x4d, 0x00, 0xcf, 0xb9, 0x82, 0x6e, 0x29, 0x3f, - 0xf7, 0xaa, 0x75, 0x4c, 0xc0, 0x11, 0xd1, 0x86, 0x9a, 0x5c, 0xe3, 0x79, 0xef, 0x98, 0x80, 0xa3, - 0x3d, 0x27, 0x58, 0x0c, 0x9f, 0xfa, 0xd1, 0x66, 0xc1, 0x63, 0x78, 0xb5, 0xea, 0x72, 0x1b, 0xcd, - 0x7b, 0x2b, 0x4b, 0xd6, 0xae, 0x2b, 0x9a, 0xf3, 0x9a, 0x23, 0x5d, 0xff, 0x30, 0x8d, 0x96, 0xdd, - 0x58, 0x0f, 0x54, 0xfc, 0x8a, 0x9b, 0x94, 0x29, 0x7a, 0x34, 0x50, 0x40, 0xcd, 0xdf, 0x1c, 0x6c, - 0x9d, 0xab, 0x71, 0x85, 0xab, 0x36, 0xca, 0x66, 0x6d, 0x94, 0x97, 0x47, 0xd4, 0xfa, 0xed, 0x88, - 0x66, 0x7f, 0x3d, 0xa2, 0xf6, 0xc4, 0x88, 0x26, 0x9d, 0xef, 0xd4, 0x9c, 0x5f, 0xff, 0x18, 0x20, - 0xec, 0xfd, 0x02, 0x43, 0xff, 0x99, 0x61, 0x93, 0x6e, 0x34, 0x6a, 0x6e, 0x4c, 0x22, 0x37, 0xeb, - 0xc8, 0x9f, 0x02, 0xb4, 0xe4, 0x90, 0x77, 0x72, 0xe3, 0xff, 0x75, 0x29, 0xcf, 0x72, 0x05, 0x7f, - 0x8e, 0xbb, 0x8a, 0x90, 0xcc, 0x58, 0xf5, 0x87, 0x3d, 0x72, 0x47, 0x66, 0xac, 0xfc, 0x4a, 0x27, - 0xb9, 0x1a, 0x3f, 0xf9, 0x88, 0x0f, 0x69, 0x96, 0x03, 0x29, 0x07, 0xc3, 0x4a, 0xf4, 0x05, 0xa7, - 0xee, 0x96, 0xe2, 0x55, 0xfc, 0xbd, 0x3c, 0x8e, 0x41, 0xeb, 0xff, 0x04, 0xff, 0x7d, 0x80, 0x56, - 0x1c, 0xfe, 0x60, 0xb0, 0xff, 0xfa, 0x29, 0xd5, 0x2f, 0x14, 0x8f, 0x61, 0x5b, 0xc4, 0x0a, 0xa8, - 0x06, 0x56, 0x43, 0x0c, 0xea, 0x88, 0x0f, 0x50, 0x98, 0x50, 0x4d, 0xc6, 0xf6, 0x12, 0xe1, 0xe5, - 0xad, 0xf2, 0x25, 0xd7, 0x92, 0x5a, 0x37, 0xfb, 0xf3, 0x42, 0x19, 0xe3, 0x86, 0x4b, 0x41, 0x33, - 0xf2, 0x06, 0xa0, 0x7a, 0x55, 0xf7, 0x42, 0xde, 0x04, 0xd0, 0x1b, 0xcf, 0x3e, 0x9f, 0x45, 0xc1, - 0xe9, 0x59, 0x14, 0x7c, 0x3b, 0x8b, 0x82, 0x77, 0xe7, 0xd1, 0xd4, 0xe9, 0x79, 0x34, 0xf5, 0xf5, - 0x3c, 0x9a, 0x3a, 0x78, 0x94, 0x70, 0x93, 0xe6, 0xc3, 0x5e, 0x2c, 0x47, 0x7d, 0xbb, 0x1c, 0x1e, - 0xfa, 0xfd, 0x51, 0xed, 0x89, 0x7e, 0xd1, 0xbf, 0xb4, 0x55, 0xac, 0xf5, 0x7a, 0xd8, 0x72, 0x0b, - 0xe2, 0xf1, 0xf7, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd0, 0xf3, 0x4a, 0xbd, 0x70, 0x06, 0x00, 0x00, + // 690 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x95, 0xdf, 0x4e, 0x13, 0x41, + 0x14, 0xc6, 0x59, 0x68, 0x4b, 0x3b, 0xb4, 0xc5, 0xac, 0x55, 0x56, 0x22, 0x0d, 0xd6, 0xf8, 0xe7, + 0x42, 0x5b, 0xc4, 0x27, 0x90, 0x06, 0x84, 0x18, 0x83, 0x01, 0x0c, 0x86, 0x9b, 0xc9, 0x74, 0xf7, + 0xb8, 0x3b, 0x71, 0x3b, 0xd3, 0xcc, 0xcc, 0xd2, 0x85, 0xa7, 0x30, 0xbe, 0x87, 0x89, 0x0f, 0xe0, + 0x03, 0x78, 0xc9, 0x85, 0x17, 0x5e, 0x1a, 0x78, 0x11, 0x33, 0x33, 0xbb, 0x40, 0x17, 0xa3, 0x17, + 0x46, 0x13, 0xef, 0xe6, 0x7c, 0x67, 0xe6, 0xf0, 0x9b, 0xef, 0x5b, 0x3a, 0x68, 0xc1, 0x17, 0x5c, + 0x4a, 0x3f, 0x22, 0x94, 0xf5, 0xe0, 0x10, 0x98, 0x92, 0xdd, 0x91, 0xe0, 0x8a, 0xbb, 0x4b, 0xc7, + 0xa0, 0x88, 0xd1, 0xbb, 0x66, 0xc5, 0x05, 0x74, 0x2f, 0xf6, 0x2e, 0xb6, 0x42, 0x1e, 0x72, 0xb3, + 0xb3, 0xa7, 0x57, 0xf6, 0x50, 0xe7, 0xeb, 0x0c, 0xba, 0xb1, 0xae, 0xa7, 0x6c, 0xb1, 0x01, 0x4f, + 0x58, 0xb0, 0x41, 0x19, 0x89, 0xe9, 0x31, 0x04, 0xee, 0x32, 0xaa, 0x0f, 0x65, 0x88, 0xd5, 0xd1, + 0x08, 0x70, 0x22, 0x62, 0xcf, 0x59, 0x76, 0x1e, 0xd6, 0x76, 0xd0, 0x50, 0x86, 0x7b, 0x47, 0x23, + 0x78, 0x2d, 0x62, 0x77, 0x09, 0x21, 0xdf, 0x57, 0x29, 0xa6, 0x2c, 0x80, 0xd4, 0x9b, 0x36, 0xfd, + 0x9a, 0x56, 0xb6, 0xb4, 0xe0, 0xde, 0x44, 0x15, 0x09, 0x2c, 0x00, 0xe1, 0xcd, 0x98, 0x56, 0x56, + 0xb9, 0xb7, 0x50, 0x55, 0xa5, 0x98, 0x8b, 0x90, 0x32, 0xaf, 0x64, 0x3a, 0xb3, 0x2a, 0xdd, 0xd6, + 0xa5, 0xdb, 0x42, 0x65, 0x22, 0x25, 0x28, 0xaf, 0x6c, 0x74, 0x5b, 0xb8, 0xb7, 0x11, 0xa2, 0x0c, + 0xab, 0x14, 0x47, 0x44, 0x46, 0x5e, 0xc5, 0xb4, 0xaa, 0x94, 0xed, 0xa5, 0x9b, 0x44, 0x46, 0xee, + 0x7d, 0x34, 0x4f, 0x19, 0x1e, 0xc4, 0xdc, 0x7f, 0x87, 0x23, 0xa0, 0x61, 0xa4, 0xbc, 0x59, 0xb3, + 0xa5, 0x41, 0xd9, 0x9a, 0x56, 0x37, 0x8d, 0xe8, 0x2e, 0xa2, 0xaa, 0x00, 0x1f, 0xe8, 0x21, 0x08, + 0xaf, 0x6a, 0x67, 0xe4, 0xb5, 0x7b, 0x0f, 0x35, 0xf3, 0x35, 0x36, 0x6e, 0x79, 0x35, 0x3b, 0x22, + 0x57, 0xfb, 0x5a, 0xd4, 0x37, 0x22, 0x43, 0x9e, 0x30, 0xe5, 0x21, 0x7b, 0x23, 0x5b, 0xb9, 0x0f, + 0xd0, 0xbc, 0x80, 0x98, 0x1c, 0x41, 0x80, 0x87, 0x20, 0x25, 0x09, 0xc1, 0x9b, 0x33, 0x1b, 0x9a, + 0x99, 0xfc, 0xd2, 0xaa, 0xda, 0x31, 0x06, 0x63, 0x2c, 0x15, 0x51, 0x89, 0xf4, 0xea, 0xd6, 0x31, + 0x06, 0xe3, 0x5d, 0x23, 0x68, 0x0c, 0xdb, 0x3a, 0x1f, 0xd3, 0xb0, 0x18, 0x56, 0xcd, 0xa7, 0xdc, + 0x41, 0x75, 0x6b, 0x65, 0xc6, 0xda, 0x34, 0x9b, 0xe6, 0xac, 0x66, 0x48, 0x3b, 0x1f, 0xa7, 0xd1, + 0x82, 0x89, 0xf5, 0x40, 0xf8, 0xfb, 0x54, 0x45, 0x81, 0x20, 0xe3, 0xbe, 0x00, 0xa2, 0xfe, 0x66, + 0xb0, 0x45, 0xae, 0xd2, 0x15, 0xae, 0x42, 0x94, 0xe5, 0x42, 0x94, 0x97, 0x23, 0xaa, 0xfc, 0x36, + 0xa2, 0xd9, 0x5f, 0x47, 0x54, 0x9d, 0x88, 0x68, 0xd2, 0xf9, 0x5a, 0xc1, 0xf9, 0xce, 0x27, 0x07, + 0x79, 0xd6, 0x2f, 0x50, 0xe4, 0x9f, 0x19, 0x36, 0xe9, 0x46, 0xa9, 0xe0, 0xc6, 0x24, 0x72, 0xb9, + 0x88, 0xfc, 0xd9, 0x41, 0x2d, 0x83, 0xbc, 0x9d, 0x28, 0xfb, 0xaf, 0x4b, 0x68, 0x9c, 0x08, 0xf8, + 0x73, 0xdc, 0x25, 0x84, 0x78, 0x1c, 0xe4, 0x7f, 0xd8, 0x22, 0xd7, 0x78, 0x1c, 0x64, 0x5f, 0xe9, + 0x24, 0x57, 0xe9, 0x27, 0x1f, 0xf1, 0x21, 0x89, 0x13, 0xc0, 0x59, 0x30, 0x41, 0x86, 0xde, 0x30, + 0xea, 0x4e, 0x26, 0x5e, 0xc5, 0xdf, 0x4d, 0x7c, 0x1f, 0xa4, 0xfc, 0x4f, 0xf0, 0x3f, 0x38, 0x68, + 0xd1, 0xe0, 0xf7, 0xfb, 0x7b, 0x6f, 0x9e, 0x13, 0xf9, 0x4a, 0x50, 0x1f, 0xb6, 0x98, 0x2f, 0x80, + 0x48, 0x08, 0x0a, 0x88, 0x4e, 0x11, 0xf1, 0x11, 0x72, 0x43, 0x22, 0xf1, 0x48, 0x1f, 0xc2, 0x34, + 0x3b, 0x95, 0xdd, 0xe4, 0x5a, 0x58, 0x98, 0xa6, 0x7f, 0x5e, 0x48, 0x10, 0x50, 0x45, 0x39, 0x23, + 0x31, 0x7e, 0x0b, 0x90, 0xdf, 0xaa, 0x79, 0x21, 0x6f, 0x00, 0xc8, 0x4e, 0x8c, 0xae, 0x1b, 0xa6, + 0xf5, 0x9d, 0xfe, 0xea, 0xca, 0x7e, 0x44, 0x15, 0xc4, 0x54, 0x2a, 0x77, 0x05, 0xb5, 0xc6, 0x79, + 0x81, 0xaf, 0x60, 0xb9, 0xe7, 0xbd, 0xfe, 0x39, 0xdf, 0x5d, 0xd4, 0x38, 0x16, 0xfe, 0xea, 0x0a, + 0x26, 0x41, 0x20, 0x40, 0xca, 0x0c, 0xad, 0x6e, 0xc4, 0x67, 0x56, 0x5b, 0x7b, 0xf1, 0xe5, 0xb4, + 0xed, 0x9c, 0x9c, 0xb6, 0x9d, 0xef, 0xa7, 0x6d, 0xe7, 0xfd, 0x59, 0x7b, 0xea, 0xe4, 0xac, 0x3d, + 0xf5, 0xed, 0xac, 0x3d, 0x75, 0xf0, 0x24, 0xa4, 0x2a, 0x4a, 0x06, 0x5d, 0x9f, 0x0f, 0x7b, 0xfa, + 0x29, 0x7a, 0x6c, 0x5f, 0xab, 0xfc, 0x55, 0xea, 0xa5, 0xbd, 0x4b, 0x6f, 0x98, 0x0e, 0x5a, 0x0e, + 0x2a, 0xe6, 0x39, 0x7a, 0xfa, 0x23, 0x00, 0x00, 0xff, 0xff, 0xab, 0x7b, 0xbd, 0xa8, 0xde, 0x06, + 0x00, 0x00, } func (m *EventInboundFinalized) Marshal() (dAtA []byte, err error) { @@ -1048,6 +1105,43 @@ func (m *EventCCTXGasPriceIncreased) MarshalToSizedBuffer(dAtA []byte) (int, err return len(dAtA) - i, nil } +func (m *EventERC20Whitelist) 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 *EventERC20Whitelist) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventERC20Whitelist) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Zrc20Address) > 0 { + i -= len(m.Zrc20Address) + copy(dAtA[i:], m.Zrc20Address) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Zrc20Address))) + i-- + dAtA[i] = 0x12 + } + if len(m.WhitelistCctxIndex) > 0 { + i -= len(m.WhitelistCctxIndex) + copy(dAtA[i:], m.WhitelistCctxIndex) + i = encodeVarintEvents(dAtA, i, uint64(len(m.WhitelistCctxIndex))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintEvents(dAtA []byte, offset int, v uint64) int { offset -= sovEvents(v) base := offset @@ -1277,6 +1371,23 @@ func (m *EventCCTXGasPriceIncreased) Size() (n int) { return n } +func (m *EventERC20Whitelist) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.WhitelistCctxIndex) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.Zrc20Address) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + func sovEvents(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -2895,6 +3006,120 @@ func (m *EventCCTXGasPriceIncreased) Unmarshal(dAtA []byte) error { } return nil } +func (m *EventERC20Whitelist) 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 ErrIntOverflowEvents + } + 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: EventERC20Whitelist: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventERC20Whitelist: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field WhitelistCctxIndex", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.WhitelistCctxIndex = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Zrc20Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + 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 ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Zrc20Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipEvents(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/observer/keeper/msg_server_reset_chain_nonces.go b/x/observer/keeper/msg_server_reset_chain_nonces.go index d57cee6593..853ef1ad16 100644 --- a/x/observer/keeper/msg_server_reset_chain_nonces.go +++ b/x/observer/keeper/msg_server_reset_chain_nonces.go @@ -10,7 +10,6 @@ import ( ) // ResetChainNonces handles resetting chain nonces -// Authorized: admin policy group 2 (admin update) func (k msgServer) ResetChainNonces(goCtx context.Context, msg *types.MsgResetChainNonces) (*types.MsgResetChainNoncesResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) if !k.GetAuthorityKeeper().IsAuthorized(ctx, msg.Creator, authoritytypes.PolicyType_groupOperational) { diff --git a/zetaclient/config/config_chain.go b/zetaclient/config/config_chain.go index 869d9111ea..f211f1030d 100644 --- a/zetaclient/config/config_chain.go +++ b/zetaclient/config/config_chain.go @@ -54,6 +54,10 @@ var evmChainsConfigs = map[int64]EVMConfig{ Chain: chains.GoerliChain(), Endpoint: "", }, + chains.SepoliaChain().ChainId: { + Chain: chains.SepoliaChain(), + Endpoint: "", + }, chains.BscTestnetChain().ChainId: { Chain: chains.BscTestnetChain(), Endpoint: "", diff --git a/zetaclient/evm/evm_signer.go b/zetaclient/evm/evm_signer.go index 8301a5d267..e077d19fe2 100644 --- a/zetaclient/evm/evm_signer.go +++ b/zetaclient/evm/evm_signer.go @@ -141,7 +141,11 @@ func (signer *Signer) Sign( height uint64, ) (*ethtypes.Transaction, []byte, []byte, error) { log.Debug().Msgf("TSS SIGNER: %s", signer.tssSigner.Pubkey()) + + // TODO: use EIP-1559 transaction type + // https://github.com/zeta-chain/node/issues/1952 tx := ethtypes.NewTransaction(nonce, to, big.NewInt(0), gasLimit, gasPrice, data) + hashBytes := signer.ethSigner.Hash(tx).Bytes() sig, err := signer.tssSigner.Sign(hashBytes, height, nonce, signer.chain, "") @@ -249,7 +253,10 @@ func (signer *Signer) SignRevertTx(txData *OutBoundTransactionData) (*ethtypes.T // SignCancelTx signs a transaction from TSS address to itself with a zero amount in order to increment the nonce func (signer *Signer) SignCancelTx(nonce uint64, gasPrice *big.Int, height uint64) (*ethtypes.Transaction, error) { + // TODO: use EIP-1559 transaction type + // https://github.com/zeta-chain/node/issues/1952 tx := ethtypes.NewTransaction(nonce, signer.tssSigner.EVMAddress(), big.NewInt(0), 21000, gasPrice, nil) + hashBytes := signer.ethSigner.Hash(tx).Bytes() sig, err := signer.tssSigner.Sign(hashBytes, height, nonce, signer.chain, "") if err != nil { @@ -271,7 +278,10 @@ func (signer *Signer) SignCancelTx(nonce uint64, gasPrice *big.Int, height uint6 // SignWithdrawTx signs a withdrawal transaction sent from the TSS address to the destination func (signer *Signer) SignWithdrawTx(txData *OutBoundTransactionData) (*ethtypes.Transaction, error) { + // TODO: use EIP-1559 transaction type + // https://github.com/zeta-chain/node/issues/1952 tx := ethtypes.NewTransaction(txData.nonce, txData.to, txData.amount, 21000, txData.gasPrice, nil) + hashBytes := signer.ethSigner.Hash(tx).Bytes() sig, err := signer.tssSigner.Sign(hashBytes, txData.height, txData.nonce, signer.chain, "") if err != nil { @@ -506,6 +516,7 @@ func (signer *Signer) reportToOutTxTracker(zetaBridge interfaces.ZetaCoreBridger for { // give up after 10 minutes of monitoring time.Sleep(10 * time.Second) + if time.Since(tStart) > OutTxInclusionTimeout { // if tx is still pending after timeout, report to outTxTracker anyway as we cannot monitor forever if isPending { From 131a774b06d39e8c1a7884d34f4d962f9edff626 Mon Sep 17 00:00:00 2001 From: skosito Date: Fri, 5 Apr 2024 06:50:26 +0100 Subject: [PATCH 07/10] test: improve fungible module coverage (#1985) * Add more tests for fungible module * Changelog * Try out codecov issue * Revert * Add more tests * PR comments --- changelog.md | 1 + testutil/keeper/mocks/observer/staking.go | 5 - x/fungible/keeper/deposits_test.go | 17 + x/fungible/keeper/evm.go | 43 +- x/fungible/keeper/evm_hooks.go | 6 +- x/fungible/keeper/evm_hooks_test.go | 3 +- x/fungible/keeper/evm_test.go | 643 +++++++++++++++++- x/fungible/keeper/gas_coin_and_pool.go | 1 - x/fungible/keeper/gas_coin_and_pool_test.go | 301 +++++++- x/fungible/keeper/gas_stability_pool_test.go | 17 + .../keeper/grpc_query_code_hash_test.go | 7 + .../grpc_query_gas_stability_pool_test.go | 142 ++++ .../keeper/grpc_query_system_contract_test.go | 40 ++ .../msg_server_deploy_system_contract_test.go | 132 +++- .../msg_server_update_system_contract_test.go | 5 + ...g_server_update_zrc20_withdraw_fee_test.go | 7 +- x/fungible/types/zrc20_test.go | 15 + x/observer/keeper/hooks_test.go | 30 +- x/observer/keeper/utils_test.go | 40 +- x/observer/types/expected_keepers.go | 1 - 20 files changed, 1352 insertions(+), 104 deletions(-) create mode 100644 x/fungible/keeper/grpc_query_gas_stability_pool_test.go create mode 100644 x/fungible/keeper/grpc_query_system_contract_test.go create mode 100644 x/fungible/types/zrc20_test.go diff --git a/changelog.md b/changelog.md index 4dde927265..f847d51c33 100644 --- a/changelog.md +++ b/changelog.md @@ -56,6 +56,7 @@ * [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 +* [1985](https://github.com/zeta-chain/node/pull/1985) - improve fungible module coverage ### Fixes diff --git a/testutil/keeper/mocks/observer/staking.go b/testutil/keeper/mocks/observer/staking.go index 72bf99599f..90007b6c35 100644 --- a/testutil/keeper/mocks/observer/staking.go +++ b/testutil/keeper/mocks/observer/staking.go @@ -71,11 +71,6 @@ 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/x/fungible/keeper/deposits_test.go b/x/fungible/keeper/deposits_test.go index 858a8130ce..dc69febd24 100644 --- a/x/fungible/keeper/deposits_test.go +++ b/x/fungible/keeper/deposits_test.go @@ -6,7 +6,9 @@ import ( "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/cmd/zetacored/config" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/testutil/contracts" @@ -352,3 +354,18 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { require.EqualValues(t, int64(0), balance.Int64()) }) } + +func TestKeeper_DepositCoinZeta(t *testing.T) { + k, ctx, sdkk, _ := testkeeper.FungibleKeeper(t) + to := sample.EthAddress() + amount := big.NewInt(1) + zetaToAddress := sdk.AccAddress(to.Bytes()) + + b := sdkk.BankKeeper.GetBalance(ctx, zetaToAddress, config.BaseDenom) + require.Equal(t, int64(0), b.Amount.Int64()) + + err := k.DepositCoinZeta(ctx, to, amount) + require.NoError(t, err) + b = sdkk.BankKeeper.GetBalance(ctx, zetaToAddress, config.BaseDenom) + require.Equal(t, amount.Int64(), b.Amount.Int64()) +} diff --git a/x/fungible/keeper/evm.go b/x/fungible/keeper/evm.go index 313a1301aa..427849f19c 100644 --- a/x/fungible/keeper/evm.go +++ b/x/fungible/keeper/evm.go @@ -54,7 +54,7 @@ func (k Keeper) DeployContract(ctx sdk.Context, metadata *bind.MetaData, ctorArg } if len(metadata.Bin) <= 2 { - return common.Address{}, cosmoserrors.Wrapf(types.ErrABIGet, "metadata Bin field too short: %s", err.Error()) + return common.Address{}, cosmoserrors.Wrapf(types.ErrABIGet, "metadata Bin field too short") } bin, err := hex.DecodeString(metadata.Bin[2:]) @@ -98,10 +98,6 @@ func (k Keeper) DeployZRC20Contract( if chain == nil { return common.Address{}, cosmoserrors.Wrapf(zetaObserverTypes.ErrSupportedChains, "chain %d not found", chainID) } - chainStr := chain.ChainName.String() - if chain == nil { - return common.Address{}, cosmoserrors.Wrapf(zetaObserverTypes.ErrSupportedChains, "chain %s not found", chainStr) - } // Check if Contract has already been deployed for Asset _, found := k.GetForeignCoinFromAsset(ctx, erc20Contract, chainID) if found { @@ -309,43 +305,6 @@ func (k Keeper) DepositZRC20AndCallContract(ctx sdk.Context, ) } -// QueryWithdrawGasFee returns the gas fee for a withdrawal transaction associated with a given zrc20 -func (k Keeper) QueryWithdrawGasFee(ctx sdk.Context, contract common.Address) (*big.Int, error) { - zrc20ABI, err := zrc20.ZRC20MetaData.GetAbi() - if err != nil { - return nil, err - } - res, err := k.CallEVM( - ctx, - *zrc20ABI, - types.ModuleAddressEVM, - contract, - BigIntZero, - nil, - false, - false, - "withdrawGasFee", - ) - if err != nil { - return nil, err - } - - unpacked, err := zrc20ABI.Unpack("withdrawGasFee", res.Ret) - if err != nil { - return nil, err - } - if len(unpacked) < 2 { - return nil, fmt.Errorf("expect 2 returned values, got %d", len(unpacked)) - } - - GasFee, ok := unpacked[1].(*big.Int) - if !ok { - return nil, errors.New("can't read returned value as big.Int") - } - - return GasFee, nil -} - // QueryProtocolFlatFee returns the protocol flat fee associated with a given zrc20 func (k Keeper) QueryProtocolFlatFee(ctx sdk.Context, contract common.Address) (*big.Int, error) { zrc20ABI, err := zrc20.ZRC20MetaData.GetAbi() diff --git a/x/fungible/keeper/evm_hooks.go b/x/fungible/keeper/evm_hooks.go index 80d4303089..4e3894290c 100644 --- a/x/fungible/keeper/evm_hooks.go +++ b/x/fungible/keeper/evm_hooks.go @@ -22,12 +22,12 @@ func (k Keeper) EVMHooks() EVMHooks { // PostTxProcessing is a wrapper for calling the EVM PostTxProcessing hook on the module keeper func (h EVMHooks) PostTxProcessing(ctx sdk.Context, _ core.Message, receipt *ethtypes.Receipt) error { - return h.k.CheckPausedZRC20(ctx, receipt) + return h.k.checkPausedZRC20(ctx, receipt) } -// CheckPausedZRC20 checks the events of the receipt +// checkPausedZRC20 checks the events of the receipt // if an event is emitted from a paused ZRC20 contract it will revert the transaction -func (k Keeper) CheckPausedZRC20(ctx sdk.Context, receipt *ethtypes.Receipt) error { +func (k Keeper) checkPausedZRC20(ctx sdk.Context, receipt *ethtypes.Receipt) error { if receipt == nil { return nil } diff --git a/x/fungible/keeper/evm_hooks_test.go b/x/fungible/keeper/evm_hooks_test.go index 696fbbd9cd..70bc486c0d 100644 --- a/x/fungible/keeper/evm_hooks_test.go +++ b/x/fungible/keeper/evm_hooks_test.go @@ -123,7 +123,8 @@ func TestKeeper_CheckPausedZRC20(t *testing.T) { assertPaused(addrPausedZRC20.Hex()) // process test - err := k.CheckPausedZRC20(ctx, tc.receipt) + h := k.EVMHooks() + err := h.PostTxProcessing(ctx, nil, tc.receipt) if tc.wantErr { require.ErrorIs(t, err, types.ErrPausedZRC20) } else { diff --git a/x/fungible/keeper/evm_test.go b/x/fungible/keeper/evm_test.go index 8d52742c0c..753d82e7e6 100644 --- a/x/fungible/keeper/evm_test.go +++ b/x/fungible/keeper/evm_test.go @@ -6,16 +6,20 @@ import ( "testing" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" evmtypes "github.com/evmos/ethermint/x/evm/types" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/systemcontract.sol" + "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/wzeta.sol" + zrc20 "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zrc20.sol" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/server/config" "github.com/zeta-chain/zetacore/testutil/contracts" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" testkeeper "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" fungiblekeeper "github.com/zeta-chain/zetacore/x/fungible/keeper" @@ -169,6 +173,71 @@ func assertExampleBarValue( } func TestKeeper_DeployZRC20Contract(t *testing.T) { + t.Run("should error if chain not found", func(t *testing.T) { + k, ctx, sdkk, _ := testkeeper.FungibleKeeper(t) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) + + addr, err := k.DeployZRC20Contract( + ctx, + "foo", + "bar", + 8, + 987, + coin.CoinType_Gas, + "foobar", + big.NewInt(1000), + ) + require.Error(t, err) + require.Empty(t, addr) + }) + + t.Run("should error if system contracts not deployed", func(t *testing.T) { + k, ctx, _, _ := testkeeper.FungibleKeeper(t) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + chainID := getValidChainID(t) + + addr, err := k.DeployZRC20Contract( + ctx, + "foo", + "bar", + 8, + chainID, + coin.CoinType_Gas, + "foobar", + big.NewInt(1000), + ) + require.Error(t, err) + require.Empty(t, addr) + }) + + t.Run("should error if deploy contract fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + deploySystemContractsWithMockEvmKeeper(t, ctx, k, mockEVMKeeper) + chainID := getValidChainID(t) + mockFailedContractDeployment(ctx, t, k) + + addr, err := k.DeployZRC20Contract( + ctx, + "foo", + "bar", + 8, + chainID, + coin.CoinType_Gas, + "foobar", + big.NewInt(1000), + ) + require.Error(t, err) + require.Empty(t, addr) + }) + t.Run("can deploy the zrc20 contract", func(t *testing.T) { k, ctx, sdkk, _ := testkeeper.FungibleKeeper(t) _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) @@ -225,7 +294,61 @@ func TestKeeper_DeployZRC20Contract(t *testing.T) { }) } -func TestKeeper_DeploySystemContract(t *testing.T) { +func TestKeeper_DeploySystemContracts(t *testing.T) { + t.Run("system contract deployment should error if deploy contract fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + wzeta, uniswapV2Factory, uniswapV2Router, _, _ := deploySystemContractsWithMockEvmKeeper(t, ctx, k, mockEVMKeeper) + mockFailedContractDeployment(ctx, t, k) + + res, err := k.DeploySystemContract(ctx, wzeta, uniswapV2Factory, uniswapV2Router) + require.Error(t, err) + require.Empty(t, res) + }) + + t.Run("router deployment should error if deploy contract fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockFailedContractDeployment(ctx, t, k) + + res, err := k.DeployUniswapV2Router02(ctx, sample.EthAddress(), sample.EthAddress()) + require.Error(t, err) + require.Empty(t, res) + }) + + t.Run("wzeta deployment should error if deploy contract fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockFailedContractDeployment(ctx, t, k) + + res, err := k.DeployWZETA(ctx) + require.Error(t, err) + require.Empty(t, res) + }) + + t.Run("connector deployment should error if deploy contract fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockFailedContractDeployment(ctx, t, k) + + res, err := k.DeployConnectorZEVM(ctx, sample.EthAddress()) + require.Error(t, err) + require.Empty(t, res) + }) + t.Run("can deploy the system contracts", func(t *testing.T) { k, ctx, sdkk, _ := testkeeper.FungibleKeeper(t) _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) @@ -280,6 +403,32 @@ func TestKeeper_DeploySystemContract(t *testing.T) { } func TestKeeper_DepositZRC20AndCallContract(t *testing.T) { + t.Run("should error if system contracts not deployed", func(t *testing.T) { + k, ctx, sdkk, _ := testkeeper.FungibleKeeper(t) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + chainID := getValidChainID(t) + + example, err := k.DeployContract(ctx, contracts.ExampleMetaData) + require.NoError(t, err) + assertContractDeployment(t, sdkk.EvmKeeper, ctx, example) + + res, err := k.DepositZRC20AndCallContract( + ctx, + systemcontract.ZContext{ + Origin: sample.EthAddress().Bytes(), + Sender: sample.EthAddress(), + ChainID: big.NewInt(chainID), + }, + sample.EthAddress(), + example, + big.NewInt(42), + []byte(""), + ) + require.Error(t, err) + require.Nil(t, res) + }) + t.Run("should deposit and call the contract", func(t *testing.T) { k, ctx, sdkk, _ := testkeeper.FungibleKeeper(t) _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) @@ -685,3 +834,495 @@ func TestKeeper_CallEVMWithData(t *testing.T) { require.ErrorIs(t, err, sample.ErrSample) }) } + +func TestKeeper_DeployContract(t *testing.T) { + t.Run("should error if pack ctor args fails", func(t *testing.T) { + k, ctx, _, _ := testkeeper.FungibleKeeper(t) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + addr, err := k.DeployContract(ctx, zrc20.ZRC20MetaData, "") + require.ErrorIs(t, err, types.ErrABIGet) + require.Empty(t, addr) + }) + + t.Run("should error if metadata bin empty", func(t *testing.T) { + k, ctx, _, _ := testkeeper.FungibleKeeper(t) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + metadata := &bind.MetaData{ + ABI: wzeta.WETH9MetaData.ABI, + Bin: "", + } + addr, err := k.DeployContract(ctx, metadata) + require.ErrorIs(t, err, types.ErrABIGet) + require.Empty(t, addr) + }) + + t.Run("should error if metadata cant be decoded", func(t *testing.T) { + k, ctx, _, _ := testkeeper.FungibleKeeper(t) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + metadata := &bind.MetaData{ + ABI: wzeta.WETH9MetaData.ABI, + Bin: "0x1", + } + addr, err := k.DeployContract(ctx, metadata) + require.ErrorIs(t, err, types.ErrABIPack) + require.Empty(t, addr) + }) + + t.Run("should error if module acc not set up", func(t *testing.T) { + k, ctx, _, _ := testkeeper.FungibleKeeper(t) + addr, err := k.DeployContract(ctx, wzeta.WETH9MetaData) + require.Error(t, err) + require.Empty(t, addr) + }) +} + +func TestKeeper_QueryProtocolFlatFee(t *testing.T) { + t.Run("should error if evm call fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + mockEVMKeeper.MockEVMFailCallOnce() + + res, err := k.QueryProtocolFlatFee(ctx, sample.EthAddress()) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if unpack fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: []byte{}}) + + res, err := k.QueryProtocolFlatFee(ctx, sample.EthAddress()) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should return fee", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + + zrc20ABI, err := zrc20.ZRC20MetaData.GetAbi() + require.NoError(t, err) + fee := big.NewInt(42) + protocolFlatFee, err := zrc20ABI.Methods["PROTOCOL_FLAT_FEE"].Outputs.Pack(fee) + require.NoError(t, err) + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: protocolFlatFee}) + + res, err := k.QueryProtocolFlatFee(ctx, sample.EthAddress()) + require.NoError(t, err) + require.Equal(t, fee, res) + }) +} + +func TestKeeper_QueryGasLimit(t *testing.T) { + t.Run("should error if evm call fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + mockEVMKeeper.MockEVMFailCallOnce() + + res, err := k.QueryGasLimit(ctx, sample.EthAddress()) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if unpack fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: []byte{}}) + + res, err := k.QueryGasLimit(ctx, sample.EthAddress()) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should return gas limit", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + + zrc20ABI, err := zrc20.ZRC20MetaData.GetAbi() + require.NoError(t, err) + limit := big.NewInt(42) + gasLimit, err := zrc20ABI.Methods["GAS_LIMIT"].Outputs.Pack(limit) + require.NoError(t, err) + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: gasLimit}) + + res, err := k.QueryGasLimit(ctx, sample.EthAddress()) + require.NoError(t, err) + require.Equal(t, limit, res) + }) +} + +func TestKeeper_QueryChainIDFromContract(t *testing.T) { + t.Run("should error if evm call fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + mockEVMKeeper.MockEVMFailCallOnce() + + res, err := k.QueryChainIDFromContract(ctx, sample.EthAddress()) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if unpack fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: []byte{}}) + + res, err := k.QueryChainIDFromContract(ctx, sample.EthAddress()) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should return chain id", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + + zrc20ABI, err := zrc20.ZRC20MetaData.GetAbi() + require.NoError(t, err) + chainId := big.NewInt(42) + chainIdFromContract, err := zrc20ABI.Methods["GAS_LIMIT"].Outputs.Pack(chainId) + require.NoError(t, err) + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: chainIdFromContract}) + + res, err := k.QueryChainIDFromContract(ctx, sample.EthAddress()) + require.NoError(t, err) + require.Equal(t, chainId, res) + }) +} + +func TestKeeper_TotalSupplyZRC4(t *testing.T) { + t.Run("should error if evm call fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + mockEVMKeeper.MockEVMFailCallOnce() + + res, err := k.TotalSupplyZRC4(ctx, sample.EthAddress()) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if unpack fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: []byte{}}) + + res, err := k.TotalSupplyZRC4(ctx, sample.EthAddress()) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should return total supply", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + + zrc20ABI, err := zrc20.ZRC20MetaData.GetAbi() + require.NoError(t, err) + supply := big.NewInt(42) + supplyFromContract, err := zrc20ABI.Methods["GAS_LIMIT"].Outputs.Pack(supply) + require.NoError(t, err) + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: supplyFromContract}) + + res, err := k.TotalSupplyZRC4(ctx, sample.EthAddress()) + require.NoError(t, err) + require.Equal(t, supply, res) + }) +} + +func TestKeeper_BalanceOfZRC4(t *testing.T) { + t.Run("should error if evm call fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + mockEVMKeeper.MockEVMFailCallOnce() + + res, err := k.BalanceOfZRC4(ctx, sample.EthAddress(), sample.EthAddress()) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if unpack fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: []byte{}}) + + res, err := k.BalanceOfZRC4(ctx, sample.EthAddress(), sample.EthAddress()) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should return balance", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + + zrc20ABI, err := zrc20.ZRC20MetaData.GetAbi() + require.NoError(t, err) + balance := big.NewInt(42) + balanceFromContract, err := zrc20ABI.Methods["GAS_LIMIT"].Outputs.Pack(balance) + require.NoError(t, err) + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: balanceFromContract}) + + res, err := k.BalanceOfZRC4(ctx, sample.EthAddress(), sample.EthAddress()) + require.NoError(t, err) + require.Equal(t, balance, res) + }) +} + +func TestKeeper_QueryZRC20Data(t *testing.T) { + t.Run("should error if evm call fails for name", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + mockEVMKeeper.MockEVMFailCallOnce() + + res, err := k.QueryZRC20Data(ctx, sample.EthAddress()) + require.Error(t, err) + require.Empty(t, res) + }) + + t.Run("should error if unpack fails for name", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: []byte{}}) + + res, err := k.QueryZRC20Data(ctx, sample.EthAddress()) + require.Error(t, err) + require.Empty(t, res) + }) + + t.Run("should error if evm call fails for symbol", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + + zrc4ABI, err := zrc20.ZRC20MetaData.GetAbi() + require.NoError(t, err) + name, err := zrc4ABI.Methods["name"].Outputs.Pack("name") + require.NoError(t, err) + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: name}) + + mockEVMKeeper.MockEVMFailCallOnce() + + res, err := k.QueryZRC20Data(ctx, sample.EthAddress()) + require.Error(t, err) + require.Empty(t, res) + }) + + t.Run("should error if unpack for symbol", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + + zrc4ABI, err := zrc20.ZRC20MetaData.GetAbi() + require.NoError(t, err) + name, err := zrc4ABI.Methods["name"].Outputs.Pack("name") + require.NoError(t, err) + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: name}) + + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: []byte{}}) + + res, err := k.QueryZRC20Data(ctx, sample.EthAddress()) + require.Error(t, err) + require.Empty(t, res) + }) + + t.Run("should error if evm call fails for decimals", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + + zrc4ABI, err := zrc20.ZRC20MetaData.GetAbi() + require.NoError(t, err) + name, err := zrc4ABI.Methods["name"].Outputs.Pack("name") + require.NoError(t, err) + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: name}) + + symbol, err := zrc4ABI.Methods["symbol"].Outputs.Pack("symbol") + require.NoError(t, err) + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: symbol}) + + mockEVMKeeper.MockEVMFailCallOnce() + + res, err := k.QueryZRC20Data(ctx, sample.EthAddress()) + require.Error(t, err) + require.Empty(t, res) + }) + + t.Run("should error if unpack fails for decimals", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + + zrc4ABI, err := zrc20.ZRC20MetaData.GetAbi() + require.NoError(t, err) + name, err := zrc4ABI.Methods["name"].Outputs.Pack("name") + require.NoError(t, err) + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: name}) + + symbol, err := zrc4ABI.Methods["symbol"].Outputs.Pack("symbol") + require.NoError(t, err) + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: symbol}) + + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: []byte{}}) + + res, err := k.QueryZRC20Data(ctx, sample.EthAddress()) + require.Error(t, err) + require.Empty(t, res) + }) + + t.Run("should return zrc20 data", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + + zrc4ABI, err := zrc20.ZRC20MetaData.GetAbi() + require.NoError(t, err) + name, err := zrc4ABI.Methods["name"].Outputs.Pack("name") + require.NoError(t, err) + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: name}) + + symbol, err := zrc4ABI.Methods["symbol"].Outputs.Pack("symbol") + require.NoError(t, err) + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: symbol}) + + decimals, err := zrc4ABI.Methods["decimals"].Outputs.Pack(uint8(8)) + require.NoError(t, err) + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: decimals}) + + res, err := k.QueryZRC20Data(ctx, sample.EthAddress()) + require.NoError(t, err) + require.Equal(t, uint8(8), res.Decimals) + require.Equal(t, "name", res.Name) + require.Equal(t, "symbol", res.Symbol) + }) +} diff --git a/x/fungible/keeper/gas_coin_and_pool.go b/x/fungible/keeper/gas_coin_and_pool.go index 6b06cd8cec..60cc436965 100644 --- a/x/fungible/keeper/gas_coin_and_pool.go +++ b/x/fungible/keeper/gas_coin_and_pool.go @@ -125,7 +125,6 @@ func (k Keeper) SetupChainGasCoinAndPool( err = routerABI.UnpackIntoInterface(&[]interface{}{AmountToken, AmountETH, Liquidity}, "addLiquidityETH", res.Ret) if err != nil { return ethcommon.Address{}, cosmoserrors.Wrapf(err, "failed to UnpackIntoInterface addLiquidityETH") - } ctx.EventManager().EmitEvent( sdk.NewEvent(sdk.EventTypeMessage, diff --git a/x/fungible/keeper/gas_coin_and_pool_test.go b/x/fungible/keeper/gas_coin_and_pool_test.go index 9935f11ee6..9ee6a9d17f 100644 --- a/x/fungible/keeper/gas_coin_and_pool_test.go +++ b/x/fungible/keeper/gas_coin_and_pool_test.go @@ -1,18 +1,23 @@ package keeper_test import ( + "errors" "math/big" "testing" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ethereum/go-ethereum/common" evmkeeper "github.com/evmos/ethermint/x/evm/keeper" + evmtypes "github.com/evmos/ethermint/x/evm/types" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/systemcontract.sol" uniswapv2router02 "github.com/zeta-chain/protocol-contracts/pkg/uniswap/v2-periphery/contracts/uniswapv2router02.sol" "github.com/zeta-chain/zetacore/cmd/zetacored/config" - testkeeper "github.com/zeta-chain/zetacore/testutil/keeper" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" fungiblekeeper "github.com/zeta-chain/zetacore/x/fungible/keeper" "github.com/zeta-chain/zetacore/x/fungible/types" ) @@ -134,7 +139,7 @@ func setupZRC20Pool( func TestKeeper_SetupChainGasCoinAndPool(t *testing.T) { t.Run("can setup a new chain gas coin", func(t *testing.T) { - k, ctx, sdkk, _ := testkeeper.FungibleKeeper(t) + k, ctx, sdkk, _ := keepertest.FungibleKeeper(t) _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) chainID := getValidChainID(t) @@ -149,4 +154,296 @@ func TestKeeper_SetupChainGasCoinAndPool(t *testing.T) { require.NoError(t, err) require.Equal(t, zrc20, found) }) + + t.Run("should error if system contracts not deployed", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeper(t) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + chainID := getValidChainID(t) + + addr, err := k.SetupChainGasCoinAndPool( + ctx, + chainID, + "test", + "test", + 8, + nil, + ) + require.Error(t, err) + require.Empty(t, addr) + }) + + t.Run("should error if mint coins fails", func(t *testing.T) { + k, ctx, sdkk, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseBankMock: true, + }) + bankMock := keepertest.GetFungibleBankMock(t, k) + bankMock.On("MintCoins", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(errors.New("err")) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + chainID := getValidChainID(t) + + deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) + + addr, err := k.SetupChainGasCoinAndPool( + ctx, + chainID, + "test", + "test", + 8, + nil, + ) + require.Error(t, err) + require.Empty(t, addr) + }) + + t.Run("should error if set gas coin fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + chainID := getValidChainID(t) + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + deploySystemContractsWithMockEvmKeeper(t, ctx, k, mockEVMKeeper) + + // deployZrc20 success + mockSuccessfulContractDeployment(ctx, t, k) + + // setGasCoin fail + mockEVMKeeper.MockEVMFailCallOnce() + + addr, err := k.SetupChainGasCoinAndPool( + ctx, + chainID, + "test", + "test", + 8, + nil, + ) + require.Error(t, err) + require.Empty(t, addr) + }) + + t.Run("should error if deposit zrc20 fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + chainID := getValidChainID(t) + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + deploySystemContractsWithMockEvmKeeper(t, ctx, k, mockEVMKeeper) + + // deployZrc20 success + mockSuccessfulContractDeployment(ctx, t, k) + + // setGasCoin success + mockEVMKeeper.MockEVMSuccessCallOnce() + + // depositZrc20 fails + mockEVMKeeper.MockEVMFailCallOnce() + + addr, err := k.SetupChainGasCoinAndPool( + ctx, + chainID, + "test", + "test", + 8, + nil, + ) + require.Error(t, err) + require.Empty(t, addr) + }) + + t.Run("should error if set gas pool call fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + chainID := getValidChainID(t) + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + deploySystemContractsWithMockEvmKeeper(t, ctx, k, mockEVMKeeper) + + // deployZrc20 success + mockSuccessfulContractDeployment(ctx, t, k) + + // setGasCoin success + // depositZrc20 success + mockEVMKeeper.MockEVMSuccessCallTimes(2) + + // set gas pool call fail + mockEVMKeeper.MockEVMFailCallOnce() + + addr, err := k.SetupChainGasCoinAndPool( + ctx, + chainID, + "test", + "test", + 8, + nil, + ) + require.Error(t, err) + require.Empty(t, addr) + }) + + t.Run("should error if get uniswap router fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + chainID := getValidChainID(t) + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + deploySystemContractsWithMockEvmKeeper(t, ctx, k, mockEVMKeeper) + + // deployZrc20 success + mockSuccessfulContractDeployment(ctx, t, k) + + // setGasCoin success + // depositZrc20 success + // set gas pool call success + mockEVMKeeper.MockEVMSuccessCallTimes(3) + + // get uniswap router fails + mockEVMKeeper.MockEVMFailCallOnce() + + addr, err := k.SetupChainGasCoinAndPool( + ctx, + chainID, + "test", + "test", + 8, + nil, + ) + require.Error(t, err) + require.Empty(t, addr) + }) + + t.Run("should error if approve fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + chainID := getValidChainID(t) + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + deploySystemContractsWithMockEvmKeeper(t, ctx, k, mockEVMKeeper) + + // deployZrc20 success + mockSuccessfulContractDeployment(ctx, t, k) + + // setGasCoin success + // depositZrc20 success + // set gas pool call success + mockEVMKeeper.MockEVMSuccessCallTimes(3) + + // get uniswap router success + sysABI, err := systemcontract.SystemContractMetaData.GetAbi() + require.NoError(t, err) + routerAddr, err := sysABI.Methods["uniswapv2Router02Address"].Outputs.Pack(sample.EthAddress()) + require.NoError(t, err) + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: routerAddr}) + + // get approve fails + mockEVMKeeper.MockEVMFailCallOnce() + + addr, err := k.SetupChainGasCoinAndPool( + ctx, + chainID, + "test", + "test", + 8, + nil, + ) + require.Error(t, err) + require.Empty(t, addr) + }) + + t.Run("should error if add liquidity fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + chainID := getValidChainID(t) + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + deploySystemContractsWithMockEvmKeeper(t, ctx, k, mockEVMKeeper) + + // deployZrc20 success + mockSuccessfulContractDeployment(ctx, t, k) + + // setGasCoin success + // depositZrc20 success + // set gas pool call success + mockEVMKeeper.MockEVMSuccessCallTimes(3) + + // get uniswap router success + sysABI, err := systemcontract.SystemContractMetaData.GetAbi() + require.NoError(t, err) + routerAddr, err := sysABI.Methods["uniswapv2Router02Address"].Outputs.Pack(sample.EthAddress()) + require.NoError(t, err) + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: routerAddr}) + + // get approve success + mockEVMKeeper.MockEVMSuccessCallOnce() + + // add liquidity fails + mockEVMKeeper.MockEVMFailCallOnce() + + addr, err := k.SetupChainGasCoinAndPool( + ctx, + chainID, + "test", + "test", + 8, + nil, + ) + require.Error(t, err) + require.Empty(t, addr) + }) + + t.Run("should error if add liquidity fails to unpack", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseEVMMock: true, + }) + k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + chainID := getValidChainID(t) + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + deploySystemContractsWithMockEvmKeeper(t, ctx, k, mockEVMKeeper) + + // deployZrc20 success + mockSuccessfulContractDeployment(ctx, t, k) + + // setGasCoin success + // depositZrc20 success + // set gas pool call success + mockEVMKeeper.MockEVMSuccessCallTimes(3) + + // get uniswap router success + sysABI, err := systemcontract.SystemContractMetaData.GetAbi() + require.NoError(t, err) + routerAddr, err := sysABI.Methods["uniswapv2Router02Address"].Outputs.Pack(sample.EthAddress()) + require.NoError(t, err) + mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: routerAddr}) + + // get approve success + mockEVMKeeper.MockEVMSuccessCallOnce() + + // add liquidity success + mockEVMKeeper.MockEVMSuccessCallOnce() + + addr, err := k.SetupChainGasCoinAndPool( + ctx, + chainID, + "test", + "test", + 8, + nil, + ) + require.Error(t, err) + require.Empty(t, addr) + }) } diff --git a/x/fungible/keeper/gas_stability_pool_test.go b/x/fungible/keeper/gas_stability_pool_test.go index 1c9bbc7b12..d05e9aee19 100644 --- a/x/fungible/keeper/gas_stability_pool_test.go +++ b/x/fungible/keeper/gas_stability_pool_test.go @@ -34,6 +34,23 @@ func TestKeeper_EnsureGasStabilityPoolAccountCreated(t *testing.T) { } func TestKeeper_FundGasStabilityPool(t *testing.T) { + t.Run("should error if system contracts not deployed", func(t *testing.T) { + k, ctx, _, _ := testkeeper.FungibleKeeper(t) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + chainID := getValidChainID(t) + + balance, err := k.GetGasStabilityPoolBalance(ctx, chainID) + require.Error(t, err) + require.Nil(t, balance) + + err = k.FundGasStabilityPool(ctx, chainID, big.NewInt(100)) + require.Error(t, err) + + err = k.WithdrawFromGasStabilityPool(ctx, chainID, big.NewInt(50)) + require.Error(t, err) + }) + t.Run("can fund the gas stability pool and withdraw", func(t *testing.T) { k, ctx, sdkk, _ := testkeeper.FungibleKeeper(t) _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) diff --git a/x/fungible/keeper/grpc_query_code_hash_test.go b/x/fungible/keeper/grpc_query_code_hash_test.go index e2359dc530..5a31f5c736 100644 --- a/x/fungible/keeper/grpc_query_code_hash_test.go +++ b/x/fungible/keeper/grpc_query_code_hash_test.go @@ -11,6 +11,13 @@ import ( ) func TestKeeper_CodeHash(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeper(t) + res, err := k.CodeHash(ctx, nil) + require.Error(t, err) + require.Nil(t, res) + }) + t.Run("should return code hash", func(t *testing.T) { k, ctx, sdkk, _ := keepertest.FungibleKeeper(t) k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) diff --git a/x/fungible/keeper/grpc_query_gas_stability_pool_test.go b/x/fungible/keeper/grpc_query_gas_stability_pool_test.go new file mode 100644 index 0000000000..7dfe91d530 --- /dev/null +++ b/x/fungible/keeper/grpc_query_gas_stability_pool_test.go @@ -0,0 +1,142 @@ +package keeper_test + +import ( + "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/x/fungible/types" +) + +func TestKeeper_GasStabilityPoolAddress(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeper(t) + res, err := k.GasStabilityPoolAddress(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.FungibleKeeper(t) + res, err := k.GasStabilityPoolAddress(ctx, &types.QueryGetGasStabilityPoolAddress{}) + require.NoError(t, err) + require.Equal(t, &types.QueryGetGasStabilityPoolAddressResponse{ + CosmosAddress: types.GasStabilityPoolAddress().String(), + EvmAddress: types.GasStabilityPoolAddressEVM().String(), + }, res) + }) +} + +func TestKeeper_GasStabilityPoolBalance(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeper(t) + res, err := k.GasStabilityPoolBalance(ctx, nil) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if system contracts not deployed", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeper(t) + chainID := 5 + + res, err := k.GasStabilityPoolBalance(ctx, &types.QueryGetGasStabilityPoolBalance{ + ChainId: int64(chainID), + }) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should return balance", func(t *testing.T) { + k, ctx, sdkk, _ := keepertest.FungibleKeeper(t) + chainID := 5 + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) + setupGasCoin(t, ctx, k, sdkk.EvmKeeper, int64(chainID), "foobar", "foobar") + + res, err := k.GasStabilityPoolBalance(ctx, &types.QueryGetGasStabilityPoolBalance{ + ChainId: int64(chainID), + }) + require.NoError(t, err) + require.Equal(t, "0", res.Balance) + }) + +} + +func TestKeeper_GasStabilityPoolBalanceAll(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeper(t) + res, err := k.GasStabilityPoolBalanceAll(ctx, nil) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should return empty balances if no supported chains", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseObserverMock: true, + }) + observerMock := keepertest.GetFungibleObserverMock(t, k) + observerMock.On("GetSupportedChains", mock.Anything).Return([]*chains.Chain{}) + + res, err := k.GasStabilityPoolBalanceAll(ctx, &types.QueryAllGasStabilityPoolBalance{}) + require.NoError(t, err) + require.Empty(t, res.Balances) + }) + + t.Run("should error if chain is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseObserverMock: true, + }) + observerMock := keepertest.GetFungibleObserverMock(t, k) + observerMock.On("GetSupportedChains", mock.Anything).Return([]*chains.Chain{nil}) + + res, err := k.GasStabilityPoolBalanceAll(ctx, &types.QueryAllGasStabilityPoolBalance{}) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if system contracts not deployed", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseObserverMock: true, + }) + observerMock := keepertest.GetFungibleObserverMock(t, k) + chainID := 5 + observerMock.On("GetSupportedChains", mock.Anything).Return([]*chains.Chain{ + { + ChainName: chains.ChainName(chainID), + ChainId: int64(chainID), + }, + }) + + res, err := k.GasStabilityPoolBalanceAll(ctx, &types.QueryAllGasStabilityPoolBalance{}) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should return balances", func(t *testing.T) { + k, ctx, sdkk, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseObserverMock: true, + }) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + observerMock := keepertest.GetFungibleObserverMock(t, k) + chainID := 5 + observerMock.On("GetSupportedChains", mock.Anything).Return([]*chains.Chain{ + { + ChainName: chains.ChainName(chainID), + ChainId: int64(chainID), + }, + }) + + deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) + setupGasCoin(t, ctx, k, sdkk.EvmKeeper, int64(chainID), "foobar", "foobar") + + res, err := k.GasStabilityPoolBalanceAll(ctx, &types.QueryAllGasStabilityPoolBalance{}) + require.NoError(t, err) + require.Len(t, res.Balances, 1) + require.Equal(t, int64(chainID), res.Balances[0].ChainId) + require.Equal(t, "0", res.Balances[0].Balance) + }) +} diff --git a/x/fungible/keeper/grpc_query_system_contract_test.go b/x/fungible/keeper/grpc_query_system_contract_test.go new file mode 100644 index 0000000000..849e2c87de --- /dev/null +++ b/x/fungible/keeper/grpc_query_system_contract_test.go @@ -0,0 +1,40 @@ +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" + "github.com/zeta-chain/zetacore/x/fungible/types" +) + +func TestKeeper_SystemContract(t *testing.T) { + t.Run("should error if req is nil", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeper(t) + res, err := k.SystemContract(ctx, nil) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should error if system contract not found", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeper(t) + res, err := k.SystemContract(ctx, &types.QueryGetSystemContractRequest{}) + require.Error(t, err) + require.Nil(t, res) + }) + + t.Run("should return system contract if found", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeper(t) + sc := types.SystemContract{ + SystemContract: sample.EthAddress().Hex(), + ConnectorZevm: sample.EthAddress().Hex(), + } + k.SetSystemContract(ctx, sc) + res, err := k.SystemContract(ctx, &types.QueryGetSystemContractRequest{}) + require.NoError(t, err) + require.Equal(t, &types.QueryGetSystemContractResponse{ + SystemContract: sc, + }, res) + }) +} diff --git a/x/fungible/keeper/msg_server_deploy_system_contract_test.go b/x/fungible/keeper/msg_server_deploy_system_contract_test.go index 168fe6ca65..06cb2d29cd 100644 --- a/x/fungible/keeper/msg_server_deploy_system_contract_test.go +++ b/x/fungible/keeper/msg_server_deploy_system_contract_test.go @@ -1,10 +1,12 @@ package keeper_test import ( - "errors" + "context" + "math/big" "testing" 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" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" @@ -53,7 +55,7 @@ func TestMsgServer_DeploySystemContracts(t *testing.T) { require.ErrorIs(t, err, authoritytypes.ErrUnauthorized) }) - t.Run("should fail if contract deployment fails", func(t *testing.T) { + t.Run("should fail if uniswapv2factory contract deployment fails", func(t *testing.T) { k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ UseAuthorityMock: true, UseEVMMock: true, @@ -65,15 +67,129 @@ func TestMsgServer_DeploySystemContracts(t *testing.T) { authorityMock := keepertest.GetFungibleAuthorityMock(t, k) keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) - mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) - mockEVMKeeper.On( - "EstimateGas", - mock.Anything, - mock.Anything, - ).Return(nil, errors.New("failed to estimate gas")) + // mock failed uniswapv2factory deployment + mockFailedContractDeployment(ctx, t, k) _, err := msgServer.DeploySystemContracts(ctx, types.NewMsgDeploySystemContracts(admin)) require.Error(t, err) require.Contains(t, err.Error(), "failed to deploy") }) + + t.Run("should fail if wzeta contract deployment fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + UseEVMMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + admin := sample.AccAddress() + + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) + + // mock successful uniswapv2factory deployment + mockSuccessfulContractDeployment(ctx, t, k) + // mock failed wzeta deployment deployment + mockFailedContractDeployment(ctx, t, k) + + _, err := msgServer.DeploySystemContracts(ctx, types.NewMsgDeploySystemContracts(admin)) + require.Error(t, err) + require.Contains(t, err.Error(), "failed to deploy") + }) + + t.Run("should fail if uniswapv2router deployment fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + UseEVMMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + admin := sample.AccAddress() + + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) + + // mock successful uniswapv2factory and wzeta deployments + mockSuccessfulContractDeployment(ctx, t, k) + mockSuccessfulContractDeployment(ctx, t, k) + // mock failed uniswapv2router deployment + mockFailedContractDeployment(ctx, t, k) + + _, err := msgServer.DeploySystemContracts(ctx, types.NewMsgDeploySystemContracts(admin)) + require.Error(t, err) + require.Contains(t, err.Error(), "failed to deploy") + }) + + t.Run("should fail if connectorzevm deployment fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + UseEVMMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + admin := sample.AccAddress() + + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) + + // mock successful uniswapv2factory, wzeta and uniswapv2router deployments + mockSuccessfulContractDeployment(ctx, t, k) + mockSuccessfulContractDeployment(ctx, t, k) + mockSuccessfulContractDeployment(ctx, t, k) + // mock failed connectorzevm deployment + mockFailedContractDeployment(ctx, t, k) + + _, err := msgServer.DeploySystemContracts(ctx, types.NewMsgDeploySystemContracts(admin)) + require.Error(t, err) + require.Contains(t, err.Error(), "failed to deploy") + }) + + t.Run("should fail if system contract deployment fails", func(t *testing.T) { + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + UseEVMMock: true, + }) + msgServer := keeper.NewMsgServerImpl(*k) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + admin := sample.AccAddress() + + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + keepertest.MockIsAuthorized(&authorityMock.Mock, admin, authoritytypes.PolicyType_groupOperational, true) + + // mock successful uniswapv2factory, wzeta, uniswapv2router and connectorzevm deployments + mockSuccessfulContractDeployment(ctx, t, k) + mockSuccessfulContractDeployment(ctx, t, k) + mockSuccessfulContractDeployment(ctx, t, k) + mockSuccessfulContractDeployment(ctx, t, k) + // mock failed system contract deployment + mockFailedContractDeployment(ctx, t, k) + + _, err := msgServer.DeploySystemContracts(ctx, types.NewMsgDeploySystemContracts(admin)) + require.Error(t, err) + require.Contains(t, err.Error(), "failed to deploy") + }) +} + +func mockSuccessfulContractDeployment(ctx context.Context, t *testing.T, k *keeper.Keeper) { + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + mockEVMKeeper.On( + "EstimateGas", + mock.Anything, + mock.Anything, + ).Return(&evmtypes.EstimateGasResponse{Gas: 5}, nil) + mockEVMKeeper.MockEVMSuccessCallOnce() +} + +func mockFailedContractDeployment(ctx context.Context, t *testing.T, k *keeper.Keeper) { + mockEVMKeeper := keepertest.GetFungibleEVMMock(t, k) + mockEVMKeeper.On("WithChainID", mock.Anything).Maybe().Return(ctx) + mockEVMKeeper.On("ChainID").Maybe().Return(big.NewInt(1)) + mockEVMKeeper.On( + "EstimateGas", + mock.Anything, + mock.Anything, + ).Return(&evmtypes.EstimateGasResponse{Gas: 5}, nil) + mockEVMKeeper.MockEVMFailCallOnce() } diff --git a/x/fungible/keeper/msg_server_update_system_contract_test.go b/x/fungible/keeper/msg_server_update_system_contract_test.go index e64c5ea640..2d1cd30c56 100644 --- a/x/fungible/keeper/msg_server_update_system_contract_test.go +++ b/x/fungible/keeper/msg_server_update_system_contract_test.go @@ -53,6 +53,11 @@ func TestKeeper_UpdateSystemContract(t *testing.T) { wzeta, factory, router, _, oldSystemContract := deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) gas1 := setupGasCoin(t, ctx, k, sdkk.EvmKeeper, chainID1, "foo", "foo") gas2 := setupGasCoin(t, ctx, k, sdkk.EvmKeeper, chainID2, "bar", "bar") + // this one should be skipped and not impact update + fc := types.ForeignCoins{ + Zrc20ContractAddress: "0x", + } + k.SetForeignCoins(ctx, fc) // deploy a new system contracts newSystemContract, err := k.DeployContract(ctx, systemcontract.SystemContractMetaData, wzeta, factory, router) diff --git a/x/fungible/keeper/msg_server_update_zrc20_withdraw_fee_test.go b/x/fungible/keeper/msg_server_update_zrc20_withdraw_fee_test.go index e832f69e24..bef609a10e 100644 --- a/x/fungible/keeper/msg_server_update_zrc20_withdraw_fee_test.go +++ b/x/fungible/keeper/msg_server_update_zrc20_withdraw_fee_test.go @@ -227,7 +227,7 @@ func TestKeeper_UpdateZRC20WithdrawFee(t *testing.T) { mockEVMKeeper.AssertExpectations(t) }) - t.Run("should fail if contract call for setting new protocol fee fails", func(t *testing.T) { + t.Run("should fail if query gas limit fails", func(t *testing.T) { k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ UseEVMMock: true, UseAuthorityMock: true, @@ -259,11 +259,8 @@ func TestKeeper_UpdateZRC20WithdrawFee(t *testing.T) { require.NoError(t, err) mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: protocolFlatFee}) - gasLimit, err := zrc20ABI.Methods["GAS_LIMIT"].Outputs.Pack(big.NewInt(42)) + _, err = zrc20ABI.Methods["GAS_LIMIT"].Outputs.Pack(big.NewInt(42)) require.NoError(t, err) - mockEVMKeeper.MockEVMSuccessCallOnceWithReturn(&evmtypes.MsgEthereumTxResponse{Ret: gasLimit}) - - // this is the update call (commit == true) mockEVMKeeper.MockEVMFailCallOnce() _, err = msgServer.UpdateZRC20WithdrawFee(ctx, types.NewMsgUpdateZRC20WithdrawFee( diff --git a/x/fungible/types/zrc20_test.go b/x/fungible/types/zrc20_test.go new file mode 100644 index 0000000000..fcb248753f --- /dev/null +++ b/x/fungible/types/zrc20_test.go @@ -0,0 +1,15 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/x/fungible/types" +) + +func TestNewZRC20Data(t *testing.T) { + zrc20 := types.NewZRC20Data("name", "symbol", 8) + require.Equal(t, "name", zrc20.Name) + require.Equal(t, "symbol", zrc20.Symbol) + require.Equal(t, uint8(8), zrc20.Decimals) +} diff --git a/x/observer/keeper/hooks_test.go b/x/observer/keeper/hooks_test.go index ec782663ee..0727b17c71 100644 --- a/x/observer/keeper/hooks_test.go +++ b/x/observer/keeper/hooks_test.go @@ -48,16 +48,16 @@ func TestKeeper_AfterValidatorRemoved(t *testing.T) { } func TestKeeper_AfterValidatorBeginUnbonding(t *testing.T) { - k, ctx, _, _ := keepertest.ObserverKeeper(t) + k, ctx, sdkk, _ := keepertest.ObserverKeeper(t) r := rand.New(rand.NewSource(9)) validator := sample.Validator(t, r) validator.DelegatorShares = sdk.NewDec(100) - k.GetStakingKeeper().SetValidator(ctx, validator) + sdkk.StakingKeeper.SetValidator(ctx, validator) accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) require.NoError(t, err) - k.GetStakingKeeper().SetDelegation(ctx, stakingtypes.Delegation{ + sdkk.StakingKeeper.SetDelegation(ctx, stakingtypes.Delegation{ DelegatorAddress: accAddressOfValidator.String(), ValidatorAddress: validator.GetOperator().String(), Shares: sdk.NewDec(10), @@ -78,16 +78,16 @@ func TestKeeper_AfterValidatorBeginUnbonding(t *testing.T) { 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) + k, ctx, sdkk, _ := keepertest.ObserverKeeper(t) r := rand.New(rand.NewSource(9)) validator := sample.Validator(t, r) validator.DelegatorShares = sdk.NewDec(100) - k.GetStakingKeeper().SetValidator(ctx, validator) + sdkk.StakingKeeper.SetValidator(ctx, validator) accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) require.NoError(t, err) - k.GetStakingKeeper().SetDelegation(ctx, stakingtypes.Delegation{ + sdkk.StakingKeeper.SetDelegation(ctx, stakingtypes.Delegation{ DelegatorAddress: accAddressOfValidator.String(), ValidatorAddress: validator.GetOperator().String(), Shares: sdk.NewDec(10), @@ -108,16 +108,16 @@ func TestKeeper_AfterDelegationModified(t *testing.T) { }) t.Run("should clean observer if self delegation", func(t *testing.T) { - k, ctx, _, _ := keepertest.ObserverKeeper(t) + k, ctx, sdkk, _ := keepertest.ObserverKeeper(t) r := rand.New(rand.NewSource(9)) validator := sample.Validator(t, r) validator.DelegatorShares = sdk.NewDec(100) - k.GetStakingKeeper().SetValidator(ctx, validator) + sdkk.StakingKeeper.SetValidator(ctx, validator) accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) require.NoError(t, err) - k.GetStakingKeeper().SetDelegation(ctx, stakingtypes.Delegation{ + sdkk.StakingKeeper.SetDelegation(ctx, stakingtypes.Delegation{ DelegatorAddress: accAddressOfValidator.String(), ValidatorAddress: validator.GetOperator().String(), Shares: sdk.NewDec(10), @@ -150,12 +150,12 @@ func TestKeeper_BeforeValidatorSlashed(t *testing.T) { }) t.Run("should not error if observer set not found", func(t *testing.T) { - k, ctx, _, _ := keepertest.ObserverKeeper(t) + k, ctx, sdkk, _ := keepertest.ObserverKeeper(t) r := rand.New(rand.NewSource(9)) validator := sample.Validator(t, r) validator.DelegatorShares = sdk.NewDec(100) - k.GetStakingKeeper().SetValidator(ctx, validator) + sdkk.StakingKeeper.SetValidator(ctx, validator) hooks := k.Hooks() err := hooks.BeforeValidatorSlashed(ctx, validator.GetOperator(), sdk.NewDec(1)) @@ -163,13 +163,13 @@ func TestKeeper_BeforeValidatorSlashed(t *testing.T) { }) t.Run("should remove from observer set", func(t *testing.T) { - k, ctx, _, _ := keepertest.ObserverKeeper(t) + k, ctx, sdkk, _ := 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) + sdkk.StakingKeeper.SetValidator(ctx, validator) accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) require.NoError(t, err) @@ -187,13 +187,13 @@ func TestKeeper_BeforeValidatorSlashed(t *testing.T) { }) t.Run("should not remove from observer set", func(t *testing.T) { - k, ctx, _, _ := keepertest.ObserverKeeper(t) + k, ctx, sdkk, _ := 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) + sdkk.StakingKeeper.SetValidator(ctx, validator) accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) require.NoError(t, err) diff --git a/x/observer/keeper/utils_test.go b/x/observer/keeper/utils_test.go index 4aedd8ad13..4551e1ca24 100644 --- a/x/observer/keeper/utils_test.go +++ b/x/observer/keeper/utils_test.go @@ -44,13 +44,13 @@ func getValidEthChainIDWithIndex(t *testing.T, index int) int64 { func TestKeeper_IsAuthorized(t *testing.T) { t.Run("authorized observer", func(t *testing.T) { - k, ctx, _, _ := keepertest.ObserverKeeper(t) + k, ctx, sdkk, _ := keepertest.ObserverKeeper(t) r := rand.New(rand.NewSource(9)) // Set validator in the store validator := sample.Validator(t, r) - k.GetStakingKeeper().SetValidator(ctx, validator) + sdkk.StakingKeeper.SetValidator(ctx, validator) consAddress, err := validator.GetConsAddr() require.NoError(t, err) k.GetSlashingKeeper().SetValidatorSigningInfo(ctx, consAddress, slashingtypes.ValidatorSigningInfo{ @@ -71,13 +71,13 @@ func TestKeeper_IsAuthorized(t *testing.T) { }) t.Run("not authorized for tombstoned observer", func(t *testing.T) { - k, ctx, _, _ := keepertest.ObserverKeeper(t) + k, ctx, sdkk, _ := keepertest.ObserverKeeper(t) r := rand.New(rand.NewSource(9)) // Set validator in the store validator := sample.Validator(t, r) - k.GetStakingKeeper().SetValidator(ctx, validator) + sdkk.StakingKeeper.SetValidator(ctx, validator) consAddress, err := validator.GetConsAddr() require.NoError(t, err) k.GetSlashingKeeper().SetValidatorSigningInfo(ctx, consAddress, slashingtypes.ValidatorSigningInfo{ @@ -144,11 +144,11 @@ func TestKeeper_CheckObserverSelfDelegation(t *testing.T) { }) t.Run("should error if delegation not found", func(t *testing.T) { - k, ctx, _, _ := keepertest.ObserverKeeper(t) + k, ctx, sdkk, _ := keepertest.ObserverKeeper(t) r := rand.New(rand.NewSource(9)) validator := sample.Validator(t, r) - k.GetStakingKeeper().SetValidator(ctx, validator) + sdkk.StakingKeeper.SetValidator(ctx, validator) accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) require.NoError(t, err) @@ -157,16 +157,16 @@ func TestKeeper_CheckObserverSelfDelegation(t *testing.T) { }) t.Run("should remove from observer list if tokens less than min del", func(t *testing.T) { - k, ctx, _, _ := keepertest.ObserverKeeper(t) + k, ctx, sdkk, _ := keepertest.ObserverKeeper(t) r := rand.New(rand.NewSource(9)) validator := sample.Validator(t, r) validator.DelegatorShares = sdk.NewDec(100) - k.GetStakingKeeper().SetValidator(ctx, validator) + sdkk.StakingKeeper.SetValidator(ctx, validator) accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) require.NoError(t, err) - k.GetStakingKeeper().SetDelegation(ctx, stakingtypes.Delegation{ + sdkk.StakingKeeper.SetDelegation(ctx, stakingtypes.Delegation{ DelegatorAddress: accAddressOfValidator.String(), ValidatorAddress: validator.GetOperator().String(), Shares: sdk.NewDec(10), @@ -184,20 +184,20 @@ func TestKeeper_CheckObserverSelfDelegation(t *testing.T) { }) t.Run("should not remove from observer list if tokens gte than min del", func(t *testing.T) { - k, ctx, _, _ := keepertest.ObserverKeeper(t) + k, ctx, sdkk, _ := 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) + sdkk.StakingKeeper.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{ + sdkk.StakingKeeper.SetDelegation(ctx, stakingtypes.Delegation{ DelegatorAddress: accAddressOfValidator.String(), ValidatorAddress: validator.GetOperator().String(), Shares: minDelegation, @@ -235,11 +235,11 @@ func TestKeeper_IsOpeartorTombstoned(t *testing.T) { }) t.Run("should not error if validator found", func(t *testing.T) { - k, ctx, _, _ := keepertest.ObserverKeeper(t) + k, ctx, sdkk, _ := keepertest.ObserverKeeper(t) r := rand.New(rand.NewSource(9)) validator := sample.Validator(t, r) - k.GetStakingKeeper().SetValidator(ctx, validator) + sdkk.StakingKeeper.SetValidator(ctx, validator) accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) require.NoError(t, err) @@ -266,11 +266,11 @@ func TestKeeper_IsValidator(t *testing.T) { }) t.Run("should err if validator not bonded", func(t *testing.T) { - k, ctx, _, _ := keepertest.ObserverKeeper(t) + k, ctx, sdkk, _ := keepertest.ObserverKeeper(t) r := rand.New(rand.NewSource(9)) validator := sample.Validator(t, r) - k.GetStakingKeeper().SetValidator(ctx, validator) + sdkk.StakingKeeper.SetValidator(ctx, validator) accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) require.NoError(t, err) @@ -279,13 +279,13 @@ func TestKeeper_IsValidator(t *testing.T) { }) t.Run("should err if validator jailed", func(t *testing.T) { - k, ctx, _, _ := keepertest.ObserverKeeper(t) + k, ctx, sdkk, _ := 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) + sdkk.StakingKeeper.SetValidator(ctx, validator) accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) require.NoError(t, err) @@ -294,13 +294,13 @@ func TestKeeper_IsValidator(t *testing.T) { }) t.Run("should not err if validator not jailed and bonded", func(t *testing.T) { - k, ctx, _, _ := keepertest.ObserverKeeper(t) + k, ctx, sdkk, _ := 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) + sdkk.StakingKeeper.SetValidator(ctx, validator) accAddressOfValidator, err := types.GetAccAddressFromOperatorAddress(validator.OperatorAddress) require.NoError(t, err) diff --git a/x/observer/types/expected_keepers.go b/x/observer/types/expected_keepers.go index 5f402c2689..ba1c9ab9c2 100644 --- a/x/observer/types/expected_keepers.go +++ b/x/observer/types/expected_keepers.go @@ -11,7 +11,6 @@ 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 { From bc3a88cb5e1f6125c4d64eb82136345520b3f7c6 Mon Sep 17 00:00:00 2001 From: kevinssgh <79858682+kevinssgh@users.noreply.github.com> Date: Fri, 5 Apr 2024 13:23:17 -0400 Subject: [PATCH 08/10] test: unit testing zetabridge package in zetaclient (#1941) * initial commit * setup query testing * add Test function signatures * added some test cases * updates * added test case * added some more tests * update more unit tests * update more unit tests * fix gosec issue * fix some package errors * Added mock sdk Client and some unit tests for transactions * Added stub for mock sdk client and some unit tests for transactions * added unit test for updateCoreContext * ran make generate * unit tested broadcast.go * fix lint errors * address comments * increase test coverage * moved sdk client flag to zetabridge struct --------- Co-authored-by: Lucas Bertrand --- changelog.md | 1 + go.mod | 55 +- go.sum | 109 +-- zetaclient/config/types.go | 1 + zetaclient/testutils/stub/keyring.go | 111 +++ zetaclient/testutils/stub/sdk_client.go | 68 ++ zetaclient/zetabridge/broadcast.go | 45 +- zetaclient/zetabridge/broadcast_test.go | 112 ++- zetaclient/zetabridge/query_test.go | 883 +++++++++++++++++++++++ zetaclient/zetabridge/tx.go | 14 +- zetaclient/zetabridge/tx_test.go | 389 ++++++++++ zetaclient/zetabridge/zetacore_bridge.go | 45 +- 12 files changed, 1717 insertions(+), 116 deletions(-) create mode 100644 zetaclient/testutils/stub/keyring.go create mode 100644 zetaclient/testutils/stub/sdk_client.go create mode 100644 zetaclient/zetabridge/query_test.go diff --git a/changelog.md b/changelog.md index f847d51c33..73e8e04e28 100644 --- a/changelog.md +++ b/changelog.md @@ -56,6 +56,7 @@ * [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 +* [1941](https://github.com/zeta-chain/node/pull/1941) - add unit tests for zetabridge package * [1985](https://github.com/zeta-chain/node/pull/1985) - improve fungible module coverage ### Fixes diff --git a/go.mod b/go.mod index 9560f12c9e..245536d185 100644 --- a/go.mod +++ b/go.mod @@ -21,8 +21,8 @@ require ( github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tendermint/tendermint v0.34.28 github.com/tendermint/tm-db v0.6.7 - google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e // indirect - google.golang.org/grpc v1.56.3 + google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 // indirect + google.golang.org/grpc v1.60.1 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c ) @@ -45,7 +45,7 @@ require ( github.com/zeta-chain/go-tss v0.1.1-0.20240208222330-f3be0d4a0d98 github.com/zeta-chain/keystone/keys v0.0.0-20231105174229-903bc9405da2 github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20230816152528-db7d2bf9144b - google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc + google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f gopkg.in/yaml.v2 v2.4.0 ) @@ -61,11 +61,13 @@ require ( github.com/binance-chain/tss-lib v0.0.0-20201118045712-70b2cb4bf916 github.com/nanmu42/etherscan-api v1.10.0 github.com/onrik/ethrpc v1.2.0 + go.nhat.io/grpcmock v0.25.0 ) require ( github.com/DataDog/zstd v1.5.2 // indirect github.com/agl/ed25519 v0.0.0-20200225211852-fd4d107ace12 // indirect + github.com/bool64/shared v0.1.5 // indirect github.com/btcsuite/btcd/btcutil v1.1.3 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cockroachdb/errors v1.9.1 // indirect @@ -82,9 +84,11 @@ require ( github.com/getsentry/sentry-go v0.18.0 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/golang/glog v1.1.0 // indirect + github.com/golang/glog v1.1.2 // indirect github.com/golang/mock v1.6.0 // indirect github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/iancoleman/orderedmap v0.3.0 // indirect github.com/ipfs/boxo v0.10.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/libp2p/go-yamux/v4 v4.0.0 // indirect @@ -99,28 +103,35 @@ require ( github.com/quic-go/webtransport-go v0.5.3 // indirect github.com/rjeczalik/notify v0.9.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/sergi/go-diff v1.3.1 // indirect + github.com/swaggest/assertjson v1.9.0 // indirect github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect github.com/thales-e-security/pool v0.0.2 // indirect github.com/tidwall/gjson v1.14.4 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect github.com/tidwall/sjson v1.2.5 // indirect + github.com/yudai/gojsondiff v1.0.0 // indirect + github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect go.etcd.io/bbolt v1.3.7 // indirect + go.nhat.io/matcher/v2 v2.0.0 // indirect + go.nhat.io/wait v0.1.0 // indirect go.opentelemetry.io/otel v1.16.0 // indirect go.opentelemetry.io/otel/metric v1.16.0 // indirect go.opentelemetry.io/otel/trace v1.16.0 // indirect go.uber.org/dig v1.17.0 // indirect go.uber.org/fx v1.19.2 // indirect + golang.org/x/time v0.5.0 // indirect gonum.org/v1/gonum v0.13.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect ) require ( - cloud.google.com/go v0.110.0 // indirect - cloud.google.com/go/compute v1.19.1 // indirect + cloud.google.com/go v0.110.10 // indirect + cloud.google.com/go/compute v1.23.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v0.13.0 // indirect - cloud.google.com/go/storage v1.28.1 // indirect + cloud.google.com/go/iam v1.1.5 // indirect + cloud.google.com/go/storage v1.35.1 // indirect filippo.io/edwards25519 v1.0.0-rc.1 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/ChainSafe/go-schnorrkel v1.0.0 // indirect @@ -183,15 +194,15 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.2 // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/gopacket v1.1.19 // indirect github.com/google/orderedcode v0.0.1 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect - github.com/googleapis/gax-go/v2 v2.7.1 // indirect + github.com/google/uuid v1.4.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/websocket v1.5.0 - github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/gtank/merlin v0.1.1 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect @@ -283,11 +294,11 @@ require ( github.com/sasha-s/go-deadlock v0.3.1 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect - github.com/spf13/afero v1.9.3 // indirect + github.com/spf13/afero v1.11.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/viper v1.15.0 github.com/status-im/keycard-go v0.2.0 // indirect - github.com/stretchr/objx v0.5.0 // indirect + github.com/stretchr/objx v0.5.1 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/tendermint/btcd v0.1.1 // indirect github.com/tendermint/go-amino v0.16.0 // indirect @@ -307,17 +318,17 @@ require ( golang.org/x/crypto v0.17.0 // indirect golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect golang.org/x/mod v0.10.0 // indirect - golang.org/x/net v0.17.0 - golang.org/x/oauth2 v0.8.0 // indirect - golang.org/x/sync v0.2.0 + golang.org/x/net v0.19.0 + golang.org/x/oauth2 v0.15.0 // indirect + golang.org/x/sync v0.5.0 golang.org/x/sys v0.15.0 // indirect golang.org/x/term v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.9.1 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/api v0.114.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.30.0 + google.golang.org/api v0.152.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/protobuf v1.31.0 gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 96e3c53ae9..256540ef43 100644 --- a/go.sum +++ b/go.sum @@ -49,8 +49,8 @@ cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34h cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= -cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= -cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y= +cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic= cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= @@ -134,8 +134,8 @@ cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= -cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY= -cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= +cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= +cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= @@ -226,8 +226,8 @@ cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3Q cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= -cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= -cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= +cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= +cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= @@ -246,7 +246,6 @@ cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6 cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= -cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= @@ -371,8 +370,8 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= -cloud.google.com/go/storage v1.28.1 h1:F5QDG5ChchaAVQhINh24U99OWHURqrW8OmQcGKXcbgI= -cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= +cloud.google.com/go/storage v1.35.1 h1:B59ahL//eDfx2IIKFBeT5Atm9wnNmj3+8xG/W4WB//w= +cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= @@ -730,6 +729,9 @@ github.com/bombsimon/wsl/v2 v2.2.0/go.mod h1:Azh8c3XGEJl9LyX0/sFC+CKMc7Ssgua0g+6 github.com/bombsimon/wsl/v3 v3.0.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= github.com/bombsimon/wsl/v3 v3.1.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= +github.com/bool64/dev v0.2.29 h1:x+syGyh+0eWtOzQ1ItvLzOGIWyNWnyjXpHIcpF2HvL4= +github.com/bool64/shared v0.1.5 h1:fp3eUhBsrSjNCQPcSdQqZxxh9bBwrYiZ+zOKFkM0/2E= +github.com/bool64/shared v0.1.5/go.mod h1:081yz68YC9jeFB3+Bbmno2RFWvGKv1lPKkMP6MHJlPs= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/breml/bidichk v0.2.3/go.mod h1:8u2C6DnAy0g2cEq+k/A2+tr9O1s+vHGxWn0LTc70T2A= github.com/breml/errchkjson v0.3.0/go.mod h1:9Cogkyv9gcT8HREpzi3TiqBxCqDzo8awa92zSDFcofU= @@ -1448,8 +1450,8 @@ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGw github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= +github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -1545,8 +1547,9 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.0.0-20191010200024-a3d713f9b7f8/go.mod h1:KyKXa9ciM8+lgMXwOVsXi7UxGrsf9mM61Mzs+xKUrKE= github.com/google/go-containerregistry v0.1.2/go.mod h1:GPivBPgdAyd2SU+vf6EpsgOtWDuPqjW0hJZt4rNdTZ4= github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= @@ -1595,6 +1598,8 @@ github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 h1:hR7/MlvK23p6+lIw9S github.com/google/pprof v0.0.0-20230602150820-91b7bce49751/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/rpmpack v0.0.0-20191226140753-aa36bfddb3a0/go.mod h1:RaTPr0KUf2K7fnZYLNDrr8rxAamWs3iNywJLtQ2AzBg= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/trillian v1.3.11/go.mod h1:0tPraVHrSDkA3BO6vKX67zgLXs6SsOAbHEivX+9mPgw= @@ -1603,15 +1608,16 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s= github.com/google/wire v0.4.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= -github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= @@ -1625,8 +1631,8 @@ github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= -github.com/googleapis/gax-go/v2 v2.7.1 h1:gF4c0zjUP2H/s/hEGyLA3I0fA2ZWjzYiONAD6cvPr8A= -github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= @@ -1683,8 +1689,9 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmg github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s= github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.6.2/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= @@ -1804,6 +1811,8 @@ github.com/huin/goupnp v1.2.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFck github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= +github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc= +github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -2697,6 +2706,8 @@ github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfP github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc= @@ -2771,8 +2782,8 @@ github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY52 github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= -github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -2827,8 +2838,9 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= +github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v0.0.0-20170130113145-4d4bfba8f1d1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -2854,6 +2866,8 @@ github.com/subosito/gotenv v1.4.0/go.mod h1:mZd6rFysKEcUhUHXJk0C/08wAgyDBFuwEYL7 github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/swaggest/assertjson v1.9.0 h1:dKu0BfJkIxv/xe//mkCrK5yZbs79jL7OVf9Ija7o2xQ= +github.com/swaggest/assertjson v1.9.0/go.mod h1:b+ZKX2VRiUjxfUIal0HDN85W0nHPAYUbYH5WkkSsFsU= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= @@ -3020,8 +3034,11 @@ github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsT github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= github.com/ybbus/jsonrpc v2.1.2+incompatible/go.mod h1:XJrh1eMSzdIYFbM08flv0wp5G35eRniyeGut1z+LSiE= github.com/yeya24/promlinter v0.2.0/go.mod h1:u54lkmBOZrpEbQQ6gox2zWKKLKu2SGe+2KOiextY+IA= +github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA= github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= +github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI= github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -3079,6 +3096,13 @@ go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVd go.etcd.io/gofail v0.1.0/go.mod h1:VZBCXYGZhHAinaBiiqYvuDynvahNsAyLFwB3kEHKz1M= go.mozilla.org/mozlog v0.0.0-20170222151521-4bb13139d403/go.mod h1:jHoPAGnDrCy6kaI2tAze5Prf0Nr0w/oNkROt2lw3n3o= go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= +go.nhat.io/aferomock v0.4.0 h1:gs3nJzIqAezglUuaPfautAmZwulwRWLcfSSzdK4YCC0= +go.nhat.io/grpcmock v0.25.0 h1:zk03vvA60w7UrnurZbqL4wxnjmJz1Kuyb7ig2MF+n4c= +go.nhat.io/grpcmock v0.25.0/go.mod h1:5U694ASEFBkiZP7aPuz9kbbb/jphVlfpbOnocyht/rE= +go.nhat.io/matcher/v2 v2.0.0 h1:W+rbHi0hKuZHtOQH4U5g+KwyKyfVioIxrxjoGRcUETE= +go.nhat.io/matcher/v2 v2.0.0/go.mod h1:cL5oYp0M9A4L8jEGqjmUfy+k7AXVDddoVt6aYIL1r5g= +go.nhat.io/wait v0.1.0 h1:aQ4YDzaOgFbypiJ9c/eAfOIB1G25VOv7Gd2QS8uz1gw= +go.nhat.io/wait v0.1.0/go.mod h1:+ijMghc9/9zXi+HDcs49HNReprvXOZha2Q3jTOtqJrE= go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.19.1/go.mod h1:gug0GbSHa8Pafr0d2urOSgoXHZ6x/RUlaiT0d9pqb4A= @@ -3175,6 +3199,7 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= @@ -3408,8 +3433,8 @@ golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180724155351-3d292e4d0cdc/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -3444,8 +3469,8 @@ golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= -golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= +golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -3465,8 +3490,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -3696,8 +3721,9 @@ golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -3926,8 +3952,8 @@ google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91 google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= -google.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE= -google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= +google.golang.org/api v0.152.0 h1:t0r1vPnfMc260S2Ci+en7kfCZaLOPs5KI0sVV/6jZrY= +google.golang.org/api v0.152.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -3937,8 +3963,9 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -4084,12 +4111,12 @@ google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZV google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e h1:Ao9GzfUMPH3zjVfzXG5rlWlk+Q8MXWKwWpwVQE1MXfw= -google.golang.org/genproto v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 h1:1hfbdAfFbkmpg41000wDVqr7jUpK/Yo+LPnIxxGzmkg= +google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3/go.mod h1:5RBcpGRxr25RbDzY5w+dmaqpSEvl8Gwl1x2CICf60ic= +google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f h1:2yNACc1O40tTnrsbk9Cv6oxiW8pxI/pXj0wRtdlYmgY= +google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f/go.mod h1:Uy9bTZJqmfrw2rIBxgGLnamc78euZULUBrLZ9XTITKI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 h1:/jFB8jK5R3Sq3i/lmeZO0cATSzFfZaJq1J2Euan3XKU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -4144,8 +4171,8 @@ google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCD google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= -google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= +google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= +google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -4164,8 +4191,8 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/zetaclient/config/types.go b/zetaclient/config/types.go index ea861d46bb..b53bcc17dd 100644 --- a/zetaclient/config/types.go +++ b/zetaclient/config/types.go @@ -17,6 +17,7 @@ const ( KeyringBackendFile KeyringBackend = "file" ) +// ClientConfiguration is a subset of zetaclient config that is used by zetabridge type ClientConfiguration struct { ChainHost string `json:"chain_host" mapstructure:"chain_host"` ChainRPC string `json:"chain_rpc" mapstructure:"chain_rpc"` diff --git a/zetaclient/testutils/stub/keyring.go b/zetaclient/testutils/stub/keyring.go new file mode 100644 index 0000000000..819c2a6eed --- /dev/null +++ b/zetaclient/testutils/stub/keyring.go @@ -0,0 +1,111 @@ +package stub + +import ( + ckeys "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var TestKeyringPair cryptotypes.PrivKey + +func init() { + TestKeyringPair = cryptotypes.PrivKey(secp256k1.GenPrivKey()) +} + +type Keyring struct { +} + +var _ ckeys.Keyring = Keyring{} + +func NewKeyring() ckeys.Keyring { + return Keyring{} +} + +func (m Keyring) Backend() string { + return "" +} + +func (m Keyring) List() ([]*ckeys.Record, error) { + return nil, nil +} + +func (m Keyring) SupportedAlgorithms() (ckeys.SigningAlgoList, ckeys.SigningAlgoList) { + return nil, nil +} + +func (m Keyring) Key(_ string) (*ckeys.Record, error) { + return ckeys.NewLocalRecord("", TestKeyringPair, TestKeyringPair.PubKey()) +} + +func (m Keyring) KeyByAddress(_ sdk.Address) (*ckeys.Record, error) { + return nil, nil +} + +func (m Keyring) Delete(_ string) error { + return nil +} + +func (m Keyring) DeleteByAddress(_ sdk.Address) error { + return nil +} + +func (m Keyring) Rename(_ string, _ string) error { + return nil +} + +func (m Keyring) NewMnemonic(_ string, _ ckeys.Language, _, _ string, _ ckeys.SignatureAlgo) (*ckeys.Record, string, error) { + return nil, "", nil +} + +func (m Keyring) NewAccount(_, _, _, _ string, _ ckeys.SignatureAlgo) (*ckeys.Record, error) { + return nil, nil +} + +func (m Keyring) SaveLedgerKey(_ string, _ ckeys.SignatureAlgo, _ string, _, _, _ uint32) (*ckeys.Record, error) { + return nil, nil +} + +func (m Keyring) SaveOfflineKey(_ string, _ cryptotypes.PubKey) (*ckeys.Record, error) { + return nil, nil +} + +func (m Keyring) SaveMultisig(_ string, _ cryptotypes.PubKey) (*ckeys.Record, error) { + return nil, nil +} + +func (m Keyring) Sign(_ string, _ []byte) ([]byte, cryptotypes.PubKey, error) { + return nil, nil, nil +} + +func (m Keyring) SignByAddress(_ sdk.Address, _ []byte) ([]byte, cryptotypes.PubKey, error) { + return nil, nil, nil +} + +func (m Keyring) ImportPrivKey(_, _, _ string) error { + return nil +} + +func (m Keyring) ImportPubKey(_ string, _ string) error { + return nil +} + +func (m Keyring) ExportPubKeyArmor(_ string) (string, error) { + return "", nil +} + +func (m Keyring) ExportPubKeyArmorByAddress(_ sdk.Address) (string, error) { + return "", nil +} + +func (m Keyring) ExportPrivKeyArmor(_, _ string) (armor string, err error) { + return "", nil +} + +func (m Keyring) ExportPrivKeyArmorByAddress(_ sdk.Address, _ string) (armor string, err error) { + return "", nil +} + +func (m Keyring) MigrateAll() ([]*ckeys.Record, error) { + return nil, nil +} diff --git a/zetaclient/testutils/stub/sdk_client.go b/zetaclient/testutils/stub/sdk_client.go new file mode 100644 index 0000000000..c548aeb2e8 --- /dev/null +++ b/zetaclient/testutils/stub/sdk_client.go @@ -0,0 +1,68 @@ +package stub + +import ( + "context" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/bytes" + "github.com/tendermint/tendermint/rpc/client/mock" + coretypes "github.com/tendermint/tendermint/rpc/core/types" + tmtypes "github.com/tendermint/tendermint/types" +) + +type SDKClient struct { + mock.Client + err error + code uint32 +} + +func (c SDKClient) BroadcastTxCommit(_ context.Context, _ tmtypes.Tx) (*coretypes.ResultBroadcastTxCommit, error) { + return nil, c.err +} + +func (c SDKClient) BroadcastTxAsync(_ context.Context, _ tmtypes.Tx) (*coretypes.ResultBroadcastTx, error) { + return nil, c.err +} + +func (c SDKClient) BroadcastTxSync(_ context.Context, _ tmtypes.Tx) (*coretypes.ResultBroadcastTx, error) { + log := "" + if c.err != nil { + log = c.err.Error() + } + return &coretypes.ResultBroadcastTx{ + Code: c.code, + Data: bytes.HexBytes{}, + Log: log, + Codespace: "", + Hash: bytes.HexBytes{}, + }, c.err +} + +func (c SDKClient) Tx(_ context.Context, _ []byte, _ bool) (*coretypes.ResultTx, error) { + return &coretypes.ResultTx{ + Hash: bytes.HexBytes{}, + Height: 0, + Index: 0, + TxResult: abci.ResponseDeliverTx{ + Log: "", + }, + Tx: []byte{}, + Proof: tmtypes.TxProof{}, + }, c.err +} + +func (c SDKClient) Block(_ context.Context, _ *int64) (*coretypes.ResultBlock, error) { + return &coretypes.ResultBlock{Block: &tmtypes.Block{ + Header: tmtypes.Header{}, + Data: tmtypes.Data{}, + Evidence: tmtypes.EvidenceData{}, + }}, c.err +} + +func NewSDKClientWithErr(err error, code uint32) *SDKClient { + return &SDKClient{ + Client: mock.Client{}, + err: err, + code: code, + } +} diff --git a/zetaclient/zetabridge/broadcast.go b/zetaclient/zetabridge/broadcast.go index 9c1e620474..58fddbd223 100644 --- a/zetaclient/zetabridge/broadcast.go +++ b/zetaclient/zetabridge/broadcast.go @@ -6,23 +6,24 @@ import ( "strconv" "strings" - "github.com/rs/zerolog/log" - - "github.com/zeta-chain/zetacore/zetaclient/authz" - "github.com/cosmos/cosmos-sdk/client" clienttx "github.com/cosmos/cosmos-sdk/client/tx" sdktypes "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx/signing" authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/rs/zerolog/log" flag "github.com/spf13/pflag" rpchttp "github.com/tendermint/tendermint/rpc/client/http" "github.com/zeta-chain/zetacore/app/ante" "github.com/zeta-chain/zetacore/cmd/zetacored/config" + "github.com/zeta-chain/zetacore/zetaclient/authz" "github.com/zeta-chain/zetacore/zetaclient/hsm" ) +// BroadcastInterface defines the signature of the broadcast function used by zetabridge transactions +type BroadcastInterface = func(bridge *ZetaCoreBridge, gaslimit uint64, authzWrappedMsg sdktypes.Msg, authzSigner authz.Signer) (string, error) + const ( // DefaultBaseGasPrice is the default base gas price DefaultBaseGasPrice = 1_000_000 @@ -32,8 +33,17 @@ var ( // paying 50% more than the current base gas price to buffer for potential block-by-block // gas price increase due to EIP1559 feemarket on ZetaChain bufferMultiplier = sdktypes.MustNewDecFromStr("1.5") + + // Variable function used by transactions to broadcast a message to ZetaCore. This will create enough flexibility + // in the implementation to allow for more comprehensive unit testing. + zetaBridgeBroadcast BroadcastInterface = BroadcastToZetaCore ) +// BroadcastToZetaCore is the default broadcast function used to send transactions to Zeta Core +func BroadcastToZetaCore(bridge *ZetaCoreBridge, gasLimit uint64, authzWrappedMsg sdktypes.Msg, authzSigner authz.Signer) (string, error) { + return bridge.Broadcast(gasLimit, authzWrappedMsg, authzSigner) +} + // Broadcast Broadcasts tx to metachain. Returns txHash and error func (b *ZetaCoreBridge) Broadcast(gaslimit uint64, authzWrappedMsg sdktypes.Msg, authzSigner authz.Signer) (string, error) { b.broadcastLock.Lock() @@ -49,7 +59,7 @@ func (b *ZetaCoreBridge) Broadcast(gaslimit uint64, authzWrappedMsg sdktypes.Msg return "", err } if baseGasPrice == 0 { - baseGasPrice = DefaultBaseGasPrice // shoudn't happen, but just in case + baseGasPrice = DefaultBaseGasPrice // shouldn't happen, but just in case } reductionRate := sdktypes.MustNewDecFromStr(ante.GasPriceReductionRate) // multiply gas price by the system tx reduction rate @@ -163,17 +173,23 @@ func (b *ZetaCoreBridge) GetContext() (client.Context, error) { ctx = ctx.WithLegacyAmino(b.encodingCfg.Amino) ctx = ctx.WithAccountRetriever(authtypes.AccountRetriever{}) - remote := b.cfg.ChainRPC - if !strings.HasPrefix(b.cfg.ChainHost, "http") { - remote = fmt.Sprintf("tcp://%s", remote) - } + if b.enableMockSDKClient { + ctx = ctx.WithClient(b.mockSDKClient) + } else { + remote := b.cfg.ChainRPC + if !strings.HasPrefix(b.cfg.ChainHost, "http") { + remote = fmt.Sprintf("tcp://%s", remote) + } - ctx = ctx.WithNodeURI(remote) - wsClient, err := rpchttp.New(remote, "/websocket") - if err != nil { - return ctx, err + ctx = ctx.WithNodeURI(remote) + wsClient, err := rpchttp.New(remote, "/websocket") + if err != nil { + return ctx, err + } + + ctx = ctx.WithClient(wsClient) } - ctx = ctx.WithClient(wsClient) + return ctx, nil } @@ -200,6 +216,7 @@ func (b *ZetaCoreBridge) QueryTxResult(hash string) (*sdktypes.TxResponse, error } // HandleBroadcastError returns whether to retry in a few seconds, and whether to report via AddTxHashToOutTxTracker +// returns (bool retry, bool report) func HandleBroadcastError(err error, nonce, toChain, outTxHash string) (bool, bool) { if strings.Contains(err.Error(), "nonce too low") { log.Warn().Err(err).Msgf("nonce too low! this might be a unnecessary key-sign. increase re-try interval and awaits outTx confirmation") diff --git a/zetaclient/zetabridge/broadcast_test.go b/zetaclient/zetabridge/broadcast_test.go index a52ebb63eb..ce0aeaa57d 100644 --- a/zetaclient/zetabridge/broadcast_test.go +++ b/zetaclient/zetabridge/broadcast_test.go @@ -1,25 +1,111 @@ package zetabridge import ( - "fmt" - "regexp" + "encoding/hex" + "errors" + "net" + "testing" - . "gopkg.in/check.v1" + "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + feemarkettypes "github.com/evmos/ethermint/x/feemarket/types" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + observerTypes "github.com/zeta-chain/zetacore/x/observer/types" + "github.com/zeta-chain/zetacore/zetaclient/keys" + "github.com/zeta-chain/zetacore/zetaclient/testutils/stub" + "go.nhat.io/grpcmock" + "go.nhat.io/grpcmock/planner" ) -type BcastSuite struct { +func TestHandleBroadcastError(t *testing.T) { + type response struct { + retry bool + report bool + } + testCases := map[error]response{ + errors.New("nonce too low"): {retry: false, report: false}, + errors.New("replacement transaction underpriced"): {retry: false, report: false}, + errors.New("already known"): {retry: false, report: true}, + errors.New(""): {retry: true, report: false}, + } + for input, output := range testCases { + retry, report := HandleBroadcastError(input, "", "", "") + require.Equal(t, output.report, report) + require.Equal(t, output.retry, retry) + } } -var _ = Suite(&BcastSuite{}) +func TestBroadcast(t *testing.T) { + address := types.AccAddress(stub.TestKeyringPair.PubKey().Address().Bytes()) + + //Setup server for multiple grpc calls + listener, err := net.Listen("tcp", "127.0.0.1:9090") + require.NoError(t, err) + server := grpcmock.MockUnstartedServer( + grpcmock.RegisterService(crosschaintypes.RegisterQueryServer), + grpcmock.RegisterService(feemarkettypes.RegisterQueryServer), + grpcmock.RegisterService(authtypes.RegisterQueryServer), + grpcmock.WithPlanner(planner.FirstMatch()), + grpcmock.WithListener(listener), + func(s *grpcmock.Server) { + method := "/zetachain.zetacore.crosschain.Query/LastZetaHeight" + s.ExpectUnary(method). + UnlimitedTimes(). + WithPayload(crosschaintypes.QueryLastZetaHeightRequest{}). + Return(crosschaintypes.QueryLastZetaHeightResponse{Height: 0}) + + method = "/ethermint.feemarket.v1.Query/Params" + s.ExpectUnary(method). + UnlimitedTimes(). + WithPayload(feemarkettypes.QueryParamsRequest{}). + Return(feemarkettypes.QueryParamsResponse{ + Params: feemarkettypes.Params{ + BaseFee: types.NewInt(23455), + }, + }) + }, + )(t) + + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + zetabridge.keys = keys.NewKeysWithKeybase(stub.NewKeyring(), address, "", "") + + t.Run("broadcast success", func(t *testing.T) { + zetabridge.EnableMockSDKClient(stub.NewSDKClientWithErr(nil, 0)) + blockHash, err := hex.DecodeString(ethBlockHash) + require.NoError(t, err) + msg := observerTypes.NewMsgAddBlockHeader(address.String(), chains.EthChain().ChainId, blockHash, 18495266, getHeaderData(t)) + authzMsg, authzSigner, err := zetabridge.WrapMessageWithAuthz(msg) + require.NoError(t, err) + _, err = BroadcastToZetaCore(zetabridge, 10000, authzMsg, authzSigner) + require.NoError(t, err) + }) + + t.Run("broadcast failed", func(t *testing.T) { + zetabridge.EnableMockSDKClient(stub.NewSDKClientWithErr(errors.New("account sequence mismatch, expected 5 got 4"), 32)) + blockHash, err := hex.DecodeString(ethBlockHash) + require.NoError(t, err) + msg := observerTypes.NewMsgAddBlockHeader(address.String(), chains.EthChain().ChainId, blockHash, 18495266, getHeaderData(t)) + authzMsg, authzSigner, err := zetabridge.WrapMessageWithAuthz(msg) + require.NoError(t, err) + _, err = BroadcastToZetaCore(zetabridge, 10000, authzMsg, authzSigner) + require.Error(t, err) + }) -func (s *BcastSuite) SetUpTest(c *C) { - fmt.Println("hello") } -func (s *BcastSuite) TestParsingSeqNumMismatch(c *C) { - err_msg := "fail to broadcast to zetabridge,code:32, log:account sequence mismatch, expected 386232, got 386230: incorrect account sequence" - re := regexp.MustCompile(`account sequence mismatch, expected ([0-9]*), got ([0-9]*)`) - fmt.Printf("%q\n", re.FindStringSubmatch(err_msg)) - err_msg2 := "hahah" - fmt.Printf("%q\n", re.FindStringSubmatch(err_msg2)) +func TestZetaCoreBridge_GetContext(t *testing.T) { + address := types.AccAddress(stub.TestKeyringPair.PubKey().Address().Bytes()) + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + zetabridge.keys = keys.NewKeysWithKeybase(stub.NewKeyring(), address, "", "") + + _, err = zetabridge.GetContext() + require.NoError(t, err) + } diff --git a/zetaclient/zetabridge/query_test.go b/zetaclient/zetabridge/query_test.go new file mode 100644 index 0000000000..c5dabc31fd --- /dev/null +++ b/zetaclient/zetabridge/query_test.go @@ -0,0 +1,883 @@ +package zetabridge + +import ( + "net" + "testing" + + "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" + "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + feemarkettypes "github.com/evmos/ethermint/x/feemarket/types" + "github.com/stretchr/testify/require" + tmtypes "github.com/tendermint/tendermint/proto/tendermint/types" + "github.com/zeta-chain/zetacore/cmd/zetacored/config" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" + crosschainTypes "github.com/zeta-chain/zetacore/x/crosschain/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" + "github.com/zeta-chain/zetacore/zetaclient/interfaces" + "github.com/zeta-chain/zetacore/zetaclient/keys" + "github.com/zeta-chain/zetacore/zetaclient/metrics" + "github.com/zeta-chain/zetacore/zetaclient/testutils/stub" + "go.nhat.io/grpcmock" + "go.nhat.io/grpcmock/planner" +) + +func setupMockServer(t *testing.T, serviceFunc any, method string, input any, expectedOutput any) *grpcmock.Server { + listener, err := net.Listen("tcp", "127.0.0.1:9090") + require.NoError(t, err) + + server := grpcmock.MockUnstartedServer( + grpcmock.RegisterService(serviceFunc), + grpcmock.WithPlanner(planner.FirstMatch()), + grpcmock.WithListener(listener), + func(s *grpcmock.Server) { + s.ExpectUnary(method). + UnlimitedTimes(). + WithPayload(input). + Return(expectedOutput) + }, + )(t) + + return server +} + +func closeMockServer(t *testing.T, server *grpcmock.Server) { + err := server.Close() + require.NoError(t, err) +} + +func setupCoreBridge() (*ZetaCoreBridge, error) { + return NewZetaCoreBridge( + &keys.Keys{}, + "127.0.0.1", + "", + "zetachain_7000-1", + false, + &metrics.TelemetryServer{}) +} + +func TestZetaCoreBridge_GetBallot(t *testing.T) { + expectedOutput := observertypes.QueryBallotByIdentifierResponse{ + BallotIdentifier: "123", + Voters: nil, + ObservationType: 0, + BallotStatus: 0, + } + input := observertypes.QueryBallotByIdentifierRequest{BallotIdentifier: "123"} + method := "/zetachain.zetacore.observer.Query/BallotByIdentifier" + server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetBallotByID("123") + require.NoError(t, err) + require.Equal(t, expectedOutput, *resp) +} + +func TestZetaCoreBridge_GetCrosschainFlags(t *testing.T) { + expectedOutput := observertypes.QueryGetCrosschainFlagsResponse{CrosschainFlags: observertypes.CrosschainFlags{ + IsInboundEnabled: true, + IsOutboundEnabled: false, + GasPriceIncreaseFlags: nil, + BlockHeaderVerificationFlags: nil, + }} + input := observertypes.QueryGetCrosschainFlagsRequest{} + method := "/zetachain.zetacore.observer.Query/CrosschainFlags" + server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetCrosschainFlags() + require.NoError(t, err) + require.Equal(t, expectedOutput.CrosschainFlags, resp) +} + +func TestZetaCoreBridge_GetChainParamsForChainID(t *testing.T) { + expectedOutput := observertypes.QueryGetChainParamsForChainResponse{ChainParams: &observertypes.ChainParams{ + ChainId: 123, + }} + input := observertypes.QueryGetChainParamsForChainRequest{ChainId: 123} + method := "/zetachain.zetacore.observer.Query/GetChainParamsForChain" + server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetChainParamsForChainID(123) + require.NoError(t, err) + require.Equal(t, expectedOutput.ChainParams, resp) +} + +func TestZetaCoreBridge_GetChainParams(t *testing.T) { + expectedOutput := observertypes.QueryGetChainParamsResponse{ChainParams: &observertypes.ChainParamsList{ + ChainParams: []*observertypes.ChainParams{ + { + ChainId: 123, + }, + }, + }} + input := observertypes.QueryGetChainParamsRequest{} + method := "/zetachain.zetacore.observer.Query/GetChainParams" + server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetChainParams() + require.NoError(t, err) + require.Equal(t, expectedOutput.ChainParams.ChainParams, resp) +} + +func TestZetaCoreBridge_GetUpgradePlan(t *testing.T) { + expectedOutput := upgradetypes.QueryCurrentPlanResponse{ + Plan: &upgradetypes.Plan{ + Name: "big upgrade", + Height: 100, + }, + } + input := upgradetypes.QueryCurrentPlanRequest{} + method := "/cosmos.upgrade.v1beta1.Query/CurrentPlan" + server := setupMockServer(t, upgradetypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetUpgradePlan() + require.NoError(t, err) + require.Equal(t, expectedOutput.Plan, resp) +} + +func TestZetaCoreBridge_GetAllCctx(t *testing.T) { + expectedOutput := crosschainTypes.QueryAllCctxResponse{ + CrossChainTx: []*crosschainTypes.CrossChainTx{ + { + Index: "cross-chain4456", + }, + }, + Pagination: nil, + } + input := crosschainTypes.QueryAllCctxRequest{} + method := "/zetachain.zetacore.crosschain.Query/CctxAll" + server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetAllCctx() + require.NoError(t, err) + require.Equal(t, expectedOutput.CrossChainTx, resp) +} + +func TestZetaCoreBridge_GetCctxByHash(t *testing.T) { + expectedOutput := crosschainTypes.QueryGetCctxResponse{CrossChainTx: &crosschainTypes.CrossChainTx{ + Index: "9c8d02b6956b9c78ecb6090a8160faaa48e7aecfd0026fcdf533721d861436a3", + }} + input := crosschainTypes.QueryGetCctxRequest{Index: "9c8d02b6956b9c78ecb6090a8160faaa48e7aecfd0026fcdf533721d861436a3"} + method := "/zetachain.zetacore.crosschain.Query/Cctx" + server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetCctxByHash("9c8d02b6956b9c78ecb6090a8160faaa48e7aecfd0026fcdf533721d861436a3") + require.NoError(t, err) + require.Equal(t, expectedOutput.CrossChainTx, resp) +} + +func TestZetaCoreBridge_GetCctxByNonce(t *testing.T) { + expectedOutput := crosschainTypes.QueryGetCctxResponse{CrossChainTx: &crosschainTypes.CrossChainTx{ + Index: "9c8d02b6956b9c78ecb6090a8160faaa48e7aecfd0026fcdf533721d861436a3", + }} + input := crosschainTypes.QueryGetCctxByNonceRequest{ + ChainID: 7000, + Nonce: 55, + } + method := "/zetachain.zetacore.crosschain.Query/CctxByNonce" + server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetCctxByNonce(7000, 55) + require.NoError(t, err) + require.Equal(t, expectedOutput.CrossChainTx, resp) +} + +func TestZetaCoreBridge_GetObserverList(t *testing.T) { + expectedOutput := observertypes.QueryObserverSetResponse{ + Observers: []string{ + "zeta19jr7nl82lrktge35f52x9g5y5prmvchmk40zhg", + "zeta1cxj07f3ju484ry2cnnhxl5tryyex7gev0yzxtj", + "zeta1hjct6q7npsspsg3dgvzk3sdf89spmlpf7rqmnw", + }, + } + input := observertypes.QueryObserverSet{} + method := "/zetachain.zetacore.observer.Query/ObserverSet" + server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetObserverList() + require.NoError(t, err) + require.Equal(t, expectedOutput.Observers, resp) +} + +func TestZetaCoreBridge_ListPendingCctx(t *testing.T) { + expectedOutput := crosschainTypes.QueryListCctxPendingResponse{ + CrossChainTx: []*crosschainTypes.CrossChainTx{ + { + Index: "cross-chain4456", + }, + }, + TotalPending: 1, + } + input := crosschainTypes.QueryListCctxPendingRequest{ChainId: 7000} + method := "/zetachain.zetacore.crosschain.Query/CctxListPending" + server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, totalPending, err := zetabridge.ListPendingCctx(7000) + require.NoError(t, err) + require.Equal(t, expectedOutput.CrossChainTx, resp) + require.Equal(t, expectedOutput.TotalPending, totalPending) +} + +func TestZetaCoreBridge_GetAbortedZetaAmount(t *testing.T) { + expectedOutput := crosschainTypes.QueryZetaAccountingResponse{AbortedZetaAmount: "1080999"} + input := crosschainTypes.QueryZetaAccountingRequest{} + method := "/zetachain.zetacore.crosschain.Query/ZetaAccounting" + server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetAbortedZetaAmount() + require.NoError(t, err) + require.Equal(t, expectedOutput.AbortedZetaAmount, resp) +} + +// Need to test after refactor +func TestZetaCoreBridge_GetGenesisSupply(t *testing.T) { +} + +func TestZetaCoreBridge_GetZetaTokenSupplyOnNode(t *testing.T) { + expectedOutput := banktypes.QuerySupplyOfResponse{ + Amount: types.Coin{ + Denom: config.BaseDenom, + Amount: types.NewInt(329438), + }} + input := banktypes.QuerySupplyOfRequest{Denom: config.BaseDenom} + method := "/cosmos.bank.v1beta1.Query/SupplyOf" + server := setupMockServer(t, banktypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetZetaTokenSupplyOnNode() + require.NoError(t, err) + require.Equal(t, expectedOutput.GetAmount().Amount, resp) +} + +func TestZetaCoreBridge_GetLastBlockHeight(t *testing.T) { + expectedOutput := crosschainTypes.QueryAllLastBlockHeightResponse{ + LastBlockHeight: []*crosschainTypes.LastBlockHeight{ + { + Index: "test12345", + Chain: "7000", + LastSendHeight: 32345, + LastReceiveHeight: 23623, + }, + }, + } + input := crosschainTypes.QueryAllLastBlockHeightRequest{} + method := "/zetachain.zetacore.crosschain.Query/LastBlockHeightAll" + server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + t.Run("last block height", func(t *testing.T) { + resp, err := zetabridge.GetLastBlockHeight() + require.NoError(t, err) + require.Equal(t, expectedOutput.LastBlockHeight, resp) + }) +} + +func TestZetaCoreBridge_GetLatestZetaBlock(t *testing.T) { + expectedOutput := tmservice.GetLatestBlockResponse{ + SdkBlock: &tmservice.Block{ + Header: tmservice.Header{}, + Data: tmtypes.Data{}, + Evidence: tmtypes.EvidenceList{}, + LastCommit: nil, + }, + } + input := tmservice.GetLatestBlockRequest{} + method := "/cosmos.base.tendermint.v1beta1.Service/GetLatestBlock" + server := setupMockServer(t, tmservice.RegisterServiceServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetLatestZetaBlock() + require.Equal(t, expectedOutput.SdkBlock, resp) +} + +func TestZetaCoreBridge_GetNodeInfo(t *testing.T) { + expectedOutput := tmservice.GetNodeInfoResponse{ + DefaultNodeInfo: nil, + ApplicationVersion: &tmservice.VersionInfo{}, + } + input := tmservice.GetNodeInfoRequest{} + method := "/cosmos.base.tendermint.v1beta1.Service/GetNodeInfo" + server := setupMockServer(t, tmservice.RegisterServiceServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetNodeInfo() + require.NoError(t, err) + require.Equal(t, expectedOutput, *resp) +} + +func TestZetaCoreBridge_GetLastBlockHeightByChain(t *testing.T) { + index := chains.BscMainnetChain() + expectedOutput := crosschainTypes.QueryGetLastBlockHeightResponse{ + LastBlockHeight: &crosschainTypes.LastBlockHeight{ + Index: index.ChainName.String(), + Chain: "7000", + LastSendHeight: 2134123, + LastReceiveHeight: 1234333, + }, + } + input := crosschainTypes.QueryGetLastBlockHeightRequest{Index: index.ChainName.String()} + method := "/zetachain.zetacore.crosschain.Query/LastBlockHeight" + server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetLastBlockHeightByChain(index) + require.NoError(t, err) + require.Equal(t, expectedOutput.LastBlockHeight, resp) +} + +func TestZetaCoreBridge_GetZetaBlockHeight(t *testing.T) { + expectedOutput := crosschainTypes.QueryLastZetaHeightResponse{Height: 12345} + input := crosschainTypes.QueryLastZetaHeightRequest{} + method := "/zetachain.zetacore.crosschain.Query/LastZetaHeight" + server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + t.Run("get zeta block height success", func(t *testing.T) { + resp, err := zetabridge.GetZetaBlockHeight() + require.NoError(t, err) + require.Equal(t, expectedOutput.Height, resp) + }) + + t.Run("get block height success", func(t *testing.T) { + resp, err := zetabridge.GetBlockHeight() + require.NoError(t, err) + require.Equal(t, expectedOutput.Height, resp) + }) +} + +func TestZetaCoreBridge_GetBaseGasPrice(t *testing.T) { + expectedOutput := feemarkettypes.QueryParamsResponse{ + Params: feemarkettypes.Params{ + BaseFee: types.NewInt(23455), + }, + } + input := feemarkettypes.QueryParamsRequest{} + method := "/ethermint.feemarket.v1.Query/Params" + server := setupMockServer(t, feemarkettypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetBaseGasPrice() + require.NoError(t, err) + require.Equal(t, expectedOutput.Params.BaseFee.Int64(), resp) +} + +func TestZetaCoreBridge_GetNonceByChain(t *testing.T) { + chain := chains.BscMainnetChain() + expectedOutput := observertypes.QueryGetChainNoncesResponse{ + ChainNonces: observertypes.ChainNonces{ + Creator: "", + Index: "", + ChainId: chain.ChainId, + Nonce: 8446, + Signers: nil, + FinalizedHeight: 0, + }, + } + input := observertypes.QueryGetChainNoncesRequest{Index: chain.ChainName.String()} + method := "/zetachain.zetacore.observer.Query/ChainNonces" + server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetNonceByChain(chain) + require.NoError(t, err) + require.Equal(t, expectedOutput.ChainNonces, resp) +} + +func TestZetaCoreBridge_GetAllNodeAccounts(t *testing.T) { + expectedOutput := observertypes.QueryAllNodeAccountResponse{ + NodeAccount: []*observertypes.NodeAccount{ + { + Operator: "zeta19jr7nl82lrktge35f52x9g5y5prmvchmk40zhg", + GranteeAddress: "zeta1kxhesgcvl6j5upupd9m3d3g3gfz4l3pcpqfnw6", + GranteePubkey: nil, + NodeStatus: 0, + }, + }, + } + input := observertypes.QueryAllNodeAccountRequest{} + method := "/zetachain.zetacore.observer.Query/NodeAccountAll" + server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetAllNodeAccounts() + require.NoError(t, err) + require.Equal(t, expectedOutput.NodeAccount, resp) +} + +func TestZetaCoreBridge_GetKeyGen(t *testing.T) { + expectedOutput := observertypes.QueryGetKeygenResponse{ + Keygen: &observertypes.Keygen{ + Status: observertypes.KeygenStatus_KeyGenSuccess, + GranteePubkeys: nil, + BlockNumber: 5646, + }} + input := observertypes.QueryGetKeygenRequest{} + method := "/zetachain.zetacore.observer.Query/Keygen" + server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetKeyGen() + require.NoError(t, err) + require.Equal(t, expectedOutput.Keygen, resp) +} + +func TestZetaCoreBridge_GetBallotByID(t *testing.T) { + expectedOutput := observertypes.QueryBallotByIdentifierResponse{ + BallotIdentifier: "ballot1235", + } + input := observertypes.QueryBallotByIdentifierRequest{BallotIdentifier: "ballot1235"} + method := "/zetachain.zetacore.observer.Query/BallotByIdentifier" + server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetBallot("ballot1235") + require.NoError(t, err) + require.Equal(t, expectedOutput, *resp) +} + +func TestZetaCoreBridge_GetInboundTrackersForChain(t *testing.T) { + chainID := chains.BscMainnetChain().ChainId + expectedOutput := crosschainTypes.QueryAllInTxTrackerByChainResponse{ + InTxTracker: []crosschainTypes.InTxTracker{ + { + ChainId: chainID, + TxHash: "DC76A6DCCC3AA62E89E69042ADC44557C50D59E4D3210C37D78DC8AE49B3B27F", + CoinType: coin.CoinType_Gas, + }, + }, + } + input := crosschainTypes.QueryAllInTxTrackerByChainRequest{ChainId: chainID} + method := "/zetachain.zetacore.crosschain.Query/InTxTrackerAllByChain" + server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetInboundTrackersForChain(chainID) + require.NoError(t, err) + require.Equal(t, expectedOutput.InTxTracker, resp) +} + +func TestZetaCoreBridge_GetCurrentTss(t *testing.T) { + expectedOutput := observertypes.QueryGetTSSResponse{ + TSS: observertypes.TSS{ + TssPubkey: "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", + TssParticipantList: nil, + OperatorAddressList: nil, + FinalizedZetaHeight: 1000, + KeyGenZetaHeight: 900, + }, + } + input := observertypes.QueryGetTSSRequest{} + method := "/zetachain.zetacore.observer.Query/TSS" + server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetCurrentTss() + require.NoError(t, err) + require.Equal(t, expectedOutput.TSS, resp) +} + +func TestZetaCoreBridge_GetEthTssAddress(t *testing.T) { + expectedOutput := observertypes.QueryGetTssAddressResponse{ + Eth: "0x70e967acfcc17c3941e87562161406d41676fd83", + Btc: "bc1qm24wp577nk8aacckv8np465z3dvmu7ry45el6y", + } + input := observertypes.QueryGetTssAddressRequest{} + method := "/zetachain.zetacore.observer.Query/GetTssAddress" + server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetEthTssAddress() + require.NoError(t, err) + require.Equal(t, expectedOutput.Eth, resp) +} + +func TestZetaCoreBridge_GetBtcTssAddress(t *testing.T) { + expectedOutput := observertypes.QueryGetTssAddressResponse{ + Eth: "0x70e967acfcc17c3941e87562161406d41676fd83", + Btc: "bc1qm24wp577nk8aacckv8np465z3dvmu7ry45el6y", + } + input := observertypes.QueryGetTssAddressRequest{BitcoinChainId: 8332} + method := "/zetachain.zetacore.observer.Query/GetTssAddress" + server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetBtcTssAddress(8332) + require.NoError(t, err) + require.Equal(t, expectedOutput.Btc, resp) +} + +func TestZetaCoreBridge_GetTssHistory(t *testing.T) { + expectedOutput := observertypes.QueryTssHistoryResponse{ + TssList: []observertypes.TSS{ + { + TssPubkey: "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", + TssParticipantList: nil, + OperatorAddressList: nil, + FinalizedZetaHeight: 46546, + KeyGenZetaHeight: 6897, + }, + }, + } + input := observertypes.QueryTssHistoryRequest{} + method := "/zetachain.zetacore.observer.Query/TssHistory" + server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetTssHistory() + require.NoError(t, err) + require.Equal(t, expectedOutput.TssList, resp) +} + +func TestZetaCoreBridge_GetOutTxTracker(t *testing.T) { + chain := chains.BscMainnetChain() + expectedOutput := crosschainTypes.QueryGetOutTxTrackerResponse{ + OutTxTracker: crosschainTypes.OutTxTracker{ + Index: "tracker12345", + ChainId: chain.ChainId, + Nonce: 456, + HashList: nil, + }, + } + input := crosschainTypes.QueryGetOutTxTrackerRequest{ + ChainID: chain.ChainId, + Nonce: 456, + } + method := "/zetachain.zetacore.crosschain.Query/OutTxTracker" + server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetOutTxTracker(chain, 456) + require.NoError(t, err) + require.Equal(t, expectedOutput.OutTxTracker, *resp) +} + +func TestZetaCoreBridge_GetAllOutTxTrackerByChain(t *testing.T) { + chain := chains.BscMainnetChain() + expectedOutput := crosschainTypes.QueryAllOutTxTrackerByChainResponse{ + OutTxTracker: []crosschainTypes.OutTxTracker{ + { + Index: "tracker23456", + ChainId: chain.ChainId, + Nonce: 123456, + HashList: nil, + }, + }, + } + input := crosschainTypes.QueryAllOutTxTrackerByChainRequest{ + Chain: chain.ChainId, + Pagination: &query.PageRequest{ + Key: nil, + Offset: 0, + Limit: 2000, + CountTotal: false, + Reverse: false, + }, + } + method := "/zetachain.zetacore.crosschain.Query/OutTxTrackerAllByChain" + server := setupMockServer(t, crosschainTypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetAllOutTxTrackerByChain(chain.ChainId, interfaces.Ascending) + require.NoError(t, err) + require.Equal(t, expectedOutput.OutTxTracker, resp) + + resp, err = zetabridge.GetAllOutTxTrackerByChain(chain.ChainId, interfaces.Descending) + require.NoError(t, err) + require.Equal(t, expectedOutput.OutTxTracker, resp) +} + +func TestZetaCoreBridge_GetPendingNoncesByChain(t *testing.T) { + expectedOutput := observertypes.QueryPendingNoncesByChainResponse{ + PendingNonces: observertypes.PendingNonces{ + NonceLow: 0, + NonceHigh: 0, + ChainId: chains.EthChain().ChainId, + Tss: "", + }, + } + input := observertypes.QueryPendingNoncesByChainRequest{ChainId: chains.EthChain().ChainId} + method := "/zetachain.zetacore.observer.Query/PendingNoncesByChain" + server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetPendingNoncesByChain(chains.EthChain().ChainId) + require.NoError(t, err) + require.Equal(t, expectedOutput.PendingNonces, resp) +} + +func TestZetaCoreBridge_GetBlockHeaderStateByChain(t *testing.T) { + chainID := chains.BscMainnetChain().ChainId + expectedOutput := observertypes.QueryGetBlockHeaderStateResponse{BlockHeaderState: &observertypes.BlockHeaderState{ + ChainId: chainID, + LatestHeight: 5566654, + EarliestHeight: 4454445, + LatestBlockHash: nil, + }} + input := observertypes.QueryGetBlockHeaderStateRequest{ChainId: chainID} + method := "/zetachain.zetacore.observer.Query/GetBlockHeaderStateByChain" + server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetBlockHeaderStateByChain(chainID) + require.NoError(t, err) + require.Equal(t, expectedOutput, resp) +} + +func TestZetaCoreBridge_GetSupportedChains(t *testing.T) { + expectedOutput := observertypes.QuerySupportedChainsResponse{ + Chains: []*chains.Chain{ + {chains.BscMainnetChain().ChainName, + chains.BscMainnetChain().ChainId, + }, + {chains.EthChain().ChainName, + chains.EthChain().ChainId, + }, + }, + } + input := observertypes.QuerySupportedChains{} + method := "/zetachain.zetacore.observer.Query/SupportedChains" + server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetSupportedChains() + require.NoError(t, err) + require.Equal(t, expectedOutput.Chains, resp) +} + +func TestZetaCoreBridge_GetPendingNonces(t *testing.T) { + expectedOutput := observertypes.QueryAllPendingNoncesResponse{ + PendingNonces: []observertypes.PendingNonces{ + { + NonceLow: 225, + NonceHigh: 226, + ChainId: 8332, + Tss: "bc1qm24wp577nk8aacckv8np465z3dvmu7ry45el6y", + }, + }, + } + input := observertypes.QueryAllPendingNoncesRequest{} + method := "/zetachain.zetacore.observer.Query/PendingNoncesAll" + server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.GetPendingNonces() + require.NoError(t, err) + require.Equal(t, expectedOutput, *resp) +} + +func TestZetaCoreBridge_Prove(t *testing.T) { + chainId := chains.BscMainnetChain().ChainId + txHash := "9c8d02b6956b9c78ecb6090a8160faaa48e7aecfd0026fcdf533721d861436a3" + blockHash := "0000000000000000000172c9a64f86f208b867a84dc7a0b7c75be51e750ed8eb" + txIndex := 555 + expectedOutput := observertypes.QueryProveResponse{ + Valid: true, + } + input := observertypes.QueryProveRequest{ + ChainId: chainId, + TxHash: txHash, + Proof: nil, + BlockHash: blockHash, + TxIndex: int64(txIndex), + } + method := "/zetachain.zetacore.observer.Query/Prove" + server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.Prove(blockHash, txHash, int64(txIndex), nil, chainId) + require.NoError(t, err) + require.Equal(t, expectedOutput.Valid, resp) +} + +func TestZetaCoreBridge_HasVoted(t *testing.T) { + expectedOutput := observertypes.QueryHasVotedResponse{HasVoted: true} + input := observertypes.QueryHasVotedRequest{ + BallotIdentifier: "123456asdf", + VoterAddress: "zeta1l40mm7meacx03r4lp87s9gkxfan32xnznp42u6", + } + method := "/zetachain.zetacore.observer.Query/HasVoted" + server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + + resp, err := zetabridge.HasVoted("123456asdf", "zeta1l40mm7meacx03r4lp87s9gkxfan32xnznp42u6") + require.NoError(t, err) + require.Equal(t, expectedOutput.HasVoted, resp) +} + +func TestZetaCoreBridge_GetZetaHotKeyBalance(t *testing.T) { + expectedOutput := banktypes.QueryBalanceResponse{ + Balance: &types.Coin{ + Denom: config.BaseDenom, + Amount: types.NewInt(55646484), + }, + } + input := banktypes.QueryBalanceRequest{ + Address: types.AccAddress(stub.TestKeyringPair.PubKey().Address().Bytes()).String(), + Denom: config.BaseDenom, + } + method := "/cosmos.bank.v1beta1.Query/Balance" + server := setupMockServer(t, banktypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + zetabridge.keys = keys.NewKeysWithKeybase(stub.NewKeyring(), types.AccAddress{}, "", "") + + resp, err := zetabridge.GetZetaHotKeyBalance() + require.NoError(t, err) + require.Equal(t, expectedOutput.Balance.Amount, resp) +} diff --git a/zetaclient/zetabridge/tx.go b/zetaclient/zetabridge/tx.go index 90e44b16f5..e7cbebd06a 100644 --- a/zetaclient/zetabridge/tx.go +++ b/zetaclient/zetabridge/tx.go @@ -61,7 +61,7 @@ func (b *ZetaCoreBridge) PostGasPrice(chain chains.Chain, gasPrice uint64, suppl } for i := 0; i < DefaultRetryCount; i++ { - zetaTxHash, err := b.Broadcast(PostGasPriceGasLimit, authzMsg, authzSigner) + zetaTxHash, err := zetaBridgeBroadcast(b, PostGasPriceGasLimit, authzMsg, authzSigner) if err == nil { return zetaTxHash, nil } @@ -97,7 +97,7 @@ func (b *ZetaCoreBridge) AddTxHashToOutTxTracker( return "", err } - zetaTxHash, err := b.Broadcast(AddTxHashToOutTxTrackerGasLimit, authzMsg, authzSigner) + zetaTxHash, err := zetaBridgeBroadcast(b, AddTxHashToOutTxTrackerGasLimit, authzMsg, authzSigner) if err != nil { return "", err } @@ -115,7 +115,7 @@ func (b *ZetaCoreBridge) SetTSS(tssPubkey string, keyGenZetaHeight int64, status zetaTxHash := "" for i := 0; i <= DefaultRetryCount; i++ { - zetaTxHash, err = b.Broadcast(DefaultGasLimit, authzMsg, authzSigner) + zetaTxHash, err = zetaBridgeBroadcast(b, DefaultGasLimit, authzMsg, authzSigner) if err == nil { return zetaTxHash, nil } @@ -162,7 +162,7 @@ func (b *ZetaCoreBridge) PostBlameData(blame *blame.Blame, chainID int64, index var gasLimit uint64 = PostBlameDataGasLimit for i := 0; i < DefaultRetryCount; i++ { - zetaTxHash, err := b.Broadcast(gasLimit, authzMsg, authzSigner) + zetaTxHash, err := zetaBridgeBroadcast(b, gasLimit, authzMsg, authzSigner) if err == nil { return zetaTxHash, nil } @@ -184,7 +184,7 @@ func (b *ZetaCoreBridge) PostAddBlockHeader(chainID int64, blockHash []byte, hei var gasLimit uint64 = DefaultGasLimit for i := 0; i < DefaultRetryCount; i++ { - zetaTxHash, err := b.Broadcast(gasLimit, authzMsg, authzSigner) + zetaTxHash, err := zetaBridgeBroadcast(b, gasLimit, authzMsg, authzSigner) if err == nil { return zetaTxHash, nil } @@ -214,7 +214,7 @@ func (b *ZetaCoreBridge) PostVoteInbound(gasLimit, retryGasLimit uint64, msg *ty } for i := 0; i < DefaultRetryCount; i++ { - zetaTxHash, err := b.Broadcast(gasLimit, authzMsg, authzSigner) + zetaTxHash, err := zetaBridgeBroadcast(b, gasLimit, authzMsg, authzSigner) if err == nil { // monitor the result of the transaction and resend if necessary go b.MonitorVoteInboundTxResult(zetaTxHash, retryGasLimit, msg) @@ -338,7 +338,7 @@ func (b *ZetaCoreBridge) PostVoteOutboundFromMsg(gasLimit, retryGasLimit uint64, return "", ballotIndex, nil } for i := 0; i < DefaultRetryCount; i++ { - zetaTxHash, err := b.Broadcast(gasLimit, authzMsg, authzSigner) + zetaTxHash, err := zetaBridgeBroadcast(b, gasLimit, authzMsg, authzSigner) if err == nil { // monitor the result of the transaction and resend if necessary go b.MonitorVoteOutboundTxResult(zetaTxHash, retryGasLimit, msg) diff --git a/zetaclient/zetabridge/tx_test.go b/zetaclient/zetabridge/tx_test.go index 50cd82a929..20da71f1d2 100644 --- a/zetaclient/zetabridge/tx_test.go +++ b/zetaclient/zetabridge/tx_test.go @@ -1,9 +1,37 @@ package zetabridge import ( + "bytes" + "encoding/hex" + "errors" + "math/big" + "net" + "os" "testing" + "cosmossdk.io/math" + sdktypes "github.com/cosmos/cosmos-sdk/types" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/require" + "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" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" + "github.com/zeta-chain/zetacore/zetaclient/authz" + "github.com/zeta-chain/zetacore/zetaclient/config" + corecontext "github.com/zeta-chain/zetacore/zetaclient/core_context" + "github.com/zeta-chain/zetacore/zetaclient/keys" + "github.com/zeta-chain/zetacore/zetaclient/testutils/stub" + "go.nhat.io/grpcmock" + "go.nhat.io/grpcmock/planner" +) + +const ( + sampleHash = "fa51db4412144f1130669f2bae8cb44aadbd8d85958dbffcb0fe236878097e1a" + ethBlockHash = "1a17bcc359e84ba8ae03b17ec425f97022cd11c3e279f6bdf7a96fcffa12b366" ) func Test_GasPriceMultiplier(t *testing.T) { @@ -80,3 +108,364 @@ func Test_GasPriceMultiplier(t *testing.T) { }) } } + +func ZetaBridgeBroadcastTest(_ *ZetaCoreBridge, _ uint64, _ sdktypes.Msg, _ authz.Signer) (string, error) { + return sampleHash, nil +} + +func ZetaBridgeBroadcastTestErr(_ *ZetaCoreBridge, _ uint64, _ sdktypes.Msg, _ authz.Signer) (string, error) { + return sampleHash, errors.New("broadcast error") +} + +func getHeaderData(t *testing.T) proofs.HeaderData { + var header ethtypes.Header + file, err := os.Open("../../pkg/testdata/eth_header_18495266.json") + require.NoError(t, err) + defer file.Close() + headerBytes := make([]byte, 4096) + n, err := file.Read(headerBytes) + require.NoError(t, err) + err = header.UnmarshalJSON(headerBytes[:n]) + require.NoError(t, err) + var buffer bytes.Buffer + err = header.EncodeRLP(&buffer) + require.NoError(t, err) + return proofs.NewEthereumHeader(buffer.Bytes()) +} + +func TestZetaCoreBridge_PostGasPrice(t *testing.T) { + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + address := sdktypes.AccAddress(stub.TestKeyringPair.PubKey().Address().Bytes()) + zetabridge.keys = keys.NewKeysWithKeybase(stub.NewKeyring(), address, "", "") + + t.Run("post gas price success", func(t *testing.T) { + zetaBridgeBroadcast = ZetaBridgeBroadcastTest + hash, err := zetabridge.PostGasPrice(chains.BscMainnetChain(), 1000000, "100", 1234) + require.NoError(t, err) + require.Equal(t, sampleHash, hash) + }) + + // Test for failed broadcast, it will take several seconds to complete. Excluding to reduce runtime. + // + //t.Run("post gas price fail", func(t *testing.T) { + // zetaBridgeBroadcast = ZetaBridgeBroadcastTestErr + // hash, err := zetabridge.PostGasPrice(chains.BscMainnetChain(), 1000000, "100", 1234) + // require.ErrorContains(t, err, "post gasprice failed") + // require.Equal(t, "", hash) + //}) +} + +func TestZetaCoreBridge_AddTxHashToOutTxTracker(t *testing.T) { + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + address := sdktypes.AccAddress(stub.TestKeyringPair.PubKey().Address().Bytes()) + zetabridge.keys = keys.NewKeysWithKeybase(stub.NewKeyring(), address, "", "") + + t.Run("add tx hash success", func(t *testing.T) { + zetaBridgeBroadcast = ZetaBridgeBroadcastTest + hash, err := zetabridge.AddTxHashToOutTxTracker(chains.BscMainnetChain().ChainId, 123, "", nil, "", 456) + require.NoError(t, err) + require.Equal(t, sampleHash, hash) + }) + + t.Run("add tx hash fail", func(t *testing.T) { + zetaBridgeBroadcast = ZetaBridgeBroadcastTestErr + hash, err := zetabridge.AddTxHashToOutTxTracker(chains.BscMainnetChain().ChainId, 123, "", nil, "", 456) + require.Error(t, err) + require.Equal(t, "", hash) + }) +} + +func TestZetaCoreBridge_SetTSS(t *testing.T) { + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + address := sdktypes.AccAddress(stub.TestKeyringPair.PubKey().Address().Bytes()) + zetabridge.keys = keys.NewKeysWithKeybase(stub.NewKeyring(), address, "", "") + + t.Run("set tss success", func(t *testing.T) { + zetaBridgeBroadcast = ZetaBridgeBroadcastTest + hash, err := zetabridge.SetTSS( + "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", + 9987, + chains.ReceiveStatus_Success, + ) + require.NoError(t, err) + require.Equal(t, sampleHash, hash) + }) +} + +func TestZetaCoreBridge_UpdateZetaCoreContext(t *testing.T) { + //Setup server for multiple grpc calls + listener, err := net.Listen("tcp", "127.0.0.1:9090") + require.NoError(t, err) + + server := grpcmock.MockUnstartedServer( + grpcmock.RegisterService(crosschaintypes.RegisterQueryServer), + grpcmock.RegisterService(upgradetypes.RegisterQueryServer), + grpcmock.RegisterService(observertypes.RegisterQueryServer), + grpcmock.WithPlanner(planner.FirstMatch()), + grpcmock.WithListener(listener), + func(s *grpcmock.Server) { + method := "/zetachain.zetacore.crosschain.Query/LastZetaHeight" + s.ExpectUnary(method). + UnlimitedTimes(). + WithPayload(crosschaintypes.QueryLastZetaHeightRequest{}). + Return(crosschaintypes.QueryLastZetaHeightResponse{Height: 12345}) + + method = "/cosmos.upgrade.v1beta1.Query/CurrentPlan" + s.ExpectUnary(method). + UnlimitedTimes(). + WithPayload(upgradetypes.QueryCurrentPlanRequest{}). + Return(upgradetypes.QueryCurrentPlanResponse{ + Plan: &upgradetypes.Plan{ + Name: "big upgrade", + Height: 100, + }, + }) + + method = "/zetachain.zetacore.observer.Query/GetChainParams" + s.ExpectUnary(method). + UnlimitedTimes(). + WithPayload(observertypes.QueryGetChainParamsRequest{}). + Return(observertypes.QueryGetChainParamsResponse{ChainParams: &observertypes.ChainParamsList{ + ChainParams: []*observertypes.ChainParams{ + { + ChainId: 7000, + }, + }, + }}) + + method = "/zetachain.zetacore.observer.Query/SupportedChains" + s.ExpectUnary(method). + UnlimitedTimes(). + WithPayload(observertypes.QuerySupportedChains{}). + Return(observertypes.QuerySupportedChainsResponse{ + Chains: []*chains.Chain{ + {chains.BscMainnetChain().ChainName, + chains.BscMainnetChain().ChainId, + }, + {chains.EthChain().ChainName, + chains.EthChain().ChainId, + }, + }, + }) + + method = "/zetachain.zetacore.observer.Query/Keygen" + s.ExpectUnary(method). + UnlimitedTimes(). + WithPayload(observertypes.QueryGetKeygenRequest{}). + Return(observertypes.QueryGetKeygenResponse{ + Keygen: &observertypes.Keygen{ + Status: observertypes.KeygenStatus_KeyGenSuccess, + GranteePubkeys: nil, + BlockNumber: 5646, + }}) + + method = "/zetachain.zetacore.observer.Query/TSS" + s.ExpectUnary(method). + UnlimitedTimes(). + WithPayload(observertypes.QueryGetTSSRequest{}). + Return(observertypes.QueryGetTSSResponse{ + TSS: observertypes.TSS{ + TssPubkey: "zetapub1addwnpepqtadxdyt037h86z60nl98t6zk56mw5zpnm79tsmvspln3hgt5phdc79kvfc", + TssParticipantList: nil, + OperatorAddressList: nil, + FinalizedZetaHeight: 1000, + KeyGenZetaHeight: 900, + }, + }) + + method = "/zetachain.zetacore.observer.Query/CrosschainFlags" + s.ExpectUnary(method). + UnlimitedTimes(). + WithPayload(observertypes.QueryGetCrosschainFlagsRequest{}). + Return(observertypes.QueryGetCrosschainFlagsResponse{CrosschainFlags: observertypes.CrosschainFlags{ + IsInboundEnabled: true, + IsOutboundEnabled: false, + GasPriceIncreaseFlags: nil, + BlockHeaderVerificationFlags: nil, + }}) + }, + )(t) + + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + address := sdktypes.AccAddress(stub.TestKeyringPair.PubKey().Address().Bytes()) + zetabridge.keys = keys.NewKeysWithKeybase(stub.NewKeyring(), address, "", "") + zetabridge.EnableMockSDKClient(stub.NewSDKClientWithErr(nil, 0)) + + t.Run("core context update success", func(t *testing.T) { + cfg := config.NewConfig() + coreCtx := corecontext.NewZetaCoreContext(cfg) + zetaBridgeBroadcast = ZetaBridgeBroadcastTest + err := zetabridge.UpdateZetaCoreContext(coreCtx, false) + require.NoError(t, err) + }) +} + +func TestZetaCoreBridge_PostBlameData(t *testing.T) { + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + address := sdktypes.AccAddress(stub.TestKeyringPair.PubKey().Address().Bytes()) + zetabridge.keys = keys.NewKeysWithKeybase(stub.NewKeyring(), address, "", "") + + t.Run("post blame data success", func(t *testing.T) { + zetaBridgeBroadcast = ZetaBridgeBroadcastTest + hash, err := zetabridge.PostBlameData( + &blame.Blame{ + FailReason: "", + IsUnicast: false, + BlameNodes: nil, + }, + chains.BscMainnetChain().ChainId, + "102394876-bsc", + ) + require.NoError(t, err) + require.Equal(t, sampleHash, hash) + }) +} + +func TestZetaCoreBridge_PostAddBlockHeader(t *testing.T) { + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + address := sdktypes.AccAddress(stub.TestKeyringPair.PubKey().Address().Bytes()) + zetabridge.keys = keys.NewKeysWithKeybase(stub.NewKeyring(), address, "", "") + blockHash, err := hex.DecodeString(ethBlockHash) + require.NoError(t, err) + + t.Run("post add block header success", func(t *testing.T) { + zetaBridgeBroadcast = ZetaBridgeBroadcastTest + hash, err := zetabridge.PostAddBlockHeader( + chains.EthChain().ChainId, + blockHash, + 18495266, + getHeaderData(t), + ) + require.NoError(t, err) + require.Equal(t, sampleHash, hash) + }) +} + +func TestZetaCoreBridge_PostVoteInbound(t *testing.T) { + address := sdktypes.AccAddress(stub.TestKeyringPair.PubKey().Address().Bytes()) + + expectedOutput := observertypes.QueryHasVotedResponse{HasVoted: false} + input := observertypes.QueryHasVotedRequest{ + BallotIdentifier: "0x2d10e9b7ce7921fa6b61ada3020d1c797d5ec52424cdcf86ef31cbbbcd45db58", + VoterAddress: address.String(), + } + method := "/zetachain.zetacore.observer.Query/HasVoted" + server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + zetabridge.keys = keys.NewKeysWithKeybase(stub.NewKeyring(), address, "", "") + zetabridge.EnableMockSDKClient(stub.NewSDKClientWithErr(nil, 0)) + + t.Run("post inbound vote already voted", func(t *testing.T) { + zetaBridgeBroadcast = ZetaBridgeBroadcastTest + hash, _, err := zetabridge.PostVoteInbound(100, 200, &crosschaintypes.MsgVoteOnObservedInboundTx{ + Creator: address.String(), + }) + require.NoError(t, err) + require.Equal(t, sampleHash, hash) + }) +} + +func TestZetaCoreBridge_GetInBoundVoteMessage(t *testing.T) { + address := sdktypes.AccAddress(stub.TestKeyringPair.PubKey().Address().Bytes()) + t.Run("get inbound vote message", func(t *testing.T) { + zetaBridgeBroadcast = ZetaBridgeBroadcastTest + msg := GetInBoundVoteMessage( + address.String(), + chains.EthChain().ChainId, + "", + address.String(), + chains.ZetaChainMainnet().ChainId, + math.NewUint(500), + "", + "", 12345, + 1000, + coin.CoinType_Gas, + "azeta", + address.String(), + 0) + require.Equal(t, address.String(), msg.Creator) + }) +} + +func TestZetaCoreBridge_MonitorVoteInboundTxResult(t *testing.T) { + address := sdktypes.AccAddress(stub.TestKeyringPair.PubKey().Address().Bytes()) + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + zetabridge.keys = keys.NewKeysWithKeybase(stub.NewKeyring(), address, "", "") + zetabridge.EnableMockSDKClient(stub.NewSDKClientWithErr(nil, 0)) + + t.Run("monitor inbound vote", func(t *testing.T) { + zetaBridgeBroadcast = ZetaBridgeBroadcastTest + zetabridge.MonitorVoteInboundTxResult(sampleHash, 1000, &crosschaintypes.MsgVoteOnObservedInboundTx{ + Creator: address.String(), + }) + // Nothing to verify against this function + // Just running through without panic + }) +} + +func TestZetaCoreBridge_PostVoteOutbound(t *testing.T) { + address := sdktypes.AccAddress(stub.TestKeyringPair.PubKey().Address().Bytes()) + + expectedOutput := observertypes.QueryHasVotedResponse{HasVoted: false} + input := observertypes.QueryHasVotedRequest{ + BallotIdentifier: "0xc507c67847209b403def6d944486ff888c442eccf924cf9ebdc48714b22b5347", + VoterAddress: address.String(), + } + method := "/zetachain.zetacore.observer.Query/HasVoted" + server := setupMockServer(t, observertypes.RegisterQueryServer, method, input, expectedOutput) + server.Serve() + defer closeMockServer(t, server) + + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + zetabridge.keys = keys.NewKeysWithKeybase(stub.NewKeyring(), address, "", "") + zetabridge.EnableMockSDKClient(stub.NewSDKClientWithErr(nil, 0)) + + zetaBridgeBroadcast = ZetaBridgeBroadcastTest + hash, ballot, err := zetabridge.PostVoteOutbound( + sampleHash, + sampleHash, + 1234, + 1000, + big.NewInt(100), + 1200, + big.NewInt(500), + chains.ReceiveStatus_Success, + chains.EthChain(), + 10001, + coin.CoinType_Gas) + require.NoError(t, err) + require.Equal(t, sampleHash, hash) + require.Equal(t, "0xc507c67847209b403def6d944486ff888c442eccf924cf9ebdc48714b22b5347", ballot) +} + +func TestZetaCoreBridge_MonitorVoteOutboundTxResult(t *testing.T) { + address := sdktypes.AccAddress(stub.TestKeyringPair.PubKey().Address().Bytes()) + zetabridge, err := setupCoreBridge() + require.NoError(t, err) + zetabridge.keys = keys.NewKeysWithKeybase(stub.NewKeyring(), address, "", "") + zetabridge.EnableMockSDKClient(stub.NewSDKClientWithErr(nil, 0)) + + t.Run("monitor outbound vote", func(t *testing.T) { + zetaBridgeBroadcast = ZetaBridgeBroadcastTest + zetabridge.MonitorVoteOutboundTxResult(sampleHash, 1000, &crosschaintypes.MsgVoteOnObservedOutboundTx{ + Creator: address.String(), + }) + // Nothing to verify against this function + // Just running through without panic + }) +} diff --git a/zetaclient/zetabridge/zetacore_bridge.go b/zetaclient/zetabridge/zetacore_bridge.go index d8a29fa665..0de3f994b0 100644 --- a/zetaclient/zetabridge/zetacore_bridge.go +++ b/zetaclient/zetabridge/zetacore_bridge.go @@ -10,9 +10,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/hashicorp/go-retryablehttp" "github.com/rs/zerolog" "github.com/rs/zerolog/log" + rpcclient "github.com/tendermint/tendermint/rpc/client" "github.com/zeta-chain/zetacore/app" "github.com/zeta-chain/zetacore/pkg/authz" "github.com/zeta-chain/zetacore/pkg/chains" @@ -35,7 +35,6 @@ type ZetaCoreBridge struct { accountNumber map[authz.KeyType]uint64 seqNumber map[authz.KeyType]uint64 grpcConn *grpc.ClientConn - httpClient *retryablehttp.Client cfg config.ClientConfiguration encodingCfg params.EncodingConfig keys *keys.Keys @@ -45,6 +44,11 @@ type ZetaCoreBridge struct { stop chan struct{} pause chan struct{} Telemetry *metrics.TelemetryServer + + // enableMockSDKClient is a flag that determines whether the mock cosmos sdk client should be used, primarily for + // unit testing + enableMockSDKClient bool + mockSDKClient rpcclient.Client } // NewZetaCoreBridge create a new instance of ZetaCoreBridge @@ -67,9 +71,6 @@ func NewZetaCoreBridge( HsmMode: hsmMode, } - httpClient := retryablehttp.NewClient() - httpClient.Logger = nil - grpcConn, err := grpc.Dial( fmt.Sprintf("%s:9090", chainIP), grpc.WithInsecure(), @@ -91,20 +92,21 @@ func NewZetaCoreBridge( } return &ZetaCoreBridge{ - logger: logger, - grpcConn: grpcConn, - httpClient: httpClient, - accountNumber: accountsMap, - seqNumber: seqMap, - cfg: cfg, - encodingCfg: app.MakeEncodingConfig(), - keys: k, - broadcastLock: &sync.RWMutex{}, - stop: make(chan struct{}), - zetaChainID: chainID, - zetaChain: zetaChain, - pause: make(chan struct{}), - Telemetry: telemetry, + logger: logger, + grpcConn: grpcConn, + accountNumber: accountsMap, + seqNumber: seqMap, + cfg: cfg, + encodingCfg: app.MakeEncodingConfig(), + keys: k, + broadcastLock: &sync.RWMutex{}, + stop: make(chan struct{}), + zetaChainID: chainID, + zetaChain: zetaChain, + pause: make(chan struct{}), + Telemetry: telemetry, + enableMockSDKClient: false, + mockSDKClient: nil, }, nil } @@ -276,3 +278,8 @@ func (b *ZetaCoreBridge) Pause() { func (b *ZetaCoreBridge) Unpause() { b.pause <- struct{}{} } + +func (b *ZetaCoreBridge) EnableMockSDKClient(client rpcclient.Client) { + b.mockSDKClient = client + b.enableMockSDKClient = true +} From 47661d4df7f9029345df4c3de447889eac3dccdb Mon Sep 17 00:00:00 2001 From: skosito Date: Fri, 5 Apr 2024 21:28:46 +0100 Subject: [PATCH 09/10] test: remove setupKeeper from crosschain module (#1992) * Remove setupKeeper from crosschain module * Changelog --------- Co-authored-by: Lucas Bertrand --- changelog.md | 1 + x/crosschain/keeper/gas_price_test.go | 27 ++++---- .../keeper/grpc_query_gas_price_test.go | 21 +++--- .../grpc_query_last_block_height_test.go | 33 +++++----- x/crosschain/keeper/keeper_test.go | 65 ------------------- x/crosschain/keeper/last_block_height_test.go | 26 ++++---- 6 files changed, 58 insertions(+), 115 deletions(-) delete mode 100644 x/crosschain/keeper/keeper_test.go diff --git a/changelog.md b/changelog.md index 73e8e04e28..da05d7b9b4 100644 --- a/changelog.md +++ b/changelog.md @@ -58,6 +58,7 @@ * [1955](https://github.com/zeta-chain/node/pull/1955) - improve emissions module coverage * [1941](https://github.com/zeta-chain/node/pull/1941) - add unit tests for zetabridge package * [1985](https://github.com/zeta-chain/node/pull/1985) - improve fungible module coverage +* [1992](https://github.com/zeta-chain/node/pull/1992) - remove setupKeeper from crosschain module ### Fixes diff --git a/x/crosschain/keeper/gas_price_test.go b/x/crosschain/keeper/gas_price_test.go index 67aeb95cd6..bdd867081e 100644 --- a/x/crosschain/keeper/gas_price_test.go +++ b/x/crosschain/keeper/gas_price_test.go @@ -1,4 +1,4 @@ -package keeper +package keeper_test import ( "strconv" @@ -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/crosschain/keeper" "github.com/zeta-chain/zetacore/x/crosschain/types" ) // Keeper Tests -func createNGasPrice(keeper *Keeper, ctx sdk.Context, n int) []types.GasPrice { +func createNGasPrice(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.GasPrice { items := make([]types.GasPrice, n) for i := range items { items[i].Creator = "any" @@ -22,26 +24,27 @@ func createNGasPrice(keeper *Keeper, ctx sdk.Context, n int) []types.GasPrice { } func TestGasPriceGet(t *testing.T) { - keeper, ctx := setupKeeper(t) - items := createNGasPrice(keeper, ctx, 10) + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + items := createNGasPrice(k, ctx, 10) for _, item := range items { - rst, found := keeper.GetGasPrice(ctx, item.ChainId) + rst, found := k.GetGasPrice(ctx, item.ChainId) require.True(t, found) require.Equal(t, item, rst) } } + func TestGasPriceRemove(t *testing.T) { - keeper, ctx := setupKeeper(t) - items := createNGasPrice(keeper, ctx, 10) + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + items := createNGasPrice(k, ctx, 10) for _, item := range items { - keeper.RemoveGasPrice(ctx, item.Index) - _, found := keeper.GetGasPrice(ctx, item.ChainId) + k.RemoveGasPrice(ctx, item.Index) + _, found := k.GetGasPrice(ctx, item.ChainId) require.False(t, found) } } func TestGasPriceGetAll(t *testing.T) { - keeper, ctx := setupKeeper(t) - items := createNGasPrice(keeper, ctx, 10) - require.Equal(t, items, keeper.GetAllGasPrice(ctx)) + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + items := createNGasPrice(k, ctx, 10) + require.Equal(t, items, k.GetAllGasPrice(ctx)) } diff --git a/x/crosschain/keeper/grpc_query_gas_price_test.go b/x/crosschain/keeper/grpc_query_gas_price_test.go index b8e9f0606e..bba8f9ae74 100644 --- a/x/crosschain/keeper/grpc_query_gas_price_test.go +++ b/x/crosschain/keeper/grpc_query_gas_price_test.go @@ -1,4 +1,4 @@ -package keeper +package keeper_test import ( "fmt" @@ -7,15 +7,16 @@ 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/crosschain/types" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) func TestGasPriceQuerySingle(t *testing.T) { - keeper, ctx := setupKeeper(t) + k, ctx, _, _ := keepertest.CrosschainKeeper(t) wctx := sdk.WrapSDKContext(ctx) - msgs := createNGasPrice(keeper, ctx, 2) + msgs := createNGasPrice(k, ctx, 2) for _, tc := range []struct { desc string request *types.QueryGetGasPriceRequest @@ -49,7 +50,7 @@ func TestGasPriceQuerySingle(t *testing.T) { } { tc := tc t.Run(tc.desc, func(t *testing.T) { - response, err := keeper.GasPrice(wctx, tc.request) + response, err := k.GasPrice(wctx, tc.request) if tc.err != nil { require.Error(t, err) } else { @@ -60,9 +61,9 @@ func TestGasPriceQuerySingle(t *testing.T) { } func TestGasPriceQueryPaginated(t *testing.T) { - keeper, ctx := setupKeeper(t) + k, ctx, _, _ := keepertest.CrosschainKeeper(t) wctx := sdk.WrapSDKContext(ctx) - msgs := createNGasPrice(keeper, ctx, 5) + msgs := createNGasPrice(k, ctx, 5) request := func(next []byte, offset, limit uint64, total bool) *types.QueryAllGasPriceRequest { return &types.QueryAllGasPriceRequest{ @@ -77,7 +78,7 @@ func TestGasPriceQueryPaginated(t *testing.T) { t.Run("ByOffset", func(t *testing.T) { step := 2 for i := 0; i < len(msgs); i += step { - resp, err := keeper.GasPriceAll(wctx, request(nil, uint64(i), uint64(step), false)) + resp, err := k.GasPriceAll(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.GasPrice[j-i]) @@ -88,7 +89,7 @@ func TestGasPriceQueryPaginated(t *testing.T) { step := 2 var next []byte for i := 0; i < len(msgs); i += step { - resp, err := keeper.GasPriceAll(wctx, request(next, 0, uint64(step), false)) + resp, err := k.GasPriceAll(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.GasPrice[j-i]) @@ -97,12 +98,12 @@ func TestGasPriceQueryPaginated(t *testing.T) { } }) t.Run("Total", func(t *testing.T) { - resp, err := keeper.GasPriceAll(wctx, request(nil, 0, 0, true)) + resp, err := k.GasPriceAll(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.GasPriceAll(wctx, nil) + _, err := k.GasPriceAll(wctx, nil) require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "invalid request")) }) } 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 057fe120b9..81e3c7d2d9 100644 --- a/x/crosschain/keeper/grpc_query_last_block_height_test.go +++ b/x/crosschain/keeper/grpc_query_last_block_height_test.go @@ -1,4 +1,4 @@ -package keeper +package keeper_test import ( "math" @@ -7,15 +7,16 @@ 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/crosschain/types" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) func TestLastBlockHeightQuerySingle(t *testing.T) { - keeper, ctx := setupKeeper(t) + k, ctx, _, _ := keepertest.CrosschainKeeper(t) wctx := sdk.WrapSDKContext(ctx) - msgs := createNLastBlockHeight(keeper, ctx, 2) + msgs := createNLastBlockHeight(k, ctx, 2) for _, tc := range []struct { desc string request *types.QueryGetLastBlockHeightRequest @@ -44,7 +45,7 @@ func TestLastBlockHeightQuerySingle(t *testing.T) { } { tc := tc t.Run(tc.desc, func(t *testing.T) { - response, err := keeper.LastBlockHeight(wctx, tc.request) + response, err := k.LastBlockHeight(wctx, tc.request) if tc.err != nil { require.ErrorIs(t, err, tc.err) } else { @@ -56,14 +57,14 @@ 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) + k, ctx, _, _ := keepertest.CrosschainKeeper(t) wctx := sdk.WrapSDKContext(ctx) - keeper.SetLastBlockHeight(ctx, types.LastBlockHeight{ + k.SetLastBlockHeight(ctx, types.LastBlockHeight{ Index: "index", LastSendHeight: math.MaxInt64, }) - res, err := keeper.LastBlockHeight(wctx, &types.QueryGetLastBlockHeightRequest{ + res, err := k.LastBlockHeight(wctx, &types.QueryGetLastBlockHeightRequest{ Index: "index", }) require.Nil(t, res) @@ -71,15 +72,15 @@ func TestLastBlockHeightLimits(t *testing.T) { }) t.Run("should err if last receive height is max int", func(t *testing.T) { - keeper, ctx := setupKeeper(t) + k, ctx, _, _ := keepertest.CrosschainKeeper(t) wctx := sdk.WrapSDKContext(ctx) - keeper.SetLastBlockHeight(ctx, types.LastBlockHeight{ + k.SetLastBlockHeight(ctx, types.LastBlockHeight{ Index: "index", LastSendHeight: 10, LastReceiveHeight: math.MaxInt64, }) - res, err := keeper.LastBlockHeight(wctx, &types.QueryGetLastBlockHeightRequest{ + res, err := k.LastBlockHeight(wctx, &types.QueryGetLastBlockHeightRequest{ Index: "index", }) require.Nil(t, res) @@ -88,9 +89,9 @@ func TestLastBlockHeightLimits(t *testing.T) { } func TestLastBlockHeightQueryPaginated(t *testing.T) { - keeper, ctx := setupKeeper(t) + k, ctx, _, _ := keepertest.CrosschainKeeper(t) wctx := sdk.WrapSDKContext(ctx) - msgs := createNLastBlockHeight(keeper, ctx, 5) + msgs := createNLastBlockHeight(k, ctx, 5) request := func(next []byte, offset, limit uint64, total bool) *types.QueryAllLastBlockHeightRequest { return &types.QueryAllLastBlockHeightRequest{ @@ -105,7 +106,7 @@ func TestLastBlockHeightQueryPaginated(t *testing.T) { t.Run("ByOffset", func(t *testing.T) { step := 2 for i := 0; i < len(msgs); i += step { - resp, err := keeper.LastBlockHeightAll(wctx, request(nil, uint64(i), uint64(step), false)) + resp, err := k.LastBlockHeightAll(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.LastBlockHeight[j-i]) @@ -116,7 +117,7 @@ func TestLastBlockHeightQueryPaginated(t *testing.T) { step := 2 var next []byte for i := 0; i < len(msgs); i += step { - resp, err := keeper.LastBlockHeightAll(wctx, request(next, 0, uint64(step), false)) + resp, err := k.LastBlockHeightAll(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.LastBlockHeight[j-i]) @@ -125,12 +126,12 @@ func TestLastBlockHeightQueryPaginated(t *testing.T) { } }) t.Run("Total", func(t *testing.T) { - resp, err := keeper.LastBlockHeightAll(wctx, request(nil, 0, 0, true)) + resp, err := k.LastBlockHeightAll(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.LastBlockHeightAll(wctx, nil) + _, err := k.LastBlockHeightAll(wctx, nil) require.ErrorIs(t, err, status.Error(codes.InvalidArgument, "invalid request")) }) } diff --git a/x/crosschain/keeper/keeper_test.go b/x/crosschain/keeper/keeper_test.go deleted file mode 100644 index 58cc10896e..0000000000 --- a/x/crosschain/keeper/keeper_test.go +++ /dev/null @@ -1,65 +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" - authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" - bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" - typesparams "github.com/cosmos/cosmos-sdk/x/params/types" - stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/libs/log" - 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/crosschain/types" - fungiblekeeper "github.com/zeta-chain/zetacore/x/fungible/keeper" - "github.com/zeta-chain/zetacore/x/observer/keeper" -) - -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, - "ZetacoreParams", - ) - bankKeeper := bankkeeper.BaseKeeper{} - authKeeper := authkeeper.AccountKeeper{} - observerKeeper := keeper.Keeper{} - fungibleKeeper := fungiblekeeper.Keeper{} - authorityKeeper := authoritykeeper.Keeper{} - - k := NewKeeper( - codec.NewProtoCodec(registry), - storeKey, - memStoreKey, - stakingkeeper.Keeper{}, // custom - paramsSubspace, - authKeeper, - bankKeeper, - observerKeeper, - &fungibleKeeper, - authorityKeeper, - ) - - ctx := sdk.NewContext(stateStore, tmproto.Header{}, false, log.NewNopLogger()) - return k, ctx -} diff --git a/x/crosschain/keeper/last_block_height_test.go b/x/crosschain/keeper/last_block_height_test.go index 8a284f6f97..707b89ff02 100644 --- a/x/crosschain/keeper/last_block_height_test.go +++ b/x/crosschain/keeper/last_block_height_test.go @@ -1,4 +1,4 @@ -package keeper +package keeper_test import ( "fmt" @@ -6,10 +6,12 @@ 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/crosschain/keeper" "github.com/zeta-chain/zetacore/x/crosschain/types" ) -func createNLastBlockHeight(keeper *Keeper, ctx sdk.Context, n int) []types.LastBlockHeight { +func createNLastBlockHeight(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.LastBlockHeight { items := make([]types.LastBlockHeight, n) for i := range items { items[i].Creator = "any" @@ -20,26 +22,26 @@ func createNLastBlockHeight(keeper *Keeper, ctx sdk.Context, n int) []types.Last } func TestLastBlockHeightGet(t *testing.T) { - keeper, ctx := setupKeeper(t) - items := createNLastBlockHeight(keeper, ctx, 10) + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + items := createNLastBlockHeight(k, ctx, 10) for _, item := range items { - rst, found := keeper.GetLastBlockHeight(ctx, item.Index) + rst, found := k.GetLastBlockHeight(ctx, item.Index) require.True(t, found) require.Equal(t, item, rst) } } func TestLastBlockHeightRemove(t *testing.T) { - keeper, ctx := setupKeeper(t) - items := createNLastBlockHeight(keeper, ctx, 10) + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + items := createNLastBlockHeight(k, ctx, 10) for _, item := range items { - keeper.RemoveLastBlockHeight(ctx, item.Index) - _, found := keeper.GetLastBlockHeight(ctx, item.Index) + k.RemoveLastBlockHeight(ctx, item.Index) + _, found := k.GetLastBlockHeight(ctx, item.Index) require.False(t, found) } } func TestLastBlockHeightGetAll(t *testing.T) { - keeper, ctx := setupKeeper(t) - items := createNLastBlockHeight(keeper, ctx, 10) - require.Equal(t, items, keeper.GetAllLastBlockHeight(ctx)) + k, ctx, _, _ := keepertest.CrosschainKeeper(t) + items := createNLastBlockHeight(k, ctx, 10) + require.Equal(t, items, k.GetAllLastBlockHeight(ctx)) } From d8ac99e90591a7ce88a3e7a8f33a208dba6a5d5a Mon Sep 17 00:00:00 2001 From: Charlie Chen <34498985+ws4charlie@users.noreply.github.com> Date: Mon, 8 Apr 2024 03:41:54 -0500 Subject: [PATCH 10/10] fix: check crosschain flags to stop inbound/outbound; get rid of outtx tracker iteration timeout (#1984) * check crosschain flags to stop inbound/outbound; get rid of timeout in outtx processing * remove invalid test * a bit refactor and added unit tests --------- Co-authored-by: Lucas Bertrand --- changelog.md | 2 + zetaclient/bitcoin/bitcoin_client.go | 27 +++-- zetaclient/bitcoin/bitcoin_client_test.go | 9 -- zetaclient/bitcoin/inbound_tracker.go | 3 +- zetaclient/core_context/zeta_core_context.go | 12 +++ .../core_context/zeta_core_context_test.go | 101 ++++++++++++++++-- zetaclient/evm/evm_client.go | 79 +++++--------- zetaclient/evm/inbounds.go | 10 +- zetaclient/zetacore_observer.go | 17 ++- 9 files changed, 168 insertions(+), 92 deletions(-) diff --git a/changelog.md b/changelog.md index da05d7b9b4..1fa1f6d681 100644 --- a/changelog.md +++ b/changelog.md @@ -67,6 +67,8 @@ * [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 +* [1970](https://github.com/zeta-chain/node/issues/1970) - remove the timeout in the evm outtx tracker processing thread ### Chores diff --git a/zetaclient/bitcoin/bitcoin_client.go b/zetaclient/bitcoin/bitcoin_client.go index 51bc1ab758..110c89c474 100644 --- a/zetaclient/bitcoin/bitcoin_client.go +++ b/zetaclient/bitcoin/bitcoin_client.go @@ -330,10 +330,12 @@ func (ob *BTCChainClient) WatchInTx() { defer ticker.Stop() 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 !ob.GetChainParams().IsSupported { + if !corecontext.IsInboundObservationEnabled(ob.coreContext, ob.GetChainParams()) { + sampledLogger.Info().Msgf("WatchInTx: inbound observation is disabled for chain %d", ob.chain.ChainId) continue } err := ob.ObserveInTx() @@ -384,12 +386,6 @@ func (ob *BTCChainClient) postBlockHeader(tip int64) error { } func (ob *BTCChainClient) ObserveInTx() error { - // make sure inbound TXS / Send is enabled by the protocol - flags := ob.coreContext.GetCrossChainFlags() - if !flags.IsInboundEnabled { - return errors.New("inbound TXS / Send has been disabled by the protocol") - } - // get and update latest block height cnt, err := ob.rpcClient.GetBlockCount() if err != nil { @@ -438,6 +434,9 @@ func (ob *BTCChainClient) ObserveInTx() error { } // add block header to zetabridge + // TODO: consider having a separate ticker(from TSS scaning) for posting block headers + // https://github.com/zeta-chain/node/issues/1847 + flags := ob.coreContext.GetCrossChainFlags() if flags.BlockHeaderVerificationFlags != nil && flags.BlockHeaderVerificationFlags.IsBtcTypeChainEnabled { err = ob.postBlockHeader(bn) if err != nil { @@ -583,11 +582,20 @@ func (ob *BTCChainClient) IsSendOutTxProcessed(cctx *types.CrossChainTx, logger // 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.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.GasPrice.Error().Err(err).Msg("error creating ticker") return } + ob.logger.GasPrice.Info().Msgf("WatchGasPrice started for chain %d with interval %d", + ob.chain.ChainId, ob.GetChainParams().GasPriceTicker) defer ticker.Stop() for { @@ -1122,10 +1130,13 @@ func (ob *BTCChainClient) WatchOutTx() { } defer ticker.Stop() + ob.logger.OutTx.Info().Msgf("WatchInTx started for chain %d", ob.chain.ChainId) + sampledLogger := ob.logger.OutTx.Sample(&zerolog.BasicSampler{N: 10}) for { select { case <-ticker.C(): - if !ob.GetChainParams().IsSupported { + if !corecontext.IsOutboundObservationEnabled(ob.coreContext, ob.GetChainParams()) { + sampledLogger.Info().Msgf("WatchOutTx: outbound observation is disabled for chain %d", ob.chain.ChainId) continue } trackers, err := ob.zetaClient.GetAllOutTxTrackerByChain(ob.chain.ChainId, interfaces.Ascending) diff --git a/zetaclient/bitcoin/bitcoin_client_test.go b/zetaclient/bitcoin/bitcoin_client_test.go index 58377cdc65..dfa576c9c1 100644 --- a/zetaclient/bitcoin/bitcoin_client_test.go +++ b/zetaclient/bitcoin/bitcoin_client_test.go @@ -712,12 +712,3 @@ func TestGetBtcEventErrors(t *testing.T) { require.Nil(t, event) }) } - -func TestBTCChainClient_ObserveInTx(t *testing.T) { - t.Run("should return error", func(t *testing.T) { - // create mainnet mock client - btcClient := MockBTCClientMainnet() - err := btcClient.ObserveInTx() - require.ErrorContains(t, err, "inbound TXS / Send has been disabled by the protocol") - }) -} diff --git a/zetaclient/bitcoin/inbound_tracker.go b/zetaclient/bitcoin/inbound_tracker.go index b948a6c979..e0dbd595cb 100644 --- a/zetaclient/bitcoin/inbound_tracker.go +++ b/zetaclient/bitcoin/inbound_tracker.go @@ -6,6 +6,7 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/zeta-chain/zetacore/pkg/coin" + corecontext "github.com/zeta-chain/zetacore/zetaclient/core_context" "github.com/zeta-chain/zetacore/zetaclient/types" "github.com/zeta-chain/zetacore/zetaclient/zetabridge" ) @@ -22,7 +23,7 @@ func (ob *BTCChainClient) WatchIntxTracker() { for { select { case <-ticker.C(): - if !ob.GetChainParams().IsSupported { + if !corecontext.IsInboundObservationEnabled(ob.coreContext, ob.GetChainParams()) { continue } err := ob.ObserveTrackerSuggestions() diff --git a/zetaclient/core_context/zeta_core_context.go b/zetaclient/core_context/zeta_core_context.go index d35c502c86..346563be04 100644 --- a/zetaclient/core_context/zeta_core_context.go +++ b/zetaclient/core_context/zeta_core_context.go @@ -179,3 +179,15 @@ func (c *ZetaCoreContext) Update( c.currentTssPubkey = tssPubKey } } + +// IsOutboundObservationEnabled returns true if the chain is supported and outbound flag is enabled +func IsOutboundObservationEnabled(c *ZetaCoreContext, chainParams observertypes.ChainParams) bool { + flags := c.GetCrossChainFlags() + return chainParams.IsSupported && flags.IsOutboundEnabled +} + +// IsInboundObservationEnabled returns true if the chain is supported and inbound flag is enabled +func IsInboundObservationEnabled(c *ZetaCoreContext, chainParams observertypes.ChainParams) bool { + flags := c.GetCrossChainFlags() + return chainParams.IsSupported && flags.IsInboundEnabled +} diff --git a/zetaclient/core_context/zeta_core_context_test.go b/zetaclient/core_context/zeta_core_context_test.go index 14f36a1cc2..8745fdc95c 100644 --- a/zetaclient/core_context/zeta_core_context_test.go +++ b/zetaclient/core_context/zeta_core_context_test.go @@ -3,6 +3,7 @@ package corecontext_test import ( "testing" + "github.com/rs/zerolog" "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/testutil/sample" @@ -12,6 +13,44 @@ import ( corecontext "github.com/zeta-chain/zetacore/zetaclient/core_context" ) +func assertPanic(t *testing.T, f func(), errorLog string) { + defer func() { + r := recover() + if r != nil { + require.Contains(t, r, errorLog) + } + }() + f() +} + +func getTestCoreContext( + evmChain chains.Chain, + evmChainParams *observertypes.ChainParams, + ccFlags observertypes.CrosschainFlags) *corecontext.ZetaCoreContext { + // create config + cfg := config.NewConfig() + cfg.EVMChainConfigs[evmChain.ChainId] = config.EVMConfig{ + Chain: evmChain, + } + // create core context + coreContext := corecontext.NewZetaCoreContext(cfg) + evmChainParamsMap := make(map[int64]*observertypes.ChainParams) + evmChainParamsMap[evmChain.ChainId] = evmChainParams + + // feed chain params + coreContext.Update( + &observertypes.Keygen{}, + []chains.Chain{evmChain}, + evmChainParamsMap, + nil, + "", + ccFlags, + true, + zerolog.Logger{}, + ) + return coreContext +} + func TestNewZetaCoreContext(t *testing.T) { t.Run("should create new zeta core context with empty config", func(t *testing.T) { testCfg := config.NewConfig() @@ -264,12 +303,60 @@ func TestUpdateZetaCoreContext(t *testing.T) { }) } -func assertPanic(t *testing.T, f func(), errorLog string) { - defer func() { - r := recover() - if r != nil { - require.Contains(t, r, errorLog) +func TestIsOutboundObservationEnabled(t *testing.T) { + // create test chain params and flags + evmChain := chains.EthChain() + ccFlags := *sample.CrosschainFlags() + chainParams := &observertypes.ChainParams{ + ChainId: evmChain.ChainId, + IsSupported: true, + } + + t.Run("should return true if chain is supported and outbound flag is enabled", func(t *testing.T) { + coreCTX := getTestCoreContext(evmChain, chainParams, ccFlags) + require.True(t, corecontext.IsOutboundObservationEnabled(coreCTX, *chainParams)) + }) + t.Run("should return false if chain is not supported yet", func(t *testing.T) { + paramsUnsupported := &observertypes.ChainParams{ + ChainId: evmChain.ChainId, + IsSupported: false, } - }() - f() + coreCTXUnsupported := getTestCoreContext(evmChain, paramsUnsupported, ccFlags) + require.False(t, corecontext.IsOutboundObservationEnabled(coreCTXUnsupported, *paramsUnsupported)) + }) + t.Run("should return false if outbound flag is disabled", func(t *testing.T) { + flagsDisabled := ccFlags + flagsDisabled.IsOutboundEnabled = false + coreCTXDisabled := getTestCoreContext(evmChain, chainParams, flagsDisabled) + require.False(t, corecontext.IsOutboundObservationEnabled(coreCTXDisabled, *chainParams)) + }) +} + +func TestIsInboundObservationEnabled(t *testing.T) { + // create test chain params and flags + evmChain := chains.EthChain() + ccFlags := *sample.CrosschainFlags() + chainParams := &observertypes.ChainParams{ + ChainId: evmChain.ChainId, + IsSupported: true, + } + + t.Run("should return true if chain is supported and inbound flag is enabled", func(t *testing.T) { + coreCTX := getTestCoreContext(evmChain, chainParams, ccFlags) + require.True(t, corecontext.IsInboundObservationEnabled(coreCTX, *chainParams)) + }) + t.Run("should return false if chain is not supported yet", func(t *testing.T) { + paramsUnsupported := &observertypes.ChainParams{ + ChainId: evmChain.ChainId, + IsSupported: false, + } + coreCTXUnsupported := getTestCoreContext(evmChain, paramsUnsupported, ccFlags) + require.False(t, corecontext.IsInboundObservationEnabled(coreCTXUnsupported, *paramsUnsupported)) + }) + t.Run("should return false if inbound flag is disabled", func(t *testing.T) { + flagsDisabled := ccFlags + flagsDisabled.IsInboundEnabled = false + coreCTXDisabled := getTestCoreContext(evmChain, chainParams, flagsDisabled) + require.False(t, corecontext.IsInboundObservationEnabled(coreCTXDisabled, *chainParams)) + }) } diff --git a/zetaclient/evm/evm_client.go b/zetaclient/evm/evm_client.go index f94536be25..450176df90 100644 --- a/zetaclient/evm/evm_client.go +++ b/zetaclient/evm/evm_client.go @@ -613,61 +613,48 @@ func (ob *ChainClient) IsSendOutTxProcessed(cctx *crosschaintypes.CrossChainTx, // WatchOutTx watches evm chain for outgoing txs status func (ob *ChainClient) WatchOutTx() { - // read env variables if set - timeoutNonce, err := strconv.Atoi(os.Getenv("OS_TIMEOUT_NONCE")) - if err != nil || timeoutNonce <= 0 { - timeoutNonce = 100 * 3 // process up to 100 hashes - } - ob.logger.OutTx.Info().Msgf("WatchOutTx: using timeoutNonce %d seconds", timeoutNonce) - ticker, err := clienttypes.NewDynamicTicker(fmt.Sprintf("EVM_WatchOutTx_%d", ob.chain.ChainId), ob.GetChainParams().OutTxTicker) if err != nil { ob.logger.OutTx.Error().Err(err).Msg("error creating ticker") return } + ob.logger.OutTx.Info().Msgf("WatchOutTx started for chain %d", ob.chain.ChainId) + sampledLogger := ob.logger.OutTx.Sample(&zerolog.BasicSampler{N: 10}) defer ticker.Stop() for { select { case <-ticker.C(): - if !ob.GetChainParams().IsSupported { + if !corecontext.IsOutboundObservationEnabled(ob.coreContext, ob.GetChainParams()) { + sampledLogger.Info().Msgf("WatchOutTx: outbound observation is disabled for chain %d", ob.chain.ChainId) continue } trackers, err := ob.zetaClient.GetAllOutTxTrackerByChain(ob.chain.ChainId, interfaces.Ascending) if err != nil { continue } - //FIXME: remove this timeout here to ensure that all trackers are queried - outTimeout := time.After(time.Duration(timeoutNonce) * time.Second) - TRACKERLOOP: for _, tracker := range trackers { nonceInt := tracker.Nonce if ob.isTxConfirmed(nonceInt) { // Go to next tracker if this one already has a confirmed tx continue } txCount := 0 - var receipt *ethtypes.Receipt - var transaction *ethtypes.Transaction + var outtxReceipt *ethtypes.Receipt + var outtx *ethtypes.Transaction for _, txHash := range tracker.HashList { - select { - case <-outTimeout: - ob.logger.OutTx.Warn().Msgf("WatchOutTx: timeout on chain %d nonce %d", ob.chain.ChainId, nonceInt) - break TRACKERLOOP - default: - if recpt, tx, ok := ob.checkConfirmedTx(txHash.TxHash, nonceInt); ok { - txCount++ - receipt = recpt - transaction = tx - 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.OutTx.Error().Msgf( - "WatchOutTx: checkConfirmedTx passed, txCount %d chain %d nonce %d receipt %v transaction %v", txCount, ob.chain.ChainId, nonceInt, receipt, transaction) - } + if receipt, tx, ok := ob.checkConfirmedTx(txHash.TxHash, nonceInt); ok { + txCount++ + outtxReceipt = receipt + outtx = tx + 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.OutTx.Error().Msgf( + "WatchOutTx: checkConfirmedTx passed, txCount %d chain %d nonce %d receipt %v transaction %v", txCount, ob.chain.ChainId, nonceInt, outtxReceipt, outtx) } } } if txCount == 1 { // should be only one txHash confirmed for each nonce. - ob.SetTxNReceipt(nonceInt, receipt, transaction) + ob.SetTxNReceipt(nonceInt, outtxReceipt, outtx) } 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.OutTx.Error().Msgf("WatchOutTx: confirmed multiple (%d) outTx for chain %d nonce %d", txCount, ob.chain.ChainId, nonceInt) } @@ -854,8 +841,8 @@ func (ob *ChainClient) WatchInTx() { for { select { case <-ticker.C(): - if !ob.GetChainParams().IsSupported { - sampledLogger.Info().Msgf("WatchInTx: chain %d is not supported", ob.chain.ChainId) + if !corecontext.IsInboundObservationEnabled(ob.coreContext, ob.GetChainParams()) { + sampledLogger.Info().Msgf("WatchInTx: inbound observation is disabled for chain %d", ob.chain.ChainId) continue } err := ob.observeInTX(sampledLogger) @@ -918,12 +905,6 @@ func (ob *ChainClient) postBlockHeader(tip uint64) error { } func (ob *ChainClient) observeInTX(sampledLogger zerolog.Logger) error { - // make sure inbound TXS / Send is enabled by the protocol - flags := ob.coreContext.GetCrossChainFlags() - if !flags.IsInboundEnabled { - return errors.New("inbound TXS / Send has been disabled by the protocol") - } - // get and update latest block height blockNumber, err := ob.evmClient.BlockNumber(context.Background()) if err != nil { @@ -961,7 +942,7 @@ func (ob *ChainClient) observeInTX(sampledLogger zerolog.Logger) error { lastScannedDeposited := ob.ObserveERC20Deposited(startBlock, toBlock) // task 3: query the incoming tx to TSS address (read at most 100 blocks in one go) - lastScannedTssRecvd := ob.ObserverTSSReceive(startBlock, toBlock, flags) + lastScannedTssRecvd := ob.ObserverTSSReceive(startBlock, toBlock) // note: using lowest height for all 3 events is not perfect, but it's simple and good enough lastScannedLowest := lastScannedZetaSent @@ -1138,11 +1119,13 @@ 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, flags observertypes.CrosschainFlags) uint64 { +func (ob *ChainClient) ObserverTSSReceive(startBlock, toBlock uint64) uint64 { // query incoming gas asset for bn := startBlock; bn <= toBlock; bn++ { // post new block header (if any) to zetabridge and ignore error // TODO: consider having a independent ticker(from TSS scaning) for posting block headers + // https://github.com/zeta-chain/node/issues/1847 + flags := ob.coreContext.GetCrossChainFlags() if flags.BlockHeaderVerificationFlags != nil && flags.BlockHeaderVerificationFlags.IsEthTypeChainEnabled && chains.IsHeaderSupportedEvmChain(ob.chain.ChainId) { // post block header for supported chains @@ -1165,23 +1148,20 @@ func (ob *ChainClient) ObserverTSSReceive(startBlock, toBlock uint64, flags obse // WatchGasPrice watches evm chain for gas prices and post to zetacore func (ob *ChainClient) WatchGasPrice() { - ob.logger.GasPrice.Info().Msg("WatchGasPrice starting...") + // report gas price right away as the ticker takes time to kick in err := ob.PostGasPrice() if err != nil { - height, err := ob.zetaClient.GetBlockHeight() - if err != nil { - ob.logger.GasPrice.Error().Err(err).Msg("GetBlockHeight error") - } else { - ob.logger.GasPrice.Error().Err(err).Msgf("PostGasPrice error at zeta block : %d ", height) - } + 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.GasPrice.Error().Err(err).Msg("NewDynamicTicker error") return } - ob.logger.GasPrice.Info().Msgf("WatchGasPrice started with interval %d", ob.GetChainParams().GasPriceTicker) + ob.logger.GasPrice.Info().Msgf("WatchGasPrice started for chain %d with interval %d", + ob.chain.ChainId, ob.GetChainParams().GasPriceTicker) defer ticker.Stop() for { @@ -1192,12 +1172,7 @@ func (ob *ChainClient) WatchGasPrice() { } err = ob.PostGasPrice() if err != nil { - height, err := ob.zetaClient.GetBlockHeight() - if err != nil { - ob.logger.GasPrice.Error().Err(err).Msg("GetBlockHeight error") - } else { - ob.logger.GasPrice.Error().Err(err).Msgf("PostGasPrice error at zeta block : %d ", height) - } + ob.logger.GasPrice.Error().Err(err).Msgf("PostGasPrice error for chain %d", ob.chain.ChainId) } ticker.UpdateInterval(ob.GetChainParams().GasPriceTicker, ob.logger.GasPrice) case <-ob.stop: diff --git a/zetaclient/evm/inbounds.go b/zetaclient/evm/inbounds.go index d6f6fcb223..819ab09f77 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,13 +16,12 @@ 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" + corecontext "github.com/zeta-chain/zetacore/zetaclient/core_context" 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" ) @@ -43,7 +43,7 @@ func (ob *ChainClient) WatchIntxTracker() { for { select { case <-ticker.C(): - if !ob.GetChainParams().IsSupported { + if !corecontext.IsInboundObservationEnabled(ob.coreContext, ob.GetChainParams()) { continue } err := ob.ObserveIntxTrackers() diff --git a/zetaclient/zetacore_observer.go b/zetaclient/zetacore_observer.go index 34d0774507..415b5d5d21 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 ( @@ -154,8 +151,7 @@ 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) + if !corecontext.IsOutboundObservationEnabled(coreContext, ob.GetChainParams()) { continue } @@ -178,6 +174,7 @@ func (co *CoreObserver) startCctxScheduler(appContext *appcontext.AppContext) { continue } } + // update last processed block number lastBlockNum = bn metrics.LastCoreBlockNumber.Set(float64(lastBlockNum))