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