From 76624e7eb27f7bdf6cb0d070c895715c550db3f8 Mon Sep 17 00:00:00 2001 From: Hieu Vu <72878483+hieuvubk@users.noreply.github.com> Date: Thu, 3 Oct 2024 15:07:33 +0700 Subject: [PATCH 1/6] coding --- tests/systemtests/cometbft_client_test.go | 2 +- tests/systemtests/genesis_io.go | 15 +++++++++ tests/systemtests/gov_test.go | 2 +- tests/systemtests/tx | 1 + tests/systemtests/tx_test.go | 37 +++++++++++++++++++++++ 5 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 tests/systemtests/tx create mode 100644 tests/systemtests/tx_test.go diff --git a/tests/systemtests/cometbft_client_test.go b/tests/systemtests/cometbft_client_test.go index e031ae0c8f55..3c79adfe846c 100644 --- a/tests/systemtests/cometbft_client_test.go +++ b/tests/systemtests/cometbft_client_test.go @@ -1,4 +1,4 @@ -//go:build system_test +//go:build !system_test package systemtests diff --git a/tests/systemtests/genesis_io.go b/tests/systemtests/genesis_io.go index df2081ba0924..782f9efbbeee 100644 --- a/tests/systemtests/genesis_io.go +++ b/tests/systemtests/genesis_io.go @@ -1,7 +1,10 @@ package systemtests import ( + "bytes" "fmt" + "io" + "os" "testing" "time" @@ -44,3 +47,15 @@ func GetGenesisBalance(rawGenesis []byte, addr string) sdk.Coins { } return r } + +// StoreTempFile creates a temporary file in the test's temporary directory with the provided content. +// It returns a pointer to the created file. Errors during the process are handled with test assertions. +func StoreTempFile(t *testing.T, content []byte) *os.File { + t.Helper() + out, err := os.CreateTemp(t.TempDir(), "") + require.NoError(t, err) + _, err = io.Copy(out, bytes.NewReader(content)) + require.NoError(t, err) + require.NoError(t, out.Close()) + return out +} diff --git a/tests/systemtests/gov_test.go b/tests/systemtests/gov_test.go index 73bc7fd96efc..86aa61a7075e 100644 --- a/tests/systemtests/gov_test.go +++ b/tests/systemtests/gov_test.go @@ -1,4 +1,4 @@ -//go:build system_test +//go:build !system_test package systemtests diff --git a/tests/systemtests/tx b/tests/systemtests/tx new file mode 100644 index 000000000000..51ae02b210e6 --- /dev/null +++ b/tests/systemtests/tx @@ -0,0 +1 @@ +{"body":{"messages":[{"@type":"/cosmos.bank.v1beta1.MsgSend","from_address":"cosmos1psqxug2jg5w2xsyn4vmnvtm545x57crh0zr85d","to_address":"cosmos1azqhsyjw3qvv99eggyuzp4vg7fnh5jr433nnz0","amount":[{"denom":"stake","amount":"1000"}]}],"memo":"","timeout_height":"0","unordered":false,"timeout_timestamp":"1970-01-01T00:00:00Z","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[{"denom":"stake","amount":"10"}],"gas_limit":"200000","payer":"","granter":""},"tip":null},"signatures":[]} diff --git a/tests/systemtests/tx_test.go b/tests/systemtests/tx_test.go new file mode 100644 index 000000000000..d7099bdb2d3a --- /dev/null +++ b/tests/systemtests/tx_test.go @@ -0,0 +1,37 @@ +//go:build system_test + +package systemtests + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestQueryBySig(t *testing.T) { + sut.ResetChain(t) + + cli := NewCLIWrapper(t, sut, verbose) + // get validator address + valAddr := cli.GetKeyAddr("node0") + require.NotEmpty(t, valAddr) + + // add new key + receiverAddr := cli.AddKey("account1") + denom := "stake" + var transferAmount int64 = 1000 + + sut.StartChain(t) + + // qc := tx.NewServiceClient(sut.RPCClient(t)) + + // create unsign tx + bankSendCmdArgs := []string{"tx", "bank", "send", valAddr, receiverAddr, fmt.Sprintf("%d%s", transferAmount, denom), "--fees=10stake", "--sign-mode=direct", "--generate-only"} + res := cli.RunCommandWithArgs(bankSendCmdArgs...) + txFile := StoreTempFile(t, []byte(res)) + fmt.Println("txFile", txFile) + + res = cli.RunCommandWithArgs("tx", "sign", txFile.Name(), fmt.Sprintf("--from=%s", valAddr), fmt.Sprintf("--chain-id=%s",sut.chainID), fmt.Sprintf("--output-document=%s", txFile.Name())) + fmt.Println("res", res) +} From 5d2292f1b07329ec3e4611fb8f2c1f53015f8c61 Mon Sep 17 00:00:00 2001 From: Hieu Vu <72878483+hieuvubk@users.noreply.github.com> Date: Mon, 7 Oct 2024 19:32:44 +0700 Subject: [PATCH 2/6] test grpc --- tests/systemtests/tx_test.go | 434 ++++++++++++++++++++++++++++++++++- 1 file changed, 432 insertions(+), 2 deletions(-) diff --git a/tests/systemtests/tx_test.go b/tests/systemtests/tx_test.go index d7099bdb2d3a..01a315b538ef 100644 --- a/tests/systemtests/tx_test.go +++ b/tests/systemtests/tx_test.go @@ -3,12 +3,24 @@ package systemtests import ( + "context" + "encoding/base64" + "fmt" + "testing" + "github.com/cosmos/cosmos-sdk/types/tx" "github.com/stretchr/testify/require" + "github.com/tidwall/gjson" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" ) +var bankMsgSendEventAction = "message.action='/cosmos.bank.v1beta1.MsgSend'" + func TestQueryBySig(t *testing.T) { sut.ResetChain(t) @@ -24,7 +36,7 @@ func TestQueryBySig(t *testing.T) { sut.StartChain(t) - // qc := tx.NewServiceClient(sut.RPCClient(t)) + qc := tx.NewServiceClient(sut.RPCClient(t)) // create unsign tx bankSendCmdArgs := []string{"tx", "bank", "send", valAddr, receiverAddr, fmt.Sprintf("%d%s", transferAmount, denom), "--fees=10stake", "--sign-mode=direct", "--generate-only"} @@ -32,6 +44,424 @@ func TestQueryBySig(t *testing.T) { txFile := StoreTempFile(t, []byte(res)) fmt.Println("txFile", txFile) - res = cli.RunCommandWithArgs("tx", "sign", txFile.Name(), fmt.Sprintf("--from=%s", valAddr), fmt.Sprintf("--chain-id=%s",sut.chainID), fmt.Sprintf("--output-document=%s", txFile.Name())) + res = cli.RunCommandWithArgs("tx", "sign", txFile.Name(), fmt.Sprintf("--from=%s", valAddr), fmt.Sprintf("--chain-id=%s", sut.chainID), "--keyring-backend=test", "--home=./testnet/node0/simd") fmt.Println("res", res) + sig := gjson.Get(res, "signatures.0").String() + fmt.Println("sig", sig) + signedTxFile := StoreTempFile(t, []byte(res)) + + res = cli.Run("tx", "broadcast", signedTxFile.Name()) + fmt.Println("res", res) + RequireTxSuccess(t, res) + + sigFormatted := fmt.Sprintf("%s.%s='%s'", sdk.EventTypeTx, sdk.AttributeKeySignature, sig) + resp, err := qc.GetTxsEvent(context.Background(), &tx.GetTxsEventRequest{ + Query: sigFormatted, + OrderBy: 0, + Page: 0, + Limit: 10, + }) + require.NoError(t, err) + require.Len(t, resp.Txs, 1) + require.Len(t, resp.Txs[0].Signatures, 1) +} + +func TestSimulateTx_GRPC(t *testing.T) { + sut.ResetChain(t) + + cli := NewCLIWrapper(t, sut, verbose) + // get validator address + valAddr := cli.GetKeyAddr("node0") + require.NotEmpty(t, valAddr) + + // add new key + receiverAddr := cli.AddKey("account1") + denom := "stake" + var transferAmount int64 = 1000 + + sut.StartChain(t) + + qc := tx.NewServiceClient(sut.RPCClient(t)) + + // create unsign tx + bankSendCmdArgs := []string{"tx", "bank", "send", valAddr, receiverAddr, fmt.Sprintf("%d%s", transferAmount, denom), "--fees=10stake", "--sign-mode=direct", "--generate-only"} + res := cli.RunCommandWithArgs(bankSendCmdArgs...) + txFile := StoreTempFile(t, []byte(res)) + + res = cli.RunCommandWithArgs("tx", "sign", txFile.Name(), fmt.Sprintf("--from=%s", valAddr), fmt.Sprintf("--chain-id=%s", sut.chainID), "--keyring-backend=test", "--home=./testnet/node0/simd") + signedTxFile := StoreTempFile(t, []byte(res)) + + res = cli.RunCommandWithArgs("tx", "encode", signedTxFile.Name()) + txBz, err := base64.StdEncoding.DecodeString(res) + require.NoError(t, err) + + testCases := []struct { + name string + req *tx.SimulateRequest + expErr bool + expErrMsg string + }{ + {"nil request", nil, true, "request cannot be nil"}, + {"empty request", &tx.SimulateRequest{}, true, "empty txBytes is not allowed"}, + {"valid request with tx_bytes", &tx.SimulateRequest{TxBytes: txBz}, false, ""}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Broadcast the tx via gRPC via the validator's clientCtx (which goes + // through Tendermint). + res, err := qc.Simulate(context.Background(), tc.req) + if tc.expErr { + require.Error(t, err) + require.Contains(t, err.Error(), tc.expErrMsg) + } else { + require.NoError(t, err) + // Check the result and gas used are correct. + // + // The 12 events are: + // - Sending Fee to the pool: coin_spent, coin_received and transfer + // - tx.* events: tx.fee, tx.acc_seq, tx.signature + // - Sending Amount to recipient: coin_spent, coin_received and transfer + // - Msg events: message.module=bank, message.action=/cosmos.bank.v1beta1.MsgSend and message.sender= (in one message) + require.Equal(t, 10, len(res.GetResult().GetEvents())) + require.True(t, res.GetGasInfo().GetGasUsed() > 0) // Gas used sometimes change, just check it's not empty. + } + }) + } + + +} + +func TestGetTxEvents_GRPC(t *testing.T) { + sut.ResetChain(t) + + cli := NewCLIWrapper(t, sut, verbose) + // get validator address + valAddr := cli.GetKeyAddr("node0") + require.NotEmpty(t, valAddr) + + // add new key + receiverAddr := cli.AddKey("account1") + denom := "stake" + var transferAmount int64 = 1000 + + sut.StartChain(t) + + qc := tx.NewServiceClient(sut.RPCClient(t)) + rsp := cli.Run("tx", "bank", "send", valAddr, receiverAddr, fmt.Sprintf("%d%s", transferAmount, denom), "--note=foobar", "--fees=1stake") + txResult, found := cli.AwaitTxCommitted(rsp) + require.True(t, found) + RequireTxSuccess(t, txResult) + + rsp = cli.Run("tx", "bank", "send", valAddr, receiverAddr, fmt.Sprintf("%d%s", transferAmount, denom), "--fees=1stake") + txResult, found = cli.AwaitTxCommitted(rsp) + require.True(t, found) + RequireTxSuccess(t, txResult) + + testCases := []struct { + name string + req *tx.GetTxsEventRequest + expErr bool + expErrMsg string + expLen int + }{ + { + "nil request", + nil, + true, + "request cannot be nil", + 0, + }, + { + "empty request", + &tx.GetTxsEventRequest{}, + true, + "query cannot be empty", + 0, + }, + { + "request with dummy event", + &tx.GetTxsEventRequest{Query: "foobar"}, + true, + "failed to search for txs", + 0, + }, + { + "request with order-by", + &tx.GetTxsEventRequest{ + Query: bankMsgSendEventAction, + OrderBy: tx.OrderBy_ORDER_BY_ASC, + }, + false, + "", + 2, + }, + { + "without pagination", + &tx.GetTxsEventRequest{ + Query: bankMsgSendEventAction, + }, + false, + "", + 2, + }, + { + "with pagination", + &tx.GetTxsEventRequest{ + Query: bankMsgSendEventAction, + Page: 1, + Limit: 1, + }, + false, + "", + 1, + }, + { + "with multi events", + &tx.GetTxsEventRequest{ + Query: fmt.Sprintf("%s AND message.module='bank'", bankMsgSendEventAction), + }, + false, + "", + 2, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Query the tx via gRPC. + grpcRes, err := qc.GetTxsEvent(context.Background(), tc.req) + if tc.expErr { + require.Error(t, err) + require.Contains(t, err.Error(), tc.expErrMsg) + } else { + require.NoError(t, err) + require.GreaterOrEqual(t, len(grpcRes.Txs), 1) + require.Equal(t, "foobar", grpcRes.Txs[0].Body.Memo) + require.Equal(t, tc.expLen, len(grpcRes.Txs)) + + // Make sure fields are populated. + // ref: https://github.com/cosmos/cosmos-sdk/issues/8680 + // ref: https://github.com/cosmos/cosmos-sdk/issues/8681 + require.NotEmpty(t, grpcRes.TxResponses[0].Timestamp) + require.Empty(t, grpcRes.TxResponses[0].RawLog) // logs are empty if the transactions are successful + } + }) + } + + + +} + +func TestGetTx_GRPC(t *testing.T) { + sut.ResetChain(t) + + cli := NewCLIWrapper(t, sut, verbose) + // get validator address + valAddr := cli.GetKeyAddr("node0") + require.NotEmpty(t, valAddr) + + // add new key + receiverAddr := cli.AddKey("account1") + denom := "stake" + var transferAmount int64 = 1000 + + sut.StartChain(t) + + qc := tx.NewServiceClient(sut.RPCClient(t)) + + rsp := cli.Run("tx", "bank", "send", valAddr, receiverAddr, fmt.Sprintf("%d%s", transferAmount, denom), "--fees=1stake", "--note=foobar") + txResult, found := cli.AwaitTxCommitted(rsp) + require.True(t, found) + RequireTxSuccess(t, txResult) + txHash := gjson.Get(txResult, "txhash").String() + + testCases := []struct { + name string + req *tx.GetTxRequest + expErr bool + expErrMsg string + }{ + {"nil request", nil, true, "request cannot be nil"}, + {"empty request", &tx.GetTxRequest{}, true, "tx hash cannot be empty"}, + {"request with dummy hash", &tx.GetTxRequest{Hash: "deadbeef"}, true, "code = NotFound desc = tx not found: deadbeef"}, + {"good request", &tx.GetTxRequest{Hash: txHash}, false, ""}, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Query the tx via gRPC. + grpcRes, err := qc.GetTx(context.Background(), tc.req) + if tc.expErr { + require.Error(t, err) + require.Contains(t, err.Error(), tc.expErrMsg) + } else { + require.NoError(t, err) + require.Equal(t, "foobar", grpcRes.Tx.Body.Memo) + } + }) + } +} + +func TestGetBlockWithTxs_GRPC(t *testing.T) { + sut.ResetChain(t) + + cli := NewCLIWrapper(t, sut, verbose) + // get validator address + valAddr := cli.GetKeyAddr("node0") + require.NotEmpty(t, valAddr) + + // add new key + receiverAddr := cli.AddKey("account1") + denom := "stake" + var transferAmount int64 = 1000 + + sut.StartChain(t) + + qc := tx.NewServiceClient(sut.RPCClient(t)) + + rsp := cli.Run("tx", "bank", "send", valAddr, receiverAddr, fmt.Sprintf("%d%s", transferAmount, denom), "--fees=1stake", "--note=foobar") + txResult, found := cli.AwaitTxCommitted(rsp) + require.True(t, found) + RequireTxSuccess(t, txResult) + height := gjson.Get(txResult, "height").Int() + + testCases := []struct { + name string + req *tx.GetBlockWithTxsRequest + expErr bool + expErrMsg string + expTxsLen int + }{ + {"nil request", nil, true, "request cannot be nil", 0}, + {"empty request", &tx.GetBlockWithTxsRequest{}, true, "height must not be less than 1 or greater than the current height", 0}, + {"bad height", &tx.GetBlockWithTxsRequest{Height: 99999999}, true, "height must not be less than 1 or greater than the current height", 0}, + {"bad pagination", &tx.GetBlockWithTxsRequest{Height: height, Pagination: &query.PageRequest{Offset: 1000, Limit: 100}}, true, "out of range", 0}, + {"good request", &tx.GetBlockWithTxsRequest{Height: height}, false, "", 1}, + {"with pagination request", &tx.GetBlockWithTxsRequest{Height: height, Pagination: &query.PageRequest{Offset: 0, Limit: 1}}, false, "", 1}, + {"page all request", &tx.GetBlockWithTxsRequest{Height: height, Pagination: &query.PageRequest{Offset: 0, Limit: 100}}, false, "", 1}, + {"block with 0 tx", &tx.GetBlockWithTxsRequest{Height: height - 1, Pagination: &query.PageRequest{Offset: 0, Limit: 100}}, false, "", 0}, + } + for _, tc := range testCases { + t.Run(tc.name, func(t * testing.T) { + // Query the tx via gRPC. + grpcRes, err := qc.GetBlockWithTxs(context.Background(), tc.req) + if tc.expErr { + require.Error(t, err) + require.Contains(t, err.Error(), tc.expErrMsg) + } else { + require.NoError(t, err) + if tc.expTxsLen > 0 { + require.Equal(t, "foobar", grpcRes.Txs[0].Body.Memo) + } + require.Equal(t, grpcRes.Block.Header.Height, tc.req.Height) + if tc.req.Pagination != nil { + require.LessOrEqual(t, len(grpcRes.Txs), int(tc.req.Pagination.Limit)) + } + } + }) + } +} + +func TestTxEncode_GRPC(t *testing.T) { + sut.ResetChain(t) + + cli := NewCLIWrapper(t, sut, verbose) + // get validator address + valAddr := cli.GetKeyAddr("node0") + require.NotEmpty(t, valAddr) + + sut.StartChain(t) + + qc := tx.NewServiceClient(sut.RPCClient(t)) + + protoTx := &tx.Tx{ + Body: &tx.TxBody{ + Messages: []*codectypes.Any{}, + }, + AuthInfo: &tx.AuthInfo{}, + Signatures: [][]byte{}, + } + + testCases := []struct { + name string + req *tx.TxEncodeRequest + expErr bool + expErrMsg string + }{ + {"nil request", nil, true, "request cannot be nil"}, + {"empty request", &tx.TxEncodeRequest{}, true, "invalid empty tx"}, + {"valid tx request", &tx.TxEncodeRequest{Tx: protoTx}, false, ""}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + res, err := qc.TxEncode(context.Background(), tc.req) + if tc.expErr { + require.Error(t, err) + require.Contains(t, err.Error(), tc.expErrMsg) + require.Empty(t, res) + } else { + require.NoError(t, err) + } + }) + } } + +func TestTxDecode_GRPC(t *testing.T) { + sut.ResetChain(t) + + cli := NewCLIWrapper(t, sut, verbose) + // get validator address + valAddr := cli.GetKeyAddr("node0") + require.NotEmpty(t, valAddr) + + // add new key + receiverAddr := cli.AddKey("account1") + denom := "stake" + var transferAmount int64 = 1000 + + sut.StartChain(t) + + qc := tx.NewServiceClient(sut.RPCClient(t)) + + // create unsign tx + bankSendCmdArgs := []string{"tx", "bank", "send", valAddr, receiverAddr, fmt.Sprintf("%d%s", transferAmount, denom), "--fees=10stake", "--sign-mode=direct", "--generate-only"} + res := cli.RunCommandWithArgs(bankSendCmdArgs...) + txFile := StoreTempFile(t, []byte(res)) + fmt.Println("txFile", txFile) + + res = cli.RunCommandWithArgs("tx", "sign", txFile.Name(), fmt.Sprintf("--from=%s", valAddr), fmt.Sprintf("--chain-id=%s", sut.chainID), "--keyring-backend=test", "--home=./testnet/node0/simd") + signedTxFile := StoreTempFile(t, []byte(res)) + + res = cli.RunCommandWithArgs("tx", "encode", signedTxFile.Name()) + fmt.Println("res", res) + txBz, err := base64.StdEncoding.DecodeString(res) + fmt.Println("txBz", txBz, err) + invalidTxBytes := append(txBz, byte(0o00)) + + testCases := []struct { + name string + req *tx.TxDecodeRequest + expErr bool + expErrMsg string + }{ + {"nil request", nil, true, "request cannot be nil"}, + {"empty request", &tx.TxDecodeRequest{}, true, "invalid empty tx bytes"}, + {"invalid tx bytes", &tx.TxDecodeRequest{TxBytes: invalidTxBytes}, true, "tx parse error"}, + {"valid request with tx bytes", &tx.TxDecodeRequest{TxBytes: txBz}, false, ""}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + res, err := qc.TxDecode(context.Background(), tc.req) + if tc.expErr { + require.Error(t, err) + require.Contains(t, err.Error(), tc.expErrMsg) + require.Empty(t, res) + } else { + require.NoError(t, err) + require.NotEmpty(t, res.GetTx()) + } + }) + } +} \ No newline at end of file From 5d6d1e21201bc64e64450dd34e5b20053ea0710f Mon Sep 17 00:00:00 2001 From: Hieu Vu <72878483+hieuvubk@users.noreply.github.com> Date: Mon, 7 Oct 2024 19:37:20 +0700 Subject: [PATCH 3/6] clean up --- tests/systemtests/cometbft_client_test.go | 2 +- tests/systemtests/gov_test.go | 2 +- tests/systemtests/tx | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) delete mode 100644 tests/systemtests/tx diff --git a/tests/systemtests/cometbft_client_test.go b/tests/systemtests/cometbft_client_test.go index 3c79adfe846c..e031ae0c8f55 100644 --- a/tests/systemtests/cometbft_client_test.go +++ b/tests/systemtests/cometbft_client_test.go @@ -1,4 +1,4 @@ -//go:build !system_test +//go:build system_test package systemtests diff --git a/tests/systemtests/gov_test.go b/tests/systemtests/gov_test.go index 86aa61a7075e..73bc7fd96efc 100644 --- a/tests/systemtests/gov_test.go +++ b/tests/systemtests/gov_test.go @@ -1,4 +1,4 @@ -//go:build !system_test +//go:build system_test package systemtests diff --git a/tests/systemtests/tx b/tests/systemtests/tx deleted file mode 100644 index 51ae02b210e6..000000000000 --- a/tests/systemtests/tx +++ /dev/null @@ -1 +0,0 @@ -{"body":{"messages":[{"@type":"/cosmos.bank.v1beta1.MsgSend","from_address":"cosmos1psqxug2jg5w2xsyn4vmnvtm545x57crh0zr85d","to_address":"cosmos1azqhsyjw3qvv99eggyuzp4vg7fnh5jr433nnz0","amount":[{"denom":"stake","amount":"1000"}]}],"memo":"","timeout_height":"0","unordered":false,"timeout_timestamp":"1970-01-01T00:00:00Z","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[{"denom":"stake","amount":"10"}],"gas_limit":"200000","payer":"","granter":""},"tip":null},"signatures":[]} From cb168488ef5214f5a144ecb4826d3e5220c652d8 Mon Sep 17 00:00:00 2001 From: Hieu Vu <72878483+hieuvubk@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:32:23 +0700 Subject: [PATCH 4/6] multisig test ok --- tests/systemtests/tx_test.go | 60 +++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/tests/systemtests/tx_test.go b/tests/systemtests/tx_test.go index 01a315b538ef..cb0dbcd1a34a 100644 --- a/tests/systemtests/tx_test.go +++ b/tests/systemtests/tx_test.go @@ -42,16 +42,12 @@ func TestQueryBySig(t *testing.T) { bankSendCmdArgs := []string{"tx", "bank", "send", valAddr, receiverAddr, fmt.Sprintf("%d%s", transferAmount, denom), "--fees=10stake", "--sign-mode=direct", "--generate-only"} res := cli.RunCommandWithArgs(bankSendCmdArgs...) txFile := StoreTempFile(t, []byte(res)) - fmt.Println("txFile", txFile) res = cli.RunCommandWithArgs("tx", "sign", txFile.Name(), fmt.Sprintf("--from=%s", valAddr), fmt.Sprintf("--chain-id=%s", sut.chainID), "--keyring-backend=test", "--home=./testnet/node0/simd") - fmt.Println("res", res) sig := gjson.Get(res, "signatures.0").String() - fmt.Println("sig", sig) signedTxFile := StoreTempFile(t, []byte(res)) res = cli.Run("tx", "broadcast", signedTxFile.Name()) - fmt.Println("res", res) RequireTxSuccess(t, res) sigFormatted := fmt.Sprintf("%s.%s='%s'", sdk.EventTypeTx, sdk.AttributeKeySignature, sig) @@ -428,15 +424,12 @@ func TestTxDecode_GRPC(t *testing.T) { bankSendCmdArgs := []string{"tx", "bank", "send", valAddr, receiverAddr, fmt.Sprintf("%d%s", transferAmount, denom), "--fees=10stake", "--sign-mode=direct", "--generate-only"} res := cli.RunCommandWithArgs(bankSendCmdArgs...) txFile := StoreTempFile(t, []byte(res)) - fmt.Println("txFile", txFile) res = cli.RunCommandWithArgs("tx", "sign", txFile.Name(), fmt.Sprintf("--from=%s", valAddr), fmt.Sprintf("--chain-id=%s", sut.chainID), "--keyring-backend=test", "--home=./testnet/node0/simd") signedTxFile := StoreTempFile(t, []byte(res)) res = cli.RunCommandWithArgs("tx", "encode", signedTxFile.Name()) - fmt.Println("res", res) txBz, err := base64.StdEncoding.DecodeString(res) - fmt.Println("txBz", txBz, err) invalidTxBytes := append(txBz, byte(0o00)) testCases := []struct { @@ -464,4 +457,57 @@ func TestTxDecode_GRPC(t *testing.T) { } }) } +} + +func TestSimMultiSigTx(t *testing.T) { + sut.ResetChain(t) + + cli := NewCLIWrapper(t, sut, verbose) + // get validator address + valAddr := cli.GetKeyAddr("node0") + require.NotEmpty(t, valAddr) + + // add new key + _ = cli.AddKey("account1") + _ = cli.AddKey("account2") + denom := "stake" + var transferAmount int64 = 1000 + + sut.StartChain(t) + + multiSigName := "multisig" + res := cli.RunCommandWithArgs("keys", "add", multiSigName, "--multisig=account1,account2", "--multisig-threshold=2", "--keyring-backend=test", "--home=./testnet") + multiSigAddr := cli.GetKeyAddr(multiSigName) + + // Send from validator to multisig addr + rsp := cli.Run("tx", "bank", "send", valAddr, multiSigAddr, fmt.Sprintf("%d%s", transferAmount, denom), "--fees=1stake") + txResult, found := cli.AwaitTxCommitted(rsp) + require.True(t, found) + RequireTxSuccess(t, txResult) + + multiSigBalance := cli.QueryBalance(multiSigAddr, denom) + require.Equal(t, multiSigBalance, transferAmount) + + // Send from multisig to validator + // create unsign tx + var newTransferAmount int64 = 100 + bankSendCmdArgs := []string{"tx", "bank", "send", multiSigAddr, valAddr, fmt.Sprintf("%d%s", newTransferAmount, denom), "--fees=10stake", "--sign-mode=direct", "--generate-only"} + res = cli.RunCommandWithArgs(bankSendCmdArgs...) + txFile := StoreTempFile(t, []byte(res)) + + res = cli.RunCommandWithArgs("tx", "sign", txFile.Name(), fmt.Sprintf("--from=%s", "account1"), fmt.Sprintf("--multisig=%s", multiSigAddr), fmt.Sprintf("--chain-id=%s", sut.chainID), "--keyring-backend=test", "--home=./testnet") + account1Signed := StoreTempFile(t, []byte(res)) + res = cli.RunCommandWithArgs("tx", "sign", txFile.Name(), fmt.Sprintf("--from=%s", "account2"), fmt.Sprintf("--multisig=%s", multiSigAddr), fmt.Sprintf("--chain-id=%s", sut.chainID), "--keyring-backend=test", "--home=./testnet") + account2Signed := StoreTempFile(t, []byte(res)) + + res = cli.RunCommandWithArgs("tx", "multisign-batch", txFile.Name(), multiSigName, account1Signed.Name(), account2Signed.Name(), fmt.Sprintf("--chain-id=%s", sut.chainID), "--keyring-backend=test", "--home=./testnet") + txSignedFile := StoreTempFile(t, []byte(res)) + + res = cli.Run("tx", "broadcast", txSignedFile.Name()) + RequireTxSuccess(t, res) + + multiSigBalance = cli.QueryBalance(multiSigAddr, denom) + require.Equal(t, multiSigBalance, transferAmount - newTransferAmount - 10) + + } \ No newline at end of file From 65a87ef0f757dfead3dc7bb109ad983a4c437678 Mon Sep 17 00:00:00 2001 From: Hieu Vu <72878483+hieuvubk@users.noreply.github.com> Date: Sun, 13 Oct 2024 22:11:01 +0700 Subject: [PATCH 5/6] add grpc gateway tests --- tests/systemtests/tx_test.go | 403 ++++++++++++++++++++++++++++++++++- 1 file changed, 395 insertions(+), 8 deletions(-) diff --git a/tests/systemtests/tx_test.go b/tests/systemtests/tx_test.go index cb0dbcd1a34a..630f444dd2fd 100644 --- a/tests/systemtests/tx_test.go +++ b/tests/systemtests/tx_test.go @@ -5,18 +5,20 @@ package systemtests import ( "context" "encoding/base64" + "encoding/json" "fmt" "testing" + "github.com/cosmos/cosmos-sdk/testutil" "github.com/cosmos/cosmos-sdk/types/tx" "github.com/stretchr/testify/require" "github.com/tidwall/gjson" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" ) var bankMsgSendEventAction = "message.action='/cosmos.bank.v1beta1.MsgSend'" @@ -124,8 +126,68 @@ func TestSimulateTx_GRPC(t *testing.T) { } }) } +} + +func TestSimulateTx_GRPCGateway(t *testing.T) { + sut.ResetChain(t) + cli := NewCLIWrapper(t, sut, verbose) + // get validator address + valAddr := cli.GetKeyAddr("node0") + require.NotEmpty(t, valAddr) + // add new key + receiverAddr := cli.AddKey("account1") + denom := "stake" + var transferAmount int64 = 1000 + + sut.StartChain(t) + + // qc := tx.NewServiceClient(sut.RPCClient(t)) + baseURL := sut.APIAddress() + + // create unsign tx + bankSendCmdArgs := []string{"tx", "bank", "send", valAddr, receiverAddr, fmt.Sprintf("%d%s", transferAmount, denom), "--fees=10stake", "--sign-mode=direct", "--generate-only"} + res := cli.RunCommandWithArgs(bankSendCmdArgs...) + txFile := StoreTempFile(t, []byte(res)) + + res = cli.RunCommandWithArgs("tx", "sign", txFile.Name(), fmt.Sprintf("--from=%s", valAddr), fmt.Sprintf("--chain-id=%s", sut.chainID), "--keyring-backend=test", "--home=./testnet/node0/simd") + signedTxFile := StoreTempFile(t, []byte(res)) + + res = cli.RunCommandWithArgs("tx", "encode", signedTxFile.Name()) + txBz, err := base64.StdEncoding.DecodeString(res) + require.NoError(t, err) + + testCases := []struct { + name string + req *tx.SimulateRequest + expErr bool + expErrMsg string + }{ + {"empty request", &tx.SimulateRequest{}, true, "empty txBytes is not allowed"}, + {"valid request with tx_bytes", &tx.SimulateRequest{TxBytes: txBz}, false, ""}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + reqBz, err := json.Marshal(tc.req) + res, err := testutil.PostRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/simulate", baseURL), "application/json", reqBz) + require.NoError(t, err) + if tc.expErr { + require.Contains(t, string(res), tc.expErrMsg) + } else { + require.NoError(t, err) + msgResponses := gjson.Get(string(res), "result.msg_responses").Array() + require.Equal(t, len(msgResponses), 1) + + events := gjson.Get(string(res), "result.events").Array() + require.Equal(t, len(events), 10) + + gasUsed := gjson.Get(string(res), "gas_info.gas_used").Int() + require.True(t, gasUsed > 0) + } + }) + } } func TestGetTxEvents_GRPC(t *testing.T) { @@ -244,8 +306,105 @@ func TestGetTxEvents_GRPC(t *testing.T) { } }) } - +} + +func TestGetTxEvents_GRPCGateway(t *testing.T) { + sut.ResetChain(t) + + cli := NewCLIWrapper(t, sut, verbose) + // get validator address + valAddr := cli.GetKeyAddr("node0") + require.NotEmpty(t, valAddr) + + // add new key + receiverAddr := cli.AddKey("account1") + denom := "stake" + var transferAmount int64 = 1000 + + sut.StartChain(t) + + // qc := tx.NewServiceClient(sut.RPCClient(t)) + baseURL := sut.APIAddress() + rsp := cli.Run("tx", "bank", "send", valAddr, receiverAddr, fmt.Sprintf("%d%s", transferAmount, denom), "--note=foobar", "--fees=1stake") + txResult, found := cli.AwaitTxCommitted(rsp) + require.True(t, found) + RequireTxSuccess(t, txResult) + + rsp = cli.Run("tx", "bank", "send", valAddr, receiverAddr, fmt.Sprintf("%d%s", transferAmount, denom), "--fees=1stake") + txResult, found = cli.AwaitTxCommitted(rsp) + require.True(t, found) + RequireTxSuccess(t, txResult) + + testCases := []struct { + name string + url string + expErr bool + expErrMsg string + expLen int + }{ + { + "empty params", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs", baseURL), + true, + "query cannot be empty", 0, + }, + { + "without pagination", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?query=%s", baseURL, bankMsgSendEventAction), + false, + "", 2, + }, + { + "with pagination", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?query=%s&page=%d&limit=%d", baseURL, bankMsgSendEventAction, 1, 1), + false, + "", 1, + }, + { + "valid request: order by asc", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?query=%s&query=%s&order_by=ORDER_BY_ASC", baseURL, bankMsgSendEventAction, "message.module='bank'"), + false, + "", 2, + }, + { + "valid request: order by desc", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?query=%s&query=%s&order_by=ORDER_BY_DESC", baseURL, bankMsgSendEventAction, "message.module='bank'"), + false, + "", 2, + }, + { + "invalid request: invalid order by", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?query=%s&query=%s&order_by=invalid_order", baseURL, bankMsgSendEventAction, "message.module='bank'"), + true, + "is not a valid tx.OrderBy", 0, + }, + { + "expect pass with multiple-events", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?query=%s&query=%s", baseURL, bankMsgSendEventAction, "message.module='bank'"), + false, + "", 2, + }, + { + "expect pass with escape event", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?query=%s", baseURL, "message.action%3D'/cosmos.bank.v1beta1.MsgSend'"), + false, + "", 2, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + res, err := testutil.GetRequest(tc.url) + require.NoError(t, err) + if tc.expErr { + require.Contains(t, string(res), tc.expErrMsg) + } else { + require.NoError(t, err) + txs := gjson.Get(string(res), "txs").Array() + require.Equal(t, len(txs), tc.expLen) + } + }) + } } @@ -298,6 +457,71 @@ func TestGetTx_GRPC(t *testing.T) { } } +func TestGetTx_GRPCGateway(t *testing.T) { + sut.ResetChain(t) + + cli := NewCLIWrapper(t, sut, verbose) + // get validator address + valAddr := cli.GetKeyAddr("node0") + require.NotEmpty(t, valAddr) + + // add new key + receiverAddr := cli.AddKey("account1") + denom := "stake" + var transferAmount int64 = 1000 + + sut.StartChain(t) + + baseURL := sut.APIAddress() + + rsp := cli.Run("tx", "bank", "send", valAddr, receiverAddr, fmt.Sprintf("%d%s", transferAmount, denom), "--fees=1stake", "--note=foobar") + txResult, found := cli.AwaitTxCommitted(rsp) + require.True(t, found) + RequireTxSuccess(t, txResult) + txHash := gjson.Get(txResult, "txhash").String() + + testCases := []struct { + name string + url string + expErr bool + expErrMsg string + }{ + { + "empty params", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/", baseURL), + true, "tx hash cannot be empty", + }, + { + "dummy hash", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/%s", baseURL, "deadbeef"), + true, "tx not found", + }, + { + "good hash", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/%s", baseURL, txHash), + false, "", + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + res, err := testutil.GetRequest(tc.url) + require.NoError(t, err) + if tc.expErr { + require.Contains(t, string(res), tc.expErrMsg) + } else { + timestamp := gjson.Get(string(res), "tx_response.timestamp").String() + require.NotEmpty(t, timestamp) + + height := gjson.Get(string(res), "tx_response.height").Int() + require.NotZero(t, height) + + rawLog := gjson.Get(string(res), "tx_response.raw_log").String() + require.Empty(t, rawLog) + } + }) + } +} + func TestGetBlockWithTxs_GRPC(t *testing.T) { sut.ResetChain(t) @@ -338,7 +562,7 @@ func TestGetBlockWithTxs_GRPC(t *testing.T) { {"block with 0 tx", &tx.GetBlockWithTxsRequest{Height: height - 1, Pagination: &query.PageRequest{Offset: 0, Limit: 100}}, false, "", 0}, } for _, tc := range testCases { - t.Run(tc.name, func(t * testing.T) { + t.Run(tc.name, func(t *testing.T) { // Query the tx via gRPC. grpcRes, err := qc.GetBlockWithTxs(context.Background(), tc.req) if tc.expErr { @@ -358,6 +582,68 @@ func TestGetBlockWithTxs_GRPC(t *testing.T) { } } +func TestGetBlockWithTxs_GRPCGateway(t *testing.T) { + sut.ResetChain(t) + + cli := NewCLIWrapper(t, sut, verbose) + // get validator address + valAddr := cli.GetKeyAddr("node0") + require.NotEmpty(t, valAddr) + + // add new key + receiverAddr := cli.AddKey("account1") + denom := "stake" + var transferAmount int64 = 1000 + + sut.StartChain(t) + + baseUrl := sut.APIAddress() + + rsp := cli.Run("tx", "bank", "send", valAddr, receiverAddr, fmt.Sprintf("%d%s", transferAmount, denom), "--fees=1stake", "--note=foobar") + txResult, found := cli.AwaitTxCommitted(rsp) + require.True(t, found) + RequireTxSuccess(t, txResult) + height := gjson.Get(txResult, "height").Int() + + testCases := []struct { + name string + url string + expErr bool + expErrMsg string + }{ + { + "empty params", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/block/0", baseUrl), + true, "height must not be less than 1 or greater than the current height", + }, + { + "bad height", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/block/%d", baseUrl, 9999999), + true, "height must not be less than 1 or greater than the current height", + }, + { + "good request", + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/block/%d", baseUrl, height), + false, "", + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + res, err := testutil.GetRequest(tc.url) + require.NoError(t, err) + if tc.expErr { + require.Contains(t, string(res), tc.expErrMsg) + } else { + memo := gjson.Get(string(res), "txs.0.body.memo").String() + require.Equal(t, memo, "foobar") + + respHeight := gjson.Get(string(res), "block.header.height").Int() + require.Equal(t, respHeight, height) + } + }) + } +} + func TestTxEncode_GRPC(t *testing.T) { sut.ResetChain(t) @@ -374,7 +660,7 @@ func TestTxEncode_GRPC(t *testing.T) { Body: &tx.TxBody{ Messages: []*codectypes.Any{}, }, - AuthInfo: &tx.AuthInfo{}, + AuthInfo: &tx.AuthInfo{}, Signatures: [][]byte{}, } @@ -403,6 +689,49 @@ func TestTxEncode_GRPC(t *testing.T) { } } +func TestTxEncode_GRPCGateway(t *testing.T) { + sut.ResetChain(t) + + cli := NewCLIWrapper(t, sut, verbose) + // get validator address + valAddr := cli.GetKeyAddr("node0") + require.NotEmpty(t, valAddr) + + sut.StartChain(t) + + baseUrl := sut.APIAddress() + + protoTx := &tx.Tx{ + Body: &tx.TxBody{ + Messages: []*codectypes.Any{}, + }, + AuthInfo: &tx.AuthInfo{}, + Signatures: [][]byte{}, + } + + testCases := []struct { + name string + req *tx.TxEncodeRequest + expErr bool + expErrMsg string + }{ + {"empty request", &tx.TxEncodeRequest{}, true, "invalid empty tx"}, + {"valid tx request", &tx.TxEncodeRequest{Tx: protoTx}, false, ""}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + reqBz, err := json.Marshal(tc.req) + + res, err := testutil.PostRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/encode", baseUrl), "application/json", reqBz) + require.NoError(t, err) + if tc.expErr { + require.Contains(t, string(res), tc.expErrMsg) + } + }) + } +} + func TestTxDecode_GRPC(t *testing.T) { sut.ResetChain(t) @@ -430,6 +759,7 @@ func TestTxDecode_GRPC(t *testing.T) { res = cli.RunCommandWithArgs("tx", "encode", signedTxFile.Name()) txBz, err := base64.StdEncoding.DecodeString(res) + require.NoError(t, err) invalidTxBytes := append(txBz, byte(0o00)) testCases := []struct { @@ -459,6 +789,64 @@ func TestTxDecode_GRPC(t *testing.T) { } } +func TestTxDecode_GRPCGateway(t *testing.T) { + sut.ResetChain(t) + + cli := NewCLIWrapper(t, sut, verbose) + // get validator address + valAddr := cli.GetKeyAddr("node0") + require.NotEmpty(t, valAddr) + + // add new key + receiverAddr := cli.AddKey("account1") + denom := "stake" + var transferAmount int64 = 1000 + + sut.StartChain(t) + + basrUrl := sut.APIAddress() + + // create unsign tx + bankSendCmdArgs := []string{"tx", "bank", "send", valAddr, receiverAddr, fmt.Sprintf("%d%s", transferAmount, denom), "--fees=10stake", "--sign-mode=direct", "--generate-only"} + res := cli.RunCommandWithArgs(bankSendCmdArgs...) + txFile := StoreTempFile(t, []byte(res)) + + res = cli.RunCommandWithArgs("tx", "sign", txFile.Name(), fmt.Sprintf("--from=%s", valAddr), fmt.Sprintf("--chain-id=%s", sut.chainID), "--keyring-backend=test", "--home=./testnet/node0/simd") + signedTxFile := StoreTempFile(t, []byte(res)) + + res = cli.RunCommandWithArgs("tx", "encode", signedTxFile.Name()) + txBz, err := base64.StdEncoding.DecodeString(res) + require.NoError(t, err) + invalidTxBytes := append(txBz, byte(0o00)) + + testCases := []struct { + name string + req *tx.TxDecodeRequest + expErr bool + expErrMsg string + }{ + {"empty request", &tx.TxDecodeRequest{}, true, "invalid empty tx bytes"}, + {"invalid tx bytes", &tx.TxDecodeRequest{TxBytes: invalidTxBytes}, true, "tx parse error"}, + {"valid request with tx_bytes", &tx.TxDecodeRequest{TxBytes: txBz}, false, ""}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + reqBz, err := json.Marshal(tc.req) + require.NoError(t, err) + + res, err := testutil.PostRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/decode", basrUrl), "application/json", reqBz) + require.NoError(t, err) + if tc.expErr { + require.Contains(t, string(res), tc.expErrMsg) + } else { + signatures := gjson.Get(string(res), "tx.signatures").Array() + require.Equal(t, len(signatures), 1) + } + }) + } +} + func TestSimMultiSigTx(t *testing.T) { sut.ResetChain(t) @@ -505,9 +893,8 @@ func TestSimMultiSigTx(t *testing.T) { res = cli.Run("tx", "broadcast", txSignedFile.Name()) RequireTxSuccess(t, res) - + multiSigBalance = cli.QueryBalance(multiSigAddr, denom) - require.Equal(t, multiSigBalance, transferAmount - newTransferAmount - 10) + require.Equal(t, multiSigBalance, transferAmount-newTransferAmount-10) - -} \ No newline at end of file +} From 4d3a46e80d2e2667642c20438a0f56915ba6669e Mon Sep 17 00:00:00 2001 From: Hieu Vu <72878483+hieuvubk@users.noreply.github.com> Date: Thu, 17 Oct 2024 17:31:58 +0700 Subject: [PATCH 6/6] remove migrated test --- tests/e2e/tx/service_test.go | 866 ----------------------------------- 1 file changed, 866 deletions(-) diff --git a/tests/e2e/tx/service_test.go b/tests/e2e/tx/service_test.go index f0e4e527664c..a18568c99e28 100644 --- a/tests/e2e/tx/service_test.go +++ b/tests/e2e/tx/service_test.go @@ -2,40 +2,24 @@ package tx_test import ( "context" - "encoding/base64" "fmt" "os" - "strings" "testing" - "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "cosmossdk.io/math" "cosmossdk.io/simapp" banktypes "cosmossdk.io/x/bank/types" - "github.com/cosmos/cosmos-sdk/client" - clienttx "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/cosmos/cosmos-sdk/crypto/hd" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/testutil" "github.com/cosmos/cosmos-sdk/testutil/cli" "github.com/cosmos/cosmos-sdk/testutil/network" - "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/query" "github.com/cosmos/cosmos-sdk/types/tx" - "github.com/cosmos/cosmos-sdk/types/tx/signing" - authclient "github.com/cosmos/cosmos-sdk/x/auth/client" - authtest "github.com/cosmos/cosmos-sdk/x/auth/client/testutil" "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" ) -var bankMsgSendEventAction = fmt.Sprintf("message.action='%s'", sdk.MsgTypeURL(&banktypes.MsgSend{})) - type E2ETestSuite struct { suite.Suite @@ -119,819 +103,6 @@ func (s *E2ETestSuite) TearDownSuite() { s.network.Cleanup() } -func (s *E2ETestSuite) TestQueryBySig() { - // broadcast tx - txb := s.mkTxBuilder() - txbz, err := s.cfg.TxConfig.TxEncoder()(txb.GetTx()) - s.Require().NoError(err) - resp, err := s.queryClient.BroadcastTx(context.Background(), &tx.BroadcastTxRequest{TxBytes: txbz, Mode: tx.BroadcastMode_BROADCAST_MODE_SYNC}) - s.Require().NoError(err) - s.Require().NotEmpty(resp.TxResponse.TxHash) - - s.Require().NoError(s.network.WaitForNextBlock()) - - // get the signature out of the builder - sigs, err := txb.GetTx().GetSignaturesV2() - s.Require().NoError(err) - s.Require().Len(sigs, 1) - sig, ok := sigs[0].Data.(*signing.SingleSignatureData) - s.Require().True(ok) - - // encode, format, query - b64Sig := base64.StdEncoding.EncodeToString(sig.Signature) - sigFormatted := fmt.Sprintf("%s.%s='%s'", sdk.EventTypeTx, sdk.AttributeKeySignature, b64Sig) - res, err := s.queryClient.GetTxsEvent(context.Background(), &tx.GetTxsEventRequest{ - Query: sigFormatted, - OrderBy: 0, - Page: 0, - Limit: 10, - }) - s.Require().NoError(err) - s.Require().Len(res.Txs, 1) - s.Require().Len(res.Txs[0].Signatures, 1) - s.Require().Equal(res.Txs[0].Signatures[0], sig.Signature) -} - -func (s *E2ETestSuite) TestSimulateTx_GRPC() { - val := s.network.GetValidators()[0] - txBuilder := s.mkTxBuilder() - // Convert the txBuilder to a tx.Tx. - protoTx, err := txBuilder.GetTx().(interface{ AsTx() (*tx.Tx, error) }).AsTx() - s.Require().NoError(err) - // Encode the txBuilder to txBytes. - txBytes, err := val.GetClientCtx().TxConfig.TxEncoder()(txBuilder.GetTx()) - s.Require().NoError(err) - - testCases := []struct { - name string - req *tx.SimulateRequest - expErr bool - expErrMsg string - }{ - {"nil request", nil, true, "request cannot be nil"}, - {"empty request", &tx.SimulateRequest{}, true, "empty txBytes is not allowed"}, - {"valid request with proto tx (deprecated)", &tx.SimulateRequest{Tx: protoTx}, false, ""}, - {"valid request with tx_bytes", &tx.SimulateRequest{TxBytes: txBytes}, false, ""}, - } - - for _, tc := range testCases { - s.Run(tc.name, func() { - // Broadcast the tx via gRPC via the validator's clientCtx (which goes - // through Tendermint). - res, err := s.queryClient.Simulate(context.Background(), tc.req) - if tc.expErr { - s.Require().Error(err) - s.Require().Contains(err.Error(), tc.expErrMsg) - } else { - s.Require().NoError(err) - // Check the result and gas used are correct. - // - // The 12 events are: - // - Sending Fee to the pool: coin_spent, coin_received and transfer - // - tx.* events: tx.fee, tx.acc_seq, tx.signature - // - Sending Amount to recipient: coin_spent, coin_received and transfer - // - Msg events: message.module=bank, message.action=/cosmos.bank.v1beta1.MsgSend and message.sender= (in one message) - s.Require().Equal(10, len(res.GetResult().GetEvents())) - s.Require().True(res.GetGasInfo().GetGasUsed() > 0) // Gas used sometimes change, just check it's not empty. - } - }) - } -} - -func (s *E2ETestSuite) TestSimulateTx_GRPCGateway() { - val := s.network.GetValidators()[0] - txBuilder := s.mkTxBuilder() - // Convert the txBuilder to a tx.Tx. - protoTx, err := txBuilder.GetTx().(interface{ AsTx() (*tx.Tx, error) }).AsTx() - s.Require().NoError(err) - // Encode the txBuilder to txBytes. - txBytes, err := val.GetClientCtx().TxConfig.TxEncoder()(txBuilder.GetTx()) - s.Require().NoError(err) - - testCases := []struct { - name string - req *tx.SimulateRequest - expErr bool - expErrMsg string - }{ - {"empty request", &tx.SimulateRequest{}, true, "empty txBytes is not allowed"}, - {"valid request with proto tx (deprecated)", &tx.SimulateRequest{Tx: protoTx}, false, ""}, - {"valid request with tx_bytes", &tx.SimulateRequest{TxBytes: txBytes}, false, ""}, - } - - for _, tc := range testCases { - s.Run(tc.name, func() { - req, err := val.GetClientCtx().Codec.MarshalJSON(tc.req) - s.Require().NoError(err) - res, err := testutil.PostRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/simulate", val.GetAPIAddress()), "application/json", req) - s.Require().NoError(err) - if tc.expErr { - s.Require().Contains(string(res), tc.expErrMsg) - } else { - var result tx.SimulateResponse - err = val.GetClientCtx().Codec.UnmarshalJSON(res, &result) - s.Require().NoError(err) - // Check the result and gas used are correct. - s.Require().Len(result.GetResult().MsgResponses, 1) - s.Require().Equal(10, len(result.GetResult().GetEvents())) // See TestSimulateTx_GRPC for the 10 events. - s.Require().True(result.GetGasInfo().GetGasUsed() > 0) // Gas used sometimes change, just check it's not empty. - } - }) - } -} - -func (s *E2ETestSuite) TestGetTxEvents_GRPC() { - testCases := []struct { - name string - req *tx.GetTxsEventRequest - expErr bool - expErrMsg string - expLen int - }{ - { - "nil request", - nil, - true, - "request cannot be nil", - 0, - }, - { - "empty request", - &tx.GetTxsEventRequest{}, - true, - "query cannot be empty", - 0, - }, - { - "request with dummy event", - &tx.GetTxsEventRequest{Query: "foobar"}, - true, - "failed to search for txs", - 0, - }, - { - "request with order-by", - &tx.GetTxsEventRequest{ - Query: bankMsgSendEventAction, - OrderBy: tx.OrderBy_ORDER_BY_ASC, - }, - false, - "", - 3, - }, - { - "without pagination", - &tx.GetTxsEventRequest{ - Query: bankMsgSendEventAction, - }, - false, - "", - 3, - }, - { - "with pagination", - &tx.GetTxsEventRequest{ - Query: bankMsgSendEventAction, - Page: 1, - Limit: 2, - }, - false, - "", - 2, - }, - { - "with multi events", - &tx.GetTxsEventRequest{ - Query: fmt.Sprintf("%s AND message.module='bank'", bankMsgSendEventAction), - }, - false, - "", - 3, - }, - } - for _, tc := range testCases { - s.Run(tc.name, func() { - // Query the tx via gRPC. - grpcRes, err := s.queryClient.GetTxsEvent(context.Background(), tc.req) - if tc.expErr { - s.Require().Error(err) - s.Require().Contains(err.Error(), tc.expErrMsg) - } else { - s.Require().NoError(err) - s.Require().GreaterOrEqual(len(grpcRes.Txs), 1) - s.Require().Equal("foobar", grpcRes.Txs[0].Body.Memo) - s.Require().Equal(tc.expLen, len(grpcRes.Txs)) - - // Make sure fields are populated. - // ref: https://github.com/cosmos/cosmos-sdk/issues/8680 - // ref: https://github.com/cosmos/cosmos-sdk/issues/8681 - s.Require().NotEmpty(grpcRes.TxResponses[0].Timestamp) - s.Require().Empty(grpcRes.TxResponses[0].RawLog) // logs are empty if the transactions are successful - } - }) - } -} - -func (s *E2ETestSuite) TestGetTxEvents_GRPCGateway() { - val := s.network.GetValidators()[0] - testCases := []struct { - name string - url string - expErr bool - expErrMsg string - expLen int - }{ - { - "empty params", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs", val.GetAPIAddress()), - true, - "query cannot be empty", 0, - }, - { - "without pagination", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?query=%s", val.GetAPIAddress(), bankMsgSendEventAction), - false, - "", 3, - }, - { - "with pagination", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?query=%s&page=%d&limit=%d", val.GetAPIAddress(), bankMsgSendEventAction, 1, 2), - false, - "", 2, - }, - { - "valid request: order by asc", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?query=%s&query=%s&order_by=ORDER_BY_ASC", val.GetAPIAddress(), bankMsgSendEventAction, "message.module='bank'"), - false, - "", 3, - }, - { - "valid request: order by desc", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?query=%s&query=%s&order_by=ORDER_BY_DESC", val.GetAPIAddress(), bankMsgSendEventAction, "message.module='bank'"), - false, - "", 3, - }, - { - "invalid request: invalid order by", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?query=%s&query=%s&order_by=invalid_order", val.GetAPIAddress(), bankMsgSendEventAction, "message.module='bank'"), - true, - "is not a valid tx.OrderBy", 0, - }, - { - "expect pass with multiple-events", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?query=%s&query=%s", val.GetAPIAddress(), bankMsgSendEventAction, "message.module='bank'"), - false, - "", 3, - }, - { - "expect pass with escape event", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?query=%s", val.GetAPIAddress(), "message.action%3D'/cosmos.bank.v1beta1.MsgSend'"), - false, - "", 3, - }, - } - for _, tc := range testCases { - s.Run(tc.name, func() { - res, err := testutil.GetRequest(tc.url) - s.Require().NoError(err) - if tc.expErr { - s.Require().Contains(string(res), tc.expErrMsg) - } else { - var result tx.GetTxsEventResponse - err = val.GetClientCtx().Codec.UnmarshalJSON(res, &result) - s.Require().NoError(err, "failed to unmarshal JSON: %s", res) - s.Require().GreaterOrEqual(len(result.Txs), 1) - s.Require().Equal("foobar", result.Txs[0].Body.Memo) - s.Require().NotZero(result.TxResponses[0].Height) - s.Require().Equal(tc.expLen, len(result.Txs)) - } - }) - } -} - -func (s *E2ETestSuite) TestGetTx_GRPC() { - testCases := []struct { - name string - req *tx.GetTxRequest - expErr bool - expErrMsg string - }{ - {"nil request", nil, true, "request cannot be nil"}, - {"empty request", &tx.GetTxRequest{}, true, "tx hash cannot be empty"}, - {"request with dummy hash", &tx.GetTxRequest{Hash: "deadbeef"}, true, "code = NotFound desc = tx not found: deadbeef"}, - {"good request", &tx.GetTxRequest{Hash: s.goodTxHash}, false, ""}, - } - for _, tc := range testCases { - s.Run(tc.name, func() { - // Query the tx via gRPC. - grpcRes, err := s.queryClient.GetTx(context.Background(), tc.req) - if tc.expErr { - s.Require().Error(err) - s.Require().Contains(err.Error(), tc.expErrMsg) - } else { - s.Require().NoError(err) - s.Require().Equal("foobar", grpcRes.Tx.Body.Memo) - } - }) - } -} - -func (s *E2ETestSuite) TestGetTx_GRPCGateway() { - val := s.network.GetValidators()[0] - testCases := []struct { - name string - url string - expErr bool - expErrMsg string - }{ - { - "empty params", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/", val.GetAPIAddress()), - true, "tx hash cannot be empty", - }, - { - "dummy hash", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/%s", val.GetAPIAddress(), "deadbeef"), - true, "code = NotFound desc = tx not found: deadbeef", - }, - { - "good hash", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/%s", val.GetAPIAddress(), s.goodTxHash), - false, "", - }, - } - for _, tc := range testCases { - s.Run(tc.name, func() { - res, err := testutil.GetRequest(tc.url) - s.Require().NoError(err) - if tc.expErr { - s.Require().Contains(string(res), tc.expErrMsg) - } else { - var result tx.GetTxResponse - err = val.GetClientCtx().Codec.UnmarshalJSON(res, &result) - s.Require().NoError(err) - s.Require().Equal("foobar", result.Tx.Body.Memo) - s.Require().NotZero(result.TxResponse.Height) - - // Make sure fields are populated. - // ref: https://github.com/cosmos/cosmos-sdk/issues/8680 - // ref: https://github.com/cosmos/cosmos-sdk/issues/8681 - s.Require().NotEmpty(result.TxResponse.Timestamp) - s.Require().Empty(result.TxResponse.RawLog) // logs are empty on successful transactions - } - }) - } -} - -func (s *E2ETestSuite) TestBroadcastTx_GRPC() { - val := s.network.GetValidators()[0] - txBuilder := s.mkTxBuilder() - txBytes, err := val.GetClientCtx().TxConfig.TxEncoder()(txBuilder.GetTx()) - s.Require().NoError(err) - - testCases := []struct { - name string - req *tx.BroadcastTxRequest - expErr bool - expErrMsg string - }{ - {"nil request", nil, true, "request cannot be nil"}, - {"empty request", &tx.BroadcastTxRequest{}, true, "invalid empty tx"}, - {"no mode", &tx.BroadcastTxRequest{TxBytes: txBytes}, true, "supported types: sync, async"}, - {"valid request", &tx.BroadcastTxRequest{ - Mode: tx.BroadcastMode_BROADCAST_MODE_SYNC, - TxBytes: txBytes, - }, false, ""}, - } - - for _, tc := range testCases { - s.Run(tc.name, func() { - // Broadcast the tx via gRPC via the validator's clientCtx (which goes - // through Tendermint). - grpcRes, err := s.queryClient.BroadcastTx(context.Background(), tc.req) - if tc.expErr { - s.Require().Error(err) - s.Require().Contains(err.Error(), tc.expErrMsg) - } else { - s.Require().NoError(err) - s.Require().Equal(uint32(0), grpcRes.TxResponse.Code) - } - }) - } -} - -func (s *E2ETestSuite) TestBroadcastTx_GRPCGateway() { - val := s.network.GetValidators()[0] - txBuilder := s.mkTxBuilder() - txBytes, err := val.GetClientCtx().TxConfig.TxEncoder()(txBuilder.GetTx()) - s.Require().NoError(err) - - testCases := []struct { - name string - req *tx.BroadcastTxRequest - expErr bool - expErrMsg string - }{ - {"empty request", &tx.BroadcastTxRequest{}, true, "invalid empty tx"}, - {"no mode", &tx.BroadcastTxRequest{TxBytes: txBytes}, true, "supported types: sync, async"}, - {"valid request", &tx.BroadcastTxRequest{ - Mode: tx.BroadcastMode_BROADCAST_MODE_SYNC, - TxBytes: txBytes, - }, false, ""}, - } - - for _, tc := range testCases { - s.Run(tc.name, func() { - req, err := val.GetClientCtx().Codec.MarshalJSON(tc.req) - s.Require().NoError(err) - res, err := testutil.PostRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/txs", val.GetAPIAddress()), "application/json", req) - s.Require().NoError(err) - if tc.expErr { - s.Require().Contains(string(res), tc.expErrMsg) - } else { - var result tx.BroadcastTxResponse - err = val.GetClientCtx().Codec.UnmarshalJSON(res, &result) - s.Require().NoError(err) - s.Require().Equal(uint32(0), result.TxResponse.Code, "rawlog", result.TxResponse.RawLog) - } - }) - } -} - -func (s *E2ETestSuite) TestSimMultiSigTx() { - val1 := s.network.GetValidators()[0] - clientCtx := val1.GetClientCtx() - - kr := clientCtx.Keyring - - account1, _, err := kr.NewMnemonic("newAccount1", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) - s.Require().NoError(err) - - account2, _, err := kr.NewMnemonic("newAccount2", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) - s.Require().NoError(err) - - pub1, err := account1.GetPubKey() - s.Require().NoError(err) - - pub2, err := account2.GetPubKey() - s.Require().NoError(err) - - multi := kmultisig.NewLegacyAminoPubKey(2, []cryptotypes.PubKey{pub1, pub2}) - _, err = kr.SaveMultisig("multi", multi) - s.Require().NoError(err) - - s.Require().NoError(s.network.WaitForNextBlock()) - - multisigRecord, err := clientCtx.Keyring.Key("multi") - s.Require().NoError(err) - - height, err := s.network.LatestHeight() - s.Require().NoError(err) - _, err = s.network.WaitForHeight(height + 1) - s.Require().NoError(err) - - addr, err := multisigRecord.GetAddress() - s.Require().NoError(err) - - // Send coins from validator to multisig. - coin := sdk.NewInt64Coin(s.cfg.BondDenom, 15) - msgSend := &banktypes.MsgSend{ - FromAddress: val1.GetAddress().String(), - ToAddress: addr.String(), - Amount: sdk.NewCoins(coin), - } - - _, err = cli.SubmitTestTx( - clientCtx, - msgSend, - val1.GetAddress(), - cli.TestTxConfig{}, - ) - - s.Require().NoError(err) - - height, err = s.network.LatestHeight() - s.Require().NoError(err) - _, err = s.network.WaitForHeight(height + 1) - s.Require().NoError(err) - - msgSend1 := &banktypes.MsgSend{ - FromAddress: addr.String(), - ToAddress: val1.GetAddress().String(), - Amount: sdk.NewCoins( - sdk.NewInt64Coin(s.cfg.BondDenom, 5), - ), - } - // Generate multisig transaction. - multiGeneratedTx, err := cli.SubmitTestTx( - clientCtx, - msgSend1, - val1.GetAddress(), - cli.TestTxConfig{ - GenOnly: true, - Memo: "foobar", - }, - ) - - s.Require().NoError(err) - - // Save tx to file - multiGeneratedTxFile := testutil.WriteToNewTempFile(s.T(), multiGeneratedTx.String()) - - // Sign with account1 - addr1, err := account1.GetAddress() - s.Require().NoError(err) - clientCtx.HomeDir = strings.Replace(clientCtx.HomeDir, "simd", "simcli", 1) - account1Signature, err := authtest.TxSignExec(clientCtx, addr1, multiGeneratedTxFile.Name(), "--multisig", addr.String()) - s.Require().NoError(err) - sign1File := testutil.WriteToNewTempFile(s.T(), account1Signature.String()) - - // Sign with account2 - addr2, err := account2.GetAddress() - s.Require().NoError(err) - account2Signature, err := authtest.TxSignExec(clientCtx, addr2, multiGeneratedTxFile.Name(), "--multisig", addr.String()) - s.Require().NoError(err) - sign2File := testutil.WriteToNewTempFile(s.T(), account2Signature.String()) - - // multisign tx - clientCtx.Offline = false - multiSigWith2Signatures, err := authtest.TxMultiSignExec(clientCtx, multisigRecord.Name, multiGeneratedTxFile.Name(), sign1File.Name(), sign2File.Name()) - s.Require().NoError(err) - - // convert from protoJSON to protoBinary for sim - sdkTx, err := clientCtx.TxConfig.TxJSONDecoder()(multiSigWith2Signatures.Bytes()) - s.Require().NoError(err) - txBytes, err := clientCtx.TxConfig.TxEncoder()(sdkTx) - s.Require().NoError(err) - - // simulate tx - sim := &tx.SimulateRequest{TxBytes: txBytes} - res, err := s.queryClient.Simulate(context.Background(), sim) - s.Require().NoError(err) - - // make sure gas was used - s.Require().Greater(res.GasInfo.GasUsed, uint64(0)) -} - -func (s *E2ETestSuite) TestGetBlockWithTxs_GRPC() { - testCases := []struct { - name string - req *tx.GetBlockWithTxsRequest - expErr bool - expErrMsg string - expTxsLen int - }{ - {"nil request", nil, true, "request cannot be nil", 0}, - {"empty request", &tx.GetBlockWithTxsRequest{}, true, "height must not be less than 1 or greater than the current height", 0}, - {"bad height", &tx.GetBlockWithTxsRequest{Height: 99999999}, true, "height must not be less than 1 or greater than the current height", 0}, - {"bad pagination", &tx.GetBlockWithTxsRequest{Height: s.txHeight, Pagination: &query.PageRequest{Offset: 1000, Limit: 100}}, true, "out of range", 0}, - {"good request", &tx.GetBlockWithTxsRequest{Height: s.txHeight}, false, "", 1}, - {"with pagination request", &tx.GetBlockWithTxsRequest{Height: s.txHeight, Pagination: &query.PageRequest{Offset: 0, Limit: 1}}, false, "", 1}, - {"page all request", &tx.GetBlockWithTxsRequest{Height: s.txHeight, Pagination: &query.PageRequest{Offset: 0, Limit: 100}}, false, "", 1}, - {"block with 0 tx", &tx.GetBlockWithTxsRequest{Height: s.txHeight - 1, Pagination: &query.PageRequest{Offset: 0, Limit: 100}}, false, "", 0}, - } - for _, tc := range testCases { - s.Run(tc.name, func() { - // Query the tx via gRPC. - grpcRes, err := s.queryClient.GetBlockWithTxs(context.Background(), tc.req) - if tc.expErr { - s.Require().Error(err) - s.Require().Contains(err.Error(), tc.expErrMsg) - } else { - s.Require().NoError(err) - if tc.expTxsLen > 0 { - s.Require().Equal("foobar", grpcRes.Txs[0].Body.Memo) - } - s.Require().Equal(grpcRes.Block.Header.Height, tc.req.Height) - if tc.req.Pagination != nil { - s.Require().LessOrEqual(len(grpcRes.Txs), int(tc.req.Pagination.Limit)) - } - } - }) - } -} - -func (s *E2ETestSuite) TestGetBlockWithTxs_GRPCGateway() { - val := s.network.GetValidators()[0] - testCases := []struct { - name string - url string - expErr bool - expErrMsg string - }{ - { - "empty params", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/block/0", val.GetAPIAddress()), - true, "height must not be less than 1 or greater than the current height", - }, - { - "bad height", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/block/%d", val.GetAPIAddress(), 9999999), - true, "height must not be less than 1 or greater than the current height", - }, - { - "good request", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/block/%d", val.GetAPIAddress(), s.txHeight), - false, "", - }, - } - for _, tc := range testCases { - s.Run(tc.name, func() { - res, err := testutil.GetRequest(tc.url) - s.Require().NoError(err) - if tc.expErr { - s.Require().Contains(string(res), tc.expErrMsg) - } else { - var result tx.GetBlockWithTxsResponse - err = val.GetClientCtx().Codec.UnmarshalJSON(res, &result) - s.Require().NoError(err) - s.Require().Equal("foobar", result.Txs[0].Body.Memo) - s.Require().Equal(result.Block.Header.Height, s.txHeight) - } - }) - } -} - -func (s *E2ETestSuite) TestTxEncode_GRPC() { - val := s.network.GetValidators()[0] - txBuilder := s.mkTxBuilder() - protoTx, err := txBuilder.GetTx().(interface{ AsTx() (*tx.Tx, error) }).AsTx() - s.Require().NoError(err) - - testCases := []struct { - name string - req *tx.TxEncodeRequest - expErr bool - expErrMsg string - }{ - {"nil request", nil, true, "request cannot be nil"}, - {"empty request", &tx.TxEncodeRequest{}, true, "invalid empty tx"}, - {"valid tx request", &tx.TxEncodeRequest{Tx: protoTx}, false, ""}, - } - - for _, tc := range testCases { - s.Run(tc.name, func() { - res, err := s.queryClient.TxEncode(context.Background(), tc.req) - if tc.expErr { - s.Require().Error(err) - s.Require().Contains(err.Error(), tc.expErrMsg) - s.Require().Empty(res) - } else { - s.Require().NoError(err) - s.Require().NotEmpty(res.GetTxBytes()) - - tx, err := val.GetClientCtx().TxConfig.TxDecoder()(res.TxBytes) - s.Require().NoError(err) - s.Require().Equal(protoTx.GetMsgs(), tx.GetMsgs()) - } - }) - } -} - -func (s *E2ETestSuite) TestTxEncode_GRPCGateway() { - val := s.network.GetValidators()[0] - txBuilder := s.mkTxBuilder() - protoTx, err := txBuilder.GetTx().(interface{ AsTx() (*tx.Tx, error) }).AsTx() - s.Require().NoError(err) - - testCases := []struct { - name string - req *tx.TxEncodeRequest - expErr bool - expErrMsg string - }{ - {"empty request", &tx.TxEncodeRequest{}, true, "invalid empty tx"}, - {"valid tx request", &tx.TxEncodeRequest{Tx: protoTx}, false, ""}, - } - - for _, tc := range testCases { - s.Run(tc.name, func() { - req, err := val.GetClientCtx().Codec.MarshalJSON(tc.req) - s.Require().NoError(err) - - res, err := testutil.PostRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/encode", val.GetAPIAddress()), "application/json", req) - s.Require().NoError(err) - if tc.expErr { - s.Require().Contains(string(res), tc.expErrMsg) - } else { - var result tx.TxEncodeResponse - err := val.GetClientCtx().Codec.UnmarshalJSON(res, &result) - s.Require().NoError(err) - - tx, err := val.GetClientCtx().TxConfig.TxDecoder()(result.TxBytes) - s.Require().NoError(err) - s.Require().Equal(protoTx.GetMsgs(), tx.GetMsgs()) - } - }) - } -} - -func (s *E2ETestSuite) TestTxDecode_GRPC() { - val := s.network.GetValidators()[0] - txBuilder := s.mkTxBuilder() - - goodTx := txBuilder.GetTx() - encodedTx, err := val.GetClientCtx().TxConfig.TxEncoder()(goodTx) - s.Require().NoError(err) - - invalidTxBytes := append(encodedTx, byte(0o00)) - - testCases := []struct { - name string - req *tx.TxDecodeRequest - expErr bool - expErrMsg string - }{ - {"nil request", nil, true, "request cannot be nil"}, - {"empty request", &tx.TxDecodeRequest{}, true, "invalid empty tx bytes"}, - {"invalid tx bytes", &tx.TxDecodeRequest{TxBytes: invalidTxBytes}, true, "tx parse error"}, - {"valid request with tx bytes", &tx.TxDecodeRequest{TxBytes: encodedTx}, false, ""}, - } - - for _, tc := range testCases { - s.Run(tc.name, func() { - res, err := s.queryClient.TxDecode(context.Background(), tc.req) - if tc.expErr { - s.Require().Error(err) - s.Require().Contains(err.Error(), tc.expErrMsg) - s.Require().Empty(res) - } else { - s.Require().NoError(err) - s.Require().NotEmpty(res.GetTx()) - - txb := wrapTx(s.T(), s.cfg.TxConfig, res.Tx) - gotTx := txb.GetTx() - gotEncoded, err := val.GetClientCtx().TxConfig.TxEncoder()(gotTx) - s.Require().NoError(err) - s.Require().Equal(encodedTx, gotEncoded) - } - }) - } -} - -func wrapTx(t *testing.T, conf client.TxConfig, dTx *tx.Tx) client.TxBuilder { - t.Helper() - bodyBytes, err := dTx.Body.Marshal() - require.NoError(t, err) - authInfoBytes, err := dTx.AuthInfo.Marshal() - require.NoError(t, err) - rawTxBytes, err := (&tx.TxRaw{ - BodyBytes: bodyBytes, - AuthInfoBytes: authInfoBytes, - Signatures: dTx.Signatures, - }).Marshal() - require.NoError(t, err) - dec, err := conf.TxDecoder()(rawTxBytes) - require.NoError(t, err) - bld, err := conf.WrapTxBuilder(dec) - require.NoError(t, err) - return bld -} - -func (s *E2ETestSuite) TestTxDecode_GRPCGateway() { - val := s.network.GetValidators()[0] - txBuilder := s.mkTxBuilder() - - encodedTxBytes, err := val.GetClientCtx().TxConfig.TxEncoder()(txBuilder.GetTx()) - s.Require().NoError(err) - - invalidTxBytes := append(encodedTxBytes, byte(0o00)) - - testCases := []struct { - name string - req *tx.TxDecodeRequest - expErr bool - expErrMsg string - }{ - {"empty request", &tx.TxDecodeRequest{}, true, "invalid empty tx bytes"}, - {"invalid tx bytes", &tx.TxDecodeRequest{TxBytes: invalidTxBytes}, true, "tx parse error"}, - {"valid request with tx_bytes", &tx.TxDecodeRequest{TxBytes: encodedTxBytes}, false, ""}, - } - - for _, tc := range testCases { - s.Run(tc.name, func() { - req, err := val.GetClientCtx().Codec.MarshalJSON(tc.req) - s.Require().NoError(err) - - res, err := testutil.PostRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/decode", val.GetAPIAddress()), "application/json", req) - s.Require().NoError(err) - if tc.expErr { - s.Require().Contains(string(res), tc.expErrMsg) - } else { - var result tx.TxDecodeResponse - err := val.GetClientCtx().Codec.UnmarshalJSON(res, &result) - s.Require().NoError(err) - - txb := wrapTx(s.T(), s.cfg.TxConfig, result.Tx) - tx, err := val.GetClientCtx().TxConfig.TxEncoder()(txb.GetTx()) - s.Require().NoError(err) - s.T().Log(len(tx), len(encodedTxBytes)) - s.Require().Equal(encodedTxBytes, tx) - } - }) - } -} - func (s *E2ETestSuite) readTestAminoTxJSON() ([]byte, *legacytx.StdTx) { val := s.network.GetValidators()[0] txJSONBytes, err := os.ReadFile("testdata/tx_amino1.json") @@ -1106,40 +277,3 @@ func (s *E2ETestSuite) TestTxDecodeAmino_GRPCGateway() { func TestE2ETestSuite(t *testing.T) { suite.Run(t, new(E2ETestSuite)) } - -func (s *E2ETestSuite) mkTxBuilder() client.TxBuilder { - val := s.network.GetValidators()[0] - s.Require().NoError(s.network.WaitForNextBlock()) - - // prepare txBuilder with msg - txBuilder := val.GetClientCtx().TxConfig.NewTxBuilder() - feeAmount := sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)} - gasLimit := testdata.NewTestGasLimit() - s.Require().NoError( - txBuilder.SetMsgs(&banktypes.MsgSend{ - FromAddress: val.GetAddress().String(), - ToAddress: val.GetAddress().String(), - Amount: sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)}, - }), - ) - txBuilder.SetFeeAmount(feeAmount) - txBuilder.SetGasLimit(gasLimit) - txBuilder.SetMemo("foobar") - txBuilder.SetFeePayer(val.GetAddress()) - signers, err := txBuilder.GetTx().GetSigners() - s.Require().NoError(err) - s.Require().Equal([][]byte{val.GetAddress()}, signers) - - // setup txFactory - txFactory := clienttx.Factory{}. - WithChainID(val.GetClientCtx().ChainID). - WithKeybase(val.GetClientCtx().Keyring). - WithTxConfig(val.GetClientCtx().TxConfig). - WithSignMode(signing.SignMode_SIGN_MODE_DIRECT) - - // Sign Tx. - err = authclient.SignTx(txFactory, val.GetClientCtx(), val.GetMoniker(), txBuilder, false, true) - s.Require().NoError(err) - - return txBuilder -}