Skip to content

Commit

Permalink
feat: support customized nonce (#94)
Browse files Browse the repository at this point in the history
* feat: support customized nonce
  • Loading branch information
alexgao001 authored Mar 9, 2023
1 parent 1d83041 commit c1c680d
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 9 deletions.
2 changes: 1 addition & 1 deletion sdk/client/test/config.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package test

const (
TEST_PRIVATE_KEY = "ff0911d998e6f12cf4b0a88d90cc411aa3be06c9dc5a2b35834f0e25a44e2275"
TEST_PRIVATE_KEY = "a6d9af079ae116f6ba2ff5cc269bb11e028223229473418ced422e6976bb8cc9"
TEST_ADDR = "0x76d244CE05c3De4BbC6fDd7F56379B145709ade9"
TEST_ADDR2 = "0x5dEfC28ce1Ed331D56aB6F607f78708880e11Bea"
TEST_ADDR3 = "0x593107F1D5D10A68D3C3722C35ADa2eb779D44A4"
Expand Down
36 changes: 28 additions & 8 deletions sdk/client/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
}
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
30 changes: 30 additions & 0 deletions sdk/client/tx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}

}
1 change: 1 addition & 0 deletions sdk/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit c1c680d

Please sign in to comment.