From 6c8e5a6e09d1b013af21a6dfea6314b82d1079d5 Mon Sep 17 00:00:00 2001 From: Jason Mok <106209849+jasonmokk@users.noreply.github.com> Date: Tue, 4 Jun 2024 03:48:50 -0500 Subject: [PATCH 1/9] refactor: move `testdata` from `pkg` to `testutil` (#2296) * chore: move testdata from pkg to testutil * fix imports * Add changelog entry * Fix pathname in headers_test --------- Co-authored-by: Lucas Bertrand --- changelog.md | 1 + pkg/proofs/bitcoin/proof_test.go | 2 +- pkg/proofs/ethereum/proof_test.go | 2 +- pkg/proofs/headers_test.go | 6 +++--- pkg/proofs/proof_test.go | 2 +- testutil/sample/lightclient.go | 2 +- {pkg => testutil}/testdata/eth_header_18495266.json | 0 {pkg => testutil}/testdata/ethereum/header.json | 0 {pkg => testutil}/testdata/ethereum/receipt_0.json | 0 {pkg => testutil}/testdata/ethereum/receipt_1.json | 0 {pkg => testutil}/testdata/ethereum/receipt_10.json | 0 {pkg => testutil}/testdata/ethereum/receipt_11.json | 0 {pkg => testutil}/testdata/ethereum/receipt_12.json | 0 {pkg => testutil}/testdata/ethereum/receipt_13.json | 0 {pkg => testutil}/testdata/ethereum/receipt_14.json | 0 {pkg => testutil}/testdata/ethereum/receipt_15.json | 0 {pkg => testutil}/testdata/ethereum/receipt_16.json | 0 {pkg => testutil}/testdata/ethereum/receipt_17.json | 0 {pkg => testutil}/testdata/ethereum/receipt_18.json | 0 {pkg => testutil}/testdata/ethereum/receipt_19.json | 0 {pkg => testutil}/testdata/ethereum/receipt_2.json | 0 {pkg => testutil}/testdata/ethereum/receipt_20.json | 0 {pkg => testutil}/testdata/ethereum/receipt_21.json | 0 {pkg => testutil}/testdata/ethereum/receipt_22.json | 0 {pkg => testutil}/testdata/ethereum/receipt_23.json | 0 {pkg => testutil}/testdata/ethereum/receipt_24.json | 0 {pkg => testutil}/testdata/ethereum/receipt_25.json | 0 {pkg => testutil}/testdata/ethereum/receipt_26.json | 0 {pkg => testutil}/testdata/ethereum/receipt_27.json | 0 {pkg => testutil}/testdata/ethereum/receipt_28.json | 0 {pkg => testutil}/testdata/ethereum/receipt_29.json | 0 {pkg => testutil}/testdata/ethereum/receipt_3.json | 0 {pkg => testutil}/testdata/ethereum/receipt_30.json | 0 {pkg => testutil}/testdata/ethereum/receipt_31.json | 0 {pkg => testutil}/testdata/ethereum/receipt_32.json | 0 {pkg => testutil}/testdata/ethereum/receipt_33.json | 0 {pkg => testutil}/testdata/ethereum/receipt_34.json | 0 {pkg => testutil}/testdata/ethereum/receipt_35.json | 0 {pkg => testutil}/testdata/ethereum/receipt_36.json | 0 {pkg => testutil}/testdata/ethereum/receipt_37.json | 0 {pkg => testutil}/testdata/ethereum/receipt_38.json | 0 {pkg => testutil}/testdata/ethereum/receipt_39.json | 0 {pkg => testutil}/testdata/ethereum/receipt_4.json | 0 {pkg => testutil}/testdata/ethereum/receipt_40.json | 0 {pkg => testutil}/testdata/ethereum/receipt_41.json | 0 {pkg => testutil}/testdata/ethereum/receipt_42.json | 0 {pkg => testutil}/testdata/ethereum/receipt_43.json | 0 {pkg => testutil}/testdata/ethereum/receipt_44.json | 0 {pkg => testutil}/testdata/ethereum/receipt_45.json | 0 {pkg => testutil}/testdata/ethereum/receipt_46.json | 0 {pkg => testutil}/testdata/ethereum/receipt_47.json | 0 {pkg => testutil}/testdata/ethereum/receipt_48.json | 0 {pkg => testutil}/testdata/ethereum/receipt_49.json | 0 {pkg => testutil}/testdata/ethereum/receipt_5.json | 0 {pkg => testutil}/testdata/ethereum/receipt_50.json | 0 {pkg => testutil}/testdata/ethereum/receipt_51.json | 0 {pkg => testutil}/testdata/ethereum/receipt_52.json | 0 {pkg => testutil}/testdata/ethereum/receipt_53.json | 0 {pkg => testutil}/testdata/ethereum/receipt_54.json | 0 {pkg => testutil}/testdata/ethereum/receipt_55.json | 0 {pkg => testutil}/testdata/ethereum/receipt_56.json | 0 {pkg => testutil}/testdata/ethereum/receipt_57.json | 0 {pkg => testutil}/testdata/ethereum/receipt_58.json | 0 {pkg => testutil}/testdata/ethereum/receipt_59.json | 0 {pkg => testutil}/testdata/ethereum/receipt_6.json | 0 {pkg => testutil}/testdata/ethereum/receipt_60.json | 0 {pkg => testutil}/testdata/ethereum/receipt_61.json | 0 {pkg => testutil}/testdata/ethereum/receipt_62.json | 0 {pkg => testutil}/testdata/ethereum/receipt_63.json | 0 {pkg => testutil}/testdata/ethereum/receipt_64.json | 0 {pkg => testutil}/testdata/ethereum/receipt_65.json | 0 {pkg => testutil}/testdata/ethereum/receipt_66.json | 0 {pkg => testutil}/testdata/ethereum/receipt_67.json | 0 {pkg => testutil}/testdata/ethereum/receipt_68.json | 0 {pkg => testutil}/testdata/ethereum/receipt_69.json | 0 {pkg => testutil}/testdata/ethereum/receipt_7.json | 0 {pkg => testutil}/testdata/ethereum/receipt_70.json | 0 {pkg => testutil}/testdata/ethereum/receipt_71.json | 0 {pkg => testutil}/testdata/ethereum/receipt_72.json | 0 {pkg => testutil}/testdata/ethereum/receipt_73.json | 0 {pkg => testutil}/testdata/ethereum/receipt_74.json | 0 {pkg => testutil}/testdata/ethereum/receipt_75.json | 0 {pkg => testutil}/testdata/ethereum/receipt_76.json | 0 {pkg => testutil}/testdata/ethereum/receipt_77.json | 0 {pkg => testutil}/testdata/ethereum/receipt_78.json | 0 {pkg => testutil}/testdata/ethereum/receipt_79.json | 0 {pkg => testutil}/testdata/ethereum/receipt_8.json | 0 {pkg => testutil}/testdata/ethereum/receipt_80.json | 0 {pkg => testutil}/testdata/ethereum/receipt_9.json | 0 {pkg => testutil}/testdata/ethereum/tx_0.json | 0 {pkg => testutil}/testdata/ethereum/tx_1.json | 0 {pkg => testutil}/testdata/ethereum/tx_10.json | 0 {pkg => testutil}/testdata/ethereum/tx_11.json | 0 {pkg => testutil}/testdata/ethereum/tx_12.json | 0 {pkg => testutil}/testdata/ethereum/tx_13.json | 0 {pkg => testutil}/testdata/ethereum/tx_14.json | 0 {pkg => testutil}/testdata/ethereum/tx_15.json | 0 {pkg => testutil}/testdata/ethereum/tx_16.json | 0 {pkg => testutil}/testdata/ethereum/tx_17.json | 0 {pkg => testutil}/testdata/ethereum/tx_18.json | 0 {pkg => testutil}/testdata/ethereum/tx_19.json | 0 {pkg => testutil}/testdata/ethereum/tx_2.json | 0 {pkg => testutil}/testdata/ethereum/tx_20.json | 0 {pkg => testutil}/testdata/ethereum/tx_21.json | 0 {pkg => testutil}/testdata/ethereum/tx_22.json | 0 {pkg => testutil}/testdata/ethereum/tx_23.json | 0 {pkg => testutil}/testdata/ethereum/tx_24.json | 0 {pkg => testutil}/testdata/ethereum/tx_25.json | 0 {pkg => testutil}/testdata/ethereum/tx_26.json | 0 {pkg => testutil}/testdata/ethereum/tx_27.json | 0 {pkg => testutil}/testdata/ethereum/tx_28.json | 0 {pkg => testutil}/testdata/ethereum/tx_29.json | 0 {pkg => testutil}/testdata/ethereum/tx_3.json | 0 {pkg => testutil}/testdata/ethereum/tx_30.json | 0 {pkg => testutil}/testdata/ethereum/tx_31.json | 0 {pkg => testutil}/testdata/ethereum/tx_32.json | 0 {pkg => testutil}/testdata/ethereum/tx_33.json | 0 {pkg => testutil}/testdata/ethereum/tx_34.json | 0 {pkg => testutil}/testdata/ethereum/tx_35.json | 0 {pkg => testutil}/testdata/ethereum/tx_36.json | 0 {pkg => testutil}/testdata/ethereum/tx_37.json | 0 {pkg => testutil}/testdata/ethereum/tx_38.json | 0 {pkg => testutil}/testdata/ethereum/tx_39.json | 0 {pkg => testutil}/testdata/ethereum/tx_4.json | 0 {pkg => testutil}/testdata/ethereum/tx_40.json | 0 {pkg => testutil}/testdata/ethereum/tx_41.json | 0 {pkg => testutil}/testdata/ethereum/tx_42.json | 0 {pkg => testutil}/testdata/ethereum/tx_43.json | 0 {pkg => testutil}/testdata/ethereum/tx_44.json | 0 {pkg => testutil}/testdata/ethereum/tx_45.json | 0 {pkg => testutil}/testdata/ethereum/tx_46.json | 0 {pkg => testutil}/testdata/ethereum/tx_47.json | 0 {pkg => testutil}/testdata/ethereum/tx_48.json | 0 {pkg => testutil}/testdata/ethereum/tx_49.json | 0 {pkg => testutil}/testdata/ethereum/tx_5.json | 0 {pkg => testutil}/testdata/ethereum/tx_50.json | 0 {pkg => testutil}/testdata/ethereum/tx_51.json | 0 {pkg => testutil}/testdata/ethereum/tx_52.json | 0 {pkg => testutil}/testdata/ethereum/tx_53.json | 0 {pkg => testutil}/testdata/ethereum/tx_54.json | 0 {pkg => testutil}/testdata/ethereum/tx_55.json | 0 {pkg => testutil}/testdata/ethereum/tx_56.json | 0 {pkg => testutil}/testdata/ethereum/tx_57.json | 0 {pkg => testutil}/testdata/ethereum/tx_58.json | 0 {pkg => testutil}/testdata/ethereum/tx_59.json | 0 {pkg => testutil}/testdata/ethereum/tx_6.json | 0 {pkg => testutil}/testdata/ethereum/tx_60.json | 0 {pkg => testutil}/testdata/ethereum/tx_61.json | 0 {pkg => testutil}/testdata/ethereum/tx_62.json | 0 {pkg => testutil}/testdata/ethereum/tx_63.json | 0 {pkg => testutil}/testdata/ethereum/tx_64.json | 0 {pkg => testutil}/testdata/ethereum/tx_65.json | 0 {pkg => testutil}/testdata/ethereum/tx_66.json | 0 {pkg => testutil}/testdata/ethereum/tx_67.json | 0 {pkg => testutil}/testdata/ethereum/tx_68.json | 0 {pkg => testutil}/testdata/ethereum/tx_69.json | 0 {pkg => testutil}/testdata/ethereum/tx_7.json | 0 {pkg => testutil}/testdata/ethereum/tx_70.json | 0 {pkg => testutil}/testdata/ethereum/tx_71.json | 0 {pkg => testutil}/testdata/ethereum/tx_72.json | 0 {pkg => testutil}/testdata/ethereum/tx_73.json | 0 {pkg => testutil}/testdata/ethereum/tx_74.json | 0 {pkg => testutil}/testdata/ethereum/tx_75.json | 0 {pkg => testutil}/testdata/ethereum/tx_76.json | 0 {pkg => testutil}/testdata/ethereum/tx_77.json | 0 {pkg => testutil}/testdata/ethereum/tx_78.json | 0 {pkg => testutil}/testdata/ethereum/tx_79.json | 0 {pkg => testutil}/testdata/ethereum/tx_8.json | 0 {pkg => testutil}/testdata/ethereum/tx_80.json | 0 {pkg => testutil}/testdata/ethereum/tx_9.json | 0 {pkg => testutil}/testdata/test_blocks.json | 0 {pkg => testutil}/testdata/testdata.go | 0 {pkg => testutil}/testdata/types/chain_info.json | 0 {pkg => testutil}/testdata/types/policies.json | 0 x/authority/client/cli/tx_update_chain_info_test.go | 2 +- x/authority/client/cli/tx_update_policies_test.go | 2 +- x/observer/types/message_vote_block_header_test.go | 2 +- zetaclient/zetacore/tx_test.go | 2 +- 178 files changed, 12 insertions(+), 11 deletions(-) rename {pkg => testutil}/testdata/eth_header_18495266.json (100%) rename {pkg => testutil}/testdata/ethereum/header.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_0.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_1.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_10.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_11.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_12.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_13.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_14.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_15.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_16.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_17.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_18.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_19.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_2.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_20.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_21.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_22.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_23.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_24.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_25.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_26.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_27.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_28.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_29.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_3.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_30.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_31.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_32.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_33.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_34.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_35.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_36.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_37.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_38.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_39.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_4.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_40.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_41.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_42.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_43.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_44.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_45.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_46.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_47.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_48.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_49.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_5.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_50.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_51.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_52.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_53.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_54.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_55.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_56.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_57.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_58.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_59.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_6.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_60.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_61.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_62.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_63.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_64.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_65.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_66.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_67.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_68.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_69.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_7.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_70.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_71.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_72.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_73.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_74.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_75.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_76.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_77.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_78.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_79.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_8.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_80.json (100%) rename {pkg => testutil}/testdata/ethereum/receipt_9.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_0.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_1.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_10.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_11.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_12.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_13.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_14.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_15.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_16.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_17.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_18.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_19.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_2.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_20.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_21.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_22.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_23.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_24.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_25.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_26.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_27.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_28.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_29.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_3.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_30.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_31.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_32.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_33.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_34.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_35.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_36.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_37.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_38.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_39.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_4.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_40.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_41.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_42.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_43.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_44.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_45.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_46.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_47.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_48.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_49.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_5.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_50.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_51.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_52.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_53.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_54.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_55.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_56.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_57.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_58.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_59.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_6.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_60.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_61.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_62.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_63.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_64.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_65.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_66.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_67.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_68.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_69.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_7.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_70.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_71.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_72.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_73.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_74.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_75.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_76.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_77.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_78.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_79.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_8.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_80.json (100%) rename {pkg => testutil}/testdata/ethereum/tx_9.json (100%) rename {pkg => testutil}/testdata/test_blocks.json (100%) rename {pkg => testutil}/testdata/testdata.go (100%) rename {pkg => testutil}/testdata/types/chain_info.json (100%) rename {pkg => testutil}/testdata/types/policies.json (100%) diff --git a/changelog.md b/changelog.md index f0f94999ef..82d90197d2 100644 --- a/changelog.md +++ b/changelog.md @@ -39,6 +39,7 @@ * [2262](https://github.com/zeta-chain/node/pull/2262) - refactor MsgUpdateZRC20 into MsgPauseZrc20 and MsgUnPauseZRC20 * [2290](https://github.com/zeta-chain/node/pull/2290) - rename `MsgAddBlameVote` message to `MsgVoteBlame` * [2269](https://github.com/zeta-chain/node/pull/2269) - refactor MsgUpdateCrosschainFlags into MsgEnableCCTX, MsgDisableCCTX and MsgUpdateGasPriceIncreaseFlags +* [2296](https://github.com/zeta-chain/node/pull/2296) - move `testdata` package to `testutil` to organize test-related utilities ### Tests diff --git a/pkg/proofs/bitcoin/proof_test.go b/pkg/proofs/bitcoin/proof_test.go index 23444e22d6..ad9801f6ce 100644 --- a/pkg/proofs/bitcoin/proof_test.go +++ b/pkg/proofs/bitcoin/proof_test.go @@ -13,7 +13,7 @@ import ( "github.com/btcsuite/btcutil" "github.com/stretchr/testify/require" - "github.com/zeta-chain/zetacore/pkg/testdata" + "github.com/zeta-chain/zetacore/testutil/testdata" ) func TestBitcoinMerkleProof(t *testing.T) { diff --git a/pkg/proofs/ethereum/proof_test.go b/pkg/proofs/ethereum/proof_test.go index b6c676b0ee..8fb67c591b 100644 --- a/pkg/proofs/ethereum/proof_test.go +++ b/pkg/proofs/ethereum/proof_test.go @@ -6,7 +6,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/require" - "github.com/zeta-chain/zetacore/pkg/testdata" + "github.com/zeta-chain/zetacore/testutil/testdata" ) func TestProofGeneration(t *testing.T) { diff --git a/pkg/proofs/headers_test.go b/pkg/proofs/headers_test.go index 5d992a4686..8125435878 100644 --- a/pkg/proofs/headers_test.go +++ b/pkg/proofs/headers_test.go @@ -17,7 +17,7 @@ import ( ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/require" - "github.com/zeta-chain/zetacore/pkg/testdata" + "github.com/zeta-chain/zetacore/testutil/testdata" ) const numHeadersToTest = 100 @@ -25,7 +25,7 @@ const numHeadersToTest = 100 func TestTrueEthereumHeader(t *testing.T) { var header ethtypes.Header // read file into a byte slice - file, err := os.Open("../testdata/eth_header_18495266.json") + file, err := os.Open("../../testutil/testdata/eth_header_18495266.json") require.NoError(t, err) defer file.Close() headerBytes := make([]byte, 4096) @@ -52,7 +52,7 @@ func TestTrueEthereumHeader(t *testing.T) { func TestFalseEthereumHeader(t *testing.T) { var header ethtypes.Header // read file into a byte slice - file, err := os.Open("../testdata/eth_header_18495266.json") + file, err := os.Open("../../testutil/testdata/eth_header_18495266.json") require.NoError(t, err) defer file.Close() headerBytes := make([]byte, 4096) diff --git a/pkg/proofs/proof_test.go b/pkg/proofs/proof_test.go index b9210e9260..7be25350f4 100644 --- a/pkg/proofs/proof_test.go +++ b/pkg/proofs/proof_test.go @@ -18,7 +18,7 @@ import ( "github.com/zeta-chain/zetacore/pkg/proofs/bitcoin" "github.com/zeta-chain/zetacore/pkg/proofs/ethereum" - "github.com/zeta-chain/zetacore/pkg/testdata" + "github.com/zeta-chain/zetacore/testutil/testdata" ) const ( diff --git a/testutil/sample/lightclient.go b/testutil/sample/lightclient.go index 1d3725876a..70aa55b51a 100644 --- a/testutil/sample/lightclient.go +++ b/testutil/sample/lightclient.go @@ -11,7 +11,7 @@ import ( "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/proofs" "github.com/zeta-chain/zetacore/pkg/proofs/ethereum" - "github.com/zeta-chain/zetacore/pkg/testdata" + "github.com/zeta-chain/zetacore/testutil/testdata" lightclienttypes "github.com/zeta-chain/zetacore/x/lightclient/types" ) diff --git a/pkg/testdata/eth_header_18495266.json b/testutil/testdata/eth_header_18495266.json similarity index 100% rename from pkg/testdata/eth_header_18495266.json rename to testutil/testdata/eth_header_18495266.json diff --git a/pkg/testdata/ethereum/header.json b/testutil/testdata/ethereum/header.json similarity index 100% rename from pkg/testdata/ethereum/header.json rename to testutil/testdata/ethereum/header.json diff --git a/pkg/testdata/ethereum/receipt_0.json b/testutil/testdata/ethereum/receipt_0.json similarity index 100% rename from pkg/testdata/ethereum/receipt_0.json rename to testutil/testdata/ethereum/receipt_0.json diff --git a/pkg/testdata/ethereum/receipt_1.json b/testutil/testdata/ethereum/receipt_1.json similarity index 100% rename from pkg/testdata/ethereum/receipt_1.json rename to testutil/testdata/ethereum/receipt_1.json diff --git a/pkg/testdata/ethereum/receipt_10.json b/testutil/testdata/ethereum/receipt_10.json similarity index 100% rename from pkg/testdata/ethereum/receipt_10.json rename to testutil/testdata/ethereum/receipt_10.json diff --git a/pkg/testdata/ethereum/receipt_11.json b/testutil/testdata/ethereum/receipt_11.json similarity index 100% rename from pkg/testdata/ethereum/receipt_11.json rename to testutil/testdata/ethereum/receipt_11.json diff --git a/pkg/testdata/ethereum/receipt_12.json b/testutil/testdata/ethereum/receipt_12.json similarity index 100% rename from pkg/testdata/ethereum/receipt_12.json rename to testutil/testdata/ethereum/receipt_12.json diff --git a/pkg/testdata/ethereum/receipt_13.json b/testutil/testdata/ethereum/receipt_13.json similarity index 100% rename from pkg/testdata/ethereum/receipt_13.json rename to testutil/testdata/ethereum/receipt_13.json diff --git a/pkg/testdata/ethereum/receipt_14.json b/testutil/testdata/ethereum/receipt_14.json similarity index 100% rename from pkg/testdata/ethereum/receipt_14.json rename to testutil/testdata/ethereum/receipt_14.json diff --git a/pkg/testdata/ethereum/receipt_15.json b/testutil/testdata/ethereum/receipt_15.json similarity index 100% rename from pkg/testdata/ethereum/receipt_15.json rename to testutil/testdata/ethereum/receipt_15.json diff --git a/pkg/testdata/ethereum/receipt_16.json b/testutil/testdata/ethereum/receipt_16.json similarity index 100% rename from pkg/testdata/ethereum/receipt_16.json rename to testutil/testdata/ethereum/receipt_16.json diff --git a/pkg/testdata/ethereum/receipt_17.json b/testutil/testdata/ethereum/receipt_17.json similarity index 100% rename from pkg/testdata/ethereum/receipt_17.json rename to testutil/testdata/ethereum/receipt_17.json diff --git a/pkg/testdata/ethereum/receipt_18.json b/testutil/testdata/ethereum/receipt_18.json similarity index 100% rename from pkg/testdata/ethereum/receipt_18.json rename to testutil/testdata/ethereum/receipt_18.json diff --git a/pkg/testdata/ethereum/receipt_19.json b/testutil/testdata/ethereum/receipt_19.json similarity index 100% rename from pkg/testdata/ethereum/receipt_19.json rename to testutil/testdata/ethereum/receipt_19.json diff --git a/pkg/testdata/ethereum/receipt_2.json b/testutil/testdata/ethereum/receipt_2.json similarity index 100% rename from pkg/testdata/ethereum/receipt_2.json rename to testutil/testdata/ethereum/receipt_2.json diff --git a/pkg/testdata/ethereum/receipt_20.json b/testutil/testdata/ethereum/receipt_20.json similarity index 100% rename from pkg/testdata/ethereum/receipt_20.json rename to testutil/testdata/ethereum/receipt_20.json diff --git a/pkg/testdata/ethereum/receipt_21.json b/testutil/testdata/ethereum/receipt_21.json similarity index 100% rename from pkg/testdata/ethereum/receipt_21.json rename to testutil/testdata/ethereum/receipt_21.json diff --git a/pkg/testdata/ethereum/receipt_22.json b/testutil/testdata/ethereum/receipt_22.json similarity index 100% rename from pkg/testdata/ethereum/receipt_22.json rename to testutil/testdata/ethereum/receipt_22.json diff --git a/pkg/testdata/ethereum/receipt_23.json b/testutil/testdata/ethereum/receipt_23.json similarity index 100% rename from pkg/testdata/ethereum/receipt_23.json rename to testutil/testdata/ethereum/receipt_23.json diff --git a/pkg/testdata/ethereum/receipt_24.json b/testutil/testdata/ethereum/receipt_24.json similarity index 100% rename from pkg/testdata/ethereum/receipt_24.json rename to testutil/testdata/ethereum/receipt_24.json diff --git a/pkg/testdata/ethereum/receipt_25.json b/testutil/testdata/ethereum/receipt_25.json similarity index 100% rename from pkg/testdata/ethereum/receipt_25.json rename to testutil/testdata/ethereum/receipt_25.json diff --git a/pkg/testdata/ethereum/receipt_26.json b/testutil/testdata/ethereum/receipt_26.json similarity index 100% rename from pkg/testdata/ethereum/receipt_26.json rename to testutil/testdata/ethereum/receipt_26.json diff --git a/pkg/testdata/ethereum/receipt_27.json b/testutil/testdata/ethereum/receipt_27.json similarity index 100% rename from pkg/testdata/ethereum/receipt_27.json rename to testutil/testdata/ethereum/receipt_27.json diff --git a/pkg/testdata/ethereum/receipt_28.json b/testutil/testdata/ethereum/receipt_28.json similarity index 100% rename from pkg/testdata/ethereum/receipt_28.json rename to testutil/testdata/ethereum/receipt_28.json diff --git a/pkg/testdata/ethereum/receipt_29.json b/testutil/testdata/ethereum/receipt_29.json similarity index 100% rename from pkg/testdata/ethereum/receipt_29.json rename to testutil/testdata/ethereum/receipt_29.json diff --git a/pkg/testdata/ethereum/receipt_3.json b/testutil/testdata/ethereum/receipt_3.json similarity index 100% rename from pkg/testdata/ethereum/receipt_3.json rename to testutil/testdata/ethereum/receipt_3.json diff --git a/pkg/testdata/ethereum/receipt_30.json b/testutil/testdata/ethereum/receipt_30.json similarity index 100% rename from pkg/testdata/ethereum/receipt_30.json rename to testutil/testdata/ethereum/receipt_30.json diff --git a/pkg/testdata/ethereum/receipt_31.json b/testutil/testdata/ethereum/receipt_31.json similarity index 100% rename from pkg/testdata/ethereum/receipt_31.json rename to testutil/testdata/ethereum/receipt_31.json diff --git a/pkg/testdata/ethereum/receipt_32.json b/testutil/testdata/ethereum/receipt_32.json similarity index 100% rename from pkg/testdata/ethereum/receipt_32.json rename to testutil/testdata/ethereum/receipt_32.json diff --git a/pkg/testdata/ethereum/receipt_33.json b/testutil/testdata/ethereum/receipt_33.json similarity index 100% rename from pkg/testdata/ethereum/receipt_33.json rename to testutil/testdata/ethereum/receipt_33.json diff --git a/pkg/testdata/ethereum/receipt_34.json b/testutil/testdata/ethereum/receipt_34.json similarity index 100% rename from pkg/testdata/ethereum/receipt_34.json rename to testutil/testdata/ethereum/receipt_34.json diff --git a/pkg/testdata/ethereum/receipt_35.json b/testutil/testdata/ethereum/receipt_35.json similarity index 100% rename from pkg/testdata/ethereum/receipt_35.json rename to testutil/testdata/ethereum/receipt_35.json diff --git a/pkg/testdata/ethereum/receipt_36.json b/testutil/testdata/ethereum/receipt_36.json similarity index 100% rename from pkg/testdata/ethereum/receipt_36.json rename to testutil/testdata/ethereum/receipt_36.json diff --git a/pkg/testdata/ethereum/receipt_37.json b/testutil/testdata/ethereum/receipt_37.json similarity index 100% rename from pkg/testdata/ethereum/receipt_37.json rename to testutil/testdata/ethereum/receipt_37.json diff --git a/pkg/testdata/ethereum/receipt_38.json b/testutil/testdata/ethereum/receipt_38.json similarity index 100% rename from pkg/testdata/ethereum/receipt_38.json rename to testutil/testdata/ethereum/receipt_38.json diff --git a/pkg/testdata/ethereum/receipt_39.json b/testutil/testdata/ethereum/receipt_39.json similarity index 100% rename from pkg/testdata/ethereum/receipt_39.json rename to testutil/testdata/ethereum/receipt_39.json diff --git a/pkg/testdata/ethereum/receipt_4.json b/testutil/testdata/ethereum/receipt_4.json similarity index 100% rename from pkg/testdata/ethereum/receipt_4.json rename to testutil/testdata/ethereum/receipt_4.json diff --git a/pkg/testdata/ethereum/receipt_40.json b/testutil/testdata/ethereum/receipt_40.json similarity index 100% rename from pkg/testdata/ethereum/receipt_40.json rename to testutil/testdata/ethereum/receipt_40.json diff --git a/pkg/testdata/ethereum/receipt_41.json b/testutil/testdata/ethereum/receipt_41.json similarity index 100% rename from pkg/testdata/ethereum/receipt_41.json rename to testutil/testdata/ethereum/receipt_41.json diff --git a/pkg/testdata/ethereum/receipt_42.json b/testutil/testdata/ethereum/receipt_42.json similarity index 100% rename from pkg/testdata/ethereum/receipt_42.json rename to testutil/testdata/ethereum/receipt_42.json diff --git a/pkg/testdata/ethereum/receipt_43.json b/testutil/testdata/ethereum/receipt_43.json similarity index 100% rename from pkg/testdata/ethereum/receipt_43.json rename to testutil/testdata/ethereum/receipt_43.json diff --git a/pkg/testdata/ethereum/receipt_44.json b/testutil/testdata/ethereum/receipt_44.json similarity index 100% rename from pkg/testdata/ethereum/receipt_44.json rename to testutil/testdata/ethereum/receipt_44.json diff --git a/pkg/testdata/ethereum/receipt_45.json b/testutil/testdata/ethereum/receipt_45.json similarity index 100% rename from pkg/testdata/ethereum/receipt_45.json rename to testutil/testdata/ethereum/receipt_45.json diff --git a/pkg/testdata/ethereum/receipt_46.json b/testutil/testdata/ethereum/receipt_46.json similarity index 100% rename from pkg/testdata/ethereum/receipt_46.json rename to testutil/testdata/ethereum/receipt_46.json diff --git a/pkg/testdata/ethereum/receipt_47.json b/testutil/testdata/ethereum/receipt_47.json similarity index 100% rename from pkg/testdata/ethereum/receipt_47.json rename to testutil/testdata/ethereum/receipt_47.json diff --git a/pkg/testdata/ethereum/receipt_48.json b/testutil/testdata/ethereum/receipt_48.json similarity index 100% rename from pkg/testdata/ethereum/receipt_48.json rename to testutil/testdata/ethereum/receipt_48.json diff --git a/pkg/testdata/ethereum/receipt_49.json b/testutil/testdata/ethereum/receipt_49.json similarity index 100% rename from pkg/testdata/ethereum/receipt_49.json rename to testutil/testdata/ethereum/receipt_49.json diff --git a/pkg/testdata/ethereum/receipt_5.json b/testutil/testdata/ethereum/receipt_5.json similarity index 100% rename from pkg/testdata/ethereum/receipt_5.json rename to testutil/testdata/ethereum/receipt_5.json diff --git a/pkg/testdata/ethereum/receipt_50.json b/testutil/testdata/ethereum/receipt_50.json similarity index 100% rename from pkg/testdata/ethereum/receipt_50.json rename to testutil/testdata/ethereum/receipt_50.json diff --git a/pkg/testdata/ethereum/receipt_51.json b/testutil/testdata/ethereum/receipt_51.json similarity index 100% rename from pkg/testdata/ethereum/receipt_51.json rename to testutil/testdata/ethereum/receipt_51.json diff --git a/pkg/testdata/ethereum/receipt_52.json b/testutil/testdata/ethereum/receipt_52.json similarity index 100% rename from pkg/testdata/ethereum/receipt_52.json rename to testutil/testdata/ethereum/receipt_52.json diff --git a/pkg/testdata/ethereum/receipt_53.json b/testutil/testdata/ethereum/receipt_53.json similarity index 100% rename from pkg/testdata/ethereum/receipt_53.json rename to testutil/testdata/ethereum/receipt_53.json diff --git a/pkg/testdata/ethereum/receipt_54.json b/testutil/testdata/ethereum/receipt_54.json similarity index 100% rename from pkg/testdata/ethereum/receipt_54.json rename to testutil/testdata/ethereum/receipt_54.json diff --git a/pkg/testdata/ethereum/receipt_55.json b/testutil/testdata/ethereum/receipt_55.json similarity index 100% rename from pkg/testdata/ethereum/receipt_55.json rename to testutil/testdata/ethereum/receipt_55.json diff --git a/pkg/testdata/ethereum/receipt_56.json b/testutil/testdata/ethereum/receipt_56.json similarity index 100% rename from pkg/testdata/ethereum/receipt_56.json rename to testutil/testdata/ethereum/receipt_56.json diff --git a/pkg/testdata/ethereum/receipt_57.json b/testutil/testdata/ethereum/receipt_57.json similarity index 100% rename from pkg/testdata/ethereum/receipt_57.json rename to testutil/testdata/ethereum/receipt_57.json diff --git a/pkg/testdata/ethereum/receipt_58.json b/testutil/testdata/ethereum/receipt_58.json similarity index 100% rename from pkg/testdata/ethereum/receipt_58.json rename to testutil/testdata/ethereum/receipt_58.json diff --git a/pkg/testdata/ethereum/receipt_59.json b/testutil/testdata/ethereum/receipt_59.json similarity index 100% rename from pkg/testdata/ethereum/receipt_59.json rename to testutil/testdata/ethereum/receipt_59.json diff --git a/pkg/testdata/ethereum/receipt_6.json b/testutil/testdata/ethereum/receipt_6.json similarity index 100% rename from pkg/testdata/ethereum/receipt_6.json rename to testutil/testdata/ethereum/receipt_6.json diff --git a/pkg/testdata/ethereum/receipt_60.json b/testutil/testdata/ethereum/receipt_60.json similarity index 100% rename from pkg/testdata/ethereum/receipt_60.json rename to testutil/testdata/ethereum/receipt_60.json diff --git a/pkg/testdata/ethereum/receipt_61.json b/testutil/testdata/ethereum/receipt_61.json similarity index 100% rename from pkg/testdata/ethereum/receipt_61.json rename to testutil/testdata/ethereum/receipt_61.json diff --git a/pkg/testdata/ethereum/receipt_62.json b/testutil/testdata/ethereum/receipt_62.json similarity index 100% rename from pkg/testdata/ethereum/receipt_62.json rename to testutil/testdata/ethereum/receipt_62.json diff --git a/pkg/testdata/ethereum/receipt_63.json b/testutil/testdata/ethereum/receipt_63.json similarity index 100% rename from pkg/testdata/ethereum/receipt_63.json rename to testutil/testdata/ethereum/receipt_63.json diff --git a/pkg/testdata/ethereum/receipt_64.json b/testutil/testdata/ethereum/receipt_64.json similarity index 100% rename from pkg/testdata/ethereum/receipt_64.json rename to testutil/testdata/ethereum/receipt_64.json diff --git a/pkg/testdata/ethereum/receipt_65.json b/testutil/testdata/ethereum/receipt_65.json similarity index 100% rename from pkg/testdata/ethereum/receipt_65.json rename to testutil/testdata/ethereum/receipt_65.json diff --git a/pkg/testdata/ethereum/receipt_66.json b/testutil/testdata/ethereum/receipt_66.json similarity index 100% rename from pkg/testdata/ethereum/receipt_66.json rename to testutil/testdata/ethereum/receipt_66.json diff --git a/pkg/testdata/ethereum/receipt_67.json b/testutil/testdata/ethereum/receipt_67.json similarity index 100% rename from pkg/testdata/ethereum/receipt_67.json rename to testutil/testdata/ethereum/receipt_67.json diff --git a/pkg/testdata/ethereum/receipt_68.json b/testutil/testdata/ethereum/receipt_68.json similarity index 100% rename from pkg/testdata/ethereum/receipt_68.json rename to testutil/testdata/ethereum/receipt_68.json diff --git a/pkg/testdata/ethereum/receipt_69.json b/testutil/testdata/ethereum/receipt_69.json similarity index 100% rename from pkg/testdata/ethereum/receipt_69.json rename to testutil/testdata/ethereum/receipt_69.json diff --git a/pkg/testdata/ethereum/receipt_7.json b/testutil/testdata/ethereum/receipt_7.json similarity index 100% rename from pkg/testdata/ethereum/receipt_7.json rename to testutil/testdata/ethereum/receipt_7.json diff --git a/pkg/testdata/ethereum/receipt_70.json b/testutil/testdata/ethereum/receipt_70.json similarity index 100% rename from pkg/testdata/ethereum/receipt_70.json rename to testutil/testdata/ethereum/receipt_70.json diff --git a/pkg/testdata/ethereum/receipt_71.json b/testutil/testdata/ethereum/receipt_71.json similarity index 100% rename from pkg/testdata/ethereum/receipt_71.json rename to testutil/testdata/ethereum/receipt_71.json diff --git a/pkg/testdata/ethereum/receipt_72.json b/testutil/testdata/ethereum/receipt_72.json similarity index 100% rename from pkg/testdata/ethereum/receipt_72.json rename to testutil/testdata/ethereum/receipt_72.json diff --git a/pkg/testdata/ethereum/receipt_73.json b/testutil/testdata/ethereum/receipt_73.json similarity index 100% rename from pkg/testdata/ethereum/receipt_73.json rename to testutil/testdata/ethereum/receipt_73.json diff --git a/pkg/testdata/ethereum/receipt_74.json b/testutil/testdata/ethereum/receipt_74.json similarity index 100% rename from pkg/testdata/ethereum/receipt_74.json rename to testutil/testdata/ethereum/receipt_74.json diff --git a/pkg/testdata/ethereum/receipt_75.json b/testutil/testdata/ethereum/receipt_75.json similarity index 100% rename from pkg/testdata/ethereum/receipt_75.json rename to testutil/testdata/ethereum/receipt_75.json diff --git a/pkg/testdata/ethereum/receipt_76.json b/testutil/testdata/ethereum/receipt_76.json similarity index 100% rename from pkg/testdata/ethereum/receipt_76.json rename to testutil/testdata/ethereum/receipt_76.json diff --git a/pkg/testdata/ethereum/receipt_77.json b/testutil/testdata/ethereum/receipt_77.json similarity index 100% rename from pkg/testdata/ethereum/receipt_77.json rename to testutil/testdata/ethereum/receipt_77.json diff --git a/pkg/testdata/ethereum/receipt_78.json b/testutil/testdata/ethereum/receipt_78.json similarity index 100% rename from pkg/testdata/ethereum/receipt_78.json rename to testutil/testdata/ethereum/receipt_78.json diff --git a/pkg/testdata/ethereum/receipt_79.json b/testutil/testdata/ethereum/receipt_79.json similarity index 100% rename from pkg/testdata/ethereum/receipt_79.json rename to testutil/testdata/ethereum/receipt_79.json diff --git a/pkg/testdata/ethereum/receipt_8.json b/testutil/testdata/ethereum/receipt_8.json similarity index 100% rename from pkg/testdata/ethereum/receipt_8.json rename to testutil/testdata/ethereum/receipt_8.json diff --git a/pkg/testdata/ethereum/receipt_80.json b/testutil/testdata/ethereum/receipt_80.json similarity index 100% rename from pkg/testdata/ethereum/receipt_80.json rename to testutil/testdata/ethereum/receipt_80.json diff --git a/pkg/testdata/ethereum/receipt_9.json b/testutil/testdata/ethereum/receipt_9.json similarity index 100% rename from pkg/testdata/ethereum/receipt_9.json rename to testutil/testdata/ethereum/receipt_9.json diff --git a/pkg/testdata/ethereum/tx_0.json b/testutil/testdata/ethereum/tx_0.json similarity index 100% rename from pkg/testdata/ethereum/tx_0.json rename to testutil/testdata/ethereum/tx_0.json diff --git a/pkg/testdata/ethereum/tx_1.json b/testutil/testdata/ethereum/tx_1.json similarity index 100% rename from pkg/testdata/ethereum/tx_1.json rename to testutil/testdata/ethereum/tx_1.json diff --git a/pkg/testdata/ethereum/tx_10.json b/testutil/testdata/ethereum/tx_10.json similarity index 100% rename from pkg/testdata/ethereum/tx_10.json rename to testutil/testdata/ethereum/tx_10.json diff --git a/pkg/testdata/ethereum/tx_11.json b/testutil/testdata/ethereum/tx_11.json similarity index 100% rename from pkg/testdata/ethereum/tx_11.json rename to testutil/testdata/ethereum/tx_11.json diff --git a/pkg/testdata/ethereum/tx_12.json b/testutil/testdata/ethereum/tx_12.json similarity index 100% rename from pkg/testdata/ethereum/tx_12.json rename to testutil/testdata/ethereum/tx_12.json diff --git a/pkg/testdata/ethereum/tx_13.json b/testutil/testdata/ethereum/tx_13.json similarity index 100% rename from pkg/testdata/ethereum/tx_13.json rename to testutil/testdata/ethereum/tx_13.json diff --git a/pkg/testdata/ethereum/tx_14.json b/testutil/testdata/ethereum/tx_14.json similarity index 100% rename from pkg/testdata/ethereum/tx_14.json rename to testutil/testdata/ethereum/tx_14.json diff --git a/pkg/testdata/ethereum/tx_15.json b/testutil/testdata/ethereum/tx_15.json similarity index 100% rename from pkg/testdata/ethereum/tx_15.json rename to testutil/testdata/ethereum/tx_15.json diff --git a/pkg/testdata/ethereum/tx_16.json b/testutil/testdata/ethereum/tx_16.json similarity index 100% rename from pkg/testdata/ethereum/tx_16.json rename to testutil/testdata/ethereum/tx_16.json diff --git a/pkg/testdata/ethereum/tx_17.json b/testutil/testdata/ethereum/tx_17.json similarity index 100% rename from pkg/testdata/ethereum/tx_17.json rename to testutil/testdata/ethereum/tx_17.json diff --git a/pkg/testdata/ethereum/tx_18.json b/testutil/testdata/ethereum/tx_18.json similarity index 100% rename from pkg/testdata/ethereum/tx_18.json rename to testutil/testdata/ethereum/tx_18.json diff --git a/pkg/testdata/ethereum/tx_19.json b/testutil/testdata/ethereum/tx_19.json similarity index 100% rename from pkg/testdata/ethereum/tx_19.json rename to testutil/testdata/ethereum/tx_19.json diff --git a/pkg/testdata/ethereum/tx_2.json b/testutil/testdata/ethereum/tx_2.json similarity index 100% rename from pkg/testdata/ethereum/tx_2.json rename to testutil/testdata/ethereum/tx_2.json diff --git a/pkg/testdata/ethereum/tx_20.json b/testutil/testdata/ethereum/tx_20.json similarity index 100% rename from pkg/testdata/ethereum/tx_20.json rename to testutil/testdata/ethereum/tx_20.json diff --git a/pkg/testdata/ethereum/tx_21.json b/testutil/testdata/ethereum/tx_21.json similarity index 100% rename from pkg/testdata/ethereum/tx_21.json rename to testutil/testdata/ethereum/tx_21.json diff --git a/pkg/testdata/ethereum/tx_22.json b/testutil/testdata/ethereum/tx_22.json similarity index 100% rename from pkg/testdata/ethereum/tx_22.json rename to testutil/testdata/ethereum/tx_22.json diff --git a/pkg/testdata/ethereum/tx_23.json b/testutil/testdata/ethereum/tx_23.json similarity index 100% rename from pkg/testdata/ethereum/tx_23.json rename to testutil/testdata/ethereum/tx_23.json diff --git a/pkg/testdata/ethereum/tx_24.json b/testutil/testdata/ethereum/tx_24.json similarity index 100% rename from pkg/testdata/ethereum/tx_24.json rename to testutil/testdata/ethereum/tx_24.json diff --git a/pkg/testdata/ethereum/tx_25.json b/testutil/testdata/ethereum/tx_25.json similarity index 100% rename from pkg/testdata/ethereum/tx_25.json rename to testutil/testdata/ethereum/tx_25.json diff --git a/pkg/testdata/ethereum/tx_26.json b/testutil/testdata/ethereum/tx_26.json similarity index 100% rename from pkg/testdata/ethereum/tx_26.json rename to testutil/testdata/ethereum/tx_26.json diff --git a/pkg/testdata/ethereum/tx_27.json b/testutil/testdata/ethereum/tx_27.json similarity index 100% rename from pkg/testdata/ethereum/tx_27.json rename to testutil/testdata/ethereum/tx_27.json diff --git a/pkg/testdata/ethereum/tx_28.json b/testutil/testdata/ethereum/tx_28.json similarity index 100% rename from pkg/testdata/ethereum/tx_28.json rename to testutil/testdata/ethereum/tx_28.json diff --git a/pkg/testdata/ethereum/tx_29.json b/testutil/testdata/ethereum/tx_29.json similarity index 100% rename from pkg/testdata/ethereum/tx_29.json rename to testutil/testdata/ethereum/tx_29.json diff --git a/pkg/testdata/ethereum/tx_3.json b/testutil/testdata/ethereum/tx_3.json similarity index 100% rename from pkg/testdata/ethereum/tx_3.json rename to testutil/testdata/ethereum/tx_3.json diff --git a/pkg/testdata/ethereum/tx_30.json b/testutil/testdata/ethereum/tx_30.json similarity index 100% rename from pkg/testdata/ethereum/tx_30.json rename to testutil/testdata/ethereum/tx_30.json diff --git a/pkg/testdata/ethereum/tx_31.json b/testutil/testdata/ethereum/tx_31.json similarity index 100% rename from pkg/testdata/ethereum/tx_31.json rename to testutil/testdata/ethereum/tx_31.json diff --git a/pkg/testdata/ethereum/tx_32.json b/testutil/testdata/ethereum/tx_32.json similarity index 100% rename from pkg/testdata/ethereum/tx_32.json rename to testutil/testdata/ethereum/tx_32.json diff --git a/pkg/testdata/ethereum/tx_33.json b/testutil/testdata/ethereum/tx_33.json similarity index 100% rename from pkg/testdata/ethereum/tx_33.json rename to testutil/testdata/ethereum/tx_33.json diff --git a/pkg/testdata/ethereum/tx_34.json b/testutil/testdata/ethereum/tx_34.json similarity index 100% rename from pkg/testdata/ethereum/tx_34.json rename to testutil/testdata/ethereum/tx_34.json diff --git a/pkg/testdata/ethereum/tx_35.json b/testutil/testdata/ethereum/tx_35.json similarity index 100% rename from pkg/testdata/ethereum/tx_35.json rename to testutil/testdata/ethereum/tx_35.json diff --git a/pkg/testdata/ethereum/tx_36.json b/testutil/testdata/ethereum/tx_36.json similarity index 100% rename from pkg/testdata/ethereum/tx_36.json rename to testutil/testdata/ethereum/tx_36.json diff --git a/pkg/testdata/ethereum/tx_37.json b/testutil/testdata/ethereum/tx_37.json similarity index 100% rename from pkg/testdata/ethereum/tx_37.json rename to testutil/testdata/ethereum/tx_37.json diff --git a/pkg/testdata/ethereum/tx_38.json b/testutil/testdata/ethereum/tx_38.json similarity index 100% rename from pkg/testdata/ethereum/tx_38.json rename to testutil/testdata/ethereum/tx_38.json diff --git a/pkg/testdata/ethereum/tx_39.json b/testutil/testdata/ethereum/tx_39.json similarity index 100% rename from pkg/testdata/ethereum/tx_39.json rename to testutil/testdata/ethereum/tx_39.json diff --git a/pkg/testdata/ethereum/tx_4.json b/testutil/testdata/ethereum/tx_4.json similarity index 100% rename from pkg/testdata/ethereum/tx_4.json rename to testutil/testdata/ethereum/tx_4.json diff --git a/pkg/testdata/ethereum/tx_40.json b/testutil/testdata/ethereum/tx_40.json similarity index 100% rename from pkg/testdata/ethereum/tx_40.json rename to testutil/testdata/ethereum/tx_40.json diff --git a/pkg/testdata/ethereum/tx_41.json b/testutil/testdata/ethereum/tx_41.json similarity index 100% rename from pkg/testdata/ethereum/tx_41.json rename to testutil/testdata/ethereum/tx_41.json diff --git a/pkg/testdata/ethereum/tx_42.json b/testutil/testdata/ethereum/tx_42.json similarity index 100% rename from pkg/testdata/ethereum/tx_42.json rename to testutil/testdata/ethereum/tx_42.json diff --git a/pkg/testdata/ethereum/tx_43.json b/testutil/testdata/ethereum/tx_43.json similarity index 100% rename from pkg/testdata/ethereum/tx_43.json rename to testutil/testdata/ethereum/tx_43.json diff --git a/pkg/testdata/ethereum/tx_44.json b/testutil/testdata/ethereum/tx_44.json similarity index 100% rename from pkg/testdata/ethereum/tx_44.json rename to testutil/testdata/ethereum/tx_44.json diff --git a/pkg/testdata/ethereum/tx_45.json b/testutil/testdata/ethereum/tx_45.json similarity index 100% rename from pkg/testdata/ethereum/tx_45.json rename to testutil/testdata/ethereum/tx_45.json diff --git a/pkg/testdata/ethereum/tx_46.json b/testutil/testdata/ethereum/tx_46.json similarity index 100% rename from pkg/testdata/ethereum/tx_46.json rename to testutil/testdata/ethereum/tx_46.json diff --git a/pkg/testdata/ethereum/tx_47.json b/testutil/testdata/ethereum/tx_47.json similarity index 100% rename from pkg/testdata/ethereum/tx_47.json rename to testutil/testdata/ethereum/tx_47.json diff --git a/pkg/testdata/ethereum/tx_48.json b/testutil/testdata/ethereum/tx_48.json similarity index 100% rename from pkg/testdata/ethereum/tx_48.json rename to testutil/testdata/ethereum/tx_48.json diff --git a/pkg/testdata/ethereum/tx_49.json b/testutil/testdata/ethereum/tx_49.json similarity index 100% rename from pkg/testdata/ethereum/tx_49.json rename to testutil/testdata/ethereum/tx_49.json diff --git a/pkg/testdata/ethereum/tx_5.json b/testutil/testdata/ethereum/tx_5.json similarity index 100% rename from pkg/testdata/ethereum/tx_5.json rename to testutil/testdata/ethereum/tx_5.json diff --git a/pkg/testdata/ethereum/tx_50.json b/testutil/testdata/ethereum/tx_50.json similarity index 100% rename from pkg/testdata/ethereum/tx_50.json rename to testutil/testdata/ethereum/tx_50.json diff --git a/pkg/testdata/ethereum/tx_51.json b/testutil/testdata/ethereum/tx_51.json similarity index 100% rename from pkg/testdata/ethereum/tx_51.json rename to testutil/testdata/ethereum/tx_51.json diff --git a/pkg/testdata/ethereum/tx_52.json b/testutil/testdata/ethereum/tx_52.json similarity index 100% rename from pkg/testdata/ethereum/tx_52.json rename to testutil/testdata/ethereum/tx_52.json diff --git a/pkg/testdata/ethereum/tx_53.json b/testutil/testdata/ethereum/tx_53.json similarity index 100% rename from pkg/testdata/ethereum/tx_53.json rename to testutil/testdata/ethereum/tx_53.json diff --git a/pkg/testdata/ethereum/tx_54.json b/testutil/testdata/ethereum/tx_54.json similarity index 100% rename from pkg/testdata/ethereum/tx_54.json rename to testutil/testdata/ethereum/tx_54.json diff --git a/pkg/testdata/ethereum/tx_55.json b/testutil/testdata/ethereum/tx_55.json similarity index 100% rename from pkg/testdata/ethereum/tx_55.json rename to testutil/testdata/ethereum/tx_55.json diff --git a/pkg/testdata/ethereum/tx_56.json b/testutil/testdata/ethereum/tx_56.json similarity index 100% rename from pkg/testdata/ethereum/tx_56.json rename to testutil/testdata/ethereum/tx_56.json diff --git a/pkg/testdata/ethereum/tx_57.json b/testutil/testdata/ethereum/tx_57.json similarity index 100% rename from pkg/testdata/ethereum/tx_57.json rename to testutil/testdata/ethereum/tx_57.json diff --git a/pkg/testdata/ethereum/tx_58.json b/testutil/testdata/ethereum/tx_58.json similarity index 100% rename from pkg/testdata/ethereum/tx_58.json rename to testutil/testdata/ethereum/tx_58.json diff --git a/pkg/testdata/ethereum/tx_59.json b/testutil/testdata/ethereum/tx_59.json similarity index 100% rename from pkg/testdata/ethereum/tx_59.json rename to testutil/testdata/ethereum/tx_59.json diff --git a/pkg/testdata/ethereum/tx_6.json b/testutil/testdata/ethereum/tx_6.json similarity index 100% rename from pkg/testdata/ethereum/tx_6.json rename to testutil/testdata/ethereum/tx_6.json diff --git a/pkg/testdata/ethereum/tx_60.json b/testutil/testdata/ethereum/tx_60.json similarity index 100% rename from pkg/testdata/ethereum/tx_60.json rename to testutil/testdata/ethereum/tx_60.json diff --git a/pkg/testdata/ethereum/tx_61.json b/testutil/testdata/ethereum/tx_61.json similarity index 100% rename from pkg/testdata/ethereum/tx_61.json rename to testutil/testdata/ethereum/tx_61.json diff --git a/pkg/testdata/ethereum/tx_62.json b/testutil/testdata/ethereum/tx_62.json similarity index 100% rename from pkg/testdata/ethereum/tx_62.json rename to testutil/testdata/ethereum/tx_62.json diff --git a/pkg/testdata/ethereum/tx_63.json b/testutil/testdata/ethereum/tx_63.json similarity index 100% rename from pkg/testdata/ethereum/tx_63.json rename to testutil/testdata/ethereum/tx_63.json diff --git a/pkg/testdata/ethereum/tx_64.json b/testutil/testdata/ethereum/tx_64.json similarity index 100% rename from pkg/testdata/ethereum/tx_64.json rename to testutil/testdata/ethereum/tx_64.json diff --git a/pkg/testdata/ethereum/tx_65.json b/testutil/testdata/ethereum/tx_65.json similarity index 100% rename from pkg/testdata/ethereum/tx_65.json rename to testutil/testdata/ethereum/tx_65.json diff --git a/pkg/testdata/ethereum/tx_66.json b/testutil/testdata/ethereum/tx_66.json similarity index 100% rename from pkg/testdata/ethereum/tx_66.json rename to testutil/testdata/ethereum/tx_66.json diff --git a/pkg/testdata/ethereum/tx_67.json b/testutil/testdata/ethereum/tx_67.json similarity index 100% rename from pkg/testdata/ethereum/tx_67.json rename to testutil/testdata/ethereum/tx_67.json diff --git a/pkg/testdata/ethereum/tx_68.json b/testutil/testdata/ethereum/tx_68.json similarity index 100% rename from pkg/testdata/ethereum/tx_68.json rename to testutil/testdata/ethereum/tx_68.json diff --git a/pkg/testdata/ethereum/tx_69.json b/testutil/testdata/ethereum/tx_69.json similarity index 100% rename from pkg/testdata/ethereum/tx_69.json rename to testutil/testdata/ethereum/tx_69.json diff --git a/pkg/testdata/ethereum/tx_7.json b/testutil/testdata/ethereum/tx_7.json similarity index 100% rename from pkg/testdata/ethereum/tx_7.json rename to testutil/testdata/ethereum/tx_7.json diff --git a/pkg/testdata/ethereum/tx_70.json b/testutil/testdata/ethereum/tx_70.json similarity index 100% rename from pkg/testdata/ethereum/tx_70.json rename to testutil/testdata/ethereum/tx_70.json diff --git a/pkg/testdata/ethereum/tx_71.json b/testutil/testdata/ethereum/tx_71.json similarity index 100% rename from pkg/testdata/ethereum/tx_71.json rename to testutil/testdata/ethereum/tx_71.json diff --git a/pkg/testdata/ethereum/tx_72.json b/testutil/testdata/ethereum/tx_72.json similarity index 100% rename from pkg/testdata/ethereum/tx_72.json rename to testutil/testdata/ethereum/tx_72.json diff --git a/pkg/testdata/ethereum/tx_73.json b/testutil/testdata/ethereum/tx_73.json similarity index 100% rename from pkg/testdata/ethereum/tx_73.json rename to testutil/testdata/ethereum/tx_73.json diff --git a/pkg/testdata/ethereum/tx_74.json b/testutil/testdata/ethereum/tx_74.json similarity index 100% rename from pkg/testdata/ethereum/tx_74.json rename to testutil/testdata/ethereum/tx_74.json diff --git a/pkg/testdata/ethereum/tx_75.json b/testutil/testdata/ethereum/tx_75.json similarity index 100% rename from pkg/testdata/ethereum/tx_75.json rename to testutil/testdata/ethereum/tx_75.json diff --git a/pkg/testdata/ethereum/tx_76.json b/testutil/testdata/ethereum/tx_76.json similarity index 100% rename from pkg/testdata/ethereum/tx_76.json rename to testutil/testdata/ethereum/tx_76.json diff --git a/pkg/testdata/ethereum/tx_77.json b/testutil/testdata/ethereum/tx_77.json similarity index 100% rename from pkg/testdata/ethereum/tx_77.json rename to testutil/testdata/ethereum/tx_77.json diff --git a/pkg/testdata/ethereum/tx_78.json b/testutil/testdata/ethereum/tx_78.json similarity index 100% rename from pkg/testdata/ethereum/tx_78.json rename to testutil/testdata/ethereum/tx_78.json diff --git a/pkg/testdata/ethereum/tx_79.json b/testutil/testdata/ethereum/tx_79.json similarity index 100% rename from pkg/testdata/ethereum/tx_79.json rename to testutil/testdata/ethereum/tx_79.json diff --git a/pkg/testdata/ethereum/tx_8.json b/testutil/testdata/ethereum/tx_8.json similarity index 100% rename from pkg/testdata/ethereum/tx_8.json rename to testutil/testdata/ethereum/tx_8.json diff --git a/pkg/testdata/ethereum/tx_80.json b/testutil/testdata/ethereum/tx_80.json similarity index 100% rename from pkg/testdata/ethereum/tx_80.json rename to testutil/testdata/ethereum/tx_80.json diff --git a/pkg/testdata/ethereum/tx_9.json b/testutil/testdata/ethereum/tx_9.json similarity index 100% rename from pkg/testdata/ethereum/tx_9.json rename to testutil/testdata/ethereum/tx_9.json diff --git a/pkg/testdata/test_blocks.json b/testutil/testdata/test_blocks.json similarity index 100% rename from pkg/testdata/test_blocks.json rename to testutil/testdata/test_blocks.json diff --git a/pkg/testdata/testdata.go b/testutil/testdata/testdata.go similarity index 100% rename from pkg/testdata/testdata.go rename to testutil/testdata/testdata.go diff --git a/pkg/testdata/types/chain_info.json b/testutil/testdata/types/chain_info.json similarity index 100% rename from pkg/testdata/types/chain_info.json rename to testutil/testdata/types/chain_info.json diff --git a/pkg/testdata/types/policies.json b/testutil/testdata/types/policies.json similarity index 100% rename from pkg/testdata/types/policies.json rename to testutil/testdata/types/policies.json diff --git a/x/authority/client/cli/tx_update_chain_info_test.go b/x/authority/client/cli/tx_update_chain_info_test.go index 95b033777f..2d04a82b29 100644 --- a/x/authority/client/cli/tx_update_chain_info_test.go +++ b/x/authority/client/cli/tx_update_chain_info_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/pkg/chains" - "github.com/zeta-chain/zetacore/pkg/testdata" + "github.com/zeta-chain/zetacore/testutil/testdata" "github.com/zeta-chain/zetacore/x/authority/client/cli" ) diff --git a/x/authority/client/cli/tx_update_policies_test.go b/x/authority/client/cli/tx_update_policies_test.go index 45e6813fcb..35756605c9 100644 --- a/x/authority/client/cli/tx_update_policies_test.go +++ b/x/authority/client/cli/tx_update_policies_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/zeta-chain/zetacore/pkg/testdata" + "github.com/zeta-chain/zetacore/testutil/testdata" "github.com/zeta-chain/zetacore/x/authority/client/cli" authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" ) diff --git a/x/observer/types/message_vote_block_header_test.go b/x/observer/types/message_vote_block_header_test.go index 192d164d43..763114c122 100644 --- a/x/observer/types/message_vote_block_header_test.go +++ b/x/observer/types/message_vote_block_header_test.go @@ -20,7 +20,7 @@ import ( func TestMsgVoteBlockHeader_ValidateBasic(t *testing.T) { keeper.SetConfig(false) var header ethtypes.Header - file, err := os.Open("../../../pkg/testdata/eth_header_18495266.json") + file, err := os.Open("../../../testutil/testdata/eth_header_18495266.json") require.NoError(t, err) defer file.Close() headerBytes := make([]byte, 4096) diff --git a/zetaclient/zetacore/tx_test.go b/zetaclient/zetacore/tx_test.go index 919f9cc058..bd860bc894 100644 --- a/zetaclient/zetacore/tx_test.go +++ b/zetaclient/zetacore/tx_test.go @@ -123,7 +123,7 @@ func MockBroadcastError(_ *Client, _ uint64, _ sdktypes.Msg, _ authz.Signer) (st func getHeaderData(t *testing.T) proofs.HeaderData { var header ethtypes.Header - file, err := os.Open("../../pkg/testdata/eth_header_18495266.json") + file, err := os.Open("../../testutil/testdata/eth_header_18495266.json") require.NoError(t, err) defer file.Close() headerBytes := make([]byte, 4096) From c93d1c6057f4278bacf434648f8daafd342fcfd3 Mon Sep 17 00:00:00 2001 From: Alex Gartner Date: Tue, 4 Jun 2024 08:23:37 -0700 Subject: [PATCH 2/9] fix: adjust upgrade orchestrator scp quoting (#2309) --- contrib/localnet/scripts/start-upgrade-orchestrator.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/localnet/scripts/start-upgrade-orchestrator.sh b/contrib/localnet/scripts/start-upgrade-orchestrator.sh index fb7bfe7a52..a2e8ec68fd 100755 --- a/contrib/localnet/scripts/start-upgrade-orchestrator.sh +++ b/contrib/localnet/scripts/start-upgrade-orchestrator.sh @@ -34,7 +34,7 @@ done # copy zetacore0 config and keys if not running on zetacore0 if [[ $(hostname) != "zetacore0" ]]; then - scp -r zetacore0:"~/.zetacored/config ~/.zetacored/os_info ~/.zetacored/config ~/.zetacored/keyring-file ~/.zetacored/keyring-test" ~/.zetacored/ + scp -r 'zetacore0:~/.zetacored/config' 'zetacore0:~/.zetacored/os_info' 'zetacore0:~/.zetacored/config' 'zetacore0:~/.zetacored/keyring-file' 'zetacore0:~/.zetacored/keyring-test' ~/.zetacored/ sed -i 's|tcp://localhost:26657|tcp://zetacore0:26657|g' ~/.zetacored/config/client.toml fi From 26b4f69521b8316a74a2d65ff0bbbd0f386b0cfa Mon Sep 17 00:00:00 2001 From: Tanmay Date: Tue, 4 Jun 2024 13:27:04 -0400 Subject: [PATCH 3/9] feat: add MsgAddAuthorization to add or update and authorization and MsgRemoveAuthorization to remove a authorization (#2305) --- changelog.md | 1 + docs/cli/zetacored/zetacored_tx_authority.md | 4 +- ...etacored_tx_authority_add-authorization.md | 53 + ...cored_tx_authority_remove-authorization.md | 53 + .../zetacored_tx_authority_update-policies.md | 2 +- docs/openapi/openapi.swagger.yaml | 12 + docs/spec/authority/messages.md | 24 + .../zetacore/authority/policies.proto | 1 + proto/zetachain/zetacore/authority/tx.proto | 27 + testutil/keeper/authority.go | 2 +- .../zetacore/authority/policies_pb.d.ts | 9 + .../zetachain/zetacore/authority/tx_pb.d.ts | 115 ++- x/authority/client/cli/tx.go | 4 +- .../client/cli/tx_add_authorization.go | 61 ++ .../client/cli/tx_add_authorizations_test.go | 60 ++ .../client/cli/tx_remove_authorization.go | 34 + x/authority/client/cli/tx_update_policies.go | 4 +- .../keeper/msg_server_add_authorization.go | 41 + .../msg_server_add_authorization_test.go | 219 +++++ .../keeper/msg_server_remove_authorization.go | 51 + .../msg_server_remove_authorization_test.go | 204 ++++ x/authority/types/authorizations.go | 15 +- x/authority/types/authorizations_test.go | 59 +- x/authority/types/errors.go | 7 +- .../types/message_add_authorization.go | 65 ++ .../types/message_add_authorizations_test.go | 130 +++ .../types/message_remove_authorization.go | 51 + .../message_remove_authorizations_test.go | 97 ++ x/authority/types/policies.go | 5 +- x/authority/types/policies.pb.go | 45 +- x/authority/types/policy_type.go | 13 + x/authority/types/policy_type_test.go | 58 ++ x/authority/types/tx.pb.go | 916 ++++++++++++++++-- x/crosschain/keeper/evm_hooks_test.go | 12 +- 34 files changed, 2330 insertions(+), 124 deletions(-) create mode 100644 docs/cli/zetacored/zetacored_tx_authority_add-authorization.md create mode 100644 docs/cli/zetacored/zetacored_tx_authority_remove-authorization.md create mode 100644 x/authority/client/cli/tx_add_authorization.go create mode 100644 x/authority/client/cli/tx_add_authorizations_test.go create mode 100644 x/authority/client/cli/tx_remove_authorization.go create mode 100644 x/authority/keeper/msg_server_add_authorization.go create mode 100644 x/authority/keeper/msg_server_add_authorization_test.go create mode 100644 x/authority/keeper/msg_server_remove_authorization.go create mode 100644 x/authority/keeper/msg_server_remove_authorization_test.go create mode 100644 x/authority/types/message_add_authorization.go create mode 100644 x/authority/types/message_add_authorizations_test.go create mode 100644 x/authority/types/message_remove_authorization.go create mode 100644 x/authority/types/message_remove_authorizations_test.go create mode 100644 x/authority/types/policy_type.go create mode 100644 x/authority/types/policy_type_test.go diff --git a/changelog.md b/changelog.md index 82d90197d2..dbf40ef8e3 100644 --- a/changelog.md +++ b/changelog.md @@ -22,6 +22,7 @@ * [2275](https://github.com/zeta-chain/node/pull/2275) - add ChainInfo singleton state variable in authority * [2291](https://github.com/zeta-chain/node/pull/2291) - initialize cctx gateway interface * [2289](https://github.com/zeta-chain/node/pull/2289) - add an authorization list to keep track of all authorizations on the chain +* [2305](https://github.com/zeta-chain/node/pull/2305) - add new messages `MsgAddAuthorization` and `MsgRemoveAuthorization` that can be used to update the authorization list ### Refactor diff --git a/docs/cli/zetacored/zetacored_tx_authority.md b/docs/cli/zetacored/zetacored_tx_authority.md index 5a543e70b1..59a0ca9ffa 100644 --- a/docs/cli/zetacored/zetacored_tx_authority.md +++ b/docs/cli/zetacored/zetacored_tx_authority.md @@ -26,6 +26,8 @@ zetacored tx authority [flags] ### SEE ALSO * [zetacored tx](zetacored_tx.md) - Transactions subcommands +* [zetacored tx authority add-authorization](zetacored_tx_authority_add-authorization.md) - Add a new authorization or update the policy of an existing authorization. Policy type can be 0 for groupEmergency, 1 for groupOperational, 2 for groupAdmin. +* [zetacored tx authority remove-authorization](zetacored_tx_authority_remove-authorization.md) - removes an existing authorization * [zetacored tx authority update-chain-info](zetacored_tx_authority_update-chain-info.md) - Update the chain info -* [zetacored tx authority update-policies](zetacored_tx_authority_update-policies.md) - Update the policies +* [zetacored tx authority update-policies](zetacored_tx_authority_update-policies.md) - Update policies to values provided in the JSON file. diff --git a/docs/cli/zetacored/zetacored_tx_authority_add-authorization.md b/docs/cli/zetacored/zetacored_tx_authority_add-authorization.md new file mode 100644 index 0000000000..4ed717b2b6 --- /dev/null +++ b/docs/cli/zetacored/zetacored_tx_authority_add-authorization.md @@ -0,0 +1,53 @@ +# tx authority add-authorization + +Add a new authorization or update the policy of an existing authorization. Policy type can be 0 for groupEmergency, 1 for groupOperational, 2 for groupAdmin. + +``` +zetacored tx authority add-authorization [msg-url] [authorized-policy] [flags] +``` + +### Options + +``` + -a, --account-number uint The account number of the signing account (offline mode only) + --aux Generate aux signer data instead of sending a tx + -b, --broadcast-mode string Transaction broadcasting mode (sync|async) + --chain-id string The network chain ID + --dry-run ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it (when enabled, the local Keybase is not accessible) + --fee-granter string Fee granter grants fees for the transaction + --fee-payer string Fee payer pays fees for the transaction instead of deducting from the signer + --fees string Fees to pay along with transaction; eg: 10uatom + --from string Name or address of private key with which to sign + --gas string gas limit to set per-transaction; set to "auto" to calculate sufficient gas automatically. Note: "auto" option doesn't always report accurate results. Set a valid coin value to adjust the result. Can be used instead of "fees". (default 200000) + --gas-adjustment float adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored (default 1) + --gas-prices string Gas prices in decimal format to determine the transaction fee (e.g. 0.1uatom) + --generate-only Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase only accessed when providing a key name) + -h, --help help for add-authorization + --keyring-backend string Select keyring's backend (os|file|kwallet|pass|test|memory) + --keyring-dir string The client Keyring directory; if omitted, the default 'home' directory will be used + --ledger Use a connected Ledger device + --node string [host]:[port] to tendermint rpc interface for this chain + --note string Note to add a description to the transaction (previously --memo) + --offline Offline mode (does not allow any online functionality) + -o, --output string Output format (text|json) + -s, --sequence uint The sequence number of the signing account (offline mode only) + --sign-mode string Choose sign mode (direct|amino-json|direct-aux), this is an advanced feature + --timeout-height uint Set a block timeout height to prevent the tx from being committed past a certain height + --tip string Tip is the amount that is going to be transferred to the fee payer on the target chain. This flag is only valid when used with --aux, and is ignored if the target chain didn't enable the TipDecorator + -y, --yes Skip tx broadcasting prompt confirmation +``` + +### Options inherited from parent commands + +``` + --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) + --log_no_color Disable colored logs + --trace print out full stack trace on errors +``` + +### SEE ALSO + +* [zetacored tx authority](zetacored_tx_authority.md) - authority transactions subcommands + diff --git a/docs/cli/zetacored/zetacored_tx_authority_remove-authorization.md b/docs/cli/zetacored/zetacored_tx_authority_remove-authorization.md new file mode 100644 index 0000000000..a29a02bb18 --- /dev/null +++ b/docs/cli/zetacored/zetacored_tx_authority_remove-authorization.md @@ -0,0 +1,53 @@ +# tx authority remove-authorization + +removes an existing authorization + +``` +zetacored tx authority remove-authorization [msg-url] [flags] +``` + +### Options + +``` + -a, --account-number uint The account number of the signing account (offline mode only) + --aux Generate aux signer data instead of sending a tx + -b, --broadcast-mode string Transaction broadcasting mode (sync|async) + --chain-id string The network chain ID + --dry-run ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it (when enabled, the local Keybase is not accessible) + --fee-granter string Fee granter grants fees for the transaction + --fee-payer string Fee payer pays fees for the transaction instead of deducting from the signer + --fees string Fees to pay along with transaction; eg: 10uatom + --from string Name or address of private key with which to sign + --gas string gas limit to set per-transaction; set to "auto" to calculate sufficient gas automatically. Note: "auto" option doesn't always report accurate results. Set a valid coin value to adjust the result. Can be used instead of "fees". (default 200000) + --gas-adjustment float adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored (default 1) + --gas-prices string Gas prices in decimal format to determine the transaction fee (e.g. 0.1uatom) + --generate-only Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase only accessed when providing a key name) + -h, --help help for remove-authorization + --keyring-backend string Select keyring's backend (os|file|kwallet|pass|test|memory) + --keyring-dir string The client Keyring directory; if omitted, the default 'home' directory will be used + --ledger Use a connected Ledger device + --node string [host]:[port] to tendermint rpc interface for this chain + --note string Note to add a description to the transaction (previously --memo) + --offline Offline mode (does not allow any online functionality) + -o, --output string Output format (text|json) + -s, --sequence uint The sequence number of the signing account (offline mode only) + --sign-mode string Choose sign mode (direct|amino-json|direct-aux), this is an advanced feature + --timeout-height uint Set a block timeout height to prevent the tx from being committed past a certain height + --tip string Tip is the amount that is going to be transferred to the fee payer on the target chain. This flag is only valid when used with --aux, and is ignored if the target chain didn't enable the TipDecorator + -y, --yes Skip tx broadcasting prompt confirmation +``` + +### Options inherited from parent commands + +``` + --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) + --log_no_color Disable colored logs + --trace print out full stack trace on errors +``` + +### SEE ALSO + +* [zetacored tx authority](zetacored_tx_authority.md) - authority transactions subcommands + diff --git a/docs/cli/zetacored/zetacored_tx_authority_update-policies.md b/docs/cli/zetacored/zetacored_tx_authority_update-policies.md index c26e3f0080..7e375d8d1e 100644 --- a/docs/cli/zetacored/zetacored_tx_authority_update-policies.md +++ b/docs/cli/zetacored/zetacored_tx_authority_update-policies.md @@ -1,6 +1,6 @@ # tx authority update-policies -Update the policies +Update policies to values provided in the JSON file. ``` zetacored tx authority update-policies [policies-json-file] [flags] diff --git a/docs/openapi/openapi.swagger.yaml b/docs/openapi/openapi.swagger.yaml index c9e758227f..8bb38c8a67 100644 --- a/docs/openapi/openapi.swagger.yaml +++ b/docs/openapi/openapi.swagger.yaml @@ -56730,6 +56730,14 @@ definitions: ChainInfo contains static information about the chains This structure is used to dynamically update these info on a live network before hardcoding the values in a upgrade + authorityMsgAddAuthorizationResponse: + type: object + description: MsgAddAuthorizationResponse defines the MsgAddAuthorizationResponse service. + authorityMsgRemoveAuthorizationResponse: + type: object + description: |- + MsgRemoveAuthorizationResponse defines the MsgRemoveAuthorizationResponse + service. authorityMsgUpdateChainInfoResponse: type: object description: MsgUpdateChainInfoResponse defines the MsgUpdateChainInfoResponse service. @@ -56758,6 +56766,7 @@ definitions: - groupEmergency - groupOperational - groupAdmin + - groupEmpty default: groupEmergency description: |- - groupEmergency: Used for emergency situations that require immediate action @@ -56765,6 +56774,9 @@ definitions: - groupAdmin: non-sensitive protocol parameters Used for administrative tasks like changing sensitive + - groupEmpty: protocol parameters or moving funds + + Used for empty policy, no action is allowed title: PolicyType defines the type of policy authorityQueryGetChainInfoResponse: type: object diff --git a/docs/spec/authority/messages.md b/docs/spec/authority/messages.md index 899a95b989..8ee4264bab 100644 --- a/docs/spec/authority/messages.md +++ b/docs/spec/authority/messages.md @@ -23,3 +23,27 @@ message MsgUpdateChainInfo { } ``` +## MsgAddAuthorization + +AddAuthorization defines a method to add an authorization.If the authorization already exists, it will be overwritten with the provided policy. +This should be called by the admin policy account. + +```proto +message MsgAddAuthorization { + string creator = 1; + string msg_url = 2; + PolicyType authorized_policy = 3; +} +``` + +## MsgRemoveAuthorization + +RemoveAuthorization removes the authorization from the list. It should be called by the admin policy account. + +```proto +message MsgRemoveAuthorization { + string creator = 1; + string msg_url = 2; +} +``` + diff --git a/proto/zetachain/zetacore/authority/policies.proto b/proto/zetachain/zetacore/authority/policies.proto index b02f448514..faefdbf2c7 100644 --- a/proto/zetachain/zetacore/authority/policies.proto +++ b/proto/zetachain/zetacore/authority/policies.proto @@ -14,6 +14,7 @@ enum PolicyType { // non-sensitive protocol parameters groupAdmin = 2; // Used for administrative tasks like changing sensitive // protocol parameters or moving funds + groupEmpty = 3; // Used for empty policy, no action is allowed } message Policy { diff --git a/proto/zetachain/zetacore/authority/tx.proto b/proto/zetachain/zetacore/authority/tx.proto index 55509e3172..29b1b44362 100644 --- a/proto/zetachain/zetacore/authority/tx.proto +++ b/proto/zetachain/zetacore/authority/tx.proto @@ -12,8 +12,35 @@ option go_package = "github.com/zeta-chain/zetacore/x/authority/types"; service Msg { rpc UpdatePolicies(MsgUpdatePolicies) returns (MsgUpdatePoliciesResponse); rpc UpdateChainInfo(MsgUpdateChainInfo) returns (MsgUpdateChainInfoResponse); + rpc AddAuthorization(MsgAddAuthorization) + returns (MsgAddAuthorizationResponse); + rpc RemoveAuthorization(MsgRemoveAuthorization) + returns (MsgRemoveAuthorizationResponse); } +// MsgAddAuthorization defines the MsgAddAuthorization service. +// Adds an authorization to the chain. If the authorization already exists, it +// will be updated. +message MsgAddAuthorization { + string creator = 1; + string msg_url = 2; + PolicyType authorized_policy = 3; +} + +// MsgAddAuthorizationResponse defines the MsgAddAuthorizationResponse service. +message MsgAddAuthorizationResponse {} + +// MsgRemoveAuthorization defines the MsgRemoveAuthorization service. +// Removes an authorization from the chain. +message MsgRemoveAuthorization { + string creator = 1; + string msg_url = 2; +} + +// MsgRemoveAuthorizationResponse defines the MsgRemoveAuthorizationResponse +// service. +message MsgRemoveAuthorizationResponse {} + // MsgUpdatePolicies defines the MsgUpdatePolicies service. message MsgUpdatePolicies { string creator = 1; diff --git a/testutil/keeper/authority.go b/testutil/keeper/authority.go index 3e7f98b506..be9101d1df 100644 --- a/testutil/keeper/authority.go +++ b/testutil/keeper/authority.go @@ -79,7 +79,7 @@ func MockIsAuthorized(m *mock.Mock, address string, policyType types.PolicyType, m.On("IsAuthorized", mock.Anything, address, policyType).Return(isAuthorized).Once() } -func SetAdminPolices(ctx sdk.Context, ak *keeper.Keeper) string { +func SetAdminPolicies(ctx sdk.Context, ak *keeper.Keeper) string { admin := sample.AccAddress() ak.SetPolicies(ctx, types.Policies{Items: []*types.Policy{ { diff --git a/typescript/zetachain/zetacore/authority/policies_pb.d.ts b/typescript/zetachain/zetacore/authority/policies_pb.d.ts index 21646f273a..8bf6b71c00 100644 --- a/typescript/zetachain/zetacore/authority/policies_pb.d.ts +++ b/typescript/zetachain/zetacore/authority/policies_pb.d.ts @@ -34,6 +34,15 @@ export declare enum PolicyType { * @generated from enum value: groupAdmin = 2; */ groupAdmin = 2, + + /** + * protocol parameters or moving funds + * + * Used for empty policy, no action is allowed + * + * @generated from enum value: groupEmpty = 3; + */ + groupEmpty = 3, } /** diff --git a/typescript/zetachain/zetacore/authority/tx_pb.d.ts b/typescript/zetachain/zetacore/authority/tx_pb.d.ts index bd531ac2e5..827b942c9b 100644 --- a/typescript/zetachain/zetacore/authority/tx_pb.d.ts +++ b/typescript/zetachain/zetacore/authority/tx_pb.d.ts @@ -5,9 +5,122 @@ import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; import { Message, proto3 } from "@bufbuild/protobuf"; -import type { Policies } from "./policies_pb.js"; +import type { Policies, PolicyType } from "./policies_pb.js"; import type { ChainInfo } from "./chain_info_pb.js"; +/** + * MsgAddAuthorization defines the MsgAddAuthorization service. + * Adds an authorization to the chain. If the authorization already exists, it + * will be updated. + * + * @generated from message zetachain.zetacore.authority.MsgAddAuthorization + */ +export declare class MsgAddAuthorization extends Message { + /** + * @generated from field: string creator = 1; + */ + creator: string; + + /** + * @generated from field: string msg_url = 2; + */ + msgUrl: string; + + /** + * @generated from field: zetachain.zetacore.authority.PolicyType authorized_policy = 3; + */ + authorizedPolicy: PolicyType; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.authority.MsgAddAuthorization"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): MsgAddAuthorization; + + static fromJson(jsonValue: JsonValue, options?: Partial): MsgAddAuthorization; + + static fromJsonString(jsonString: string, options?: Partial): MsgAddAuthorization; + + static equals(a: MsgAddAuthorization | PlainMessage | undefined, b: MsgAddAuthorization | PlainMessage | undefined): boolean; +} + +/** + * MsgAddAuthorizationResponse defines the MsgAddAuthorizationResponse service. + * + * @generated from message zetachain.zetacore.authority.MsgAddAuthorizationResponse + */ +export declare class MsgAddAuthorizationResponse extends Message { + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.authority.MsgAddAuthorizationResponse"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): MsgAddAuthorizationResponse; + + static fromJson(jsonValue: JsonValue, options?: Partial): MsgAddAuthorizationResponse; + + static fromJsonString(jsonString: string, options?: Partial): MsgAddAuthorizationResponse; + + static equals(a: MsgAddAuthorizationResponse | PlainMessage | undefined, b: MsgAddAuthorizationResponse | PlainMessage | undefined): boolean; +} + +/** + * MsgRemoveAuthorization defines the MsgRemoveAuthorization service. + * Removes an authorization from the chain. + * + * @generated from message zetachain.zetacore.authority.MsgRemoveAuthorization + */ +export declare class MsgRemoveAuthorization extends Message { + /** + * @generated from field: string creator = 1; + */ + creator: string; + + /** + * @generated from field: string msg_url = 2; + */ + msgUrl: string; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.authority.MsgRemoveAuthorization"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): MsgRemoveAuthorization; + + static fromJson(jsonValue: JsonValue, options?: Partial): MsgRemoveAuthorization; + + static fromJsonString(jsonString: string, options?: Partial): MsgRemoveAuthorization; + + static equals(a: MsgRemoveAuthorization | PlainMessage | undefined, b: MsgRemoveAuthorization | PlainMessage | undefined): boolean; +} + +/** + * MsgRemoveAuthorizationResponse defines the MsgRemoveAuthorizationResponse + * service. + * + * @generated from message zetachain.zetacore.authority.MsgRemoveAuthorizationResponse + */ +export declare class MsgRemoveAuthorizationResponse extends Message { + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.authority.MsgRemoveAuthorizationResponse"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): MsgRemoveAuthorizationResponse; + + static fromJson(jsonValue: JsonValue, options?: Partial): MsgRemoveAuthorizationResponse; + + static fromJsonString(jsonString: string, options?: Partial): MsgRemoveAuthorizationResponse; + + static equals(a: MsgRemoveAuthorizationResponse | PlainMessage | undefined, b: MsgRemoveAuthorizationResponse | PlainMessage | undefined): boolean; +} + /** * MsgUpdatePolicies defines the MsgUpdatePolicies service. * diff --git a/x/authority/client/cli/tx.go b/x/authority/client/cli/tx.go index 1455773bf5..8c1c1396db 100644 --- a/x/authority/client/cli/tx.go +++ b/x/authority/client/cli/tx.go @@ -20,8 +20,10 @@ func GetTxCmd() *cobra.Command { } cmd.AddCommand( - CmdUpdatePolices(), + CmdUpdatePolicies(), CmdUpdateChainInfo(), + CmdAddAuthorization(), + CmdRemoveAuthorization(), ) return cmd diff --git a/x/authority/client/cli/tx_add_authorization.go b/x/authority/client/cli/tx_add_authorization.go new file mode 100644 index 0000000000..5658d24e91 --- /dev/null +++ b/x/authority/client/cli/tx_add_authorization.go @@ -0,0 +1,61 @@ +package cli + +import ( + "fmt" + "strconv" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/spf13/cobra" + + "github.com/zeta-chain/zetacore/x/authority/types" +) + +func CmdAddAuthorization() *cobra.Command { + cmd := &cobra.Command{ + Use: "add-authorization [msg-url] [authorized-policy]", + Short: "Add a new authorization or update the policy of an existing authorization. Policy type can be 0 for groupEmergency, 1 for groupOperational, 2 for groupAdmin.", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) (err error) { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + authorizedPolicy, err := GetPolicyType(args[1]) + if err != nil { + return err + } + msg := &types.MsgAddAuthorization{ + MsgUrl: args[0], + AuthorizedPolicy: authorizedPolicy, + } + err = msg.ValidateBasic() + if err != nil { + return err + } + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + flags.AddTxFlagsToCmd(cmd) + return cmd +} + +func GetPolicyType(policyTypeString string) (types.PolicyType, error) { + policyType, err := strconv.ParseInt(policyTypeString, 10, 64) + if err != nil { + return types.PolicyType_groupEmpty, fmt.Errorf("failed to parse policy type: %w", err) + } + + switch policyType { + case 0: + return types.PolicyType_groupEmergency, nil + case 1: + return types.PolicyType_groupOperational, nil + case 2: + return types.PolicyType_groupAdmin, nil + default: + return types.PolicyType_groupEmpty, fmt.Errorf("invalid policy type value: %d", policyType) + } + +} diff --git a/x/authority/client/cli/tx_add_authorizations_test.go b/x/authority/client/cli/tx_add_authorizations_test.go new file mode 100644 index 0000000000..5c5042ee36 --- /dev/null +++ b/x/authority/client/cli/tx_add_authorizations_test.go @@ -0,0 +1,60 @@ +package cli_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/zetacore/x/authority/client/cli" + "github.com/zeta-chain/zetacore/x/authority/types" +) + +func Test_GetPolicyType(t *testing.T) { + tt := []struct { + name string + policyTypeString string + expectedPolicyType types.PolicyType + expecterErrorString string + }{ + { + name: "groupEmergency", + policyTypeString: "0", + expectedPolicyType: types.PolicyType_groupEmergency, + expecterErrorString: "", + }, + { + name: "groupOperational", + policyTypeString: "1", + expectedPolicyType: types.PolicyType_groupOperational, + expecterErrorString: "", + }, + { + name: "groupAdmin", + policyTypeString: "2", + expectedPolicyType: types.PolicyType_groupAdmin, + expecterErrorString: "", + }, + { + name: "groupEmpty", + policyTypeString: "3", + expectedPolicyType: types.PolicyType_groupEmpty, + expecterErrorString: "invalid policy type value", + }, + { + name: "string literal for policy type not accepted", + policyTypeString: "groupEmergency", + expectedPolicyType: types.PolicyType_groupEmpty, + expecterErrorString: "failed to parse policy type", + }, + } + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + policyType, err := cli.GetPolicyType(tc.policyTypeString) + require.Equal(t, tc.expectedPolicyType, policyType) + if tc.expectedPolicyType == types.PolicyType_groupEmpty { + require.ErrorContains(t, err, tc.expecterErrorString) + } + }) + } + +} diff --git a/x/authority/client/cli/tx_remove_authorization.go b/x/authority/client/cli/tx_remove_authorization.go new file mode 100644 index 0000000000..3fee09f12f --- /dev/null +++ b/x/authority/client/cli/tx_remove_authorization.go @@ -0,0 +1,34 @@ +package cli + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/spf13/cobra" + + "github.com/zeta-chain/zetacore/x/authority/types" +) + +func CmdRemoveAuthorization() *cobra.Command { + cmd := &cobra.Command{ + Use: "remove-authorization [msg-url]", + Short: "removes an existing authorization", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) (err error) { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + msg := &types.MsgRemoveAuthorization{ + MsgUrl: args[0], + } + err = msg.ValidateBasic() + if err != nil { + return err + } + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + flags.AddTxFlagsToCmd(cmd) + return cmd +} diff --git a/x/authority/client/cli/tx_update_policies.go b/x/authority/client/cli/tx_update_policies.go index 19bfe6ab4f..69b8cd71f0 100644 --- a/x/authority/client/cli/tx_update_policies.go +++ b/x/authority/client/cli/tx_update_policies.go @@ -14,10 +14,10 @@ import ( "github.com/zeta-chain/zetacore/x/authority/types" ) -func CmdUpdatePolices() *cobra.Command { +func CmdUpdatePolicies() *cobra.Command { cmd := &cobra.Command{ Use: "update-policies [policies-json-file]", - Short: "Update the policies", + Short: "Update policies to values provided in the JSON file.", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) (err error) { policies, err := ReadPoliciesFromFile(os.DirFS("."), args[0]) diff --git a/x/authority/keeper/msg_server_add_authorization.go b/x/authority/keeper/msg_server_add_authorization.go new file mode 100644 index 0000000000..252884cd3f --- /dev/null +++ b/x/authority/keeper/msg_server_add_authorization.go @@ -0,0 +1,41 @@ +package keeper + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/zeta-chain/zetacore/x/authority/types" +) + +// AddAuthorization defines a method to add an authorization.If the authorization already exists, it will be overwritten with the provided policy. +// This should be called by the admin policy account. +func (k msgServer) AddAuthorization( + goCtx context.Context, + msg *types.MsgAddAuthorization, +) (*types.MsgAddAuthorizationResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + if !k.IsAuthorized(ctx, msg.Creator, types.PolicyType_groupAdmin) { + return nil, errorsmod.Wrap( + types.ErrUnauthorized, + "AddAuthorization can only be executed by the admin policy account", + ) + } + + authorizationList, found := k.GetAuthorizationList(ctx) + if !found { + authorizationList = types.AuthorizationList{Authorizations: []types.Authorization{}} + } + authorizationList.SetAuthorization(types.Authorization{MsgUrl: msg.MsgUrl, AuthorizedPolicy: msg.AuthorizedPolicy}) + + // validate the authorization list after adding the authorization as a precautionary measure. + err := authorizationList.Validate() + if err != nil { + return nil, errorsmod.Wrap(err, "authorization list is invalid") + } + + k.SetAuthorizationList(ctx, authorizationList) + return &types.MsgAddAuthorizationResponse{}, nil +} diff --git a/x/authority/keeper/msg_server_add_authorization_test.go b/x/authority/keeper/msg_server_add_authorization_test.go new file mode 100644 index 0000000000..95c962951e --- /dev/null +++ b/x/authority/keeper/msg_server_add_authorization_test.go @@ -0,0 +1,219 @@ +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/authority/keeper" + "github.com/zeta-chain/zetacore/x/authority/types" +) + +func TestMsgServer_AddAuthorization(t *testing.T) { + const url = "/zetachain.zetacore.sample.ABC" + t.Run("successfully add authorization of type admin to existing authorization list", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + admin := keepertest.SetAdminPolicies(ctx, k) + k.SetAuthorizationList(ctx, types.DefaultAuthorizationsList()) + msgServer := keeper.NewMsgServerImpl(*k) + prevLen := len(types.DefaultAuthorizationsList().Authorizations) + + msg := &types.MsgAddAuthorization{ + Creator: admin, + MsgUrl: url, + AuthorizedPolicy: types.PolicyType_groupAdmin, + } + + _, err := msgServer.AddAuthorization(sdk.WrapSDKContext(ctx), msg) + require.NoError(t, err) + + authorizationList, found := k.GetAuthorizationList(ctx) + require.True(t, found) + policy, err := authorizationList.GetAuthorizedPolicy(url) + require.NoError(t, err) + require.Equal(t, types.PolicyType_groupAdmin, policy) + require.Equal(t, prevLen+1, len(authorizationList.Authorizations)) + }) + + t.Run("successfully add authorization of type operational to existing authorization list", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + admin := keepertest.SetAdminPolicies(ctx, k) + k.SetAuthorizationList(ctx, types.DefaultAuthorizationsList()) + prevLen := len(types.DefaultAuthorizationsList().Authorizations) + msgServer := keeper.NewMsgServerImpl(*k) + + msg := &types.MsgAddAuthorization{ + Creator: admin, + MsgUrl: url, + AuthorizedPolicy: types.PolicyType_groupOperational, + } + + _, err := msgServer.AddAuthorization(sdk.WrapSDKContext(ctx), msg) + require.NoError(t, err) + + authorizationList, found := k.GetAuthorizationList(ctx) + require.True(t, found) + policy, err := authorizationList.GetAuthorizedPolicy(url) + require.NoError(t, err) + require.Equal(t, types.PolicyType_groupOperational, policy) + require.Equal(t, prevLen+1, len(authorizationList.Authorizations)) + }) + + t.Run("successfully add authorization of type emergency to existing authorization list", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + admin := keepertest.SetAdminPolicies(ctx, k) + k.SetAuthorizationList(ctx, types.DefaultAuthorizationsList()) + prevLen := len(types.DefaultAuthorizationsList().Authorizations) + msgServer := keeper.NewMsgServerImpl(*k) + + msg := &types.MsgAddAuthorization{ + Creator: admin, + MsgUrl: url, + AuthorizedPolicy: types.PolicyType_groupEmergency, + } + + _, err := msgServer.AddAuthorization(sdk.WrapSDKContext(ctx), msg) + require.NoError(t, err) + + authorizationList, found := k.GetAuthorizationList(ctx) + require.True(t, found) + policy, err := authorizationList.GetAuthorizedPolicy(url) + require.NoError(t, err) + require.Equal(t, types.PolicyType_groupEmergency, policy) + require.Equal(t, prevLen+1, len(authorizationList.Authorizations)) + }) + + t.Run("successfully add authorization to empty authorization list", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + admin := keepertest.SetAdminPolicies(ctx, k) + k.SetAuthorizationList(ctx, types.AuthorizationList{}) + msgServer := keeper.NewMsgServerImpl(*k) + + msg := &types.MsgAddAuthorization{ + Creator: admin, + MsgUrl: url, + AuthorizedPolicy: types.PolicyType_groupAdmin, + } + + _, err := msgServer.AddAuthorization(sdk.WrapSDKContext(ctx), msg) + require.NoError(t, err) + + authorizationList, found := k.GetAuthorizationList(ctx) + require.True(t, found) + policy, err := authorizationList.GetAuthorizedPolicy(url) + require.NoError(t, err) + require.Equal(t, types.PolicyType_groupAdmin, policy) + require.Equal(t, 1, len(authorizationList.Authorizations)) + }) + + t.Run("successfully set authorization when list is not found ", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + admin := keepertest.SetAdminPolicies(ctx, k) + msgServer := keeper.NewMsgServerImpl(*k) + authorizationList, found := k.GetAuthorizationList(ctx) + require.False(t, found) + + msg := &types.MsgAddAuthorization{ + Creator: admin, + MsgUrl: url, + AuthorizedPolicy: types.PolicyType_groupAdmin, + } + + _, err := msgServer.AddAuthorization(sdk.WrapSDKContext(ctx), msg) + require.NoError(t, err) + + authorizationList, found = k.GetAuthorizationList(ctx) + require.True(t, found) + policy, err := authorizationList.GetAuthorizedPolicy(url) + require.NoError(t, err) + require.Equal(t, types.PolicyType_groupAdmin, policy) + require.Equal(t, 1, len(authorizationList.Authorizations)) + }) + + t.Run("update existing authorization", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + admin := keepertest.SetAdminPolicies(ctx, k) + authorizationList := types.AuthorizationList{Authorizations: []types.Authorization{ + { + MsgUrl: "/zetachain.zetacore.sample.ABC", + AuthorizedPolicy: types.PolicyType_groupOperational, + }, + }, + } + k.SetAuthorizationList(ctx, authorizationList) + prevLen := len(authorizationList.Authorizations) + + msgServer := keeper.NewMsgServerImpl(*k) + + msg := &types.MsgAddAuthorization{ + Creator: admin, + MsgUrl: url, + AuthorizedPolicy: types.PolicyType_groupAdmin, + } + + _, err := msgServer.AddAuthorization(sdk.WrapSDKContext(ctx), msg) + require.NoError(t, err) + + authorizationList, found := k.GetAuthorizationList(ctx) + require.True(t, found) + policy, err := authorizationList.GetAuthorizedPolicy(url) + require.NoError(t, err) + require.Equal(t, types.PolicyType_groupAdmin, policy) + require.Equal(t, prevLen, len(authorizationList.Authorizations)) + }) + + t.Run("fail to add authorization with invalid policy as creator", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + k.SetAuthorizationList(ctx, types.DefaultAuthorizationsList()) + prevLen := len(types.DefaultAuthorizationsList().Authorizations) + msgServer := keeper.NewMsgServerImpl(*k) + + msg := &types.MsgAddAuthorization{ + Creator: sample.AccAddress(), + MsgUrl: url, + AuthorizedPolicy: types.PolicyType_groupAdmin, + } + + _, err := msgServer.AddAuthorization(sdk.WrapSDKContext(ctx), msg) + require.ErrorIs(t, err, types.ErrUnauthorized) + + authorizationList, found := k.GetAuthorizationList(ctx) + require.True(t, found) + require.Equal(t, prevLen, len(authorizationList.Authorizations)) + }) + + // This scenario is not possible as the authorization list is always valid.But it is good to have in case the validation logic is changed in the future + t.Run("fail to set invalid authorization list", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + admin := keepertest.SetAdminPolicies(ctx, k) + authorizationList := types.AuthorizationList{Authorizations: []types.Authorization{ + { + MsgUrl: url, + AuthorizedPolicy: types.PolicyType_groupOperational, + }, + { + MsgUrl: url, + AuthorizedPolicy: types.PolicyType_groupOperational, + }, + }} + k.SetAuthorizationList(ctx, authorizationList) + prevLen := len(authorizationList.Authorizations) + msgServer := keeper.NewMsgServerImpl(*k) + + msg := &types.MsgAddAuthorization{ + Creator: admin, + MsgUrl: url, + AuthorizedPolicy: types.PolicyType_groupAdmin, + } + + _, err := msgServer.AddAuthorization(sdk.WrapSDKContext(ctx), msg) + require.ErrorIs(t, err, types.ErrInvalidAuthorizationList) + + authorizationList, found := k.GetAuthorizationList(ctx) + require.True(t, found) + require.Equal(t, prevLen, len(authorizationList.Authorizations)) + }) +} diff --git a/x/authority/keeper/msg_server_remove_authorization.go b/x/authority/keeper/msg_server_remove_authorization.go new file mode 100644 index 0000000000..f84e2fdffe --- /dev/null +++ b/x/authority/keeper/msg_server_remove_authorization.go @@ -0,0 +1,51 @@ +package keeper + +import ( + "context" + "fmt" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/zeta-chain/zetacore/x/authority/types" +) + +// RemoveAuthorization defines a method to remove an authorization. +// This should be called by the admin policy account. +func (k msgServer) RemoveAuthorization( + goCtx context.Context, + msg *types.MsgRemoveAuthorization, +) (*types.MsgRemoveAuthorizationResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + if !k.IsAuthorized(ctx, msg.Creator, types.PolicyType_groupAdmin) { + return nil, errorsmod.Wrap( + types.ErrUnauthorized, + "RemoveAuthorization can only be executed by the admin policy account", + ) + } + + // check if the authorization list exists, we can return early if there is no list. + authorizationList, found := k.GetAuthorizationList(ctx) + if !found { + return nil, types.ErrAuthorizationListNotFound + } + + // check if the authorization exists, we can return early if the authorization does not exist. + _, err := authorizationList.GetAuthorizedPolicy(msg.MsgUrl) + if err != nil { + return nil, errorsmod.Wrap(err, fmt.Sprintf("msg url %s", msg.MsgUrl)) + } + + // remove the authorization + authorizationList.RemoveAuthorization(msg.MsgUrl) + + // validate the authorization list after adding the authorization as a precautionary measure. + err = authorizationList.Validate() + if err != nil { + return nil, errorsmod.Wrap(err, "authorization list is invalid") + } + k.SetAuthorizationList(ctx, authorizationList) + + return &types.MsgRemoveAuthorizationResponse{}, nil +} diff --git a/x/authority/keeper/msg_server_remove_authorization_test.go b/x/authority/keeper/msg_server_remove_authorization_test.go new file mode 100644 index 0000000000..566369839a --- /dev/null +++ b/x/authority/keeper/msg_server_remove_authorization_test.go @@ -0,0 +1,204 @@ +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/authority/keeper" + "github.com/zeta-chain/zetacore/x/authority/types" +) + +func TestMsgServer_RemoveAuthorization(t *testing.T) { + t.Run("successfully remove operational policy authorization", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + admin := keepertest.SetAdminPolicies(ctx, k) + k.SetAuthorizationList(ctx, types.DefaultAuthorizationsList()) + prevLen := len(types.DefaultAuthorizationsList().Authorizations) + msgServer := keeper.NewMsgServerImpl(*k) + url := types.OperationPolicyMessages[0] + + msg := &types.MsgRemoveAuthorization{ + Creator: admin, + MsgUrl: url, + } + + authorizationList, found := k.GetAuthorizationList(ctx) + require.True(t, found) + _, err := authorizationList.GetAuthorizedPolicy(url) + require.NoError(t, err, types.ErrAuthorizationNotFound) + + _, err = msgServer.RemoveAuthorization(sdk.WrapSDKContext(ctx), msg) + require.NoError(t, err) + + authorizationList, found = k.GetAuthorizationList(ctx) + require.True(t, found) + _, err = authorizationList.GetAuthorizedPolicy(url) + require.ErrorIs(t, err, types.ErrAuthorizationNotFound) + require.Equal(t, prevLen-1, len(authorizationList.Authorizations)) + }) + + t.Run("successfully remove admin policy authorization", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + admin := keepertest.SetAdminPolicies(ctx, k) + k.SetAuthorizationList(ctx, types.DefaultAuthorizationsList()) + prevLen := len(types.DefaultAuthorizationsList().Authorizations) + msgServer := keeper.NewMsgServerImpl(*k) + url := types.AdminPolicyMessages[0] + + msg := &types.MsgRemoveAuthorization{ + Creator: admin, + MsgUrl: url, + } + + authorizationList, found := k.GetAuthorizationList(ctx) + require.True(t, found) + _, err := authorizationList.GetAuthorizedPolicy(url) + require.NoError(t, err, types.ErrAuthorizationNotFound) + + _, err = msgServer.RemoveAuthorization(sdk.WrapSDKContext(ctx), msg) + require.NoError(t, err) + + authorizationList, found = k.GetAuthorizationList(ctx) + require.True(t, found) + _, err = authorizationList.GetAuthorizedPolicy(url) + require.ErrorIs(t, err, types.ErrAuthorizationNotFound) + require.Equal(t, prevLen-1, len(authorizationList.Authorizations)) + }) + + t.Run("successfully remove emergency policy authorization", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + admin := keepertest.SetAdminPolicies(ctx, k) + k.SetAuthorizationList(ctx, types.DefaultAuthorizationsList()) + prevLen := len(types.DefaultAuthorizationsList().Authorizations) + msgServer := keeper.NewMsgServerImpl(*k) + url := types.EmergencyPolicyMessages[0] + + msg := &types.MsgRemoveAuthorization{ + Creator: admin, + MsgUrl: url, + } + + authorizationList, found := k.GetAuthorizationList(ctx) + require.True(t, found) + _, err := authorizationList.GetAuthorizedPolicy(url) + require.NoError(t, err, types.ErrAuthorizationNotFound) + + _, err = msgServer.RemoveAuthorization(sdk.WrapSDKContext(ctx), msg) + require.NoError(t, err) + + authorizationList, found = k.GetAuthorizationList(ctx) + require.True(t, found) + _, err = authorizationList.GetAuthorizedPolicy(url) + require.ErrorIs(t, err, types.ErrAuthorizationNotFound) + require.Equal(t, prevLen-1, len(authorizationList.Authorizations)) + }) + + t.Run("unable to remove authorization if creator is not the correct policy", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + k.SetAuthorizationList(ctx, types.DefaultAuthorizationsList()) + prevLen := len(types.DefaultAuthorizationsList().Authorizations) + msgServer := keeper.NewMsgServerImpl(*k) + url := types.OperationPolicyMessages[0] + + msg := &types.MsgRemoveAuthorization{ + Creator: sample.AccAddress(), + MsgUrl: url, + } + + authorizationList, found := k.GetAuthorizationList(ctx) + require.True(t, found) + _, err := authorizationList.GetAuthorizedPolicy(url) + require.NoError(t, err, types.ErrAuthorizationNotFound) + + _, err = msgServer.RemoveAuthorization(sdk.WrapSDKContext(ctx), msg) + require.ErrorIs(t, err, types.ErrUnauthorized) + + authorizationList, found = k.GetAuthorizationList(ctx) + require.True(t, found) + require.Equal(t, types.DefaultAuthorizationsList(), authorizationList) + require.Equal(t, prevLen, len(authorizationList.Authorizations)) + }) + + t.Run("unable to remove authorization if authorization list does not exist", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + admin := keepertest.SetAdminPolicies(ctx, k) + msgServer := keeper.NewMsgServerImpl(*k) + url := types.OperationPolicyMessages[0] + + msg := &types.MsgRemoveAuthorization{ + Creator: admin, + MsgUrl: url, + } + + _, err := msgServer.RemoveAuthorization(sdk.WrapSDKContext(ctx), msg) + require.ErrorIs(t, err, types.ErrAuthorizationListNotFound) + }) + + t.Run("unable to remove authorization if authorization does not exist", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + admin := keepertest.SetAdminPolicies(ctx, k) + authorizationList := types.AuthorizationList{Authorizations: []types.Authorization{ + { + MsgUrl: "ABC", + AuthorizedPolicy: types.PolicyType_groupOperational, + }, + }} + k.SetAuthorizationList(ctx, authorizationList) + msgServer := keeper.NewMsgServerImpl(*k) + url := "invalid" + + msg := &types.MsgRemoveAuthorization{ + Creator: admin, + MsgUrl: url, + } + + authorizationList, found := k.GetAuthorizationList(ctx) + require.True(t, found) + _, err := authorizationList.GetAuthorizedPolicy(url) + require.ErrorIs(t, err, types.ErrAuthorizationNotFound) + + _, err = msgServer.RemoveAuthorization(sdk.WrapSDKContext(ctx), msg) + require.ErrorIs(t, err, types.ErrAuthorizationNotFound) + + authorizationListNew, found := k.GetAuthorizationList(ctx) + require.True(t, found) + require.Equal(t, authorizationList, authorizationListNew) + }) + + t.Run("unable to remove authorization if authorization list is invalid", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + admin := keepertest.SetAdminPolicies(ctx, k) + authorizationList := types.AuthorizationList{Authorizations: []types.Authorization{ + { + MsgUrl: "ABC", + AuthorizedPolicy: types.PolicyType_groupOperational, + }, + { + MsgUrl: "ABC", + AuthorizedPolicy: types.PolicyType_groupOperational, + }, + { + MsgUrl: "ABC", + AuthorizedPolicy: types.PolicyType_groupOperational, + }, + }} + k.SetAuthorizationList(ctx, authorizationList) + msgServer := keeper.NewMsgServerImpl(*k) + + msg := &types.MsgRemoveAuthorization{ + Creator: admin, + MsgUrl: "ABC", + } + + _, err := msgServer.RemoveAuthorization(sdk.WrapSDKContext(ctx), msg) + require.ErrorIs(t, err, types.ErrInvalidAuthorizationList) + + authorizationListNew, found := k.GetAuthorizationList(ctx) + require.True(t, found) + require.Equal(t, authorizationList, authorizationListNew) + }) +} diff --git a/x/authority/types/authorizations.go b/x/authority/types/authorizations.go index 0de98a768e..848b1da1d2 100644 --- a/x/authority/types/authorizations.go +++ b/x/authority/types/authorizations.go @@ -34,6 +34,8 @@ var ( "/zetachain.zetacore.fungible.MsgUpdateContractBytecode", "/zetachain.zetacore.fungible.MsgUpdateSystemContract", "/zetachain.zetacore.observer.MsgUpdateObserver", + "/zetachain.zetacore.authority.MsgAddAuthorization", + "/zetachain.zetacore.authority.MsgRemoveAuthorization", } // EmergencyPolicyMessages keeps track of the message URLs that can, by default, only be executed by emergency policy address EmergencyPolicyMessages = []string{ @@ -93,25 +95,24 @@ func (a *AuthorizationList) SetAuthorization(authorization Authorization) { a.Authorizations = append(a.Authorizations, authorization) } -// RemoveAuthorization removes the authorization from the list. It does not check if the authorization exists or not. -func (a *AuthorizationList) RemoveAuthorization(authorization Authorization) { +// RemoveAuthorization removes the authorization from the list. It should be called by the admin policy account. +func (a *AuthorizationList) RemoveAuthorization(msgURL string) { for i, auth := range a.Authorizations { - if auth.MsgUrl == authorization.MsgUrl { + if auth.MsgUrl == msgURL { a.Authorizations = append(a.Authorizations[:i], a.Authorizations[i+1:]...) + return } } } -// GetAuthorizedPolicy returns the policy for the given message url. If the message url is not found, - +// GetAuthorizedPolicy returns the policy for the given message url. If the message url is not found, it returns an error. func (a *AuthorizationList) GetAuthorizedPolicy(msgURL string) (PolicyType, error) { for _, auth := range a.Authorizations { if auth.MsgUrl == msgURL { return auth.AuthorizedPolicy, nil } } - // Returning first value of enum, can consider adding a default value of `EmptyPolicy` in the enum. - return PolicyType(0), ErrAuthorizationNotFound + return PolicyType_groupEmpty, ErrAuthorizationNotFound } // Validate checks if the authorization list is valid. It returns an error if the message url is duplicated with different policies. diff --git a/x/authority/types/authorizations_test.go b/x/authority/types/authorizations_test.go index 8bb7ed805a..009e958c15 100644 --- a/x/authority/types/authorizations_test.go +++ b/x/authority/types/authorizations_test.go @@ -176,21 +176,21 @@ func TestAuthorizationList_GetAuthorizations(t *testing.T) { }, }}, getPolicyMsgUrl: "XYZ", - expectedPolicy: types.PolicyType(0), + expectedPolicy: types.PolicyType_groupEmpty, error: types.ErrAuthorizationNotFound, }, { name: "get authorizations fails when msg not found in list", authorizations: types.AuthorizationList{Authorizations: []types.Authorization{}}, getPolicyMsgUrl: "ABC", - expectedPolicy: types.PolicyType(0), + expectedPolicy: types.PolicyType_groupEmpty, error: types.ErrAuthorizationNotFound, }, { name: "get authorizations fails when when queried for empty string", authorizations: types.AuthorizationList{Authorizations: []types.Authorization{}}, getPolicyMsgUrl: "", - expectedPolicy: types.PolicyType(0), + expectedPolicy: types.PolicyType_groupEmpty, error: types.ErrAuthorizationNotFound, }, } @@ -275,10 +275,10 @@ func TestAuthorizationList_Validate(t *testing.T) { func TestAuthorizationList_RemoveAuthorizations(t *testing.T) { tt := []struct { - name string - oldList types.AuthorizationList - removeAuthorization types.Authorization - expectedList types.AuthorizationList + name string + oldList types.AuthorizationList + removeMsgUrl string + expectedList types.AuthorizationList }{ { name: "remove authorization successfully", @@ -292,9 +292,7 @@ func TestAuthorizationList_RemoveAuthorizations(t *testing.T) { AuthorizedPolicy: types.PolicyType_groupOperational, }, }}, - removeAuthorization: types.Authorization{ - MsgUrl: "ABC", - }, + removeMsgUrl: "ABC", expectedList: types.AuthorizationList{Authorizations: []types.Authorization{ { MsgUrl: "XYZ", @@ -318,9 +316,7 @@ func TestAuthorizationList_RemoveAuthorizations(t *testing.T) { AuthorizedPolicy: types.PolicyType_groupOperational, }, }}, - removeAuthorization: types.Authorization{ - MsgUrl: "XYZ", - }, + removeMsgUrl: "XYZ", expectedList: types.AuthorizationList{Authorizations: []types.Authorization{ { MsgUrl: "ABC", @@ -333,11 +329,9 @@ func TestAuthorizationList_RemoveAuthorizations(t *testing.T) { }}, }, { - name: "do not remove anything when trying to remove from an empty list", - oldList: types.AuthorizationList{Authorizations: []types.Authorization{}}, - removeAuthorization: types.Authorization{ - MsgUrl: "XYZ", - }, + name: "do not remove anything when trying to remove from an empty list", + oldList: types.AuthorizationList{Authorizations: []types.Authorization{}}, + removeMsgUrl: "XYZ", expectedList: types.AuthorizationList{Authorizations: []types.Authorization{}}, }, { @@ -348,9 +342,28 @@ func TestAuthorizationList_RemoveAuthorizations(t *testing.T) { AuthorizedPolicy: types.PolicyType_groupOperational, }, }}, - removeAuthorization: types.Authorization{ - MsgUrl: "XYZ", - }, + removeMsgUrl: "XYZ", + expectedList: types.AuthorizationList{Authorizations: []types.Authorization{ + { + MsgUrl: "ABC", + AuthorizedPolicy: types.PolicyType_groupOperational, + }, + }}, + }, + // The list is invalid, but this test case tries to assert the expected functionality + { + name: "return after removing first occurrence", + oldList: types.AuthorizationList{Authorizations: []types.Authorization{ + { + MsgUrl: "ABC", + AuthorizedPolicy: types.PolicyType_groupOperational, + }, + { + MsgUrl: "ABC", + AuthorizedPolicy: types.PolicyType_groupOperational, + }, + }}, + removeMsgUrl: "ABC", expectedList: types.AuthorizationList{Authorizations: []types.Authorization{ { MsgUrl: "ABC", @@ -361,7 +374,7 @@ func TestAuthorizationList_RemoveAuthorizations(t *testing.T) { } for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - tc.oldList.RemoveAuthorization(tc.removeAuthorization) + tc.oldList.RemoveAuthorization(tc.removeMsgUrl) require.Equal(t, tc.expectedList, tc.oldList) }) } @@ -407,6 +420,8 @@ func TestDefaultAuthorizationsList(t *testing.T) { sdk.MsgTypeURL(&fungibletypes.MsgUpdateContractBytecode{}), sdk.MsgTypeURL(&fungibletypes.MsgUpdateSystemContract{}), sdk.MsgTypeURL(&observertypes.MsgUpdateObserver{}), + sdk.MsgTypeURL(&types.MsgAddAuthorization{}), + sdk.MsgTypeURL(&types.MsgRemoveAuthorization{}), } defaultList := types.DefaultAuthorizationsList() for _, msgUrl := range OperationalPolicyMessageList { diff --git a/x/authority/types/errors.go b/x/authority/types/errors.go index 88b44b9887..9d9509e9d4 100644 --- a/x/authority/types/errors.go +++ b/x/authority/types/errors.go @@ -3,7 +3,8 @@ package types import errorsmod "cosmossdk.io/errors" var ( - ErrUnauthorized = errorsmod.Register(ModuleName, 1102, "sender not authorized") - ErrInvalidAuthorizationList = errorsmod.Register(ModuleName, 1103, "invalid authorization list") - ErrAuthorizationNotFound = errorsmod.Register(ModuleName, 1104, "authorization not found") + ErrUnauthorized = errorsmod.Register(ModuleName, 1102, "sender not authorized") + ErrInvalidAuthorizationList = errorsmod.Register(ModuleName, 1103, "invalid authorization list") + ErrAuthorizationNotFound = errorsmod.Register(ModuleName, 1104, "authorization not found") + ErrAuthorizationListNotFound = errorsmod.Register(ModuleName, 1105, "authorization list not found") ) diff --git a/x/authority/types/message_add_authorization.go b/x/authority/types/message_add_authorization.go new file mode 100644 index 0000000000..0de3162a78 --- /dev/null +++ b/x/authority/types/message_add_authorization.go @@ -0,0 +1,65 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +const TypeMsgAddAuthorization = "AddAuthorization" + +var _ sdk.Msg = &MsgAddAuthorization{} + +func NewMsgAddAuthorization(creator string, msgURL string, authorizedPolicy PolicyType) *MsgAddAuthorization { + return &MsgAddAuthorization{ + Creator: creator, + MsgUrl: msgURL, + AuthorizedPolicy: authorizedPolicy, + } +} + +func (msg *MsgAddAuthorization) Route() string { + return RouterKey +} + +func (msg *MsgAddAuthorization) Type() string { + return TypeMsgAddAuthorization +} + +func (msg *MsgAddAuthorization) GetSigners() []sdk.AccAddress { + signer, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + panic(err) + } + return []sdk.AccAddress{signer} +} + +func (msg *MsgAddAuthorization) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +func (msg *MsgAddAuthorization) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(msg.Creator); err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err) + } + + // the authorized policy must be valid + if err := msg.AuthorizedPolicy.Validate(); err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "invalid authorized policy: %s", err.Error()) + } + + // the message URL must be valid + if err := ValidateMsgURL(msg.MsgUrl); err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "invalid msg url: %s", err.Error()) + } + + return nil +} + +func ValidateMsgURL(url string) error { + if len(url) == 0 { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "message URL cannot be empty") + } + return nil +} diff --git a/x/authority/types/message_add_authorizations_test.go b/x/authority/types/message_add_authorizations_test.go new file mode 100644 index 0000000000..dcc970adbd --- /dev/null +++ b/x/authority/types/message_add_authorizations_test.go @@ -0,0 +1,130 @@ +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/testutil/sample" + "github.com/zeta-chain/zetacore/x/authority/types" +) + +func TestMsgAddAuthorization_ValidateBasic(t *testing.T) { + tests := []struct { + name string + msg *types.MsgAddAuthorization + expectErr require.ErrorAssertionFunc + }{ + { + name: "invalid creator address", + msg: types.NewMsgAddAuthorization("invalid", "url", types.PolicyType_groupAdmin), + expectErr: func(t require.TestingT, err error, msgAndArgs ...interface{}) { + require.ErrorIs(t, err, sdkerrors.ErrInvalidAddress) + require.ErrorContains(t, err, "invalid creator address") + }, + }, + { + name: "invalid authorized policy", + msg: types.NewMsgAddAuthorization(sample.AccAddress(), "url", types.PolicyType_groupEmpty), + expectErr: func(t require.TestingT, err error, msgAndArgs ...interface{}) { + require.ErrorIs(t, err, sdkerrors.ErrInvalidRequest) + require.ErrorContains(t, err, "invalid authorized policy") + }, + }, + { + name: "invalid msg url", + msg: types.NewMsgAddAuthorization(sample.AccAddress(), "", types.PolicyType_groupAdmin), + expectErr: func(t require.TestingT, err error, msgAndArgs ...interface{}) { + require.ErrorIs(t, err, sdkerrors.ErrInvalidRequest) + require.ErrorContains(t, err, "invalid msg url") + }, + }, + { + name: "valid message", + msg: types.NewMsgAddAuthorization(sample.AccAddress(), "url", types.PolicyType_groupAdmin), + expectErr: require.NoError, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.expectErr(t, tt.msg.ValidateBasic()) + }) + } +} + +func TestMsgAddAuthorization_GetSigners(t *testing.T) { + signer := sample.AccAddress() + tests := []struct { + name string + msg *types.MsgAddAuthorization + panics bool + }{ + { + name: "valid signer", + msg: types.NewMsgAddAuthorization(signer, "url", types.PolicyType_groupAdmin), + panics: false, + }, + { + name: "invalid signer", + msg: types.NewMsgAddAuthorization("creator", "url", types.PolicyType_groupAdmin), + 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 TestMsgAddAuthorization_Type(t *testing.T) { + msg := types.NewMsgAddAuthorization(sample.AccAddress(), "url", types.PolicyType_groupAdmin) + require.Equal(t, types.TypeMsgAddAuthorization, msg.Type()) +} + +func TestMsgAddAuthorization_Route(t *testing.T) { + msg := types.NewMsgAddAuthorization(sample.AccAddress(), "url", types.PolicyType_groupAdmin) + require.Equal(t, types.RouterKey, msg.Route()) +} + +func TestMsgAddAuthorization_GetSignBytes(t *testing.T) { + msg := types.NewMsgAddAuthorization(sample.AccAddress(), "url", types.PolicyType_groupAdmin) + require.NotPanics(t, func() { + msg.GetSignBytes() + }) +} + +func TestValidateMsgUrl(t *testing.T) { + tests := []struct { + name string + url string + expectErr error + }{ + { + name: "empty url", + url: "", + expectErr: sdkerrors.ErrInvalidRequest, + }, + { + name: "valid url", + url: "/zetachain.zetacore.crosschain.MsgRefundAbortedCCTX", + expectErr: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := types.ValidateMsgURL(tt.url) + require.ErrorIs(t, err, tt.expectErr) + }) + } +} diff --git a/x/authority/types/message_remove_authorization.go b/x/authority/types/message_remove_authorization.go new file mode 100644 index 0000000000..2404282088 --- /dev/null +++ b/x/authority/types/message_remove_authorization.go @@ -0,0 +1,51 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +const TypeRemoveAuthorization = "RemoveAuthorization" + +var _ sdk.Msg = &MsgRemoveAuthorization{} + +func NewMsgRemoveAuthorization(creator string, msgURL string) *MsgRemoveAuthorization { + return &MsgRemoveAuthorization{ + Creator: creator, + MsgUrl: msgURL, + } +} + +func (msg *MsgRemoveAuthorization) Route() string { + return RouterKey +} + +func (msg *MsgRemoveAuthorization) Type() string { + return TypeRemoveAuthorization +} + +func (msg *MsgRemoveAuthorization) GetSigners() []sdk.AccAddress { + signer, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + panic(err) + } + return []sdk.AccAddress{signer} +} + +func (msg *MsgRemoveAuthorization) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +func (msg *MsgRemoveAuthorization) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(msg.Creator); err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err) + } + + if err := ValidateMsgURL(msg.MsgUrl); err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "invalid msg url: %s", err.Error()) + } + + return nil +} diff --git a/x/authority/types/message_remove_authorizations_test.go b/x/authority/types/message_remove_authorizations_test.go new file mode 100644 index 0000000000..d9e35e45e0 --- /dev/null +++ b/x/authority/types/message_remove_authorizations_test.go @@ -0,0 +1,97 @@ +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/testutil/sample" + "github.com/zeta-chain/zetacore/x/authority/types" +) + +func TestMsgRemoveAuthorization_ValidateBasic(t *testing.T) { + tests := []struct { + name string + msg *types.MsgRemoveAuthorization + expectErr require.ErrorAssertionFunc + }{ + { + name: "invalid creator address", + msg: types.NewMsgRemoveAuthorization("invalid", "url"), + expectErr: func(t require.TestingT, err error, msgAndArgs ...interface{}) { + require.ErrorIs(t, err, sdkerrors.ErrInvalidAddress) + require.ErrorContains(t, err, "invalid creator address") + }, + }, + { + name: "invalid msg url", + msg: types.NewMsgRemoveAuthorization(sample.AccAddress(), ""), + expectErr: func(t require.TestingT, err error, msgAndArgs ...interface{}) { + require.ErrorIs(t, err, sdkerrors.ErrInvalidRequest) + require.ErrorContains(t, err, "invalid msg url") + }, + }, + { + name: "valid message", + msg: types.NewMsgRemoveAuthorization(sample.AccAddress(), "url"), + expectErr: require.NoError, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.expectErr(t, tt.msg.ValidateBasic()) + }) + } +} + +func TestMsgRemoveAuthorization_GetSigners(t *testing.T) { + signer := sample.AccAddress() + tests := []struct { + name string + msg *types.MsgRemoveAuthorization + panics bool + }{ + { + name: "valid signer", + msg: types.NewMsgRemoveAuthorization(signer, "url"), + panics: false, + }, + { + name: "invalid signer", + msg: types.NewMsgRemoveAuthorization("creator", "url"), + 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 TestMsgRemoveAuthorization_Type(t *testing.T) { + msg := types.NewMsgRemoveAuthorization(sample.AccAddress(), "url") + require.Equal(t, types.TypeRemoveAuthorization, msg.Type()) +} + +func TestMsgRemoveAuthorization_Route(t *testing.T) { + msg := types.NewMsgRemoveAuthorization(sample.AccAddress(), "url") + require.Equal(t, types.RouterKey, msg.Route()) +} + +func TestMsgRemoveAuthorization_GetSignBytes(t *testing.T) { + msg := types.NewMsgRemoveAuthorization(sample.AccAddress(), "url") + require.NotPanics(t, func() { + msg.GetSignBytes() + }) +} diff --git a/x/authority/types/policies.go b/x/authority/types/policies.go index 28a97594ce..af3642bfd7 100644 --- a/x/authority/types/policies.go +++ b/x/authority/types/policies.go @@ -42,9 +42,8 @@ func (p Policies) Validate() error { return fmt.Errorf("invalid address: %s", err) } - if policy.PolicyType != PolicyType_groupEmergency && policy.PolicyType != PolicyType_groupAdmin && - policy.PolicyType != PolicyType_groupOperational { - return fmt.Errorf("invalid policy type: %s", policy.PolicyType) + if err := policy.PolicyType.Validate(); err != nil { + return err } if policyTypeMap[policy.PolicyType] { diff --git a/x/authority/types/policies.pb.go b/x/authority/types/policies.pb.go index 3b2eda6564..c67621c996 100644 --- a/x/authority/types/policies.pb.go +++ b/x/authority/types/policies.pb.go @@ -31,18 +31,22 @@ const ( PolicyType_groupOperational PolicyType = 1 // non-sensitive protocol parameters PolicyType_groupAdmin PolicyType = 2 + // protocol parameters or moving funds + PolicyType_groupEmpty PolicyType = 3 ) var PolicyType_name = map[int32]string{ 0: "groupEmergency", 1: "groupOperational", 2: "groupAdmin", + 3: "groupEmpty", } var PolicyType_value = map[string]int32{ "groupEmergency": 0, "groupOperational": 1, "groupAdmin": 2, + "groupEmpty": 3, } func (x PolicyType) String() string { @@ -161,26 +165,27 @@ func init() { } var fileDescriptor_afa9e3e7b996ef74 = []byte{ - // 303 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0xae, 0x4a, 0x2d, 0x49, - 0x4c, 0xce, 0x48, 0xcc, 0xcc, 0xd3, 0x07, 0xb3, 0xf2, 0x8b, 0x52, 0xf5, 0x13, 0x4b, 0x4b, 0x32, - 0xf2, 0x8b, 0x32, 0x4b, 0x2a, 0xf5, 0x0b, 0xf2, 0x73, 0x32, 0x93, 0x33, 0x53, 0x8b, 0xf5, 0x0a, - 0x8a, 0xf2, 0x4b, 0xf2, 0x85, 0x64, 0xe0, 0x8a, 0xf5, 0x60, 0x8a, 0xf5, 0xe0, 0x8a, 0xa5, 0x44, - 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x0a, 0xf5, 0x41, 0x2c, 0x88, 0x1e, 0xa5, 0x5c, 0x2e, 0xb6, 0x00, - 0x90, 0x29, 0x95, 0x42, 0x9e, 0x5c, 0xdc, 0x60, 0xf3, 0x2a, 0xe3, 0x4b, 0x2a, 0x0b, 0x52, 0x25, - 0x18, 0x15, 0x18, 0x35, 0xf8, 0x8c, 0x34, 0xf4, 0xf0, 0x99, 0xa9, 0x07, 0xd1, 0x1a, 0x52, 0x59, - 0x90, 0x1a, 0xc4, 0x55, 0x00, 0x67, 0x0b, 0x49, 0x70, 0xb1, 0x27, 0xa6, 0xa4, 0x14, 0xa5, 0x16, - 0x17, 0x4b, 0x30, 0x29, 0x30, 0x6a, 0x70, 0x06, 0xc1, 0xb8, 0x4a, 0x6e, 0x5c, 0x1c, 0x01, 0x50, - 0x47, 0x0b, 0x59, 0x71, 0xb1, 0x66, 0x96, 0xa4, 0xe6, 0x16, 0x4b, 0x30, 0x2a, 0x30, 0x6b, 0x70, - 0x1b, 0xa9, 0x10, 0x63, 0x55, 0x10, 0x44, 0x8b, 0x96, 0x0f, 0x17, 0x17, 0xc2, 0x6e, 0x21, 0x21, - 0x2e, 0xbe, 0xf4, 0xa2, 0xfc, 0xd2, 0x02, 0xd7, 0xdc, 0xd4, 0xa2, 0xf4, 0xd4, 0xbc, 0xe4, 0x4a, - 0x01, 0x06, 0x21, 0x11, 0x2e, 0x01, 0xb0, 0x98, 0x7f, 0x41, 0x6a, 0x51, 0x62, 0x49, 0x66, 0x7e, - 0x5e, 0x62, 0x8e, 0x00, 0xa3, 0x10, 0x1f, 0x17, 0x17, 0x58, 0xd4, 0x31, 0x25, 0x37, 0x33, 0x4f, - 0x80, 0x49, 0x8a, 0x65, 0xc5, 0x12, 0x39, 0x46, 0x27, 0xaf, 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, 0x48, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0x05, - 0x47, 0x80, 0x2e, 0x5a, 0x5c, 0x54, 0x20, 0xc5, 0x06, 0x28, 0xd8, 0x8a, 0x93, 0xd8, 0xc0, 0xe1, - 0x6a, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xe4, 0x91, 0xe5, 0xab, 0xba, 0x01, 0x00, 0x00, + // 310 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x51, 0xc1, 0x4a, 0x03, 0x31, + 0x14, 0xdc, 0xb4, 0x5a, 0xf5, 0x15, 0xca, 0x12, 0x7a, 0x58, 0x8a, 0x84, 0x52, 0x3c, 0x2c, 0x8a, + 0x59, 0xa9, 0x37, 0x6f, 0x0a, 0x15, 0xf4, 0x62, 0x29, 0x9e, 0x44, 0x90, 0x74, 0x1b, 0xb6, 0x81, + 0x66, 0x13, 0xb2, 0x29, 0x18, 0xbf, 0xc2, 0x8f, 0xf0, 0xe0, 0xa7, 0x78, 0xec, 0xd1, 0xa3, 0xb4, + 0x3f, 0x22, 0xcd, 0xda, 0x55, 0x3c, 0x88, 0xb7, 0x79, 0x8f, 0x99, 0x79, 0x8f, 0x19, 0x38, 0x7a, + 0xe2, 0x96, 0xa5, 0x53, 0x26, 0xf2, 0xc4, 0x23, 0x65, 0x78, 0xc2, 0xe6, 0x76, 0xaa, 0x8c, 0xb0, + 0x2e, 0xd1, 0x6a, 0x26, 0x52, 0xc1, 0x0b, 0xaa, 0x8d, 0xb2, 0x0a, 0xef, 0x57, 0x64, 0xba, 0x21, + 0xd3, 0x8a, 0xdc, 0x69, 0x67, 0x2a, 0x53, 0x9e, 0x98, 0xac, 0x51, 0xa9, 0xe9, 0x49, 0x68, 0x0c, + 0xd7, 0x2e, 0x0e, 0x5f, 0x41, 0xd3, 0xfb, 0xb9, 0x07, 0xeb, 0x34, 0x8f, 0x50, 0x17, 0xc5, 0xad, + 0x7e, 0x4c, 0xff, 0xf2, 0xa4, 0xa5, 0xf4, 0xd6, 0x69, 0x3e, 0x02, 0x5d, 0x61, 0x1c, 0xc1, 0x0e, + 0x9b, 0x4c, 0x0c, 0x2f, 0x8a, 0xa8, 0xd6, 0x45, 0xf1, 0xde, 0x68, 0x33, 0xf6, 0x2e, 0x61, 0x77, + 0xf8, 0xf5, 0x34, 0x3e, 0x83, 0x6d, 0x61, 0xb9, 0x2c, 0x22, 0xd4, 0xad, 0xc7, 0xcd, 0xfe, 0xc1, + 0x7f, 0x4e, 0x8d, 0x4a, 0xc9, 0xe1, 0x3d, 0xc0, 0xf7, 0x6d, 0x8c, 0xa1, 0x95, 0x19, 0x35, 0xd7, + 0x03, 0xc9, 0x4d, 0xc6, 0xf3, 0xd4, 0x85, 0x01, 0x6e, 0x43, 0xe8, 0x77, 0x37, 0x9a, 0x1b, 0x66, + 0x85, 0xca, 0xd9, 0x2c, 0x44, 0xb8, 0x05, 0xe0, 0xb7, 0xe7, 0x13, 0x29, 0xf2, 0xb0, 0x56, 0xcd, + 0x03, 0xa9, 0xad, 0x0b, 0xeb, 0x9d, 0xad, 0xd7, 0x17, 0x82, 0x2e, 0xae, 0xdf, 0x96, 0x04, 0x2d, + 0x96, 0x04, 0x7d, 0x2c, 0x09, 0x7a, 0x5e, 0x91, 0x60, 0xb1, 0x22, 0xc1, 0xfb, 0x8a, 0x04, 0x77, + 0x27, 0x99, 0xb0, 0xd3, 0xf9, 0x98, 0xa6, 0x4a, 0xfa, 0x42, 0x8e, 0x7f, 0x75, 0xf3, 0xf8, 0xa3, + 0x9d, 0x75, 0x8c, 0xc5, 0xb8, 0xe1, 0x73, 0x3e, 0xfd, 0x0c, 0x00, 0x00, 0xff, 0xff, 0x37, 0x5d, + 0x49, 0x63, 0xca, 0x01, 0x00, 0x00, } func (m *Policy) Marshal() (dAtA []byte, err error) { diff --git a/x/authority/types/policy_type.go b/x/authority/types/policy_type.go new file mode 100644 index 0000000000..c3593e7fae --- /dev/null +++ b/x/authority/types/policy_type.go @@ -0,0 +1,13 @@ +package types + +import "fmt" + +// Validate PolicyType validates the policy type. +// Valid policy types are groupEmergency, groupOperational, and groupAdmin. +func (p PolicyType) Validate() error { + if p != PolicyType_groupEmergency && p != PolicyType_groupAdmin && + p != PolicyType_groupOperational { + return fmt.Errorf("invalid policy type: %s", p) + } + return nil +} diff --git a/x/authority/types/policy_type_test.go b/x/authority/types/policy_type_test.go new file mode 100644 index 0000000000..c6778c9200 --- /dev/null +++ b/x/authority/types/policy_type_test.go @@ -0,0 +1,58 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/zetacore/x/authority/types" +) + +func TestPolicyType_Validate(t *testing.T) { + tests := []struct { + name string + policyType types.PolicyType + wantErr bool + }{ + { + name: "valid groupEmergency", + policyType: types.PolicyType_groupEmergency, + wantErr: false, + }, + { + name: "valid groupOperational", + policyType: types.PolicyType_groupOperational, + wantErr: false, + }, + { + name: "valid groupAdmin", + policyType: types.PolicyType_groupAdmin, + wantErr: false, + }, + { + name: "invalid policy type", + policyType: types.PolicyType(20), + wantErr: true, + }, + { + name: "invalid policy type more than max length", + policyType: types.PolicyType(len(types.PolicyType_name) + 1), + wantErr: true, + }, + { + name: "empty policy type", + policyType: types.PolicyType_groupEmpty, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.policyType.Validate() + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/x/authority/types/tx.pb.go b/x/authority/types/tx.pb.go index ac5c04416b..928353417f 100644 --- a/x/authority/types/tx.pb.go +++ b/x/authority/types/tx.pb.go @@ -28,6 +28,198 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +// MsgAddAuthorization defines the MsgAddAuthorization service. +// Adds an authorization to the chain. If the authorization already exists, it +// will be updated. +type MsgAddAuthorization struct { + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + MsgUrl string `protobuf:"bytes,2,opt,name=msg_url,json=msgUrl,proto3" json:"msg_url,omitempty"` + AuthorizedPolicy PolicyType `protobuf:"varint,3,opt,name=authorized_policy,json=authorizedPolicy,proto3,enum=zetachain.zetacore.authority.PolicyType" json:"authorized_policy,omitempty"` +} + +func (m *MsgAddAuthorization) Reset() { *m = MsgAddAuthorization{} } +func (m *MsgAddAuthorization) String() string { return proto.CompactTextString(m) } +func (*MsgAddAuthorization) ProtoMessage() {} +func (*MsgAddAuthorization) Descriptor() ([]byte, []int) { + return fileDescriptor_42e081863c477116, []int{0} +} +func (m *MsgAddAuthorization) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgAddAuthorization) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgAddAuthorization.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 *MsgAddAuthorization) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgAddAuthorization.Merge(m, src) +} +func (m *MsgAddAuthorization) XXX_Size() int { + return m.Size() +} +func (m *MsgAddAuthorization) XXX_DiscardUnknown() { + xxx_messageInfo_MsgAddAuthorization.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgAddAuthorization proto.InternalMessageInfo + +func (m *MsgAddAuthorization) GetCreator() string { + if m != nil { + return m.Creator + } + return "" +} + +func (m *MsgAddAuthorization) GetMsgUrl() string { + if m != nil { + return m.MsgUrl + } + return "" +} + +func (m *MsgAddAuthorization) GetAuthorizedPolicy() PolicyType { + if m != nil { + return m.AuthorizedPolicy + } + return PolicyType_groupEmergency +} + +// MsgAddAuthorizationResponse defines the MsgAddAuthorizationResponse service. +type MsgAddAuthorizationResponse struct { +} + +func (m *MsgAddAuthorizationResponse) Reset() { *m = MsgAddAuthorizationResponse{} } +func (m *MsgAddAuthorizationResponse) String() string { return proto.CompactTextString(m) } +func (*MsgAddAuthorizationResponse) ProtoMessage() {} +func (*MsgAddAuthorizationResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_42e081863c477116, []int{1} +} +func (m *MsgAddAuthorizationResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgAddAuthorizationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgAddAuthorizationResponse.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 *MsgAddAuthorizationResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgAddAuthorizationResponse.Merge(m, src) +} +func (m *MsgAddAuthorizationResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgAddAuthorizationResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgAddAuthorizationResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgAddAuthorizationResponse proto.InternalMessageInfo + +// MsgRemoveAuthorization defines the MsgRemoveAuthorization service. +// Removes an authorization from the chain. +type MsgRemoveAuthorization struct { + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + MsgUrl string `protobuf:"bytes,2,opt,name=msg_url,json=msgUrl,proto3" json:"msg_url,omitempty"` +} + +func (m *MsgRemoveAuthorization) Reset() { *m = MsgRemoveAuthorization{} } +func (m *MsgRemoveAuthorization) String() string { return proto.CompactTextString(m) } +func (*MsgRemoveAuthorization) ProtoMessage() {} +func (*MsgRemoveAuthorization) Descriptor() ([]byte, []int) { + return fileDescriptor_42e081863c477116, []int{2} +} +func (m *MsgRemoveAuthorization) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRemoveAuthorization) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRemoveAuthorization.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 *MsgRemoveAuthorization) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRemoveAuthorization.Merge(m, src) +} +func (m *MsgRemoveAuthorization) XXX_Size() int { + return m.Size() +} +func (m *MsgRemoveAuthorization) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRemoveAuthorization.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRemoveAuthorization proto.InternalMessageInfo + +func (m *MsgRemoveAuthorization) GetCreator() string { + if m != nil { + return m.Creator + } + return "" +} + +func (m *MsgRemoveAuthorization) GetMsgUrl() string { + if m != nil { + return m.MsgUrl + } + return "" +} + +// MsgRemoveAuthorizationResponse defines the MsgRemoveAuthorizationResponse +// service. +type MsgRemoveAuthorizationResponse struct { +} + +func (m *MsgRemoveAuthorizationResponse) Reset() { *m = MsgRemoveAuthorizationResponse{} } +func (m *MsgRemoveAuthorizationResponse) String() string { return proto.CompactTextString(m) } +func (*MsgRemoveAuthorizationResponse) ProtoMessage() {} +func (*MsgRemoveAuthorizationResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_42e081863c477116, []int{3} +} +func (m *MsgRemoveAuthorizationResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgRemoveAuthorizationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgRemoveAuthorizationResponse.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 *MsgRemoveAuthorizationResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgRemoveAuthorizationResponse.Merge(m, src) +} +func (m *MsgRemoveAuthorizationResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgRemoveAuthorizationResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgRemoveAuthorizationResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgRemoveAuthorizationResponse proto.InternalMessageInfo + // MsgUpdatePolicies defines the MsgUpdatePolicies service. type MsgUpdatePolicies struct { Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` @@ -38,7 +230,7 @@ func (m *MsgUpdatePolicies) Reset() { *m = MsgUpdatePolicies{} } func (m *MsgUpdatePolicies) String() string { return proto.CompactTextString(m) } func (*MsgUpdatePolicies) ProtoMessage() {} func (*MsgUpdatePolicies) Descriptor() ([]byte, []int) { - return fileDescriptor_42e081863c477116, []int{0} + return fileDescriptor_42e081863c477116, []int{4} } func (m *MsgUpdatePolicies) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -89,7 +281,7 @@ func (m *MsgUpdatePoliciesResponse) Reset() { *m = MsgUpdatePoliciesResp func (m *MsgUpdatePoliciesResponse) String() string { return proto.CompactTextString(m) } func (*MsgUpdatePoliciesResponse) ProtoMessage() {} func (*MsgUpdatePoliciesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_42e081863c477116, []int{1} + return fileDescriptor_42e081863c477116, []int{5} } func (m *MsgUpdatePoliciesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -128,7 +320,7 @@ func (m *MsgUpdateChainInfo) Reset() { *m = MsgUpdateChainInfo{} } func (m *MsgUpdateChainInfo) String() string { return proto.CompactTextString(m) } func (*MsgUpdateChainInfo) ProtoMessage() {} func (*MsgUpdateChainInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_42e081863c477116, []int{2} + return fileDescriptor_42e081863c477116, []int{6} } func (m *MsgUpdateChainInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -179,7 +371,7 @@ func (m *MsgUpdateChainInfoResponse) Reset() { *m = MsgUpdateChainInfoRe func (m *MsgUpdateChainInfoResponse) String() string { return proto.CompactTextString(m) } func (*MsgUpdateChainInfoResponse) ProtoMessage() {} func (*MsgUpdateChainInfoResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_42e081863c477116, []int{3} + return fileDescriptor_42e081863c477116, []int{7} } func (m *MsgUpdateChainInfoResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -209,6 +401,10 @@ func (m *MsgUpdateChainInfoResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgUpdateChainInfoResponse proto.InternalMessageInfo func init() { + proto.RegisterType((*MsgAddAuthorization)(nil), "zetachain.zetacore.authority.MsgAddAuthorization") + proto.RegisterType((*MsgAddAuthorizationResponse)(nil), "zetachain.zetacore.authority.MsgAddAuthorizationResponse") + proto.RegisterType((*MsgRemoveAuthorization)(nil), "zetachain.zetacore.authority.MsgRemoveAuthorization") + proto.RegisterType((*MsgRemoveAuthorizationResponse)(nil), "zetachain.zetacore.authority.MsgRemoveAuthorizationResponse") proto.RegisterType((*MsgUpdatePolicies)(nil), "zetachain.zetacore.authority.MsgUpdatePolicies") proto.RegisterType((*MsgUpdatePoliciesResponse)(nil), "zetachain.zetacore.authority.MsgUpdatePoliciesResponse") proto.RegisterType((*MsgUpdateChainInfo)(nil), "zetachain.zetacore.authority.MsgUpdateChainInfo") @@ -220,29 +416,38 @@ func init() { } var fileDescriptor_42e081863c477116 = []byte{ - // 350 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xad, 0x4a, 0x2d, 0x49, - 0x4c, 0xce, 0x48, 0xcc, 0xcc, 0xd3, 0x07, 0xb3, 0xf2, 0x8b, 0x52, 0xf5, 0x13, 0x4b, 0x4b, 0x32, - 0xf2, 0x8b, 0x32, 0x4b, 0x2a, 0xf5, 0x4b, 0x2a, 0xf4, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85, 0x64, - 0xe0, 0xca, 0xf4, 0x60, 0xca, 0xf4, 0xe0, 0xca, 0xa4, 0xb4, 0xf1, 0x1a, 0x52, 0x90, 0x9f, 0x93, - 0x99, 0x9c, 0x99, 0x5a, 0x0c, 0x31, 0x4a, 0x4a, 0x17, 0xaf, 0x62, 0xb0, 0x44, 0x7c, 0x66, 0x5e, - 0x5a, 0x3e, 0x54, 0xb9, 0x01, 0x5e, 0xe5, 0x50, 0x56, 0x55, 0x62, 0x49, 0x66, 0x7e, 0x1e, 0x54, - 0x87, 0x48, 0x7a, 0x7e, 0x7a, 0x3e, 0x98, 0xa9, 0x0f, 0x62, 0x41, 0x44, 0x95, 0xca, 0xb9, 0x04, - 0x7d, 0x8b, 0xd3, 0x43, 0x0b, 0x52, 0x12, 0x4b, 0x52, 0x03, 0xa0, 0x2e, 0x12, 0x92, 0xe0, 0x62, - 0x4f, 0x2e, 0x4a, 0x4d, 0x2c, 0xc9, 0x2f, 0x92, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x82, 0x71, - 0x85, 0x3c, 0xb8, 0x38, 0x60, 0xee, 0x96, 0x60, 0x52, 0x60, 0xd4, 0xe0, 0x36, 0x52, 0xd3, 0xc3, - 0x17, 0x06, 0x7a, 0x30, 0x33, 0x9d, 0x58, 0x4e, 0xdc, 0x93, 0x67, 0x08, 0x82, 0xeb, 0x56, 0x92, - 0xe6, 0x92, 0xc4, 0xb0, 0x38, 0x28, 0xb5, 0xb8, 0x20, 0x3f, 0xaf, 0x38, 0x55, 0xa9, 0x86, 0x4b, - 0x08, 0x2e, 0xe9, 0x0c, 0x32, 0xda, 0x33, 0x2f, 0x2d, 0x1f, 0x8f, 0xb3, 0x7c, 0xb8, 0xb8, 0x10, - 0x21, 0x04, 0x75, 0x98, 0x3a, 0x7e, 0x87, 0xc1, 0x8d, 0x85, 0xba, 0x8c, 0x33, 0x19, 0x26, 0xa0, - 0x24, 0xc3, 0x25, 0x85, 0x69, 0x3b, 0xcc, 0x6d, 0x46, 0x0d, 0x4c, 0x5c, 0xcc, 0xbe, 0xc5, 0xe9, - 0x42, 0x55, 0x5c, 0x7c, 0x68, 0xc1, 0xa6, 0x8f, 0xdf, 0x46, 0x0c, 0xef, 0x4a, 0x99, 0x93, 0xa8, - 0x01, 0xe6, 0x06, 0xa1, 0x5a, 0x2e, 0x7e, 0xf4, 0xc0, 0x31, 0x20, 0xd2, 0x2c, 0xb8, 0x0e, 0x29, - 0x0b, 0x52, 0x75, 0xc0, 0xac, 0x77, 0xf2, 0x3a, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, - 0x07, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, 0xf0, 0x58, 0x8e, 0xe1, 0xc6, 0x63, 0x39, - 0x86, 0x28, 0x83, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0x70, 0xba, 0xd4, - 0x45, 0x4b, 0xa2, 0x15, 0xc8, 0xb9, 0xa8, 0xb2, 0x20, 0xb5, 0x38, 0x89, 0x0d, 0x9c, 0x0e, 0x8d, - 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xe8, 0xeb, 0xa6, 0x40, 0x72, 0x03, 0x00, 0x00, + // 487 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0x4f, 0x6f, 0xd3, 0x30, + 0x14, 0xaf, 0xd9, 0xb4, 0xb1, 0x87, 0x34, 0xb6, 0x0c, 0x41, 0xc9, 0x46, 0xa8, 0x22, 0x01, 0x95, + 0xd0, 0x92, 0x52, 0x90, 0x00, 0x89, 0xcb, 0xc6, 0x85, 0x7f, 0x95, 0xa6, 0x88, 0x5e, 0xb8, 0x54, + 0x59, 0xea, 0xb9, 0x96, 0xda, 0x38, 0x8a, 0x5d, 0x58, 0x2b, 0x90, 0xb8, 0x72, 0x82, 0x6f, 0xc0, + 0xd7, 0xd9, 0x71, 0x47, 0x4e, 0x08, 0xb5, 0x5f, 0x04, 0xcd, 0x8d, 0xbd, 0x29, 0x89, 0xbc, 0x16, + 0x6e, 0x2f, 0xf6, 0xef, 0x9f, 0x9f, 0xe3, 0x07, 0xf7, 0xc6, 0x58, 0x84, 0x51, 0x2f, 0xa4, 0xb1, + 0x2f, 0x2b, 0x96, 0x62, 0x3f, 0x1c, 0x8a, 0x1e, 0x4b, 0xa9, 0x18, 0xf9, 0xe2, 0xd8, 0x4b, 0x52, + 0x26, 0x98, 0xb5, 0xa3, 0x61, 0x9e, 0x82, 0x79, 0x1a, 0x66, 0x3f, 0x34, 0x8a, 0x24, 0xac, 0x4f, + 0x23, 0x8a, 0xf9, 0x4c, 0xca, 0xde, 0x35, 0x82, 0xe5, 0x46, 0x87, 0xc6, 0x47, 0x2c, 0x83, 0x37, + 0x8c, 0xf0, 0xac, 0x1a, 0x87, 0x82, 0xb2, 0x38, 0x63, 0xdc, 0x20, 0x8c, 0x30, 0x59, 0xfa, 0x67, + 0xd5, 0x6c, 0xd5, 0xfd, 0x89, 0x60, 0xab, 0xc5, 0xc9, 0x5e, 0xb7, 0xbb, 0x77, 0x91, 0x63, 0x55, + 0x61, 0x35, 0x4a, 0x71, 0x28, 0x58, 0x5a, 0x45, 0x35, 0x54, 0x5f, 0x0b, 0xd4, 0xa7, 0x75, 0x0b, + 0x56, 0x07, 0x9c, 0x74, 0x86, 0x69, 0xbf, 0x7a, 0x45, 0xee, 0xac, 0x0c, 0x38, 0x69, 0xa7, 0x7d, + 0xab, 0x0d, 0x9b, 0xca, 0x17, 0x77, 0x3b, 0xf2, 0x78, 0xa3, 0xea, 0x52, 0x0d, 0xd5, 0xd7, 0x9b, + 0x75, 0xcf, 0xd4, 0x28, 0xef, 0x40, 0x62, 0xdf, 0x8f, 0x12, 0x1c, 0x6c, 0x9c, 0x4b, 0xcc, 0x56, + 0xdd, 0x3b, 0xb0, 0x5d, 0x12, 0x30, 0xc0, 0x3c, 0x61, 0x31, 0xc7, 0xee, 0x5b, 0xb8, 0xd9, 0xe2, + 0x24, 0xc0, 0x03, 0xf6, 0x11, 0xff, 0xef, 0x11, 0xdc, 0x1a, 0x38, 0xe5, 0x62, 0xda, 0xee, 0x13, + 0x6c, 0xb6, 0x38, 0x69, 0x27, 0xdd, 0x50, 0xe0, 0x83, 0xec, 0x06, 0x0d, 0x4e, 0xaf, 0xe0, 0xaa, + 0xba, 0x67, 0x69, 0x75, 0xad, 0x79, 0x7f, 0x8e, 0x56, 0x50, 0xcc, 0xf7, 0x97, 0x4f, 0x7e, 0xdf, + 0xad, 0x04, 0x9a, 0xed, 0x6e, 0xc3, 0xed, 0x82, 0xb1, 0x4e, 0xf5, 0x19, 0x2c, 0xbd, 0xf9, 0xf2, + 0x4c, 0xfa, 0x75, 0x7c, 0xc4, 0x0c, 0xb1, 0xde, 0x01, 0x9c, 0xff, 0x51, 0x59, 0xb0, 0x07, 0xe6, + 0x60, 0x5a, 0x36, 0x4b, 0xb6, 0x16, 0xa9, 0x05, 0x77, 0x07, 0xec, 0xa2, 0xbb, 0xca, 0xd6, 0xfc, + 0xbe, 0x0c, 0x4b, 0x2d, 0x4e, 0xac, 0x31, 0xac, 0xe7, 0xda, 0xe6, 0x9b, 0x1d, 0x0b, 0xc7, 0xb5, + 0x9f, 0x2e, 0x48, 0x50, 0x19, 0xac, 0x2f, 0x70, 0x3d, 0xdf, 0x9c, 0xc6, 0x9c, 0x5a, 0x9a, 0x61, + 0x3f, 0x5b, 0x94, 0xa1, 0xed, 0xbf, 0x22, 0xd8, 0x28, 0xbc, 0xb0, 0x47, 0x97, 0xca, 0xe5, 0x29, + 0xf6, 0xf3, 0x85, 0x29, 0x3a, 0xc2, 0x37, 0x04, 0x5b, 0x65, 0x8f, 0xe4, 0xc9, 0xa5, 0x92, 0x25, + 0x2c, 0xfb, 0xc5, 0xbf, 0xb0, 0x54, 0x96, 0xfd, 0x37, 0x27, 0x13, 0x07, 0x9d, 0x4e, 0x1c, 0xf4, + 0x67, 0xe2, 0xa0, 0x1f, 0x53, 0xa7, 0x72, 0x3a, 0x75, 0x2a, 0xbf, 0xa6, 0x4e, 0xe5, 0x43, 0x83, + 0x50, 0xd1, 0x1b, 0x1e, 0x7a, 0x11, 0x1b, 0xc8, 0xb1, 0xb6, 0x9b, 0x9b, 0x70, 0xc7, 0x17, 0x87, + 0xf0, 0x28, 0xc1, 0xfc, 0x70, 0x45, 0x8e, 0xb1, 0xc7, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x33, + 0xb2, 0x7c, 0x5f, 0xb1, 0x05, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -259,6 +464,8 @@ const _ = grpc.SupportPackageIsVersion4 type MsgClient interface { UpdatePolicies(ctx context.Context, in *MsgUpdatePolicies, opts ...grpc.CallOption) (*MsgUpdatePoliciesResponse, error) UpdateChainInfo(ctx context.Context, in *MsgUpdateChainInfo, opts ...grpc.CallOption) (*MsgUpdateChainInfoResponse, error) + AddAuthorization(ctx context.Context, in *MsgAddAuthorization, opts ...grpc.CallOption) (*MsgAddAuthorizationResponse, error) + RemoveAuthorization(ctx context.Context, in *MsgRemoveAuthorization, opts ...grpc.CallOption) (*MsgRemoveAuthorizationResponse, error) } type msgClient struct { @@ -287,10 +494,30 @@ func (c *msgClient) UpdateChainInfo(ctx context.Context, in *MsgUpdateChainInfo, return out, nil } +func (c *msgClient) AddAuthorization(ctx context.Context, in *MsgAddAuthorization, opts ...grpc.CallOption) (*MsgAddAuthorizationResponse, error) { + out := new(MsgAddAuthorizationResponse) + err := c.cc.Invoke(ctx, "/zetachain.zetacore.authority.Msg/AddAuthorization", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) RemoveAuthorization(ctx context.Context, in *MsgRemoveAuthorization, opts ...grpc.CallOption) (*MsgRemoveAuthorizationResponse, error) { + out := new(MsgRemoveAuthorizationResponse) + err := c.cc.Invoke(ctx, "/zetachain.zetacore.authority.Msg/RemoveAuthorization", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // MsgServer is the server API for Msg service. type MsgServer interface { UpdatePolicies(context.Context, *MsgUpdatePolicies) (*MsgUpdatePoliciesResponse, error) UpdateChainInfo(context.Context, *MsgUpdateChainInfo) (*MsgUpdateChainInfoResponse, error) + AddAuthorization(context.Context, *MsgAddAuthorization) (*MsgAddAuthorizationResponse, error) + RemoveAuthorization(context.Context, *MsgRemoveAuthorization) (*MsgRemoveAuthorizationResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. @@ -303,6 +530,12 @@ func (*UnimplementedMsgServer) UpdatePolicies(ctx context.Context, req *MsgUpdat func (*UnimplementedMsgServer) UpdateChainInfo(ctx context.Context, req *MsgUpdateChainInfo) (*MsgUpdateChainInfoResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method UpdateChainInfo not implemented") } +func (*UnimplementedMsgServer) AddAuthorization(ctx context.Context, req *MsgAddAuthorization) (*MsgAddAuthorizationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddAuthorization not implemented") +} +func (*UnimplementedMsgServer) RemoveAuthorization(ctx context.Context, req *MsgRemoveAuthorization) (*MsgRemoveAuthorizationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveAuthorization not implemented") +} func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) @@ -344,6 +577,42 @@ func _Msg_UpdateChainInfo_Handler(srv interface{}, ctx context.Context, dec func return interceptor(ctx, in, info, handler) } +func _Msg_AddAuthorization_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgAddAuthorization) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).AddAuthorization(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/zetachain.zetacore.authority.Msg/AddAuthorization", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).AddAuthorization(ctx, req.(*MsgAddAuthorization)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_RemoveAuthorization_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgRemoveAuthorization) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).RemoveAuthorization(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/zetachain.zetacore.authority.Msg/RemoveAuthorization", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).RemoveAuthorization(ctx, req.(*MsgRemoveAuthorization)) + } + return interceptor(ctx, in, info, handler) +} + var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "zetachain.zetacore.authority.Msg", HandlerType: (*MsgServer)(nil), @@ -356,12 +625,20 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "UpdateChainInfo", Handler: _Msg_UpdateChainInfo_Handler, }, + { + MethodName: "AddAuthorization", + Handler: _Msg_AddAuthorization_Handler, + }, + { + MethodName: "RemoveAuthorization", + Handler: _Msg_RemoveAuthorization_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "zetachain/zetacore/authority/tx.proto", } -func (m *MsgUpdatePolicies) Marshal() (dAtA []byte, err error) { +func (m *MsgAddAuthorization) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -371,26 +648,28 @@ func (m *MsgUpdatePolicies) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *MsgUpdatePolicies) MarshalTo(dAtA []byte) (int, error) { +func (m *MsgAddAuthorization) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *MsgUpdatePolicies) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *MsgAddAuthorization) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l - { - size, err := m.Policies.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintTx(dAtA, i, uint64(size)) + if m.AuthorizedPolicy != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.AuthorizedPolicy)) + i-- + dAtA[i] = 0x18 + } + if len(m.MsgUrl) > 0 { + i -= len(m.MsgUrl) + copy(dAtA[i:], m.MsgUrl) + i = encodeVarintTx(dAtA, i, uint64(len(m.MsgUrl))) + i-- + dAtA[i] = 0x12 } - i-- - dAtA[i] = 0x12 if len(m.Creator) > 0 { i -= len(m.Creator) copy(dAtA[i:], m.Creator) @@ -401,7 +680,7 @@ func (m *MsgUpdatePolicies) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *MsgUpdatePoliciesResponse) Marshal() (dAtA []byte, err error) { +func (m *MsgAddAuthorizationResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -411,12 +690,12 @@ func (m *MsgUpdatePoliciesResponse) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *MsgUpdatePoliciesResponse) MarshalTo(dAtA []byte) (int, error) { +func (m *MsgAddAuthorizationResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *MsgUpdatePoliciesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *MsgAddAuthorizationResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -424,7 +703,7 @@ func (m *MsgUpdatePoliciesResponse) MarshalToSizedBuffer(dAtA []byte) (int, erro return len(dAtA) - i, nil } -func (m *MsgUpdateChainInfo) Marshal() (dAtA []byte, err error) { +func (m *MsgRemoveAuthorization) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -434,18 +713,78 @@ func (m *MsgUpdateChainInfo) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *MsgUpdateChainInfo) MarshalTo(dAtA []byte) (int, error) { +func (m *MsgRemoveAuthorization) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *MsgUpdateChainInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *MsgRemoveAuthorization) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.MsgUrl) > 0 { + i -= len(m.MsgUrl) + copy(dAtA[i:], m.MsgUrl) + i = encodeVarintTx(dAtA, i, uint64(len(m.MsgUrl))) + 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 *MsgRemoveAuthorizationResponse) 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 *MsgRemoveAuthorizationResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgRemoveAuthorizationResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgUpdatePolicies) 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 *MsgUpdatePolicies) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdatePolicies) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l { - size, err := m.ChainInfo.MarshalToSizedBuffer(dAtA[:i]) + size, err := m.Policies.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -464,7 +803,7 @@ func (m *MsgUpdateChainInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *MsgUpdateChainInfoResponse) Marshal() (dAtA []byte, err error) { +func (m *MsgUpdatePoliciesResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -474,12 +813,12 @@ func (m *MsgUpdateChainInfoResponse) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *MsgUpdateChainInfoResponse) MarshalTo(dAtA []byte) (int, error) { +func (m *MsgUpdatePoliciesResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *MsgUpdateChainInfoResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *MsgUpdatePoliciesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -487,17 +826,135 @@ func (m *MsgUpdateChainInfoResponse) MarshalToSizedBuffer(dAtA []byte) (int, err return len(dAtA) - i, nil } -func encodeVarintTx(dAtA []byte, offset int, v uint64) int { - offset -= sovTx(v) - base := offset - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ +func (m *MsgUpdateChainInfo) 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 *MsgUpdateChainInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateChainInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.ChainInfo.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + 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 *MsgUpdateChainInfoResponse) 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 *MsgUpdateChainInfoResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateChainInfoResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ } dAtA[offset] = uint8(v) return base } +func (m *MsgAddAuthorization) 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.MsgUrl) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.AuthorizedPolicy != 0 { + n += 1 + sovTx(uint64(m.AuthorizedPolicy)) + } + return n +} + +func (m *MsgAddAuthorizationResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgRemoveAuthorization) 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.MsgUrl) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgRemoveAuthorizationResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + func (m *MsgUpdatePolicies) Size() (n int) { if m == nil { return 0 @@ -552,6 +1009,353 @@ func sovTx(x uint64) (n int) { func sozTx(x uint64) (n int) { return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } +func (m *MsgAddAuthorization) 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: MsgAddAuthorization: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgAddAuthorization: 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 MsgUrl", 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.MsgUrl = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthorizedPolicy", wireType) + } + m.AuthorizedPolicy = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.AuthorizedPolicy |= PolicyType(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 *MsgAddAuthorizationResponse) 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: MsgAddAuthorizationResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgAddAuthorizationResponse: 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 *MsgRemoveAuthorization) 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: MsgRemoveAuthorization: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRemoveAuthorization: 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 MsgUrl", 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.MsgUrl = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgRemoveAuthorizationResponse) 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: MsgRemoveAuthorizationResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgRemoveAuthorizationResponse: 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 *MsgUpdatePolicies) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/crosschain/keeper/evm_hooks_test.go b/x/crosschain/keeper/evm_hooks_test.go index 3ec4b49e65..80f8c3b28b 100644 --- a/x/crosschain/keeper/evm_hooks_test.go +++ b/x/crosschain/keeper/evm_hooks_test.go @@ -470,7 +470,7 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { setSupportedChain(ctx, zk, chainID) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - admin := keepertest.SetAdminPolices(ctx, zk.AuthorityKeeper) + admin := keepertest.SetAdminPolicies(ctx, zk.AuthorityKeeper) SetupStateForProcessLogsZetaSent(t, ctx, k, zk, sdkk, chain, admin) amount, ok := sdkmath.NewIntFromString("20000000000000000000000") @@ -516,7 +516,7 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { chainID := chain.ChainId setSupportedChain(ctx, zk, chainID) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - admin := keepertest.SetAdminPolices(ctx, zk.AuthorityKeeper) + admin := keepertest.SetAdminPolicies(ctx, zk.AuthorityKeeper) SetupStateForProcessLogsZetaSent(t, ctx, k, zk, sdkk, chain, admin) event, err := crosschainkeeper.ParseZetaSentEvent( @@ -538,7 +538,7 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { chain := chains.Ethereum SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - admin := keepertest.SetAdminPolices(ctx, zk.AuthorityKeeper) + admin := keepertest.SetAdminPolicies(ctx, zk.AuthorityKeeper) SetupStateForProcessLogsZetaSent(t, ctx, k, zk, sdkk, chain, admin) amount, ok := sdkmath.NewIntFromString("20000000000000000000000") @@ -570,7 +570,7 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { chainID := chain.ChainId setSupportedChain(ctx, zk, chainID) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - admin := keepertest.SetAdminPolices(ctx, zk.AuthorityKeeper) + admin := keepertest.SetAdminPolicies(ctx, zk.AuthorityKeeper) SetupStateForProcessLogsZetaSent(t, ctx, k, zk, sdkk, chain, admin) amount, ok := sdkmath.NewIntFromString("20000000000000000000000") @@ -634,7 +634,7 @@ func TestKeeper_ProcessZetaSentEvent(t *testing.T) { setSupportedChain(ctx, zk, chainID) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - admin := keepertest.SetAdminPolices(ctx, zk.AuthorityKeeper) + admin := keepertest.SetAdminPolicies(ctx, zk.AuthorityKeeper) SetupStateForProcessLogsZetaSent(t, ctx, k, zk, sdkk, chain, admin) zk.ObserverKeeper.SetChainNonces(ctx, observertypes.ChainNonces{ @@ -699,7 +699,7 @@ func TestKeeper_ProcessLogs(t *testing.T) { chainID := chain.ChainId setSupportedChain(ctx, zk, chainID) SetupStateForProcessLogs(t, ctx, k, zk, sdkk, chain) - admin := keepertest.SetAdminPolices(ctx, zk.AuthorityKeeper) + admin := keepertest.SetAdminPolicies(ctx, zk.AuthorityKeeper) SetupStateForProcessLogsZetaSent(t, ctx, k, zk, sdkk, chain, admin) amount, ok := sdkmath.NewIntFromString("20000000000000000000000") From 00006b58ba7fc359463124f8140310b5b5f162a9 Mon Sep 17 00:00:00 2001 From: Charlie Chen <34498985+ws4charlie@users.noreply.github.com> Date: Wed, 5 Jun 2024 02:24:05 -0500 Subject: [PATCH 4/9] refactor: removed unused code and unified tx signing logic (#2306) * removed unused code and unified tx signing logic * added changelog entry * define contstant for outbound zero amount * use type big.Int for the constant zeroValue * updated existing tests and added more unit tests to cover signing errors --- changelog.md | 1 + pkg/constant/constant.go | 2 + zetaclient/chains/evm/constant.go | 3 + .../chains/evm/signer/outbound_data_test.go | 6 +- zetaclient/chains/evm/signer/signer.go | 133 +++----- zetaclient/chains/evm/signer/signer_test.go | 290 ++++++++++++++---- zetaclient/testutils/mocks/tss_signer.go | 19 ++ 7 files changed, 304 insertions(+), 150 deletions(-) diff --git a/changelog.md b/changelog.md index dbf40ef8e3..6f0fa7e632 100644 --- a/changelog.md +++ b/changelog.md @@ -40,6 +40,7 @@ * [2262](https://github.com/zeta-chain/node/pull/2262) - refactor MsgUpdateZRC20 into MsgPauseZrc20 and MsgUnPauseZRC20 * [2290](https://github.com/zeta-chain/node/pull/2290) - rename `MsgAddBlameVote` message to `MsgVoteBlame` * [2269](https://github.com/zeta-chain/node/pull/2269) - refactor MsgUpdateCrosschainFlags into MsgEnableCCTX, MsgDisableCCTX and MsgUpdateGasPriceIncreaseFlags +* [2306](https://github.com/zeta-chain/node/pull/2306) - refactor zetaclient outbound transaction signing logic * [2296](https://github.com/zeta-chain/node/pull/2296) - move `testdata` package to `testutil` to organize test-related utilities ### Tests diff --git a/pkg/constant/constant.go b/pkg/constant/constant.go index 447eca98e5..9a81e1d8b2 100644 --- a/pkg/constant/constant.go +++ b/pkg/constant/constant.go @@ -4,8 +4,10 @@ const ( // DonationMessage is the message for donation transactions // Transaction sent to the TSS or ERC20 Custody address containing this message are considered as a donation DonationMessage = "I am rich!" + // CmdWhitelistERC20 is used for CCTX of type cmd to give the instruction to the TSS to whitelist an ERC20 on an exeternal chain CmdWhitelistERC20 = "cmd_whitelist_erc20" + // CmdMigrateTssFunds is used for CCTX of type cmd to give the instruction to the TSS to transfer its funds on a new address CmdMigrateTssFunds = "cmd_migrate_tss_funds" ) diff --git a/zetaclient/chains/evm/constant.go b/zetaclient/chains/evm/constant.go index e15453df59..b754d57f30 100644 --- a/zetaclient/chains/evm/constant.go +++ b/zetaclient/chains/evm/constant.go @@ -12,6 +12,9 @@ const ( // OutboundTrackerReportTimeout is the timeout for waiting for an outbound tracker report OutboundTrackerReportTimeout = 10 * time.Minute + // EthTransferGasLimit is the gas limit for a standard ETH transfer + EthTransferGasLimit = 21000 + // TopicsZetaSent is the number of topics for a Zeta sent event // [signature, zetaTxSenderAddress, destinationChainId] // https://github.com/zeta-chain/protocol-contracts/blob/d65814debf17648a6c67d757ba03646415842790/contracts/evm/ZetaConnector.base.sol#L34 diff --git a/zetaclient/chains/evm/signer/outbound_data_test.go b/zetaclient/chains/evm/signer/outbound_data_test.go index 77d0f9a040..bd9e81061c 100644 --- a/zetaclient/chains/evm/signer/outbound_data_test.go +++ b/zetaclient/chains/evm/signer/outbound_data_test.go @@ -45,7 +45,7 @@ func TestSigner_SetChainAndSender(t *testing.T) { func TestSigner_SetupGas(t *testing.T) { cctx := getCCTX(t) - evmSigner, err := getNewEvmSigner() + evmSigner, err := getNewEvmSigner(nil) require.NoError(t, err) txData := &OutboundData{} @@ -67,10 +67,10 @@ func TestSigner_SetupGas(t *testing.T) { func TestSigner_NewOutboundData(t *testing.T) { // Setup evm signer - evmSigner, err := getNewEvmSigner() + evmSigner, err := getNewEvmSigner(nil) require.NoError(t, err) - mockObserver, err := getNewEvmChainObserver() + mockObserver, err := getNewEvmChainObserver(nil) require.NoError(t, err) t.Run("NewOutboundData success", func(t *testing.T) { diff --git a/zetaclient/chains/evm/signer/signer.go b/zetaclient/chains/evm/signer/signer.go index 8ab5608959..00f6f35fd5 100644 --- a/zetaclient/chains/evm/signer/signer.go +++ b/zetaclient/chains/evm/signer/signer.go @@ -39,7 +39,12 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/zetacore" ) -var _ interfaces.ChainSigner = &Signer{} +var ( + _ interfaces.ChainSigner = &Signer{} + + // zeroValue is for outbounds that carry no ETH (gas token) value + zeroValue = big.NewInt(0) +) // Signer deals with the signing EVM transactions and implements the ChainSigner interface type Signer struct { @@ -138,6 +143,7 @@ func (signer *Signer) GetERC20CustodyAddress() ethcommon.Address { func (signer *Signer) Sign( data []byte, to ethcommon.Address, + amount *big.Int, gasLimit uint64, gasPrice *big.Int, nonce uint64, @@ -147,7 +153,7 @@ func (signer *Signer) Sign( // 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) + tx := ethtypes.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data) hashBytes := signer.ethSigner.Hash(tx).Bytes() @@ -207,12 +213,13 @@ func (signer *Signer) SignOutbound(txData *OutboundData) (*ethtypes.Transaction, tx, _, _, err := signer.Sign(data, signer.zetaConnectorAddress, + zeroValue, txData.gasLimit, txData.gasPrice, txData.nonce, txData.height) if err != nil { - return nil, fmt.Errorf("onReceive sign error: %w", err) + return nil, fmt.Errorf("sign onReceive error: %w", err) } return tx, nil @@ -241,74 +248,57 @@ func (signer *Signer) SignRevertTx(txData *OutboundData) (*ethtypes.Transaction, txData.message, txData.cctxIndex) if err != nil { - return nil, fmt.Errorf("pack error: %w", err) + return nil, fmt.Errorf("onRevert pack error: %w", err) } tx, _, _, err := signer.Sign(data, signer.zetaConnectorAddress, + zeroValue, txData.gasLimit, txData.gasPrice, txData.nonce, txData.height) if err != nil { - return nil, fmt.Errorf("Sign error: %w", err) + return nil, fmt.Errorf("sign onRevert error: %w", err) } return tx, nil } // 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 { - return nil, err - } - - pubk, err := crypto.SigToPub(hashBytes, sig[:]) - if err != nil { - signer.logger.Std.Error().Err(err).Msgf("SigToPub error") - } - - addr := crypto.PubkeyToAddress(*pubk) - signer.logger.Std.Info().Msgf("Sign: Ecrecovery of signature: %s", addr.Hex()) - signedTX, err := tx.WithSignature(signer.ethSigner, sig[:]) +func (signer *Signer) SignCancelTx(txData *OutboundData) (*ethtypes.Transaction, error) { + tx, _, _, err := signer.Sign( + nil, + signer.tssSigner.EVMAddress(), + zeroValue, // zero out the amount to cancel the tx + evm.EthTransferGasLimit, + txData.gasPrice, + txData.nonce, + txData.height, + ) if err != nil { - return nil, err + return nil, fmt.Errorf("SignCancelTx error: %w", err) } - return signedTX, nil + return tx, nil } // SignWithdrawTx signs a withdrawal transaction sent from the TSS address to the destination func (signer *Signer) SignWithdrawTx(txData *OutboundData) (*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 { - return nil, err - } - - pubk, err := crypto.SigToPub(hashBytes, sig[:]) - if err != nil { - signer.logger.Std.Error().Err(err).Msgf("SigToPub error") - } - - addr := crypto.PubkeyToAddress(*pubk) - signer.logger.Std.Info().Msgf("Sign: Ecrecovery of signature: %s", addr.Hex()) - signedTX, err := tx.WithSignature(signer.ethSigner, sig[:]) + tx, _, _, err := signer.Sign( + nil, + txData.to, + txData.amount, + evm.EthTransferGasLimit, + txData.gasPrice, + txData.nonce, + txData.height, + ) if err != nil { - return nil, err + return nil, fmt.Errorf("SignWithdrawTx error: %w", err) } - return signedTX, nil + return tx, nil } // SignCommandTx signs a transaction based on the given command includes: @@ -389,7 +379,7 @@ func (signer *Signer) TryProcessOutbound( cctx.GetCurrentOutboundParam().CoinType.String(), ) - tx, err = signer.SignCancelTx(txData.nonce, txData.gasPrice, height) // cancel the tx + tx, err = signer.SignCancelTx(txData) // cancel the tx if err != nil { logger.Warn().Err(err).Msg(ErrorMsg(cctx)) return @@ -589,52 +579,20 @@ func (signer *Signer) SignERC20WithdrawTx(txData *OutboundData) (*ethtypes.Trans var err error data, err = signer.erc20CustodyABI.Pack("withdraw", txData.to, txData.asset, txData.amount) if err != nil { - return nil, fmt.Errorf("pack error: %w", err) + return nil, fmt.Errorf("withdraw pack error: %w", err) } tx, _, _, err := signer.Sign( data, signer.er20CustodyAddress, + zeroValue, txData.gasLimit, txData.gasPrice, txData.nonce, txData.height, ) if err != nil { - return nil, fmt.Errorf("sign error: %w", err) - } - - return tx, nil -} - -// SignWhitelistTx -// function whitelist( -// address asset, -// ) external onlyTssAddress -// function unwhitelist( -// address asset, -// ) external onlyTssAddress -func (signer *Signer) SignWhitelistTx( - action string, - _ ethcommon.Address, - asset ethcommon.Address, - gasLimit uint64, - nonce uint64, - gasPrice *big.Int, - height uint64, -) (*ethtypes.Transaction, error) { - var data []byte - - var err error - - data, err = signer.erc20CustodyABI.Pack(action, asset) - if err != nil { - return nil, fmt.Errorf("pack error: %w", err) - } - - tx, _, _, err := signer.Sign(data, signer.er20CustodyAddress, gasLimit, gasPrice, nonce, height) - if err != nil { - return nil, fmt.Errorf("Sign error: %w", err) + return nil, fmt.Errorf("sign withdraw error: %w", err) } return tx, nil @@ -686,34 +644,35 @@ func (signer *Signer) SignWhitelistERC20Cmd(txData *OutboundData, params string) } data, err := custodyAbi.Pack("whitelist", erc20) if err != nil { - return nil, err + return nil, fmt.Errorf("whitelist pack error: %w", err) } tx, _, _, err := signer.Sign( data, txData.to, + zeroValue, txData.gasLimit, txData.gasPrice, outboundParams.TssNonce, txData.height, ) if err != nil { - return nil, fmt.Errorf("sign error: %w", err) + return nil, fmt.Errorf("sign whitelist error: %w", err) } return tx, nil } func (signer *Signer) SignMigrateTssFundsCmd(txData *OutboundData) (*ethtypes.Transaction, error) { - outboundParams := txData.outboundParams tx, _, _, err := signer.Sign( nil, txData.to, + txData.amount, txData.gasLimit, txData.gasPrice, - outboundParams.TssNonce, + txData.nonce, txData.height, ) if err != nil { - return nil, err + return nil, fmt.Errorf("SignMigrateTssFundsCmd error: %w", err) } return tx, nil } diff --git a/zetaclient/chains/evm/signer/signer_test.go b/zetaclient/chains/evm/signer/signer_test.go index 6039c44803..5bd1b6a967 100644 --- a/zetaclient/chains/evm/signer/signer_test.go +++ b/zetaclient/chains/evm/signer/signer_test.go @@ -1,9 +1,12 @@ package signer import ( + "math/big" "testing" sdktypes "github.com/cosmos/cosmos-sdk/types" + ethcommon "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/rs/zerolog" "github.com/stretchr/testify/require" @@ -13,6 +16,7 @@ import ( "github.com/zeta-chain/zetacore/testutil/sample" crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" "github.com/zeta-chain/zetacore/zetaclient/chains/evm/observer" + "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" "github.com/zeta-chain/zetacore/zetaclient/common" "github.com/zeta-chain/zetacore/zetaclient/config" "github.com/zeta-chain/zetacore/zetaclient/context" @@ -29,16 +33,23 @@ var ( ERC20CustodyAddress = sample.EthAddress() ) -func getNewEvmSigner() (*Signer, error) { +// getNewEvmSigner creates a new EVM chain signer for testing +func getNewEvmSigner(tss interfaces.TSSSigner) (*Signer, error) { + // use default mock TSS if not provided + if tss == nil { + tss = mocks.NewTSSMainnet() + } + mpiAddress := ConnectorAddress erc20CustodyAddress := ERC20CustodyAddress logger := common.ClientLogger{} ts := &metrics.TelemetryServer{} cfg := config.NewConfig() + return NewSigner( chains.BscMainnet, mocks.EVMRPCEnabled, - mocks.NewTSSMainnet(), + tss, config.GetConnectorABI(), config.GetERC20CustodyABI(), mpiAddress, @@ -48,11 +59,16 @@ func getNewEvmSigner() (*Signer, error) { ts) } -func getNewEvmChainObserver() (*observer.Observer, error) { +// getNewEvmChainObserver creates a new EVM chain observer for testing +func getNewEvmChainObserver(tss interfaces.TSSSigner) (*observer.Observer, error) { + // use default mock TSS if not provided + if tss == nil { + tss = mocks.NewTSSMainnet() + } + logger := common.ClientLogger{} ts := &metrics.TelemetryServer{} cfg := config.NewConfig() - tss := mocks.NewTSSMainnet() evmcfg := config.EVMConfig{Chain: chains.BscMainnet, Endpoint: "http://localhost:8545"} cfg.EVMChainConfigs[chains.BscMainnet.ChainId] = evmcfg @@ -78,8 +94,31 @@ func getInvalidCCTX(t *testing.T) *crosschaintypes.CrossChainTx { return cctx } +// verifyTxSignature is a helper function to verify the signature of a transaction +func verifyTxSignature(t *testing.T, tx *ethtypes.Transaction, tssPubkey []byte, signer ethtypes.Signer) { + _, r, s := tx.RawSignatureValues() + signature := append(r.Bytes(), s.Bytes()...) + hash := signer.Hash(tx) + + verified := crypto.VerifySignature(tssPubkey, hash.Bytes(), signature) + require.True(t, verified) +} + +// verifyTxBodyBasics is a helper function to verify 'to', 'nonce' and 'amount' of a transaction +func verifyTxBodyBasics( + t *testing.T, + tx *ethtypes.Transaction, + to ethcommon.Address, + nonce uint64, + amount *big.Int, +) { + require.Equal(t, to, *tx.To()) + require.Equal(t, nonce, tx.Nonce()) + require.True(t, amount.Cmp(tx.Value()) == 0) +} + func TestSigner_SetGetConnectorAddress(t *testing.T) { - evmSigner, err := getNewEvmSigner() + evmSigner, err := getNewEvmSigner(nil) require.NoError(t, err) // Get and compare require.Equal(t, ConnectorAddress, evmSigner.GetZetaConnectorAddress()) @@ -91,7 +130,7 @@ func TestSigner_SetGetConnectorAddress(t *testing.T) { } func TestSigner_SetGetERC20CustodyAddress(t *testing.T) { - evmSigner, err := getNewEvmSigner() + evmSigner, err := getNewEvmSigner(nil) require.NoError(t, err) // Get and compare require.Equal(t, ERC20CustodyAddress, evmSigner.GetERC20CustodyAddress()) @@ -103,11 +142,11 @@ func TestSigner_SetGetERC20CustodyAddress(t *testing.T) { } func TestSigner_TryProcessOutbound(t *testing.T) { - evmSigner, err := getNewEvmSigner() + evmSigner, err := getNewEvmSigner(nil) require.NoError(t, err) cctx := getCCTX(t) processorManager := getNewOutboundProcessor() - mockObserver, err := getNewEvmChainObserver() + mockObserver, err := getNewEvmChainObserver(nil) require.NoError(t, err) // Test with mock client that has keys @@ -121,13 +160,14 @@ func TestSigner_TryProcessOutbound(t *testing.T) { func TestSigner_SignOutbound(t *testing.T) { // Setup evm signer - evmSigner, err := getNewEvmSigner() + tss := mocks.NewTSSMainnet() + evmSigner, err := getNewEvmSigner(tss) require.NoError(t, err) // Setup txData struct cctx := getCCTX(t) - mockObserver, err := getNewEvmChainObserver() + mockObserver, err := getNewEvmChainObserver(tss) require.NoError(t, err) txData, skip, err := NewOutboundData(cctx, mockObserver, evmSigner.EvmClient(), zerolog.Logger{}, 123) require.False(t, skip) @@ -140,23 +180,28 @@ func TestSigner_SignOutbound(t *testing.T) { // Verify Signature tss := mocks.NewTSSMainnet() - _, r, s := tx.RawSignatureValues() - signature := append(r.Bytes(), s.Bytes()...) - hash := evmSigner.EvmSigner().Hash(tx) + verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + }) + t.Run("SignOutbound - should fail if keysign fails", func(t *testing.T) { + // Pause tss to make keysign fail + tss.Pause() - verified := crypto.VerifySignature(tss.Pubkey(), hash.Bytes(), signature) - require.True(t, verified) + // Call SignOutbound + tx, err := evmSigner.SignOutbound(txData) + require.ErrorContains(t, err, "sign onReceive error") + require.Nil(t, tx) }) } func TestSigner_SignRevertTx(t *testing.T) { // Setup evm signer - evmSigner, err := getNewEvmSigner() + tss := mocks.NewTSSMainnet() + evmSigner, err := getNewEvmSigner(tss) require.NoError(t, err) // Setup txData struct cctx := getCCTX(t) - mockObserver, err := getNewEvmChainObserver() + mockObserver, err := getNewEvmChainObserver(tss) require.NoError(t, err) txData, skip, err := NewOutboundData(cctx, mockObserver, evmSigner.EvmClient(), zerolog.Logger{}, 123) require.False(t, skip) @@ -167,25 +212,72 @@ func TestSigner_SignRevertTx(t *testing.T) { tx, err := evmSigner.SignRevertTx(txData) require.NoError(t, err) - // Verify Signature + // Verify tx signature + tss := mocks.NewTSSMainnet() + verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + + // Verify tx body basics + // Note: Revert tx calls connector contract with 0 gas token + verifyTxBodyBasics(t, tx, evmSigner.zetaConnectorAddress, txData.nonce, big.NewInt(0)) + }) + t.Run("SignRevertTx - should fail if keysign fails", func(t *testing.T) { + // Pause tss to make keysign fail + tss.Pause() + + // Call SignRevertTx + tx, err := evmSigner.SignRevertTx(txData) + require.ErrorContains(t, err, "sign onRevert error") + require.Nil(t, tx) + }) +} + +func TestSigner_SignCancelTx(t *testing.T) { + // Setup evm signer + tss := mocks.NewTSSMainnet() + evmSigner, err := getNewEvmSigner(tss) + require.NoError(t, err) + + // Setup txData struct + cctx := getCCTX(t) + mockObserver, err := getNewEvmChainObserver(tss) + require.NoError(t, err) + txData, skip, err := NewOutboundData(cctx, mockObserver, evmSigner.EvmClient(), zerolog.Logger{}, 123) + require.False(t, skip) + require.NoError(t, err) + + t.Run("SignCancelTx - should successfully sign", func(t *testing.T) { + // Call SignRevertTx + tx, err := evmSigner.SignCancelTx(txData) + require.NoError(t, err) + + // Verify tx signature tss := mocks.NewTSSMainnet() - _, r, s := tx.RawSignatureValues() - signature := append(r.Bytes(), s.Bytes()...) - hash := evmSigner.EvmSigner().Hash(tx) + verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) - verified := crypto.VerifySignature(tss.Pubkey(), hash.Bytes(), signature) - require.True(t, verified) + // Verify tx body basics + // Note: Cancel tx sends 0 gas token to TSS self address + verifyTxBodyBasics(t, tx, evmSigner.tssSigner.EVMAddress(), txData.nonce, big.NewInt(0)) + }) + t.Run("SignCancelTx - should fail if keysign fails", func(t *testing.T) { + // Pause tss to make keysign fail + tss.Pause() + + // Call SignCancelTx + tx, err := evmSigner.SignCancelTx(txData) + require.ErrorContains(t, err, "SignCancelTx error") + require.Nil(t, tx) }) } func TestSigner_SignWithdrawTx(t *testing.T) { // Setup evm signer - evmSigner, err := getNewEvmSigner() + tss := mocks.NewTSSMainnet() + evmSigner, err := getNewEvmSigner(tss) require.NoError(t, err) // Setup txData struct cctx := getCCTX(t) - mockObserver, err := getNewEvmChainObserver() + mockObserver, err := getNewEvmChainObserver(tss) require.NoError(t, err) txData, skip, err := NewOutboundData(cctx, mockObserver, evmSigner.EvmClient(), zerolog.Logger{}, 123) require.False(t, skip) @@ -196,25 +288,32 @@ func TestSigner_SignWithdrawTx(t *testing.T) { tx, err := evmSigner.SignWithdrawTx(txData) require.NoError(t, err) - // Verify Signature + // Verify tx signature tss := mocks.NewTSSMainnet() - _, r, s := tx.RawSignatureValues() - signature := append(r.Bytes(), s.Bytes()...) - hash := evmSigner.EvmSigner().Hash(tx) + verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) - verified := crypto.VerifySignature(tss.Pubkey(), hash.Bytes(), signature) - require.True(t, verified) + // Verify tx body basics + verifyTxBodyBasics(t, tx, txData.to, txData.nonce, txData.amount) + }) + t.Run("SignWithdrawTx - should fail if keysign fails", func(t *testing.T) { + // Pause tss to make keysign fail + tss.Pause() + + // Call SignWithdrawTx + tx, err := evmSigner.SignWithdrawTx(txData) + require.ErrorContains(t, err, "SignWithdrawTx error") + require.Nil(t, tx) }) } func TestSigner_SignCommandTx(t *testing.T) { // Setup evm signer - evmSigner, err := getNewEvmSigner() + evmSigner, err := getNewEvmSigner(nil) require.NoError(t, err) // Setup txData struct cctx := getCCTX(t) - mockObserver, err := getNewEvmChainObserver() + mockObserver, err := getNewEvmChainObserver(nil) require.NoError(t, err) txData, skip, err := NewOutboundData(cctx, mockObserver, evmSigner.EvmClient(), zerolog.Logger{}, 123) require.False(t, skip) @@ -227,14 +326,13 @@ func TestSigner_SignCommandTx(t *testing.T) { tx, err := evmSigner.SignCommandTx(txData, cmd, params) require.NoError(t, err) - // Verify Signature + // Verify tx signature tss := mocks.NewTSSMainnet() - _, r, s := tx.RawSignatureValues() - signature := append(r.Bytes(), s.Bytes()...) - hash := evmSigner.EvmSigner().Hash(tx) + verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) - verified := crypto.VerifySignature(tss.Pubkey(), hash.Bytes(), signature) - require.True(t, verified) + // Verify tx body basics + // Note: Revert tx calls erc20 custody contract with 0 gas token + verifyTxBodyBasics(t, tx, txData.to, txData.nonce, big.NewInt(0)) }) t.Run("SignCommandTx CmdMigrateTssFunds", func(t *testing.T) { @@ -243,25 +341,24 @@ func TestSigner_SignCommandTx(t *testing.T) { tx, err := evmSigner.SignCommandTx(txData, cmd, "") require.NoError(t, err) - // Verify Signature + // Verify tx signature tss := mocks.NewTSSMainnet() - _, r, s := tx.RawSignatureValues() - signature := append(r.Bytes(), s.Bytes()...) - hash := evmSigner.EvmSigner().Hash(tx) + verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) - verified := crypto.VerifySignature(tss.Pubkey(), hash.Bytes(), signature) - require.True(t, verified) + // Verify tx body basics + verifyTxBodyBasics(t, tx, txData.to, txData.nonce, txData.amount) }) } func TestSigner_SignERC20WithdrawTx(t *testing.T) { // Setup evm signer - evmSigner, err := getNewEvmSigner() + tss := mocks.NewTSSMainnet() + evmSigner, err := getNewEvmSigner(tss) require.NoError(t, err) // Setup txData struct cctx := getCCTX(t) - mockObserver, err := getNewEvmChainObserver() + mockObserver, err := getNewEvmChainObserver(tss) require.NoError(t, err) txData, skip, err := NewOutboundData(cctx, mockObserver, evmSigner.EvmClient(), zerolog.Logger{}, 123) require.False(t, skip) @@ -272,25 +369,34 @@ func TestSigner_SignERC20WithdrawTx(t *testing.T) { tx, err := evmSigner.SignERC20WithdrawTx(txData) require.NoError(t, err) - // Verify Signature + // Verify tx signature tss := mocks.NewTSSMainnet() - _, r, s := tx.RawSignatureValues() - signature := append(r.Bytes(), s.Bytes()...) - hash := evmSigner.EvmSigner().Hash(tx) + verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) - verified := crypto.VerifySignature(tss.Pubkey(), hash.Bytes(), signature) - require.True(t, verified) + // Verify tx body basics + // Note: Withdraw tx calls erc20 custody contract with 0 gas token + verifyTxBodyBasics(t, tx, evmSigner.er20CustodyAddress, txData.nonce, big.NewInt(0)) + }) + + t.Run("SignERC20WithdrawTx - should fail if keysign fails", func(t *testing.T) { + // pause tss to make keysign fail + tss.Pause() + + // Call SignERC20WithdrawTx + tx, err := evmSigner.SignERC20WithdrawTx(txData) + require.ErrorContains(t, err, "sign withdraw error") + require.Nil(t, tx) }) } func TestSigner_BroadcastOutbound(t *testing.T) { // Setup evm signer - evmSigner, err := getNewEvmSigner() + evmSigner, err := getNewEvmSigner(nil) require.NoError(t, err) // Setup txData struct cctx := getCCTX(t) - mockObserver, err := getNewEvmChainObserver() + mockObserver, err := getNewEvmChainObserver(nil) require.NoError(t, err) txData, skip, err := NewOutboundData(cctx, mockObserver, evmSigner.EvmClient(), zerolog.Logger{}, 123) require.False(t, skip) @@ -334,18 +440,82 @@ func TestSigner_SignerErrorMsg(t *testing.T) { func TestSigner_SignWhitelistERC20Cmd(t *testing.T) { // Setup evm signer - evmSigner, err := getNewEvmSigner() + tss := mocks.NewTSSMainnet() + evmSigner, err := getNewEvmSigner(tss) require.NoError(t, err) // Setup txData struct cctx := getCCTX(t) - mockObserver, err := getNewEvmChainObserver() + mockObserver, err := getNewEvmChainObserver(tss) require.NoError(t, err) txData, skip, err := NewOutboundData(cctx, mockObserver, evmSigner.EvmClient(), zerolog.Logger{}, 123) require.False(t, skip) require.NoError(t, err) - tx, err := evmSigner.SignWhitelistERC20Cmd(txData, "") - require.Nil(t, tx) - require.ErrorContains(t, err, "invalid erc20 address") + t.Run("SignWhitelistERC20Cmd - should successfully sign", func(t *testing.T) { + // Call SignWhitelistERC20Cmd + tx, err := evmSigner.SignWhitelistERC20Cmd(txData, sample.EthAddress().Hex()) + require.NoError(t, err) + require.NotNil(t, tx) + + // Verify tx signature + tss := mocks.NewTSSMainnet() + verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + + // Verify tx body basics + verifyTxBodyBasics(t, tx, txData.to, txData.nonce, zeroValue) + }) + t.Run("SignWhitelistERC20Cmd - should fail on invalid erc20 address", func(t *testing.T) { + tx, err := evmSigner.SignWhitelistERC20Cmd(txData, "") + require.Nil(t, tx) + require.ErrorContains(t, err, "invalid erc20 address") + }) + t.Run("SignWhitelistERC20Cmd - should fail if keysign fails", func(t *testing.T) { + // Pause tss to make keysign fail + tss.Pause() + + // Call SignWhitelistERC20Cmd + tx, err := evmSigner.SignWhitelistERC20Cmd(txData, sample.EthAddress().Hex()) + require.ErrorContains(t, err, "sign whitelist error") + require.Nil(t, tx) + }) +} + +func TestSigner_SignMigrateTssFundsCmd(t *testing.T) { + // Setup evm signer + tss := mocks.NewTSSMainnet() + evmSigner, err := getNewEvmSigner(tss) + require.NoError(t, err) + + // Setup txData struct + cctx := getCCTX(t) + mockObserver, err := getNewEvmChainObserver(tss) + require.NoError(t, err) + txData, skip, err := NewOutboundData(cctx, mockObserver, evmSigner.EvmClient(), zerolog.Logger{}, 123) + require.False(t, skip) + require.NoError(t, err) + + t.Run("SignMigrateTssFundsCmd - should successfully sign", func(t *testing.T) { + // Call SignMigrateTssFundsCmd + tx, err := evmSigner.SignMigrateTssFundsCmd(txData) + require.NoError(t, err) + require.NotNil(t, tx) + + // Verify tx signature + tss := mocks.NewTSSMainnet() + verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + + // Verify tx body basics + verifyTxBodyBasics(t, tx, txData.to, txData.nonce, txData.amount) + }) + + t.Run("SignMigrateTssFundsCmd - should fail if keysign fails", func(t *testing.T) { + // Pause tss to make keysign fail + tss.Pause() + + // Call SignMigrateTssFundsCmd + tx, err := evmSigner.SignMigrateTssFundsCmd(txData) + require.ErrorContains(t, err, "SignMigrateTssFundsCmd error") + require.Nil(t, tx) + }) } diff --git a/zetaclient/testutils/mocks/tss_signer.go b/zetaclient/testutils/mocks/tss_signer.go index 6226200d08..87d2f2ac3b 100644 --- a/zetaclient/testutils/mocks/tss_signer.go +++ b/zetaclient/testutils/mocks/tss_signer.go @@ -31,6 +31,8 @@ var _ interfaces.TSSSigner = (*TSS)(nil) // TSS is a mock of TSS signer for testing type TSS struct { + paused bool + // set evmAddress/btcAddress if just want to mock EVMAddress()/BTCAddress() chain chains.Chain evmAddress string @@ -42,6 +44,7 @@ type TSS struct { func NewMockTSS(chain chains.Chain, evmAddress string, btcAddress string) *TSS { return &TSS{ + paused: false, chain: chain, evmAddress: evmAddress, btcAddress: btcAddress, @@ -65,6 +68,11 @@ func (s *TSS) WithPrivKey(privKey *ecdsa.PrivateKey) *TSS { // Sign uses test key unrelated to any tss key in production func (s *TSS) Sign(data []byte, _ uint64, _ uint64, _ *chains.Chain, _ string) ([65]byte, error) { + // return error if tss is paused + if s.paused { + return [65]byte{}, fmt.Errorf("tss is paused") + } + signature, err := crypto.Sign(data, s.PrivKey) if err != nil { return [65]byte{}, err @@ -161,3 +169,14 @@ func (s *TSS) btcAddressPubkey() *btcutil.AddressPubKey { } return testnet3Addr } + +// ---------------------------------------------------------------------------- +// methods to control the mock for testing +// ---------------------------------------------------------------------------- +func (s *TSS) Pause() { + s.paused = true +} + +func (s *TSS) Unpause() { + s.paused = false +} From a28d7e1d2f4a220890cba5decb37f08a11ea8ed9 Mon Sep 17 00:00:00 2001 From: Charlie <31941002+CharlieMc0@users.noreply.github.com> Date: Wed, 5 Jun 2024 01:04:06 -0700 Subject: [PATCH 5/9] chore: change max execution period (#2314) * Adjusted max execution time * Adjusted max execution time * Update app/app.go --------- Co-authored-by: Lucas Bertrand --- app/app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/app.go b/app/app.go index eaad38a8a2..243a0e7665 100644 --- a/app/app.go +++ b/app/app.go @@ -630,7 +630,7 @@ func New( app.MsgServiceRouter(), app.AccountKeeper, group.Config{ - MaxExecutionPeriod: 2 * time.Hour, // Two hours. + MaxExecutionPeriod: 48 * time.Hour, MaxMetadataLen: 255, }, ) From cc18372bfb9af80ff0e286f78224e3edc86d303f Mon Sep 17 00:00:00 2001 From: skosito Date: Thu, 6 Jun 2024 13:51:46 +0100 Subject: [PATCH 6/9] test: add and fix existing ethermint rpc unit test (#2294) --- changelog.md | 1 + codecov.yml | 1 - rpc/backend/account_info_test.go | 460 ++++++ rpc/backend/backend_suite_test.go | 199 +++ rpc/backend/blocks_test.go | 1615 ++++++++++++++++++++ rpc/backend/call_tx_test.go | 504 ++++++ rpc/backend/chain_info_test.go | 454 ++++++ rpc/backend/client_test.go | 319 ++++ rpc/backend/evm_query_client_test.go | 291 ++++ rpc/backend/feemarket_query_client_test.go | 22 + rpc/backend/filters_test.go | 121 ++ rpc/backend/mocks/client.go | 50 +- rpc/backend/node_info_test.go | 359 +++++ rpc/backend/sign_tx_test.go | 274 ++++ rpc/backend/tracing_test.go | 316 ++++ rpc/backend/tx_info.go | 7 +- rpc/backend/tx_info_test.go | 639 ++++++++ rpc/backend/utils_test.go | 52 + 18 files changed, 5678 insertions(+), 6 deletions(-) create mode 100644 rpc/backend/account_info_test.go create mode 100644 rpc/backend/backend_suite_test.go create mode 100644 rpc/backend/blocks_test.go create mode 100644 rpc/backend/call_tx_test.go create mode 100644 rpc/backend/chain_info_test.go create mode 100644 rpc/backend/client_test.go create mode 100644 rpc/backend/evm_query_client_test.go create mode 100644 rpc/backend/feemarket_query_client_test.go create mode 100644 rpc/backend/filters_test.go create mode 100644 rpc/backend/node_info_test.go create mode 100644 rpc/backend/sign_tx_test.go create mode 100644 rpc/backend/tracing_test.go create mode 100644 rpc/backend/tx_info_test.go create mode 100644 rpc/backend/utils_test.go diff --git a/changelog.md b/changelog.md index 6f0fa7e632..47ab793048 100644 --- a/changelog.md +++ b/changelog.md @@ -51,6 +51,7 @@ * [2199](https://github.com/zeta-chain/node/pull/2199) - custom priority mempool unit tests * [2240](https://github.com/zeta-chain/node/pull/2240) - removed hard-coded Bitcoin regnet chainID in E2E withdraw tests * [2266](https://github.com/zeta-chain/node/pull/2266) - try fixing E2E test `crosschain_swap` failure `btc transaction not signed` +* [2294](https://github.com/zeta-chain/node/pull/2294) - add and fix existing ethermint rpc unit test * [2299](https://github.com/zeta-chain/node/pull/2299) - add `zetae2e` command to deploy test contracts ### Fixes diff --git a/codecov.yml b/codecov.yml index 99fe33e09e..5646dcdf96 100644 --- a/codecov.yml +++ b/codecov.yml @@ -68,7 +68,6 @@ ignore: - "cmd/**/*" - "contrib/**/*" - "docs/**/*" - - "rpc/**/*" - "proto/**/*" - "scripts/**/*" - "server/**/*" diff --git a/rpc/backend/account_info_test.go b/rpc/backend/account_info_test.go new file mode 100644 index 0000000000..f54b743d5f --- /dev/null +++ b/rpc/backend/account_info_test.go @@ -0,0 +1,460 @@ +package backend + +import ( + "fmt" + "math/big" + + tmrpcclient "github.com/cometbft/cometbft/rpc/client" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/evmos/ethermint/tests" + evmtypes "github.com/evmos/ethermint/x/evm/types" + "google.golang.org/grpc/metadata" + + "github.com/zeta-chain/zetacore/rpc/backend/mocks" + rpctypes "github.com/zeta-chain/zetacore/rpc/types" +) + +func (suite *BackendTestSuite) TestGetCode() { + blockNr := rpctypes.NewBlockNumber(big.NewInt(1)) + contractCode := []byte( + "0xef616c92f3cfc9e92dc270d6acff9cea213cecc7020a76ee4395af09bdceb4837a1ebdb5735e11e7d3adb6104e0c3ac55180b4ddf5e54d022cc5e8837f6a4f971b", + ) + + testCases := []struct { + name string + addr common.Address + blockNrOrHash rpctypes.BlockNumberOrHash + registerMock func(common.Address) + expPass bool + expCode hexutil.Bytes + }{ + { + "fail - BlockHash and BlockNumber are both nil ", + tests.GenerateAddress(), + rpctypes.BlockNumberOrHash{}, + func(addr common.Address) {}, + false, + nil, + }, + { + "fail - query client errors on getting Code", + tests.GenerateAddress(), + rpctypes.BlockNumberOrHash{BlockNumber: &blockNr}, + func(addr common.Address) { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterCodeError(queryClient, addr) + }, + false, + nil, + }, + { + "pass", + tests.GenerateAddress(), + rpctypes.BlockNumberOrHash{BlockNumber: &blockNr}, + func(addr common.Address) { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterCode(queryClient, addr, contractCode) + }, + true, + contractCode, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset + tc.registerMock(tc.addr) + + code, err := suite.backend.GetCode(tc.addr, tc.blockNrOrHash) + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(tc.expCode, code) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestGetProof() { + blockNrInvalid := rpctypes.NewBlockNumber(big.NewInt(1)) + blockNr := rpctypes.NewBlockNumber(big.NewInt(4)) + address1 := tests.GenerateAddress() + + testCases := []struct { + name string + addr common.Address + storageKeys []string + blockNrOrHash rpctypes.BlockNumberOrHash + registerMock func(rpctypes.BlockNumber, common.Address) + expPass bool + expAccRes *rpctypes.AccountResult + }{ + { + "fail - BlockNumeber = 1 (invalidBlockNumber)", + address1, + []string{}, + rpctypes.BlockNumberOrHash{BlockNumber: &blockNrInvalid}, + func(bn rpctypes.BlockNumber, addr common.Address) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlock(client, bn.Int64(), nil) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterAccount(queryClient, addr, blockNrInvalid.Int64()) + }, + false, + &rpctypes.AccountResult{}, + }, + { + "fail - Block doesn't exist)", + address1, + []string{}, + rpctypes.BlockNumberOrHash{BlockNumber: &blockNrInvalid}, + func(bn rpctypes.BlockNumber, addr common.Address) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockError(client, bn.Int64()) + }, + false, + &rpctypes.AccountResult{}, + }, + { + "pass", + address1, + []string{"0x0"}, + rpctypes.BlockNumberOrHash{BlockNumber: &blockNr}, + func(bn rpctypes.BlockNumber, addr common.Address) { + suite.backend.ctx = rpctypes.ContextWithHeight(bn.Int64()) + + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlock(client, bn.Int64(), nil) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterAccount(queryClient, addr, bn.Int64()) + + // Use the IAVL height if a valid tendermint height is passed in. + iavlHeight := bn.Int64() + RegisterABCIQueryWithOptions( + client, + bn.Int64(), + "store/evm/key", + evmtypes.StateKey(address1, common.HexToHash("0x0").Bytes()), + tmrpcclient.ABCIQueryOptions{Height: iavlHeight, Prove: true}, + ) + RegisterABCIQueryWithOptions( + client, + bn.Int64(), + "store/acc/key", + authtypes.AddressStoreKey(sdk.AccAddress(address1.Bytes())), + tmrpcclient.ABCIQueryOptions{Height: iavlHeight, Prove: true}, + ) + }, + true, + &rpctypes.AccountResult{ + Address: address1, + AccountProof: []string{""}, + Balance: (*hexutil.Big)(big.NewInt(0)), + CodeHash: common.HexToHash(""), + Nonce: 0x0, + StorageHash: common.Hash{}, + StorageProof: []rpctypes.StorageResult{ + { + Key: "0x0", + Value: (*hexutil.Big)(big.NewInt(2)), + Proof: []string{""}, + }, + }, + }, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() + tc.registerMock(*tc.blockNrOrHash.BlockNumber, tc.addr) + + accRes, err := suite.backend.GetProof(tc.addr, tc.storageKeys, tc.blockNrOrHash) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(tc.expAccRes, accRes) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestGetStorageAt() { + blockNr := rpctypes.NewBlockNumber(big.NewInt(1)) + + testCases := []struct { + name string + addr common.Address + key string + blockNrOrHash rpctypes.BlockNumberOrHash + registerMock func(common.Address, string, string) + expPass bool + expStorage hexutil.Bytes + }{ + { + "fail - BlockHash and BlockNumber are both nil", + tests.GenerateAddress(), + "0x0", + rpctypes.BlockNumberOrHash{}, + func(addr common.Address, key string, storage string) {}, + false, + nil, + }, + { + "fail - query client errors on getting Storage", + tests.GenerateAddress(), + "0x0", + rpctypes.BlockNumberOrHash{BlockNumber: &blockNr}, + func(addr common.Address, key string, storage string) { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterStorageAtError(queryClient, addr, key) + }, + false, + nil, + }, + { + "pass", + tests.GenerateAddress(), + "0x0", + rpctypes.BlockNumberOrHash{BlockNumber: &blockNr}, + func(addr common.Address, key string, storage string) { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterStorageAt(queryClient, addr, key, storage) + }, + true, + hexutil.Bytes{ + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + }, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() + tc.registerMock(tc.addr, tc.key, tc.expStorage.String()) + + storage, err := suite.backend.GetStorageAt(tc.addr, tc.key, tc.blockNrOrHash) + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(tc.expStorage, storage) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestGetBalance() { + blockNr := rpctypes.NewBlockNumber(big.NewInt(1)) + + testCases := []struct { + name string + addr common.Address + blockNrOrHash rpctypes.BlockNumberOrHash + registerMock func(rpctypes.BlockNumber, common.Address) + expPass bool + expBalance *hexutil.Big + }{ + { + "fail - BlockHash and BlockNumber are both nil", + tests.GenerateAddress(), + rpctypes.BlockNumberOrHash{}, + func(bn rpctypes.BlockNumber, addr common.Address) { + }, + false, + nil, + }, + { + "fail - tendermint client failed to get block", + tests.GenerateAddress(), + rpctypes.BlockNumberOrHash{BlockNumber: &blockNr}, + func(bn rpctypes.BlockNumber, addr common.Address) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockError(client, bn.Int64()) + }, + false, + nil, + }, + { + "fail - query client failed to get balance", + tests.GenerateAddress(), + rpctypes.BlockNumberOrHash{BlockNumber: &blockNr}, + func(bn rpctypes.BlockNumber, addr common.Address) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlock(client, bn.Int64(), nil) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBalanceError(queryClient, addr, bn.Int64()) + }, + false, + nil, + }, + { + "fail - invalid balance", + tests.GenerateAddress(), + rpctypes.BlockNumberOrHash{BlockNumber: &blockNr}, + func(bn rpctypes.BlockNumber, addr common.Address) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlock(client, bn.Int64(), nil) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBalanceInvalid(queryClient, addr, bn.Int64()) + }, + false, + nil, + }, + { + "fail - pruned node state", + tests.GenerateAddress(), + rpctypes.BlockNumberOrHash{BlockNumber: &blockNr}, + func(bn rpctypes.BlockNumber, addr common.Address) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlock(client, bn.Int64(), nil) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBalanceNegative(queryClient, addr, bn.Int64()) + }, + false, + nil, + }, + { + "pass", + tests.GenerateAddress(), + rpctypes.BlockNumberOrHash{BlockNumber: &blockNr}, + func(bn rpctypes.BlockNumber, addr common.Address) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlock(client, bn.Int64(), nil) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBalance(queryClient, addr, bn.Int64()) + }, + true, + (*hexutil.Big)(big.NewInt(1)), + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() + + // avoid nil pointer reference + if tc.blockNrOrHash.BlockNumber != nil { + tc.registerMock(*tc.blockNrOrHash.BlockNumber, tc.addr) + } + + balance, err := suite.backend.GetBalance(tc.addr, tc.blockNrOrHash) + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(tc.expBalance, balance) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestGetTransactionCount() { + testCases := []struct { + name string + accExists bool + blockNum rpctypes.BlockNumber + registerMock func(common.Address, rpctypes.BlockNumber) + expPass bool + expTxCount hexutil.Uint64 + }{ + { + "pass - account doesn't exist", + false, + rpctypes.NewBlockNumber(big.NewInt(1)), + func(addr common.Address, bn rpctypes.BlockNumber) { + var header metadata.MD + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterParams(queryClient, &header, 1) + }, + true, + hexutil.Uint64(0), + }, + { + "fail - block height is in the future", + false, + rpctypes.NewBlockNumber(big.NewInt(10000)), + func(addr common.Address, bn rpctypes.BlockNumber) { + var header metadata.MD + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterParams(queryClient, &header, 1) + }, + false, + hexutil.Uint64(0), + }, + // TODO (https://github.com/zeta-chain/node/issues/2302): Error mocking the GetAccount call - problem with Any type + //{ + // "pass - returns the number of transactions at the given address up to the given block number", + // true, + // rpctypes.NewBlockNumber(big.NewInt(1)), + // func(addr common.Address, bn rpctypes.BlockNumber) { + // client := suite.backend.clientCtx.Client.(*mocks.Client) + // account, err := suite.backend.clientCtx.AccountRetriever.GetAccount(suite.backend.clientCtx, suite.acc) + // suite.Require().NoError(err) + // request := &authtypes.QueryAccountRequest{Address: sdk.AccAddress(suite.acc.Bytes()).String()} + // requestMarshal, _ := request.Marshal() + // RegisterABCIQueryAccount( + // client, + // requestMarshal, + // tmrpcclient.ABCIQueryOptions{Height: int64(1), Prove: false}, + // account, + // ) + // }, + // true, + // hexutil.Uint64(0), + //}, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() + + addr := tests.GenerateAddress() + if tc.accExists { + addr = common.BytesToAddress(suite.acc.Bytes()) + } + + tc.registerMock(addr, tc.blockNum) + + txCount, err := suite.backend.GetTransactionCount(addr, tc.blockNum) + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(tc.expTxCount, *txCount) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/rpc/backend/backend_suite_test.go b/rpc/backend/backend_suite_test.go new file mode 100644 index 0000000000..0194d5902f --- /dev/null +++ b/rpc/backend/backend_suite_test.go @@ -0,0 +1,199 @@ +package backend + +import ( + "bufio" + "math/big" + "os" + "path/filepath" + "testing" + + dbm "github.com/cometbft/cometbft-db" + tmrpctypes "github.com/cometbft/cometbft/rpc/core/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/server" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/evmos/ethermint/app" + "github.com/evmos/ethermint/crypto/ethsecp256k1" + "github.com/evmos/ethermint/crypto/hd" + "github.com/evmos/ethermint/encoding" + "github.com/evmos/ethermint/indexer" + "github.com/evmos/ethermint/tests" + evmtypes "github.com/evmos/ethermint/x/evm/types" + "github.com/stretchr/testify/suite" + + "github.com/zeta-chain/zetacore/rpc/backend/mocks" + rpctypes "github.com/zeta-chain/zetacore/rpc/types" +) + +type BackendTestSuite struct { + suite.Suite + backend *Backend + acc sdk.AccAddress + signer keyring.Signer +} + +func TestBackendTestSuite(t *testing.T) { + suite.Run(t, new(BackendTestSuite)) +} + +const ChainID = "zetachain_7001-1" + +// SetupTest is executed before every BackendTestSuite test +func (suite *BackendTestSuite) SetupTest() { + ctx := server.NewDefaultContext() + ctx.Viper.Set("telemetry.global-labels", []interface{}{}) + + baseDir := suite.T().TempDir() + nodeDirName := "node" + clientDir := filepath.Join(baseDir, nodeDirName, "evmoscli") + keyRing, err := suite.generateTestKeyring(clientDir) + if err != nil { + panic(err) + } + + // Create Account with set sequence + suite.acc = sdk.AccAddress(tests.GenerateAddress().Bytes()) + accounts := map[string]client.TestAccount{} + accounts[suite.acc.String()] = client.TestAccount{ + Address: suite.acc, + Num: uint64(1), + Seq: uint64(1), + } + + priv, err := ethsecp256k1.GenerateKey() + suite.signer = tests.NewSigner(priv) + suite.Require().NoError(err) + + encodingConfig := encoding.MakeConfig(app.ModuleBasics) + clientCtx := client.Context{}.WithChainID(ChainID). + WithHeight(1). + WithTxConfig(encodingConfig.TxConfig). + WithKeyringDir(clientDir). + WithKeyring(keyRing). + WithAccountRetriever(client.TestAccountRetriever{Accounts: accounts}) + + allowUnprotectedTxs := false + idxer := indexer.NewKVIndexer(dbm.NewMemDB(), ctx.Logger, clientCtx) + + suite.backend = NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs, idxer) + suite.backend.queryClient.QueryClient = mocks.NewEVMQueryClient(suite.T()) + suite.backend.clientCtx.Client = mocks.NewClient(suite.T()) + suite.backend.queryClient.FeeMarket = mocks.NewFeeMarketQueryClient(suite.T()) + suite.backend.ctx = rpctypes.ContextWithHeight(1) + + // Add codec + encCfg := encoding.MakeConfig(app.ModuleBasics) + suite.backend.clientCtx.Codec = encCfg.Codec +} + +// buildEthereumTx returns an example legacy Ethereum transaction +func (suite *BackendTestSuite) buildEthereumTx() (*evmtypes.MsgEthereumTx, []byte) { + msgEthereumTx := evmtypes.NewTx( + suite.backend.chainID, + uint64(0), + &common.Address{}, + big.NewInt(0), + 100000, + big.NewInt(1), + nil, + nil, + nil, + nil, + ) + suite.signAndEncodeEthTx(msgEthereumTx) + // A valid msg should have empty `From` + msgEthereumTx.From = "" + + txBuilder := suite.backend.clientCtx.TxConfig.NewTxBuilder() + txBuilder.SetSignatures() + err := txBuilder.SetMsgs(msgEthereumTx) + suite.Require().NoError(err) + + bz, err := suite.backend.clientCtx.TxConfig.TxEncoder()(txBuilder.GetTx()) + suite.Require().NoError(err) + return msgEthereumTx, bz +} + +// buildFormattedBlock returns a formatted block for testing +func (suite *BackendTestSuite) buildFormattedBlock( + blockRes *tmrpctypes.ResultBlockResults, + resBlock *tmrpctypes.ResultBlock, + fullTx bool, + tx *evmtypes.MsgEthereumTx, + validator sdk.AccAddress, + baseFee *big.Int, +) map[string]interface{} { + header := resBlock.Block.Header + gasLimit := int64(^uint32(0)) // for `MaxGas = -1` (DefaultConsensusParams) + gasUsed := new(big.Int).SetUint64(uint64(blockRes.TxsResults[0].GasUsed)) + + root := common.Hash{}.Bytes() + receipt := ethtypes.NewReceipt(root, false, gasUsed.Uint64()) + bloom := ethtypes.CreateBloom(ethtypes.Receipts{receipt}) + + ethRPCTxs := []interface{}{} + if tx != nil { + if fullTx { + rpcTx, err := rpctypes.NewRPCTransaction( + tx.AsTransaction(), + common.BytesToHash(header.Hash()), + uint64(header.Height), + uint64(0), + baseFee, + suite.backend.chainID, + ) + suite.Require().NoError(err) + ethRPCTxs = []interface{}{rpcTx} + } else { + ethRPCTxs = []interface{}{common.HexToHash(tx.Hash)} + } + } + + return rpctypes.FormatBlock( + header, + resBlock.Block.Size(), + gasLimit, + gasUsed, + ethRPCTxs, + bloom, + common.BytesToAddress(validator.Bytes()), + baseFee, + ) +} + +func (suite *BackendTestSuite) generateTestKeyring(clientDir string) (keyring.Keyring, error) { + buf := bufio.NewReader(os.Stdin) + encCfg := encoding.MakeConfig(app.ModuleBasics) + return keyring.New( + sdk.KeyringServiceName(), + keyring.BackendTest, + clientDir, + buf, + encCfg.Codec, + []keyring.Option{hd.EthSecp256k1Option()}...) +} + +func (suite *BackendTestSuite) signAndEncodeEthTx(msgEthereumTx *evmtypes.MsgEthereumTx) []byte { + from, priv := tests.NewAddrKey() + signer := tests.NewSigner(priv) + + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterParamsWithoutHeader(queryClient, 1) + + ethSigner := ethtypes.LatestSigner(suite.backend.ChainConfig()) + msgEthereumTx.From = from.String() + err := msgEthereumTx.Sign(ethSigner, signer) + suite.Require().NoError(err) + + tx, err := msgEthereumTx.BuildTx(suite.backend.clientCtx.TxConfig.NewTxBuilder(), "azeta") + suite.Require().NoError(err) + + txEncoder := suite.backend.clientCtx.TxConfig.TxEncoder() + txBz, err := txEncoder(tx) + suite.Require().NoError(err) + + return txBz +} diff --git a/rpc/backend/blocks_test.go b/rpc/backend/blocks_test.go new file mode 100644 index 0000000000..218680ebe5 --- /dev/null +++ b/rpc/backend/blocks_test.go @@ -0,0 +1,1615 @@ +package backend + +import ( + "fmt" + "math/big" + + sdkmath "cosmossdk.io/math" + "github.com/cometbft/cometbft/abci/types" + tmrpctypes "github.com/cometbft/cometbft/rpc/core/types" + tmtypes "github.com/cometbft/cometbft/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/trie" + "github.com/evmos/ethermint/tests" + evmtypes "github.com/evmos/ethermint/x/evm/types" + "google.golang.org/grpc/metadata" + + "github.com/zeta-chain/zetacore/rpc/backend/mocks" + ethrpc "github.com/zeta-chain/zetacore/rpc/types" +) + +func (suite *BackendTestSuite) TestBlockNumber() { + testCases := []struct { + name string + registerMock func() + expBlockNumber hexutil.Uint64 + expPass bool + }{ + { + "fail - invalid block header height", + func() { + var header metadata.MD + height := int64(1) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterParamsInvalidHeight(queryClient, &header, int64(height)) + }, + 0x0, + false, + }, + { + "fail - invalid block header", + func() { + var header metadata.MD + height := int64(1) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterParamsInvalidHeader(queryClient, &header, int64(height)) + }, + 0x0, + false, + }, + { + "pass - app state header height 1", + func() { + var header metadata.MD + height := int64(1) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterParams(queryClient, &header, int64(height)) + }, + 0x1, + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock() + + blockNumber, err := suite.backend.BlockNumber() + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(tc.expBlockNumber, blockNumber) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestGetBlockByNumber() { + var ( + blockRes *tmrpctypes.ResultBlockResults + resBlock *tmrpctypes.ResultBlock + ) + msgEthereumTx, bz := suite.buildEthereumTx() + testCases := []struct { + name string + blockNumber ethrpc.BlockNumber + fullTx bool + baseFee *big.Int + validator sdk.AccAddress + tx *evmtypes.MsgEthereumTx + txBz []byte + registerMock func(ethrpc.BlockNumber, sdkmath.Int, sdk.AccAddress, []byte) + expNoop bool + expPass bool + }{ + { + "pass - tendermint block not found", + ethrpc.BlockNumber(1), + true, + sdk.NewInt(1).BigInt(), + sdk.AccAddress(tests.GenerateAddress().Bytes()), + nil, + nil, + func(blockNum ethrpc.BlockNumber, _ sdkmath.Int, _ sdk.AccAddress, _ []byte) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockError(client, height) + }, + true, + true, + }, + { + "pass - block not found (e.g. request block height that is greater than current one)", + ethrpc.BlockNumber(1), + true, + sdk.NewInt(1).BigInt(), + sdk.AccAddress(tests.GenerateAddress().Bytes()), + nil, + nil, + func(blockNum ethrpc.BlockNumber, baseFee sdkmath.Int, validator sdk.AccAddress, txBz []byte) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + resBlock, _ = RegisterBlockNotFound(client, height) + }, + true, + true, + }, + { + "pass - block results error", + ethrpc.BlockNumber(1), + true, + sdk.NewInt(1).BigInt(), + sdk.AccAddress(tests.GenerateAddress().Bytes()), + nil, + nil, + func(blockNum ethrpc.BlockNumber, baseFee sdkmath.Int, validator sdk.AccAddress, txBz []byte) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + resBlock, _ = RegisterBlock(client, height, txBz) + RegisterBlockResultsError(client, blockNum.Int64()) + }, + true, + true, + }, + { + "pass - without tx", + ethrpc.BlockNumber(1), + true, + sdk.NewInt(1).BigInt(), + sdk.AccAddress(tests.GenerateAddress().Bytes()), + nil, + nil, + func(blockNum ethrpc.BlockNumber, baseFee sdkmath.Int, validator sdk.AccAddress, txBz []byte) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + resBlock, _ = RegisterBlock(client, height, txBz) + blockRes, _ = RegisterBlockResults(client, blockNum.Int64()) + RegisterConsensusParams(client, height) + + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFee(queryClient, baseFee) + RegisterValidatorAccount(queryClient, validator) + }, + false, + true, + }, + { + "pass - with tx", + ethrpc.BlockNumber(1), + true, + sdk.NewInt(1).BigInt(), + sdk.AccAddress(tests.GenerateAddress().Bytes()), + msgEthereumTx, + bz, + func(blockNum ethrpc.BlockNumber, baseFee sdkmath.Int, validator sdk.AccAddress, txBz []byte) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + resBlock, _ = RegisterBlock(client, height, txBz) + blockRes, _ = RegisterBlockResults(client, blockNum.Int64()) + RegisterConsensusParams(client, height) + + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + + RegisterBaseFee(queryClient, baseFee) + RegisterValidatorAccount(queryClient, validator) + }, + false, + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock(tc.blockNumber, sdk.NewIntFromBigInt(tc.baseFee), tc.validator, tc.txBz) + + block, err := suite.backend.GetBlockByNumber(tc.blockNumber, tc.fullTx) + + if tc.expPass { + if tc.expNoop { + suite.Require().Nil(block) + } else { + expBlock := suite.buildFormattedBlock( + blockRes, + resBlock, + tc.fullTx, + tc.tx, + tc.validator, + tc.baseFee, + ) + suite.Require().Equal(expBlock, block) + } + suite.Require().NoError(err) + + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestGetBlockByHash() { + var ( + blockRes *tmrpctypes.ResultBlockResults + resBlock *tmrpctypes.ResultBlock + ) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterParamsWithoutHeader(queryClient, 1) + msgEthereumTx, bz := suite.buildEthereumTx() + + block := tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil) + + testCases := []struct { + name string + hash common.Hash + fullTx bool + baseFee *big.Int + validator sdk.AccAddress + tx *evmtypes.MsgEthereumTx + txBz []byte + registerMock func(common.Hash, sdkmath.Int, sdk.AccAddress, []byte) + expNoop bool + expPass bool + }{ + { + "fail - tendermint failed to get block", + common.BytesToHash(block.Hash()), + true, + sdk.NewInt(1).BigInt(), + sdk.AccAddress(tests.GenerateAddress().Bytes()), + nil, + nil, + func(hash common.Hash, baseFee sdkmath.Int, validator sdk.AccAddress, txBz []byte) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHashError(client, hash, txBz) + }, + false, + false, + }, + { + "noop - tendermint blockres not found", + common.BytesToHash(block.Hash()), + true, + sdk.NewInt(1).BigInt(), + sdk.AccAddress(tests.GenerateAddress().Bytes()), + nil, + nil, + func(hash common.Hash, baseFee sdkmath.Int, validator sdk.AccAddress, txBz []byte) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHashNotFound(client, hash, txBz) + }, + true, + true, + }, + { + "noop - tendermint failed to fetch block result", + common.BytesToHash(block.Hash()), + true, + sdk.NewInt(1).BigInt(), + sdk.AccAddress(tests.GenerateAddress().Bytes()), + nil, + nil, + func(hash common.Hash, baseFee sdkmath.Int, validator sdk.AccAddress, txBz []byte) { + height := int64(1) + client := suite.backend.clientCtx.Client.(*mocks.Client) + resBlock, _ = RegisterBlockByHash(client, hash, txBz) + + RegisterBlockResultsError(client, height) + }, + true, + true, + }, + { + "pass - without tx", + common.BytesToHash(block.Hash()), + true, + sdk.NewInt(1).BigInt(), + sdk.AccAddress(tests.GenerateAddress().Bytes()), + nil, + nil, + func(hash common.Hash, baseFee sdkmath.Int, validator sdk.AccAddress, txBz []byte) { + height := int64(1) + client := suite.backend.clientCtx.Client.(*mocks.Client) + resBlock, _ = RegisterBlockByHash(client, hash, txBz) + + blockRes, _ = RegisterBlockResults(client, height) + RegisterConsensusParams(client, height) + + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFee(queryClient, baseFee) + RegisterValidatorAccount(queryClient, validator) + }, + false, + true, + }, + { + "pass - with tx", + common.BytesToHash(block.Hash()), + true, + sdk.NewInt(1).BigInt(), + sdk.AccAddress(tests.GenerateAddress().Bytes()), + msgEthereumTx, + bz, + func(hash common.Hash, baseFee sdkmath.Int, validator sdk.AccAddress, txBz []byte) { + height := int64(1) + client := suite.backend.clientCtx.Client.(*mocks.Client) + resBlock, _ = RegisterBlockByHash(client, hash, txBz) + + blockRes, _ = RegisterBlockResults(client, height) + RegisterConsensusParams(client, height) + + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFee(queryClient, baseFee) + RegisterValidatorAccount(queryClient, validator) + }, + false, + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock(tc.hash, sdk.NewIntFromBigInt(tc.baseFee), tc.validator, tc.txBz) + + block, err := suite.backend.GetBlockByHash(tc.hash, tc.fullTx) + + if tc.expPass { + if tc.expNoop { + suite.Require().Nil(block) + } else { + expBlock := suite.buildFormattedBlock( + blockRes, + resBlock, + tc.fullTx, + tc.tx, + tc.validator, + tc.baseFee, + ) + suite.Require().Equal(expBlock, block) + } + suite.Require().NoError(err) + + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestGetBlockTransactionCountByHash() { + _, bz := suite.buildEthereumTx() + block := tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil) + emptyBlock := tmtypes.MakeBlock(1, []tmtypes.Tx{}, nil, nil) + + testCases := []struct { + name string + hash common.Hash + registerMock func(common.Hash) + expCount hexutil.Uint + expPass bool + }{ + { + "fail - block not found", + common.BytesToHash(emptyBlock.Hash()), + func(hash common.Hash) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHashError(client, hash, nil) + }, + hexutil.Uint(0), + false, + }, + { + "fail - tendermint client failed to get block result", + common.BytesToHash(emptyBlock.Hash()), + func(hash common.Hash) { + height := int64(1) + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHash(client, hash, nil) + RegisterBlockResultsError(client, height) + }, + hexutil.Uint(0), + false, + }, + { + "pass - block without tx", + common.BytesToHash(emptyBlock.Hash()), + func(hash common.Hash) { + height := int64(1) + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHash(client, hash, nil) + RegisterBlockResults(client, height) + }, + hexutil.Uint(0), + true, + }, + { + "pass - block with tx", + common.BytesToHash(block.Hash()), + func(hash common.Hash) { + height := int64(1) + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHash(client, hash, bz) + RegisterBlockResults(client, height) + }, + hexutil.Uint(1), + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + + tc.registerMock(tc.hash) + count := suite.backend.GetBlockTransactionCountByHash(tc.hash) + if tc.expPass { + suite.Require().Equal(tc.expCount, *count) + } else { + suite.Require().Nil(count) + } + }) + } +} + +func (suite *BackendTestSuite) TestGetBlockTransactionCountByNumber() { + _, bz := suite.buildEthereumTx() + block := tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil) + emptyBlock := tmtypes.MakeBlock(1, []tmtypes.Tx{}, nil, nil) + + testCases := []struct { + name string + blockNum ethrpc.BlockNumber + registerMock func(ethrpc.BlockNumber) + expCount hexutil.Uint + expPass bool + }{ + { + "fail - block not found", + ethrpc.BlockNumber(emptyBlock.Height), + func(blockNum ethrpc.BlockNumber) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockError(client, height) + }, + hexutil.Uint(0), + false, + }, + { + "fail - tendermint client failed to get block result", + ethrpc.BlockNumber(emptyBlock.Height), + func(blockNum ethrpc.BlockNumber) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlock(client, height, nil) + RegisterBlockResultsError(client, height) + }, + hexutil.Uint(0), + false, + }, + { + "pass - block without tx", + ethrpc.BlockNumber(emptyBlock.Height), + func(blockNum ethrpc.BlockNumber) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlock(client, height, nil) + RegisterBlockResults(client, height) + }, + hexutil.Uint(0), + true, + }, + { + "pass - block with tx", + ethrpc.BlockNumber(block.Height), + func(blockNum ethrpc.BlockNumber) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlock(client, height, bz) + RegisterBlockResults(client, height) + }, + hexutil.Uint(1), + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + + tc.registerMock(tc.blockNum) + count := suite.backend.GetBlockTransactionCountByNumber(tc.blockNum) + if tc.expPass { + suite.Require().Equal(tc.expCount, *count) + } else { + suite.Require().Nil(count) + } + }) + } +} + +func (suite *BackendTestSuite) TestTendermintBlockByNumber() { + var expResultBlock *tmrpctypes.ResultBlock + + testCases := []struct { + name string + blockNumber ethrpc.BlockNumber + registerMock func(ethrpc.BlockNumber) + found bool + expPass bool + }{ + { + "fail - client error", + ethrpc.BlockNumber(1), + func(blockNum ethrpc.BlockNumber) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockError(client, height) + }, + false, + false, + }, + { + "noop - block not found", + ethrpc.BlockNumber(1), + func(blockNum ethrpc.BlockNumber) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockNotFound(client, height) + }, + false, + true, + }, + { + "fail - blockNum < 0 with app state height error", + ethrpc.BlockNumber(-1), + func(_ ethrpc.BlockNumber) { + var header metadata.MD + appHeight := int64(1) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterParamsError(queryClient, &header, appHeight) + }, + false, + false, + }, + { + "pass - blockNum < 0 with app state height >= 1", + ethrpc.BlockNumber(-1), + func(blockNum ethrpc.BlockNumber) { + var header metadata.MD + appHeight := int64(1) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterParams(queryClient, &header, appHeight) + + tmHeight := appHeight + client := suite.backend.clientCtx.Client.(*mocks.Client) + expResultBlock, _ = RegisterBlock(client, tmHeight, nil) + }, + true, + true, + }, + { + "pass - blockNum = 0 (defaults to blockNum = 1 due to a difference between tendermint heights and geth heights", + ethrpc.BlockNumber(0), + func(blockNum ethrpc.BlockNumber) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + expResultBlock, _ = RegisterBlock(client, height, nil) + }, + true, + true, + }, + { + "pass - blockNum = 1", + ethrpc.BlockNumber(1), + func(blockNum ethrpc.BlockNumber) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + expResultBlock, _ = RegisterBlock(client, height, nil) + }, + true, + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + + tc.registerMock(tc.blockNumber) + resultBlock, err := suite.backend.TendermintBlockByNumber(tc.blockNumber) + + if tc.expPass { + suite.Require().NoError(err) + + if !tc.found { + suite.Require().Nil(resultBlock) + } else { + suite.Require().Equal(expResultBlock, resultBlock) + suite.Require().Equal(expResultBlock.Block.Header.Height, resultBlock.Block.Header.Height) + } + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestTendermintBlockResultByNumber() { + var expBlockRes *tmrpctypes.ResultBlockResults + + testCases := []struct { + name string + blockNumber int64 + registerMock func(int64) + expPass bool + }{ + { + "fail", + 1, + func(blockNum int64) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockResultsError(client, blockNum) + }, + false, + }, + { + "pass", + 1, + func(blockNum int64) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockResults(client, blockNum) + + expBlockRes = &tmrpctypes.ResultBlockResults{ + Height: blockNum, + TxsResults: []*types.ResponseDeliverTx{{Code: 0, GasUsed: 0}}, + } + }, + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock(tc.blockNumber) + + blockRes, err := suite.backend.TendermintBlockResultByNumber(&tc.blockNumber) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(expBlockRes, blockRes) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestBlockNumberFromTendermint() { + var resBlock *tmrpctypes.ResultBlock + + _, bz := suite.buildEthereumTx() + block := tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil) + blockNum := ethrpc.NewBlockNumber(big.NewInt(block.Height)) + blockHash := common.BytesToHash(block.Hash()) + + testCases := []struct { + name string + blockNum *ethrpc.BlockNumber + hash *common.Hash + registerMock func(*common.Hash) + expPass bool + }{ + { + "error - without blockHash or blockNum", + nil, + nil, + func(hash *common.Hash) {}, + false, + }, + { + "error - with blockHash, tendermint client failed to get block", + nil, + &blockHash, + func(hash *common.Hash) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHashError(client, *hash, bz) + }, + false, + }, + { + "pass - with blockHash", + nil, + &blockHash, + func(hash *common.Hash) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + resBlock, _ = RegisterBlockByHash(client, *hash, bz) + }, + true, + }, + { + "pass - without blockHash & with blockNumber", + &blockNum, + nil, + func(hash *common.Hash) {}, + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + + blockNrOrHash := ethrpc.BlockNumberOrHash{ + BlockNumber: tc.blockNum, + BlockHash: tc.hash, + } + + tc.registerMock(tc.hash) + blockNum, err := suite.backend.BlockNumberFromTendermint(blockNrOrHash) + + if tc.expPass { + suite.Require().NoError(err) + if tc.hash == nil { + suite.Require().Equal(*tc.blockNum, blockNum) + } else { + expHeight := ethrpc.NewBlockNumber(big.NewInt(resBlock.Block.Height)) + suite.Require().Equal(expHeight, blockNum) + } + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestBlockNumberFromTendermintByHash() { + var resBlock *tmrpctypes.ResultBlock + + _, bz := suite.buildEthereumTx() + block := tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil) + emptyBlock := tmtypes.MakeBlock(1, []tmtypes.Tx{}, nil, nil) + + testCases := []struct { + name string + hash common.Hash + registerMock func(common.Hash) + expPass bool + }{ + { + "fail - tendermint client failed to get block", + common.BytesToHash(block.Hash()), + func(hash common.Hash) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHashError(client, hash, bz) + }, + false, + }, + { + "pass - block without tx", + common.BytesToHash(emptyBlock.Hash()), + func(hash common.Hash) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + resBlock, _ = RegisterBlockByHash(client, hash, bz) + }, + true, + }, + { + "pass - block with tx", + common.BytesToHash(block.Hash()), + func(hash common.Hash) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + resBlock, _ = RegisterBlockByHash(client, hash, bz) + }, + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + + tc.registerMock(tc.hash) + blockNum, err := suite.backend.BlockNumberFromTendermintByHash(tc.hash) + if tc.expPass { + expHeight := big.NewInt(resBlock.Block.Height) + suite.Require().NoError(err) + suite.Require().Equal(expHeight, blockNum) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestBlockBloom() { + testCases := []struct { + name string + blockRes *tmrpctypes.ResultBlockResults + expBlockBloom ethtypes.Bloom + expPass bool + }{ + { + "fail - empty block result", + &tmrpctypes.ResultBlockResults{}, + ethtypes.Bloom{}, + false, + }, + { + "fail - non block bloom event type", + &tmrpctypes.ResultBlockResults{ + EndBlockEvents: []types.Event{{Type: evmtypes.EventTypeEthereumTx}}, + }, + ethtypes.Bloom{}, + false, + }, + { + "fail - nonblock bloom attribute key", + &tmrpctypes.ResultBlockResults{ + EndBlockEvents: []types.Event{ + { + Type: evmtypes.EventTypeBlockBloom, + Attributes: []types.EventAttribute{ + {Key: string(evmtypes.AttributeKeyEthereumTxHash)}, + }, + }, + }, + }, + ethtypes.Bloom{}, + false, + }, + { + "pass - block bloom attribute key", + &tmrpctypes.ResultBlockResults{ + EndBlockEvents: []types.Event{ + { + Type: evmtypes.EventTypeBlockBloom, + Attributes: []types.EventAttribute{ + {Key: string(bAttributeKeyEthereumBloom)}, + }, + }, + }, + }, + ethtypes.Bloom{}, + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + blockBloom, err := suite.backend.BlockBloom(tc.blockRes) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(tc.expBlockBloom, blockBloom) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestGetEthBlockFromTendermint() { + msgEthereumTx, bz := suite.buildEthereumTx() + emptyBlock := tmtypes.MakeBlock(1, []tmtypes.Tx{}, nil, nil) + + testCases := []struct { + name string + baseFee *big.Int + validator sdk.AccAddress + height int64 + resBlock *tmrpctypes.ResultBlock + blockRes *tmrpctypes.ResultBlockResults + fullTx bool + registerMock func(sdkmath.Int, sdk.AccAddress, int64) + expTxs bool + expPass bool + }{ + { + "pass - block without tx", + sdk.NewInt(1).BigInt(), + sdk.AccAddress(common.Address{}.Bytes()), + int64(1), + &tmrpctypes.ResultBlock{Block: emptyBlock}, + &tmrpctypes.ResultBlockResults{ + Height: 1, + TxsResults: []*types.ResponseDeliverTx{{Code: 0, GasUsed: 0}}, + }, + false, + func(baseFee sdkmath.Int, validator sdk.AccAddress, height int64) { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFee(queryClient, baseFee) + RegisterValidatorAccount(queryClient, validator) + + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterConsensusParams(client, height) + }, + false, + true, + }, + { + "pass - block with tx - with BaseFee error", + nil, + sdk.AccAddress(tests.GenerateAddress().Bytes()), + int64(1), + &tmrpctypes.ResultBlock{ + Block: tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil), + }, + &tmrpctypes.ResultBlockResults{ + Height: 1, + TxsResults: []*types.ResponseDeliverTx{{Code: 0, GasUsed: 0}}, + }, + true, + func(baseFee sdkmath.Int, validator sdk.AccAddress, height int64) { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFeeError(queryClient) + RegisterValidatorAccount(queryClient, validator) + + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterConsensusParams(client, height) + }, + true, + true, + }, + { + "pass - block with tx - with ValidatorAccount error", + sdk.NewInt(1).BigInt(), + sdk.AccAddress(common.Address{}.Bytes()), + int64(1), + &tmrpctypes.ResultBlock{ + Block: tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil), + }, + &tmrpctypes.ResultBlockResults{ + Height: 1, + TxsResults: []*types.ResponseDeliverTx{{Code: 0, GasUsed: 0}}, + }, + true, + func(baseFee sdkmath.Int, validator sdk.AccAddress, height int64) { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFee(queryClient, baseFee) + RegisterValidatorAccountError(queryClient) + + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterConsensusParams(client, height) + }, + true, + true, + }, + { + "pass - block with tx - with ConsensusParams error - BlockMaxGas defaults to max uint32", + sdk.NewInt(1).BigInt(), + sdk.AccAddress(tests.GenerateAddress().Bytes()), + int64(1), + &tmrpctypes.ResultBlock{ + Block: tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil), + }, + &tmrpctypes.ResultBlockResults{ + Height: 1, + TxsResults: []*types.ResponseDeliverTx{{Code: 0, GasUsed: 0}}, + }, + true, + func(baseFee sdkmath.Int, validator sdk.AccAddress, height int64) { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFee(queryClient, baseFee) + RegisterValidatorAccount(queryClient, validator) + + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterConsensusParamsError(client, height) + }, + true, + true, + }, + { + "pass - block with tx - with ShouldIgnoreGasUsed - empty txs", + sdk.NewInt(1).BigInt(), + sdk.AccAddress(tests.GenerateAddress().Bytes()), + int64(1), + &tmrpctypes.ResultBlock{ + Block: tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil), + }, + &tmrpctypes.ResultBlockResults{ + Height: 1, + TxsResults: []*types.ResponseDeliverTx{ + { + Code: 11, + GasUsed: 0, + Log: "no block gas left to run tx: out of gas", + }, + }, + }, + true, + func(baseFee sdkmath.Int, validator sdk.AccAddress, height int64) { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFee(queryClient, baseFee) + RegisterValidatorAccount(queryClient, validator) + + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterConsensusParams(client, height) + }, + false, + true, + }, + { + "pass - block with tx - non fullTx", + sdk.NewInt(1).BigInt(), + sdk.AccAddress(tests.GenerateAddress().Bytes()), + int64(1), + &tmrpctypes.ResultBlock{ + Block: tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil), + }, + &tmrpctypes.ResultBlockResults{ + Height: 1, + TxsResults: []*types.ResponseDeliverTx{{Code: 0, GasUsed: 0}}, + }, + false, + func(baseFee sdkmath.Int, validator sdk.AccAddress, height int64) { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFee(queryClient, baseFee) + RegisterValidatorAccount(queryClient, validator) + + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterConsensusParams(client, height) + }, + true, + true, + }, + { + "pass - block with tx", + sdk.NewInt(1).BigInt(), + sdk.AccAddress(tests.GenerateAddress().Bytes()), + int64(1), + &tmrpctypes.ResultBlock{ + Block: tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil), + }, + &tmrpctypes.ResultBlockResults{ + Height: 1, + TxsResults: []*types.ResponseDeliverTx{{Code: 0, GasUsed: 0}}, + }, + true, + func(baseFee sdkmath.Int, validator sdk.AccAddress, height int64) { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFee(queryClient, baseFee) + RegisterValidatorAccount(queryClient, validator) + + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterConsensusParams(client, height) + }, + true, + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock(sdk.NewIntFromBigInt(tc.baseFee), tc.validator, tc.height) + + block, err := suite.backend.RPCBlockFromTendermintBlock(tc.resBlock, tc.blockRes, tc.fullTx) + + var expBlock map[string]interface{} + header := tc.resBlock.Block.Header + gasLimit := int64(^uint32(0)) // for `MaxGas = -1` (DefaultConsensusParams) + gasUsed := new(big.Int).SetUint64(uint64(tc.blockRes.TxsResults[0].GasUsed)) + + root := common.Hash{}.Bytes() + receipt := ethtypes.NewReceipt(root, false, gasUsed.Uint64()) + bloom := ethtypes.CreateBloom(ethtypes.Receipts{receipt}) + + ethRPCTxs := []interface{}{} + + if tc.expTxs { + if tc.fullTx { + rpcTx, err := ethrpc.NewRPCTransaction( + msgEthereumTx.AsTransaction(), + common.BytesToHash(header.Hash()), + uint64(header.Height), + uint64(0), + tc.baseFee, + suite.backend.chainID, + ) + suite.Require().NoError(err) + ethRPCTxs = []interface{}{rpcTx} + } else { + ethRPCTxs = []interface{}{common.HexToHash(msgEthereumTx.Hash)} + } + } + + expBlock = ethrpc.FormatBlock( + header, + tc.resBlock.Block.Size(), + gasLimit, + gasUsed, + ethRPCTxs, + bloom, + common.BytesToAddress(tc.validator.Bytes()), + tc.baseFee, + ) + + if tc.expPass { + suite.Require().Equal(expBlock, block) + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestEthMsgsFromTendermintBlock() { + msgEthereumTx, bz := suite.buildEthereumTx() + + testCases := []struct { + name string + resBlock *tmrpctypes.ResultBlock + blockRes *tmrpctypes.ResultBlockResults + expMsgs []*evmtypes.MsgEthereumTx + }{ + { + "tx in not included in block - unsuccessful tx without ExceedBlockGasLimit error", + &tmrpctypes.ResultBlock{ + Block: tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil), + }, + &tmrpctypes.ResultBlockResults{ + TxsResults: []*types.ResponseDeliverTx{ + { + Code: 1, + }, + }, + }, + []*evmtypes.MsgEthereumTx(nil), + }, + { + "tx included in block - unsuccessful tx with ExceedBlockGasLimit error", + &tmrpctypes.ResultBlock{ + Block: tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil), + }, + &tmrpctypes.ResultBlockResults{ + TxsResults: []*types.ResponseDeliverTx{ + { + Code: 1, + Log: ethrpc.ExceedBlockGasLimitError, + }, + }, + }, + []*evmtypes.MsgEthereumTx{msgEthereumTx}, + }, + { + "pass", + &tmrpctypes.ResultBlock{ + Block: tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil), + }, + &tmrpctypes.ResultBlockResults{ + TxsResults: []*types.ResponseDeliverTx{ + { + Code: 0, + Log: ethrpc.ExceedBlockGasLimitError, + }, + }, + }, + []*evmtypes.MsgEthereumTx{msgEthereumTx}, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + + msgs, _ := suite.backend.EthMsgsFromTendermintBlock(tc.resBlock, tc.blockRes) + suite.Require().Equal(tc.expMsgs, msgs) + }) + } +} + +func (suite *BackendTestSuite) TestHeaderByNumber() { + var expResultBlock *tmrpctypes.ResultBlock + + _, bz := suite.buildEthereumTx() + + testCases := []struct { + name string + blockNumber ethrpc.BlockNumber + baseFee *big.Int + registerMock func(ethrpc.BlockNumber, sdkmath.Int) + expPass bool + }{ + { + "fail - tendermint client failed to get block", + ethrpc.BlockNumber(1), + sdk.NewInt(1).BigInt(), + func(blockNum ethrpc.BlockNumber, baseFee sdkmath.Int) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockError(client, height) + }, + false, + }, + { + "fail - block not found for height", + ethrpc.BlockNumber(1), + sdk.NewInt(1).BigInt(), + func(blockNum ethrpc.BlockNumber, baseFee sdkmath.Int) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockNotFound(client, height) + }, + false, + }, + { + "fail - block not found for height", + ethrpc.BlockNumber(1), + sdk.NewInt(1).BigInt(), + func(blockNum ethrpc.BlockNumber, baseFee sdkmath.Int) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlock(client, height, nil) + RegisterBlockResultsError(client, height) + }, + false, + }, + { + "pass - without Base Fee, failed to fetch from prunned block", + ethrpc.BlockNumber(1), + nil, + func(blockNum ethrpc.BlockNumber, baseFee sdkmath.Int) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + expResultBlock, _ = RegisterBlock(client, height, nil) + RegisterBlockResults(client, height) + + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFeeError(queryClient) + }, + true, + }, + { + "pass - blockNum = 1, without tx", + ethrpc.BlockNumber(1), + sdk.NewInt(1).BigInt(), + func(blockNum ethrpc.BlockNumber, baseFee sdkmath.Int) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + expResultBlock, _ = RegisterBlock(client, height, nil) + RegisterBlockResults(client, height) + + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFee(queryClient, baseFee) + }, + true, + }, + { + "pass - blockNum = 1, with tx", + ethrpc.BlockNumber(1), + sdk.NewInt(1).BigInt(), + func(blockNum ethrpc.BlockNumber, baseFee sdkmath.Int) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + expResultBlock, _ = RegisterBlock(client, height, bz) + RegisterBlockResults(client, height) + + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFee(queryClient, baseFee) + }, + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + + tc.registerMock(tc.blockNumber, sdk.NewIntFromBigInt(tc.baseFee)) + header, err := suite.backend.HeaderByNumber(tc.blockNumber) + + if tc.expPass { + expHeader := ethrpc.EthHeaderFromTendermint(expResultBlock.Block.Header, ethtypes.Bloom{}, tc.baseFee) + suite.Require().NoError(err) + suite.Require().Equal(expHeader, header) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestHeaderByHash() { + var expResultBlock *tmrpctypes.ResultBlock + + _, bz := suite.buildEthereumTx() + block := tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil) + emptyBlock := tmtypes.MakeBlock(1, []tmtypes.Tx{}, nil, nil) + + testCases := []struct { + name string + hash common.Hash + baseFee *big.Int + registerMock func(common.Hash, sdkmath.Int) + expPass bool + }{ + { + "fail - tendermint client failed to get block", + common.BytesToHash(block.Hash()), + sdk.NewInt(1).BigInt(), + func(hash common.Hash, baseFee sdkmath.Int) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHashError(client, hash, bz) + }, + false, + }, + { + "fail - block not found for height", + common.BytesToHash(block.Hash()), + sdk.NewInt(1).BigInt(), + func(hash common.Hash, baseFee sdkmath.Int) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHashNotFound(client, hash, bz) + }, + false, + }, + { + "fail - block not found for height", + common.BytesToHash(block.Hash()), + sdk.NewInt(1).BigInt(), + func(hash common.Hash, baseFee sdkmath.Int) { + height := int64(1) + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHash(client, hash, bz) + RegisterBlockResultsError(client, height) + }, + false, + }, + { + "pass - without Base Fee, failed to fetch from prunned block", + common.BytesToHash(block.Hash()), + nil, + func(hash common.Hash, baseFee sdkmath.Int) { + height := int64(1) + client := suite.backend.clientCtx.Client.(*mocks.Client) + expResultBlock, _ = RegisterBlockByHash(client, hash, bz) + RegisterBlockResults(client, height) + + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFeeError(queryClient) + }, + true, + }, + { + "pass - blockNum = 1, without tx", + common.BytesToHash(emptyBlock.Hash()), + sdk.NewInt(1).BigInt(), + func(hash common.Hash, baseFee sdkmath.Int) { + height := int64(1) + client := suite.backend.clientCtx.Client.(*mocks.Client) + expResultBlock, _ = RegisterBlockByHash(client, hash, nil) + RegisterBlockResults(client, height) + + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFee(queryClient, baseFee) + }, + true, + }, + { + "pass - with tx", + common.BytesToHash(block.Hash()), + sdk.NewInt(1).BigInt(), + func(hash common.Hash, baseFee sdkmath.Int) { + height := int64(1) + client := suite.backend.clientCtx.Client.(*mocks.Client) + expResultBlock, _ = RegisterBlockByHash(client, hash, bz) + RegisterBlockResults(client, height) + + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFee(queryClient, baseFee) + }, + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + + tc.registerMock(tc.hash, sdk.NewIntFromBigInt(tc.baseFee)) + header, err := suite.backend.HeaderByHash(tc.hash) + + if tc.expPass { + expHeader := ethrpc.EthHeaderFromTendermint(expResultBlock.Block.Header, ethtypes.Bloom{}, tc.baseFee) + suite.Require().NoError(err) + suite.Require().Equal(expHeader, header) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestEthBlockByNumber() { + msgEthereumTx, bz := suite.buildEthereumTx() + emptyBlock := tmtypes.MakeBlock(1, []tmtypes.Tx{}, nil, nil) + + testCases := []struct { + name string + blockNumber ethrpc.BlockNumber + registerMock func(ethrpc.BlockNumber) + expEthBlock *ethtypes.Block + expPass bool + }{ + { + "fail - tendermint client failed to get block", + ethrpc.BlockNumber(1), + func(blockNum ethrpc.BlockNumber) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockError(client, height) + }, + nil, + false, + }, + { + "fail - block result not found for height", + ethrpc.BlockNumber(1), + func(blockNum ethrpc.BlockNumber) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlock(client, height, nil) + RegisterBlockResultsError(client, blockNum.Int64()) + }, + nil, + false, + }, + { + "pass - block without tx", + ethrpc.BlockNumber(1), + func(blockNum ethrpc.BlockNumber) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlock(client, height, nil) + + RegisterBlockResults(client, blockNum.Int64()) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + baseFee := sdk.NewInt(1) + RegisterBaseFee(queryClient, baseFee) + }, + ethtypes.NewBlock( + ethrpc.EthHeaderFromTendermint( + emptyBlock.Header, + ethtypes.Bloom{}, + sdk.NewInt(1).BigInt(), + ), + []*ethtypes.Transaction{}, + nil, + nil, + nil, + ), + true, + }, + { + "pass - block with tx", + ethrpc.BlockNumber(1), + func(blockNum ethrpc.BlockNumber) { + height := blockNum.Int64() + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlock(client, height, bz) + + RegisterBlockResults(client, blockNum.Int64()) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + baseFee := sdk.NewInt(1) + RegisterBaseFee(queryClient, baseFee) + }, + ethtypes.NewBlock( + ethrpc.EthHeaderFromTendermint( + emptyBlock.Header, + ethtypes.Bloom{}, + sdk.NewInt(1).BigInt(), + ), + []*ethtypes.Transaction{msgEthereumTx.AsTransaction()}, + nil, + nil, + trie.NewStackTrie(nil), + ), + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock(tc.blockNumber) + + ethBlock, err := suite.backend.EthBlockByNumber(tc.blockNumber) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(tc.expEthBlock.Header(), ethBlock.Header()) + suite.Require().Equal(tc.expEthBlock.Uncles(), ethBlock.Uncles()) + suite.Require().Equal(tc.expEthBlock.ReceiptHash(), ethBlock.ReceiptHash()) + for i, tx := range tc.expEthBlock.Transactions() { + suite.Require().Equal(tx.Data(), ethBlock.Transactions()[i].Data()) + } + + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestEthBlockFromTendermintBlock() { + msgEthereumTx, bz := suite.buildEthereumTx() + emptyBlock := tmtypes.MakeBlock(1, []tmtypes.Tx{}, nil, nil) + + testCases := []struct { + name string + baseFee *big.Int + resBlock *tmrpctypes.ResultBlock + blockRes *tmrpctypes.ResultBlockResults + registerMock func(sdkmath.Int, int64) + expEthBlock *ethtypes.Block + expPass bool + }{ + { + "pass - block without tx", + sdk.NewInt(1).BigInt(), + &tmrpctypes.ResultBlock{ + Block: emptyBlock, + }, + &tmrpctypes.ResultBlockResults{ + Height: 1, + TxsResults: []*types.ResponseDeliverTx{{Code: 0, GasUsed: 0}}, + }, + func(baseFee sdkmath.Int, blockNum int64) { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFee(queryClient, baseFee) + }, + ethtypes.NewBlock( + ethrpc.EthHeaderFromTendermint( + emptyBlock.Header, + ethtypes.Bloom{}, + sdk.NewInt(1).BigInt(), + ), + []*ethtypes.Transaction{}, + nil, + nil, + nil, + ), + true, + }, + { + "pass - block with tx", + sdk.NewInt(1).BigInt(), + &tmrpctypes.ResultBlock{ + Block: tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil), + }, + &tmrpctypes.ResultBlockResults{ + Height: 1, + TxsResults: []*types.ResponseDeliverTx{{Code: 0, GasUsed: 0}}, + EndBlockEvents: []types.Event{ + { + Type: evmtypes.EventTypeBlockBloom, + Attributes: []types.EventAttribute{ + {Key: string(bAttributeKeyEthereumBloom)}, + }, + }, + }, + }, + func(baseFee sdkmath.Int, blockNum int64) { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFee(queryClient, baseFee) + }, + ethtypes.NewBlock( + ethrpc.EthHeaderFromTendermint( + emptyBlock.Header, + ethtypes.Bloom{}, + sdk.NewInt(1).BigInt(), + ), + []*ethtypes.Transaction{msgEthereumTx.AsTransaction()}, + nil, + nil, + trie.NewStackTrie(nil), + ), + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock(sdk.NewIntFromBigInt(tc.baseFee), tc.blockRes.Height) + + ethBlock, err := suite.backend.EthBlockFromTendermintBlock(tc.resBlock, tc.blockRes) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(tc.expEthBlock.Header(), ethBlock.Header()) + suite.Require().Equal(tc.expEthBlock.Uncles(), ethBlock.Uncles()) + suite.Require().Equal(tc.expEthBlock.ReceiptHash(), ethBlock.ReceiptHash()) + for i, tx := range tc.expEthBlock.Transactions() { + suite.Require().Equal(tx.Data(), ethBlock.Transactions()[i].Data()) + } + + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/rpc/backend/call_tx_test.go b/rpc/backend/call_tx_test.go new file mode 100644 index 0000000000..e0c7158ceb --- /dev/null +++ b/rpc/backend/call_tx_test.go @@ -0,0 +1,504 @@ +package backend + +import ( + "encoding/json" + "fmt" + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/rlp" + "github.com/evmos/ethermint/tests" + evmtypes "github.com/evmos/ethermint/x/evm/types" + "google.golang.org/grpc/metadata" + + "github.com/zeta-chain/zetacore/rpc/backend/mocks" + rpctypes "github.com/zeta-chain/zetacore/rpc/types" +) + +func (suite *BackendTestSuite) TestResend() { + txNonce := (hexutil.Uint64)(1) + baseFee := sdk.NewInt(1) + gasPrice := new(hexutil.Big) + toAddr := tests.GenerateAddress() + chainID := (*hexutil.Big)(suite.backend.chainID) + callArgs := evmtypes.TransactionArgs{ + From: nil, + To: &toAddr, + Gas: nil, + GasPrice: nil, + MaxFeePerGas: gasPrice, + MaxPriorityFeePerGas: gasPrice, + Value: gasPrice, + Nonce: &txNonce, + Input: nil, + Data: nil, + AccessList: nil, + ChainID: chainID, + } + + testCases := []struct { + name string + registerMock func() + args evmtypes.TransactionArgs + gasPrice *hexutil.Big + gasLimit *hexutil.Uint64 + expHash common.Hash + expPass bool + }{ + { + "fail - Missing transaction nonce ", + func() {}, + evmtypes.TransactionArgs{ + Nonce: nil, + }, + nil, + nil, + common.Hash{}, + false, + }, + { + "pass - Can't set Tx defaults BaseFee disabled", + func() { + var header metadata.MD + client := suite.backend.clientCtx.Client.(*mocks.Client) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterParams(queryClient, &header, 1) + RegisterBlock(client, 1, nil) + RegisterBlockResults(client, 1) + RegisterBaseFeeDisabled(queryClient) + }, + evmtypes.TransactionArgs{ + Nonce: &txNonce, + ChainID: callArgs.ChainID, + }, + nil, + nil, + common.Hash{}, + true, + }, + { + "pass - Can't set Tx defaults ", + func() { + var header metadata.MD + client := suite.backend.clientCtx.Client.(*mocks.Client) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + feeMarketClient := suite.backend.queryClient.FeeMarket.(*mocks.FeeMarketQueryClient) + RegisterParams(queryClient, &header, 1) + RegisterFeeMarketParams(feeMarketClient, 1) + RegisterBlock(client, 1, nil) + RegisterBlockResults(client, 1) + RegisterBaseFee(queryClient, baseFee) + }, + evmtypes.TransactionArgs{ + Nonce: &txNonce, + }, + nil, + nil, + common.Hash{}, + true, + }, + { + "pass - MaxFeePerGas is nil", + func() { + var header metadata.MD + client := suite.backend.clientCtx.Client.(*mocks.Client) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterParams(queryClient, &header, 1) + RegisterBlock(client, 1, nil) + RegisterBlockResults(client, 1) + RegisterBaseFeeDisabled(queryClient) + }, + evmtypes.TransactionArgs{ + Nonce: &txNonce, + MaxPriorityFeePerGas: nil, + GasPrice: nil, + MaxFeePerGas: nil, + }, + nil, + nil, + common.Hash{}, + true, + }, + { + "fail - GasPrice and (MaxFeePerGas or MaxPriorityPerGas specified", + func() {}, + evmtypes.TransactionArgs{ + Nonce: &txNonce, + MaxPriorityFeePerGas: nil, + GasPrice: gasPrice, + MaxFeePerGas: gasPrice, + }, + nil, + nil, + common.Hash{}, + false, + }, + { + "fail - Block error", + func() { + var header metadata.MD + client := suite.backend.clientCtx.Client.(*mocks.Client) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterParams(queryClient, &header, 1) + RegisterBlockError(client, 1) + }, + evmtypes.TransactionArgs{ + Nonce: &txNonce, + }, + nil, + nil, + common.Hash{}, + false, + }, + { + "pass - MaxFeePerGas is nil", + func() { + var header metadata.MD + client := suite.backend.clientCtx.Client.(*mocks.Client) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterParams(queryClient, &header, 1) + RegisterBlock(client, 1, nil) + RegisterBlockResults(client, 1) + RegisterBaseFee(queryClient, baseFee) + }, + evmtypes.TransactionArgs{ + Nonce: &txNonce, + GasPrice: nil, + MaxPriorityFeePerGas: gasPrice, + MaxFeePerGas: gasPrice, + ChainID: callArgs.ChainID, + }, + nil, + nil, + common.Hash{}, + true, + }, + { + "pass - Chain Id is nil", + func() { + var header metadata.MD + client := suite.backend.clientCtx.Client.(*mocks.Client) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterParams(queryClient, &header, 1) + RegisterBlock(client, 1, nil) + RegisterBlockResults(client, 1) + RegisterBaseFee(queryClient, baseFee) + }, + evmtypes.TransactionArgs{ + Nonce: &txNonce, + MaxPriorityFeePerGas: gasPrice, + ChainID: nil, + }, + nil, + nil, + common.Hash{}, + true, + }, + { + "fail - Pending transactions error", + func() { + var header metadata.MD + client := suite.backend.clientCtx.Client.(*mocks.Client) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBlock(client, 1, nil) + RegisterBlockResults(client, 1) + RegisterBaseFee(queryClient, baseFee) + RegisterEstimateGas(queryClient, callArgs) + RegisterParams(queryClient, &header, 1) + RegisterParamsWithoutHeader(queryClient, 1) + RegisterUnconfirmedTxsError(client, nil) + }, + evmtypes.TransactionArgs{ + Nonce: &txNonce, + To: &toAddr, + MaxFeePerGas: gasPrice, + MaxPriorityFeePerGas: gasPrice, + Value: gasPrice, + Gas: nil, + ChainID: callArgs.ChainID, + }, + gasPrice, + nil, + common.Hash{}, + false, + }, + { + "fail - Not Ethereum txs", + func() { + var header metadata.MD + client := suite.backend.clientCtx.Client.(*mocks.Client) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBlock(client, 1, nil) + RegisterBlockResults(client, 1) + RegisterBaseFee(queryClient, baseFee) + RegisterEstimateGas(queryClient, callArgs) + RegisterParams(queryClient, &header, 1) + RegisterParamsWithoutHeader(queryClient, 1) + RegisterUnconfirmedTxsEmpty(client, nil) + }, + evmtypes.TransactionArgs{ + Nonce: &txNonce, + To: &toAddr, + MaxFeePerGas: gasPrice, + MaxPriorityFeePerGas: gasPrice, + Value: gasPrice, + Gas: nil, + ChainID: callArgs.ChainID, + }, + gasPrice, + nil, + common.Hash{}, + false, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock() + + hash, err := suite.backend.Resend(tc.args, tc.gasPrice, tc.gasLimit) + + if tc.expPass { + suite.Require().Equal(tc.expHash, hash) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestSendRawTransaction() { + ethTx, bz := suite.buildEthereumTx() + rlpEncodedBz, err := rlp.EncodeToBytes(ethTx.AsTransaction()) + suite.Require().NoError(err) + cosmosTx, err := ethTx.BuildTx(suite.backend.clientCtx.TxConfig.NewTxBuilder(), "azeta") + suite.Require().NoError(err) + txBytes, err := suite.backend.clientCtx.TxConfig.TxEncoder()(cosmosTx) + suite.Require().NoError(err) + + testCases := []struct { + name string + registerMock func() + rawTx []byte + expHash common.Hash + expPass bool + }{ + { + "fail - empty bytes", + func() {}, + []byte{}, + common.Hash{}, + false, + }, + { + "fail - no RLP encoded bytes", + func() {}, + bz, + common.Hash{}, + false, + }, + { + "fail - unprotected transactions", + func() { + suite.backend.allowUnprotectedTxs = false + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterParamsWithoutHeaderError(queryClient, 1) + }, + rlpEncodedBz, + common.Hash{}, + false, + }, + { + "fail - failed to get evm params", + func() { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + suite.backend.allowUnprotectedTxs = true + RegisterParamsWithoutHeaderError(queryClient, 1) + }, + rlpEncodedBz, + common.Hash{}, + false, + }, + { + "fail - failed to broadcast transaction", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + suite.backend.allowUnprotectedTxs = true + RegisterParamsWithoutHeader(queryClient, 1) + RegisterBroadcastTxError(client, txBytes) + }, + rlpEncodedBz, + common.HexToHash(ethTx.Hash), + false, + }, + { + "pass - Gets the correct transaction hash of the eth transaction", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + suite.backend.allowUnprotectedTxs = true + RegisterParamsWithoutHeader(queryClient, 1) + RegisterBroadcastTx(client, txBytes) + }, + rlpEncodedBz, + common.HexToHash(ethTx.Hash), + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock() + + hash, err := suite.backend.SendRawTransaction(tc.rawTx) + + if tc.expPass { + suite.Require().Equal(tc.expHash, hash) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestDoCall() { + _, bz := suite.buildEthereumTx() + gasPrice := (*hexutil.Big)(big.NewInt(1)) + toAddr := tests.GenerateAddress() + chainID := (*hexutil.Big)(suite.backend.chainID) + callArgs := evmtypes.TransactionArgs{ + From: nil, + To: &toAddr, + Gas: nil, + GasPrice: nil, + MaxFeePerGas: gasPrice, + MaxPriorityFeePerGas: gasPrice, + Value: gasPrice, + Input: nil, + Data: nil, + AccessList: nil, + ChainID: chainID, + } + argsBz, err := json.Marshal(callArgs) + suite.Require().NoError(err) + + testCases := []struct { + name string + registerMock func() + blockNum rpctypes.BlockNumber + callArgs evmtypes.TransactionArgs + expEthTx *evmtypes.MsgEthereumTxResponse + expPass bool + }{ + { + "fail - Invalid request", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBlock(client, 1, bz) + RegisterEthCallError( + queryClient, + &evmtypes.EthCallRequest{Args: argsBz, ChainId: suite.backend.chainID.Int64()}, + ) + }, + rpctypes.BlockNumber(1), + callArgs, + &evmtypes.MsgEthereumTxResponse{}, + false, + }, + { + "pass - Returned transaction response", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBlock(client, 1, bz) + RegisterEthCall( + queryClient, + &evmtypes.EthCallRequest{Args: argsBz, ChainId: suite.backend.chainID.Int64()}, + ) + }, + rpctypes.BlockNumber(1), + callArgs, + &evmtypes.MsgEthereumTxResponse{}, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock() + + msgEthTx, err := suite.backend.DoCall(tc.callArgs, tc.blockNum) + + if tc.expPass { + suite.Require().Equal(tc.expEthTx, msgEthTx) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestGasPrice() { + defaultGasPrice := (*hexutil.Big)(big.NewInt(1)) + + testCases := []struct { + name string + registerMock func() + expGas *hexutil.Big + expPass bool + }{ + { + "pass - get the default gas price", + func() { + var header metadata.MD + client := suite.backend.clientCtx.Client.(*mocks.Client) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + feeMarketClient := suite.backend.queryClient.FeeMarket.(*mocks.FeeMarketQueryClient) + RegisterFeeMarketParams(feeMarketClient, 1) + RegisterParams(queryClient, &header, 1) + RegisterBlock(client, 1, nil) + RegisterBlockResults(client, 1) + RegisterBaseFee(queryClient, sdk.NewInt(1)) + }, + defaultGasPrice, + true, + }, + { + "fail - can't get gasFee, FeeMarketParams error", + func() { + var header metadata.MD + client := suite.backend.clientCtx.Client.(*mocks.Client) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + feeMarketClient := suite.backend.queryClient.FeeMarket.(*mocks.FeeMarketQueryClient) + RegisterFeeMarketParamsError(feeMarketClient, 1) + RegisterParams(queryClient, &header, 1) + RegisterBlock(client, 1, nil) + RegisterBlockResults(client, 1) + RegisterBaseFee(queryClient, sdk.NewInt(1)) + }, + defaultGasPrice, + false, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock() + + gasPrice, err := suite.backend.GasPrice() + if tc.expPass { + suite.Require().Equal(tc.expGas, gasPrice) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/rpc/backend/chain_info_test.go b/rpc/backend/chain_info_test.go new file mode 100644 index 0000000000..c39215d1bf --- /dev/null +++ b/rpc/backend/chain_info_test.go @@ -0,0 +1,454 @@ +package backend + +import ( + "fmt" + "math/big" + + "github.com/cometbft/cometbft/abci/types" + tmrpctypes "github.com/cometbft/cometbft/rpc/core/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common/hexutil" + ethrpc "github.com/ethereum/go-ethereum/rpc" + "github.com/evmos/ethermint/tests" + evmtypes "github.com/evmos/ethermint/x/evm/types" + feemarkettypes "github.com/evmos/ethermint/x/feemarket/types" + "google.golang.org/grpc/metadata" + + "github.com/zeta-chain/zetacore/rpc/backend/mocks" + rpc "github.com/zeta-chain/zetacore/rpc/types" +) + +func (suite *BackendTestSuite) TestBaseFee() { + baseFee := sdk.NewInt(1) + + testCases := []struct { + name string + blockRes *tmrpctypes.ResultBlockResults + registerMock func() + expBaseFee *big.Int + expPass bool + }{ + { + "fail - grpc BaseFee error", + &tmrpctypes.ResultBlockResults{Height: 1}, + func() { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFeeError(queryClient) + }, + nil, + false, + }, + { + "fail - grpc BaseFee error - with non feemarket block event", + &tmrpctypes.ResultBlockResults{ + Height: 1, + BeginBlockEvents: []types.Event{ + { + Type: evmtypes.EventTypeBlockBloom, + }, + }, + }, + func() { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFeeError(queryClient) + }, + nil, + false, + }, + { + "fail - grpc BaseFee error - with feemarket block event", + &tmrpctypes.ResultBlockResults{ + Height: 1, + BeginBlockEvents: []types.Event{ + { + Type: feemarkettypes.EventTypeFeeMarket, + }, + }, + }, + func() { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFeeError(queryClient) + }, + nil, + false, + }, + { + "fail - grpc BaseFee error - with feemarket block event with wrong attribute value", + &tmrpctypes.ResultBlockResults{ + Height: 1, + BeginBlockEvents: []types.Event{ + { + Type: feemarkettypes.EventTypeFeeMarket, + Attributes: []types.EventAttribute{ + {Value: "/1"}, + }, + }, + }, + }, + func() { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFeeError(queryClient) + }, + nil, + false, + }, + { + "fail - grpc baseFee error - with feemarket block event with baseFee attribute value", + &tmrpctypes.ResultBlockResults{ + Height: 1, + BeginBlockEvents: []types.Event{ + { + Type: feemarkettypes.EventTypeFeeMarket, + Attributes: []types.EventAttribute{ + {Value: baseFee.String()}, + }, + }, + }, + }, + func() { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFeeError(queryClient) + }, + baseFee.BigInt(), + true, + }, + { + "fail - base fee or london fork not enabled", + &tmrpctypes.ResultBlockResults{Height: 1}, + func() { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFeeDisabled(queryClient) + }, + nil, + true, + }, + { + "pass", + &tmrpctypes.ResultBlockResults{Height: 1}, + func() { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBaseFee(queryClient, baseFee) + }, + baseFee.BigInt(), + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock() + + baseFee, err := suite.backend.BaseFee(tc.blockRes) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(tc.expBaseFee, baseFee) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestChainId() { + expChainId := (*hexutil.Big)(big.NewInt(7001)) + testCases := []struct { + name string + registerMock func() + expChainId *hexutil.Big + expPass bool + }{ + { + "pass - block is at or past the EIP-155 replay-protection fork block, return chainID from config ", + func() { + var header metadata.MD + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterParamsInvalidHeight(queryClient, &header, int64(1)) + }, + expChainId, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock() + + chainId, err := suite.backend.ChainID() + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(tc.expChainId, chainId) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestGetCoinbase() { + validatorAcc := sdk.AccAddress(tests.GenerateAddress().Bytes()) + testCases := []struct { + name string + registerMock func() + accAddr sdk.AccAddress + expPass bool + }{ + { + "fail - Can't retrieve status from node", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterStatusError(client) + }, + validatorAcc, + false, + }, + { + "fail - Can't query validator account", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterStatus(client) + RegisterValidatorAccountError(queryClient) + }, + validatorAcc, + false, + }, + { + "pass - Gets coinbase account", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterStatus(client) + RegisterValidatorAccount(queryClient, validatorAcc) + }, + validatorAcc, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock() + + accAddr, err := suite.backend.GetCoinbase() + + if tc.expPass { + suite.Require().Equal(tc.accAddr, accAddr) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestSuggestGasTipCap() { + testCases := []struct { + name string + registerMock func() + baseFee *big.Int + expGasTipCap *big.Int + expPass bool + }{ + { + "pass - London hardfork not enabled or feemarket not enabled ", + func() {}, + nil, + big.NewInt(0), + true, + }, + { + "pass - Gets the suggest gas tip cap ", + func() {}, + nil, + big.NewInt(0), + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock() + + maxDelta, err := suite.backend.SuggestGasTipCap(tc.baseFee) + + if tc.expPass { + suite.Require().Equal(tc.expGasTipCap, maxDelta) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestGlobalMinGasPrice() { + testCases := []struct { + name string + registerMock func() + expMinGasPrice sdk.Dec + expPass bool + }{ + { + "fail - Can't get FeeMarket params", + func() { + feeMarketCleint := suite.backend.queryClient.FeeMarket.(*mocks.FeeMarketQueryClient) + RegisterFeeMarketParamsError(feeMarketCleint, int64(1)) + }, + sdk.ZeroDec(), + false, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock() + + globalMinGasPrice, err := suite.backend.GlobalMinGasPrice() + + if tc.expPass { + suite.Require().Equal(tc.expMinGasPrice, globalMinGasPrice) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestFeeHistory() { + testCases := []struct { + name string + registerMock func(validator sdk.AccAddress) + userBlockCount ethrpc.DecimalOrHex + latestBlock ethrpc.BlockNumber + expFeeHistory *rpc.FeeHistoryResult + validator sdk.AccAddress + expPass bool + }{ + { + "fail - can't get params ", + func(validator sdk.AccAddress) { + var header metadata.MD + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + suite.backend.cfg.JSONRPC.FeeHistoryCap = 0 + RegisterParamsError(queryClient, &header, ethrpc.BlockNumber(1).Int64()) + }, + 1, + -1, + nil, + nil, + false, + }, + { + "fail - user block count higher than max block count ", + func(validator sdk.AccAddress) { + var header metadata.MD + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + suite.backend.cfg.JSONRPC.FeeHistoryCap = 0 + RegisterParams(queryClient, &header, ethrpc.BlockNumber(1).Int64()) + }, + 1, + -1, + nil, + nil, + false, + }, + { + "fail - Tendermint block fetching error ", + func(validator sdk.AccAddress) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + suite.backend.cfg.JSONRPC.FeeHistoryCap = 2 + RegisterBlockError(client, ethrpc.BlockNumber(1).Int64()) + }, + 1, + 1, + nil, + nil, + false, + }, + { + "fail - Eth block fetching error", + func(validator sdk.AccAddress) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + suite.backend.cfg.JSONRPC.FeeHistoryCap = 2 + RegisterBlock(client, ethrpc.BlockNumber(1).Int64(), nil) + RegisterBlockResultsError(client, 1) + }, + 1, + 1, + nil, + nil, + true, + }, + { + "fail - Invalid base fee", + func(validator sdk.AccAddress) { + // baseFee := sdk.NewInt(1) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + client := suite.backend.clientCtx.Client.(*mocks.Client) + suite.backend.cfg.JSONRPC.FeeHistoryCap = 2 + RegisterBlock(client, ethrpc.BlockNumber(1).Int64(), nil) + RegisterBlockResults(client, 1) + RegisterBaseFeeError(queryClient) + RegisterValidatorAccount(queryClient, validator) + RegisterConsensusParams(client, 1) + }, + 1, + 1, + nil, + sdk.AccAddress(tests.GenerateAddress().Bytes()), + false, + }, + { + "pass - Valid FeeHistoryResults object", + func(validator sdk.AccAddress) { + var header metadata.MD + baseFee := sdk.NewInt(1) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + client := suite.backend.clientCtx.Client.(*mocks.Client) + suite.backend.cfg.JSONRPC.FeeHistoryCap = 2 + RegisterBlock(client, ethrpc.BlockNumber(1).Int64(), nil) + RegisterBlockResults(client, 1) + RegisterBaseFee(queryClient, baseFee) + RegisterValidatorAccount(queryClient, validator) + RegisterConsensusParams(client, 1) + RegisterParams(queryClient, &header, 1) + RegisterParamsWithoutHeader(queryClient, 1) + }, + 1, + 1, + &rpc.FeeHistoryResult{ + OldestBlock: (*hexutil.Big)(big.NewInt(1)), + BaseFee: []*hexutil.Big{(*hexutil.Big)(big.NewInt(1)), (*hexutil.Big)(big.NewInt(1))}, + GasUsedRatio: []float64{0}, + Reward: [][]*hexutil.Big{ + { + (*hexutil.Big)(big.NewInt(0)), + (*hexutil.Big)(big.NewInt(0)), + (*hexutil.Big)(big.NewInt(0)), + (*hexutil.Big)(big.NewInt(0)), + }, + }, + }, + sdk.AccAddress(tests.GenerateAddress().Bytes()), + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock(tc.validator) + + feeHistory, err := suite.backend.FeeHistory(tc.userBlockCount, tc.latestBlock, []float64{25, 50, 75, 100}) + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(feeHistory, tc.expFeeHistory) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/rpc/backend/client_test.go b/rpc/backend/client_test.go new file mode 100644 index 0000000000..74d6ae0bfd --- /dev/null +++ b/rpc/backend/client_test.go @@ -0,0 +1,319 @@ +package backend + +import ( + "context" + "testing" + + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/libs/bytes" + tmrpcclient "github.com/cometbft/cometbft/rpc/client" + tmrpctypes "github.com/cometbft/cometbft/rpc/core/types" + "github.com/cometbft/cometbft/types" + "github.com/cosmos/cosmos-sdk/client" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + errortypes "github.com/cosmos/cosmos-sdk/types/errors" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/ethereum/go-ethereum/common" + evmtypes "github.com/evmos/ethermint/x/evm/types" + mock "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/zetacore/rpc/backend/mocks" + rpc "github.com/zeta-chain/zetacore/rpc/types" +) + +// Client defines a mocked object that implements the Tendermint JSON-RPC Client +// interface. It allows for performing Client queries without having to run a +// Tendermint RPC Client server. +// +// To use a mock method it has to be registered in a given test. +var _ tmrpcclient.Client = &mocks.Client{} + +// Tx Search +func RegisterTxSearch(client *mocks.Client, query string, txBz []byte) { + resulTxs := []*tmrpctypes.ResultTx{{Tx: txBz}} + client.On("TxSearch", rpc.ContextWithHeight(1), query, false, (*int)(nil), (*int)(nil), ""). + Return(&tmrpctypes.ResultTxSearch{Txs: resulTxs, TotalCount: 1}, nil) +} + +func RegisterTxSearchEmpty(client *mocks.Client, query string) { + client.On("TxSearch", rpc.ContextWithHeight(1), query, false, (*int)(nil), (*int)(nil), ""). + Return(&tmrpctypes.ResultTxSearch{}, nil) +} + +func RegisterTxSearchError(client *mocks.Client, query string) { + client.On("TxSearch", rpc.ContextWithHeight(1), query, false, (*int)(nil), (*int)(nil), ""). + Return(nil, errortypes.ErrInvalidRequest) +} + +// Broadcast Tx +func RegisterBroadcastTx(client *mocks.Client, tx types.Tx) { + client.On("BroadcastTxSync", context.Background(), tx). + Return(&tmrpctypes.ResultBroadcastTx{}, nil) +} + +func RegisterBroadcastTxError(client *mocks.Client, tx types.Tx) { + client.On("BroadcastTxSync", context.Background(), tx). + Return(nil, errortypes.ErrInvalidRequest) +} + +// Unconfirmed Transactions +func RegisterUnconfirmedTxs(client *mocks.Client, limit *int, txs []types.Tx) { + client.On("UnconfirmedTxs", rpc.ContextWithHeight(1), limit). + Return(&tmrpctypes.ResultUnconfirmedTxs{Txs: txs}, nil) +} + +func RegisterUnconfirmedTxsEmpty(client *mocks.Client, limit *int) { + client.On("UnconfirmedTxs", rpc.ContextWithHeight(1), limit). + Return(&tmrpctypes.ResultUnconfirmedTxs{ + Txs: make([]types.Tx, 2), + }, nil) +} + +func RegisterUnconfirmedTxsError(client *mocks.Client, limit *int) { + client.On("UnconfirmedTxs", rpc.ContextWithHeight(1), limit). + Return(nil, errortypes.ErrInvalidRequest) +} + +// Status +func RegisterStatus(client *mocks.Client) { + client.On("Status", rpc.ContextWithHeight(1)). + Return(&tmrpctypes.ResultStatus{}, nil) +} + +func RegisterStatusError(client *mocks.Client) { + client.On("Status", rpc.ContextWithHeight(1)). + Return(nil, errortypes.ErrInvalidRequest) +} + +// Block +func RegisterBlockMultipleTxs( + client *mocks.Client, + height int64, + txs []types.Tx, +) (*tmrpctypes.ResultBlock, error) { + block := types.MakeBlock(height, txs, nil, nil) + block.ChainID = ChainID + resBlock := &tmrpctypes.ResultBlock{Block: block} + client.On("Block", rpc.ContextWithHeight(height), mock.AnythingOfType("*int64")).Return(resBlock, nil) + return resBlock, nil +} + +func RegisterBlock( + client *mocks.Client, + height int64, + tx []byte, +) (*tmrpctypes.ResultBlock, error) { + // without tx + if tx == nil { + emptyBlock := types.MakeBlock(height, []types.Tx{}, nil, nil) + emptyBlock.ChainID = ChainID + resBlock := &tmrpctypes.ResultBlock{Block: emptyBlock} + client.On("Block", rpc.ContextWithHeight(height), mock.AnythingOfType("*int64")).Return(resBlock, nil) + return resBlock, nil + } + + // with tx + block := types.MakeBlock(height, []types.Tx{tx}, nil, nil) + block.ChainID = ChainID + resBlock := &tmrpctypes.ResultBlock{Block: block} + client.On("Block", rpc.ContextWithHeight(height), mock.AnythingOfType("*int64")).Return(resBlock, nil) + return resBlock, nil +} + +// Block returns error +func RegisterBlockError(client *mocks.Client, height int64) { + client.On("Block", rpc.ContextWithHeight(height), mock.AnythingOfType("*int64")). + Return(nil, errortypes.ErrInvalidRequest) +} + +// Block not found +func RegisterBlockNotFound( + client *mocks.Client, + height int64, +) (*tmrpctypes.ResultBlock, error) { + client.On("Block", rpc.ContextWithHeight(height), mock.AnythingOfType("*int64")). + Return(&tmrpctypes.ResultBlock{Block: nil}, nil) + + return &tmrpctypes.ResultBlock{Block: nil}, nil +} + +func TestRegisterBlock(t *testing.T) { + client := mocks.NewClient(t) + height := rpc.BlockNumber(1).Int64() + RegisterBlock(client, height, nil) + + res, err := client.Block(rpc.ContextWithHeight(height), &height) + + emptyBlock := types.MakeBlock(height, []types.Tx{}, nil, nil) + emptyBlock.ChainID = ChainID + resBlock := &tmrpctypes.ResultBlock{Block: emptyBlock} + require.Equal(t, resBlock, res) + require.NoError(t, err) +} + +// ConsensusParams +func RegisterConsensusParams(client *mocks.Client, height int64) { + consensusParams := types.DefaultConsensusParams() + client.On("ConsensusParams", rpc.ContextWithHeight(height), mock.AnythingOfType("*int64")). + Return(&tmrpctypes.ResultConsensusParams{ConsensusParams: *consensusParams}, nil) +} + +func RegisterConsensusParamsError(client *mocks.Client, height int64) { + client.On("ConsensusParams", rpc.ContextWithHeight(height), mock.AnythingOfType("*int64")). + Return(nil, errortypes.ErrInvalidRequest) +} + +func TestRegisterConsensusParams(t *testing.T) { + client := mocks.NewClient(t) + height := int64(1) + RegisterConsensusParams(client, height) + + res, err := client.ConsensusParams(rpc.ContextWithHeight(height), &height) + consensusParams := types.DefaultConsensusParams() + require.Equal(t, &tmrpctypes.ResultConsensusParams{ConsensusParams: *consensusParams}, res) + require.NoError(t, err) +} + +// BlockResults + +func RegisterBlockResultsWithEventLog(client *mocks.Client, height int64) (*tmrpctypes.ResultBlockResults, error) { + res := &tmrpctypes.ResultBlockResults{ + Height: height, + TxsResults: []*abci.ResponseDeliverTx{ + {Code: 0, GasUsed: 0, Events: []abci.Event{{ + Type: evmtypes.EventTypeTxLog, + Attributes: []abci.EventAttribute{{ + Key: evmtypes.AttributeKeyTxLog, + Value: "{\"test\": \"hello\"}", + Index: true, + }}, + }}}, + }, + } + client.On("BlockResults", rpc.ContextWithHeight(height), mock.AnythingOfType("*int64")). + Return(res, nil) + return res, nil +} + +func RegisterBlockResults( + client *mocks.Client, + height int64, +) (*tmrpctypes.ResultBlockResults, error) { + res := &tmrpctypes.ResultBlockResults{ + Height: height, + TxsResults: []*abci.ResponseDeliverTx{{Code: 0, GasUsed: 0}}, + } + + client.On("BlockResults", rpc.ContextWithHeight(height), mock.AnythingOfType("*int64")). + Return(res, nil) + return res, nil +} + +func RegisterBlockResultsWithTxResults( + client *mocks.Client, + height int64, + txResults []*abci.ResponseDeliverTx, +) (*tmrpctypes.ResultBlockResults, error) { + res := &tmrpctypes.ResultBlockResults{ + Height: height, + TxsResults: txResults, + } + + client.On("BlockResults", rpc.ContextWithHeight(height), mock.AnythingOfType("*int64")). + Return(res, nil) + return res, nil +} + +func RegisterBlockResultsError(client *mocks.Client, height int64) { + client.On("BlockResults", rpc.ContextWithHeight(height), mock.AnythingOfType("*int64")). + Return(nil, errortypes.ErrInvalidRequest) +} + +func TestRegisterBlockResults(t *testing.T) { + client := mocks.NewClient(t) + height := int64(1) + RegisterBlockResults(client, height) + + res, err := client.BlockResults(rpc.ContextWithHeight(height), &height) + expRes := &tmrpctypes.ResultBlockResults{ + Height: height, + TxsResults: []*abci.ResponseDeliverTx{{Code: 0, GasUsed: 0}}, + } + require.Equal(t, expRes, res) + require.NoError(t, err) +} + +// BlockByHash +func RegisterBlockByHash( + client *mocks.Client, + hash common.Hash, + tx []byte, +) (*tmrpctypes.ResultBlock, error) { + block := types.MakeBlock(1, []types.Tx{tx}, nil, nil) + resBlock := &tmrpctypes.ResultBlock{Block: block} + + client.On("BlockByHash", rpc.ContextWithHeight(1), []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}). + Return(resBlock, nil) + return resBlock, nil +} + +func RegisterBlockByHashError(client *mocks.Client, hash common.Hash, tx []byte) { + client.On("BlockByHash", rpc.ContextWithHeight(1), []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}). + Return(nil, errortypes.ErrInvalidRequest) +} + +func RegisterBlockByHashNotFound(client *mocks.Client, hash common.Hash, tx []byte) { + client.On("BlockByHash", rpc.ContextWithHeight(1), []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}). + Return(nil, nil) +} + +func RegisterABCIQueryWithOptions( + client *mocks.Client, + height int64, + path string, + data bytes.HexBytes, + opts tmrpcclient.ABCIQueryOptions, +) { + client.On("ABCIQueryWithOptions", context.Background(), path, data, opts). + Return(&tmrpctypes.ResultABCIQuery{ + Response: abci.ResponseQuery{ + Value: []byte{2}, // TODO (https://github.com/zeta-chain/node/issues/2302) replace with data.Bytes(), + Height: height, + }, + }, nil) +} + +func RegisterABCIQueryWithOptionsError( + clients *mocks.Client, + path string, + data bytes.HexBytes, + opts tmrpcclient.ABCIQueryOptions, +) { + clients.On("ABCIQueryWithOptions", context.Background(), path, data, opts). + Return(nil, errortypes.ErrInvalidRequest) +} + +func RegisterABCIQueryAccount( + clients *mocks.Client, + data bytes.HexBytes, + opts tmrpcclient.ABCIQueryOptions, + acc client.Account, +) { + baseAccount := authtypes.NewBaseAccount( + acc.GetAddress(), + acc.GetPubKey(), + acc.GetAccountNumber(), + acc.GetSequence(), + ) + accAny, _ := codectypes.NewAnyWithValue(baseAccount) + accResponse := authtypes.QueryAccountResponse{Account: accAny} + respBz, _ := accResponse.Marshal() + clients.On("ABCIQueryWithOptions", context.Background(), "/cosmos.auth.v1beta1.Query/Account", data, opts). + Return(&tmrpctypes.ResultABCIQuery{ + Response: abci.ResponseQuery{ + Value: respBz, + Height: 1, + }, + }, nil) +} diff --git a/rpc/backend/evm_query_client_test.go b/rpc/backend/evm_query_client_test.go new file mode 100644 index 0000000000..24b32b1328 --- /dev/null +++ b/rpc/backend/evm_query_client_test.go @@ -0,0 +1,291 @@ +package backend + +import ( + "context" + "encoding/json" + "fmt" + "strconv" + "testing" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + errortypes "github.com/cosmos/cosmos-sdk/types/errors" + grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" + "github.com/ethereum/go-ethereum/common" + "github.com/evmos/ethermint/tests" + evmtypes "github.com/evmos/ethermint/x/evm/types" + mock "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + + "github.com/zeta-chain/zetacore/rpc/backend/mocks" + rpc "github.com/zeta-chain/zetacore/rpc/types" +) + +// QueryClient defines a mocked object that implements the ethermint GRPC +// QueryClient interface. It allows for performing QueryClient queries without having +// to run a ethermint GRPC server. +// +// To use a mock method it has to be registered in a given test. +var _ evmtypes.QueryClient = &mocks.EVMQueryClient{} + +// TraceTransaction +func RegisterTraceTransactionWithPredecessors( + queryClient *mocks.EVMQueryClient, + msgEthTx *evmtypes.MsgEthereumTx, + predecessors []*evmtypes.MsgEthereumTx, +) { + data := []byte{0x7b, 0x22, 0x74, 0x65, 0x73, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x22, 0x7d} + queryClient.On("TraceTx", rpc.ContextWithHeight(1), + &evmtypes.QueryTraceTxRequest{Msg: msgEthTx, BlockNumber: 1, Predecessors: predecessors, ChainId: 7001}). + Return(&evmtypes.QueryTraceTxResponse{Data: data}, nil) +} + +func RegisterTraceTransaction(queryClient *mocks.EVMQueryClient, msgEthTx *evmtypes.MsgEthereumTx) { + data := []byte{0x7b, 0x22, 0x74, 0x65, 0x73, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x22, 0x7d} + queryClient.On("TraceTx", rpc.ContextWithHeight(1), &evmtypes.QueryTraceTxRequest{Msg: msgEthTx, BlockNumber: 1, Predecessors: []*evmtypes.MsgEthereumTx{}, ChainId: 7001}). + Return(&evmtypes.QueryTraceTxResponse{Data: data}, nil) +} + +func RegisterTraceTransactionError(queryClient *mocks.EVMQueryClient, msgEthTx *evmtypes.MsgEthereumTx) { + queryClient.On("TraceTx", rpc.ContextWithHeight(1), &evmtypes.QueryTraceTxRequest{Msg: msgEthTx, BlockNumber: 1, ChainId: 7001}). + Return(nil, errortypes.ErrInvalidRequest) +} + +// TraceBlock +func RegisterTraceBlock(queryClient *mocks.EVMQueryClient, txs []*evmtypes.MsgEthereumTx) { + data := []byte{0x7b, 0x22, 0x74, 0x65, 0x73, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x22, 0x7d} + queryClient.On( + "TraceBlock", + rpc.ContextWithHeight(1), + &evmtypes.QueryTraceBlockRequest{ + Txs: txs, + BlockNumber: 1, + TraceConfig: &evmtypes.TraceConfig{}, + ChainId: 7001, + }, + ). + Return(&evmtypes.QueryTraceBlockResponse{Data: data}, nil) +} + +func RegisterTraceBlockError(queryClient *mocks.EVMQueryClient) { + queryClient.On("TraceBlock", rpc.ContextWithHeight(1), &evmtypes.QueryTraceBlockRequest{}). + Return(nil, errortypes.ErrInvalidRequest) +} + +// Params +func RegisterParams(queryClient *mocks.EVMQueryClient, header *metadata.MD, height int64) { + queryClient.On("Params", rpc.ContextWithHeight(height), &evmtypes.QueryParamsRequest{}, grpc.Header(header)). + Return(&evmtypes.QueryParamsResponse{}, nil). + Run(func(args mock.Arguments) { + // If Params call is successful, also update the header height + arg := args.Get(2).(grpc.HeaderCallOption) + h := metadata.MD{} + h.Set(grpctypes.GRPCBlockHeightHeader, fmt.Sprint(height)) + *arg.HeaderAddr = h + }) +} + +func RegisterParamsWithoutHeader(queryClient *mocks.EVMQueryClient, height int64) { + params := evmtypes.DefaultParams() + params.EvmDenom = "azeta" + queryClient.On("Params", rpc.ContextWithHeight(height), &evmtypes.QueryParamsRequest{}). + Return(&evmtypes.QueryParamsResponse{Params: params}, nil) +} + +func RegisterParamsInvalidHeader(queryClient *mocks.EVMQueryClient, header *metadata.MD, height int64) { + queryClient.On("Params", rpc.ContextWithHeight(height), &evmtypes.QueryParamsRequest{}, grpc.Header(header)). + Return(&evmtypes.QueryParamsResponse{}, nil). + Run(func(args mock.Arguments) { + // If Params call is successful, also update the header height + arg := args.Get(2).(grpc.HeaderCallOption) + h := metadata.MD{} + *arg.HeaderAddr = h + }) +} + +func RegisterParamsInvalidHeight(queryClient *mocks.EVMQueryClient, header *metadata.MD, height int64) { + queryClient.On("Params", rpc.ContextWithHeight(height), &evmtypes.QueryParamsRequest{}, grpc.Header(header)). + Return(&evmtypes.QueryParamsResponse{}, nil). + Run(func(args mock.Arguments) { + // If Params call is successful, also update the header height + arg := args.Get(2).(grpc.HeaderCallOption) + h := metadata.MD{} + h.Set(grpctypes.GRPCBlockHeightHeader, "invalid") + *arg.HeaderAddr = h + }) +} + +func RegisterParamsWithoutHeaderError(queryClient *mocks.EVMQueryClient, height int64) { + queryClient.On("Params", rpc.ContextWithHeight(height), &evmtypes.QueryParamsRequest{}). + Return(nil, errortypes.ErrInvalidRequest) +} + +// Params returns error +func RegisterParamsError(queryClient *mocks.EVMQueryClient, header *metadata.MD, height int64) { + queryClient.On("Params", rpc.ContextWithHeight(height), &evmtypes.QueryParamsRequest{}, grpc.Header(header)). + Return(nil, errortypes.ErrInvalidRequest) +} + +func TestRegisterParams(t *testing.T) { + var header metadata.MD + queryClient := mocks.NewEVMQueryClient(t) + + height := int64(1) + RegisterParams(queryClient, &header, height) + + _, err := queryClient.Params(rpc.ContextWithHeight(height), &evmtypes.QueryParamsRequest{}, grpc.Header(&header)) + require.NoError(t, err) + blockHeightHeader := header.Get(grpctypes.GRPCBlockHeightHeader) + headerHeight, err := strconv.ParseInt(blockHeightHeader[0], 10, 64) + require.NoError(t, err) + require.Equal(t, height, headerHeight) +} + +func TestRegisterParamsError(t *testing.T) { + queryClient := mocks.NewEVMQueryClient(t) + RegisterBaseFeeError(queryClient) + _, err := queryClient.BaseFee(rpc.ContextWithHeight(1), &evmtypes.QueryBaseFeeRequest{}) + require.Error(t, err) +} + +// ETH Call +func RegisterEthCall(queryClient *mocks.EVMQueryClient, request *evmtypes.EthCallRequest) { + ctx, _ := context.WithCancel(rpc.ContextWithHeight(1)) + queryClient.On("EthCall", ctx, request). + Return(&evmtypes.MsgEthereumTxResponse{}, nil) +} + +func RegisterEthCallError(queryClient *mocks.EVMQueryClient, request *evmtypes.EthCallRequest) { + ctx, _ := context.WithCancel(rpc.ContextWithHeight(1)) + queryClient.On("EthCall", ctx, request). + Return(nil, errortypes.ErrInvalidRequest) +} + +// Estimate Gas +func RegisterEstimateGas(queryClient *mocks.EVMQueryClient, args evmtypes.TransactionArgs) { + bz, _ := json.Marshal(args) + queryClient.On("EstimateGas", rpc.ContextWithHeight(1), &evmtypes.EthCallRequest{Args: bz, ChainId: args.ChainID.ToInt().Int64()}). + Return(&evmtypes.EstimateGasResponse{}, nil) +} + +// BaseFee +func RegisterBaseFee(queryClient *mocks.EVMQueryClient, baseFee sdkmath.Int) { + queryClient.On("BaseFee", rpc.ContextWithHeight(1), &evmtypes.QueryBaseFeeRequest{}). + Return(&evmtypes.QueryBaseFeeResponse{BaseFee: &baseFee}, nil) +} + +// Base fee returns error +func RegisterBaseFeeError(queryClient *mocks.EVMQueryClient) { + queryClient.On("BaseFee", rpc.ContextWithHeight(1), &evmtypes.QueryBaseFeeRequest{}). + Return(&evmtypes.QueryBaseFeeResponse{}, evmtypes.ErrInvalidBaseFee) +} + +// Base fee not enabled +func RegisterBaseFeeDisabled(queryClient *mocks.EVMQueryClient) { + queryClient.On("BaseFee", rpc.ContextWithHeight(1), &evmtypes.QueryBaseFeeRequest{}). + Return(&evmtypes.QueryBaseFeeResponse{}, nil) +} + +func TestRegisterBaseFee(t *testing.T) { + baseFee := sdk.NewInt(1) + queryClient := mocks.NewEVMQueryClient(t) + RegisterBaseFee(queryClient, baseFee) + res, err := queryClient.BaseFee(rpc.ContextWithHeight(1), &evmtypes.QueryBaseFeeRequest{}) + require.Equal(t, &evmtypes.QueryBaseFeeResponse{BaseFee: &baseFee}, res) + require.NoError(t, err) +} + +func TestRegisterBaseFeeError(t *testing.T) { + queryClient := mocks.NewEVMQueryClient(t) + RegisterBaseFeeError(queryClient) + res, err := queryClient.BaseFee(rpc.ContextWithHeight(1), &evmtypes.QueryBaseFeeRequest{}) + require.Equal(t, &evmtypes.QueryBaseFeeResponse{}, res) + require.Error(t, err) +} + +func TestRegisterBaseFeeDisabled(t *testing.T) { + queryClient := mocks.NewEVMQueryClient(t) + RegisterBaseFeeDisabled(queryClient) + res, err := queryClient.BaseFee(rpc.ContextWithHeight(1), &evmtypes.QueryBaseFeeRequest{}) + require.Equal(t, &evmtypes.QueryBaseFeeResponse{}, res) + require.NoError(t, err) +} + +// ValidatorAccount +func RegisterValidatorAccount(queryClient *mocks.EVMQueryClient, validator sdk.AccAddress) { + queryClient.On("ValidatorAccount", rpc.ContextWithHeight(1), &evmtypes.QueryValidatorAccountRequest{}). + Return(&evmtypes.QueryValidatorAccountResponse{AccountAddress: validator.String()}, nil) +} + +func RegisterValidatorAccountError(queryClient *mocks.EVMQueryClient) { + queryClient.On("ValidatorAccount", rpc.ContextWithHeight(1), &evmtypes.QueryValidatorAccountRequest{}). + Return(nil, status.Error(codes.InvalidArgument, "empty request")) +} + +func TestRegisterValidatorAccount(t *testing.T) { + queryClient := mocks.NewEVMQueryClient(t) + + validator := sdk.AccAddress(tests.GenerateAddress().Bytes()) + RegisterValidatorAccount(queryClient, validator) + res, err := queryClient.ValidatorAccount(rpc.ContextWithHeight(1), &evmtypes.QueryValidatorAccountRequest{}) + require.Equal(t, &evmtypes.QueryValidatorAccountResponse{AccountAddress: validator.String()}, res) + require.NoError(t, err) +} + +// Code +func RegisterCode(queryClient *mocks.EVMQueryClient, addr common.Address, code []byte) { + queryClient.On("Code", rpc.ContextWithHeight(1), &evmtypes.QueryCodeRequest{Address: addr.String()}). + Return(&evmtypes.QueryCodeResponse{Code: code}, nil) +} + +func RegisterCodeError(queryClient *mocks.EVMQueryClient, addr common.Address) { + queryClient.On("Code", rpc.ContextWithHeight(1), &evmtypes.QueryCodeRequest{Address: addr.String()}). + Return(nil, errortypes.ErrInvalidRequest) +} + +// Storage +func RegisterStorageAt(queryClient *mocks.EVMQueryClient, addr common.Address, key string, storage string) { + queryClient.On("Storage", rpc.ContextWithHeight(1), &evmtypes.QueryStorageRequest{Address: addr.String(), Key: key}). + Return(&evmtypes.QueryStorageResponse{Value: storage}, nil) +} + +func RegisterStorageAtError(queryClient *mocks.EVMQueryClient, addr common.Address, key string) { + queryClient.On("Storage", rpc.ContextWithHeight(1), &evmtypes.QueryStorageRequest{Address: addr.String(), Key: key}). + Return(nil, errortypes.ErrInvalidRequest) +} + +func RegisterAccount(queryClient *mocks.EVMQueryClient, addr common.Address, height int64) { + queryClient.On("Account", rpc.ContextWithHeight(height), &evmtypes.QueryAccountRequest{Address: addr.String()}). + Return(&evmtypes.QueryAccountResponse{ + Balance: "0", + CodeHash: "", + Nonce: 0, + }, + nil, + ) +} + +// Balance +func RegisterBalance(queryClient *mocks.EVMQueryClient, addr common.Address, height int64) { + queryClient.On("Balance", rpc.ContextWithHeight(height), &evmtypes.QueryBalanceRequest{Address: addr.String()}). + Return(&evmtypes.QueryBalanceResponse{Balance: "1"}, nil) +} + +func RegisterBalanceInvalid(queryClient *mocks.EVMQueryClient, addr common.Address, height int64) { + queryClient.On("Balance", rpc.ContextWithHeight(height), &evmtypes.QueryBalanceRequest{Address: addr.String()}). + Return(&evmtypes.QueryBalanceResponse{Balance: "invalid"}, nil) +} + +func RegisterBalanceNegative(queryClient *mocks.EVMQueryClient, addr common.Address, height int64) { + queryClient.On("Balance", rpc.ContextWithHeight(height), &evmtypes.QueryBalanceRequest{Address: addr.String()}). + Return(&evmtypes.QueryBalanceResponse{Balance: "-1"}, nil) +} + +func RegisterBalanceError(queryClient *mocks.EVMQueryClient, addr common.Address, height int64) { + queryClient.On("Balance", rpc.ContextWithHeight(height), &evmtypes.QueryBalanceRequest{Address: addr.String()}). + Return(nil, errortypes.ErrInvalidRequest) +} diff --git a/rpc/backend/feemarket_query_client_test.go b/rpc/backend/feemarket_query_client_test.go new file mode 100644 index 0000000000..d24507eae4 --- /dev/null +++ b/rpc/backend/feemarket_query_client_test.go @@ -0,0 +1,22 @@ +package backend + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + feemarkettypes "github.com/evmos/ethermint/x/feemarket/types" + + "github.com/zeta-chain/zetacore/rpc/backend/mocks" + rpc "github.com/zeta-chain/zetacore/rpc/types" +) + +var _ feemarkettypes.QueryClient = &mocks.FeeMarketQueryClient{} + +// Params +func RegisterFeeMarketParams(feeMarketClient *mocks.FeeMarketQueryClient, height int64) { + feeMarketClient.On("Params", rpc.ContextWithHeight(height), &feemarkettypes.QueryParamsRequest{}). + Return(&feemarkettypes.QueryParamsResponse{Params: feemarkettypes.DefaultParams()}, nil) +} + +func RegisterFeeMarketParamsError(feeMarketClient *mocks.FeeMarketQueryClient, height int64) { + feeMarketClient.On("Params", rpc.ContextWithHeight(height), &feemarkettypes.QueryParamsRequest{}). + Return(nil, sdkerrors.ErrInvalidRequest) +} diff --git a/rpc/backend/filters_test.go b/rpc/backend/filters_test.go new file mode 100644 index 0000000000..486e51ebf2 --- /dev/null +++ b/rpc/backend/filters_test.go @@ -0,0 +1,121 @@ +package backend + +import ( + "encoding/json" + + tmtypes "github.com/cometbft/cometbft/types" + "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + evmtypes "github.com/evmos/ethermint/x/evm/types" + + "github.com/zeta-chain/zetacore/rpc/backend/mocks" + ethrpc "github.com/zeta-chain/zetacore/rpc/types" +) + +func (suite *BackendTestSuite) TestGetLogs() { + _, bz := suite.buildEthereumTx() + block := tmtypes.MakeBlock(1, []tmtypes.Tx{bz}, nil, nil) + logs := make([]*evmtypes.Log, 0, 1) + var log evmtypes.Log + json.Unmarshal( + []byte{0x7b, 0x22, 0x74, 0x65, 0x73, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x22, 0x7d}, + &log, + ) + logs = append(logs, &log) + + testCases := []struct { + name string + registerMock func(hash common.Hash) + blockHash common.Hash + expLogs [][]*ethtypes.Log + expPass bool + }{ + { + "fail - no block with that hash", + func(hash common.Hash) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHashNotFound(client, hash, bz) + }, + common.Hash{}, + nil, + false, + }, + { + "fail - error fetching block by hash", + func(hash common.Hash) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHashError(client, hash, bz) + }, + common.Hash{}, + nil, + false, + }, + { + "fail - error getting block results", + func(hash common.Hash) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHash(client, hash, bz) + RegisterBlockResultsError(client, 1) + }, + common.Hash{}, + nil, + false, + }, + { + "success - getting logs with block hash", + func(hash common.Hash) { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHash(client, hash, bz) + RegisterBlockResultsWithEventLog(client, ethrpc.BlockNumber(1).Int64()) + }, + common.BytesToHash(block.Hash()), + [][]*ethtypes.Log{evmtypes.LogsToEthereum(logs)}, + true, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() + + tc.registerMock(tc.blockHash) + logs, err := suite.backend.GetLogs(tc.blockHash) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(tc.expLogs, logs) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestBloomStatus() { + testCases := []struct { + name string + registerMock func() + expResult uint64 + expPass bool + }{ + { + "pass - returns the BloomBitsBlocks and the number of processed sections maintained", + func() {}, + 4096, + true, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() + + tc.registerMock() + bloom, _ := suite.backend.BloomStatus() + + if tc.expPass { + suite.Require().Equal(tc.expResult, bloom) + } + }) + } +} diff --git a/rpc/backend/mocks/client.go b/rpc/backend/mocks/client.go index dc4e2428cd..16090fcc69 100644 --- a/rpc/backend/mocks/client.go +++ b/rpc/backend/mocks/client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by mockery. DO NOT EDIT. package mocks @@ -459,6 +459,52 @@ func (_m *Client) GenesisChunked(_a0 context.Context, _a1 uint) (*coretypes.Resu return r0, r1 } +// Header provides a mock function with given fields: ctx, height +func (_m *Client) Header(ctx context.Context, height *int64) (*coretypes.ResultHeader, error) { + ret := _m.Called(ctx, height) + + var r0 *coretypes.ResultHeader + if rf, ok := ret.Get(0).(func(context.Context, *int64) *coretypes.ResultHeader); ok { + r0 = rf(ctx, height) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*coretypes.ResultHeader) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *int64) error); ok { + r1 = rf(ctx, height) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// HeaderByHash provides a mock function with given fields: ctx, hash +func (_m *Client) HeaderByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultHeader, error) { + ret := _m.Called(ctx, hash) + + var r0 *coretypes.ResultHeader + if rf, ok := ret.Get(0).(func(context.Context, bytes.HexBytes) *coretypes.ResultHeader); ok { + r0 = rf(ctx, hash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*coretypes.ResultHeader) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, bytes.HexBytes) error); ok { + r1 = rf(ctx, hash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // Health provides a mock function with given fields: _a0 func (_m *Client) Health(_a0 context.Context) (*coretypes.ResultHealth, error) { ret := _m.Called(_a0) @@ -838,4 +884,4 @@ func NewClient(t mockConstructorTestingTNewClient) *Client { t.Cleanup(func() { mock.AssertExpectations(t) }) return mock -} +} \ No newline at end of file diff --git a/rpc/backend/node_info_test.go b/rpc/backend/node_info_test.go new file mode 100644 index 0000000000..a98c433f11 --- /dev/null +++ b/rpc/backend/node_info_test.go @@ -0,0 +1,359 @@ +package backend + +import ( + "fmt" + "math/big" + + tmrpcclient "github.com/cometbft/cometbft/rpc/client" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/evmos/ethermint/crypto/ethsecp256k1" + ethermint "github.com/evmos/ethermint/types" + "github.com/spf13/viper" + "google.golang.org/grpc/metadata" + + "github.com/zeta-chain/zetacore/rpc/backend/mocks" +) + +func (suite *BackendTestSuite) TestRPCMinGasPrice() { + testCases := []struct { + name string + registerMock func() + expMinGasPrice int64 + expPass bool + }{ + { + "pass - default gas price", + func() { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterParamsWithoutHeaderError(queryClient, 1) + }, + ethermint.DefaultGasPrice, + true, + }, + { + "pass - min gas price is 0", + func() { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterParamsWithoutHeader(queryClient, 1) + }, + ethermint.DefaultGasPrice, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock() + + minPrice := suite.backend.RPCMinGasPrice() + if tc.expPass { + suite.Require().Equal(tc.expMinGasPrice, minPrice) + } else { + suite.Require().NotEqual(tc.expMinGasPrice, minPrice) + } + }) + } +} + +func (suite *BackendTestSuite) TestSetGasPrice() { + defaultGasPrice := (*hexutil.Big)(big.NewInt(1)) + testCases := []struct { + name string + registerMock func() + gasPrice hexutil.Big + expOutput bool + }{ + { + "pass - cannot get server config", + func() { + suite.backend.clientCtx.Viper = viper.New() + }, + *defaultGasPrice, + false, + }, + { + "pass - cannot find coin denom", + func() { + suite.backend.clientCtx.Viper = viper.New() + suite.backend.clientCtx.Viper.Set("telemetry.global-labels", []interface{}{}) + }, + *defaultGasPrice, + false, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock() + output := suite.backend.SetGasPrice(tc.gasPrice) + suite.Require().Equal(tc.expOutput, output) + }) + } +} + +// TODO (https://github.com/zeta-chain/node/issues/2302): Combine these 2 into one test since the code is identical +func (suite *BackendTestSuite) TestListAccounts() { + testCases := []struct { + name string + registerMock func() + expAddr []common.Address + expPass bool + }{ + { + "pass - returns empty address", + func() {}, + []common.Address{}, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock() + + output, err := suite.backend.ListAccounts() + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(tc.expAddr, output) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestAccounts() { + testCases := []struct { + name string + registerMock func() + expAddr []common.Address + expPass bool + }{ + { + "pass - returns empty address", + func() {}, + []common.Address{}, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock() + + output, err := suite.backend.Accounts() + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(tc.expAddr, output) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestSyncing() { + testCases := []struct { + name string + registerMock func() + expResponse interface{} + expPass bool + }{ + { + "fail - Can't get status", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterStatusError(client) + }, + false, + false, + }, + { + "pass - Node not catching up", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterStatus(client) + }, + false, + true, + }, + { + "pass - Node is catching up", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterStatus(client) + status, _ := client.Status(suite.backend.ctx) + status.SyncInfo.CatchingUp = true + }, + map[string]interface{}{ + "startingBlock": hexutil.Uint64(0), + "currentBlock": hexutil.Uint64(0), + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock() + + output, err := suite.backend.Syncing() + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(tc.expResponse, output) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestSetEtherbase() { + testCases := []struct { + name string + registerMock func() + etherbase common.Address + expResult bool + }{ + { + "pass - Failed to get coinbase address", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterStatusError(client) + }, + common.Address{}, + false, + }, + { + "pass - the minimum fee is not set", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterStatus(client) + RegisterValidatorAccount(queryClient, suite.acc) + }, + common.Address{}, + false, + }, + { + "fail - error querying for account ", + func() { + var header metadata.MD + client := suite.backend.clientCtx.Client.(*mocks.Client) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterStatus(client) + RegisterValidatorAccount(queryClient, suite.acc) + RegisterParams(queryClient, &header, 1) + c := sdk.NewDecCoin("azeta", sdk.NewIntFromBigInt(big.NewInt(1))) + suite.backend.cfg.SetMinGasPrices(sdk.DecCoins{c}) + delAddr, _ := suite.backend.GetCoinbase() + // account, _ := suite.backend.clientCtx.AccountRetriever.GetAccount(suite.backend.clientCtx, delAddr) + delCommonAddr := common.BytesToAddress(delAddr.Bytes()) + request := &authtypes.QueryAccountRequest{Address: sdk.AccAddress(delCommonAddr.Bytes()).String()} + requestMarshal, _ := request.Marshal() + RegisterABCIQueryWithOptionsError( + client, + "/cosmos.auth.v1beta1.Query/Account", + requestMarshal, + tmrpcclient.ABCIQueryOptions{Height: int64(1), Prove: false}, + ) + }, + common.Address{}, + false, + }, + // TODO (https://github.com/zeta-chain/node/issues/2302): Finish this test case once ABCIQuery GetAccount is fixed + //{ + // "pass - set the etherbase for the miner", + // func() { + // client := suite.backend.clientCtx.Client.(*mocks.Client) + // queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + // RegisterStatus(client) + // RegisterValidatorAccount(queryClient, suite.acc) + // c := sdk.NewDecCoin("azeta", sdk.NewIntFromBigInt(big.NewInt(1))) + // suite.backend.cfg.SetMinGasPrices(sdk.DecCoins{c}) + // delAddr, _ := suite.backend.GetCoinbase() + // account, _ := suite.backend.clientCtx.AccountRetriever.GetAccount(suite.backend.clientCtx, delAddr) + // delCommonAddr := common.BytesToAddress(delAddr.Bytes()) + // request := &authtypes.QueryAccountRequest{Address: sdk.AccAddress(delCommonAddr.Bytes()).String()} + // requestMarshal, _ := request.Marshal() + // RegisterABCIQueryAccount( + // client, + // requestMarshal, + // tmrpcclient.ABCIQueryOptions{Height: int64(1), Prove: false}, + // account, + // ) + // }, + // common.Address{}, + // false, + //}, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock() + + output := suite.backend.SetEtherbase(tc.etherbase) + + suite.Require().Equal(tc.expResult, output) + }) + } +} + +func (suite *BackendTestSuite) TestImportRawKey() { + priv, _ := ethsecp256k1.GenerateKey() + privHex := common.Bytes2Hex(priv.Bytes()) + pubAddr := common.BytesToAddress(priv.PubKey().Address().Bytes()) + + testCases := []struct { + name string + registerMock func() + privKey string + password string + expAddr common.Address + expPass bool + }{ + { + "fail - not a valid private key", + func() {}, + "", + "", + common.Address{}, + false, + }, + { + "pass - returning correct address", + func() {}, + privHex, + "", + pubAddr, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock() + + output, err := suite.backend.ImportRawKey(tc.privKey, tc.password) + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(tc.expAddr, output) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/rpc/backend/sign_tx_test.go b/rpc/backend/sign_tx_test.go new file mode 100644 index 0000000000..153e326cd6 --- /dev/null +++ b/rpc/backend/sign_tx_test.go @@ -0,0 +1,274 @@ +package backend + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/crypto" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + ethtypes "github.com/ethereum/go-ethereum/core/types" + goethcrypto "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/signer/core/apitypes" + "github.com/evmos/ethermint/crypto/ethsecp256k1" + "github.com/evmos/ethermint/tests" + evmtypes "github.com/evmos/ethermint/x/evm/types" + "google.golang.org/grpc/metadata" + + "github.com/zeta-chain/zetacore/rpc/backend/mocks" +) + +func (suite *BackendTestSuite) TestSendTransaction() { + gasPrice := new(hexutil.Big) + gas := hexutil.Uint64(1) + zeroGas := hexutil.Uint64(0) + toAddr := tests.GenerateAddress() + priv, err := ethsecp256k1.GenerateKey() + suite.Require().NoError(err) + from := common.BytesToAddress(priv.PubKey().Address().Bytes()) + nonce := hexutil.Uint64(1) + baseFee := sdk.NewInt(1) + callArgsDefault := evmtypes.TransactionArgs{ + From: &from, + To: &toAddr, + GasPrice: gasPrice, + Gas: &gas, + Nonce: &nonce, + } + + hash := common.Hash{} + + testCases := []struct { + name string + registerMock func() + args evmtypes.TransactionArgs + expHash common.Hash + expPass bool + }{ + { + "fail - Can't find account in Keyring", + func() {}, + evmtypes.TransactionArgs{}, + hash, + false, + }, + { + "fail - Block error can't set Tx defaults", + func() { + var header metadata.MD + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + client := suite.backend.clientCtx.Client.(*mocks.Client) + armor := crypto.EncryptArmorPrivKey(priv, "", "eth_secp256k1") + suite.backend.clientCtx.Keyring.ImportPrivKey("test_key", armor, "") + RegisterParams(queryClient, &header, 1) + RegisterBlockError(client, 1) + }, + callArgsDefault, + hash, + false, + }, + { + "fail - Cannot validate transaction gas set to 0", + func() { + var header metadata.MD + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + client := suite.backend.clientCtx.Client.(*mocks.Client) + armor := crypto.EncryptArmorPrivKey(priv, "", "eth_secp256k1") + suite.backend.clientCtx.Keyring.ImportPrivKey("test_key", armor, "") + RegisterParams(queryClient, &header, 1) + RegisterBlock(client, 1, nil) + RegisterBlockResults(client, 1) + RegisterBaseFee(queryClient, baseFee) + }, + evmtypes.TransactionArgs{ + From: &from, + To: &toAddr, + GasPrice: gasPrice, + Gas: &zeroGas, + Nonce: &nonce, + }, + hash, + false, + }, + { + "fail - Cannot broadcast transaction", + func() { + var header metadata.MD + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + client := suite.backend.clientCtx.Client.(*mocks.Client) + armor := crypto.EncryptArmorPrivKey(priv, "", "eth_secp256k1") + suite.backend.clientCtx.Keyring.ImportPrivKey("test_key", armor, "") + RegisterParams(queryClient, &header, 1) + RegisterBlock(client, 1, nil) + RegisterBlockResults(client, 1) + RegisterBaseFee(queryClient, baseFee) + RegisterParamsWithoutHeader(queryClient, 1) + ethSigner := ethtypes.LatestSigner(suite.backend.ChainConfig()) + msg := callArgsDefault.ToTransaction() + msg.Sign(ethSigner, suite.backend.clientCtx.Keyring) + tx, err := msg.BuildTx(suite.backend.clientCtx.TxConfig.NewTxBuilder(), "azeta") + suite.Require().NoError(err) + txEncoder := suite.backend.clientCtx.TxConfig.TxEncoder() + txBytes, err := txEncoder(tx) + suite.Require().NoError(err) + RegisterBroadcastTxError(client, txBytes) + }, + callArgsDefault, + common.Hash{}, + false, + }, + { + "pass - Return the transaction hash", + func() { + var header metadata.MD + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + client := suite.backend.clientCtx.Client.(*mocks.Client) + armor := crypto.EncryptArmorPrivKey(priv, "", "eth_secp256k1") + suite.backend.clientCtx.Keyring.ImportPrivKey("test_key", armor, "") + RegisterParams(queryClient, &header, 1) + RegisterBlock(client, 1, nil) + RegisterBlockResults(client, 1) + RegisterBaseFee(queryClient, baseFee) + RegisterParamsWithoutHeader(queryClient, 1) + ethSigner := ethtypes.LatestSigner(suite.backend.ChainConfig()) + msg := callArgsDefault.ToTransaction() + msg.Sign(ethSigner, suite.backend.clientCtx.Keyring) + tx, err := msg.BuildTx(suite.backend.clientCtx.TxConfig.NewTxBuilder(), "azeta") + suite.Require().NoError(err) + txEncoder := suite.backend.clientCtx.TxConfig.TxEncoder() + txBytes, err := txEncoder(tx) + suite.Require().NoError(err) + RegisterBroadcastTx(client, txBytes) + }, + callArgsDefault, + hash, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock() + + if tc.expPass { + // Sign the transaction and get the hash + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterParamsWithoutHeader(queryClient, 1) + ethSigner := ethtypes.LatestSigner(suite.backend.ChainConfig()) + msg := callArgsDefault.ToTransaction() + msg.Sign(ethSigner, suite.backend.clientCtx.Keyring) + tc.expHash = msg.AsTransaction().Hash() + } + responseHash, err := suite.backend.SendTransaction(tc.args) + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(tc.expHash, responseHash) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestSign() { + from, priv := tests.NewAddrKey() + testCases := []struct { + name string + registerMock func() + fromAddr common.Address + inputBz hexutil.Bytes + expPass bool + }{ + { + "fail - can't find key in Keyring", + func() {}, + from, + nil, + false, + }, + { + "pass - sign nil data", + func() { + armor := crypto.EncryptArmorPrivKey(priv, "", "eth_secp256k1") + suite.backend.clientCtx.Keyring.ImportPrivKey("test_key", armor, "") + }, + from, + nil, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock() + + responseBz, err := suite.backend.Sign(tc.fromAddr, tc.inputBz) + if tc.expPass { + signature, _, err := suite.backend.clientCtx.Keyring.SignByAddress( + (sdk.AccAddress)(from.Bytes()), + tc.inputBz, + ) + signature[goethcrypto.RecoveryIDOffset] += 27 + suite.Require().NoError(err) + suite.Require().Equal((hexutil.Bytes)(signature), responseBz) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestSignTypedData() { + from, priv := tests.NewAddrKey() + testCases := []struct { + name string + registerMock func() + fromAddr common.Address + inputTypedData apitypes.TypedData + expPass bool + }{ + { + "fail - can't find key in Keyring", + func() {}, + from, + apitypes.TypedData{}, + false, + }, + { + "fail - empty TypeData", + func() { + armor := crypto.EncryptArmorPrivKey(priv, "", "eth_secp256k1") + suite.backend.clientCtx.Keyring.ImportPrivKey("test_key", armor, "") + }, + from, + apitypes.TypedData{}, + false, + }, + // TODO (https://github.com/zeta-chain/node/issues/2302): Generate a TypedData msg + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock() + + responseBz, err := suite.backend.SignTypedData(tc.fromAddr, tc.inputTypedData) + + if tc.expPass { + sigHash, _, err := apitypes.TypedDataAndHash(tc.inputTypedData) + suite.Require().NoError(err) + signature, _, err := suite.backend.clientCtx.Keyring.SignByAddress( + (sdk.AccAddress)(from.Bytes()), + sigHash, + ) + signature[goethcrypto.RecoveryIDOffset] += 27 + suite.Require().NoError(err) + suite.Require().Equal((hexutil.Bytes)(signature), responseBz) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/rpc/backend/tracing_test.go b/rpc/backend/tracing_test.go new file mode 100644 index 0000000000..feb6ca2c42 --- /dev/null +++ b/rpc/backend/tracing_test.go @@ -0,0 +1,316 @@ +package backend + +import ( + "fmt" + + dbm "github.com/cometbft/cometbft-db" + abci "github.com/cometbft/cometbft/abci/types" + tmlog "github.com/cometbft/cometbft/libs/log" + tmrpctypes "github.com/cometbft/cometbft/rpc/core/types" + "github.com/cometbft/cometbft/types" + "github.com/cosmos/cosmos-sdk/crypto" + "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/evmos/ethermint/crypto/ethsecp256k1" + "github.com/evmos/ethermint/indexer" + evmtypes "github.com/evmos/ethermint/x/evm/types" + + "github.com/zeta-chain/zetacore/rpc/backend/mocks" +) + +func (suite *BackendTestSuite) TestTraceTransaction() { + msgEthereumTx, _ := suite.buildEthereumTx() + msgEthereumTx2, _ := suite.buildEthereumTx() + + txHash := msgEthereumTx.AsTransaction().Hash() + txHash2 := msgEthereumTx2.AsTransaction().Hash() + + priv, err := ethsecp256k1.GenerateKey() + suite.Require().NoError(err) + from := common.BytesToAddress(priv.PubKey().Address().Bytes()) + + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterParamsWithoutHeader(queryClient, 1) + + armor := crypto.EncryptArmorPrivKey(priv, "", "eth_secp256k1") + suite.backend.clientCtx.Keyring.ImportPrivKey("test_key", armor, "") + ethSigner := ethtypes.LatestSigner(suite.backend.ChainConfig()) + + txEncoder := suite.backend.clientCtx.TxConfig.TxEncoder() + + msgEthereumTx.From = from.String() + msgEthereumTx.Sign(ethSigner, suite.signer) + tx, err := msgEthereumTx.BuildTx(suite.backend.clientCtx.TxConfig.NewTxBuilder(), "azeta") + suite.Require().NoError(err) + txBz, err := txEncoder(tx) + suite.Require().NoError(err) + + msgEthereumTx2.From = from.String() + msgEthereumTx2.Sign(ethSigner, suite.signer) + tx2, err := msgEthereumTx2.BuildTx(suite.backend.clientCtx.TxConfig.NewTxBuilder(), "azeta") + suite.Require().NoError(err) + txBz2, err := txEncoder(tx2) + suite.Require().NoError(err) + + testCases := []struct { + name string + txHash common.Hash + registerMock func() + block *types.Block + responseBlock []*abci.ResponseDeliverTx + expResult interface{} + expPass bool + }{ + { + "fail - tx not found", + txHash, + func() {}, + &types.Block{Header: types.Header{Height: 1}, Data: types.Data{Txs: []types.Tx{}}}, + []*abci.ResponseDeliverTx{ + { + Code: 0, + Events: []abci.Event{ + {Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{ + {Key: "ethereumTxHash", Value: txHash.Hex()}, + {Key: "txIndex", Value: "0"}, + {Key: "amount", Value: "1000"}, + {Key: "txGasUsed", Value: "21000"}, + {Key: "txHash", Value: ""}, + {Key: "recipient", Value: "0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7"}, + }}, + }, + }, + }, + nil, + false, + }, + { + "fail - block not found", + txHash, + func() { + // var header metadata.MD + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockError(client, 1) + }, + &types.Block{Header: types.Header{Height: 1}, Data: types.Data{Txs: []types.Tx{txBz}}}, + []*abci.ResponseDeliverTx{ + { + Code: 0, + Events: []abci.Event{ + {Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{ + {Key: "ethereumTxHash", Value: txHash.Hex()}, + {Key: "txIndex", Value: "0"}, + {Key: "amount", Value: "1000"}, + {Key: "txGasUsed", Value: "21000"}, + {Key: "txHash", Value: ""}, + {Key: "recipient", Value: "0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7"}, + }}, + }, + }, + }, + map[string]interface{}{"test": "hello"}, + false, + }, + { + "pass - transaction found in a block with multiple transactions", + txHash2, // tx1 is predecessor of tx2 + func() { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockMultipleTxs(client, 1, []types.Tx{txBz, txBz2}) + RegisterTraceTransactionWithPredecessors( + queryClient, + msgEthereumTx2, + []*evmtypes.MsgEthereumTx{msgEthereumTx}, + ) + txResults := []*abci.ResponseDeliverTx{ + { + Code: 0, + Events: []abci.Event{ + {Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{ + {Key: "ethereumTxHash", Value: txHash.Hex()}, + {Key: "txIndex", Value: "0"}, + {Key: "amount", Value: "1000"}, + {Key: "txGasUsed", Value: "21000"}, + {Key: "txHash", Value: ""}, + {Key: "recipient", Value: "0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7"}, + }}, + }, + }, + { + Code: 0, + Events: []abci.Event{ + {Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{ + {Key: "ethereumTxHash", Value: txHash2.Hex()}, + {Key: "txIndex", Value: "1"}, + {Key: "amount", Value: "1000"}, + {Key: "txGasUsed", Value: "21000"}, + {Key: "txHash", Value: ""}, + {Key: "recipient", Value: "0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7"}, + }}, + }, + }, + } + + RegisterBlockResultsWithTxResults(client, 1, txResults) + }, + &types.Block{ + Header: types.Header{Height: 1, ChainID: ChainID}, + Data: types.Data{Txs: []types.Tx{txBz, txBz2}}, + }, + []*abci.ResponseDeliverTx{ + { + Code: 0, + Events: []abci.Event{ + {Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{ + {Key: "ethereumTxHash", Value: txHash.Hex()}, + {Key: "txIndex", Value: "0"}, + {Key: "amount", Value: "1000"}, + {Key: "txGasUsed", Value: "21000"}, + {Key: "txHash", Value: ""}, + {Key: "recipient", Value: "0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7"}, + }}, + }, + }, + { + Code: 0, + Events: []abci.Event{ + {Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{ + {Key: "ethereumTxHash", Value: txHash2.Hex()}, + {Key: "txIndex", Value: "1"}, + {Key: "amount", Value: "1000"}, + {Key: "txGasUsed", Value: "21000"}, + {Key: "txHash", Value: ""}, + {Key: "recipient", Value: "0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7"}, + }}, + }, + }, + }, + map[string]interface{}{"test": "hello"}, + true, + }, + { + "pass - transaction found", + txHash, + func() { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlock(client, 1, txBz) + RegisterTraceTransaction(queryClient, msgEthereumTx) + txResults := []*abci.ResponseDeliverTx{ + { + Code: 0, + Events: []abci.Event{ + {Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{ + {Key: "ethereumTxHash", Value: txHash.Hex()}, + {Key: "txIndex", Value: "0"}, + {Key: "amount", Value: "1000"}, + {Key: "txGasUsed", Value: "21000"}, + {Key: "txHash", Value: ""}, + {Key: "recipient", Value: "0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7"}, + }}, + }, + }, + } + RegisterBlockResultsWithTxResults(client, 1, txResults) + + }, + &types.Block{Header: types.Header{Height: 1}, Data: types.Data{Txs: []types.Tx{txBz}}}, + []*abci.ResponseDeliverTx{ + { + Code: 0, + Events: []abci.Event{ + {Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{ + {Key: "ethereumTxHash", Value: txHash.Hex()}, + {Key: "txIndex", Value: "0"}, + {Key: "amount", Value: "1000"}, + {Key: "txGasUsed", Value: "21000"}, + {Key: "txHash", Value: ""}, + {Key: "recipient", Value: "0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7"}, + }}, + }, + }, + }, + map[string]interface{}{"test": "hello"}, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock() + + db := dbm.NewMemDB() + suite.backend.indexer = indexer.NewKVIndexer(db, tmlog.NewNopLogger(), suite.backend.clientCtx) + + err := suite.backend.indexer.IndexBlock(tc.block, tc.responseBlock) + suite.Require().NoError(err) + txResult, err := suite.backend.TraceTransaction(tc.txHash, nil) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(tc.expResult, txResult) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestTraceBlock() { + msgEthTx, bz := suite.buildEthereumTx() + emptyBlock := types.MakeBlock(1, []types.Tx{}, nil, nil) + emptyBlock.ChainID = ChainID + filledBlock := types.MakeBlock(1, []types.Tx{bz}, nil, nil) + filledBlock.ChainID = ChainID + resBlockEmpty := tmrpctypes.ResultBlock{Block: emptyBlock, BlockID: emptyBlock.LastBlockID} + resBlockFilled := tmrpctypes.ResultBlock{Block: filledBlock, BlockID: filledBlock.LastBlockID} + + testCases := []struct { + name string + registerMock func() + expTraceResults []*evmtypes.TxTraceResult + resBlock *tmrpctypes.ResultBlock + config *evmtypes.TraceConfig + expPass bool + }{ + { + "pass - no transaction returning empty array", + func() {}, + []*evmtypes.TxTraceResult{}, + &resBlockEmpty, + &evmtypes.TraceConfig{}, + true, + }, + { + "fail - cannot unmarshal data", + func() { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterTraceBlock(queryClient, []*evmtypes.MsgEthereumTx{msgEthTx}) + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockResults(client, 1) + }, + []*evmtypes.TxTraceResult{}, + &resBlockFilled, + &evmtypes.TraceConfig{}, + false, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("case %s", tc.name), func() { + suite.SetupTest() // reset test and queries + tc.registerMock() + + traceResults, err := suite.backend.TraceBlock(1, tc.config, tc.resBlock) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(tc.expTraceResults, traceResults) + } else { + suite.Require().Error(err) + } + }) + } +} diff --git a/rpc/backend/tx_info.go b/rpc/backend/tx_info.go index e08f36db42..d43dc26177 100644 --- a/rpc/backend/tx_info.go +++ b/rpc/backend/tx_info.go @@ -44,7 +44,7 @@ func (b *Backend) GetTransactionByHash(txHash common.Hash) (*rpctypes.RPCTransac resBlock, err := b.TendermintBlockByNumber(rpctypes.BlockNumber(res.Height)) if err != nil { b.logger.Debug("block not found", "height", res.Height, "error", err.Error()) - return nil, nil + return nil, err } blockRes, err := b.TendermintBlockResultByNumber(&res.Height) @@ -388,9 +388,10 @@ func (b *Backend) GetTransactionByBlockNumberAndIndex( func (b *Backend) GetTxByEthHash(hash common.Hash) (*ethermint.TxResult, *rpctypes.TxResultAdditionalFields, error) { if b.indexer != nil { txRes, err := b.indexer.GetByTxHash(hash) - if err == nil { - return txRes, nil, nil + if err != nil { + return nil, nil, err } + return txRes, nil, nil } // fallback to tendermint tx indexer diff --git a/rpc/backend/tx_info_test.go b/rpc/backend/tx_info_test.go new file mode 100644 index 0000000000..9827c6eff1 --- /dev/null +++ b/rpc/backend/tx_info_test.go @@ -0,0 +1,639 @@ +package backend + +import ( + "fmt" + "math/big" + + dbm "github.com/cometbft/cometbft-db" + abci "github.com/cometbft/cometbft/abci/types" + tmlog "github.com/cometbft/cometbft/libs/log" + tmrpctypes "github.com/cometbft/cometbft/rpc/core/types" + "github.com/cometbft/cometbft/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/evmos/ethermint/indexer" + ethermint "github.com/evmos/ethermint/types" + evmtypes "github.com/evmos/ethermint/x/evm/types" + "google.golang.org/grpc/metadata" + + "github.com/zeta-chain/zetacore/rpc/backend/mocks" + rpctypes "github.com/zeta-chain/zetacore/rpc/types" +) + +func (suite *BackendTestSuite) TestGetTransactionByHash() { + msgEthereumTx, _ := suite.buildEthereumTx() + txHash := msgEthereumTx.AsTransaction().Hash() + + txBz := suite.signAndEncodeEthTx(msgEthereumTx) + block := &types.Block{Header: types.Header{Height: 1, ChainID: "test"}, Data: types.Data{Txs: []types.Tx{txBz}}} + responseDeliver := []*abci.ResponseDeliverTx{ + { + Code: 0, + Events: []abci.Event{ + {Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{ + {Key: "ethereumTxHash", Value: txHash.Hex()}, + {Key: "txIndex", Value: "0"}, + {Key: "amount", Value: "1000"}, + {Key: "txGasUsed", Value: "21000"}, + {Key: "txHash", Value: ""}, + {Key: "recipient", Value: ""}, + }}, + }, + }, + } + + rpcTransaction, err := rpctypes.NewRPCTransaction( + msgEthereumTx.AsTransaction(), + common.Hash{}, + 0, + 0, + big.NewInt(1), + suite.backend.chainID, + ) + suite.Require().NoError(err) + + testCases := []struct { + name string + registerMock func() + tx *evmtypes.MsgEthereumTx + expRPCTx *rpctypes.RPCTransaction + expPass bool + }{ + { + "fail - Block error", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockError(client, 1) + }, + msgEthereumTx, + rpcTransaction, + false, + }, + { + "fail - Block Result error", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlock(client, 1, txBz) + RegisterBlockResultsError(client, 1) + }, + msgEthereumTx, + nil, + true, + }, + { + "pass - Base fee error", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBlock(client, 1, txBz) + RegisterBlockResults(client, 1) + RegisterBaseFeeError(queryClient) + }, + msgEthereumTx, + rpcTransaction, + true, + }, + { + "pass - Transaction found and returned", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBlock(client, 1, txBz) + RegisterBlockResults(client, 1) + RegisterBaseFee(queryClient, sdk.NewInt(1)) + }, + msgEthereumTx, + rpcTransaction, + true, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() // reset + tc.registerMock() + + db := dbm.NewMemDB() + suite.backend.indexer = indexer.NewKVIndexer(db, tmlog.NewNopLogger(), suite.backend.clientCtx) + err := suite.backend.indexer.IndexBlock(block, responseDeliver) + suite.Require().NoError(err) + + rpcTx, err := suite.backend.GetTransactionByHash(common.HexToHash(tc.tx.Hash)) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(rpcTx, tc.expRPCTx) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestGetTransactionsByHashPending() { + msgEthereumTx, bz := suite.buildEthereumTx() + rpcTransaction, err := rpctypes.NewRPCTransaction( + msgEthereumTx.AsTransaction(), + common.Hash{}, + 0, + 0, + big.NewInt(1), + suite.backend.chainID, + ) + suite.Require().NoError(err) + + testCases := []struct { + name string + registerMock func() + tx *evmtypes.MsgEthereumTx + expRPCTx *rpctypes.RPCTransaction + expPass bool + }{ + { + "fail - Pending transactions returns error", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterUnconfirmedTxsError(client, nil) + }, + msgEthereumTx, + nil, + true, + }, + { + "fail - Tx not found return nil", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterUnconfirmedTxs(client, nil, nil) + }, + msgEthereumTx, + nil, + true, + }, + { + "pass - Tx found and returned", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterUnconfirmedTxs(client, nil, types.Txs{bz}) + }, + msgEthereumTx, + rpcTransaction, + true, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() // reset + tc.registerMock() + + rpcTx, err := suite.backend.getTransactionByHashPending(common.HexToHash(tc.tx.Hash)) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(rpcTx, tc.expRPCTx) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestGetTxByEthHash() { + msgEthereumTx, bz := suite.buildEthereumTx() + rpcTransaction, err := rpctypes.NewRPCTransaction( + msgEthereumTx.AsTransaction(), + common.Hash{}, + 0, + 0, + big.NewInt(1), + suite.backend.chainID, + ) + suite.Require().NoError(err) + + testCases := []struct { + name string + registerMock func() + tx *evmtypes.MsgEthereumTx + expRPCTx *rpctypes.RPCTransaction + expPass bool + }{ + { + "fail - Indexer disabled can't find transaction", + func() { + suite.backend.indexer = nil + client := suite.backend.clientCtx.Client.(*mocks.Client) + query := fmt.Sprintf( + "%s.%s='%s'", + evmtypes.TypeMsgEthereumTx, + evmtypes.AttributeKeyEthereumTxHash, + common.HexToHash(msgEthereumTx.Hash).Hex(), + ) + RegisterTxSearch(client, query, bz) + }, + msgEthereumTx, + rpcTransaction, + false, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() // reset + tc.registerMock() + + rpcTx, _, err := suite.backend.GetTxByEthHash(common.HexToHash(tc.tx.Hash)) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(rpcTx, tc.expRPCTx) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestGetTransactionByBlockHashAndIndex() { + _, bz := suite.buildEthereumTx() + + testCases := []struct { + name string + registerMock func() + blockHash common.Hash + expRPCTx *rpctypes.RPCTransaction + expPass bool + }{ + { + "pass - block not found", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHashError(client, common.Hash{}, bz) + }, + common.Hash{}, + nil, + true, + }, + { + "pass - Block results error", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockByHash(client, common.Hash{}, bz) + RegisterBlockResultsError(client, 1) + }, + common.Hash{}, + nil, + true, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() // reset + tc.registerMock() + + rpcTx, err := suite.backend.GetTransactionByBlockHashAndIndex(tc.blockHash, 1) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(rpcTx, tc.expRPCTx) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestGetTransactionByBlockAndIndex() { + msgEthTx, bz := suite.buildEthereumTx() + + defaultBlock := types.MakeBlock(1, []types.Tx{bz}, nil, nil) + defaultResponseDeliverTx := []*abci.ResponseDeliverTx{ + { + Code: 0, + Events: []abci.Event{ + {Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{ + {Key: "ethereumTxHash", Value: common.HexToHash(msgEthTx.Hash).Hex()}, + {Key: "txIndex", Value: "0"}, + {Key: "amount", Value: "1000"}, + {Key: "txGasUsed", Value: "21000"}, + {Key: "txHash", Value: ""}, + {Key: "recipient", Value: ""}, + }}, + }, + }, + } + + txFromMsg, err := rpctypes.NewTransactionFromMsg( + msgEthTx, + common.BytesToHash(defaultBlock.Hash().Bytes()), + 1, + 0, + big.NewInt(1), + suite.backend.chainID, + nil, + ) + suite.Require().NoError(err) + + testCases := []struct { + name string + registerMock func() + block *tmrpctypes.ResultBlock + idx hexutil.Uint + expRPCTx *rpctypes.RPCTransaction + expPass bool + }{ + { + "pass - block txs index out of bound ", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockResults(client, 1) + }, + &tmrpctypes.ResultBlock{Block: types.MakeBlock(1, []types.Tx{bz}, nil, nil)}, + 1, + nil, + true, + }, + { + "pass - Can't fetch base fee", + func() { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockResults(client, 1) + RegisterBaseFeeError(queryClient) + }, + &tmrpctypes.ResultBlock{Block: defaultBlock}, + 0, + txFromMsg, + true, + }, + { + "pass - Gets Tx by transaction index", + func() { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + client := suite.backend.clientCtx.Client.(*mocks.Client) + db := dbm.NewMemDB() + suite.backend.indexer = indexer.NewKVIndexer(db, tmlog.NewNopLogger(), suite.backend.clientCtx) + txBz := suite.signAndEncodeEthTx(msgEthTx) + block := &types.Block{ + Header: types.Header{Height: 1, ChainID: "test"}, + Data: types.Data{Txs: []types.Tx{txBz}}, + } + err := suite.backend.indexer.IndexBlock(block, defaultResponseDeliverTx) + suite.Require().NoError(err) + RegisterBlockResults(client, 1) + RegisterBaseFee(queryClient, sdk.NewInt(1)) + }, + &tmrpctypes.ResultBlock{Block: defaultBlock}, + 0, + txFromMsg, + true, + }, + { + "pass - returns the Ethereum format transaction by the Ethereum hash", + func() { + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockResults(client, 1) + RegisterBaseFee(queryClient, sdk.NewInt(1)) + }, + &tmrpctypes.ResultBlock{Block: defaultBlock}, + 0, + txFromMsg, + true, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() // reset + tc.registerMock() + + rpcTx, err := suite.backend.GetTransactionByBlockAndIndex(tc.block, tc.idx) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(rpcTx, tc.expRPCTx) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestGetTransactionByBlockNumberAndIndex() { + msgEthTx, bz := suite.buildEthereumTx() + defaultBlock := types.MakeBlock(1, []types.Tx{bz}, nil, nil) + txFromMsg, err := rpctypes.NewTransactionFromMsg( + msgEthTx, + common.BytesToHash(defaultBlock.Hash().Bytes()), + 1, + 0, + big.NewInt(1), + suite.backend.chainID, + nil, + ) + suite.Require().NoError(err) + + testCases := []struct { + name string + registerMock func() + blockNum rpctypes.BlockNumber + idx hexutil.Uint + expRPCTx *rpctypes.RPCTransaction + expPass bool + }{ + { + "fail - block not found return nil", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterBlockError(client, 1) + }, + 0, + 0, + nil, + true, + }, + { + "pass - returns the transaction identified by block number and index", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + RegisterBlock(client, 1, bz) + RegisterBlockResults(client, 1) + RegisterBaseFee(queryClient, sdk.NewInt(1)) + }, + 0, + 0, + txFromMsg, + true, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() // reset + tc.registerMock() + + rpcTx, err := suite.backend.GetTransactionByBlockNumberAndIndex(tc.blockNum, tc.idx) + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(rpcTx, tc.expRPCTx) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestGetTransactionByTxIndex() { + _, bz := suite.buildEthereumTx() + + testCases := []struct { + name string + registerMock func() + height int64 + index uint + expTxResult *ethermint.TxResult + expPass bool + }{ + { + "fail - Ethereum tx with query not found", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + suite.backend.indexer = nil + RegisterTxSearch(client, "tx.height=0 AND ethereum_tx.txIndex=0", bz) + }, + 0, + 0, + ðermint.TxResult{}, + false, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() // reset + tc.registerMock() + + txResults, _, err := suite.backend.GetTxByTxIndex(tc.height, tc.index) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(txResults, tc.expTxResult) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestQueryTendermintTxIndexer() { + testCases := []struct { + name string + registerMock func() + txGetter func(*rpctypes.ParsedTxs) *rpctypes.ParsedTx + query string + expTxResult *ethermint.TxResult + expPass bool + }{ + { + "fail - Ethereum tx with query not found", + func() { + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterTxSearchEmpty(client, "") + }, + func(txs *rpctypes.ParsedTxs) *rpctypes.ParsedTx { + return &rpctypes.ParsedTx{} + }, + "", + ðermint.TxResult{}, + false, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() // reset + tc.registerMock() + + txResults, _, err := suite.backend.queryTendermintTxIndexer(tc.query, tc.txGetter) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(txResults, tc.expTxResult) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *BackendTestSuite) TestGetTransactionReceipt() { + msgEthereumTx, _ := suite.buildEthereumTx() + txHash := msgEthereumTx.AsTransaction().Hash() + + txBz := suite.signAndEncodeEthTx(msgEthereumTx) + + testCases := []struct { + name string + registerMock func() + tx *evmtypes.MsgEthereumTx + block *types.Block + blockResult []*abci.ResponseDeliverTx + expTxReceipt map[string]interface{} + expPass bool + }{ + { + "fail - Receipts do not match ", + func() { + var header metadata.MD + queryClient := suite.backend.queryClient.QueryClient.(*mocks.EVMQueryClient) + client := suite.backend.clientCtx.Client.(*mocks.Client) + RegisterParams(queryClient, &header, 1) + RegisterParamsWithoutHeader(queryClient, 1) + RegisterBlock(client, 1, txBz) + RegisterBlockResults(client, 1) + }, + msgEthereumTx, + &types.Block{Header: types.Header{Height: 1}, Data: types.Data{Txs: []types.Tx{txBz}}}, + []*abci.ResponseDeliverTx{ + { + Code: 0, + Events: []abci.Event{ + {Type: evmtypes.EventTypeEthereumTx, Attributes: []abci.EventAttribute{ + {Key: "ethereumTxHash", Value: txHash.Hex()}, + {Key: "txIndex", Value: "0"}, + {Key: "amount", Value: "1000"}, + {Key: "txGasUsed", Value: "21000"}, + {Key: "txHash", Value: ""}, + {Key: "recipient", Value: "0x775b87ef5D82ca211811C1a02CE0fE0CA3a455d7"}, + }}, + }, + }, + }, + map[string]interface{}(nil), + false, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() // reset + tc.registerMock() + + db := dbm.NewMemDB() + suite.backend.indexer = indexer.NewKVIndexer(db, tmlog.NewNopLogger(), suite.backend.clientCtx) + err := suite.backend.indexer.IndexBlock(tc.block, tc.blockResult) + suite.Require().NoError(err) + + txReceipt, err := suite.backend.GetTransactionReceipt(common.HexToHash(tc.tx.Hash)) + if tc.expPass { + suite.Require().NoError(err) + suite.Require().Equal(txReceipt, tc.expTxReceipt) + } else { + suite.Require().NotEqual(txReceipt, tc.expTxReceipt) + } + }) + } +} diff --git a/rpc/backend/utils_test.go b/rpc/backend/utils_test.go new file mode 100644 index 0000000000..a3b3c2dd5f --- /dev/null +++ b/rpc/backend/utils_test.go @@ -0,0 +1,52 @@ +package backend + +import ( + "fmt" + + "github.com/cometbft/cometbft/proto/tendermint/crypto" +) + +func mockProofs(num int, withData bool) *crypto.ProofOps { + var proofOps *crypto.ProofOps + if num > 0 { + proofOps = new(crypto.ProofOps) + for i := 0; i < num; i++ { + proof := crypto.ProofOp{} + if withData { + proof.Data = []byte("\n\031\n\003KEY\022\005VALUE\032\013\010\001\030\001 \001*\003\000\002\002") + } + proofOps.Ops = append(proofOps.Ops, proof) + } + } + return proofOps +} + +func (suite *BackendTestSuite) TestGetHexProofs() { + defaultRes := []string{""} + testCases := []struct { + name string + proof *crypto.ProofOps + exp []string + }{ + { + "no proof provided", + mockProofs(0, false), + defaultRes, + }, + { + "no proof data provided", + mockProofs(1, false), + defaultRes, + }, + { + "valid proof provided", + mockProofs(1, true), + []string{"0x0a190a034b4559120556414c55451a0b0801180120012a03000202"}, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.Require().Equal(tc.exp, GetHexProofs(tc.proof)) + }) + } +} From aec6ac204ee8002b58696afac191b30557b0678f Mon Sep 17 00:00:00 2001 From: Lucas Bertrand Date: Thu, 6 Jun 2024 15:28:38 +0200 Subject: [PATCH 7/9] test: allow running Bitcoin tests on live networks (tentative) (#2303) * add local check * improve mineblocks * only generate on local bitcoin * simplify some functions * try removing min amount * simplify error message * refactor utxos list * comments --- e2e/e2etests/helper_bitcoin.go | 12 +- .../test_bitcoin_withdraw_invalid_address.go | 11 +- .../test_bitcoin_withdraw_multiple.go | 4 +- e2e/e2etests/test_crosschain_swap.go | 35 +---- e2e/runner/bitcoin.go | 125 ++++++++---------- e2e/runner/setup_bitcoin.go | 4 +- 6 files changed, 70 insertions(+), 121 deletions(-) diff --git a/e2e/e2etests/helper_bitcoin.go b/e2e/e2etests/helper_bitcoin.go index 338cd7027f..09cb4ded96 100644 --- a/e2e/e2etests/helper_bitcoin.go +++ b/e2e/e2etests/helper_bitcoin.go @@ -64,11 +64,7 @@ func withdrawBTCZRC20(r *runner.E2ERunner, to btcutil.Address, amount *big.Int) } // mine blocks if testing on regnet - var stop chan struct{} - isRegnet := chains.IsBitcoinRegnet(r.GetBitcoinChainID()) - if isRegnet { - stop = r.MineBlocks() - } + stop := r.MineBlocksIfLocalBitcoin() // withdraw 'amount' of BTC from ZRC20 to BTC address tx, err = r.BTCZRC20.Withdraw(r.ZEVMAuth, []byte(to.EncodeAddress()), amount) @@ -81,7 +77,7 @@ func withdrawBTCZRC20(r *runner.E2ERunner, to btcutil.Address, amount *big.Int) } // mine 10 blocks to confirm the withdraw tx - _, err = r.BtcRPCClient.GenerateToAddress(10, to, nil) + _, err = r.GenerateToAddressIfLocalBitcoin(10, to) if err != nil { panic(err) } @@ -118,9 +114,7 @@ func withdrawBTCZRC20(r *runner.E2ERunner, to btcutil.Address, amount *big.Int) } // stop mining - if isRegnet { - stop <- struct{}{} - } + stop() return rawTx } diff --git a/e2e/e2etests/test_bitcoin_withdraw_invalid_address.go b/e2e/e2etests/test_bitcoin_withdraw_invalid_address.go index 8d4698f489..aaa0725a99 100644 --- a/e2e/e2etests/test_bitcoin_withdraw_invalid_address.go +++ b/e2e/e2etests/test_bitcoin_withdraw_invalid_address.go @@ -9,7 +9,6 @@ import ( "github.com/zeta-chain/zetacore/e2e/runner" "github.com/zeta-chain/zetacore/e2e/utils" - "github.com/zeta-chain/zetacore/pkg/chains" ) func TestBitcoinWithdrawToInvalidAddress(r *runner.E2ERunner, args []string) { @@ -47,11 +46,7 @@ func withdrawToInvalidAddress(r *runner.E2ERunner, amount *big.Int) { } // mine blocks if testing on regnet - var stop chan struct{} - isRegnet := chains.IsBitcoinRegnet(r.GetBitcoinChainID()) - if isRegnet { - stop = r.MineBlocks() - } + stop := r.MineBlocksIfLocalBitcoin() // withdraw amount provided as test arg BTC from ZRC20 to BTC legacy address // the address "1EYVvXLusCxtVuEwoYvWRyN5EZTXwPVvo3" is for mainnet, not regtest @@ -65,7 +60,5 @@ func withdrawToInvalidAddress(r *runner.E2ERunner, amount *big.Int) { } // stop mining - if isRegnet { - stop <- struct{}{} - } + stop() } diff --git a/e2e/e2etests/test_bitcoin_withdraw_multiple.go b/e2e/e2etests/test_bitcoin_withdraw_multiple.go index 683b9fb0f9..b9271b8b91 100644 --- a/e2e/e2etests/test_bitcoin_withdraw_multiple.go +++ b/e2e/e2etests/test_bitcoin_withdraw_multiple.go @@ -42,7 +42,7 @@ package e2etests // go func() { // for { // time.Sleep(3 * time.Second) -// _, err = r.BtcRPCClient.GenerateToAddress(1, r.BTCDeployerAddress, nil) +// _, err = r.GenerateToAddressIfLocalBitcoin(1, r.BTCDeployerAddress) // if err != nil { // panic(err) // } @@ -64,7 +64,7 @@ package e2etests // if receipt.Status != 1 { // panic(fmt.Errorf("withdraw receipt status is not 1")) // } -// _, err = r.BtcRPCClient.GenerateToAddress(10, r.BTCDeployerAddress, nil) +// _, err = r.GenerateToAddressIfLocalBitcoin(10, r.BTCDeployerAddress) // if err != nil { // panic(err) // } diff --git a/e2e/e2etests/test_crosschain_swap.go b/e2e/e2etests/test_crosschain_swap.go index cb74056710..358e9ba7d7 100644 --- a/e2e/e2etests/test_crosschain_swap.go +++ b/e2e/e2etests/test_crosschain_swap.go @@ -10,7 +10,6 @@ import ( "github.com/zeta-chain/zetacore/e2e/runner" "github.com/zeta-chain/zetacore/e2e/utils" - "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/x/crosschain/types" ) @@ -110,17 +109,13 @@ func TestCrosschainSwap(r *runner.E2ERunner, _ []string) { } // mine 10 blocks to confirm the outbound tx - _, err = r.BtcRPCClient.GenerateToAddress(10, r.BTCDeployerAddress, nil) + _, err = r.GenerateToAddressIfLocalBitcoin(10, r.BTCDeployerAddress) if err != nil { panic(err) } // mine blocks if testing on regnet - var stop chan struct{} - isRegnet := chains.IsBitcoinRegnet(r.GetBitcoinChainID()) - if isRegnet { - stop = r.MineBlocks() - } + stop := r.MineBlocksIfLocalBitcoin() // cctx1 index acts like the inboundHash for the second cctx (the one that withdraws BTC) cctx2 := utils.WaitCctxMinedByInboundHash(r.Ctx, cctx1.Index, r.CctxClient, r.Logger, r.CctxTimeout) @@ -137,8 +132,8 @@ func TestCrosschainSwap(r *runner.E2ERunner, _ []string) { r.Logger.Info("cctx2 outbound tx hash %s", cctx2.GetCurrentOutboundParam().Hash) r.Logger.Info("******* Second test: BTC -> ERC20ZRC20") - // list deployer utxos that have at least 1 BTC - utxos, err := r.ListDeployerUTXOs(1.0) + // list deployer utxos + utxos, err := r.ListDeployerUTXOs() if err != nil { panic(err) } @@ -151,14 +146,7 @@ func TestCrosschainSwap(r *runner.E2ERunner, _ []string) { memo = append(r.ZEVMSwapAppAddr.Bytes(), memo...) r.Logger.Info("memo length %d", len(memo)) - txID, err := r.SendToTSSFromDeployerWithMemo( - r.BTCTSSAddress, - 0.01, - utxos[0:1], - r.BtcRPCClient, - memo, - r.BTCDeployerAddress, - ) + txID, err := r.SendToTSSFromDeployerWithMemo(0.01, utxos[0:1], memo) if err != nil { panic(err) } @@ -200,14 +188,7 @@ func TestCrosschainSwap(r *runner.E2ERunner, _ []string) { r.Logger.Info("memo length %d", len(memo)) amount := 0.1 - txid, err := r.SendToTSSFromDeployerWithMemo( - r.BTCTSSAddress, - amount, - utxos[1:2], - r.BtcRPCClient, - memo, - r.BTCDeployerAddress, - ) + txid, err := r.SendToTSSFromDeployerWithMemo(amount, utxos[1:2], memo) if err != nil { panic(err) } @@ -239,7 +220,5 @@ func TestCrosschainSwap(r *runner.E2ERunner, _ []string) { } // stop mining - if isRegnet { - stop <- struct{}{} - } + stop() } diff --git a/e2e/runner/bitcoin.go b/e2e/runner/bitcoin.go index 9143926d06..493a7a3fd3 100644 --- a/e2e/runner/bitcoin.go +++ b/e2e/runner/bitcoin.go @@ -9,7 +9,6 @@ import ( "github.com/btcsuite/btcd/btcjson" "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" @@ -29,8 +28,8 @@ import ( var blockHeaderBTCTimeout = 5 * time.Minute -// ListDeployerUTXOs list the deployer's UTXOs that have at least `minAmount` -func (runner *E2ERunner) ListDeployerUTXOs(minAmount float64) ([]btcjson.ListUnspentResult, error) { +// ListDeployerUTXOs list the deployer's UTXOs +func (runner *E2ERunner) ListDeployerUTXOs() ([]btcjson.ListUnspentResult, error) { // query UTXOs from node utxos, err := runner.BtcRPCClient.ListUnspentMinMaxAddresses( 1, @@ -41,23 +40,15 @@ func (runner *E2ERunner) ListDeployerUTXOs(minAmount float64) ([]btcjson.ListUns return nil, err } - // filter UTXOs by `minAmount` - filtered := []btcjson.ListUnspentResult{} - for _, utxo := range utxos { - if utxo.Amount >= minAmount { - filtered = append(filtered, utxo) - } - } - - return filtered, nil + return utxos, nil } // DepositBTCWithAmount deposits BTC on ZetaChain with a specific amount func (runner *E2ERunner) DepositBTCWithAmount(amount float64) (txHash *chainhash.Hash) { runner.Logger.Print("⏳ depositing BTC into ZEVM") - // list deployer utxos that have at least 1 BTC - utxos, err := runner.ListDeployerUTXOs(1.0) + // list deployer utxos + utxos, err := runner.ListDeployerUTXOs() if err != nil { panic(err) } @@ -72,7 +63,11 @@ func (runner *E2ERunner) DepositBTCWithAmount(amount float64) (txHash *chainhash } if spendableAmount < amount { - panic(fmt.Errorf("not enough spendable BTC to run the test; have %f", spendableAmount)) + panic(fmt.Errorf( + "not enough spendable BTC to run the test; have %f, require %f", + spendableAmount, + amount, + )) } runner.Logger.Info("ListUnspent:") @@ -81,13 +76,7 @@ func (runner *E2ERunner) DepositBTCWithAmount(amount float64) (txHash *chainhash runner.Logger.Info("Now sending two txs to TSS address...") amount = amount + zetabitcoin.DefaultDepositorFee - txHash, err = runner.SendToTSSFromDeployerToDeposit( - runner.BTCTSSAddress, - amount, - utxos, - runner.BtcRPCClient, - runner.BTCDeployerAddress, - ) + txHash, err = runner.SendToTSSFromDeployerToDeposit(amount, utxos) if err != nil { panic(err) } @@ -104,8 +93,8 @@ func (runner *E2ERunner) DepositBTC(testHeader bool) { runner.Logger.Print("✅ BTC deposited in %s", time.Since(startTime)) }() - // list deployer utxos that have at least 1 BTC - utxos, err := runner.ListDeployerUTXOs(1.0) + // list deployer utxos + utxos, err := runner.ListDeployerUTXOs() if err != nil { panic(err) } @@ -133,38 +122,19 @@ func (runner *E2ERunner) DepositBTC(testHeader bool) { // send two transactions to the TSS address amount1 := 1.1 + zetabitcoin.DefaultDepositorFee - txHash1, err := runner.SendToTSSFromDeployerToDeposit( - runner.BTCTSSAddress, - amount1, - utxos[:2], - runner.BtcRPCClient, - runner.BTCDeployerAddress, - ) + txHash1, err := runner.SendToTSSFromDeployerToDeposit(amount1, utxos[:2]) if err != nil { panic(err) } amount2 := 0.05 + zetabitcoin.DefaultDepositorFee - txHash2, err := runner.SendToTSSFromDeployerToDeposit( - runner.BTCTSSAddress, - amount2, - utxos[2:4], - runner.BtcRPCClient, - runner.BTCDeployerAddress, - ) + txHash2, err := runner.SendToTSSFromDeployerToDeposit(amount2, utxos[2:4]) if err != nil { panic(err) } // send a donation to the TSS address to compensate for the funds minted automatically during pool creation // and prevent accounting errors - _, err = runner.SendToTSSFromDeployerWithMemo( - runner.BTCTSSAddress, - 0.11, - utxos[4:5], - runner.BtcRPCClient, - []byte(constant.DonationMessage), - runner.BTCDeployerAddress, - ) + _, err = runner.SendToTSSFromDeployerWithMemo(0.11, utxos[4:5], []byte(constant.DonationMessage)) if err != nil { panic(err) } @@ -201,31 +171,22 @@ func (runner *E2ERunner) DepositBTC(testHeader bool) { } } -func (runner *E2ERunner) SendToTSSFromDeployerToDeposit( - to btcutil.Address, - amount float64, - inputUTXOs []btcjson.ListUnspentResult, - btc *rpcclient.Client, - btcDeployerAddress *btcutil.AddressWitnessPubKeyHash, -) (*chainhash.Hash, error) { - return runner.SendToTSSFromDeployerWithMemo( - to, - amount, - inputUTXOs, - btc, - runner.DeployerAddress.Bytes(), - btcDeployerAddress, - ) +func (runner *E2ERunner) SendToTSSFromDeployerToDeposit(amount float64, inputUTXOs []btcjson.ListUnspentResult) ( + *chainhash.Hash, + error, +) { + return runner.SendToTSSFromDeployerWithMemo(amount, inputUTXOs, runner.DeployerAddress.Bytes()) } func (runner *E2ERunner) SendToTSSFromDeployerWithMemo( - to btcutil.Address, amount float64, inputUTXOs []btcjson.ListUnspentResult, - btcRPC *rpcclient.Client, memo []byte, - btcDeployerAddress *btcutil.AddressWitnessPubKeyHash, ) (*chainhash.Hash, error) { + btcRPC := runner.BtcRPCClient + to := runner.BTCTSSAddress + btcDeployerAddress := runner.BTCDeployerAddress + // prepare inputs inputs := make([]btcjson.TransactionInput, len(inputUTXOs)) inputSats := btcutil.Amount(0) @@ -312,7 +273,7 @@ func (runner *E2ERunner) SendToTSSFromDeployerWithMemo( panic(err) } runner.Logger.Info("txid: %+v", txid) - _, err = btcRPC.GenerateToAddress(6, btcDeployerAddress, nil) + _, err = runner.GenerateToAddressIfLocalBitcoin(6, btcDeployerAddress) if err != nil { panic(err) } @@ -359,17 +320,36 @@ func (runner *E2ERunner) GetBitcoinChainID() int64 { return chainID } -// MineBlocks mines blocks on the BTC chain at a rate of 1 blocks every 5 seconds +// IsLocalBitcoin returns true if the runner is running on a local bitcoin network +func (runner *E2ERunner) IsLocalBitcoin() bool { + return runner.BitcoinParams.Name == chains.BitcoinRegnetParams.Name +} + +// GenerateToAddressIfLocalBitcoin generates blocks to an address if the runner is interacting +// with a local bitcoin network +func (runner *E2ERunner) GenerateToAddressIfLocalBitcoin( + numBlocks int64, + address btcutil.Address, +) ([]*chainhash.Hash, error) { + // if not local bitcoin network, do nothing + if runner.IsLocalBitcoin() { + return runner.BtcRPCClient.GenerateToAddress(numBlocks, address, nil) + } + return nil, nil +} + +// MineBlocksIfLocalBitcoin mines blocks on the local BTC chain at a rate of 1 blocks every 5 seconds // and returns a channel that can be used to stop the mining -func (runner *E2ERunner) MineBlocks() chan struct{} { - stop := make(chan struct{}) +// If the chain is not local, the function does nothing +func (runner *E2ERunner) MineBlocksIfLocalBitcoin() func() { + stopChan := make(chan struct{}) go func() { for { select { - case <-stop: + case <-stopChan: return default: - _, err := runner.BtcRPCClient.GenerateToAddress(1, runner.BTCDeployerAddress, nil) + _, err := runner.GenerateToAddressIfLocalBitcoin(1, runner.BTCDeployerAddress) if err != nil { panic(err) } @@ -377,7 +357,10 @@ func (runner *E2ERunner) MineBlocks() chan struct{} { } } }() - return stop + + return func() { + close(stopChan) + } } // ProveBTCTransaction proves that a BTC transaction is in a block header and that the block header is in ZetaChain diff --git a/e2e/runner/setup_bitcoin.go b/e2e/runner/setup_bitcoin.go index 5872fc4101..15af182915 100644 --- a/e2e/runner/setup_bitcoin.go +++ b/e2e/runner/setup_bitcoin.go @@ -34,12 +34,12 @@ func (runner *E2ERunner) SetupBitcoinAccount(initNetwork bool) { } // mine some blocks to get some BTC into the deployer address - _, err = runner.BtcRPCClient.GenerateToAddress(101, runner.BTCDeployerAddress, nil) + _, err = runner.GenerateToAddressIfLocalBitcoin(101, runner.BTCDeployerAddress) if err != nil { panic(err) } - _, err = runner.BtcRPCClient.GenerateToAddress(4, runner.BTCDeployerAddress, nil) + _, err = runner.GenerateToAddressIfLocalBitcoin(4, runner.BTCDeployerAddress) if err != nil { panic(err) } From 5929b5138e62260d4a492a8e3102639b56f77633 Mon Sep 17 00:00:00 2001 From: Tanmay Date: Thu, 6 Jun 2024 10:28:57 -0400 Subject: [PATCH 8/9] feat: add CheckAuthorization function (#2313) --- changelog.md | 1 + testutil/sample/authority.go | 18 ++ x/authority/keeper/authorization_list.go | 49 ++++ x/authority/keeper/authorization_list_test.go | 250 ++++++++++++++++++ x/authority/keeper/policies.go | 14 - ...uthorizations.go => authorization_list.go} | 0 ...ons_test.go => authorization_list_test.go} | 0 x/authority/types/errors.go | 5 + x/authority/types/policies.go | 12 + x/authority/types/policies_test.go | 131 +++++++++ 10 files changed, 466 insertions(+), 14 deletions(-) rename x/authority/types/{authorizations.go => authorization_list.go} (100%) rename x/authority/types/{authorizations_test.go => authorization_list_test.go} (100%) diff --git a/changelog.md b/changelog.md index 47ab793048..0e08931d37 100644 --- a/changelog.md +++ b/changelog.md @@ -23,6 +23,7 @@ * [2291](https://github.com/zeta-chain/node/pull/2291) - initialize cctx gateway interface * [2289](https://github.com/zeta-chain/node/pull/2289) - add an authorization list to keep track of all authorizations on the chain * [2305](https://github.com/zeta-chain/node/pull/2305) - add new messages `MsgAddAuthorization` and `MsgRemoveAuthorization` that can be used to update the authorization list +* [2313](https://github.com/zeta-chain/node/pull/2313) - add `CheckAuthorization` function to replace the `IsAuthorized` function. The new function uses the authorization list to verify the signer's authorization. ### Refactor diff --git a/testutil/sample/authority.go b/testutil/sample/authority.go index c7e3b7e6b8..be9ca2c555 100644 --- a/testutil/sample/authority.go +++ b/testutil/sample/authority.go @@ -3,6 +3,8 @@ package sample import ( "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/zeta-chain/zetacore/pkg/chains" authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" ) @@ -65,3 +67,19 @@ func Authorization() authoritytypes.Authorization { AuthorizedPolicy: authoritytypes.PolicyType_groupOperational, } } + +// MultipleSignerMessage is a sample message which has two signers instead of one. This is used to test cases when we have checks for number of signers such as authorized transactions. +type MultipleSignerMessage struct{} + +var _ sdk.Msg = &MultipleSignerMessage{} + +func (m *MultipleSignerMessage) Reset() {} +func (m *MultipleSignerMessage) String() string { return "MultipleSignerMessage" } +func (m *MultipleSignerMessage) ProtoMessage() {} +func (m *MultipleSignerMessage) ValidateBasic() error { return nil } +func (m *MultipleSignerMessage) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{ + sdk.MustAccAddressFromBech32(AccAddress()), + sdk.MustAccAddressFromBech32(AccAddress()), + } +} diff --git a/x/authority/keeper/authorization_list.go b/x/authority/keeper/authorization_list.go index 0e7fba9163..24255bd089 100644 --- a/x/authority/keeper/authorization_list.go +++ b/x/authority/keeper/authorization_list.go @@ -1,6 +1,9 @@ package keeper import ( + "fmt" + + "cosmossdk.io/errors" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" @@ -24,3 +27,49 @@ func (k Keeper) GetAuthorizationList(ctx sdk.Context) (val types.AuthorizationLi k.cdc.MustUnmarshal(b, &val) return val, true } + +// IsAuthorized checks if the address is authorized for the given policy type +func (k Keeper) IsAuthorized(ctx sdk.Context, address string, policyType types.PolicyType) bool { + policies, found := k.GetPolicies(ctx) + if !found { + return false + } + for _, policy := range policies.Items { + if policy.Address == address && policy.PolicyType == policyType { + return true + } + } + return false +} + +// CheckAuthorization checks if the signer is authorized to sign the message +// It uses both the authorization list and the policies to check if the signer is authorized +func (k Keeper) CheckAuthorization(ctx sdk.Context, msg sdk.Msg) error { + // Policy transactions must have only one signer + if len(msg.GetSigners()) != 1 { + return errors.Wrapf(types.ErrSigners, "msg: %v", sdk.MsgTypeURL(msg)) + } + + signer := msg.GetSigners()[0].String() + msgURL := sdk.MsgTypeURL(msg) + + authorizationsList, found := k.GetAuthorizationList(ctx) + if !found { + return types.ErrAuthorizationListNotFound + } + + policyRequired, err := authorizationsList.GetAuthorizedPolicy(msgURL) + if err != nil { + return errors.Wrap(types.ErrAuthorizationNotFound, fmt.Sprintf("msg: %v", msgURL)) + } + if policyRequired == types.PolicyType_groupEmpty { + return errors.Wrap(types.ErrInvalidPolicyType, fmt.Sprintf("Empty policy for msg: %v", msgURL)) + } + + policies, found := k.GetPolicies(ctx) + if !found { + return errors.Wrap(types.ErrPoliciesNotFound, fmt.Sprintf("msg: %v", msgURL)) + } + + return policies.CheckSigner(signer, policyRequired) +} diff --git a/x/authority/keeper/authorization_list_test.go b/x/authority/keeper/authorization_list_test.go index 008be61f25..a4c0d0e7bd 100644 --- a/x/authority/keeper/authorization_list_test.go +++ b/x/authority/keeper/authorization_list_test.go @@ -1,13 +1,16 @@ package keeper_test import ( + "fmt" "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/authority/types" + lightclienttypes "github.com/zeta-chain/zetacore/x/lightclient/types" ) func TestKeeper_GetAuthorizationList(t *testing.T) { @@ -47,3 +50,250 @@ func TestKeeper_SetAuthorizationList(t *testing.T) { require.Equal(t, newAuthorizationList, list) }) } + +func TestKeeper_CheckAuthorization(t *testing.T) { + t.Run("successfully check authorization", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + signer := sample.AccAddress() + msg := lightclienttypes.MsgDisableHeaderVerification{ + Creator: signer, + } + authorizationList := types.AuthorizationList{Authorizations: []types.Authorization{ + { + MsgUrl: sdk.MsgTypeURL(&msg), + AuthorizedPolicy: types.PolicyType_groupOperational, + }, + }, + } + policies := types.Policies{ + Items: []*types.Policy{ + { + Address: signer, + PolicyType: types.PolicyType_groupOperational, + }, + }, + } + + k.SetPolicies(ctx, policies) + k.SetAuthorizationList(ctx, authorizationList) + + err := k.CheckAuthorization(ctx, &msg) + require.NoError(t, err) + }) + + t.Run("successfully check authorization against large authorization list", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + signer := sample.AccAddress() + msg := lightclienttypes.MsgDisableHeaderVerification{ + Creator: signer, + } + authorizationList := types.DefaultAuthorizationsList() + // Add 300 more authorizations to the list + for i := 0; i < 100; i++ { + authorizationList.Authorizations = append( + authorizationList.Authorizations, + sample.AuthorizationList(fmt.Sprintf("sample%d", i)).Authorizations...) + } + policies := types.Policies{ + Items: []*types.Policy{ + { + Address: signer, + PolicyType: types.PolicyType_groupEmergency, + }, + }, + } + + k.SetPolicies(ctx, policies) + k.SetAuthorizationList(ctx, authorizationList) + + err := k.CheckAuthorization(ctx, &msg) + require.NoError(t, err) + + list, found := k.GetAuthorizationList(ctx) + require.True(t, found) + require.Equal(t, authorizationList, list) + }) + + t.Run("check authorization against fails against large authorization list", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + signer := sample.AccAddress() + msg := lightclienttypes.MsgDisableHeaderVerification{ + Creator: signer, + } + authorizationList := types.AuthorizationList{} + // Add 300 more authorizations to the list + for i := 0; i < 100; i++ { + authorizationList.Authorizations = append( + authorizationList.Authorizations, + sample.AuthorizationList(fmt.Sprintf("sample%d", i)).Authorizations...) + } + policies := types.Policies{ + Items: []*types.Policy{ + { + Address: signer, + PolicyType: types.PolicyType_groupEmergency, + }, + }, + } + + k.SetPolicies(ctx, policies) + k.SetAuthorizationList(ctx, authorizationList) + + err := k.CheckAuthorization(ctx, &msg) + require.ErrorIs(t, err, types.ErrAuthorizationNotFound) + + list, found := k.GetAuthorizationList(ctx) + require.True(t, found) + require.Equal(t, authorizationList, list) + }) + + t.Run("unable to check authorization with multiple signers", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + signer := sample.AccAddress() + msg := &sample.MultipleSignerMessage{} + authorizationList := types.AuthorizationList{Authorizations: []types.Authorization{ + { + MsgUrl: sdk.MsgTypeURL(msg), + AuthorizedPolicy: types.PolicyType_groupOperational, + }, + }, + } + policies := types.Policies{ + Items: []*types.Policy{ + { + Address: signer, + PolicyType: types.PolicyType_groupOperational, + }, + }, + } + k.SetPolicies(ctx, policies) + k.SetAuthorizationList(ctx, authorizationList) + + err := k.CheckAuthorization(ctx, msg) + require.ErrorIs(t, err, types.ErrSigners) + }) + + t.Run("unable to check authorization with no authorization list", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + signer := sample.AccAddress() + msg := lightclienttypes.MsgDisableHeaderVerification{ + Creator: signer, + } + + policies := types.Policies{ + Items: []*types.Policy{ + { + Address: signer, + PolicyType: types.PolicyType_groupOperational, + }, + }, + } + k.SetPolicies(ctx, policies) + + err := k.CheckAuthorization(ctx, &msg) + require.ErrorIs(t, err, types.ErrAuthorizationListNotFound) + }) + + t.Run("unable to check authorization with no policies", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + signer := sample.AccAddress() + msg := lightclienttypes.MsgDisableHeaderVerification{ + Creator: signer, + } + authorizationList := types.AuthorizationList{Authorizations: []types.Authorization{ + { + MsgUrl: sdk.MsgTypeURL(&msg), + AuthorizedPolicy: types.PolicyType_groupOperational, + }, + }, + } + k.SetAuthorizationList(ctx, authorizationList) + + err := k.CheckAuthorization(ctx, &msg) + require.ErrorIs(t, err, types.ErrPoliciesNotFound) + }) + + t.Run("unable to check authorization when the required authorization doesnt exist", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + signer := sample.AccAddress() + msg := lightclienttypes.MsgDisableHeaderVerification{ + Creator: signer, + } + authorizationList := types.AuthorizationList{Authorizations: []types.Authorization{ + { + MsgUrl: "/zetachain.zetacore.observer.MsgDisableCCTX", + AuthorizedPolicy: types.PolicyType_groupOperational, + }, + }, + } + policies := types.Policies{ + Items: []*types.Policy{ + { + Address: signer, + PolicyType: types.PolicyType_groupOperational, + }, + }, + } + k.SetPolicies(ctx, policies) + k.SetAuthorizationList(ctx, authorizationList) + + err := k.CheckAuthorization(ctx, &msg) + require.ErrorIs(t, err, types.ErrAuthorizationNotFound) + }) + + t.Run("unable to check authorization when check signer fails", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + signer := sample.AccAddress() + msg := lightclienttypes.MsgDisableHeaderVerification{ + Creator: signer, + } + authorizationList := types.AuthorizationList{Authorizations: []types.Authorization{ + { + MsgUrl: sdk.MsgTypeURL(&msg), + AuthorizedPolicy: types.PolicyType_groupOperational, + }, + }, + } + policies := types.Policies{ + Items: []*types.Policy{ + { + Address: signer, + PolicyType: types.PolicyType_groupAdmin, + }, + }, + } + k.SetPolicies(ctx, policies) + k.SetAuthorizationList(ctx, authorizationList) + + err := k.CheckAuthorization(ctx, &msg) + require.ErrorIs(t, err, types.ErrSignerDoesntMatch) + }) + + t.Run("unable to check authorization when the required policy is empty", func(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + signer := sample.AccAddress() + msg := lightclienttypes.MsgDisableHeaderVerification{ + Creator: signer, + } + authorizationList := types.AuthorizationList{Authorizations: []types.Authorization{ + { + MsgUrl: sdk.MsgTypeURL(&msg), + AuthorizedPolicy: types.PolicyType_groupEmpty, + }, + }, + } + policies := types.Policies{ + Items: []*types.Policy{ + { + Address: signer, + PolicyType: types.PolicyType_groupOperational, + }, + }, + } + k.SetPolicies(ctx, policies) + k.SetAuthorizationList(ctx, authorizationList) + + err := k.CheckAuthorization(ctx, &msg) + require.ErrorIs(t, err, types.ErrInvalidPolicyType) + }) +} diff --git a/x/authority/keeper/policies.go b/x/authority/keeper/policies.go index 448f922749..cd04a23d34 100644 --- a/x/authority/keeper/policies.go +++ b/x/authority/keeper/policies.go @@ -24,17 +24,3 @@ func (k Keeper) GetPolicies(ctx sdk.Context) (val types.Policies, found bool) { k.cdc.MustUnmarshal(b, &val) return val, true } - -// IsAuthorized checks if the address is authorized for the given policy type -func (k Keeper) IsAuthorized(ctx sdk.Context, address string, policyType types.PolicyType) bool { - policies, found := k.GetPolicies(ctx) - if !found { - return false - } - for _, policy := range policies.Items { - if policy.Address == address && policy.PolicyType == policyType { - return true - } - } - return false -} diff --git a/x/authority/types/authorizations.go b/x/authority/types/authorization_list.go similarity index 100% rename from x/authority/types/authorizations.go rename to x/authority/types/authorization_list.go diff --git a/x/authority/types/authorizations_test.go b/x/authority/types/authorization_list_test.go similarity index 100% rename from x/authority/types/authorizations_test.go rename to x/authority/types/authorization_list_test.go diff --git a/x/authority/types/errors.go b/x/authority/types/errors.go index 9d9509e9d4..776132d525 100644 --- a/x/authority/types/errors.go +++ b/x/authority/types/errors.go @@ -7,4 +7,9 @@ var ( ErrInvalidAuthorizationList = errorsmod.Register(ModuleName, 1103, "invalid authorization list") ErrAuthorizationNotFound = errorsmod.Register(ModuleName, 1104, "authorization not found") ErrAuthorizationListNotFound = errorsmod.Register(ModuleName, 1105, "authorization list not found") + ErrSigners = errorsmod.Register(ModuleName, 1106, "policy transactions must have only one signer") + ErrMsgNotAuthorized = errorsmod.Register(ModuleName, 1107, "msg type is not authorized") + ErrPoliciesNotFound = errorsmod.Register(ModuleName, 1108, "policies not found") + ErrSignerDoesntMatch = errorsmod.Register(ModuleName, 1109, "signer doesn't match required policy") + ErrInvalidPolicyType = errorsmod.Register(ModuleName, 1110, "invalid policy type") ) diff --git a/x/authority/types/policies.go b/x/authority/types/policies.go index af3642bfd7..c11bfbba6d 100644 --- a/x/authority/types/policies.go +++ b/x/authority/types/policies.go @@ -3,6 +3,7 @@ package types import ( "fmt" + "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -54,3 +55,14 @@ func (p Policies) Validate() error { return nil } + +// CheckSigner checks if the signer is authorized for the given policy type +func (p Policies) CheckSigner(signer string, policyRequired PolicyType) error { + for _, policy := range p.Items { + if policy.Address == signer && policy.PolicyType == policyRequired { + return nil + } + } + return errors.Wrap(ErrSignerDoesntMatch, fmt.Sprintf("signer: %s, policy required for message: %s ", + signer, policyRequired.String())) +} diff --git a/x/authority/types/policies_test.go b/x/authority/types/policies_test.go index 7f8d2ff52c..57749b35f6 100644 --- a/x/authority/types/policies_test.go +++ b/x/authority/types/policies_test.go @@ -130,3 +130,134 @@ func TestPolicies_Validate(t *testing.T) { }) } } + +func TestPolicies_CheckSigner(t *testing.T) { + signer := sample.AccAddress() + tt := []struct { + name string + policies types.Policies + signer string + policyRequired types.PolicyType + expectedErr error + }{ + { + name: "successfully check signer for policyType groupEmergency", + policies: types.Policies{ + Items: []*types.Policy{ + { + Address: signer, + PolicyType: types.PolicyType_groupEmergency, + }, + { + Address: signer, + PolicyType: types.PolicyType_groupAdmin, + }, + { + Address: signer, + PolicyType: types.PolicyType_groupOperational, + }, + }, + }, + signer: signer, + policyRequired: types.PolicyType_groupEmergency, + expectedErr: nil, + }, + { + name: "successfully check signer for policyType groupOperational", + policies: types.Policies{ + Items: []*types.Policy{ + { + Address: signer, + PolicyType: types.PolicyType_groupEmergency, + }, + { + Address: signer, + PolicyType: types.PolicyType_groupAdmin, + }, + { + Address: signer, + PolicyType: types.PolicyType_groupOperational, + }, + }, + }, + signer: signer, + policyRequired: types.PolicyType_groupOperational, + expectedErr: nil, + }, + { + name: "successfully check signer for policyType groupAdmin", + policies: types.Policies{ + Items: []*types.Policy{ + { + Address: signer, + PolicyType: types.PolicyType_groupEmergency, + }, + { + Address: signer, + PolicyType: types.PolicyType_groupAdmin, + }, + { + Address: signer, + PolicyType: types.PolicyType_groupOperational, + }, + }, + }, + signer: signer, + policyRequired: types.PolicyType_groupAdmin, + expectedErr: nil, + }, + { + name: "signer not found", + policies: types.Policies{ + Items: []*types.Policy{ + { + Address: signer, + PolicyType: types.PolicyType_groupEmergency, + }, + { + Address: signer, + PolicyType: types.PolicyType_groupAdmin, + }, + { + Address: signer, + PolicyType: types.PolicyType_groupOperational, + }, + }, + }, + signer: sample.AccAddress(), + policyRequired: types.PolicyType_groupEmergency, + expectedErr: types.ErrSignerDoesntMatch, + }, + { + name: "policy required not found", + policies: types.Policies{ + Items: []*types.Policy{ + { + Address: signer, + PolicyType: types.PolicyType_groupAdmin, + }, + { + Address: sample.AccAddress(), + PolicyType: types.PolicyType_groupOperational, + }, + }, + }, + signer: signer, + policyRequired: types.PolicyType_groupEmergency, + expectedErr: types.ErrSignerDoesntMatch, + }, + { + name: "empty policies", + policies: types.Policies{}, + signer: signer, + policyRequired: types.PolicyType_groupEmergency, + expectedErr: types.ErrSignerDoesntMatch, + }, + } + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + err := tc.policies.CheckSigner(tc.signer, tc.policyRequired) + require.ErrorIs(t, err, tc.expectedErr) + }) + } +} From 3f02526d929068e663b6dac192215a1b63c5a2ae Mon Sep 17 00:00:00 2001 From: Tanmay Date: Thu, 6 Jun 2024 10:54:51 -0400 Subject: [PATCH 9/9] feat: add queries ShowAuthorization and ListAuthorizations (#2312) --- changelog.md | 1 + .../zetacored/zetacored_query_authority.md | 2 + ...red_query_authority_list-authorizations.md | 34 + ...ored_query_authority_show-authorization.md | 34 + docs/openapi/openapi.swagger.yaml | 70 ++ .../zetachain/zetacore/authority/query.proto | 31 + .../zetacore/authority/query_pb.d.ts | 104 +++ x/authority/client/cli/query.go | 2 + .../client/cli/query_authorization_list.go | 62 ++ .../keeper/grpc_query_authorization_list.go | 58 ++ .../grpc_query_authothorization_list_test.go | 161 ++++ x/authority/types/query.pb.go | 799 +++++++++++++++++- x/authority/types/query.pb.gw.go | 166 ++++ 13 files changed, 1486 insertions(+), 38 deletions(-) create mode 100644 docs/cli/zetacored/zetacored_query_authority_list-authorizations.md create mode 100644 docs/cli/zetacored/zetacored_query_authority_show-authorization.md create mode 100644 x/authority/client/cli/query_authorization_list.go create mode 100644 x/authority/keeper/grpc_query_authorization_list.go create mode 100644 x/authority/keeper/grpc_query_authothorization_list_test.go diff --git a/changelog.md b/changelog.md index 0e08931d37..598ce8dd6a 100644 --- a/changelog.md +++ b/changelog.md @@ -24,6 +24,7 @@ * [2289](https://github.com/zeta-chain/node/pull/2289) - add an authorization list to keep track of all authorizations on the chain * [2305](https://github.com/zeta-chain/node/pull/2305) - add new messages `MsgAddAuthorization` and `MsgRemoveAuthorization` that can be used to update the authorization list * [2313](https://github.com/zeta-chain/node/pull/2313) - add `CheckAuthorization` function to replace the `IsAuthorized` function. The new function uses the authorization list to verify the signer's authorization. +* [2312](https://github.com/zeta-chain/node/pull/2312) - add queries `ShowAuthorization` and `ListAuthorizations` ### Refactor diff --git a/docs/cli/zetacored/zetacored_query_authority.md b/docs/cli/zetacored/zetacored_query_authority.md index f1e6bd9b69..75265b3195 100644 --- a/docs/cli/zetacored/zetacored_query_authority.md +++ b/docs/cli/zetacored/zetacored_query_authority.md @@ -26,6 +26,8 @@ zetacored query authority [flags] ### SEE ALSO * [zetacored query](zetacored_query.md) - Querying subcommands +* [zetacored query authority list-authorizations](zetacored_query_authority_list-authorizations.md) - lists all authorizations +* [zetacored query authority show-authorization](zetacored_query_authority_show-authorization.md) - shows the authorization for a given message URL * [zetacored query authority show-chain-info](zetacored_query_authority_show-chain-info.md) - show the chain info * [zetacored query authority show-policies](zetacored_query_authority_show-policies.md) - show the policies diff --git a/docs/cli/zetacored/zetacored_query_authority_list-authorizations.md b/docs/cli/zetacored/zetacored_query_authority_list-authorizations.md new file mode 100644 index 0000000000..32443ff20c --- /dev/null +++ b/docs/cli/zetacored/zetacored_query_authority_list-authorizations.md @@ -0,0 +1,34 @@ +# query authority list-authorizations + +lists all authorizations + +``` +zetacored query authority list-authorizations [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 list-authorizations + --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) + --log_no_color Disable colored logs + --trace print out full stack trace on errors +``` + +### SEE ALSO + +* [zetacored query authority](zetacored_query_authority.md) - Querying commands for the authority module + diff --git a/docs/cli/zetacored/zetacored_query_authority_show-authorization.md b/docs/cli/zetacored/zetacored_query_authority_show-authorization.md new file mode 100644 index 0000000000..336129f1a1 --- /dev/null +++ b/docs/cli/zetacored/zetacored_query_authority_show-authorization.md @@ -0,0 +1,34 @@ +# query authority show-authorization + +shows the authorization for a given message URL + +``` +zetacored query authority show-authorization [msg-url] [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 show-authorization + --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) + --log_no_color Disable colored logs + --trace print out full stack trace on errors +``` + +### SEE ALSO + +* [zetacored query authority](zetacored_query_authority.md) - Querying commands for the authority module + diff --git a/docs/openapi/openapi.swagger.yaml b/docs/openapi/openapi.swagger.yaml index 8bb38c8a67..ac40f0b45a 100644 --- a/docs/openapi/openapi.swagger.yaml +++ b/docs/openapi/openapi.swagger.yaml @@ -28254,6 +28254,39 @@ paths: type: boolean tags: - Query + /zeta-chain/authority/authorization/{msg_url}: + get: + operationId: Query_Authorization + responses: + "200": + description: A successful response. + schema: + $ref: '#/definitions/authorityQueryAuthorizationResponse' + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/googlerpcStatus' + parameters: + - name: msg_url + in: path + required: true + type: string + tags: + - Query + /zeta-chain/authority/authorizations: + get: + operationId: Query_AuthorizationList + responses: + "200": + description: A successful response. + schema: + $ref: '#/definitions/authorityQueryAuthorizationListResponse' + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/googlerpcStatus' + tags: + - Query /zeta-chain/authority/chainInfo: get: summary: Queries ChainInfo @@ -56718,6 +56751,27 @@ definitions: format: int64 balance: type: string + authorityAuthorization: + type: object + properties: + msg_url: + type: string + title: The URL of the message that needs to be authorized + authorized_policy: + $ref: '#/definitions/authorityPolicyType' + title: The policy that is authorized to access the message + title: |- + Authorization defines the authorization required to access use a message + which needs special permissions + authorityAuthorizationList: + type: object + properties: + authorizations: + type: array + items: + type: object + $ref: '#/definitions/authorityAuthorization' + title: AuthorizationList holds the list of authorizations on zetachain authorityChainInfo: type: object properties: @@ -56778,6 +56832,22 @@ definitions: Used for empty policy, no action is allowed title: PolicyType defines the type of policy + authorityQueryAuthorizationListResponse: + type: object + properties: + authorization_list: + $ref: '#/definitions/authorityAuthorizationList' + title: |- + QueryAuthorizationListResponse is the response type for the + Query/AuthorizationList RPC + authorityQueryAuthorizationResponse: + type: object + properties: + authorization: + $ref: '#/definitions/authorityAuthorization' + description: |- + QueryAuthorizationResponse is the response type for the Query/Authorization + RPC method. authorityQueryGetChainInfoResponse: type: object properties: diff --git a/proto/zetachain/zetacore/authority/query.proto b/proto/zetachain/zetacore/authority/query.proto index c5cb89d47a..33431783a0 100644 --- a/proto/zetachain/zetacore/authority/query.proto +++ b/proto/zetachain/zetacore/authority/query.proto @@ -3,6 +3,7 @@ package zetachain.zetacore.authority; import "zetachain/zetacore/authority/policies.proto"; import "zetachain/zetacore/authority/chain_info.proto"; +import "zetachain/zetacore/authority/authorization.proto"; import "cosmos/base/query/v1beta1/pagination.proto"; import "gogoproto/gogo.proto"; import "google/api/annotations.proto"; @@ -20,6 +21,36 @@ service Query { rpc ChainInfo(QueryGetChainInfoRequest) returns (QueryGetChainInfoResponse) { option (google.api.http).get = "/zeta-chain/authority/chainInfo"; } + + rpc AuthorizationList(QueryAuthorizationListRequest) + returns (QueryAuthorizationListResponse) { + option (google.api.http).get = "/zeta-chain/authority/authorizations"; + } + + rpc Authorization(QueryAuthorizationRequest) + returns (QueryAuthorizationResponse) { + option (google.api.http).get = + "/zeta-chain/authority/authorization/{msg_url}"; + } +} + +// QueryAuthorizationListRequest is the request type for the +// Query/AuthorizationList RPC method. +message QueryAuthorizationListRequest {} +// QueryAuthorizationListResponse is the response type for the +// Query/AuthorizationList RPC +message QueryAuthorizationListResponse { + AuthorizationList authorization_list = 1 [ (gogoproto.nullable) = false ]; +} + +// QueryAuthorizationRequest is the request type for the Query/Authorization RPC +// method. +message QueryAuthorizationRequest { string msg_url = 1; } + +// QueryAuthorizationResponse is the response type for the Query/Authorization +// RPC method. +message QueryAuthorizationResponse { + Authorization authorization = 1 [ (gogoproto.nullable) = false ]; } // QueryGetPoliciesRequest is the request type for the Query/Policies RPC diff --git a/typescript/zetachain/zetacore/authority/query_pb.d.ts b/typescript/zetachain/zetacore/authority/query_pb.d.ts index 13192f4592..df4d0e5c9e 100644 --- a/typescript/zetachain/zetacore/authority/query_pb.d.ts +++ b/typescript/zetachain/zetacore/authority/query_pb.d.ts @@ -5,9 +5,113 @@ import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; import { Message, proto3 } from "@bufbuild/protobuf"; +import type { Authorization, AuthorizationList } from "./authorization_pb.js"; import type { Policies } from "./policies_pb.js"; import type { ChainInfo } from "./chain_info_pb.js"; +/** + * QueryAuthorizationListRequest is the request type for the + * Query/AuthorizationList RPC method. + * + * @generated from message zetachain.zetacore.authority.QueryAuthorizationListRequest + */ +export declare class QueryAuthorizationListRequest extends Message { + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.authority.QueryAuthorizationListRequest"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): QueryAuthorizationListRequest; + + static fromJson(jsonValue: JsonValue, options?: Partial): QueryAuthorizationListRequest; + + static fromJsonString(jsonString: string, options?: Partial): QueryAuthorizationListRequest; + + static equals(a: QueryAuthorizationListRequest | PlainMessage | undefined, b: QueryAuthorizationListRequest | PlainMessage | undefined): boolean; +} + +/** + * QueryAuthorizationListResponse is the response type for the + * Query/AuthorizationList RPC + * + * @generated from message zetachain.zetacore.authority.QueryAuthorizationListResponse + */ +export declare class QueryAuthorizationListResponse extends Message { + /** + * @generated from field: zetachain.zetacore.authority.AuthorizationList authorization_list = 1; + */ + authorizationList?: AuthorizationList; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.authority.QueryAuthorizationListResponse"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): QueryAuthorizationListResponse; + + static fromJson(jsonValue: JsonValue, options?: Partial): QueryAuthorizationListResponse; + + static fromJsonString(jsonString: string, options?: Partial): QueryAuthorizationListResponse; + + static equals(a: QueryAuthorizationListResponse | PlainMessage | undefined, b: QueryAuthorizationListResponse | PlainMessage | undefined): boolean; +} + +/** + * QueryAuthorizationRequest is the request type for the Query/Authorization RPC + * method. + * + * @generated from message zetachain.zetacore.authority.QueryAuthorizationRequest + */ +export declare class QueryAuthorizationRequest extends Message { + /** + * @generated from field: string msg_url = 1; + */ + msgUrl: string; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.authority.QueryAuthorizationRequest"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): QueryAuthorizationRequest; + + static fromJson(jsonValue: JsonValue, options?: Partial): QueryAuthorizationRequest; + + static fromJsonString(jsonString: string, options?: Partial): QueryAuthorizationRequest; + + static equals(a: QueryAuthorizationRequest | PlainMessage | undefined, b: QueryAuthorizationRequest | PlainMessage | undefined): boolean; +} + +/** + * QueryAuthorizationResponse is the response type for the Query/Authorization + * RPC method. + * + * @generated from message zetachain.zetacore.authority.QueryAuthorizationResponse + */ +export declare class QueryAuthorizationResponse extends Message { + /** + * @generated from field: zetachain.zetacore.authority.Authorization authorization = 1; + */ + authorization?: Authorization; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.authority.QueryAuthorizationResponse"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): QueryAuthorizationResponse; + + static fromJson(jsonValue: JsonValue, options?: Partial): QueryAuthorizationResponse; + + static fromJsonString(jsonString: string, options?: Partial): QueryAuthorizationResponse; + + static equals(a: QueryAuthorizationResponse | PlainMessage | undefined, b: QueryAuthorizationResponse | PlainMessage | undefined): boolean; +} + /** * QueryGetPoliciesRequest is the request type for the Query/Policies RPC * method. diff --git a/x/authority/client/cli/query.go b/x/authority/client/cli/query.go index ec6f965b7f..687158d917 100644 --- a/x/authority/client/cli/query.go +++ b/x/authority/client/cli/query.go @@ -23,6 +23,8 @@ func GetQueryCmd(_ string) *cobra.Command { cmd.AddCommand( CmdShowPolicies(), CmdShowChainInfo(), + CmdAuthorizationsList(), + CmdAuthorization(), ) return cmd diff --git a/x/authority/client/cli/query_authorization_list.go b/x/authority/client/cli/query_authorization_list.go new file mode 100644 index 0000000000..a822e9603b --- /dev/null +++ b/x/authority/client/cli/query_authorization_list.go @@ -0,0 +1,62 @@ +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/authority/types" +) + +// CmdAuthorizationsList shows the list of authorizations +func CmdAuthorizationsList() *cobra.Command { + cmd := &cobra.Command{ + Use: "list-authorizations", + Short: "lists all authorizations", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + + queryClient := types.NewQueryClient(clientCtx) + + res, err := queryClient.AuthorizationList(context.Background(), &types.QueryAuthorizationListRequest{}) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + return cmd +} + +// CmdAuthorization shows the authorization for a given message URL +func CmdAuthorization() *cobra.Command { + cmd := &cobra.Command{ + Use: "show-authorization [msg-url]", + Short: "shows the authorization for a given message URL", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + + queryClient := types.NewQueryClient(clientCtx) + + msgURL := args[0] + res, err := queryClient.Authorization(context.Background(), &types.QueryAuthorizationRequest{ + MsgUrl: msgURL, + }) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + return cmd +} diff --git a/x/authority/keeper/grpc_query_authorization_list.go b/x/authority/keeper/grpc_query_authorization_list.go new file mode 100644 index 0000000000..34c3f89708 --- /dev/null +++ b/x/authority/keeper/grpc_query_authorization_list.go @@ -0,0 +1,58 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/zeta-chain/zetacore/x/authority/types" +) + +// AuthorizationList returns the list of authorizations +func (k Keeper) AuthorizationList(c context.Context, + req *types.QueryAuthorizationListRequest, +) (*types.QueryAuthorizationListResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + ctx := sdk.UnwrapSDKContext(c) + + authorizationList, found := k.GetAuthorizationList(ctx) + if !found { + return nil, status.Error(codes.Internal, types.ErrAuthorizationListNotFound.Error()) + } + + return &types.QueryAuthorizationListResponse{AuthorizationList: authorizationList}, nil +} + +// Authorization returns the authorization for a given message URL +func (k Keeper) Authorization(c context.Context, + req *types.QueryAuthorizationRequest, +) (*types.QueryAuthorizationResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + ctx := sdk.UnwrapSDKContext(c) + + err := types.ValidateMsgURL(req.MsgUrl) + if err != nil { + return nil, err + } + + authorizationList, found := k.GetAuthorizationList(ctx) + if !found { + return nil, status.Error(codes.Internal, types.ErrAuthorizationListNotFound.Error()) + } + + authorization, err := authorizationList.GetAuthorizedPolicy(req.MsgUrl) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + + return &types.QueryAuthorizationResponse{Authorization: types.Authorization{ + MsgUrl: req.MsgUrl, + AuthorizedPolicy: authorization, + }}, nil +} diff --git a/x/authority/keeper/grpc_query_authothorization_list_test.go b/x/authority/keeper/grpc_query_authothorization_list_test.go new file mode 100644 index 0000000000..47148309c5 --- /dev/null +++ b/x/authority/keeper/grpc_query_authothorization_list_test.go @@ -0,0 +1,161 @@ +package keeper_test + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/store/prefix" + 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/authority/keeper" + "github.com/zeta-chain/zetacore/x/authority/types" +) + +func TestKeeper_Authorization(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + authorizationList := types.AuthorizationList{Authorizations: []types.Authorization{ + { + MsgUrl: "ABC", + AuthorizedPolicy: types.PolicyType_groupOperational, + }, + { + MsgUrl: "DEF", + AuthorizedPolicy: types.PolicyType_groupAdmin, + }, + }} + + tt := []struct { + name string + setAuthorizationList bool + req *types.QueryAuthorizationRequest + expectedResponse *types.QueryAuthorizationResponse + expecterErrorString string + }{ + { + name: "successfully get authorization", + setAuthorizationList: true, + req: &types.QueryAuthorizationRequest{ + MsgUrl: "ABC", + }, + expectedResponse: &types.QueryAuthorizationResponse{ + Authorization: types.Authorization{ + MsgUrl: "ABC", + AuthorizedPolicy: types.PolicyType_groupOperational, + }, + }, + expecterErrorString: "", + }, + { + name: "invalid request", + setAuthorizationList: true, + req: nil, + expectedResponse: nil, + expecterErrorString: "invalid request", + }, + { + name: "invalid msg url", + setAuthorizationList: true, + req: &types.QueryAuthorizationRequest{ + MsgUrl: "", + }, + expectedResponse: nil, + expecterErrorString: "message URL cannot be empty", + }, + { + name: "authorization not found", + setAuthorizationList: true, + req: &types.QueryAuthorizationRequest{ + MsgUrl: "GHI", + }, + expectedResponse: nil, + expecterErrorString: "authorization not found", + }, + { + name: "authorization list not found", + setAuthorizationList: false, + req: &types.QueryAuthorizationRequest{ + MsgUrl: "ABC", + }, + expectedResponse: nil, + expecterErrorString: "authorization list not found", + }, + } + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + removeAuthorizationList(ctx, *k) + if tc.setAuthorizationList { + k.SetAuthorizationList(ctx, authorizationList) + } + response, err := k.Authorization(ctx, tc.req) + require.Equal(t, tc.expectedResponse, response) + if tc.expecterErrorString != "" { + require.ErrorContains(t, err, tc.expecterErrorString) + } + }) + } +} + +func TestKeeper_AuthorizationList(t *testing.T) { + k, ctx := keepertest.AuthorityKeeper(t) + authorizationList := types.AuthorizationList{Authorizations: []types.Authorization{ + { + MsgUrl: "ABC", + AuthorizedPolicy: types.PolicyType_groupOperational, + }, + { + MsgUrl: "DEF", + AuthorizedPolicy: types.PolicyType_groupAdmin, + }, + }} + tt := []struct { + name string + setAuthorizationList bool + req *types.QueryAuthorizationListRequest + expectedResponse *types.QueryAuthorizationListResponse + expecterErrorString string + }{ + { + name: "successfully get authorization list", + setAuthorizationList: true, + req: &types.QueryAuthorizationListRequest{}, + expectedResponse: &types.QueryAuthorizationListResponse{ + AuthorizationList: authorizationList, + }, + expecterErrorString: "", + }, + { + name: "invalid request", + setAuthorizationList: true, + req: nil, + expectedResponse: nil, + expecterErrorString: "invalid request", + }, + { + name: "authorization list not found", + setAuthorizationList: false, + req: &types.QueryAuthorizationListRequest{}, + expectedResponse: nil, + expecterErrorString: "authorization list not found", + }, + } + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + removeAuthorizationList(ctx, *k) + if tc.setAuthorizationList { + k.SetAuthorizationList(ctx, authorizationList) + } + response, err := k.AuthorizationList(ctx, tc.req) + require.Equal(t, tc.expectedResponse, response) + if tc.expecterErrorString != "" { + require.ErrorContains(t, err, tc.expecterErrorString) + } + }) + + } +} + +func removeAuthorizationList(ctx sdk.Context, k keeper.Keeper) { + store := prefix.NewStore(ctx.KVStore(k.GetStoreKey()), types.KeyPrefix(types.AuthorizationListKey)) + store.Delete([]byte{0}) +} diff --git a/x/authority/types/query.pb.go b/x/authority/types/query.pb.go index e9a4c227c3..ca13419203 100644 --- a/x/authority/types/query.pb.go +++ b/x/authority/types/query.pb.go @@ -30,6 +30,182 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +// QueryAuthorizationListRequest is the request type for the +// Query/AuthorizationList RPC method. +type QueryAuthorizationListRequest struct { +} + +func (m *QueryAuthorizationListRequest) Reset() { *m = QueryAuthorizationListRequest{} } +func (m *QueryAuthorizationListRequest) String() string { return proto.CompactTextString(m) } +func (*QueryAuthorizationListRequest) ProtoMessage() {} +func (*QueryAuthorizationListRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_5fe6130bc825be8d, []int{0} +} +func (m *QueryAuthorizationListRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAuthorizationListRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAuthorizationListRequest.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 *QueryAuthorizationListRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAuthorizationListRequest.Merge(m, src) +} +func (m *QueryAuthorizationListRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryAuthorizationListRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAuthorizationListRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAuthorizationListRequest proto.InternalMessageInfo + +// QueryAuthorizationListResponse is the response type for the +// Query/AuthorizationList RPC +type QueryAuthorizationListResponse struct { + AuthorizationList AuthorizationList `protobuf:"bytes,1,opt,name=authorization_list,json=authorizationList,proto3" json:"authorization_list"` +} + +func (m *QueryAuthorizationListResponse) Reset() { *m = QueryAuthorizationListResponse{} } +func (m *QueryAuthorizationListResponse) String() string { return proto.CompactTextString(m) } +func (*QueryAuthorizationListResponse) ProtoMessage() {} +func (*QueryAuthorizationListResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_5fe6130bc825be8d, []int{1} +} +func (m *QueryAuthorizationListResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAuthorizationListResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAuthorizationListResponse.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 *QueryAuthorizationListResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAuthorizationListResponse.Merge(m, src) +} +func (m *QueryAuthorizationListResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryAuthorizationListResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAuthorizationListResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAuthorizationListResponse proto.InternalMessageInfo + +func (m *QueryAuthorizationListResponse) GetAuthorizationList() AuthorizationList { + if m != nil { + return m.AuthorizationList + } + return AuthorizationList{} +} + +// QueryAuthorizationRequest is the request type for the Query/Authorization RPC +// method. +type QueryAuthorizationRequest struct { + MsgUrl string `protobuf:"bytes,1,opt,name=msg_url,json=msgUrl,proto3" json:"msg_url,omitempty"` +} + +func (m *QueryAuthorizationRequest) Reset() { *m = QueryAuthorizationRequest{} } +func (m *QueryAuthorizationRequest) String() string { return proto.CompactTextString(m) } +func (*QueryAuthorizationRequest) ProtoMessage() {} +func (*QueryAuthorizationRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_5fe6130bc825be8d, []int{2} +} +func (m *QueryAuthorizationRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAuthorizationRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAuthorizationRequest.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 *QueryAuthorizationRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAuthorizationRequest.Merge(m, src) +} +func (m *QueryAuthorizationRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryAuthorizationRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAuthorizationRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAuthorizationRequest proto.InternalMessageInfo + +func (m *QueryAuthorizationRequest) GetMsgUrl() string { + if m != nil { + return m.MsgUrl + } + return "" +} + +// QueryAuthorizationResponse is the response type for the Query/Authorization +// RPC method. +type QueryAuthorizationResponse struct { + Authorization Authorization `protobuf:"bytes,1,opt,name=authorization,proto3" json:"authorization"` +} + +func (m *QueryAuthorizationResponse) Reset() { *m = QueryAuthorizationResponse{} } +func (m *QueryAuthorizationResponse) String() string { return proto.CompactTextString(m) } +func (*QueryAuthorizationResponse) ProtoMessage() {} +func (*QueryAuthorizationResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_5fe6130bc825be8d, []int{3} +} +func (m *QueryAuthorizationResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAuthorizationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAuthorizationResponse.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 *QueryAuthorizationResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAuthorizationResponse.Merge(m, src) +} +func (m *QueryAuthorizationResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryAuthorizationResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAuthorizationResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAuthorizationResponse proto.InternalMessageInfo + +func (m *QueryAuthorizationResponse) GetAuthorization() Authorization { + if m != nil { + return m.Authorization + } + return Authorization{} +} + // QueryGetPoliciesRequest is the request type for the Query/Policies RPC // method. type QueryGetPoliciesRequest struct { @@ -39,7 +215,7 @@ func (m *QueryGetPoliciesRequest) Reset() { *m = QueryGetPoliciesRequest func (m *QueryGetPoliciesRequest) String() string { return proto.CompactTextString(m) } func (*QueryGetPoliciesRequest) ProtoMessage() {} func (*QueryGetPoliciesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5fe6130bc825be8d, []int{0} + return fileDescriptor_5fe6130bc825be8d, []int{4} } func (m *QueryGetPoliciesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -78,7 +254,7 @@ func (m *QueryGetPoliciesResponse) Reset() { *m = QueryGetPoliciesRespon func (m *QueryGetPoliciesResponse) String() string { return proto.CompactTextString(m) } func (*QueryGetPoliciesResponse) ProtoMessage() {} func (*QueryGetPoliciesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5fe6130bc825be8d, []int{1} + return fileDescriptor_5fe6130bc825be8d, []int{5} } func (m *QueryGetPoliciesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -123,7 +299,7 @@ func (m *QueryGetChainInfoRequest) Reset() { *m = QueryGetChainInfoReque func (m *QueryGetChainInfoRequest) String() string { return proto.CompactTextString(m) } func (*QueryGetChainInfoRequest) ProtoMessage() {} func (*QueryGetChainInfoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5fe6130bc825be8d, []int{2} + return fileDescriptor_5fe6130bc825be8d, []int{6} } func (m *QueryGetChainInfoRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -162,7 +338,7 @@ func (m *QueryGetChainInfoResponse) Reset() { *m = QueryGetChainInfoResp func (m *QueryGetChainInfoResponse) String() string { return proto.CompactTextString(m) } func (*QueryGetChainInfoResponse) ProtoMessage() {} func (*QueryGetChainInfoResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5fe6130bc825be8d, []int{3} + return fileDescriptor_5fe6130bc825be8d, []int{7} } func (m *QueryGetChainInfoResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -199,6 +375,10 @@ func (m *QueryGetChainInfoResponse) GetChainInfo() ChainInfo { } func init() { + proto.RegisterType((*QueryAuthorizationListRequest)(nil), "zetachain.zetacore.authority.QueryAuthorizationListRequest") + proto.RegisterType((*QueryAuthorizationListResponse)(nil), "zetachain.zetacore.authority.QueryAuthorizationListResponse") + proto.RegisterType((*QueryAuthorizationRequest)(nil), "zetachain.zetacore.authority.QueryAuthorizationRequest") + proto.RegisterType((*QueryAuthorizationResponse)(nil), "zetachain.zetacore.authority.QueryAuthorizationResponse") proto.RegisterType((*QueryGetPoliciesRequest)(nil), "zetachain.zetacore.authority.QueryGetPoliciesRequest") proto.RegisterType((*QueryGetPoliciesResponse)(nil), "zetachain.zetacore.authority.QueryGetPoliciesResponse") proto.RegisterType((*QueryGetChainInfoRequest)(nil), "zetachain.zetacore.authority.QueryGetChainInfoRequest") @@ -210,33 +390,43 @@ func init() { } var fileDescriptor_5fe6130bc825be8d = []byte{ - // 403 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x93, 0x4f, 0xcf, 0xd2, 0x40, - 0x10, 0xc6, 0x5b, 0xa2, 0x06, 0xd6, 0xdb, 0xc6, 0x44, 0x68, 0x48, 0xc1, 0x1e, 0x80, 0x68, 0xe8, - 0x0a, 0x46, 0xbd, 0xe3, 0xc1, 0x3f, 0xf1, 0xa0, 0x1c, 0xbd, 0x98, 0x6d, 0x5d, 0xca, 0x26, 0xb0, - 0x53, 0xba, 0x5b, 0x23, 0x1e, 0xfd, 0x04, 0x26, 0x7e, 0x02, 0x0f, 0x7e, 0x17, 0x8e, 0x24, 0x5c, - 0x3c, 0x19, 0x03, 0x7e, 0x10, 0xc3, 0x76, 0x5b, 0xc8, 0x0b, 0x6f, 0xf3, 0x72, 0x9b, 0xec, 0x3c, - 0xf3, 0xcc, 0x6f, 0x26, 0xb3, 0xa8, 0xf7, 0x95, 0x29, 0x1a, 0x4e, 0x29, 0x17, 0x44, 0x47, 0x90, - 0x30, 0x42, 0x53, 0x35, 0x85, 0x84, 0xab, 0x25, 0x59, 0xa4, 0x2c, 0x59, 0xfa, 0x71, 0x02, 0x0a, - 0x70, 0xb3, 0x50, 0xfa, 0xb9, 0xd2, 0x2f, 0x94, 0xce, 0xa3, 0x52, 0x9f, 0x18, 0x66, 0x3c, 0xe4, - 0x4c, 0x66, 0x56, 0x4e, 0xbf, 0x54, 0xac, 0x13, 0x1f, 0xb9, 0x98, 0x80, 0x91, 0x3f, 0x0c, 0x41, - 0xce, 0x41, 0x92, 0x80, 0x4a, 0x96, 0x21, 0x91, 0xcf, 0x83, 0x80, 0x29, 0x3a, 0x20, 0x31, 0x8d, - 0xb8, 0xa0, 0x8a, 0x83, 0x30, 0xda, 0x7b, 0x11, 0x44, 0xa0, 0x43, 0xb2, 0x8f, 0xcc, 0x6b, 0x33, - 0x02, 0x88, 0x66, 0x8c, 0xd0, 0x98, 0x13, 0x2a, 0x04, 0x28, 0x5d, 0x62, 0x70, 0xbc, 0x06, 0xba, - 0xff, 0x7e, 0xef, 0xfa, 0x92, 0xa9, 0x77, 0x06, 0x74, 0xcc, 0x16, 0x29, 0x93, 0xca, 0xfb, 0x84, - 0xea, 0xa7, 0x29, 0x19, 0x83, 0x90, 0x0c, 0xbf, 0x42, 0xd5, 0x7c, 0xae, 0xba, 0xdd, 0xb6, 0x7b, - 0x77, 0x87, 0x1d, 0xbf, 0x6c, 0x47, 0x7e, 0xee, 0x30, 0xba, 0xb5, 0xfa, 0xd3, 0xb2, 0xc6, 0x45, - 0xb5, 0xe7, 0x1c, 0xba, 0xbc, 0xd8, 0x17, 0xbf, 0x16, 0x13, 0xc8, 0x09, 0x38, 0x6a, 0x9c, 0xc9, - 0x19, 0x84, 0xb7, 0x08, 0x1d, 0xb6, 0x65, 0x20, 0xba, 0xe5, 0x10, 0x85, 0x89, 0xa1, 0xa8, 0x85, - 0xf9, 0xc3, 0x70, 0x53, 0x41, 0xb7, 0x75, 0x2f, 0xfc, 0xd3, 0x46, 0xd5, 0x9c, 0x16, 0x3f, 0x2d, - 0x37, 0xbc, 0x66, 0x75, 0xce, 0xb3, 0x4b, 0xcb, 0xb2, 0x99, 0xbc, 0xce, 0xb7, 0xcd, 0xbf, 0x1f, - 0x95, 0x36, 0x76, 0xf5, 0x6d, 0xf4, 0xb3, 0x33, 0x39, 0x3d, 0x25, 0xfc, 0xcb, 0x46, 0xb5, 0x62, - 0x18, 0x7c, 0xc3, 0x6e, 0x57, 0xd7, 0xeb, 0x3c, 0xbf, 0xb8, 0xce, 0x60, 0x76, 0x35, 0xe6, 0x03, - 0xdc, 0x3a, 0x8f, 0x59, 0x6c, 0x75, 0xf4, 0x66, 0xb5, 0x75, 0xed, 0xf5, 0xd6, 0xb5, 0xff, 0x6e, - 0x5d, 0xfb, 0xfb, 0xce, 0xb5, 0xd6, 0x3b, 0xd7, 0xfa, 0xbd, 0x73, 0xad, 0x0f, 0x8f, 0x23, 0xae, - 0xa6, 0x69, 0xe0, 0x87, 0x30, 0x3f, 0x36, 0x29, 0xbe, 0xc4, 0x97, 0x23, 0x3f, 0xb5, 0x8c, 0x99, - 0x0c, 0xee, 0xe8, 0x83, 0x7d, 0xf2, 0x3f, 0x00, 0x00, 0xff, 0xff, 0xc1, 0xd1, 0x6c, 0x32, 0xb6, - 0x03, 0x00, 0x00, + // 573 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x94, 0x4f, 0x6b, 0x13, 0x41, + 0x18, 0xc6, 0x33, 0x62, 0x6b, 0x33, 0xd2, 0x43, 0x07, 0xa1, 0xed, 0x52, 0x37, 0x75, 0x91, 0xb4, + 0x58, 0xb3, 0xd3, 0x56, 0x6b, 0x05, 0xbd, 0x58, 0x0f, 0xfe, 0xa1, 0x07, 0x0d, 0x88, 0xe0, 0x25, + 0x4c, 0xd2, 0xe9, 0x66, 0x20, 0xd9, 0xd9, 0xee, 0xcc, 0x8a, 0xa9, 0x78, 0xf1, 0xe0, 0x59, 0xf0, + 0x13, 0x78, 0xf0, 0x43, 0x08, 0x7e, 0x80, 0x9e, 0xa4, 0xe0, 0xc5, 0x93, 0x48, 0xe2, 0x07, 0x91, + 0x4c, 0xde, 0xdd, 0x66, 0x9b, 0x64, 0xc9, 0xf6, 0x36, 0xcc, 0xbc, 0xcf, 0xf3, 0xfc, 0xe6, 0x65, + 0xde, 0xc1, 0xeb, 0xc7, 0x5c, 0xb3, 0x46, 0x93, 0x09, 0x9f, 0x9a, 0x95, 0x0c, 0x39, 0x65, 0x91, + 0x6e, 0xca, 0x50, 0xe8, 0x0e, 0x3d, 0x8a, 0x78, 0xd8, 0x71, 0x83, 0x50, 0x6a, 0x49, 0x56, 0x92, + 0x4a, 0x37, 0xae, 0x74, 0x93, 0x4a, 0x6b, 0x23, 0xd3, 0x27, 0x90, 0x2d, 0xd1, 0x10, 0x5c, 0x0d, + 0xac, 0xac, 0x4a, 0x66, 0xb1, 0x39, 0xa8, 0x09, 0xff, 0x50, 0x42, 0xf9, 0x66, 0x66, 0x39, 0xac, + 0x8e, 0x99, 0x16, 0xd2, 0x07, 0xc5, 0xad, 0x86, 0x54, 0x6d, 0xa9, 0x68, 0x9d, 0x29, 0x3e, 0xb8, + 0x04, 0x7d, 0xbb, 0x55, 0xe7, 0x9a, 0x6d, 0xd1, 0x80, 0x79, 0xc2, 0x1f, 0xae, 0xbd, 0xe6, 0x49, + 0x4f, 0x9a, 0x25, 0xed, 0xaf, 0x60, 0x77, 0xc5, 0x93, 0xd2, 0x6b, 0x71, 0xca, 0x02, 0x41, 0x99, + 0xef, 0x4b, 0x6d, 0x24, 0x70, 0x01, 0xa7, 0x84, 0xaf, 0xbf, 0xec, 0xbb, 0x3e, 0x1a, 0xce, 0xde, + 0x17, 0x4a, 0x57, 0xf9, 0x51, 0xc4, 0x95, 0x76, 0x3e, 0x21, 0x6c, 0x4f, 0xaa, 0x50, 0x81, 0xf4, + 0x15, 0x27, 0x07, 0x98, 0xa4, 0xd0, 0x6b, 0x2d, 0xa1, 0xf4, 0x12, 0x5a, 0x45, 0xeb, 0x57, 0xb7, + 0xa9, 0x9b, 0xd5, 0x6c, 0x77, 0xc4, 0x74, 0xef, 0xf2, 0xc9, 0x9f, 0x52, 0xa1, 0xba, 0xc0, 0xce, + 0x1f, 0x38, 0x77, 0xf1, 0xf2, 0x28, 0x07, 0x50, 0x92, 0x45, 0x7c, 0xa5, 0xad, 0xbc, 0x5a, 0x14, + 0xb6, 0x4c, 0x6e, 0xb1, 0x3a, 0xdb, 0x56, 0xde, 0xab, 0xb0, 0xe5, 0x44, 0xd8, 0x1a, 0xa7, 0x02, + 0xf2, 0xd7, 0x78, 0x3e, 0x15, 0x04, 0xd0, 0x1b, 0x39, 0xa0, 0x01, 0x38, 0xed, 0xe3, 0x2c, 0xe3, + 0x45, 0x13, 0xfb, 0x84, 0xeb, 0x17, 0xf0, 0x62, 0xe2, 0x86, 0x1e, 0xe0, 0xa5, 0xd1, 0x23, 0xe0, + 0x79, 0x8a, 0xe7, 0xe2, 0x07, 0x06, 0x28, 0xe5, 0x6c, 0x94, 0xd8, 0x01, 0x28, 0x12, 0xb5, 0x63, + 0x9d, 0xa5, 0x3c, 0xee, 0x8b, 0x9f, 0xf9, 0x87, 0x32, 0x26, 0x10, 0xd0, 0xc9, 0xf4, 0x19, 0x20, + 0xec, 0x63, 0x7c, 0xf6, 0x6c, 0x01, 0x62, 0x2d, 0x1b, 0x22, 0x31, 0x01, 0x8a, 0x62, 0x23, 0xde, + 0xd8, 0xfe, 0x39, 0x83, 0x67, 0x4c, 0x16, 0xf9, 0x8a, 0xf0, 0x5c, 0x4c, 0x4b, 0x76, 0xb2, 0x0d, + 0x27, 0xb4, 0xce, 0xba, 0x97, 0x57, 0x36, 0xb8, 0x93, 0x53, 0xfe, 0xf8, 0xeb, 0xdf, 0x97, 0x4b, + 0xab, 0xc4, 0x36, 0x53, 0x57, 0x19, 0x0c, 0xe0, 0xe8, 0x4c, 0x93, 0x6f, 0x08, 0x17, 0x93, 0xcb, + 0x90, 0x29, 0xd3, 0xce, 0xb7, 0xd7, 0xda, 0xcd, 0xad, 0x03, 0xcc, 0x35, 0x83, 0x79, 0x83, 0x94, + 0xc6, 0x63, 0x26, 0x5d, 0x25, 0x3f, 0x10, 0x5e, 0x18, 0x99, 0x1c, 0xf2, 0x60, 0x8a, 0xdc, 0x49, + 0x63, 0x6e, 0x3d, 0xbc, 0x98, 0x18, 0xc8, 0x6f, 0x1b, 0xf2, 0x32, 0xb9, 0x39, 0x9e, 0x3c, 0x35, + 0x1b, 0x8a, 0x7c, 0x47, 0x78, 0x3e, 0xe5, 0x45, 0x76, 0xf3, 0xa6, 0xc7, 0xd8, 0xf7, 0xf3, 0x0b, + 0x01, 0x79, 0xc7, 0x20, 0x53, 0x52, 0x99, 0x02, 0x99, 0xbe, 0x87, 0xcf, 0xe5, 0xc3, 0xde, 0xf3, + 0x93, 0xae, 0x8d, 0x4e, 0xbb, 0x36, 0xfa, 0xdb, 0xb5, 0xd1, 0xe7, 0x9e, 0x5d, 0x38, 0xed, 0xd9, + 0x85, 0xdf, 0x3d, 0xbb, 0xf0, 0x66, 0xd3, 0x13, 0xba, 0x19, 0xd5, 0xdd, 0x86, 0x6c, 0x0f, 0x5b, + 0x26, 0xff, 0xfc, 0xbb, 0x21, 0x77, 0xdd, 0x09, 0xb8, 0xaa, 0xcf, 0x9a, 0x2f, 0xf8, 0xce, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x4f, 0xcd, 0x31, 0xbd, 0xba, 0x06, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -255,6 +445,8 @@ type QueryClient interface { Policies(ctx context.Context, in *QueryGetPoliciesRequest, opts ...grpc.CallOption) (*QueryGetPoliciesResponse, error) // Queries ChainInfo ChainInfo(ctx context.Context, in *QueryGetChainInfoRequest, opts ...grpc.CallOption) (*QueryGetChainInfoResponse, error) + AuthorizationList(ctx context.Context, in *QueryAuthorizationListRequest, opts ...grpc.CallOption) (*QueryAuthorizationListResponse, error) + Authorization(ctx context.Context, in *QueryAuthorizationRequest, opts ...grpc.CallOption) (*QueryAuthorizationResponse, error) } type queryClient struct { @@ -283,12 +475,32 @@ func (c *queryClient) ChainInfo(ctx context.Context, in *QueryGetChainInfoReques return out, nil } +func (c *queryClient) AuthorizationList(ctx context.Context, in *QueryAuthorizationListRequest, opts ...grpc.CallOption) (*QueryAuthorizationListResponse, error) { + out := new(QueryAuthorizationListResponse) + err := c.cc.Invoke(ctx, "/zetachain.zetacore.authority.Query/AuthorizationList", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *queryClient) Authorization(ctx context.Context, in *QueryAuthorizationRequest, opts ...grpc.CallOption) (*QueryAuthorizationResponse, error) { + out := new(QueryAuthorizationResponse) + err := c.cc.Invoke(ctx, "/zetachain.zetacore.authority.Query/Authorization", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. type QueryServer interface { // Queries Policies Policies(context.Context, *QueryGetPoliciesRequest) (*QueryGetPoliciesResponse, error) // Queries ChainInfo ChainInfo(context.Context, *QueryGetChainInfoRequest) (*QueryGetChainInfoResponse, error) + AuthorizationList(context.Context, *QueryAuthorizationListRequest) (*QueryAuthorizationListResponse, error) + Authorization(context.Context, *QueryAuthorizationRequest) (*QueryAuthorizationResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -301,6 +513,12 @@ func (*UnimplementedQueryServer) Policies(ctx context.Context, req *QueryGetPoli func (*UnimplementedQueryServer) ChainInfo(ctx context.Context, req *QueryGetChainInfoRequest) (*QueryGetChainInfoResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ChainInfo not implemented") } +func (*UnimplementedQueryServer) AuthorizationList(ctx context.Context, req *QueryAuthorizationListRequest) (*QueryAuthorizationListResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AuthorizationList not implemented") +} +func (*UnimplementedQueryServer) Authorization(ctx context.Context, req *QueryAuthorizationRequest) (*QueryAuthorizationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Authorization not implemented") +} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -342,6 +560,42 @@ func _Query_ChainInfo_Handler(srv interface{}, ctx context.Context, dec func(int return interceptor(ctx, in, info, handler) } +func _Query_AuthorizationList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryAuthorizationListRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).AuthorizationList(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/zetachain.zetacore.authority.Query/AuthorizationList", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).AuthorizationList(ctx, req.(*QueryAuthorizationListRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Query_Authorization_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryAuthorizationRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Authorization(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/zetachain.zetacore.authority.Query/Authorization", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Authorization(ctx, req.(*QueryAuthorizationRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "zetachain.zetacore.authority.Query", HandlerType: (*QueryServer)(nil), @@ -354,11 +608,138 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "ChainInfo", Handler: _Query_ChainInfo_Handler, }, + { + MethodName: "AuthorizationList", + Handler: _Query_AuthorizationList_Handler, + }, + { + MethodName: "Authorization", + Handler: _Query_Authorization_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "zetachain/zetacore/authority/query.proto", } +func (m *QueryAuthorizationListRequest) 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 *QueryAuthorizationListRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAuthorizationListRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryAuthorizationListResponse) 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 *QueryAuthorizationListResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAuthorizationListResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.AuthorizationList.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 *QueryAuthorizationRequest) 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 *QueryAuthorizationRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAuthorizationRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.MsgUrl) > 0 { + i -= len(m.MsgUrl) + copy(dAtA[i:], m.MsgUrl) + i = encodeVarintQuery(dAtA, i, uint64(len(m.MsgUrl))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *QueryAuthorizationResponse) 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 *QueryAuthorizationResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAuthorizationResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Authorization.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 *QueryGetPoliciesRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -482,7 +863,7 @@ func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) return base } -func (m *QueryGetPoliciesRequest) Size() (n int) { +func (m *QueryAuthorizationListRequest) Size() (n int) { if m == nil { return 0 } @@ -491,43 +872,385 @@ func (m *QueryGetPoliciesRequest) Size() (n int) { return n } -func (m *QueryGetPoliciesResponse) Size() (n int) { +func (m *QueryAuthorizationListResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l - l = m.Policies.Size() + l = m.AuthorizationList.Size() n += 1 + l + sovQuery(uint64(l)) return n } -func (m *QueryGetChainInfoRequest) Size() (n int) { +func (m *QueryAuthorizationRequest) Size() (n int) { if m == nil { return 0 } var l int _ = l + l = len(m.MsgUrl) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } return n } -func (m *QueryGetChainInfoResponse) Size() (n int) { +func (m *QueryAuthorizationResponse) Size() (n int) { if m == nil { return 0 } var l int _ = l - l = m.ChainInfo.Size() + l = m.Authorization.Size() n += 1 + l + sovQuery(uint64(l)) return n } -func sovQuery(x uint64) (n int) { +func (m *QueryGetPoliciesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryGetPoliciesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Policies.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func (m *QueryGetChainInfoRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryGetChainInfoResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ChainInfo.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func sovQuery(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } func sozQuery(x uint64) (n int) { return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } +func (m *QueryAuthorizationListRequest) 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: QueryAuthorizationListRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAuthorizationListRequest: 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 *QueryAuthorizationListResponse) 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: QueryAuthorizationListResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAuthorizationListResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthorizationList", 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.AuthorizationList.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 *QueryAuthorizationRequest) 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: QueryAuthorizationRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAuthorizationRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MsgUrl", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.MsgUrl = string(dAtA[iNdEx:postIndex]) + 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 *QueryAuthorizationResponse) 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: QueryAuthorizationResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAuthorizationResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Authorization", 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.Authorization.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 *QueryGetPoliciesRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/authority/types/query.pb.gw.go b/x/authority/types/query.pb.gw.go index f465e4750f..22988faa3b 100644 --- a/x/authority/types/query.pb.gw.go +++ b/x/authority/types/query.pb.gw.go @@ -69,6 +69,78 @@ func local_request_Query_ChainInfo_0(ctx context.Context, marshaler runtime.Mars } +func request_Query_AuthorizationList_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAuthorizationListRequest + var metadata runtime.ServerMetadata + + msg, err := client.AuthorizationList(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_AuthorizationList_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAuthorizationListRequest + var metadata runtime.ServerMetadata + + msg, err := server.AuthorizationList(ctx, &protoReq) + return msg, metadata, err + +} + +func request_Query_Authorization_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAuthorizationRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["msg_url"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "msg_url") + } + + protoReq.MsgUrl, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "msg_url", err) + } + + msg, err := client.Authorization(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Authorization_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAuthorizationRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["msg_url"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "msg_url") + } + + protoReq.MsgUrl, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "msg_url", err) + } + + msg, err := server.Authorization(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -121,6 +193,52 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_AuthorizationList_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_AuthorizationList_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_AuthorizationList_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Authorization_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_Authorization_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_Authorization_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -202,6 +320,46 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_AuthorizationList_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_AuthorizationList_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_AuthorizationList_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Query_Authorization_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_Authorization_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_Authorization_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -209,10 +367,18 @@ var ( pattern_Query_Policies_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"zeta-chain", "authority", "policies"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_ChainInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"zeta-chain", "authority", "chainInfo"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_AuthorizationList_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"zeta-chain", "authority", "authorizations"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_Authorization_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"zeta-chain", "authority", "authorization", "msg_url"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( forward_Query_Policies_0 = runtime.ForwardResponseMessage forward_Query_ChainInfo_0 = runtime.ForwardResponseMessage + + forward_Query_AuthorizationList_0 = runtime.ForwardResponseMessage + + forward_Query_Authorization_0 = runtime.ForwardResponseMessage )