github.com/Finschia/finschia-sdk@v0.49.1/x/auth/tx/service_test.go (about)

     1  // build +norace
     2  
     3  package tx_test
     4  
     5  import (
     6  	"context"
     7  	"fmt"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/stretchr/testify/suite"
    13  
    14  	"github.com/Finschia/finschia-sdk/client"
    15  	"github.com/Finschia/finschia-sdk/client/flags"
    16  	clienttx "github.com/Finschia/finschia-sdk/client/tx"
    17  	"github.com/Finschia/finschia-sdk/crypto/hd"
    18  	"github.com/Finschia/finschia-sdk/crypto/keyring"
    19  	kmultisig "github.com/Finschia/finschia-sdk/crypto/keys/multisig"
    20  	cryptotypes "github.com/Finschia/finschia-sdk/crypto/types"
    21  	"github.com/Finschia/finschia-sdk/testutil"
    22  	"github.com/Finschia/finschia-sdk/testutil/network"
    23  	"github.com/Finschia/finschia-sdk/testutil/rest"
    24  	"github.com/Finschia/finschia-sdk/testutil/testdata"
    25  	sdk "github.com/Finschia/finschia-sdk/types"
    26  	sdkerrors "github.com/Finschia/finschia-sdk/types/errors"
    27  	"github.com/Finschia/finschia-sdk/types/query"
    28  	"github.com/Finschia/finschia-sdk/types/tx"
    29  	"github.com/Finschia/finschia-sdk/types/tx/signing"
    30  	authclient "github.com/Finschia/finschia-sdk/x/auth/client"
    31  	authtest "github.com/Finschia/finschia-sdk/x/auth/client/testutil"
    32  	tx2 "github.com/Finschia/finschia-sdk/x/auth/tx"
    33  	bankcli "github.com/Finschia/finschia-sdk/x/bank/client/testutil"
    34  	banktypes "github.com/Finschia/finschia-sdk/x/bank/types"
    35  )
    36  
    37  var bankMsgSendEventAction = fmt.Sprintf("message.action='%s'", sdk.MsgTypeURL(&banktypes.MsgSend{}))
    38  
    39  type IntegrationTestSuite struct {
    40  	suite.Suite
    41  
    42  	cfg     network.Config
    43  	network *network.Network
    44  
    45  	txHeight    int64
    46  	queryClient tx.ServiceClient
    47  	txRes       sdk.TxResponse
    48  }
    49  
    50  func (s *IntegrationTestSuite) SetupSuite() {
    51  	s.T().Log("setting up integration test suite")
    52  
    53  	cfg := network.DefaultConfig()
    54  	cfg.NumValidators = 1
    55  
    56  	s.cfg = cfg
    57  	s.network = network.New(s.T(), cfg)
    58  	s.Require().NotNil(s.network)
    59  
    60  	val := s.network.Validators[0]
    61  
    62  	_, err := s.network.WaitForHeight(1)
    63  	s.Require().NoError(err)
    64  
    65  	s.queryClient = tx.NewServiceClient(val.ClientCtx)
    66  
    67  	// Create a new MsgSend tx from val to itself.
    68  	out, err := bankcli.MsgSendExec(
    69  		val.ClientCtx,
    70  		val.Address,
    71  		val.Address,
    72  		sdk.NewCoins(
    73  			sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10)),
    74  		),
    75  		fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
    76  		fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
    77  		fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
    78  		fmt.Sprintf("--gas=%d", flags.DefaultGasLimit),
    79  		fmt.Sprintf("--%s=foobar", flags.FlagNote),
    80  	)
    81  	s.Require().NoError(err)
    82  	s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &s.txRes))
    83  	s.Require().Equal(uint32(0), s.txRes.Code)
    84  
    85  	out, err = bankcli.MsgSendExec(
    86  		val.ClientCtx,
    87  		val.Address,
    88  		val.Address,
    89  		sdk.NewCoins(
    90  			sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(1)),
    91  		),
    92  		fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
    93  		fmt.Sprintf("--%s=2", flags.FlagSequence),
    94  		fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
    95  		fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
    96  		fmt.Sprintf("--gas=%d", flags.DefaultGasLimit),
    97  		fmt.Sprintf("--%s=foobar", flags.FlagNote),
    98  	)
    99  	s.Require().NoError(err)
   100  	var tr sdk.TxResponse
   101  	s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &tr))
   102  	s.Require().Equal(uint32(0), tr.Code)
   103  
   104  	s.Require().NoError(s.network.WaitForNextBlock())
   105  	height, err := s.network.LatestHeight()
   106  	s.Require().NoError(err)
   107  	s.txHeight = height
   108  }
   109  
   110  func (s *IntegrationTestSuite) TearDownSuite() {
   111  	s.T().Log("tearing down integration test suite")
   112  	s.network.Cleanup()
   113  }
   114  
   115  func (s *IntegrationTestSuite) TestSimulateTx_GRPC() {
   116  	val := s.network.Validators[0]
   117  	txBuilder := s.mkTxBuilder()
   118  	// Convert the txBuilder to a tx.Tx.
   119  	protoTx, err := txBuilderToProtoTx(txBuilder)
   120  	s.Require().NoError(err)
   121  	// Encode the txBuilder to txBytes.
   122  	txBytes, err := val.ClientCtx.TxConfig.TxEncoder()(txBuilder.GetTx())
   123  	s.Require().NoError(err)
   124  
   125  	testCases := []struct {
   126  		name      string
   127  		req       *tx.SimulateRequest
   128  		expErr    bool
   129  		expErrMsg string
   130  	}{
   131  		{"nil request", nil, true, "request cannot be nil"},
   132  		{"empty request", &tx.SimulateRequest{}, true, "empty txBytes is not allowed"},
   133  		{"valid request with proto tx (deprecated)", &tx.SimulateRequest{Tx: protoTx}, false, ""},
   134  		{"valid request with tx_bytes", &tx.SimulateRequest{TxBytes: txBytes}, false, ""},
   135  	}
   136  
   137  	for _, tc := range testCases {
   138  		s.Run(tc.name, func() {
   139  			// Broadcast the tx via gRPC via the validator's clientCtx (which goes
   140  			// through Tendermint).
   141  			res, err := s.queryClient.Simulate(context.Background(), tc.req)
   142  			if tc.expErr {
   143  				s.Require().Error(err)
   144  				s.Require().Contains(err.Error(), tc.expErrMsg)
   145  			} else {
   146  				s.Require().NoError(err)
   147  				// Check the result and gas used are correct.
   148  				//
   149  				// NOTE(0Tech): This comment should be updated after applying #11985.
   150  				// Events from the antehandlers would not be included in the simulation. The 4 events are:
   151  				// - Sending Amount to recipient: coin_spent, coin_received and transfer
   152  				// - Msg events: message.module=bank, message.action=/cosmos.bank.v1beta1.MsgSend and message.sender=<val1> (in one message)
   153  				s.Require().Equal(4, len(res.GetResult().GetEvents()))
   154  				s.Require().True(res.GetGasInfo().GetGasUsed() > 0) // Gas used sometimes change, just check it's not empty.
   155  			}
   156  		})
   157  	}
   158  }
   159  
   160  func (s *IntegrationTestSuite) TestSimulateTx_GRPCGateway() {
   161  	val := s.network.Validators[0]
   162  	txBuilder := s.mkTxBuilder()
   163  	// Convert the txBuilder to a tx.Tx.
   164  	protoTx, err := txBuilderToProtoTx(txBuilder)
   165  	s.Require().NoError(err)
   166  	// Encode the txBuilder to txBytes.
   167  	txBytes, err := val.ClientCtx.TxConfig.TxEncoder()(txBuilder.GetTx())
   168  	s.Require().NoError(err)
   169  
   170  	testCases := []struct {
   171  		name      string
   172  		req       *tx.SimulateRequest
   173  		expErr    bool
   174  		expErrMsg string
   175  	}{
   176  		{"empty request", &tx.SimulateRequest{}, true, "empty txBytes is not allowed"},
   177  		{"valid request with proto tx (deprecated)", &tx.SimulateRequest{Tx: protoTx}, false, ""},
   178  		{"valid request with tx_bytes", &tx.SimulateRequest{TxBytes: txBytes}, false, ""},
   179  	}
   180  
   181  	for _, tc := range testCases {
   182  		s.Run(tc.name, func() {
   183  			req, err := val.ClientCtx.Codec.MarshalJSON(tc.req)
   184  			s.Require().NoError(err)
   185  			res, err := rest.PostRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/simulate", val.APIAddress), "application/json", req)
   186  			s.Require().NoError(err)
   187  			if tc.expErr {
   188  				s.Require().Contains(string(res), tc.expErrMsg)
   189  			} else {
   190  				var result tx.SimulateResponse
   191  				err = val.ClientCtx.Codec.UnmarshalJSON(res, &result)
   192  				s.Require().NoError(err)
   193  				// Check the result and gas used are correct.
   194  				s.Require().Equal(4, len(result.GetResult().GetEvents())) // See TestSimulateTx_GRPC for the 4 events.
   195  				s.Require().True(result.GetGasInfo().GetGasUsed() > 0)    // Gas used sometimes change, just check it's not empty.
   196  			}
   197  		})
   198  	}
   199  }
   200  
   201  func (s *IntegrationTestSuite) TestGetTxEvents_GRPC() {
   202  	testCases := []struct {
   203  		name      string
   204  		req       *tx.GetTxsEventRequest
   205  		expErr    bool
   206  		expErrMsg string
   207  	}{
   208  		{
   209  			"nil request",
   210  			nil,
   211  			true, "request cannot be nil",
   212  		},
   213  		{
   214  			"empty request",
   215  			&tx.GetTxsEventRequest{},
   216  			true, "must declare at least one event to search",
   217  		},
   218  		{
   219  			"request with dummy event",
   220  			&tx.GetTxsEventRequest{Events: []string{"foobar"}},
   221  			true, "event foobar should be of the format: {eventType}.{eventAttribute}={value}",
   222  		},
   223  		{
   224  			"request with order-by",
   225  			&tx.GetTxsEventRequest{
   226  				Events:  []string{bankMsgSendEventAction},
   227  				OrderBy: tx.OrderBy_ORDER_BY_ASC,
   228  			},
   229  			false, "",
   230  		},
   231  		{
   232  			"without pagination",
   233  			&tx.GetTxsEventRequest{
   234  				Events: []string{bankMsgSendEventAction},
   235  			},
   236  			false, "",
   237  		},
   238  		{
   239  			"with pagination",
   240  			&tx.GetTxsEventRequest{
   241  				Events: []string{bankMsgSendEventAction},
   242  				Pagination: &query.PageRequest{
   243  					CountTotal: false,
   244  					Offset:     0,
   245  					Limit:      1,
   246  				},
   247  			},
   248  			false, "",
   249  		},
   250  		{
   251  			"with multi events",
   252  			&tx.GetTxsEventRequest{
   253  				Events: []string{bankMsgSendEventAction, "message.module='bank'"},
   254  			},
   255  			false, "",
   256  		},
   257  	}
   258  	for _, tc := range testCases {
   259  		s.Run(tc.name, func() {
   260  			// Query the tx via gRPC.
   261  			grpcRes, err := s.queryClient.GetTxsEvent(context.Background(), tc.req)
   262  			if tc.expErr {
   263  				s.Require().Error(err)
   264  				s.Require().Contains(err.Error(), tc.expErrMsg)
   265  			} else {
   266  				s.Require().NoError(err)
   267  				s.Require().GreaterOrEqual(len(grpcRes.Txs), 1)
   268  				s.Require().Equal("foobar", grpcRes.Txs[0].Body.Memo)
   269  
   270  				// Make sure fields are populated.
   271  				// ref: https://github.com/cosmos/cosmos-sdk/issues/8680
   272  				// ref: https://github.com/cosmos/cosmos-sdk/issues/8681
   273  				s.Require().NotEmpty(grpcRes.TxResponses[0].Timestamp)
   274  				s.Require().NotEmpty(grpcRes.TxResponses[0].RawLog)
   275  			}
   276  		})
   277  	}
   278  }
   279  
   280  func (s *IntegrationTestSuite) TestGetTxEvents_GRPCGateway() {
   281  	val := s.network.Validators[0]
   282  	testCases := []struct {
   283  		name      string
   284  		url       string
   285  		expErr    bool
   286  		expErrMsg string
   287  	}{
   288  		{
   289  			"empty params",
   290  			fmt.Sprintf("%s/cosmos/tx/v1beta1/txs", val.APIAddress),
   291  			true,
   292  			"must declare at least one event to search",
   293  		},
   294  		{
   295  			"without pagination",
   296  			fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s", val.APIAddress, bankMsgSendEventAction),
   297  			false,
   298  			"",
   299  		},
   300  		{
   301  			"with pagination",
   302  			fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&pagination.offset=%d&pagination.limit=%d", val.APIAddress, bankMsgSendEventAction, 0, 10),
   303  			false,
   304  			"",
   305  		},
   306  		{
   307  			"valid request: order by asc",
   308  			fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&events=%s&order_by=ORDER_BY_ASC", val.APIAddress, bankMsgSendEventAction, "message.module='bank'"),
   309  			false,
   310  			"",
   311  		},
   312  		{
   313  			"valid request: order by desc",
   314  			fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&events=%s&order_by=ORDER_BY_DESC", val.APIAddress, bankMsgSendEventAction, "message.module='bank'"),
   315  			false,
   316  			"",
   317  		},
   318  		{
   319  			"invalid request: invalid order by",
   320  			fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&events=%s&order_by=invalid_order", val.APIAddress, bankMsgSendEventAction, "message.module='bank'"),
   321  			true,
   322  			"is not a valid tx.OrderBy",
   323  		},
   324  		{
   325  			"expect pass with multiple-events",
   326  			fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&events=%s", val.APIAddress, bankMsgSendEventAction, "message.module='bank'"),
   327  			false,
   328  			"",
   329  		},
   330  		{
   331  			"expect pass with escape event",
   332  			fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s", val.APIAddress, "message.action%3D'/cosmos.bank.v1beta1.MsgSend'"),
   333  			false,
   334  			"",
   335  		},
   336  	}
   337  	for _, tc := range testCases {
   338  		s.Run(tc.name, func() {
   339  			res, err := rest.GetRequest(tc.url)
   340  			s.Require().NoError(err)
   341  			if tc.expErr {
   342  				s.Require().Contains(string(res), tc.expErrMsg)
   343  			} else {
   344  				var result tx.GetTxsEventResponse
   345  				err = val.ClientCtx.Codec.UnmarshalJSON(res, &result)
   346  				s.Require().NoError(err)
   347  				s.Require().GreaterOrEqual(len(result.Txs), 1)
   348  				s.Require().Equal("foobar", result.Txs[0].Body.Memo)
   349  				s.Require().NotZero(result.TxResponses[0].Height)
   350  			}
   351  		})
   352  	}
   353  }
   354  
   355  func (s *IntegrationTestSuite) TestGetTx_GRPC() {
   356  	testCases := []struct {
   357  		name      string
   358  		req       *tx.GetTxRequest
   359  		expErr    bool
   360  		expErrMsg string
   361  	}{
   362  		{"nil request", nil, true, "request cannot be nil"},
   363  		{"empty request", &tx.GetTxRequest{}, true, "tx hash cannot be empty"},
   364  		{"request with dummy hash of wrong length", &tx.GetTxRequest{Hash: "deadbeef"}, true, "The length of tx hash must be 64"},
   365  		{"request with dummy hash of correct length but invalid", &tx.GetTxRequest{Hash: "2AAC6096A87A9B9ABF604316313950D518DFDD86F2E597DD84A5808582DD0C02"}, true, "tx not found: 2AAC6096A87A9B9ABF604316313950D518DFDD86F2E597DD84A5808582DD0C02"},
   366  		{"good request", &tx.GetTxRequest{Hash: s.txRes.TxHash}, false, ""},
   367  	}
   368  	for _, tc := range testCases {
   369  		s.Run(tc.name, func() {
   370  			// Query the tx via gRPC.
   371  			grpcRes, err := s.queryClient.GetTx(context.Background(), tc.req)
   372  			if tc.expErr {
   373  				s.Require().Error(err)
   374  				s.Require().Contains(err.Error(), tc.expErrMsg)
   375  			} else {
   376  				s.Require().NoError(err)
   377  				s.Require().Equal("foobar", grpcRes.Tx.Body.Memo)
   378  			}
   379  		})
   380  	}
   381  }
   382  
   383  func (s *IntegrationTestSuite) TestGetTx_GRPCGateway() {
   384  	val := s.network.Validators[0]
   385  	testCases := []struct {
   386  		name      string
   387  		url       string
   388  		expErr    bool
   389  		expErrMsg string
   390  	}{
   391  		{
   392  			"empty params",
   393  			fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/", val.APIAddress),
   394  			true, "tx hash cannot be empty",
   395  		},
   396  		{
   397  			"dummy hash of wrong length",
   398  			fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/%s", val.APIAddress, "deadbeef"),
   399  			true, "The length of tx hash must be 64",
   400  		},
   401  		{
   402  			"dummy hash of correct length but invalid",
   403  			fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/%s", val.APIAddress, "2AAC6096A87A9B9ABF604316313950D518DFDD86F2E597DD84A5808582DD0C02"),
   404  			true, "tx not found: 2AAC6096A87A9B9ABF604316313950D518DFDD86F2E597DD84A5808582DD0C02",
   405  		},
   406  		{
   407  			"good hash",
   408  			fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/%s", val.APIAddress, s.txRes.TxHash),
   409  			false, "",
   410  		},
   411  	}
   412  	for _, tc := range testCases {
   413  		s.Run(tc.name, func() {
   414  			res, err := rest.GetRequest(tc.url)
   415  			s.Require().NoError(err)
   416  			if tc.expErr {
   417  				s.Require().Contains(string(res), tc.expErrMsg)
   418  			} else {
   419  				var result tx.GetTxResponse
   420  				err = val.ClientCtx.Codec.UnmarshalJSON(res, &result)
   421  				s.Require().NoError(err)
   422  				s.Require().Equal("foobar", result.Tx.Body.Memo)
   423  				s.Require().NotZero(result.TxResponse.Height)
   424  
   425  				// Make sure fields are populated.
   426  				// ref: https://github.com/cosmos/cosmos-sdk/issues/8680
   427  				// ref: https://github.com/cosmos/cosmos-sdk/issues/8681
   428  				s.Require().NotEmpty(result.TxResponse.Timestamp)
   429  				s.Require().NotEmpty(result.TxResponse.RawLog)
   430  			}
   431  		})
   432  	}
   433  }
   434  
   435  func (s *IntegrationTestSuite) TestBroadcastTx_GRPC() {
   436  	val := s.network.Validators[0]
   437  	txBuilder := s.mkTxBuilder()
   438  	txBytes, err := val.ClientCtx.TxConfig.TxEncoder()(txBuilder.GetTx())
   439  	s.Require().NoError(err)
   440  
   441  	testCases := []struct {
   442  		name      string
   443  		req       *tx.BroadcastTxRequest
   444  		expErr    bool
   445  		expErrMsg string
   446  	}{
   447  		{"nil request", nil, true, "request cannot be nil"},
   448  		{"empty request", &tx.BroadcastTxRequest{}, true, "invalid empty tx"},
   449  		{"no mode", &tx.BroadcastTxRequest{
   450  			TxBytes: txBytes,
   451  		}, true, "supported types: sync, async, block"},
   452  		{"valid request", &tx.BroadcastTxRequest{
   453  			Mode:    tx.BroadcastMode_BROADCAST_MODE_SYNC,
   454  			TxBytes: txBytes,
   455  		}, false, ""},
   456  	}
   457  
   458  	for _, tc := range testCases {
   459  		s.Run(tc.name, func() {
   460  			// Broadcast the tx via gRPC via the validator's clientCtx (which goes
   461  			// through Tendermint).
   462  			grpcRes, err := s.queryClient.BroadcastTx(context.Background(), tc.req)
   463  			if tc.expErr {
   464  				s.Require().Error(err)
   465  				s.Require().Contains(err.Error(), tc.expErrMsg)
   466  			} else {
   467  				s.Require().NoError(err)
   468  				s.Require().Equal(uint32(0), grpcRes.TxResponse.Code)
   469  			}
   470  		})
   471  	}
   472  	time.Sleep(1 * time.Second) // wait for block confirm time before executing next test
   473  }
   474  
   475  func (s *IntegrationTestSuite) TestBroadcastTx_GRPCGateway() {
   476  	val := s.network.Validators[0]
   477  	txBuilder := s.mkTxBuilder()
   478  	txBytes, err := val.ClientCtx.TxConfig.TxEncoder()(txBuilder.GetTx())
   479  	s.Require().NoError(err)
   480  
   481  	testCases := []struct {
   482  		name      string
   483  		req       *tx.BroadcastTxRequest
   484  		expErr    bool
   485  		expErrMsg string
   486  	}{
   487  		{"empty request", &tx.BroadcastTxRequest{}, true, "invalid empty tx"},
   488  		{"no mode", &tx.BroadcastTxRequest{TxBytes: txBytes}, true, "supported types: sync, async, block"},
   489  		{"valid request", &tx.BroadcastTxRequest{
   490  			Mode:    tx.BroadcastMode_BROADCAST_MODE_SYNC,
   491  			TxBytes: txBytes,
   492  		}, false, ""},
   493  	}
   494  
   495  	for _, tc := range testCases {
   496  		s.Run(tc.name, func() {
   497  			req, err := val.ClientCtx.Codec.MarshalJSON(tc.req)
   498  			s.Require().NoError(err)
   499  			res, err := rest.PostRequest(fmt.Sprintf("%s/cosmos/tx/v1beta1/txs", val.APIAddress), "application/json", req)
   500  			s.Require().NoError(err)
   501  			if tc.expErr {
   502  				s.Require().Contains(string(res), tc.expErrMsg)
   503  			} else {
   504  				var result tx.BroadcastTxResponse
   505  				err = val.ClientCtx.Codec.UnmarshalJSON(res, &result)
   506  				s.Require().NoError(err)
   507  				s.Require().Equal(uint32(0), result.TxResponse.Code, "rawlog", result.TxResponse.RawLog)
   508  			}
   509  		})
   510  	}
   511  	time.Sleep(1 * time.Second) // wait for block confirm time before executing next test
   512  }
   513  
   514  func (s *IntegrationTestSuite) TestSimMultiSigTx() {
   515  	val1 := *s.network.Validators[0]
   516  
   517  	kr := val1.ClientCtx.Keyring
   518  
   519  	account1, _, err := kr.NewMnemonic("newAccount1", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1)
   520  	s.Require().NoError(err)
   521  
   522  	account2, _, err := kr.NewMnemonic("newAccount2", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1)
   523  	s.Require().NoError(err)
   524  
   525  	multi := kmultisig.NewLegacyAminoPubKey(2, []cryptotypes.PubKey{account1.GetPubKey(), account2.GetPubKey()})
   526  	_, err = kr.SaveMultisig("multi", multi)
   527  	s.Require().NoError(err)
   528  
   529  	_, err = s.network.WaitForHeight(1)
   530  	s.Require().NoError(err)
   531  
   532  	multisigInfo, err := val1.ClientCtx.Keyring.Key("multi")
   533  	s.Require().NoError(err)
   534  
   535  	height, err := s.network.LatestHeight()
   536  	s.Require().NoError(err)
   537  	_, err = s.network.WaitForHeight(height + 1)
   538  	s.Require().NoError(err)
   539  
   540  	// Send coins from validator to multisig.
   541  	coins := sdk.NewInt64Coin(s.cfg.BondDenom, 15)
   542  	_, err = bankcli.MsgSendExec(
   543  		val1.ClientCtx,
   544  		val1.Address,
   545  		multisigInfo.GetAddress(),
   546  		sdk.NewCoins(coins),
   547  		fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
   548  		fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
   549  		fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
   550  		fmt.Sprintf("--gas=%d", flags.DefaultGasLimit),
   551  	)
   552  	s.Require().NoError(err)
   553  
   554  	height, err = s.network.LatestHeight()
   555  	s.Require().NoError(err)
   556  	_, err = s.network.WaitForHeight(height + 1)
   557  	s.Require().NoError(err)
   558  
   559  	// Generate multisig transaction.
   560  	multiGeneratedTx, err := bankcli.MsgSendExec(
   561  		val1.ClientCtx,
   562  		multisigInfo.GetAddress(),
   563  		val1.Address,
   564  		sdk.NewCoins(
   565  			sdk.NewInt64Coin(s.cfg.BondDenom, 5),
   566  		),
   567  		fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
   568  		fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
   569  		fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
   570  		fmt.Sprintf("--%s=true", flags.FlagGenerateOnly),
   571  		fmt.Sprintf("--%s=foobar", flags.FlagNote),
   572  	)
   573  	s.Require().NoError(err)
   574  
   575  	// Save tx to file
   576  	multiGeneratedTxFile := testutil.WriteToNewTempFile(s.T(), multiGeneratedTx.String())
   577  
   578  	// Sign with account1
   579  	val1.ClientCtx.HomeDir = strings.Replace(val1.ClientCtx.HomeDir, "simd", "simcli", 1)
   580  	account1Signature, err := authtest.TxSignExec(val1.ClientCtx, account1.GetAddress(), multiGeneratedTxFile.Name(), "--multisig", multisigInfo.GetAddress().String())
   581  	s.Require().NoError(err)
   582  	sign1File := testutil.WriteToNewTempFile(s.T(), account1Signature.String())
   583  
   584  	// Sign with account2
   585  	account2Signature, err := authtest.TxSignExec(val1.ClientCtx, account2.GetAddress(), multiGeneratedTxFile.Name(), "--multisig", multisigInfo.GetAddress().String())
   586  	s.Require().NoError(err)
   587  	sign2File := testutil.WriteToNewTempFile(s.T(), account2Signature.String())
   588  
   589  	// multisign tx
   590  	val1.ClientCtx.Offline = false
   591  	multiSigWith2Signatures, err := authtest.TxMultiSignExec(val1.ClientCtx, multisigInfo.GetName(), multiGeneratedTxFile.Name(), sign1File.Name(), sign2File.Name())
   592  	s.Require().NoError(err)
   593  
   594  	// convert from protoJSON to protoBinary for sim
   595  	sdkTx, err := val1.ClientCtx.TxConfig.TxJSONDecoder()(multiSigWith2Signatures.Bytes())
   596  	s.Require().NoError(err)
   597  	txBytes, err := val1.ClientCtx.TxConfig.TxEncoder()(sdkTx)
   598  	s.Require().NoError(err)
   599  
   600  	// simulate tx
   601  	sim := &tx.SimulateRequest{TxBytes: txBytes}
   602  	res, err := s.queryClient.Simulate(context.Background(), sim)
   603  	s.Require().NoError(err)
   604  
   605  	// make sure gas was used
   606  	s.Require().Greater(res.GasInfo.GasUsed, uint64(0))
   607  }
   608  
   609  func (s *IntegrationTestSuite) TestGetBlockWithTxs_GRPC() {
   610  	testCases := []struct {
   611  		name      string
   612  		req       *tx.GetBlockWithTxsRequest
   613  		expErr    bool
   614  		expErrMsg string
   615  		expTxsLen int
   616  	}{
   617  		{"nil request", nil, true, "request cannot be nil", 0},
   618  		{"empty request", &tx.GetBlockWithTxsRequest{}, true, "height must not be less than 1 or greater than the current height", 0},
   619  		{"bad height", &tx.GetBlockWithTxsRequest{Height: 99999999}, true, "height must not be less than 1 or greater than the current height", 0},
   620  		{"bad pagination", &tx.GetBlockWithTxsRequest{Height: s.txHeight, Pagination: &query.PageRequest{Offset: 1000, Limit: 100}}, true, "out of range", 0},
   621  		{"good request", &tx.GetBlockWithTxsRequest{Height: s.txHeight}, false, "", 1},
   622  		{"with pagination request", &tx.GetBlockWithTxsRequest{Height: s.txHeight, Pagination: &query.PageRequest{Offset: 0, Limit: 1}}, false, "", 1},
   623  		{"page all request", &tx.GetBlockWithTxsRequest{Height: s.txHeight, Pagination: &query.PageRequest{Offset: 0, Limit: 100}}, false, "", 1},
   624  		{"block with 0 tx", &tx.GetBlockWithTxsRequest{Height: s.txHeight - 1, Pagination: &query.PageRequest{Offset: 0, Limit: 100}}, false, "", 0},
   625  	}
   626  	for _, tc := range testCases {
   627  		s.Run(tc.name, func() {
   628  			// Query the tx via gRPC.
   629  			grpcRes, err := s.queryClient.GetBlockWithTxs(context.Background(), tc.req)
   630  			if tc.expErr {
   631  				s.Require().Error(err)
   632  				s.Require().Contains(err.Error(), tc.expErrMsg)
   633  			} else {
   634  				s.Require().NoError(err)
   635  				if tc.expTxsLen > 0 {
   636  					s.Require().Equal("foobar", grpcRes.Txs[0].Body.Memo)
   637  				}
   638  				s.Require().Equal(grpcRes.Block.Header.Height, tc.req.Height)
   639  				if tc.req.Pagination != nil {
   640  					s.Require().LessOrEqual(len(grpcRes.Txs), int(tc.req.Pagination.Limit))
   641  				}
   642  			}
   643  		})
   644  	}
   645  }
   646  
   647  func (s *IntegrationTestSuite) TestGetBlockWithTxs() {
   648  	srv := tx2.NewTxServer(client.Context{}, nil, nil)
   649  
   650  	_, err := srv.GetBlockWithTxs(context.Background(), nil)
   651  	s.Require().Contains(err.Error(), "request cannot be nil")
   652  }
   653  
   654  func (s *IntegrationTestSuite) TestGetBlockWithTxs_GRPCGateway() {
   655  	val := s.network.Validators[0]
   656  	testCases := []struct {
   657  		name      string
   658  		url       string
   659  		expErr    bool
   660  		expErrMsg string
   661  	}{
   662  		{
   663  			"empty params",
   664  			fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/block/0", val.APIAddress),
   665  			true, "height must not be less than 1 or greater than the current height",
   666  		},
   667  		{
   668  			"bad height",
   669  			fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/block/%d", val.APIAddress, 9999999),
   670  			true, "height must not be less than 1 or greater than the current height",
   671  		},
   672  		{
   673  			"good request",
   674  			fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/block/%d", val.APIAddress, s.txHeight),
   675  			false, "",
   676  		},
   677  	}
   678  	for _, tc := range testCases {
   679  		s.Run(tc.name, func() {
   680  			res, err := rest.GetRequest(tc.url)
   681  			s.Require().NoError(err)
   682  			if tc.expErr {
   683  				s.Require().Contains(string(res), tc.expErrMsg)
   684  			} else {
   685  				var result tx.GetBlockWithTxsResponse
   686  				err = val.ClientCtx.Codec.UnmarshalJSON(res, &result)
   687  				s.Require().NoError(err)
   688  				s.Require().Equal("foobar", result.Txs[0].Body.Memo)
   689  				s.Require().Equal(result.Block.Header.Height, s.txHeight)
   690  			}
   691  		})
   692  	}
   693  }
   694  
   695  func TestIntegrationTestSuite(t *testing.T) {
   696  	suite.Run(t, new(IntegrationTestSuite))
   697  }
   698  
   699  func (s *IntegrationTestSuite) mkTxBuilder() client.TxBuilder {
   700  	val := s.network.Validators[0]
   701  	s.Require().NoError(s.network.WaitForNextBlock())
   702  
   703  	// prepare txBuilder with msg
   704  	txBuilder := val.ClientCtx.TxConfig.NewTxBuilder()
   705  	feeAmount := sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)}
   706  	gasLimit := testdata.NewTestGasLimit()
   707  	s.Require().NoError(
   708  		txBuilder.SetMsgs(&banktypes.MsgSend{
   709  			FromAddress: val.Address.String(),
   710  			ToAddress:   val.Address.String(),
   711  			Amount:      sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)},
   712  		}),
   713  	)
   714  	txBuilder.SetFeeAmount(feeAmount)
   715  	txBuilder.SetGasLimit(gasLimit)
   716  	txBuilder.SetMemo("foobar")
   717  
   718  	// setup txFactory
   719  	txFactory := clienttx.Factory{}.
   720  		WithChainID(val.ClientCtx.ChainID).
   721  		WithKeybase(val.ClientCtx.Keyring).
   722  		WithTxConfig(val.ClientCtx.TxConfig).
   723  		WithSignMode(signing.SignMode_SIGN_MODE_DIRECT)
   724  
   725  	// Sign Tx.
   726  	err := authclient.SignTx(txFactory, val.ClientCtx, val.Moniker, txBuilder, false, true)
   727  	s.Require().NoError(err)
   728  
   729  	return txBuilder
   730  }
   731  
   732  // protoTxProvider is a type which can provide a proto transaction. It is a
   733  // workaround to get access to the wrapper TxBuilder's method GetProtoTx().
   734  // Deprecated: It's only used for testing the deprecated Simulate gRPC endpoint
   735  // using a proto Tx field.
   736  type protoTxProvider interface {
   737  	GetProtoTx() *tx.Tx
   738  }
   739  
   740  // txBuilderToProtoTx converts a txBuilder into a proto tx.Tx.
   741  // Deprecated: It's only used for testing the deprecated Simulate gRPC endpoint
   742  // using a proto Tx field.
   743  func txBuilderToProtoTx(txBuilder client.TxBuilder) (*tx.Tx, error) {
   744  	protoProvider, ok := txBuilder.(protoTxProvider)
   745  	if !ok {
   746  		return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "expected proto tx builder, got %T", txBuilder)
   747  	}
   748  
   749  	return protoProvider.GetProtoTx(), nil
   750  }