github.com/Finschia/finschia-sdk@v0.49.1/client/tx/tx_test.go (about)

     1  package tx_test
     2  
     3  import (
     4  	gocontext "context"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/require"
     9  	"google.golang.org/grpc"
    10  
    11  	"github.com/Finschia/finschia-sdk/client"
    12  	"github.com/Finschia/finschia-sdk/client/tx"
    13  	"github.com/Finschia/finschia-sdk/crypto/hd"
    14  	"github.com/Finschia/finschia-sdk/crypto/keyring"
    15  	cryptotypes "github.com/Finschia/finschia-sdk/crypto/types"
    16  	"github.com/Finschia/finschia-sdk/simapp"
    17  	"github.com/Finschia/finschia-sdk/testutil/network"
    18  	sdk "github.com/Finschia/finschia-sdk/types"
    19  	txtypes "github.com/Finschia/finschia-sdk/types/tx"
    20  	signingtypes "github.com/Finschia/finschia-sdk/types/tx/signing"
    21  	"github.com/Finschia/finschia-sdk/x/auth/signing"
    22  	authtypes "github.com/Finschia/finschia-sdk/x/auth/types"
    23  	banktypes "github.com/Finschia/finschia-sdk/x/bank/types"
    24  )
    25  
    26  func NewTestTxConfig() client.TxConfig {
    27  	cfg := simapp.MakeTestEncodingConfig()
    28  	return cfg.TxConfig
    29  }
    30  
    31  // mockContext is a mock client.Context to return abitrary simulation response, used to
    32  // unit test CalculateGas.
    33  type mockContext struct {
    34  	gasUsed uint64
    35  	wantErr bool
    36  }
    37  
    38  func (m mockContext) Invoke(grpcCtx gocontext.Context, method string, req, reply interface{}, opts ...grpc.CallOption) (err error) {
    39  	if m.wantErr {
    40  		return fmt.Errorf("mock err")
    41  	}
    42  
    43  	*(reply.(*txtypes.SimulateResponse)) = txtypes.SimulateResponse{
    44  		GasInfo: &sdk.GasInfo{GasUsed: m.gasUsed, GasWanted: m.gasUsed},
    45  		Result:  &sdk.Result{Data: []byte("tx data"), Log: "log"},
    46  	}
    47  
    48  	return nil
    49  }
    50  
    51  func (mockContext) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) {
    52  	panic("not implemented")
    53  }
    54  
    55  func TestCalculateGas(t *testing.T) {
    56  	type args struct {
    57  		mockGasUsed uint64
    58  		mockWantErr bool
    59  		adjustment  float64
    60  	}
    61  
    62  	testCases := []struct {
    63  		name         string
    64  		args         args
    65  		wantEstimate uint64
    66  		wantAdjusted uint64
    67  		expPass      bool
    68  	}{
    69  		{"error", args{0, true, 1.2}, 0, 0, false},
    70  		{"adjusted gas", args{10, false, 1.2}, 10, 12, true},
    71  	}
    72  
    73  	for _, tc := range testCases {
    74  		txCfg := NewTestTxConfig()
    75  
    76  		txf := tx.Factory{}.
    77  			WithChainID("test-chain").
    78  			WithTxConfig(txCfg).WithSignMode(txCfg.SignModeHandler().DefaultMode())
    79  
    80  		t.Run(tc.name, func(t *testing.T) {
    81  			mockClientCtx := mockContext{
    82  				gasUsed: tc.args.mockGasUsed,
    83  				wantErr: tc.args.mockWantErr,
    84  			}
    85  			simRes, gotAdjusted, err := tx.CalculateGas(mockClientCtx, txf.WithGasAdjustment(tc.args.adjustment))
    86  			if tc.expPass {
    87  				require.NoError(t, err)
    88  				require.Equal(t, simRes.GasInfo.GasUsed, tc.wantEstimate)
    89  				require.Equal(t, gotAdjusted, tc.wantAdjusted)
    90  				require.NotNil(t, simRes.Result)
    91  			} else {
    92  				require.Error(t, err)
    93  				require.Nil(t, simRes)
    94  			}
    95  		})
    96  	}
    97  }
    98  
    99  func TestBuildSimTx(t *testing.T) {
   100  	txCfg := NewTestTxConfig()
   101  
   102  	msg := banktypes.NewMsgSend(sdk.AccAddress("from"), sdk.AccAddress("to"), nil)
   103  	kb, err := keyring.New(t.Name(), "test", t.TempDir(), nil)
   104  	require.NoError(t, err)
   105  
   106  	txf := tx.Factory{}.
   107  		WithTxConfig(txCfg).
   108  		WithAccountNumber(50).
   109  		WithSequence(23).
   110  		WithFees("50stake").
   111  		WithMemo("memo").
   112  		WithChainID("test-chain").
   113  		WithSignMode(txCfg.SignModeHandler().DefaultMode()).
   114  		WithKeybase(kb)
   115  
   116  	// empty keybase list
   117  	bz, err := tx.BuildSimTx(txf, msg)
   118  	require.Error(t, err)
   119  	require.Nil(t, bz)
   120  
   121  	// with keybase list
   122  	path := hd.CreateHDPath(118, 0, 0).String()
   123  	_, _, err = kb.NewMnemonic("test_key1", keyring.English, path, keyring.DefaultBIP39Passphrase, hd.Secp256k1)
   124  	require.NoError(t, err)
   125  	txf = txf.WithKeybase(kb)
   126  	bz, err = tx.BuildSimTx(txf, msg)
   127  	require.NoError(t, err)
   128  	require.NotNil(t, bz)
   129  
   130  	// no ChainID
   131  	txf = txf.WithChainID("")
   132  	bz, err = tx.BuildSimTx(txf, msg)
   133  	require.Error(t, err)
   134  	require.Nil(t, bz)
   135  }
   136  
   137  func TestBuildUnsignedTx(t *testing.T) {
   138  	kb, err := keyring.New(t.Name(), "test", t.TempDir(), nil)
   139  	require.NoError(t, err)
   140  
   141  	path := hd.CreateHDPath(118, 0, 0).String()
   142  
   143  	_, _, err = kb.NewMnemonic("test_key1", keyring.English, path, keyring.DefaultBIP39Passphrase, hd.Secp256k1)
   144  	require.NoError(t, err)
   145  
   146  	txf := tx.Factory{}.
   147  		WithTxConfig(NewTestTxConfig()).
   148  		WithAccountNumber(50).
   149  		WithSequence(23).
   150  		WithGasPrices("50stake,50cony").
   151  		WithMemo("memo").
   152  		WithChainID("test-chain")
   153  
   154  	msg := banktypes.NewMsgSend(sdk.AccAddress("from"), sdk.AccAddress("to"), nil)
   155  	txBuiler, err := tx.BuildUnsignedTx(txf, msg)
   156  	require.NoError(t, err)
   157  	require.NotNil(t, txBuiler)
   158  
   159  	sigs, err := txBuiler.GetTx().(signing.SigVerifiableTx).GetSignaturesV2()
   160  	require.NoError(t, err)
   161  	require.Empty(t, sigs)
   162  
   163  	// no ChainID
   164  	txf = txf.WithChainID("")
   165  	txBuiler, err = tx.BuildUnsignedTx(txf, msg)
   166  	require.Nil(t, txBuiler)
   167  	require.Error(t, err)
   168  
   169  	// both fees and gas prices
   170  	txf = txf.
   171  		WithChainID("test-chain").
   172  		WithFees("50stake")
   173  	txBuiler, err = tx.BuildUnsignedTx(txf, msg)
   174  	require.Nil(t, txBuiler)
   175  	require.Error(t, err)
   176  }
   177  
   178  func TestPrintUnsignedTx(t *testing.T) {
   179  	txConfig := NewTestTxConfig()
   180  	txf := tx.Factory{}.
   181  		WithTxConfig(txConfig).
   182  		WithAccountNumber(50).
   183  		WithSequence(23).
   184  		WithFees("50stake").
   185  		WithMemo("memo").
   186  		WithChainID("test-chain")
   187  
   188  	msg := banktypes.NewMsgSend(sdk.AccAddress("from"), sdk.AccAddress("to"), nil)
   189  	clientCtx := client.Context{}.
   190  		WithTxConfig(txConfig)
   191  	err := txf.PrintUnsignedTx(clientCtx, msg)
   192  	require.NoError(t, err)
   193  
   194  	// no ChainID
   195  	txf = txf.WithChainID("")
   196  	err = txf.PrintUnsignedTx(clientCtx, msg)
   197  	require.Error(t, err)
   198  
   199  	// SimulateAndExecute
   200  	// failed at CaculateGas
   201  	txf = txf.WithSimulateAndExecute(true)
   202  	err = txf.PrintUnsignedTx(clientCtx, msg)
   203  	require.Error(t, err)
   204  
   205  	// Offline
   206  	clientCtx = clientCtx.WithOffline(true)
   207  	err = txf.PrintUnsignedTx(clientCtx, msg)
   208  	require.Error(t, err)
   209  }
   210  
   211  func TestSign(t *testing.T) {
   212  	requireT := require.New(t)
   213  	path := hd.CreateHDPath(118, 0, 0).String()
   214  	kr, err := keyring.New(t.Name(), "test", t.TempDir(), nil)
   215  	requireT.NoError(err)
   216  
   217  	from1 := "test_key1"
   218  	from2 := "test_key2"
   219  
   220  	// create a new key using a mnemonic generator and test if we can reuse seed to recreate that account
   221  	_, seed, err := kr.NewMnemonic(from1, keyring.English, path, keyring.DefaultBIP39Passphrase, hd.Secp256k1)
   222  	requireT.NoError(err)
   223  	requireT.NoError(kr.Delete(from1))
   224  	info1, _, err := kr.NewMnemonic(from1, keyring.English, path, keyring.DefaultBIP39Passphrase, hd.Secp256k1)
   225  	requireT.NoError(err)
   226  
   227  	info2, err := kr.NewAccount(from2, seed, "", path, hd.Secp256k1)
   228  	requireT.NoError(err)
   229  
   230  	pubKey1 := info1.GetPubKey()
   231  	pubKey2 := info2.GetPubKey()
   232  	requireT.NotEqual(pubKey1.Bytes(), pubKey2.Bytes())
   233  	t.Log("Pub keys:", pubKey1, pubKey2)
   234  
   235  	txfNoKeybase := tx.Factory{}.
   236  		WithTxConfig(NewTestTxConfig()).
   237  		WithAccountNumber(50).
   238  		WithSequence(23).
   239  		WithFees("50stake").
   240  		WithMemo("memo").
   241  		WithChainID("test-chain")
   242  	txfDirect := txfNoKeybase.
   243  		WithKeybase(kr).
   244  		WithSignMode(signingtypes.SignMode_SIGN_MODE_DIRECT)
   245  	txfAmino := txfDirect.
   246  		WithSignMode(signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON)
   247  	msg1 := banktypes.NewMsgSend(info1.GetAddress(), sdk.AccAddress("to"), nil)
   248  	msg2 := banktypes.NewMsgSend(info2.GetAddress(), sdk.AccAddress("to"), nil)
   249  
   250  	txb, err := tx.BuildUnsignedTx(txfNoKeybase, msg1, msg2)
   251  	requireT.NoError(err)
   252  	txb2, err := tx.BuildUnsignedTx(txfNoKeybase, msg1, msg2)
   253  	requireT.NoError(err)
   254  	txbSimple, err := tx.BuildUnsignedTx(txfNoKeybase, msg2)
   255  	requireT.NoError(err)
   256  
   257  	testCases := []struct {
   258  		name         string
   259  		txf          tx.Factory
   260  		txb          client.TxBuilder
   261  		from         string
   262  		overwrite    bool
   263  		expectedPKs  []cryptotypes.PubKey
   264  		matchingSigs []int // if not nil, check matching signature against old ones.
   265  	}{
   266  		{
   267  			"should fail if txf without keyring",
   268  			txfNoKeybase, txb, from1, true, nil, nil,
   269  		},
   270  		{
   271  			"should fail for non existing key",
   272  			txfAmino, txb, "unknown", true, nil, nil,
   273  		},
   274  		{
   275  			"amino: should succeed with keyring",
   276  			txfAmino, txbSimple, from1, true,
   277  			[]cryptotypes.PubKey{pubKey1},
   278  			nil,
   279  		},
   280  		{
   281  			"direct: should succeed with keyring",
   282  			txfDirect, txbSimple, from1, true,
   283  			[]cryptotypes.PubKey{pubKey1},
   284  			nil,
   285  		},
   286  
   287  		/**** test double sign Amino mode ****/
   288  		{
   289  			"amino: should sign multi-signers tx",
   290  			txfAmino, txb, from1, true,
   291  			[]cryptotypes.PubKey{pubKey1},
   292  			nil,
   293  		},
   294  		{
   295  			"amino: should append a second signature and not overwrite",
   296  			txfAmino, txb, from2, false,
   297  			[]cryptotypes.PubKey{pubKey1, pubKey2},
   298  			[]int{0, 0},
   299  		},
   300  		{
   301  			"amino: should overwrite a signature",
   302  			txfAmino, txb, from2, true,
   303  			[]cryptotypes.PubKey{pubKey2},
   304  			[]int{1, 0},
   305  		},
   306  
   307  		/**** test double sign Direct mode
   308  		  signing transaction with 2 or more DIRECT signers should fail in DIRECT mode ****/
   309  		{
   310  			"direct: should append a DIRECT signature with existing AMINO",
   311  			// txb already has 1 AMINO signature
   312  			txfDirect, txb, from1, false,
   313  			[]cryptotypes.PubKey{pubKey2, pubKey1},
   314  			nil,
   315  		},
   316  		{
   317  			"direct: should add single DIRECT sig in multi-signers tx",
   318  			txfDirect, txb2, from1, false,
   319  			[]cryptotypes.PubKey{pubKey1},
   320  			nil,
   321  		},
   322  		{
   323  			"direct: should fail to append 2nd DIRECT sig in multi-signers tx",
   324  			txfDirect, txb2, from2, false,
   325  			[]cryptotypes.PubKey{},
   326  			nil,
   327  		},
   328  		{
   329  			"amino: should append 2nd AMINO sig in multi-signers tx with 1 DIRECT sig",
   330  			// txb2 already has 1 DIRECT signature
   331  			txfAmino, txb2, from2, false,
   332  			[]cryptotypes.PubKey{},
   333  			nil,
   334  		},
   335  		{
   336  			"direct: should overwrite multi-signers tx with DIRECT sig",
   337  			txfDirect, txb2, from1, true,
   338  			[]cryptotypes.PubKey{pubKey1},
   339  			nil,
   340  		},
   341  	}
   342  	var prevSigs []signingtypes.SignatureV2
   343  	for _, tc := range testCases {
   344  		t.Run(tc.name, func(_ *testing.T) {
   345  			err = tx.Sign(tc.txf, tc.from, tc.txb, tc.overwrite)
   346  			if len(tc.expectedPKs) == 0 {
   347  				requireT.Error(err)
   348  			} else {
   349  				requireT.NoError(err)
   350  				sigs := testSigners(requireT, tc.txb.GetTx(), tc.expectedPKs...)
   351  				if tc.matchingSigs != nil {
   352  					requireT.Equal(prevSigs[tc.matchingSigs[0]], sigs[tc.matchingSigs[1]])
   353  				}
   354  				prevSigs = sigs
   355  			}
   356  		})
   357  	}
   358  }
   359  
   360  func testSigners(require *require.Assertions, tr signing.Tx, pks ...cryptotypes.PubKey) []signingtypes.SignatureV2 {
   361  	sigs, err := tr.GetSignaturesV2()
   362  	require.Len(sigs, len(pks))
   363  	require.NoError(err)
   364  	require.Len(sigs, len(pks))
   365  	for i := range pks {
   366  		require.True(sigs[i].PubKey.Equals(pks[i]), "Signature is signed with a wrong pubkey. Got: %s, expected: %s", sigs[i].PubKey, pks[i])
   367  	}
   368  	return sigs
   369  }
   370  
   371  func TestPrepare(t *testing.T) {
   372  	txf := tx.Factory{}.
   373  		WithAccountRetriever(authtypes.AccountRetriever{})
   374  
   375  	cfg := network.DefaultConfig()
   376  	cfg.NumValidators = 1
   377  
   378  	network := network.New(t, cfg)
   379  	defer network.Cleanup()
   380  
   381  	_, err := network.WaitForHeight(3)
   382  	require.NoError(t, err)
   383  
   384  	val := network.Validators[0]
   385  	clientCtx := val.ClientCtx.
   386  		WithHeight(2).
   387  		WithFromAddress(val.Address)
   388  
   389  	_, err = txf.Prepare(clientCtx)
   390  	require.NoError(t, err)
   391  }