diff --git a/CHANGELOG.md b/CHANGELOG.md index 0175961411d8..e977998bf3c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -177,6 +177,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Bug Fixes +* (client) [\#11283](https://github.com/cosmos/cosmos-sdk/issues/11283) Support multiple keys for tx simulation and setting automatic gas for txs. * (store) [\#11177](https://github.com/cosmos/cosmos-sdk/pull/11177) Update the prune `everything` strategy to store the last two heights. * [\#10844](https://github.com/cosmos/cosmos-sdk/pull/10844) Automatic recovering non-consistent keyring storage during public key import. * (store) [\#11117](https://github.com/cosmos/cosmos-sdk/pull/11117) Fix data race in store trace component diff --git a/client/tx/factory.go b/client/tx/factory.go index 79fe2b45f9f2..5e8053282238 100644 --- a/client/tx/factory.go +++ b/client/tx/factory.go @@ -11,6 +11,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx" "github.com/cosmos/cosmos-sdk/types/tx/signing" @@ -306,10 +307,31 @@ func (f Factory) BuildSimTx(msgs ...sdk.Msg) ([]byte, error) { return nil, err } + // use the first element from the list of keys in order to generate a valid + // pubkey that supports multiple algorithms + + var ( + ok bool + pk cryptotypes.PubKey = &secp256k1.PubKey{} // use default public key type + ) + + if f.keybase != nil { + records, _ := f.keybase.List() + if len(records) == 0 { + return nil, errors.New("cannot build signature for simulation, key records slice is empty") + } + + // take the first record just for simulation purposes + pk, ok = records[0].PubKey.GetCachedValue().(cryptotypes.PubKey) + if !ok { + return nil, errors.New("cannot build signature for simulation, failed to convert proto Any to public key") + } + } + // Create an empty signature literal as the ante handler will populate with a // sentinel pubkey. sig := signing.SignatureV2{ - PubKey: &secp256k1.PubKey{}, + PubKey: pk, Data: &signing.SingleSignatureData{ SignMode: f.signMode, }, diff --git a/client/tx/tx_test.go b/client/tx/tx_test.go index 181c857f9000..5e917fa1bff3 100644 --- a/client/tx/tx_test.go +++ b/client/tx/tx_test.go @@ -45,6 +45,7 @@ func (m mockContext) Invoke(grpcCtx gocontext.Context, method string, req, reply return nil } + func (mockContext) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) { panic("not implemented") } @@ -96,6 +97,14 @@ func TestCalculateGas(t *testing.T) { func TestBuildSimTx(t *testing.T) { txCfg := NewTestTxConfig() + encCfg := simapp.MakeTestEncodingConfig() + + kb, err := keyring.New(t.Name(), "test", t.TempDir(), nil, encCfg.Codec) + require.NoError(t, err) + + path := hd.CreateHDPath(118, 0, 0).String() + _, _, err = kb.NewMnemonic("test_key1", keyring.English, path, keyring.DefaultBIP39Passphrase, hd.Secp256k1) + require.NoError(t, err) txf := tx.Factory{}. WithTxConfig(txCfg). @@ -104,7 +113,8 @@ func TestBuildSimTx(t *testing.T) { WithFees("50stake"). WithMemo("memo"). WithChainID("test-chain"). - WithSignMode(txCfg.SignModeHandler().DefaultMode()) + WithSignMode(txCfg.SignModeHandler().DefaultMode()). + WithKeybase(kb) msg := banktypes.NewMsgSend(sdk.AccAddress("from"), sdk.AccAddress("to"), nil) bz, err := txf.BuildSimTx(msg) @@ -113,13 +123,23 @@ func TestBuildSimTx(t *testing.T) { } func TestBuildUnsignedTx(t *testing.T) { + encCfg := simapp.MakeTestEncodingConfig() + kb, err := keyring.New(t.Name(), "test", t.TempDir(), nil, encCfg.Codec) + require.NoError(t, err) + + path := hd.CreateHDPath(118, 0, 0).String() + + _, _, err = kb.NewMnemonic("test_key1", keyring.English, path, keyring.DefaultBIP39Passphrase, hd.Secp256k1) + require.NoError(t, err) + txf := tx.Factory{}. WithTxConfig(NewTestTxConfig()). WithAccountNumber(50). WithSequence(23). WithFees("50stake"). WithMemo("memo"). - WithChainID("test-chain") + WithChainID("test-chain"). + WithKeybase(kb) msg := banktypes.NewMsgSend(sdk.AccAddress("from"), sdk.AccAddress("to"), nil) tx, err := txf.BuildUnsignedTx(msg) @@ -138,8 +158,8 @@ func TestSign(t *testing.T) { kb, err := keyring.New(t.Name(), "test", t.TempDir(), nil, encCfg.Codec) requireT.NoError(err) - var from1 = "test_key1" - var from2 = "test_key2" + from1 := "test_key1" + from2 := "test_key2" // create a new key using a mnemonic generator and test if we can reuse seed to recreate that account _, seed, err := kb.NewMnemonic(from1, keyring.English, path, keyring.DefaultBIP39Passphrase, hd.Secp256k1) @@ -192,37 +212,81 @@ func TestSign(t *testing.T) { expectedPKs []cryptotypes.PubKey matchingSigs []int // if not nil, check matching signature against old ones. }{ - {"should fail if txf without keyring", - txfNoKeybase, txb, from1, true, nil, nil}, - {"should fail for non existing key", - txfAmino, txb, "unknown", true, nil, nil}, - {"amino: should succeed with keyring", - txfAmino, txbSimple, from1, true, []cryptotypes.PubKey{pubKey1}, nil}, - {"direct: should succeed with keyring", - txfDirect, txbSimple, from1, true, []cryptotypes.PubKey{pubKey1}, nil}, + { + "should fail if txf without keyring", + txfNoKeybase, txb, from1, true, nil, nil, + }, + { + "should fail for non existing key", + txfAmino, txb, "unknown", true, nil, nil, + }, + { + "amino: should succeed with keyring", + txfAmino, txbSimple, from1, true, + []cryptotypes.PubKey{pubKey1}, + nil, + }, + { + "direct: should succeed with keyring", + txfDirect, txbSimple, from1, true, + []cryptotypes.PubKey{pubKey1}, + nil, + }, /**** test double sign Amino mode ****/ - {"amino: should sign multi-signers tx", - txfAmino, txb, from1, true, []cryptotypes.PubKey{pubKey1}, nil}, - {"amino: should append a second signature and not overwrite", - txfAmino, txb, from2, false, []cryptotypes.PubKey{pubKey1, pubKey2}, []int{0, 0}}, - {"amino: should overwrite a signature", - txfAmino, txb, from2, true, []cryptotypes.PubKey{pubKey2}, []int{1, 0}}, + { + "amino: should sign multi-signers tx", + txfAmino, txb, from1, true, + []cryptotypes.PubKey{pubKey1}, + nil, + }, + { + "amino: should append a second signature and not overwrite", + txfAmino, txb, from2, false, + []cryptotypes.PubKey{pubKey1, pubKey2}, + []int{0, 0}, + }, + { + "amino: should overwrite a signature", + txfAmino, txb, from2, true, + []cryptotypes.PubKey{pubKey2}, + []int{1, 0}, + }, /**** test double sign Direct mode signing transaction with 2 or more DIRECT signers should fail in DIRECT mode ****/ - {"direct: should append a DIRECT signature with existing AMINO", + { + "direct: should append a DIRECT signature with existing AMINO", // txb already has 1 AMINO signature - txfDirect, txb, from1, false, []cryptotypes.PubKey{pubKey2, pubKey1}, nil}, - {"direct: should add single DIRECT sig in multi-signers tx", - txfDirect, txb2, from1, false, []cryptotypes.PubKey{pubKey1}, nil}, - {"direct: should fail to append 2nd DIRECT sig in multi-signers tx", - txfDirect, txb2, from2, false, []cryptotypes.PubKey{}, nil}, - {"amino: should append 2nd AMINO sig in multi-signers tx with 1 DIRECT sig", + txfDirect, txb, from1, false, + []cryptotypes.PubKey{pubKey2, pubKey1}, + nil, + }, + { + "direct: should add single DIRECT sig in multi-signers tx", + txfDirect, txb2, from1, false, + []cryptotypes.PubKey{pubKey1}, + nil, + }, + { + "direct: should fail to append 2nd DIRECT sig in multi-signers tx", + txfDirect, txb2, from2, false, + []cryptotypes.PubKey{}, + nil, + }, + { + "amino: should append 2nd AMINO sig in multi-signers tx with 1 DIRECT sig", // txb2 already has 1 DIRECT signature - txfAmino, txb2, from2, false, []cryptotypes.PubKey{}, nil}, - {"direct: should overwrite multi-signers tx with DIRECT sig", - txfDirect, txb2, from1, true, []cryptotypes.PubKey{pubKey1}, nil}, + txfAmino, txb2, from2, false, + []cryptotypes.PubKey{}, + nil, + }, + { + "direct: should overwrite multi-signers tx with DIRECT sig", + txfDirect, txb2, from1, true, + []cryptotypes.PubKey{pubKey1}, + nil, + }, } var prevSigs []signingtypes.SignatureV2