Add support for protobuf TxGenerator and SIGN_MODE_DIRECT (#6385)
* Add TxWrapper, encoder, decoder and DirectModeHandler * fix pkg name * Update API and leave test TODO's * Update TxWrapper API * tests for tx wrapper (#6410) * WIP: added test for direct mode handler * updated code * Add msg * Update TxWrapper API * Fix pubkey declaration * Add pubkey for tests * Fix SetFee * Remove logs * Avoid global var declaration for tests * Add test for GetPubKeys * Fix direct signing tests * Add more test cases for GetSignBytes * Revert SetFee API * Remove logs * Refactor tests Co-authored-by: anilCSE <anil@vitwit.com> Co-authored-by: sahith-narahari <sahithnarahari@gmail.com> * Refactoring * Refactoring * Integrate SignatureV2 API * Fix wrapper tests * Fix tests * Linting and API tweaks * Update API * WIP on updating API * Fix tests * Update to new SigVerifiableTx * Rename * Update docs to reflect ADR 020 * proto-gen * proto docs * cleanup * cleanup * cleanup * cleanup * cleanup * cleanup * cleanup * Add tests * Refactor and improving test coverage * WIP on test coverage * WIP on test coverage * proto-gen * Fix CompactBitArray.Size() bug * Rename * Remove Builder interface * Address review comments * Update x/auth/tx/sigs.go Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * Update x/auth/tx/encoder.go Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * Update x/auth/tx/encoder.go Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * Address review feedback * Fix build issues * Resolve conflicts * Fix ValidateBasic test coverage * Add test for malicious multisig Co-authored-by: atheeshp <59333759+atheeshp@users.noreply.github.com> Co-authored-by: anilCSE <anil@vitwit.com> Co-authored-by: sahith-narahari <sahithnarahari@gmail.com> Co-authored-by: Alexander Bezobchuk <alexanderbez@users.noreply.github.com> Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
co-authored by
anilCSE
sahith-narahari
Federico Kunze
atheeshp
Alexander Bezobchuk
parent
feb69770ef
commit
2f44fbf2ab
@@ -4,11 +4,12 @@ import (
|
||||
gocontext "context"
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec/types"
|
||||
gogogrpc "github.com/gogo/protobuf/grpc"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/encoding"
|
||||
"google.golang.org/grpc/encoding/proto"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec/types"
|
||||
)
|
||||
|
||||
var _ gogogrpc.ClientConn = Context{}
|
||||
|
||||
+220
-42
@@ -1,9 +1,14 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec/testdata"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/types/multisig"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
|
||||
@@ -25,86 +30,259 @@ func NewTxGeneratorTestSuite(txGenerator client.TxGenerator) *TxGeneratorTestSui
|
||||
}
|
||||
|
||||
func (s *TxGeneratorTestSuite) TestTxBuilderGetTx() {
|
||||
stdTxBuilder := s.TxGenerator.NewTxBuilder()
|
||||
tx := stdTxBuilder.GetTx()
|
||||
txBuilder := s.TxGenerator.NewTxBuilder()
|
||||
tx := txBuilder.GetTx()
|
||||
s.Require().NotNil(tx)
|
||||
s.Require().Equal(len(tx.GetMsgs()), 0)
|
||||
}
|
||||
|
||||
func (s *TxGeneratorTestSuite) TestTxBuilderSetFeeAmount() {
|
||||
stdTxBuilder := s.TxGenerator.NewTxBuilder()
|
||||
txBuilder := s.TxGenerator.NewTxBuilder()
|
||||
feeAmount := sdk.Coins{
|
||||
sdk.NewInt64Coin("atom", 20000000),
|
||||
}
|
||||
stdTxBuilder.SetFeeAmount(feeAmount)
|
||||
feeTx := stdTxBuilder.GetTx().(sdk.FeeTx)
|
||||
txBuilder.SetFeeAmount(feeAmount)
|
||||
feeTx := txBuilder.GetTx()
|
||||
s.Require().Equal(feeAmount, feeTx.GetFee())
|
||||
}
|
||||
|
||||
func (s *TxGeneratorTestSuite) TestTxBuilderSetGasLimit() {
|
||||
const newGas uint64 = 300000
|
||||
stdTxBuilder := s.TxGenerator.NewTxBuilder()
|
||||
stdTxBuilder.SetGasLimit(newGas)
|
||||
feeTx := stdTxBuilder.GetTx().(sdk.FeeTx)
|
||||
txBuilder := s.TxGenerator.NewTxBuilder()
|
||||
txBuilder.SetGasLimit(newGas)
|
||||
feeTx := txBuilder.GetTx()
|
||||
s.Require().Equal(newGas, feeTx.GetGas())
|
||||
}
|
||||
|
||||
func (s *TxGeneratorTestSuite) TestTxBuilderSetMemo() {
|
||||
const newMemo string = "newfoomemo"
|
||||
stdTxBuilder := s.TxGenerator.NewTxBuilder()
|
||||
stdTxBuilder.SetMemo(newMemo)
|
||||
txWithMemo := stdTxBuilder.GetTx().(sdk.TxWithMemo)
|
||||
txBuilder := s.TxGenerator.NewTxBuilder()
|
||||
txBuilder.SetMemo(newMemo)
|
||||
txWithMemo := txBuilder.GetTx()
|
||||
s.Require().Equal(txWithMemo.GetMemo(), newMemo)
|
||||
}
|
||||
|
||||
func (s *TxGeneratorTestSuite) TestTxBuilderSetMsgs() {
|
||||
stdTxBuilder := s.TxGenerator.NewTxBuilder()
|
||||
tx := stdTxBuilder.GetTx()
|
||||
err := stdTxBuilder.SetMsgs(sdk.NewTestMsg(), sdk.NewTestMsg())
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotEqual(tx, stdTxBuilder.GetTx())
|
||||
s.Require().Equal(len(stdTxBuilder.GetTx().GetMsgs()), 2)
|
||||
}
|
||||
_, _, addr1 := authtypes.KeyTestPubAddr()
|
||||
_, _, addr2 := authtypes.KeyTestPubAddr()
|
||||
msg1 := testdata.NewTestMsg(addr1)
|
||||
msg2 := testdata.NewTestMsg(addr2)
|
||||
msgs := []sdk.Msg{msg1, msg2}
|
||||
|
||||
type HasSignaturesTx interface {
|
||||
GetSignatures() [][]byte
|
||||
GetSigners() []sdk.AccAddress
|
||||
GetPubKeys() []crypto.PubKey // If signer already has pubkey in context, this list will have nil in its place
|
||||
txBuilder := s.TxGenerator.NewTxBuilder()
|
||||
|
||||
err := txBuilder.SetMsgs(msgs...)
|
||||
s.Require().NoError(err)
|
||||
tx := txBuilder.GetTx()
|
||||
s.Require().Equal(msgs, tx.GetMsgs())
|
||||
s.Require().Equal([]sdk.AccAddress{addr1, addr2}, tx.GetSigners())
|
||||
s.Require().Equal(addr1, tx.FeePayer())
|
||||
s.Require().Error(tx.ValidateBasic()) // should fail because of no signatures
|
||||
}
|
||||
|
||||
func (s *TxGeneratorTestSuite) TestTxBuilderSetSignatures() {
|
||||
priv := secp256k1.GenPrivKey()
|
||||
privKey, pubkey, addr := authtypes.KeyTestPubAddr()
|
||||
privKey2, pubkey2, _ := authtypes.KeyTestPubAddr()
|
||||
multisigPk := multisig.NewPubKeyMultisigThreshold(2, []crypto.PubKey{pubkey, pubkey2})
|
||||
|
||||
stdTxBuilder := s.TxGenerator.NewTxBuilder()
|
||||
tx := stdTxBuilder.GetTx()
|
||||
singleSignatureData := signingtypes.SingleSignatureData{
|
||||
Signature: priv.PubKey().Bytes(),
|
||||
SignMode: signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
|
||||
txBuilder := s.TxGenerator.NewTxBuilder()
|
||||
|
||||
// set test msg
|
||||
msg := testdata.NewTestMsg(addr)
|
||||
msigAddr := sdk.AccAddress(multisigPk.Address())
|
||||
msg2 := testdata.NewTestMsg(msigAddr)
|
||||
err := txBuilder.SetMsgs(msg, msg2)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// check that validation fails
|
||||
s.Require().Error(txBuilder.GetTx().ValidateBasic())
|
||||
|
||||
signModeHandler := s.TxGenerator.SignModeHandler()
|
||||
s.Require().Contains(signModeHandler.Modes(), signModeHandler.DefaultMode())
|
||||
|
||||
// set SignatureV2 without actual signature bytes
|
||||
sigData1 := &signingtypes.SingleSignatureData{SignMode: signModeHandler.DefaultMode()}
|
||||
sig1 := signingtypes.SignatureV2{PubKey: pubkey, Data: sigData1}
|
||||
|
||||
msigData := multisig.NewMultisig(2)
|
||||
multisig.AddSignature(msigData, &signingtypes.SingleSignatureData{SignMode: signModeHandler.DefaultMode()}, 0)
|
||||
multisig.AddSignature(msigData, &signingtypes.SingleSignatureData{SignMode: signModeHandler.DefaultMode()}, 1)
|
||||
msig := signingtypes.SignatureV2{PubKey: multisigPk, Data: msigData}
|
||||
|
||||
// fail validation without required signers
|
||||
err = txBuilder.SetSignatures(sig1)
|
||||
s.Require().NoError(err)
|
||||
sigTx := txBuilder.GetTx()
|
||||
s.Require().Error(sigTx.ValidateBasic())
|
||||
|
||||
err = txBuilder.SetSignatures(sig1, msig)
|
||||
s.Require().NoError(err)
|
||||
sigTx = txBuilder.GetTx()
|
||||
s.Require().Len(sigTx.GetSignatures(), 2)
|
||||
sigsV2, err := sigTx.GetSignaturesV2()
|
||||
s.Require().NoError(err)
|
||||
s.Require().Len(sigsV2, 2)
|
||||
s.Require().True(sigEquals(sig1, sigsV2[0]))
|
||||
s.Require().True(sigEquals(msig, sigsV2[1]))
|
||||
s.Require().Equal([]sdk.AccAddress{addr, msigAddr}, sigTx.GetSigners())
|
||||
s.Require().NoError(sigTx.ValidateBasic())
|
||||
|
||||
// sign transaction
|
||||
signerData := signing.SignerData{
|
||||
ChainID: "test",
|
||||
AccountNumber: 1,
|
||||
AccountSequence: 2,
|
||||
}
|
||||
signBytes, err := signModeHandler.GetSignBytes(signModeHandler.DefaultMode(), signerData, sigTx)
|
||||
s.Require().NoError(err)
|
||||
sigBz, err := privKey.Sign(signBytes)
|
||||
s.Require().NoError(err)
|
||||
|
||||
signerData = signing.SignerData{
|
||||
ChainID: "test",
|
||||
AccountNumber: 3,
|
||||
AccountSequence: 4,
|
||||
}
|
||||
mSignBytes, err := signModeHandler.GetSignBytes(signModeHandler.DefaultMode(), signerData, sigTx)
|
||||
s.Require().NoError(err)
|
||||
mSigBz1, err := privKey.Sign(mSignBytes)
|
||||
s.Require().NoError(err)
|
||||
mSigBz2, err := privKey2.Sign(mSignBytes)
|
||||
s.Require().NoError(err)
|
||||
msigData = multisig.NewMultisig(2)
|
||||
multisig.AddSignature(msigData, &signingtypes.SingleSignatureData{
|
||||
SignMode: signModeHandler.DefaultMode(), Signature: mSigBz1}, 0)
|
||||
multisig.AddSignature(msigData, &signingtypes.SingleSignatureData{
|
||||
SignMode: signModeHandler.DefaultMode(), Signature: mSigBz2}, 0)
|
||||
|
||||
// set signature
|
||||
sigData1.Signature = sigBz
|
||||
sig1 = signingtypes.SignatureV2{PubKey: pubkey, Data: sigData1}
|
||||
msig = signingtypes.SignatureV2{PubKey: multisigPk, Data: msigData}
|
||||
err = txBuilder.SetSignatures(sig1, msig)
|
||||
s.Require().NoError(err)
|
||||
sigTx = txBuilder.GetTx()
|
||||
s.Require().Len(sigTx.GetSignatures(), 2)
|
||||
sigsV2, err = sigTx.GetSignaturesV2()
|
||||
s.Require().NoError(err)
|
||||
s.Require().Len(sigsV2, 2)
|
||||
s.Require().True(sigEquals(sig1, sigsV2[0]))
|
||||
s.Require().True(sigEquals(msig, sigsV2[1]))
|
||||
s.Require().Equal([]sdk.AccAddress{addr, msigAddr}, sigTx.GetSigners())
|
||||
s.Require().NoError(sigTx.ValidateBasic())
|
||||
}
|
||||
|
||||
func sigEquals(sig1, sig2 signingtypes.SignatureV2) bool {
|
||||
if !sig1.PubKey.Equals(sig2.PubKey) {
|
||||
return false
|
||||
}
|
||||
|
||||
err := stdTxBuilder.SetSignatures(signingtypes.SignatureV2{
|
||||
PubKey: priv.PubKey(),
|
||||
Data: &singleSignatureData,
|
||||
})
|
||||
sigTx := stdTxBuilder.GetTx().(HasSignaturesTx)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotEqual(tx, stdTxBuilder.GetTx())
|
||||
s.Require().Equal(sigTx.GetSignatures()[0], priv.PubKey().Bytes())
|
||||
if sig1.Data == nil && sig2.Data == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return sigDataEquals(sig1.Data, sig2.Data)
|
||||
}
|
||||
|
||||
func sigDataEquals(data1, data2 signingtypes.SignatureData) bool {
|
||||
switch data1 := data1.(type) {
|
||||
case *signingtypes.SingleSignatureData:
|
||||
data2, ok := data2.(*signingtypes.SingleSignatureData)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if data1.SignMode != data2.SignMode {
|
||||
return false
|
||||
}
|
||||
|
||||
return bytes.Equal(data1.Signature, data2.Signature)
|
||||
case *signingtypes.MultiSignatureData:
|
||||
data2, ok := data2.(*signingtypes.MultiSignatureData)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if data1.BitArray.ExtraBitsStored != data2.BitArray.ExtraBitsStored {
|
||||
return false
|
||||
}
|
||||
|
||||
if !bytes.Equal(data1.BitArray.Elems, data2.BitArray.Elems) {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(data1.Signatures) != len(data2.Signatures) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, s := range data1.Signatures {
|
||||
if !sigDataEquals(s, data2.Signatures[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TxGeneratorTestSuite) TestTxEncodeDecode() {
|
||||
_, pubkey, addr := authtypes.KeyTestPubAddr()
|
||||
feeAmount := sdk.Coins{sdk.NewInt64Coin("atom", 150)}
|
||||
gasLimit := uint64(50000)
|
||||
memo := "foomemo"
|
||||
msg := testdata.NewTestMsg(addr)
|
||||
dummySig := []byte("dummySig")
|
||||
sig := signingtypes.SignatureV2{
|
||||
PubKey: pubkey,
|
||||
Data: &signingtypes.SingleSignatureData{
|
||||
SignMode: signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
|
||||
Signature: dummySig,
|
||||
},
|
||||
}
|
||||
|
||||
txBuilder := s.TxGenerator.NewTxBuilder()
|
||||
txBuilder.SetFeeAmount(sdk.Coins{sdk.NewInt64Coin("atom", 150)})
|
||||
txBuilder.SetGasLimit(50000)
|
||||
txBuilder.SetMemo("foomemo")
|
||||
txBuilder.SetFeeAmount(feeAmount)
|
||||
txBuilder.SetGasLimit(gasLimit)
|
||||
txBuilder.SetMemo(memo)
|
||||
err := txBuilder.SetMsgs(msg)
|
||||
s.Require().NoError(err)
|
||||
err = txBuilder.SetSignatures(sig)
|
||||
s.Require().NoError(err)
|
||||
tx := txBuilder.GetTx()
|
||||
|
||||
// Encode transaction
|
||||
s.T().Log("encode transaction")
|
||||
txBytes, err := s.TxGenerator.TxEncoder()(tx)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(txBytes)
|
||||
|
||||
s.T().Log("decode transaction")
|
||||
tx2, err := s.TxGenerator.TxDecoder()(txBytes)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(tx, tx2)
|
||||
tx3, ok := tx2.(signing.SigFeeMemoTx)
|
||||
s.Require().True(ok)
|
||||
s.Require().Equal([]sdk.Msg{msg}, tx3.GetMsgs())
|
||||
s.Require().Equal(feeAmount, tx3.GetFee())
|
||||
s.Require().Equal(gasLimit, tx3.GetGas())
|
||||
s.Require().Equal(memo, tx3.GetMemo())
|
||||
s.Require().Equal([][]byte{dummySig}, tx3.GetSignatures())
|
||||
s.Require().Equal([]crypto.PubKey{pubkey}, tx3.GetPubKeys())
|
||||
|
||||
s.T().Log("JSON encode transaction")
|
||||
jsonTxBytes, err := s.TxGenerator.TxJSONEncoder()(tx)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(jsonTxBytes)
|
||||
|
||||
s.T().Log("JSON decode transaction")
|
||||
tx2, err = s.TxGenerator.TxJSONDecoder()(jsonTxBytes)
|
||||
s.Require().NoError(err)
|
||||
tx3, ok = tx2.(signing.SigFeeMemoTx)
|
||||
s.Require().True(ok)
|
||||
s.Require().Equal([]sdk.Msg{msg}, tx3.GetMsgs())
|
||||
s.Require().Equal(feeAmount, tx3.GetFee())
|
||||
s.Require().Equal(gasLimit, tx3.GetGas())
|
||||
s.Require().Equal(memo, tx3.GetMemo())
|
||||
s.Require().Equal([][]byte{dummySig}, tx3.GetSignatures())
|
||||
s.Require().Equal([]crypto.PubKey{pubkey}, tx3.GetPubKeys())
|
||||
}
|
||||
|
||||
@@ -4,12 +4,12 @@ import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||
"github.com/cosmos/cosmos-sdk/tests"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/ante"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
@@ -111,7 +111,7 @@ func TestBuildUnsignedTx(t *testing.T) {
|
||||
tx, err := tx.BuildUnsignedTx(txf, msg)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, tx)
|
||||
require.Empty(t, tx.GetTx().(ante.SigVerifiableTx).GetSignatures())
|
||||
require.Empty(t, tx.GetTx().(signing.SigVerifiableTx).GetSignatures())
|
||||
}
|
||||
|
||||
func TestSign(t *testing.T) {
|
||||
|
||||
@@ -25,7 +25,7 @@ type (
|
||||
// signatures, and provide canonical bytes to sign over. The transaction must
|
||||
// also know how to encode itself.
|
||||
TxBuilder interface {
|
||||
GetTx() sdk.Tx
|
||||
GetTx() signing.SigFeeMemoTx
|
||||
|
||||
SetMsgs(msgs ...sdk.Msg) error
|
||||
SetSignatures(signatures ...signingtypes.SignatureV2) error
|
||||
|
||||
Reference in New Issue
Block a user