From c1c680d6159ff5794ae7829e23897b141cc5cf5d Mon Sep 17 00:00:00 2001 From: alexgao001 <118710506+alexgao001@users.noreply.github.com> Date: Thu, 9 Mar 2023 11:49:03 +0800 Subject: [PATCH] feat: support customized nonce (#94) * feat: support customized nonce --- sdk/client/test/config.go | 2 +- sdk/client/tx.go | 36 ++++++++++++++++++++++++++++-------- sdk/client/tx_test.go | 30 ++++++++++++++++++++++++++++++ sdk/types/types.go | 1 + 4 files changed, 60 insertions(+), 9 deletions(-) diff --git a/sdk/client/test/config.go b/sdk/client/test/config.go index b897e1a97..9f965b239 100644 --- a/sdk/client/test/config.go +++ b/sdk/client/test/config.go @@ -1,7 +1,7 @@ package test const ( - TEST_PRIVATE_KEY = "ff0911d998e6f12cf4b0a88d90cc411aa3be06c9dc5a2b35834f0e25a44e2275" + TEST_PRIVATE_KEY = "a6d9af079ae116f6ba2ff5cc269bb11e028223229473418ced422e6976bb8cc9" TEST_ADDR = "0x76d244CE05c3De4BbC6fDd7F56379B145709ade9" TEST_ADDR2 = "0x5dEfC28ce1Ed331D56aB6F607f78708880e11Bea" TEST_ADDR3 = "0x593107F1D5D10A68D3C3722C35ADa2eb779D44A4" diff --git a/sdk/client/tx.go b/sdk/client/tx.go index 67c1e2813..f40ceb487 100644 --- a/sdk/client/tx.go +++ b/sdk/client/tx.go @@ -20,6 +20,7 @@ type TransactionClient interface { BroadcastTx(msgs []sdk.Msg, txOpt *types.TxOption, opts ...grpc.CallOption) (*tx.BroadcastTxResponse, error) SimulateTx(msgs []sdk.Msg, txOpt *types.TxOption, opts ...grpc.CallOption) (*tx.SimulateResponse, error) SignTx(msgs []sdk.Msg, txOpt *types.TxOption) ([]byte, error) + GetNonce() (uint64, error) } // BroadcastTx signs and broadcasts a tx with simulated gas(if not provided in txOpt) @@ -33,7 +34,7 @@ func (c *GreenfieldClient) BroadcastTx(msgs []sdk.Msg, txOpt *types.TxOption, op } // sign a tx - txSignedBytes, err := c.signTx(txConfig, txBuilder) + txSignedBytes, err := c.signTx(txConfig, txBuilder, txOpt) if err != nil { return nil, err } @@ -96,29 +97,35 @@ func (c *GreenfieldClient) SignTx(msgs []sdk.Msg, txOpt *types.TxOption) ([]byte if err := c.constructTxWithGasInfo(msgs, txOpt, txConfig, txBuilder); err != nil { return nil, err } - return c.signTx(txConfig, txBuilder) + return c.signTx(txConfig, txBuilder, txOpt) } -func (c *GreenfieldClient) signTx(txConfig client.TxConfig, txBuilder client.TxBuilder) ([]byte, error) { +func (c *GreenfieldClient) signTx(txConfig client.TxConfig, txBuilder client.TxBuilder, txOpt *types.TxOption) ([]byte, error) { km, err := c.GetKeyManager() if err != nil { return nil, err } + var nonce uint64 account, err := c.getAccount() if err != nil { return nil, err } + nonce = account.GetSequence() + if txOpt != nil && txOpt.Nonce != 0 { + nonce = txOpt.Nonce + } + signerData := xauthsigning.SignerData{ ChainID: c.chainId, AccountNumber: account.GetAccountNumber(), - Sequence: account.GetSequence(), + Sequence: nonce, } sig, err := clitx.SignWithPrivKey(signing.SignMode_SIGN_MODE_EIP_712, signerData, txBuilder, km.GetPrivKey(), txConfig, - account.GetSequence(), + nonce, ) if err != nil { return nil, err @@ -135,21 +142,26 @@ func (c *GreenfieldClient) signTx(txConfig client.TxConfig, txBuilder client.TxB } // setSingerInfo gathers the signer info by doing "empty signature" hack, and inject it into txBuilder -func (c *GreenfieldClient) setSingerInfo(txBuilder client.TxBuilder) error { +func (c *GreenfieldClient) setSingerInfo(txBuilder client.TxBuilder, txOpt *types.TxOption) error { km, err := c.GetKeyManager() if err != nil { return err } + var nonce uint64 account, err := c.getAccount() if err != nil { return err } + nonce = account.GetSequence() + if txOpt != nil && txOpt.Nonce != 0 { + nonce = txOpt.Nonce + } sig := signing.SignatureV2{ PubKey: km.GetPrivKey().PubKey(), Data: &signing.SingleSignatureData{ SignMode: signing.SignMode_SIGN_MODE_EIP_712, }, - Sequence: account.GetSequence(), + Sequence: nonce, } if err := txBuilder.SetSignatures(sig); err != nil { return err @@ -176,7 +188,7 @@ func (c *GreenfieldClient) constructTx(msgs []sdk.Msg, txOpt *types.TxOption, tx } } // inject signer info into txBuilder, it is needed for simulating and signing - return c.setSingerInfo(txBuilder) + return c.setSingerInfo(txBuilder, txOpt) } func (c *GreenfieldClient) constructTxWithGasInfo(msgs []sdk.Msg, txOpt *types.TxOption, txConfig client.TxConfig, txBuilder client.TxBuilder) error { @@ -215,6 +227,14 @@ func (c *GreenfieldClient) constructTxWithGasInfo(msgs []sdk.Msg, txOpt *types.T return nil } +func (c *GreenfieldClient) GetNonce() (uint64, error) { + account, err := c.getAccount() + if err != nil { + return 0, err + } + return account.GetSequence(), nil +} + func (c *GreenfieldClient) getAccount() (authtypes.AccountI, error) { km, err := c.GetKeyManager() if err != nil { diff --git a/sdk/client/tx_test.go b/sdk/client/tx_test.go index 5cbe9adf7..2ee188b9b 100644 --- a/sdk/client/tx_test.go +++ b/sdk/client/tx_test.go @@ -67,3 +67,33 @@ func TestSimulateTx(t *testing.T) { assert.NoError(t, err) t.Log(simulateRes.GasInfo.String()) } + +func TestSendTokenWithCustomizedNonce(t *testing.T) { + km, err := keys.NewPrivateKeyManager(test.TEST_PRIVATE_KEY) + assert.NoError(t, err) + gnfdCli := NewGreenfieldClient(test.TEST_GRPC_ADDR, test.TEST_CHAIN_ID, + WithKeyManager(km), + WithGrpcDialOption(grpc.WithTransportCredentials(insecure.NewCredentials()))) + to, err := sdk.AccAddressFromHexUnsafe(test.TEST_ADDR) + assert.NoError(t, err) + transfer := banktypes.NewMsgSend(km.GetAddr(), to, sdk.NewCoins(sdk.NewInt64Coin(test.TEST_TOKEN_NAME, 100))) + payerAddr, err := sdk.AccAddressFromHexUnsafe(km.GetAddr().String()) + assert.NoError(t, err) + nonce, err := gnfdCli.GetNonce() + assert.NoError(t, err) + + for i := 0; i < 50; i++ { + txOpt := &types.TxOption{ + GasLimit: 123456, + Memo: "test", + FeePayer: payerAddr, + Nonce: nonce, + } + response, err := gnfdCli.BroadcastTx([]sdk.Msg{transfer}, txOpt) + assert.NoError(t, err) + nonce++ + assert.Equal(t, uint32(0), response.TxResponse.Code) + t.Log(response.TxResponse.String()) + } + +} diff --git a/sdk/types/types.go b/sdk/types/types.go index c56d18855..c82b0f306 100644 --- a/sdk/types/types.go +++ b/sdk/types/types.go @@ -24,6 +24,7 @@ type TxOption struct { Memo string FeeAmount sdk.Coins FeePayer sdk.AccAddress + Nonce uint64 } func NewIntFromInt64WithDecimal(amount int64, decimal int64) sdkmath.Int {