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 }