refactor(auth): refactor auth/tx to use x/tx (#19224)

Co-authored-by: Facundo <facundomedica@gmail.com>
Co-authored-by: Matt Kocubinski <mkocubinski@gmail.com>
Co-authored-by: Facundo Medica <14063057+facundomedica@users.noreply.github.com>
This commit is contained in:
testinginprod 2024-02-21 10:36:21 +01:00 committed by GitHub
parent 0cf0c285ea
commit e846eca366
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
46 changed files with 2944 additions and 1831 deletions

View File

@ -930,19 +930,6 @@ func TestABCI_InvalidTransaction(t *testing.T) {
require.EqualValues(t, sdkerrors.ErrUnknownRequest.Codespace(), space, err)
require.EqualValues(t, sdkerrors.ErrUnknownRequest.ABCICode(), code, err)
}
// Transaction with an unregistered message
{
txBuilder := suite.txConfig.NewTxBuilder()
err = txBuilder.SetMsgs(&testdata.MsgCreateDog{})
require.NoError(t, err)
tx := txBuilder.GetTx()
_, _, err := suite.baseApp.SimDeliver(suite.txConfig.TxEncoder(), tx)
space, code, _ := errorsmod.ABCIInfo(err, false)
require.EqualValues(t, sdkerrors.ErrTxDecode.ABCICode(), code)
require.EqualValues(t, sdkerrors.ErrTxDecode.Codespace(), space)
}
}
func TestABCI_TxGasLimits(t *testing.T) {

View File

@ -469,15 +469,15 @@ func (s *ABCIUtilsTestSuite) TestDefaultProposalHandler_PriorityNonceMempoolTxSe
testTxs[i].size = int(cmttypes.ComputeProtoSizeForTxs([]cmttypes.Tx{bz}))
}
s.Require().Equal(testTxs[0].size, 111)
s.Require().Equal(testTxs[1].size, 121)
s.Require().Equal(testTxs[2].size, 112)
s.Require().Equal(testTxs[3].size, 112)
s.Require().Equal(testTxs[4].size, 195)
s.Require().Equal(testTxs[5].size, 205)
s.Require().Equal(testTxs[6].size, 196)
s.Require().Equal(testTxs[7].size, 196)
s.Require().Equal(testTxs[8].size, 196)
s.Require().Equal(180, testTxs[0].size)
s.Require().Equal(190, testTxs[1].size)
s.Require().Equal(181, testTxs[2].size)
s.Require().Equal(181, testTxs[3].size)
s.Require().Equal(263, testTxs[4].size)
s.Require().Equal(273, testTxs[5].size)
s.Require().Equal(264, testTxs[6].size)
s.Require().Equal(264, testTxs[7].size)
s.Require().Equal(264, testTxs[8].size)
testCases := map[string]struct {
ctx sdk.Context
@ -490,7 +490,7 @@ func (s *ABCIUtilsTestSuite) TestDefaultProposalHandler_PriorityNonceMempoolTxSe
ctx: s.ctx,
txInputs: []testTx{testTxs[0], testTxs[1], testTxs[2], testTxs[3]},
req: &abci.RequestPrepareProposal{
MaxTxBytes: 111 + 112,
MaxTxBytes: 180 + 181,
},
expectedTxs: []int{0, 3},
},
@ -498,7 +498,7 @@ func (s *ABCIUtilsTestSuite) TestDefaultProposalHandler_PriorityNonceMempoolTxSe
ctx: s.ctx,
txInputs: []testTx{testTxs[4], testTxs[5], testTxs[6], testTxs[7], testTxs[8]},
req: &abci.RequestPrepareProposal{
MaxTxBytes: 195 + 196,
MaxTxBytes: 263 + 264,
},
expectedTxs: []int{4, 8},
},
@ -507,7 +507,7 @@ func (s *ABCIUtilsTestSuite) TestDefaultProposalHandler_PriorityNonceMempoolTxSe
ctx: s.ctx,
txInputs: []testTx{testTxs[9], testTxs[10], testTxs[11]},
req: &abci.RequestPrepareProposal{
MaxTxBytes: 195 + 196,
MaxTxBytes: 263 + 264,
},
expectedTxs: []int{9},
},
@ -571,9 +571,7 @@ func marshalDelimitedFn(msg proto.Message) ([]byte, error) {
func buildMsg(t *testing.T, txConfig client.TxConfig, value []byte, secrets [][]byte, nonces []uint64) sdk.Tx {
t.Helper()
builder := txConfig.NewTxBuilder()
_ = builder.SetMsgs(
&baseapptestutil.MsgKeyValue{Value: value},
)
require.Equal(t, len(secrets), len(nonces))
signatures := make([]signingtypes.SignatureV2, 0)
for index, secret := range secrets {
@ -586,6 +584,14 @@ func buildMsg(t *testing.T, txConfig client.TxConfig, value []byte, secrets [][]
Data: &signingtypes.SingleSignatureData{},
})
}
_ = builder.SetMsgs(
&baseapptestutil.MsgKeyValue{
Signer: sdk.AccAddress(signatures[0].PubKey.Bytes()).String(),
Value: value,
},
)
setTxSignatureWithSecret(t, builder, signatures...)
return builder.GetTx()
}

View File

@ -352,6 +352,7 @@ func setFailOnHandler(t *testing.T, cfg client.TxConfig, tx signing.Tx, fail boo
msgs[i] = &baseapptestutil.MsgCounter{
Counter: msg.(*baseapptestutil.MsgCounter).Counter,
FailOnHandler: fail,
Signer: sdk.AccAddress("addr").String(),
}
}
@ -367,7 +368,9 @@ func wonkyMsg(t *testing.T, cfg client.TxConfig, tx signing.Tx) signing.Tx {
builder.SetMemo(tx.GetMemo())
msgs := tx.GetMsgs()
msgs = append(msgs, &baseapptestutil.MsgCounter2{})
msgs = append(msgs, &baseapptestutil.MsgCounter2{
Signer: sdk.AccAddress("wonky").String(),
})
err := builder.SetMsgs(msgs...)
require.NoError(t, err)

View File

@ -417,14 +417,16 @@ func TestPreprocessHook(t *testing.T) {
err = txfDirect.PreprocessTx(from, txb)
requireT.NoError(err)
hasExtOptsTx, ok := txb.(ante.HasExtensionOptionsTx)
tx := txb.GetTx()
hasExtOptsTx, ok := tx.(ante.HasExtensionOptionsTx)
requireT.True(ok)
hasOneExt := len(hasExtOptsTx.GetExtensionOptions()) == 1
requireT.True(hasOneExt)
opt := hasExtOptsTx.GetExtensionOptions()[0]
requireT.Equal(opt, extAny)
requireT.Equal(opt.TypeUrl, extAny.TypeUrl)
requireT.Equal(opt.Value, extAny.Value)
}
func testSigners(require *require.Assertions, tr signing.Tx, pks ...cryptotypes.PubKey) []signingtypes.SignatureV2 {

File diff suppressed because it is too large Load Diff

View File

@ -1466,6 +1466,7 @@ func (s *E2ETestSuite) TestSignWithMultiSignersAminoJSON() {
}
func (s *E2ETestSuite) TestAuxSigner() {
s.T().Skip("re-enable this when we bring back sign mode aux client testing")
require := s.Require()
val := s.network.GetValidators()[0]
val0Coin := sdk.NewCoin(fmt.Sprintf("%stoken", val.GetMoniker()), math.NewInt(10))

View File

@ -47,7 +47,7 @@ func BenchmarkTx(b *testing.B) {
val := s.network.GetValidators()[0]
txBuilder := mkTxBuilder(b, s)
// Convert the txBuilder to a tx.Tx.
protoTx, err := txBuilderToProtoTx(txBuilder)
protoTx, err := txBuilder.GetTx().(interface{ AsTx() (*tx.Tx, error) }).AsTx()
assert.NilError(b, err)
// Encode the txBuilder to txBytes.
txBytes, err := val.GetClientCtx().TxConfig.TxEncoder()(txBuilder.GetTx())

View File

@ -8,15 +8,14 @@ import (
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/math"
"cosmossdk.io/simapp"
authclient "cosmossdk.io/x/auth/client"
authtest "cosmossdk.io/x/auth/client/testutil"
"cosmossdk.io/x/auth/migrations/legacytx"
authtx "cosmossdk.io/x/auth/tx"
banktypes "cosmossdk.io/x/bank/types"
"github.com/cosmos/cosmos-sdk/client"
@ -30,7 +29,6 @@ import (
"github.com/cosmos/cosmos-sdk/testutil/network"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/query"
"github.com/cosmos/cosmos-sdk/types/tx"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
@ -158,7 +156,7 @@ func (s *E2ETestSuite) TestSimulateTx_GRPC() {
val := s.network.GetValidators()[0]
txBuilder := s.mkTxBuilder()
// Convert the txBuilder to a tx.Tx.
protoTx, err := txBuilderToProtoTx(txBuilder)
protoTx, err := txBuilder.GetTx().(interface{ AsTx() (*tx.Tx, error) }).AsTx()
s.Require().NoError(err)
// Encode the txBuilder to txBytes.
txBytes, err := val.GetClientCtx().TxConfig.TxEncoder()(txBuilder.GetTx())
@ -205,7 +203,7 @@ func (s *E2ETestSuite) TestSimulateTx_GRPCGateway() {
val := s.network.GetValidators()[0]
txBuilder := s.mkTxBuilder()
// Convert the txBuilder to a tx.Tx.
protoTx, err := txBuilderToProtoTx(txBuilder)
protoTx, err := txBuilder.GetTx().(interface{ AsTx() (*tx.Tx, error) }).AsTx()
s.Require().NoError(err)
// Encode the txBuilder to txBytes.
txBytes, err := val.GetClientCtx().TxConfig.TxEncoder()(txBuilder.GetTx())
@ -759,7 +757,7 @@ func (s *E2ETestSuite) TestGetBlockWithTxs_GRPCGateway() {
func (s *E2ETestSuite) TestTxEncode_GRPC() {
val := s.network.GetValidators()[0]
txBuilder := s.mkTxBuilder()
protoTx, err := txBuilderToProtoTx(txBuilder)
protoTx, err := txBuilder.GetTx().(interface{ AsTx() (*tx.Tx, error) }).AsTx()
s.Require().NoError(err)
testCases := []struct {
@ -796,7 +794,7 @@ func (s *E2ETestSuite) TestTxEncode_GRPC() {
func (s *E2ETestSuite) TestTxEncode_GRPCGateway() {
val := s.network.GetValidators()[0]
txBuilder := s.mkTxBuilder()
protoTx, err := txBuilderToProtoTx(txBuilder)
protoTx, err := txBuilder.GetTx().(interface{ AsTx() (*tx.Tx, error) }).AsTx()
s.Require().NoError(err)
testCases := []struct {
@ -835,7 +833,8 @@ func (s *E2ETestSuite) TestTxDecode_GRPC() {
val := s.network.GetValidators()[0]
txBuilder := s.mkTxBuilder()
encodedTx, err := val.GetClientCtx().TxConfig.TxEncoder()(txBuilder.GetTx())
goodTx := txBuilder.GetTx()
encodedTx, err := val.GetClientCtx().TxConfig.TxEncoder()(goodTx)
s.Require().NoError(err)
invalidTxBytes := append(encodedTx, byte(0o00))
@ -864,15 +863,35 @@ func (s *E2ETestSuite) TestTxDecode_GRPC() {
s.Require().NoError(err)
s.Require().NotEmpty(res.GetTx())
txb := authtx.WrapTx(res.Tx)
tx, err := val.GetClientCtx().TxConfig.TxEncoder()(txb.GetTx())
txb := wrapTx(s.T(), s.cfg.TxConfig, res.Tx)
gotTx := txb.GetTx()
gotEncoded, err := val.GetClientCtx().TxConfig.TxEncoder()(gotTx)
s.Require().NoError(err)
s.Require().Equal(encodedTx, tx)
s.Require().Equal(encodedTx, gotEncoded)
}
})
}
}
func wrapTx(t *testing.T, conf client.TxConfig, dTx *tx.Tx) client.TxBuilder {
t.Helper()
bodyBytes, err := dTx.Body.Marshal()
require.NoError(t, err)
authInfoBytes, err := dTx.AuthInfo.Marshal()
require.NoError(t, err)
rawTxBytes, err := (&tx.TxRaw{
BodyBytes: bodyBytes,
AuthInfoBytes: authInfoBytes,
Signatures: dTx.Signatures,
}).Marshal()
require.NoError(t, err)
dec, err := conf.TxDecoder()(rawTxBytes)
require.NoError(t, err)
bld, err := conf.WrapTxBuilder(dec)
require.NoError(t, err)
return bld
}
func (s *E2ETestSuite) TestTxDecode_GRPCGateway() {
val := s.network.GetValidators()[0]
txBuilder := s.mkTxBuilder()
@ -907,9 +926,10 @@ func (s *E2ETestSuite) TestTxDecode_GRPCGateway() {
err := val.GetClientCtx().Codec.UnmarshalJSON(res, &result)
s.Require().NoError(err)
txb := authtx.WrapTx(result.Tx)
txb := wrapTx(s.T(), s.cfg.TxConfig, result.Tx)
tx, err := val.GetClientCtx().TxConfig.TxEncoder()(txb.GetTx())
s.Require().NoError(err)
s.T().Log(len(tx), len(encodedTxBytes))
s.Require().Equal(encodedTxBytes, tx)
}
})
@ -1111,6 +1131,7 @@ func (s *E2ETestSuite) mkTxBuilder() client.TxBuilder {
txBuilder.SetFeeAmount(feeAmount)
txBuilder.SetGasLimit(gasLimit)
txBuilder.SetMemo("foobar")
txBuilder.SetFeePayer(val.GetAddress())
signers, err := txBuilder.GetTx().GetSigners()
s.Require().NoError(err)
s.Require().Equal([][]byte{val.GetAddress()}, signers)
@ -1128,23 +1149,3 @@ func (s *E2ETestSuite) mkTxBuilder() client.TxBuilder {
return txBuilder
}
// protoTxProvider is a type which can provide a proto transaction. It is a
// workaround to get access to the wrapper TxBuilder's method GetProtoTx().
// Deprecated: It's only used for testing the deprecated Simulate gRPC endpoint
// using a proto Tx field.
type protoTxProvider interface {
GetProtoTx() *tx.Tx
}
// txBuilderToProtoTx converts a txBuilder into a proto tx.Tx.
// Deprecated: It's used for testing the deprecated Simulate gRPC endpoint
// using a proto Tx field and for testing the TxEncode endpoint.
func txBuilderToProtoTx(txBuilder client.TxBuilder) (*tx.Tx, error) {
protoProvider, ok := txBuilder.(protoTxProvider)
if !ok {
return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidType, "expected proto tx builder, got %T", txBuilder)
}
return protoProvider.GetProtoTx(), nil
}

View File

@ -7,7 +7,7 @@ require (
cosmossdk.io/collections v0.4.0
cosmossdk.io/core v0.12.1-0.20231114100755-569e3ff6a0d7
cosmossdk.io/depinject v1.0.0-alpha.4
cosmossdk.io/errors v1.0.1
cosmossdk.io/errors v1.0.1 // indirect
cosmossdk.io/log v1.3.1
cosmossdk.io/math v1.2.0
cosmossdk.io/simapp v0.0.0-20230309163709-87da587416ba

View File

@ -982,6 +982,7 @@ func (s *CLITestSuite) TestSignWithMultiSignersAminoJSON() {
}
func (s *CLITestSuite) TestAuxSigner() {
s.T().Skip("re-enable this when we bring back sign mode aux client testing")
val0Coin := sdk.NewCoin("testtoken", math.NewInt(10))
testCases := []struct {

View File

@ -25,6 +25,7 @@ import (
_ "cosmossdk.io/x/staking"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/runtime"
@ -100,6 +101,7 @@ type suite struct {
AccountKeeper types.AccountKeeper
DistributionKeeper distrkeeper.Keeper
App *runtime.App
TxConfig client.TxConfig
}
func createTestSuite(t *testing.T, genesisAccounts []authtypes.GenesisAccount) suite {
@ -128,7 +130,7 @@ func createTestSuite(t *testing.T, genesisAccounts []authtypes.GenesisAccount) s
),
depinject.Supply(log.NewNopLogger()),
),
startupCfg, &res.BankKeeper, &res.AccountKeeper, &res.DistributionKeeper)
startupCfg, &res.BankKeeper, &res.AccountKeeper, &res.DistributionKeeper, &res.TxConfig)
res.App = app
@ -223,7 +225,7 @@ func TestMsgMultiSendWithAccounts(t *testing.T) {
},
{
desc: "wrong accNum should pass Simulate, but not Deliver",
msgs: []sdk.Msg{multiSendMsg1, multiSendMsg2},
msgs: []sdk.Msg{multiSendMsg1},
accNums: []uint64{1}, // wrong account number
accSeqs: []uint64{1},
expSimPass: true, // doesn't check signature
@ -251,19 +253,20 @@ func TestMsgMultiSendWithAccounts(t *testing.T) {
}
for _, tc := range testCases {
t.Logf("testing %s", tc.desc)
header := header.Info{Height: baseApp.LastBlockHeight() + 1}
txConfig := moduletestutil.MakeTestTxConfig()
_, _, err := simtestutil.SignCheckDeliver(t, txConfig, baseApp, header, tc.msgs, "", tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...)
if tc.expPass {
require.NoError(t, err)
} else {
require.Error(t, err)
}
t.Run(tc.desc, func(t *testing.T) {
header := header.Info{Height: baseApp.LastBlockHeight() + 1}
txConfig := moduletestutil.MakeTestTxConfig()
_, _, err := simtestutil.SignCheckDeliver(t, txConfig, baseApp, header, tc.msgs, "", tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...)
if tc.expPass {
require.NoError(t, err)
} else {
require.Error(t, err)
}
for _, eb := range tc.expectedBalances {
checkBalance(t, baseApp, eb.addr, eb.coins, s.BankKeeper)
}
for _, eb := range tc.expectedBalances {
checkBalance(t, baseApp, eb.addr, eb.coins, s.BankKeeper)
}
})
}
}
@ -438,8 +441,7 @@ func TestMsgSetSendEnabled(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.desc, func(tt *testing.T) {
header := header.Info{Height: s.App.LastBlockHeight() + 1}
txGen := moduletestutil.MakeTestTxConfig()
_, _, err = simtestutil.SignCheckDeliver(tt, txGen, s.App.BaseApp, header, tc.msgs, "", []uint64{0}, tc.accSeqs, tc.expSimPass, tc.expPass, priv1)
_, _, err = simtestutil.SignCheckDeliver(tt, s.TxConfig, s.App.BaseApp, header, tc.msgs, "", []uint64{0}, tc.accSeqs, tc.expSimPass, tc.expPass, priv1)
if len(tc.expInError) > 0 {
require.Error(tt, err)
for _, exp := range tc.expInError {

View File

@ -17,13 +17,13 @@ import (
stakingkeeper "cosmossdk.io/x/staking/keeper"
stakingtypes "cosmossdk.io/x/staking/types"
"github.com/cosmos/cosmos-sdk/client"
codecaddress "github.com/cosmos/cosmos-sdk/codec/address"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/cosmos/cosmos-sdk/testutil/configurator"
"github.com/cosmos/cosmos-sdk/testutil/sims"
sdk "github.com/cosmos/cosmos-sdk/types"
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
)
var (
@ -56,6 +56,7 @@ func TestSlashingMsgs(t *testing.T) {
stakingKeeper *stakingkeeper.Keeper
bankKeeper bankkeeper.Keeper
slashingKeeper keeper.Keeper
txConfig client.TxConfig
)
app, err := sims.SetupWithConfiguration(
@ -70,7 +71,7 @@ func TestSlashingMsgs(t *testing.T) {
),
depinject.Supply(log.NewNopLogger()),
),
startupCfg, &stakingKeeper, &bankKeeper, &slashingKeeper)
startupCfg, &stakingKeeper, &bankKeeper, &slashingKeeper, &txConfig)
require.NoError(t, err)
baseApp := app.BaseApp
@ -91,7 +92,6 @@ func TestSlashingMsgs(t *testing.T) {
require.NoError(t, err)
headerInfo := header.Info{Height: app.LastBlockHeight() + 1}
txConfig := moduletestutil.MakeTestTxConfig()
_, _, err = sims.SignCheckDeliver(t, txConfig, app.BaseApp, headerInfo, []sdk.Msg{createValidatorMsg}, "", []uint64{0}, []uint64{0}, true, true, priv1)
require.NoError(t, err)
require.True(t, sdk.Coins{genCoin.Sub(bondCoin)}.Equal(bankKeeper.GetAllBalances(ctxCheck, addr1)))

View File

@ -4,6 +4,7 @@ import (
"context"
"fmt"
"reflect"
"strings"
"testing"
"time"
@ -115,6 +116,19 @@ func TestAminoJSON_Equivalence(t *testing.T) {
msg := gen.Draw(t, "msg")
postFixPulsarMessage(msg)
// txBuilder.GetTx will fail if the msg has no signers
// so it does not make sense to run these cases, apparently.
signers, err := encCfg.TxConfig.SigningContext().GetSigners(msg)
if len(signers) == 0 {
// skip
return
}
if err != nil {
if strings.Contains(err.Error(), "empty address string is not allowed") {
return
}
require.NoError(t, err)
}
gogo := tt.Gogo
sanity := tt.Pulsar

View File

@ -1,6 +1,7 @@
package tx
import (
"strings"
"testing"
"github.com/cosmos/cosmos-proto/rapidproto"
@ -83,6 +84,16 @@ func TestDecode(t *testing.T) {
gen := rapidproto.MessageGenerator(tt.Pulsar, tt.Opts)
rapid.Check(t, func(t *rapid.T) {
msg := gen.Draw(t, "msg")
signers, err := encCfg.TxConfig.SigningContext().GetSigners(msg)
if len(signers) == 0 {
return
}
if err != nil {
if strings.Contains(err.Error(), "empty address string is not allowed") {
return
}
require.NoError(t, err)
}
gogo := tt.Gogo
sanity := tt.Pulsar

View File

@ -11,13 +11,13 @@ import (
"cosmossdk.io/log"
storetypes "cosmossdk.io/store/types"
authtx "cosmossdk.io/x/auth/tx"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/runtime"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/kv"
"github.com/cosmos/cosmos-sdk/types/module"
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
)
@ -51,11 +51,11 @@ func SetupSimulation(config simtypes.Config, dirPrefix, dbName string, verbose,
// SimulationOperations retrieves the simulation params from the provided file path
// and returns all the modules weighted operations
func SimulationOperations(app runtime.AppSimI, cdc codec.JSONCodec, config simtypes.Config) []simtypes.WeightedOperation {
func SimulationOperations(app runtime.AppSimI, cdc codec.Codec, config simtypes.Config) []simtypes.WeightedOperation {
simState := module.SimulationState{
AppParams: make(simtypes.AppParams),
Cdc: cdc,
TxConfig: moduletestutil.MakeTestTxConfig(),
TxConfig: authtx.NewTxConfig(cdc, authtx.DefaultSignModes), // TODO(tip): we should extract this from app
BondDenom: sdk.DefaultBondDenom,
}

View File

@ -9,6 +9,8 @@ import (
"github.com/stretchr/testify/suite"
protov2 "google.golang.org/protobuf/proto"
_ "cosmossdk.io/api/cosmos/counter/v1"
_ "cosmossdk.io/api/cosmos/crypto/secp256k1"
"cosmossdk.io/log"
"cosmossdk.io/x/auth/signing"

View File

@ -1,52 +0,0 @@
package registry
import (
"sync"
"github.com/cosmos/gogoproto/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
"cosmossdk.io/x/tx/signing"
)
var (
mergedRegistryOnce sync.Once
mergedRegistry *protoregistry.Files
_ signing.ProtoFileResolver = lazyProtoRegistry{}
)
// lazyProtoRegistry is a lazy loading wrapper around the global protobuf registry.
type lazyProtoRegistry struct{}
func getRegistry() *protoregistry.Files {
var err error
mergedRegistryOnce.Do(func() {
mergedRegistry, err = proto.MergedRegistry()
if err != nil {
panic(err)
}
})
return mergedRegistry
}
func (l lazyProtoRegistry) FindFileByPath(s string) (protoreflect.FileDescriptor, error) {
reg := getRegistry()
return reg.FindFileByPath(s)
}
func (l lazyProtoRegistry) FindDescriptorByName(name protoreflect.FullName) (protoreflect.Descriptor, error) {
reg := getRegistry()
return reg.FindDescriptorByName(name)
}
func (l lazyProtoRegistry) RangeFiles(f func(protoreflect.FileDescriptor) bool) {
reg := getRegistry()
reg.RangeFiles(f)
}
// MergedProtoRegistry returns a lazy loading wrapper around the global protobuf registry, a merged registry
// containing both gogo proto and pulsar types.
func MergedProtoRegistry() signing.ProtoFileResolver {
return lazyProtoRegistry{}
}

View File

@ -38,6 +38,8 @@ require (
)
require (
buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.32.0-20230509103710-5e5b9fdd0180.1 // indirect
buf.build/gen/go/tendermint/tendermint/protocolbuffers/go v1.32.0-20231117195010-33ed361a9051.1 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
github.com/99designs/keyring v1.2.2 // indirect
@ -167,6 +169,7 @@ replace github.com/cosmos/cosmos-sdk => ../../.
// TODO remove post spinning out all modules
replace (
cosmossdk.io/api => ../../api
cosmossdk.io/core => ../../core
cosmossdk.io/depinject => ../../depinject
cosmossdk.io/x/accounts => ../accounts

View File

@ -1,7 +1,9 @@
buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.32.0-20230509103710-5e5b9fdd0180.1 h1:7LKjxs607BNfGhtKLf+bi3SDJgpiGuTgOvemojsH8Hc=
buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.32.0-20230509103710-5e5b9fdd0180.1/go.mod h1:5GqIYthcy/ASmnKcaT26APpxMhZirnIHXHKki69zjWI=
buf.build/gen/go/tendermint/tendermint/protocolbuffers/go v1.32.0-20231117195010-33ed361a9051.1 h1:VooqQ3rklp3PwMTAE890M76w/8Z01OPa7RdgU9posFE=
buf.build/gen/go/tendermint/tendermint/protocolbuffers/go v1.32.0-20231117195010-33ed361a9051.1/go.mod h1:9KmeMJUsSG3IiIwK63Lh1ipZJrwd7KHrWZseJeHukcs=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cosmossdk.io/api v0.7.3-0.20231113122742-912390d5fc4a h1:Zr++x1RCJWi+K8bTZsQKdjtL4SzyHBLGM3Fcn75iWf0=
cosmossdk.io/api v0.7.3-0.20231113122742-912390d5fc4a/go.mod h1:7B/5XWh1HYwJk3DzWeNoxOSI+nGx1m5UyYfHLFuKzkw=
cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s=
cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0=
cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0=

View File

@ -1,130 +0,0 @@
package tx
import (
"google.golang.org/protobuf/types/known/anypb"
basev1beta1 "cosmossdk.io/api/cosmos/base/v1beta1"
multisigv1beta1 "cosmossdk.io/api/cosmos/crypto/multisig/v1beta1"
signingv1beta1 "cosmossdk.io/api/cosmos/tx/signing/v1beta1"
txv1beta1 "cosmossdk.io/api/cosmos/tx/v1beta1"
txsigning "cosmossdk.io/x/tx/signing"
"github.com/cosmos/cosmos-sdk/types/tx"
)
// GetSigningTxData returns an x/tx/signing.TxData representation of a transaction for use in the signing
// API defined in x/tx. The reason for all of this conversion is that x/tx depends on the protoreflect API
// defined in google.golang.org/protobuf while x/auth/tx depends on the legacy proto API defined in
// github.com/gogo/protobuf and the downstream SDK fork of that library, github.com/cosmos/gogoproto.
// Therefore we need to convert between the two APIs.
func (w *wrapper) GetSigningTxData() txsigning.TxData {
body := w.tx.Body
authInfo := w.tx.AuthInfo
msgs := make([]*anypb.Any, len(body.Messages))
for i, msg := range body.Messages {
msgs[i] = &anypb.Any{
TypeUrl: msg.TypeUrl,
Value: msg.Value,
}
}
extOptions := make([]*anypb.Any, len(body.ExtensionOptions))
for i, extOption := range body.ExtensionOptions {
extOptions[i] = &anypb.Any{
TypeUrl: extOption.TypeUrl,
Value: extOption.Value,
}
}
nonCriticalExtOptions := make([]*anypb.Any, len(body.NonCriticalExtensionOptions))
for i, extOption := range body.NonCriticalExtensionOptions {
nonCriticalExtOptions[i] = &anypb.Any{
TypeUrl: extOption.TypeUrl,
Value: extOption.Value,
}
}
feeCoins := authInfo.Fee.Amount
feeAmount := make([]*basev1beta1.Coin, len(feeCoins))
for i, coin := range feeCoins {
feeAmount[i] = &basev1beta1.Coin{
Denom: coin.Denom,
Amount: coin.Amount.String(),
}
}
txSignerInfos := make([]*txv1beta1.SignerInfo, len(authInfo.SignerInfos))
for i, signerInfo := range authInfo.SignerInfos {
modeInfo := &txv1beta1.ModeInfo{}
adaptModeInfo(signerInfo.ModeInfo, modeInfo)
txSignerInfo := &txv1beta1.SignerInfo{
PublicKey: &anypb.Any{
TypeUrl: signerInfo.PublicKey.TypeUrl,
Value: signerInfo.PublicKey.Value,
},
Sequence: signerInfo.Sequence,
ModeInfo: modeInfo,
}
txSignerInfos[i] = txSignerInfo
}
txAuthInfo := &txv1beta1.AuthInfo{
SignerInfos: txSignerInfos,
Fee: &txv1beta1.Fee{
Amount: feeAmount,
GasLimit: authInfo.Fee.GasLimit,
Payer: authInfo.Fee.Payer,
Granter: authInfo.Fee.Granter,
},
}
txBody := &txv1beta1.TxBody{
Messages: msgs,
Memo: body.Memo,
TimeoutHeight: body.TimeoutHeight,
ExtensionOptions: extOptions,
NonCriticalExtensionOptions: nonCriticalExtOptions,
}
txData := txsigning.TxData{
AuthInfo: txAuthInfo,
AuthInfoBytes: w.getAuthInfoBytes(),
Body: txBody,
BodyBytes: w.getBodyBytes(),
}
return txData
}
func adaptModeInfo(legacy *tx.ModeInfo, res *txv1beta1.ModeInfo) {
// handle nil modeInfo. this is permissible through the code path:
// https://github.com/cosmos/cosmos-sdk/blob/4a6a1e3cb8de459891cb0495052589673d14ef51/x/auth/tx/builder.go#L295
// -> https://github.com/cosmos/cosmos-sdk/blob/b7841e3a76a38d069c1b9cb3d48368f7a67e9c26/x/auth/tx/sigs.go#L15-L17
// when signature.Data is nil.
if legacy == nil {
return
}
switch mi := legacy.Sum.(type) {
case *tx.ModeInfo_Single_:
res.Sum = &txv1beta1.ModeInfo_Single_{
Single: &txv1beta1.ModeInfo_Single{
Mode: signingv1beta1.SignMode(legacy.GetSingle().Mode),
},
}
case *tx.ModeInfo_Multi_:
multiModeInfos := legacy.GetMulti().ModeInfos
modeInfos := make([]*txv1beta1.ModeInfo, len(multiModeInfos))
for _, modeInfo := range multiModeInfos {
adaptModeInfo(modeInfo, &txv1beta1.ModeInfo{})
}
res.Sum = &txv1beta1.ModeInfo_Multi_{
Multi: &txv1beta1.ModeInfo_Multi{
Bitarray: &multisigv1beta1.CompactBitArray{
Elems: mi.Multi.Bitarray.Elems,
ExtraBitsStored: mi.Multi.Bitarray.ExtraBitsStored,
},
ModeInfos: modeInfos,
},
}
}
}

View File

@ -38,6 +38,7 @@ var (
// Then it tests integrating the 2 AuxSignerData into a
// client.TxBuilder created by the fee payer.
func TestBuilderWithAux(t *testing.T) {
t.Skip("restore when we re-enable aux on the TX builder")
encodingConfig := moduletestutil.MakeTestEncodingConfig()
interfaceRegistry := encodingConfig.InterfaceRegistry
txConfig := encodingConfig.TxConfig

View File

@ -1,352 +1,204 @@
package tx
import (
"bytes"
"fmt"
"github.com/cosmos/gogoproto/proto"
protov2 "google.golang.org/protobuf/proto"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/x/auth/ante"
authsigning "cosmossdk.io/x/auth/signing"
basev1beta1 "cosmossdk.io/api/cosmos/base/v1beta1"
multisigv1beta1 "cosmossdk.io/api/cosmos/crypto/multisig/v1beta1"
signingv1beta1 "cosmossdk.io/api/cosmos/tx/signing/v1beta1"
txv1beta1 "cosmossdk.io/api/cosmos/tx/v1beta1"
"cosmossdk.io/core/address"
authsign "cosmossdk.io/x/auth/signing"
"cosmossdk.io/x/tx/decode"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/tx"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
)
// wrapper is a wrapper around the tx.Tx proto.Message which retain the raw
// body and auth_info bytes.
type wrapper struct {
cdc codec.Codec
tx *tx.Tx
// bodyBz represents the protobuf encoding of TxBody. This should be encoding
// from the client using TxRaw if the tx was decoded from the wire
bodyBz []byte
// authInfoBz represents the protobuf encoding of TxBody. This should be encoding
// from the client using TxRaw if the tx was decoded from the wire
authInfoBz []byte
txBodyHasUnknownNonCriticals bool
signers [][]byte
msgsV2 []protov2.Message
}
var (
_ authsigning.Tx = &wrapper{}
_ client.TxBuilder = &wrapper{}
_ ante.HasExtensionOptionsTx = &wrapper{}
_ ExtensionOptionsTxBuilder = &wrapper{}
_ client.TxBuilder = &builder{}
_ ExtensionOptionsTxBuilder = &builder{}
)
// ExtensionOptionsTxBuilder defines a TxBuilder that can also set extensions.
type ExtensionOptionsTxBuilder interface {
client.TxBuilder
SetExtensionOptions(...*codectypes.Any)
SetNonCriticalExtensionOptions(...*codectypes.Any)
func newBuilder(addressCodec address.Codec, decoder *decode.Decoder, codec codec.BinaryCodec) *builder {
return &builder{addressCodec: addressCodec, decoder: decoder, codec: codec}
}
func newBuilder(cdc codec.Codec) *wrapper {
w := &wrapper{
cdc: cdc,
tx: &tx.Tx{
Body: &tx.TxBody{},
AuthInfo: &tx.AuthInfo{
Fee: &tx.Fee{},
},
},
}
return w
}
func newBuilderFromDecodedTx(addrCodec address.Codec, decoder *decode.Decoder, codec codec.BinaryCodec, decoded *gogoTxWrapper) (*builder, error) {
signatures := make([][]byte, len(decoded.decodedTx.Tx.Signatures))
copy(signatures, decoded.decodedTx.Tx.Signatures)
func (w *wrapper) GetMsgs() []sdk.Msg {
return w.tx.GetMsgs()
}
func (w *wrapper) GetMsgsV2() ([]protov2.Message, error) {
if w.msgsV2 == nil {
err := w.initSignersAndMsgsV2()
if err != nil {
return nil, err
sigInfos := make([]*tx.SignerInfo, len(decoded.decodedTx.Tx.AuthInfo.SignerInfos))
for i, sigInfo := range decoded.decodedTx.Tx.AuthInfo.SignerInfos {
modeInfoV1 := new(tx.ModeInfo)
fromV2ModeInfo(sigInfo.ModeInfo, modeInfoV1)
sigInfos[i] = &tx.SignerInfo{
PublicKey: intoAnyV1([]*anypb.Any{sigInfo.PublicKey})[0],
ModeInfo: modeInfoV1,
Sequence: sigInfo.Sequence,
}
}
return w.msgsV2, nil
var payer []byte
if decoded.feePayer != nil {
payer = decoded.feePayer
}
return &builder{
addressCodec: addrCodec,
decoder: decoder,
codec: codec,
msgs: decoded.msgsV1,
timeoutHeight: decoded.GetTimeoutHeight(),
granter: decoded.FeeGranter(),
payer: payer,
unordered: decoded.GetUnordered(),
memo: decoded.GetMemo(),
gasLimit: decoded.GetGas(),
fees: decoded.GetFee(),
signerInfos: sigInfos,
signatures: signatures,
extensionOptions: decoded.GetExtensionOptions(),
nonCriticalExtensionOptions: decoded.GetNonCriticalExtensionOptions(),
}, nil
}
func (w *wrapper) ValidateBasic() error {
if w.tx == nil {
return fmt.Errorf("bad Tx")
}
type builder struct {
addressCodec address.Codec
decoder *decode.Decoder
codec codec.BinaryCodec
if err := w.tx.ValidateBasic(); err != nil {
return err
}
msgs []sdk.Msg
timeoutHeight uint64
granter []byte
payer []byte
unordered bool
memo string
gasLimit uint64
fees sdk.Coins
signerInfos []*tx.SignerInfo
signatures [][]byte
sigs := w.tx.Signatures
signers, err := w.GetSigners()
extensionOptions []*codectypes.Any
nonCriticalExtensionOptions []*codectypes.Any
}
func (w *builder) GetTx() authsign.Tx {
buildTx, err := w.getTx()
if err != nil {
return err
panic(err)
}
if len(sigs) != len(signers) {
return errorsmod.Wrapf(
sdkerrors.ErrUnauthorized,
"wrong number of signers; expected %d, got %d", len(signers), len(sigs),
)
}
return nil
return buildTx
}
func (w *wrapper) getBodyBytes() []byte {
if len(w.bodyBz) == 0 {
// if bodyBz is empty, then marshal the body. bodyBz will generally
// be set to nil whenever SetBody is called so the result of calling
// this method should always return the correct bytes. Note that after
// decoding bodyBz is derived from TxRaw so that it matches what was
// transmitted over the wire
var err error
w.bodyBz, err = proto.Marshal(w.tx.Body)
if err != nil {
panic(err)
}
}
return w.bodyBz
var marshalOption = proto.MarshalOptions{
Deterministic: true,
}
func (w *wrapper) getAuthInfoBytes() []byte {
if len(w.authInfoBz) == 0 {
// if authInfoBz is empty, then marshal the body. authInfoBz will generally
// be set to nil whenever SetAuthInfo is called so the result of calling
// this method should always return the correct bytes. Note that after
// decoding authInfoBz is derived from TxRaw so that it matches what was
// transmitted over the wire
var err error
w.authInfoBz, err = proto.Marshal(w.tx.AuthInfo)
if err != nil {
panic(err)
}
}
return w.authInfoBz
}
func (w *wrapper) initSignersAndMsgsV2() error {
var err error
w.signers, w.msgsV2, err = w.tx.GetSigners(w.cdc)
return err
}
func (w *wrapper) GetSigners() ([][]byte, error) {
if w.signers == nil {
err := w.initSignersAndMsgsV2()
if err != nil {
return nil, err
}
}
return w.signers, nil
}
func (w *wrapper) GetPubKeys() ([]cryptotypes.PubKey, error) {
signerInfos := w.tx.AuthInfo.SignerInfos
pks := make([]cryptotypes.PubKey, len(signerInfos))
for i, si := range signerInfos {
// NOTE: it is okay to leave this nil if there is no PubKey in the SignerInfo.
// PubKey's can be left unset in SignerInfo.
if si.PublicKey == nil {
continue
}
pkAny := si.PublicKey.GetCachedValue()
pk, ok := pkAny.(cryptotypes.PubKey)
if ok {
pks[i] = pk
} else {
return nil, errorsmod.Wrapf(sdkerrors.ErrLogic, "Expecting PubKey, got: %T", pkAny)
}
}
return pks, nil
}
func (w *wrapper) GetGas() uint64 {
return w.tx.AuthInfo.Fee.GasLimit
}
func (w *wrapper) GetFee() sdk.Coins {
return w.tx.AuthInfo.Fee.Amount
}
func (w *wrapper) FeePayer() []byte {
feePayer := w.tx.AuthInfo.Fee.Payer
if feePayer != "" {
feePayerAddr, err := w.cdc.InterfaceRegistry().SigningContext().AddressCodec().StringToBytes(feePayer)
if err != nil {
panic(err)
}
return feePayerAddr
}
// use first signer as default if no payer specified
signers, err := w.GetSigners()
if err != nil {
return nil
}
return signers[0]
}
func (w *wrapper) FeeGranter() []byte {
return w.tx.FeeGranter(w.cdc)
}
func (w *wrapper) GetMemo() string {
return w.tx.Body.Memo
}
// GetTimeoutHeight returns the transaction's timeout height (if set).
func (w *wrapper) GetTimeoutHeight() uint64 {
return w.tx.Body.TimeoutHeight
}
// GetUnordered returns the transaction's unordered field (if set).
func (w *wrapper) GetUnordered() bool {
return w.tx.Body.Unordered
}
func (w *wrapper) GetSignaturesV2() ([]signing.SignatureV2, error) {
signerInfos := w.tx.AuthInfo.SignerInfos
sigs := w.tx.Signatures
pubKeys, err := w.GetPubKeys()
func (w *builder) getTx() (*gogoTxWrapper, error) {
anyMsgs, err := msgsV1toAnyV2(w.msgs)
if err != nil {
return nil, err
}
n := len(signerInfos)
res := make([]signing.SignatureV2, n)
for i, si := range signerInfos {
// handle nil signatures (in case of simulation)
if si.ModeInfo == nil {
res[i] = signing.SignatureV2{
PubKey: pubKeys[i],
}
} else {
var err error
sigData, err := ModeInfoAndSigToSignatureData(si.ModeInfo, sigs[i])
if err != nil {
return nil, err
}
// sequence number is functionally a transaction nonce and referred to as such in the SDK
nonce := si.GetSequence()
res[i] = signing.SignatureV2{
PubKey: pubKeys[i],
Data: sigData,
Sequence: nonce,
}
}
body := &txv1beta1.TxBody{
Messages: anyMsgs,
Memo: w.memo,
TimeoutHeight: w.timeoutHeight,
Unordered: w.unordered,
ExtensionOptions: intoAnyV2(w.extensionOptions),
NonCriticalExtensionOptions: intoAnyV2(w.nonCriticalExtensionOptions),
}
return res, nil
fee, err := w.getFee()
if err != nil {
return nil, fmt.Errorf("unable to parse fee: %w", err)
}
authInfo := &txv1beta1.AuthInfo{
SignerInfos: intoV2SignerInfo(w.signerInfos),
Fee: fee,
Tip: nil, // deprecated
}
bodyBytes, err := marshalOption.Marshal(body)
if err != nil {
return nil, err
}
authInfoBytes, err := marshalOption.Marshal(authInfo)
if err != nil {
return nil, err
}
txRawBytes, err := marshalOption.Marshal(&txv1beta1.TxRaw{
BodyBytes: bodyBytes,
AuthInfoBytes: authInfoBytes,
Signatures: w.signatures,
})
if err != nil {
return nil, err
}
decodedTx, err := w.decoder.Decode(txRawBytes)
if err != nil {
return nil, err
}
return newWrapperFromDecodedTx(w.addressCodec, w.codec, decodedTx)
}
func (w *wrapper) SetMsgs(msgs ...sdk.Msg) error {
anys, err := tx.SetMsgs(msgs)
if err != nil {
return err
func msgsV1toAnyV2(msgs []sdk.Msg) ([]*anypb.Any, error) {
anys := make([]*codectypes.Any, len(msgs))
for i, msg := range msgs {
anyMsg, err := codectypes.NewAnyWithValue(msg)
if err != nil {
return nil, err
}
anys[i] = anyMsg
}
w.tx.Body.Messages = anys
return intoAnyV2(anys), nil
}
// set bodyBz to nil because the cached bodyBz no longer matches tx.Body
w.bodyBz = nil
// reset signers and msgsV2
w.signers = nil
w.msgsV2 = nil
func intoV2Fees(fees sdk.Coins) []*basev1beta1.Coin {
coins := make([]*basev1beta1.Coin, len(fees))
for i, c := range fees {
coins[i] = &basev1beta1.Coin{
Denom: c.Denom,
Amount: c.Amount.String(),
}
}
return coins
}
func (w *builder) SetMsgs(msgs ...sdk.Msg) error {
w.msgs = msgs
return nil
}
// SetTimeoutHeight sets the transaction's height timeout.
func (w *wrapper) SetTimeoutHeight(height uint64) {
w.tx.Body.TimeoutHeight = height
func (w *builder) SetTimeoutHeight(height uint64) { w.timeoutHeight = height }
// set bodyBz to nil because the cached bodyBz no longer matches tx.Body
w.bodyBz = nil
}
func (w *builder) SetUnordered(v bool) { w.unordered = v }
func (w *wrapper) SetUnordered(v bool) {
w.tx.Body.Unordered = v
func (w *builder) SetMemo(memo string) { w.memo = memo }
// set bodyBz to nil because the cached bodyBz no longer matches tx.Body
w.bodyBz = nil
}
func (w *builder) SetGasLimit(limit uint64) { w.gasLimit = limit }
func (w *wrapper) SetMemo(memo string) {
w.tx.Body.Memo = memo
func (w *builder) SetFeeAmount(coins sdk.Coins) { w.fees = coins }
// set bodyBz to nil because the cached bodyBz no longer matches tx.Body
w.bodyBz = nil
}
func (w *builder) SetFeePayer(feePayer sdk.AccAddress) { w.payer = feePayer }
func (w *wrapper) SetGasLimit(limit uint64) {
if w.tx.AuthInfo.Fee == nil {
w.tx.AuthInfo.Fee = &tx.Fee{}
}
func (w *builder) SetFeeGranter(feeGranter sdk.AccAddress) { w.granter = feeGranter }
w.tx.AuthInfo.Fee.GasLimit = limit
// set authInfoBz to nil because the cached authInfoBz no longer matches tx.AuthInfo
w.authInfoBz = nil
}
func (w *wrapper) SetFeeAmount(coins sdk.Coins) {
if w.tx.AuthInfo.Fee == nil {
w.tx.AuthInfo.Fee = &tx.Fee{}
}
w.tx.AuthInfo.Fee.Amount = coins
// set authInfoBz to nil because the cached authInfoBz no longer matches tx.AuthInfo
w.authInfoBz = nil
}
func (w *wrapper) SetFeePayer(feePayer sdk.AccAddress) {
if w.tx.AuthInfo.Fee == nil {
w.tx.AuthInfo.Fee = &tx.Fee{}
}
w.tx.AuthInfo.Fee.Payer = feePayer.String()
// set authInfoBz to nil because the cached authInfoBz no longer matches tx.AuthInfo
w.authInfoBz = nil
}
func (w *wrapper) SetFeeGranter(feeGranter sdk.AccAddress) {
if w.tx.AuthInfo.Fee == nil {
w.tx.AuthInfo.Fee = &tx.Fee{}
}
w.tx.AuthInfo.Fee.Granter = feeGranter.String()
// set authInfoBz to nil because the cached authInfoBz no longer matches tx.AuthInfo
w.authInfoBz = nil
}
func (w *wrapper) SetSignatures(signatures ...signing.SignatureV2) error {
func (w *builder) SetSignatures(signatures ...signing.SignatureV2) error {
n := len(signatures)
signerInfos := make([]*tx.SignerInfo, n)
rawSigs := make([][]byte, n)
@ -377,181 +229,137 @@ func (w *wrapper) SetSignatures(signatures ...signing.SignatureV2) error {
return nil
}
func (w *wrapper) setSignerInfos(infos []*tx.SignerInfo) {
w.tx.AuthInfo.SignerInfos = infos
// set authInfoBz to nil because the cached authInfoBz no longer matches tx.AuthInfo
w.authInfoBz = nil
func (w *builder) setSignerInfos(infos []*tx.SignerInfo) { w.signerInfos = infos }
func (w *builder) setSignatures(sigs [][]byte) { w.signatures = sigs }
func (w *builder) SetExtensionOptions(extOpts ...*codectypes.Any) { w.extensionOptions = extOpts }
func (w *builder) SetNonCriticalExtensionOptions(extOpts ...*codectypes.Any) {
w.nonCriticalExtensionOptions = extOpts
}
func (w *wrapper) setSignerInfoAtIndex(index int, info *tx.SignerInfo) {
signers, err := w.GetSigners()
if err != nil {
panic(err)
}
func (w *builder) AddAuxSignerData(data tx.AuxSignerData) error { return fmt.Errorf("not supported") }
if w.tx.AuthInfo.SignerInfos == nil {
w.tx.AuthInfo.SignerInfos = make([]*tx.SignerInfo, len(signers))
}
w.tx.AuthInfo.SignerInfos[index] = info
// set authInfoBz to nil because the cached authInfoBz no longer matches tx.AuthInfo
w.authInfoBz = nil
}
func (w *wrapper) setSignatures(sigs [][]byte) {
w.tx.Signatures = sigs
}
func (w *wrapper) setSignatureAtIndex(index int, sig []byte) {
signers, err := w.GetSigners()
if err != nil {
panic(err)
}
if w.tx.Signatures == nil {
w.tx.Signatures = make([][]byte, len(signers))
}
w.tx.Signatures[index] = sig
}
func (w *wrapper) GetTx() authsigning.Tx {
return w
}
func (w *wrapper) GetProtoTx() *tx.Tx {
return w.tx
}
func (w *wrapper) GetRawTx() *tx.TxRaw {
return &tx.TxRaw{
BodyBytes: w.bodyBz,
AuthInfoBytes: w.authInfoBz,
Signatures: w.tx.Signatures,
}
}
// Deprecated: AsAny extracts proto Tx and wraps it into Any.
// NOTE: You should probably use `GetProtoTx` if you want to serialize the transaction.
func (w *wrapper) AsAny() *codectypes.Any {
return codectypes.UnsafePackAny(w.tx)
}
// WrapTx creates a TxBuilder wrapper around a tx.Tx proto message.
func WrapTx(protoTx *tx.Tx) client.TxBuilder {
return &wrapper{
tx: protoTx,
}
}
func (w *wrapper) GetExtensionOptions() []*codectypes.Any {
return w.tx.Body.ExtensionOptions
}
func (w *wrapper) GetNonCriticalExtensionOptions() []*codectypes.Any {
return w.tx.Body.NonCriticalExtensionOptions
}
func (w *wrapper) SetExtensionOptions(extOpts ...*codectypes.Any) {
w.tx.Body.ExtensionOptions = extOpts
w.bodyBz = nil
}
func (w *wrapper) SetNonCriticalExtensionOptions(extOpts ...*codectypes.Any) {
w.tx.Body.NonCriticalExtensionOptions = extOpts
w.bodyBz = nil
}
func (w *wrapper) AddAuxSignerData(data tx.AuxSignerData) error {
err := data.ValidateBasic()
if err != nil {
return err
}
w.bodyBz = data.SignDoc.BodyBytes
var body tx.TxBody
err = w.cdc.Unmarshal(w.bodyBz, &body)
if err != nil {
return err
}
if w.tx.Body.Memo != "" && w.tx.Body.Memo != body.Memo {
return sdkerrors.ErrInvalidRequest.Wrapf("TxBuilder has memo %s, got %s in AuxSignerData", w.tx.Body.Memo, body.Memo)
}
if w.tx.Body.TimeoutHeight != 0 && w.tx.Body.TimeoutHeight != body.TimeoutHeight {
return sdkerrors.ErrInvalidRequest.Wrapf("TxBuilder has timeout height %d, got %d in AuxSignerData", w.tx.Body.TimeoutHeight, body.TimeoutHeight)
}
if len(w.tx.Body.ExtensionOptions) != 0 {
if len(w.tx.Body.ExtensionOptions) != len(body.ExtensionOptions) {
return sdkerrors.ErrInvalidRequest.Wrapf("TxBuilder has %d extension options, got %d in AuxSignerData", len(w.tx.Body.ExtensionOptions), len(body.ExtensionOptions))
}
for i, o := range w.tx.Body.ExtensionOptions {
if !o.Equal(body.ExtensionOptions[i]) {
return sdkerrors.ErrInvalidRequest.Wrapf("TxBuilder has extension option %+v at index %d, got %+v in AuxSignerData", o, i, body.ExtensionOptions[i])
}
}
}
if len(w.tx.Body.NonCriticalExtensionOptions) != 0 {
if len(w.tx.Body.NonCriticalExtensionOptions) != len(body.NonCriticalExtensionOptions) {
return sdkerrors.ErrInvalidRequest.Wrapf("TxBuilder has %d non-critical extension options, got %d in AuxSignerData", len(w.tx.Body.NonCriticalExtensionOptions), len(body.NonCriticalExtensionOptions))
}
for i, o := range w.tx.Body.NonCriticalExtensionOptions {
if !o.Equal(body.NonCriticalExtensionOptions[i]) {
return sdkerrors.ErrInvalidRequest.Wrapf("TxBuilder has non-critical extension option %+v at index %d, got %+v in AuxSignerData", o, i, body.NonCriticalExtensionOptions[i])
}
}
}
if len(w.tx.Body.Messages) != 0 {
if len(w.tx.Body.Messages) != len(body.Messages) {
return sdkerrors.ErrInvalidRequest.Wrapf("TxBuilder has %d Msgs, got %d in AuxSignerData", len(w.tx.Body.Messages), len(body.Messages))
}
for i, o := range w.tx.Body.Messages {
if !o.Equal(body.Messages[i]) {
return sdkerrors.ErrInvalidRequest.Wrapf("TxBuilder has Msg %+v at index %d, got %+v in AuxSignerData", o, i, body.Messages[i])
}
}
}
w.SetMemo(body.Memo)
w.SetTimeoutHeight(body.TimeoutHeight)
w.SetExtensionOptions(body.ExtensionOptions...)
w.SetNonCriticalExtensionOptions(body.NonCriticalExtensionOptions...)
msgs := make([]sdk.Msg, len(body.Messages))
for i, msgAny := range body.Messages {
msgs[i] = msgAny.GetCachedValue().(sdk.Msg)
}
err = w.SetMsgs(msgs...)
if err != nil {
return err
}
// Get the aux signer's index in GetSigners.
signerIndex := -1
signers, err := w.GetSigners()
if err != nil {
return err
}
for i, signer := range signers {
addrBz, err := w.cdc.InterfaceRegistry().SigningContext().AddressCodec().StringToBytes(data.Address)
func (w *builder) getFee() (fee *txv1beta1.Fee, err error) {
granterStr := ""
if w.granter != nil {
granterStr, err = w.addressCodec.BytesToString(w.granter)
if err != nil {
return err
}
if bytes.Equal(signer, addrBz) {
signerIndex = i
return nil, err
}
}
if signerIndex < 0 {
return sdkerrors.ErrLogic.Wrapf("address %s is not a signer", data.Address)
payerStr := ""
if w.payer != nil {
payerStr, err = w.addressCodec.BytesToString(w.payer)
if err != nil {
return nil, err
}
}
fee = &txv1beta1.Fee{
Amount: intoV2Fees(w.fees),
GasLimit: w.gasLimit,
Payer: payerStr,
Granter: granterStr,
}
w.setSignerInfoAtIndex(signerIndex, &tx.SignerInfo{
PublicKey: data.SignDoc.PublicKey,
ModeInfo: &tx.ModeInfo{Sum: &tx.ModeInfo_Single_{Single: &tx.ModeInfo_Single{Mode: data.Mode}}},
Sequence: data.SignDoc.Sequence,
})
w.setSignatureAtIndex(signerIndex, data.Sig)
return nil
return fee, nil
}
func intoAnyV2(v1s []*codectypes.Any) []*anypb.Any {
v2s := make([]*anypb.Any, len(v1s))
for i, v1 := range v1s {
v2s[i] = &anypb.Any{
TypeUrl: v1.TypeUrl,
Value: v1.Value,
}
}
return v2s
}
func intoV2SignerInfo(v1s []*tx.SignerInfo) []*txv1beta1.SignerInfo {
v2s := make([]*txv1beta1.SignerInfo, len(v1s))
for i, v1 := range v1s {
modeInfoV2 := new(txv1beta1.ModeInfo)
intoV2ModeInfo(v1.ModeInfo, modeInfoV2)
v2 := &txv1beta1.SignerInfo{
PublicKey: intoAnyV2([]*codectypes.Any{v1.PublicKey})[0],
ModeInfo: modeInfoV2,
Sequence: v1.Sequence,
}
v2s[i] = v2
}
return v2s
}
func intoV2ModeInfo(v1 *tx.ModeInfo, v2 *txv1beta1.ModeInfo) {
// handle nil modeInfo. this is permissible through the code path:
// https://github.com/cosmos/cosmos-sdk/blob/4a6a1e3cb8de459891cb0495052589673d14ef51/x/auth/tx/builder.go#L295
// -> https://github.com/cosmos/cosmos-sdk/blob/b7841e3a76a38d069c1b9cb3d48368f7a67e9c26/x/auth/tx/sigs.go#L15-L17
// when signature.Data is nil.
if v1 == nil {
return
}
switch mi := v1.Sum.(type) {
case *tx.ModeInfo_Single_:
v2.Sum = &txv1beta1.ModeInfo_Single_{
Single: &txv1beta1.ModeInfo_Single{
Mode: signingv1beta1.SignMode(v1.GetSingle().Mode),
},
}
case *tx.ModeInfo_Multi_:
multiModeInfos := v1.GetMulti().ModeInfos
modeInfos := make([]*txv1beta1.ModeInfo, len(multiModeInfos))
for i, modeInfo := range multiModeInfos {
modeInfos[i] = new(txv1beta1.ModeInfo)
intoV2ModeInfo(modeInfo, modeInfos[i])
}
v2.Sum = &txv1beta1.ModeInfo_Multi_{
Multi: &txv1beta1.ModeInfo_Multi{
Bitarray: &multisigv1beta1.CompactBitArray{
Elems: mi.Multi.Bitarray.Elems,
ExtraBitsStored: mi.Multi.Bitarray.ExtraBitsStored,
},
ModeInfos: modeInfos,
},
}
}
}
func fromV2ModeInfo(v2 *txv1beta1.ModeInfo, v1 *tx.ModeInfo) {
// Check if v2 is nil. If so, return as there's nothing to convert.
if v2 == nil {
return
}
switch mi := v2.Sum.(type) {
case *txv1beta1.ModeInfo_Single_:
// Convert from v2 single mode to v1 single mode
v1.Sum = &tx.ModeInfo_Single_{
Single: &tx.ModeInfo_Single{
Mode: signing.SignMode(mi.Single.Mode),
},
}
case *txv1beta1.ModeInfo_Multi_:
// Convert from v2 multi mode to v1 multi mode
multiModeInfos := mi.Multi.ModeInfos
modeInfos := make([]*tx.ModeInfo, len(multiModeInfos))
// Recursively convert each modeInfo
for i, modeInfo := range multiModeInfos {
modeInfos[i] = &tx.ModeInfo{}
fromV2ModeInfo(modeInfo, modeInfos[i])
}
v1.Sum = &tx.ModeInfo_Multi_{
Multi: &tx.ModeInfo_Multi{
Bitarray: &cryptotypes.CompactBitArray{
Elems: mi.Multi.Bitarray.Elems,
ExtraBitsStored: mi.Multi.Bitarray.ExtraBitsStored,
},
ModeInfos: modeInfos,
},
}
}
}

View File

@ -1,335 +0,0 @@
package tx
import (
"testing"
"github.com/stretchr/testify/require"
errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/legacy"
"github.com/cosmos/cosmos-sdk/codec/testutil"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
)
func TestTxBuilder(t *testing.T) {
_, pubkey, addr := testdata.KeyTestPubAddr()
marshaler := codec.NewProtoCodec(codectypes.NewInterfaceRegistry())
txBuilder := newBuilder(marshaler)
memo := "testmemo"
msgs := []sdk.Msg{testdata.NewTestMsg(addr)}
accSeq := uint64(2) // Arbitrary account sequence
any, err := codectypes.NewAnyWithValue(pubkey)
require.NoError(t, err)
var signerInfo []*txtypes.SignerInfo
signerInfo = append(signerInfo, &txtypes.SignerInfo{
PublicKey: any,
ModeInfo: &txtypes.ModeInfo{
Sum: &txtypes.ModeInfo_Single_{
Single: &txtypes.ModeInfo_Single{
Mode: signing.SignMode_SIGN_MODE_DIRECT,
},
},
},
Sequence: accSeq,
})
sig := signing.SignatureV2{
PubKey: pubkey,
Data: &signing.SingleSignatureData{
SignMode: signing.SignMode_SIGN_MODE_DIRECT,
Signature: legacy.Cdc.MustMarshal(pubkey),
},
Sequence: accSeq,
}
fee := txtypes.Fee{Amount: sdk.NewCoins(sdk.NewInt64Coin("atom", 150)), GasLimit: 20000}
t.Log("verify that authInfo bytes encoded with DefaultTxEncoder and decoded with DefaultTxDecoder can be retrieved from getAuthInfoBytes")
authInfo := &txtypes.AuthInfo{
Fee: &fee,
SignerInfos: signerInfo,
}
authInfoBytes := marshaler.MustMarshal(authInfo)
require.NotEmpty(t, authInfoBytes)
t.Log("verify that body bytes encoded with DefaultTxEncoder and decoded with DefaultTxDecoder can be retrieved from getBodyBytes")
anys := make([]*codectypes.Any, len(msgs))
for i, msg := range msgs {
var err error
anys[i], err = codectypes.NewAnyWithValue(msg)
if err != nil {
panic(err)
}
}
txBody := &txtypes.TxBody{
Memo: memo,
Messages: anys,
}
bodyBytes := marshaler.MustMarshal(txBody)
require.NotEmpty(t, bodyBytes)
require.Empty(t, txBuilder.getBodyBytes())
t.Log("verify that calling the SetMsgs, SetMemo results in the correct getBodyBytes")
require.NotEqual(t, bodyBytes, txBuilder.getBodyBytes())
err = txBuilder.SetMsgs(msgs...)
require.NoError(t, err)
require.NotEqual(t, bodyBytes, txBuilder.getBodyBytes())
txBuilder.SetMemo(memo)
require.Equal(t, bodyBytes, txBuilder.getBodyBytes())
require.Equal(t, len(msgs), len(txBuilder.GetMsgs()))
pks, err := txBuilder.GetPubKeys()
require.NoError(t, err)
require.Empty(t, pks)
t.Log("verify that updated AuthInfo results in the correct getAuthInfoBytes and GetPubKeys")
require.NotEqual(t, authInfoBytes, txBuilder.getAuthInfoBytes())
txBuilder.SetFeeAmount(fee.Amount)
require.NotEqual(t, authInfoBytes, txBuilder.getAuthInfoBytes())
txBuilder.SetGasLimit(fee.GasLimit)
require.NotEqual(t, authInfoBytes, txBuilder.getAuthInfoBytes())
err = txBuilder.SetSignatures(sig)
require.NoError(t, err)
// once fee, gas and signerInfos are all set, AuthInfo bytes should match
require.Equal(t, authInfoBytes, txBuilder.getAuthInfoBytes())
require.Equal(t, len(msgs), len(txBuilder.GetMsgs()))
pks, err = txBuilder.GetPubKeys()
require.NoError(t, err)
require.Equal(t, 1, len(pks))
require.True(t, pubkey.Equals(pks[0]))
any, err = codectypes.NewAnyWithValue(testdata.NewTestMsg())
require.NoError(t, err)
txBuilder.SetExtensionOptions(any)
require.Equal(t, []*codectypes.Any{any}, txBuilder.GetExtensionOptions())
txBuilder.SetNonCriticalExtensionOptions(any)
require.Equal(t, []*codectypes.Any{any}, txBuilder.GetNonCriticalExtensionOptions())
txBuilder = &wrapper{}
require.NotPanics(t, func() {
_ = txBuilder.GetMsgs()
})
}
func TestSetSignaturesNoPublicKey(t *testing.T) {
_, pubkey, _ := testdata.KeyTestPubAddr()
txBuilder := newBuilder(nil)
sig2 := signing.SignatureV2{
Data: &signing.SingleSignatureData{
SignMode: signing.SignMode_SIGN_MODE_DIRECT,
Signature: legacy.Cdc.MustMarshal(pubkey),
},
Sequence: 1,
}
err := txBuilder.SetSignatures(sig2)
require.NoError(t, err)
}
func TestBuilderValidateBasic(t *testing.T) {
// keys and addresses
_, pubKey1, addr1 := testdata.KeyTestPubAddr()
_, pubKey2, addr2 := testdata.KeyTestPubAddr()
// msg and signatures
msg1 := testdata.NewTestMsg(addr1, addr2)
feeAmount := testdata.NewTestFeeAmount()
msgs := []sdk.Msg{msg1}
// require to fail validation upon invalid fee
badFeeAmount := testdata.NewTestFeeAmount()
badFeeAmount[0].Amount = sdkmath.NewInt(-5)
txBuilder := newBuilder(testutil.CodecOptions{}.NewCodec())
var sig1, sig2 signing.SignatureV2
sig1 = signing.SignatureV2{
PubKey: pubKey1,
Data: &signing.SingleSignatureData{
SignMode: signing.SignMode_SIGN_MODE_DIRECT,
Signature: legacy.Cdc.MustMarshal(pubKey1),
},
Sequence: 0, // Arbitrary account sequence
}
sig2 = signing.SignatureV2{
PubKey: pubKey2,
Data: &signing.SingleSignatureData{
SignMode: signing.SignMode_SIGN_MODE_DIRECT,
Signature: legacy.Cdc.MustMarshal(pubKey2),
},
Sequence: 0, // Arbitrary account sequence
}
err := txBuilder.SetMsgs(msgs...)
require.NoError(t, err)
txBuilder.SetGasLimit(200000)
err = txBuilder.SetSignatures(sig1, sig2)
require.NoError(t, err)
txBuilder.SetFeeAmount(badFeeAmount)
err = txBuilder.ValidateBasic()
require.Error(t, err)
_, code, _ := errorsmod.ABCIInfo(err, false)
require.Equal(t, sdkerrors.ErrInsufficientFee.ABCICode(), code)
// require to fail validation when no signatures exist
err = txBuilder.SetSignatures()
require.NoError(t, err)
txBuilder.SetFeeAmount(feeAmount)
err = txBuilder.ValidateBasic()
require.Error(t, err)
_, code, _ = errorsmod.ABCIInfo(err, false)
require.Equal(t, sdkerrors.ErrNoSignatures.ABCICode(), code)
// require to fail with nil values for tx, authinfo
err = txBuilder.SetMsgs(msgs...)
require.NoError(t, err)
err = txBuilder.ValidateBasic()
require.Error(t, err)
// require to fail validation when signatures do not match expected signers
err = txBuilder.SetSignatures(sig1)
require.NoError(t, err)
err = txBuilder.ValidateBasic()
require.Error(t, err)
_, code, _ = errorsmod.ABCIInfo(err, false)
require.Equal(t, sdkerrors.ErrUnauthorized.ABCICode(), code)
require.Error(t, err)
txBuilder.SetFeeAmount(feeAmount)
err = txBuilder.SetSignatures(sig1, sig2)
require.NoError(t, err)
err = txBuilder.ValidateBasic()
require.NoError(t, err)
// gas limit too high
txBuilder.SetGasLimit(txtypes.MaxGasWanted + 1)
err = txBuilder.ValidateBasic()
require.Error(t, err)
txBuilder.SetGasLimit(txtypes.MaxGasWanted - 1)
err = txBuilder.ValidateBasic()
require.NoError(t, err)
// bad builder structs
// missing body
body := txBuilder.tx.Body
txBuilder.tx.Body = nil
err = txBuilder.ValidateBasic()
require.Error(t, err)
txBuilder.tx.Body = body
err = txBuilder.ValidateBasic()
require.NoError(t, err)
// missing fee
f := txBuilder.tx.AuthInfo.Fee
txBuilder.tx.AuthInfo.Fee = nil
err = txBuilder.ValidateBasic()
require.Error(t, err)
txBuilder.tx.AuthInfo.Fee = f
err = txBuilder.ValidateBasic()
require.NoError(t, err)
// missing AuthInfo
authInfo := txBuilder.tx.AuthInfo
txBuilder.tx.AuthInfo = nil
err = txBuilder.ValidateBasic()
require.Error(t, err)
txBuilder.tx.AuthInfo = authInfo
err = txBuilder.ValidateBasic()
require.NoError(t, err)
// missing tx
txBuilder.tx = nil
err = txBuilder.ValidateBasic()
require.Error(t, err)
}
func TestBuilderFeePayer(t *testing.T) {
// keys and addresses
_, _, addr1 := testdata.KeyTestPubAddr()
_, _, addr2 := testdata.KeyTestPubAddr()
_, _, addr3 := testdata.KeyTestPubAddr()
// msg and signatures
msg1 := testdata.NewTestMsg(addr1, addr2)
feeAmount := testdata.NewTestFeeAmount()
msgs := []sdk.Msg{msg1}
cases := map[string]struct {
txFeePayer sdk.AccAddress
expectedSigners [][]byte
expectedPayer []byte
}{
"no fee payer specified": {
expectedSigners: [][]byte{addr1, addr2},
expectedPayer: addr1,
},
"secondary signer set as fee payer": {
txFeePayer: addr2,
expectedSigners: [][]byte{addr1, addr2},
expectedPayer: addr2,
},
"outside signer set as fee payer": {
txFeePayer: addr3,
expectedSigners: [][]byte{addr1, addr2, addr3},
expectedPayer: addr3,
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
// setup basic tx
txBuilder := newBuilder(testutil.CodecOptions{}.NewCodec())
err := txBuilder.SetMsgs(msgs...)
require.NoError(t, err)
txBuilder.SetGasLimit(200000)
txBuilder.SetFeeAmount(feeAmount)
// set fee payer
txBuilder.SetFeePayer(tc.txFeePayer)
// and check it updates fields properly
signers, err := txBuilder.GetSigners()
require.NoError(t, err)
require.Equal(t, tc.expectedSigners, signers)
require.Equal(t, tc.expectedPayer, txBuilder.FeePayer())
})
}
}
func TestBuilderFeeGranter(t *testing.T) {
// keys and addresses
_, _, addr1 := testdata.KeyTestPubAddr()
// msg and signatures
msg1 := testdata.NewTestMsg(addr1, addr2)
feeAmount := testdata.NewTestFeeAmount()
msgs := []sdk.Msg{msg1}
txBuilder := newBuilder(testutil.CodecOptions{}.NewCodec())
err := txBuilder.SetMsgs(msgs...)
require.NoError(t, err)
txBuilder.SetGasLimit(200000)
txBuilder.SetFeeAmount(feeAmount)
require.Empty(t, txBuilder.GetTx().FeeGranter())
// set fee granter
txBuilder.SetFeeGranter(addr1)
require.Equal(t, addr1.String(), sdk.AccAddress(txBuilder.GetTx().FeeGranter()).String())
}

View File

@ -3,7 +3,9 @@ package tx
import (
"fmt"
"cosmossdk.io/core/address"
authcodec "cosmossdk.io/x/auth/codec"
txdecode "cosmossdk.io/x/tx/decode"
txsigning "cosmossdk.io/x/tx/signing"
"cosmossdk.io/x/tx/signing/aminojson"
"cosmossdk.io/x/tx/signing/direct"
@ -24,6 +26,7 @@ type config struct {
jsonEncoder sdk.TxEncoder
protoCodec codec.Codec
signingContext *txsigning.Context
txDecoder *txdecode.Decoder
}
// ConfigOptions define the configuration of a TxConfig when calling NewTxConfigWithOptions.
@ -172,18 +175,6 @@ func NewTxConfigWithOptions(protoCodec codec.Codec, configOptions ConfigOptions)
jsonDecoder: configOptions.JSONDecoder,
jsonEncoder: configOptions.JSONEncoder,
}
if configOptions.ProtoDecoder == nil {
txConfig.decoder = DefaultTxDecoder(protoCodec)
}
if configOptions.ProtoEncoder == nil {
txConfig.encoder = DefaultTxEncoder()
}
if configOptions.JSONDecoder == nil {
txConfig.jsonDecoder = DefaultJSONTxDecoder(protoCodec)
}
if configOptions.JSONEncoder == nil {
txConfig.jsonEncoder = DefaultJSONTxEncoder(protoCodec)
}
var err error
if configOptions.SigningContext == nil {
@ -201,6 +192,25 @@ func NewTxConfigWithOptions(protoCodec codec.Codec, configOptions ConfigOptions)
return nil, err
}
}
if configOptions.ProtoDecoder == nil {
dec, err := txdecode.NewDecoder(txdecode.Options{SigningContext: configOptions.SigningContext})
if err != nil {
return nil, err
}
txConfig.decoder = txV2toInterface(configOptions.SigningOptions.AddressCodec, protoCodec, dec)
txConfig.txDecoder = dec
}
if configOptions.ProtoEncoder == nil {
txConfig.encoder = DefaultTxEncoder()
}
if configOptions.JSONDecoder == nil {
txConfig.jsonDecoder = DefaultJSONTxDecoder(configOptions.SigningOptions.AddressCodec, protoCodec, txConfig.txDecoder)
}
if configOptions.JSONEncoder == nil {
txConfig.jsonEncoder = DefaultJSONTxEncoder(protoCodec)
}
txConfig.signingContext = configOptions.SigningContext
if configOptions.SigningHandler != nil {
@ -217,17 +227,17 @@ func NewTxConfigWithOptions(protoCodec codec.Codec, configOptions ConfigOptions)
}
func (g config) NewTxBuilder() client.TxBuilder {
return newBuilder(g.protoCodec)
return newBuilder(g.signingContext.AddressCodec(), g.txDecoder, g.protoCodec)
}
// WrapTxBuilder returns a builder from provided transaction
func (g config) WrapTxBuilder(newTx sdk.Tx) (client.TxBuilder, error) {
newBuilder, ok := newTx.(*wrapper)
gogoTx, ok := newTx.(*gogoTxWrapper)
if !ok {
return nil, fmt.Errorf("expected %T, got %T", &wrapper{}, newTx)
return nil, fmt.Errorf("expected %T, got %T", &gogoTxWrapper{}, newTx)
}
return newBuilder, nil
return newBuilderFromDecodedTx(g.signingContext.AddressCodec(), g.txDecoder, g.protoCodec, gogoTx)
}
func (g config) SignModeHandler() *txsigning.HandlerMap {
@ -253,3 +263,13 @@ func (g config) TxJSONDecoder() sdk.TxDecoder {
func (g config) SigningContext() *txsigning.Context {
return g.signingContext
}
func txV2toInterface(addrCodec address.Codec, cdc codec.BinaryCodec, decoder *txdecode.Decoder) func([]byte) (sdk.Tx, error) {
return func(txBytes []byte) (sdk.Tx, error) {
decodedTx, err := decoder.Decode(txBytes)
if err != nil {
return nil, err
}
return newWrapperFromDecodedTx(addrCodec, cdc, decodedTx)
}
}

View File

@ -4,6 +4,7 @@ import (
"context"
"fmt"
gogoproto "github.com/cosmos/gogoproto/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
grpcstatus "google.golang.org/grpc/status"
@ -25,7 +26,6 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/runtime"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/registry"
signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing"
)
@ -61,7 +61,7 @@ type ModuleOutputs struct {
}
func ProvideProtoRegistry() txsigning.ProtoFileResolver {
return registry.MergedProtoRegistry()
return gogoproto.HybridResolver
}
func ProvideModule(in ModuleInputs) ModuleOutputs {

View File

@ -6,6 +6,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
_ "cosmossdk.io/api/cosmos/crypto/secp256k1"
"cosmossdk.io/x/auth/tx"
txtestutil "cosmossdk.io/x/auth/tx/testutil"

View File

@ -1,174 +1,53 @@
package tx
import (
"fmt"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/encoding/protowire"
errorsmod "cosmossdk.io/errors"
txv1beta1 "cosmossdk.io/api/cosmos/tx/v1beta1"
"cosmossdk.io/core/address"
"cosmossdk.io/x/tx/decode"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/unknownproto"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/tx"
)
// DefaultTxDecoder returns a default protobuf TxDecoder using the provided Marshaler.
func DefaultTxDecoder(cdc codec.Codec) sdk.TxDecoder {
// DefaultJSONTxDecoder returns a default protobuf JSON TxDecoder using the provided Marshaler.
func DefaultJSONTxDecoder(addrCodec address.Codec, cdc codec.BinaryCodec, decoder *decode.Decoder) sdk.TxDecoder {
jsonUnmarshaller := protojson.UnmarshalOptions{
AllowPartial: false,
DiscardUnknown: false,
}
return func(txBytes []byte) (sdk.Tx, error) {
// Make sure txBytes follow ADR-027.
err := rejectNonADR027TxRaw(txBytes)
if err != nil {
return nil, errorsmod.Wrap(sdkerrors.ErrTxDecode, err.Error())
}
var raw tx.TxRaw
// reject all unknown proto fields in the root TxRaw
err = unknownproto.RejectUnknownFieldsStrict(txBytes, &raw, cdc.InterfaceRegistry())
if err != nil {
return nil, errorsmod.Wrap(sdkerrors.ErrTxDecode, err.Error())
}
err = cdc.Unmarshal(txBytes, &raw)
jsonTx := new(txv1beta1.Tx)
err := jsonUnmarshaller.Unmarshal(txBytes, jsonTx)
if err != nil {
return nil, err
}
var body tx.TxBody
// allow non-critical unknown fields in TxBody
txBodyHasUnknownNonCriticals, err := unknownproto.RejectUnknownFields(raw.BodyBytes, &body, true, cdc.InterfaceRegistry())
// need to convert jsonTx into raw tx.
bodyBytes, err := marshalOption.Marshal(jsonTx.Body)
if err != nil {
return nil, errorsmod.Wrap(sdkerrors.ErrTxDecode, err.Error())
return nil, err
}
err = cdc.Unmarshal(raw.BodyBytes, &body)
authInfoBytes, err := marshalOption.Marshal(jsonTx.AuthInfo)
if err != nil {
return nil, errorsmod.Wrap(sdkerrors.ErrTxDecode, err.Error())
return nil, err
}
var authInfo tx.AuthInfo
// reject all unknown proto fields in AuthInfo
err = unknownproto.RejectUnknownFieldsStrict(raw.AuthInfoBytes, &authInfo, cdc.InterfaceRegistry())
protoTxBytes, err := marshalOption.Marshal(&txv1beta1.TxRaw{
BodyBytes: bodyBytes,
AuthInfoBytes: authInfoBytes,
Signatures: jsonTx.Signatures,
})
if err != nil {
return nil, errorsmod.Wrap(sdkerrors.ErrTxDecode, err.Error())
return nil, err
}
err = cdc.Unmarshal(raw.AuthInfoBytes, &authInfo)
decodedTx, err := decoder.Decode(protoTxBytes)
if err != nil {
return nil, errorsmod.Wrap(sdkerrors.ErrTxDecode, err.Error())
return nil, err
}
theTx := &tx.Tx{
Body: &body,
AuthInfo: &authInfo,
Signatures: raw.Signatures,
}
return &wrapper{
tx: theTx,
bodyBz: raw.BodyBytes,
authInfoBz: raw.AuthInfoBytes,
txBodyHasUnknownNonCriticals: txBodyHasUnknownNonCriticals,
cdc: cdc,
}, nil
}
}
// DefaultJSONTxDecoder returns a default protobuf JSON TxDecoder using the provided Marshaler.
func DefaultJSONTxDecoder(cdc codec.Codec) sdk.TxDecoder {
return func(txBytes []byte) (sdk.Tx, error) {
var theTx tx.Tx
err := cdc.UnmarshalJSON(txBytes, &theTx)
if err != nil {
return nil, errorsmod.Wrap(sdkerrors.ErrTxDecode, err.Error())
}
return &wrapper{
tx: &theTx,
cdc: cdc,
}, nil
}
}
// rejectNonADR027TxRaw rejects txBytes that do not follow ADR-027. This is NOT
// a generic ADR-027 checker, it only applies decoding TxRaw. Specifically, it
// only checks that:
// - field numbers are in ascending order (1, 2, and potentially multiple 3s),
// - and varints are as short as possible.
// All other ADR-027 edge cases (e.g. default values) are not applicable with
// TxRaw.
func rejectNonADR027TxRaw(txBytes []byte) error {
// Make sure all fields are ordered in ascending order with this variable.
prevTagNum := protowire.Number(0)
for len(txBytes) > 0 {
tagNum, wireType, m := protowire.ConsumeTag(txBytes)
if m < 0 {
return fmt.Errorf("invalid length; %w", protowire.ParseError(m))
}
// TxRaw only has bytes fields.
if wireType != protowire.BytesType {
return fmt.Errorf("expected %d wire type, got %d", protowire.BytesType, wireType)
}
// Make sure fields are ordered in ascending order.
if tagNum < prevTagNum {
return fmt.Errorf("txRaw must follow ADR-027, got tagNum %d after tagNum %d", tagNum, prevTagNum)
}
prevTagNum = tagNum
// All 3 fields of TxRaw have wireType == 2, so their next component
// is a varint, so we can safely call ConsumeVarint here.
// Byte structure: <varint of bytes length><bytes sequence>
// Inner fields are verified in `DefaultTxDecoder`
lengthPrefix, m := protowire.ConsumeVarint(txBytes[m:])
if m < 0 {
return fmt.Errorf("invalid length; %w", protowire.ParseError(m))
}
// We make sure that this varint is as short as possible.
n := varintMinLength(lengthPrefix)
if n != m {
return fmt.Errorf("length prefix varint for tagNum %d is not as short as possible, read %d, only need %d", tagNum, m, n)
}
// Skip over the bytes that store fieldNumber and wireType bytes.
_, _, m = protowire.ConsumeField(txBytes)
if m < 0 {
return fmt.Errorf("invalid length; %w", protowire.ParseError(m))
}
txBytes = txBytes[m:]
}
return nil
}
// varintMinLength returns the minimum number of bytes necessary to encode an
// uint using varint encoding.
func varintMinLength(n uint64) int {
switch {
// Note: 1<<N == 2**N.
case n < 1<<(7):
return 1
case n < 1<<(7*2):
return 2
case n < 1<<(7*3):
return 3
case n < 1<<(7*4):
return 4
case n < 1<<(7*5):
return 5
case n < 1<<(7*6):
return 6
case n < 1<<(7*7):
return 7
case n < 1<<(7*8):
return 8
case n < 1<<(7*9):
return 9
default:
return 10
return newWrapperFromDecodedTx(addrCodec, cdc, decodedTx)
}
}

View File

@ -1,281 +0,0 @@
package tx
import (
"encoding/binary"
"fmt"
"math"
"testing"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/encoding/protowire"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
"github.com/cosmos/cosmos-sdk/types/tx"
)
func TestDefaultTxDecoderError(t *testing.T) {
registry := codectypes.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(registry)
encoder := DefaultTxEncoder()
decoder := DefaultTxDecoder(cdc)
builder := newBuilder(nil)
err := builder.SetMsgs(testdata.NewTestMsg())
require.NoError(t, err)
txBz, err := encoder(builder.GetTx())
require.NoError(t, err)
_, err = decoder(txBz)
require.EqualError(t, err, "unable to resolve type URL /testpb.TestMsg: tx parse error")
testdata.RegisterInterfaces(registry)
_, err = decoder(txBz)
require.NoError(t, err)
}
func TestUnknownFields(t *testing.T) {
registry := codectypes.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(registry)
decoder := DefaultTxDecoder(cdc)
tests := []struct {
name string
body *testdata.TestUpdatedTxBody
authInfo *testdata.TestUpdatedAuthInfo
shouldErr bool
}{
{
name: "no new fields should pass",
body: &testdata.TestUpdatedTxBody{
Memo: "foo",
},
authInfo: &testdata.TestUpdatedAuthInfo{},
shouldErr: false,
},
{
name: "non-critical fields in TxBody should not error on decode, but should error with amino",
body: &testdata.TestUpdatedTxBody{
Memo: "foo",
SomeNewFieldNonCriticalField: "blah",
},
authInfo: &testdata.TestUpdatedAuthInfo{},
shouldErr: false,
},
{
// If new fields are added to TxBody the number for some_new_field in the proto definition must be set to
// one that it's not used in TxBody.
name: "critical fields in TxBody should error on decode",
body: &testdata.TestUpdatedTxBody{
Memo: "foo",
SomeNewField: 10,
},
authInfo: &testdata.TestUpdatedAuthInfo{},
shouldErr: true,
},
{
name: "critical fields in AuthInfo should error on decode",
body: &testdata.TestUpdatedTxBody{
Memo: "foo",
},
authInfo: &testdata.TestUpdatedAuthInfo{
NewField_3: []byte("xyz"),
},
shouldErr: true,
},
{
name: "non-critical fields in AuthInfo should error on decode",
body: &testdata.TestUpdatedTxBody{
Memo: "foo",
},
authInfo: &testdata.TestUpdatedAuthInfo{
NewField_1024: []byte("xyz"),
},
shouldErr: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
bodyBz, err := tt.body.Marshal()
require.NoError(t, err)
authInfoBz, err := tt.authInfo.Marshal()
require.NoError(t, err)
txRaw := &tx.TxRaw{
BodyBytes: bodyBz,
AuthInfoBytes: authInfoBz,
}
txBz, err := txRaw.Marshal()
require.NoError(t, err)
_, err = decoder(txBz)
if tt.shouldErr {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
t.Log("test TxRaw no new fields, should succeed")
txRaw := &testdata.TestUpdatedTxRaw{}
txBz, err := txRaw.Marshal()
require.NoError(t, err)
_, err = decoder(txBz)
require.NoError(t, err)
t.Log("new field in TxRaw should fail")
txRaw = &testdata.TestUpdatedTxRaw{
NewField_5: []byte("abc"),
}
txBz, err = txRaw.Marshal()
require.NoError(t, err)
_, err = decoder(txBz)
require.Error(t, err)
//
t.Log("new \"non-critical\" field in TxRaw should fail")
txRaw = &testdata.TestUpdatedTxRaw{
NewField_1024: []byte("abc"),
}
txBz, err = txRaw.Marshal()
require.NoError(t, err)
_, err = decoder(txBz)
require.Error(t, err)
}
func TestRejectNonADR027(t *testing.T) {
registry := codectypes.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(registry)
decoder := DefaultTxDecoder(cdc)
body := &testdata.TestUpdatedTxBody{Memo: "AAA"} // Look for "65 65 65" when debugging the bytes stream.
bodyBz, err := body.Marshal()
require.NoError(t, err)
authInfo := &testdata.TestUpdatedAuthInfo{Fee: &tx.Fee{GasLimit: 127}} // Look for "127" when debugging the bytes stream.
authInfoBz, err := authInfo.Marshal()
require.NoError(t, err)
txRaw := &tx.TxRaw{
BodyBytes: bodyBz,
AuthInfoBytes: authInfoBz,
Signatures: [][]byte{{41}, {42}, {43}}, // Look for "42" when debugging the bytes stream.
}
// We know these bytes are ADR-027-compliant.
txBz, err := txRaw.Marshal()
// From the `txBz`, we extract the 3 components:
// bodyBz, authInfoBz, sigsBz.
// In our tests, we will try to decode txs with those 3 components in all
// possible orders.
//
// Consume "BodyBytes" field.
_, _, m := protowire.ConsumeField(txBz)
bodyBz = append([]byte{}, txBz[:m]...)
txBz = txBz[m:] // Skip over "BodyBytes" bytes.
// Consume "AuthInfoBytes" field.
_, _, m = protowire.ConsumeField(txBz)
authInfoBz = append([]byte{}, txBz[:m]...)
txBz = txBz[m:] // Skip over "AuthInfoBytes" bytes.
// Consume "Signature" field, it's the remaining bytes.
sigsBz := append([]byte{}, txBz...)
// bodyBz's length prefix is 5, with `5` as varint encoding. We also try a
// longer varint encoding for 5: `133 00`.
longVarintBodyBz := append(append([]byte{bodyBz[0]}, byte(133), byte(0o0)), bodyBz[2:]...)
tests := []struct {
name string
txBz []byte
shouldErr bool
}{
{
"authInfo, body, sigs",
append(append(authInfoBz, bodyBz...), sigsBz...),
true,
},
{
"authInfo, sigs, body",
append(append(authInfoBz, sigsBz...), bodyBz...),
true,
},
{
"sigs, body, authInfo",
append(append(sigsBz, bodyBz...), authInfoBz...),
true,
},
{
"sigs, authInfo, body",
append(append(sigsBz, authInfoBz...), bodyBz...),
true,
},
{
"body, sigs, authInfo",
append(append(bodyBz, sigsBz...), authInfoBz...),
true,
},
{
"body, authInfo, sigs (valid txRaw)",
append(append(bodyBz, authInfoBz...), sigsBz...),
false,
},
{
"longer varint than needed",
append(append(longVarintBodyBz, authInfoBz...), sigsBz...),
true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
_, err = decoder(tt.txBz)
if tt.shouldErr {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
}
func TestVarintMinLength(t *testing.T) {
tests := []struct {
n uint64
}{
{1<<7 - 1},
{1 << 7},
{1<<14 - 1},
{1 << 14},
{1<<21 - 1},
{1 << 21},
{1<<28 - 1},
{1 << 28},
{1<<35 - 1},
{1 << 35},
{1<<42 - 1},
{1 << 42},
{1<<49 - 1},
{1 << 49},
{1<<56 - 1},
{1 << 56},
{1<<63 - 1},
{1 << 63},
{math.MaxUint64},
}
for _, tt := range tests {
tt := tt
t.Run(fmt.Sprintf("test %d", tt.n), func(t *testing.T) {
l1 := varintMinLength(tt.n)
buf := make([]byte, binary.MaxVarintLen64)
l2 := binary.PutUvarint(buf, tt.n)
require.Equal(t, l2, l1)
})
}
}

View File

@ -3,39 +3,35 @@ package tx
import (
"fmt"
"github.com/cosmos/gogoproto/proto"
"google.golang.org/protobuf/encoding/protojson"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
)
// DefaultTxEncoder returns a default protobuf TxEncoder using the provided Marshaler
func DefaultTxEncoder() sdk.TxEncoder {
return func(tx sdk.Tx) ([]byte, error) {
txWrapper, ok := tx.(*wrapper)
gogoWrapper, ok := tx.(*gogoTxWrapper)
if !ok {
return nil, fmt.Errorf("expected %T, got %T", &wrapper{}, tx)
return nil, fmt.Errorf("unexpected tx type: %T", tx)
}
raw := &txtypes.TxRaw{
BodyBytes: txWrapper.getBodyBytes(),
AuthInfoBytes: txWrapper.getAuthInfoBytes(),
Signatures: txWrapper.tx.Signatures,
}
return proto.Marshal(raw)
return marshalOption.Marshal(gogoWrapper.decodedTx.TxRaw)
}
}
// DefaultJSONTxEncoder returns a default protobuf JSON TxEncoder using the provided Marshaler.
func DefaultJSONTxEncoder(cdc codec.Codec) sdk.TxEncoder {
jsonMarshaler := protojson.MarshalOptions{
Indent: "",
UseProtoNames: true,
UseEnumNumbers: false,
}
return func(tx sdk.Tx) ([]byte, error) {
txWrapper, ok := tx.(*wrapper)
if ok {
return cdc.MarshalJSON(txWrapper.tx)
gogoWrapper, ok := tx.(*gogoTxWrapper)
if !ok {
return nil, fmt.Errorf("unexpected tx type: %T", tx)
}
return nil, fmt.Errorf("expected %T, got %T", &wrapper{}, tx)
return jsonMarshaler.Marshal(gogoWrapper.decodedTx.Tx)
}
}

302
x/auth/tx/gogotx.go Normal file
View File

@ -0,0 +1,302 @@
package tx
import (
"fmt"
"reflect"
"strings"
"github.com/cosmos/gogoproto/proto"
protov2 "google.golang.org/protobuf/proto"
anypb "google.golang.org/protobuf/types/known/anypb"
"cosmossdk.io/core/address"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/math"
"cosmossdk.io/x/auth/ante"
authsigning "cosmossdk.io/x/auth/signing"
"cosmossdk.io/x/tx/decode"
txsigning "cosmossdk.io/x/tx/signing"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
)
func newWrapperFromDecodedTx(addrCodec address.Codec, cdc codec.BinaryCodec, decodedTx *decode.DecodedTx) (w *gogoTxWrapper, err error) {
// set msgsv1
msgv1, err := decodeMsgsV1(cdc, decodedTx.Tx.Body.Messages)
if err != nil {
return nil, fmt.Errorf("unable to convert messagev2 to messagev1: %w", err)
}
// set fees
fees := make(sdk.Coins, len(decodedTx.Tx.AuthInfo.Fee.Amount))
for i, fee := range decodedTx.Tx.AuthInfo.Fee.Amount {
amtInt, ok := math.NewIntFromString(fee.Amount)
if !ok {
return nil, fmt.Errorf("invalid fee coin amount at index %d: %s", i, fee.Amount)
}
if err = sdk.ValidateDenom(fee.Denom); err != nil {
return nil, fmt.Errorf("invalid fee coin denom at index %d: %w", i, err)
}
fees[i] = sdk.Coin{
Denom: fee.Denom,
Amount: amtInt,
}
}
if !fees.IsSorted() {
return nil, fmt.Errorf("invalid not sorted tx fees: %s", fees.String())
}
// set fee payer
var feePayer []byte
if len(decodedTx.Signers) != 0 {
feePayer = decodedTx.Signers[0]
if decodedTx.Tx.AuthInfo.Fee.Payer != "" {
feePayer, err = addrCodec.StringToBytes(decodedTx.Tx.AuthInfo.Fee.Payer)
if err != nil {
return nil, err
}
}
}
// fee granter
var feeGranter []byte
if decodedTx.Tx.AuthInfo.Fee.Granter != "" {
feeGranter, err = addrCodec.StringToBytes(decodedTx.Tx.AuthInfo.Fee.Granter)
if err != nil {
return nil, err
}
}
return &gogoTxWrapper{
cdc: cdc,
decodedTx: decodedTx,
msgsV1: msgv1,
fees: fees,
feePayer: feePayer,
feeGranter: feeGranter,
}, nil
}
// gogoTxWrapper is a gogoTxWrapper around the tx.Tx proto.Message which retain the raw
// body and auth_info bytes.
type gogoTxWrapper struct {
decodedTx *decode.DecodedTx
cdc codec.BinaryCodec
msgsV1 []proto.Message
fees sdk.Coins
feePayer []byte
feeGranter []byte
}
func (w *gogoTxWrapper) String() string { return w.decodedTx.Tx.String() }
var (
_ authsigning.Tx = &gogoTxWrapper{}
_ ante.HasExtensionOptionsTx = &gogoTxWrapper{}
)
// ExtensionOptionsTxBuilder defines a TxBuilder that can also set extensions.
type ExtensionOptionsTxBuilder interface {
client.TxBuilder
SetExtensionOptions(...*codectypes.Any)
SetNonCriticalExtensionOptions(...*codectypes.Any)
}
func (w *gogoTxWrapper) GetMsgs() []sdk.Msg {
if w.msgsV1 == nil {
panic("fill in msgs")
}
return w.msgsV1
}
func (w *gogoTxWrapper) GetMsgsV2() ([]protov2.Message, error) {
return w.decodedTx.Messages, nil
}
func (w *gogoTxWrapper) ValidateBasic() error {
if len(w.decodedTx.Tx.Signatures) == 0 {
return sdkerrors.ErrNoSignatures.Wrapf("empty signatures")
}
if len(w.decodedTx.Signers) != len(w.decodedTx.Tx.Signatures) {
return sdkerrors.ErrUnauthorized.Wrapf("invalid number of signatures: got %d signatures and %d signers", len(w.decodedTx.Tx.Signatures), len(w.decodedTx.Signers))
}
return nil
}
func (w *gogoTxWrapper) GetSigners() ([][]byte, error) {
return w.decodedTx.Signers, nil
}
func (w *gogoTxWrapper) GetPubKeys() ([]cryptotypes.PubKey, error) {
signerInfos := w.decodedTx.Tx.AuthInfo.SignerInfos
pks := make([]cryptotypes.PubKey, len(signerInfos))
for i, si := range signerInfos {
// NOTE: it is okay to leave this nil if there is no PubKey in the SignerInfo.
// PubKey's can be left unset in SignerInfo.
if si.PublicKey == nil {
continue
}
maybePK, err := decodeFromAny(w.cdc, si.PublicKey)
if err != nil {
return nil, err
}
pk, ok := maybePK.(cryptotypes.PubKey)
if ok {
pks[i] = pk
} else {
return nil, errorsmod.Wrapf(sdkerrors.ErrLogic, "expecting pubkey, got: %T", maybePK)
}
}
return pks, nil
}
func (w *gogoTxWrapper) GetGas() uint64 {
return w.decodedTx.Tx.AuthInfo.Fee.GasLimit
}
func (w *gogoTxWrapper) GetFee() sdk.Coins { return w.fees }
func (w *gogoTxWrapper) FeePayer() []byte { return w.feePayer }
func (w *gogoTxWrapper) FeeGranter() []byte { return w.feeGranter }
func (w *gogoTxWrapper) GetMemo() string { return w.decodedTx.Tx.Body.Memo }
// GetTimeoutHeight returns the transaction's timeout height (if set).
func (w *gogoTxWrapper) GetTimeoutHeight() uint64 { return w.decodedTx.Tx.Body.TimeoutHeight }
// GetUnordered returns the transaction's unordered field (if set).
func (w *gogoTxWrapper) GetUnordered() bool { return w.decodedTx.Tx.Body.Unordered }
// GetSignaturesV2 returns the signatures of the Tx.
func (w *gogoTxWrapper) GetSignaturesV2() ([]signing.SignatureV2, error) {
signerInfos := w.decodedTx.Tx.AuthInfo.SignerInfos
sigs := w.decodedTx.Tx.Signatures
pubKeys, err := w.GetPubKeys()
if err != nil {
return nil, err
}
n := len(signerInfos)
res := make([]signing.SignatureV2, n)
for i, si := range signerInfos {
// handle nil signatures (in case of simulation)
if si.ModeInfo == nil || si.ModeInfo.Sum == nil {
res[i] = signing.SignatureV2{
PubKey: pubKeys[i],
}
} else {
var err error
sigData, err := ModeInfoAndSigToSignatureData(si.ModeInfo, sigs[i])
if err != nil {
return nil, err
}
// sequence number is functionally a transaction nonce and referred to as such in the SDK
nonce := si.GetSequence()
res[i] = signing.SignatureV2{
PubKey: pubKeys[i],
Data: sigData,
Sequence: nonce,
}
}
}
return res, nil
}
// GetSigningTxData returns an x/tx/signing.TxData representation of a transaction for use in the signing
// TODO: evaluate if this is even needed considering we have decoded tx.
func (w *gogoTxWrapper) GetSigningTxData() txsigning.TxData {
return txsigning.TxData{
Body: w.decodedTx.Tx.Body,
AuthInfo: w.decodedTx.Tx.AuthInfo,
BodyBytes: w.decodedTx.TxRaw.BodyBytes,
AuthInfoBytes: w.decodedTx.TxRaw.AuthInfoBytes,
BodyHasUnknownNonCriticals: w.decodedTx.TxBodyHasUnknownNonCriticals,
}
}
func (w *gogoTxWrapper) GetExtensionOptions() []*codectypes.Any {
return intoAnyV1(w.decodedTx.Tx.Body.ExtensionOptions)
}
func (w *gogoTxWrapper) GetNonCriticalExtensionOptions() []*codectypes.Any {
return intoAnyV1(w.decodedTx.Tx.Body.NonCriticalExtensionOptions)
}
func (w *gogoTxWrapper) AsTx() (*txtypes.Tx, error) {
body := new(txtypes.TxBody)
authInfo := new(txtypes.AuthInfo)
err := w.cdc.Unmarshal(w.decodedTx.TxRaw.BodyBytes, body)
if err != nil {
return nil, err
}
err = w.cdc.Unmarshal(w.decodedTx.TxRaw.AuthInfoBytes, authInfo)
if err != nil {
return nil, err
}
return &txtypes.Tx{
Body: body,
AuthInfo: authInfo,
Signatures: w.decodedTx.TxRaw.Signatures,
}, nil
}
func (w *gogoTxWrapper) AsTxRaw() (*txtypes.TxRaw, error) {
return &txtypes.TxRaw{
BodyBytes: w.decodedTx.TxRaw.BodyBytes,
AuthInfoBytes: w.decodedTx.TxRaw.AuthInfoBytes,
Signatures: w.decodedTx.TxRaw.Signatures,
}, nil
}
func intoAnyV1(v2s []*anypb.Any) []*codectypes.Any {
v1s := make([]*codectypes.Any, len(v2s))
for i, v2 := range v2s {
v1s[i] = &codectypes.Any{
TypeUrl: v2.TypeUrl,
Value: v2.Value,
}
}
return v1s
}
// decodeMsgsV1 will decode the given messages into
func decodeMsgsV1(cdc codec.BinaryCodec, anyPBs []*anypb.Any) ([]proto.Message, error) {
v1s := make([]proto.Message, len(anyPBs))
for i, anyPB := range anyPBs {
v1, err := decodeFromAny(cdc, anyPB)
if err != nil {
return nil, err
}
v1s[i] = v1
}
return v1s, nil
}
func decodeFromAny(cdc codec.BinaryCodec, anyPB *anypb.Any) (proto.Message, error) {
messageName := anyPB.TypeUrl
if i := strings.LastIndexByte(anyPB.TypeUrl, '/'); i >= 0 {
messageName = messageName[i+len("/"):]
}
typ := proto.MessageType(messageName)
if typ == nil {
return nil, fmt.Errorf("cannot find type: %s", anyPB.TypeUrl)
}
v1 := reflect.New(typ.Elem()).Interface().(proto.Message)
err := cdc.Unmarshal(anyPB.Value, v1)
if err != nil {
return nil, err
}
return v1, nil
}

1
x/auth/tx/gogotx_test.go Normal file
View File

@ -0,0 +1 @@
package tx

View File

@ -43,16 +43,16 @@ func (s signModeLegacyAminoJSONHandler) GetSignBytes(mode signingtypes.SignMode,
return nil, fmt.Errorf("expected %s, got %s", signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, mode)
}
protoTx, ok := tx.(*wrapper)
protoTx, ok := tx.(*gogoTxWrapper)
if !ok {
return nil, fmt.Errorf("can only handle a protobuf Tx, got %T", tx)
}
if protoTx.txBodyHasUnknownNonCriticals {
if protoTx.decodedTx.TxBodyHasUnknownNonCriticals {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, aminoNonCriticalFieldsError)
}
body := protoTx.tx.Body
body := protoTx.decodedTx.Tx.Body
if len(body.ExtensionOptions) != 0 || len(body.NonCriticalExtensionOptions) != 0 {
return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "%s does not support protobuf extension options", signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON)
@ -68,8 +68,8 @@ func (s signModeLegacyAminoJSONHandler) GetSignBytes(mode signingtypes.SignMode,
legacytx.StdFee{
Amount: protoTx.GetFee(),
Gas: protoTx.GetGas(),
Payer: protoTx.tx.AuthInfo.Fee.Payer,
Granter: protoTx.tx.AuthInfo.Fee.Granter,
Payer: protoTx.decodedTx.Tx.AuthInfo.Fee.Payer,
Granter: protoTx.decodedTx.Tx.AuthInfo.Fee.Granter,
},
tx.GetMsgs(), protoTx.GetMemo(),
), nil

View File

@ -1,223 +0,0 @@
package tx
import (
"context"
"testing"
"github.com/stretchr/testify/require"
"cosmossdk.io/x/auth/migrations/legacytx"
"cosmossdk.io/x/auth/signing"
"cosmossdk.io/x/auth/types"
txsigning "cosmossdk.io/x/tx/signing"
"cosmossdk.io/x/tx/signing/aminojson"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/legacy"
cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing"
)
var (
_, pubkey1, addr1 = testdata.KeyTestPubAddr()
_, _, addr2 = testdata.KeyTestPubAddr()
coins = sdk.Coins{sdk.NewInt64Coin("foocoin", 10)}
gas = uint64(10000)
msg = &types.MsgUpdateParams{Authority: addr1.String()}
memo = "foo"
timeout = uint64(10)
)
func buildTx(t *testing.T, bldr *wrapper) {
t.Helper()
bldr.SetFeeAmount(coins)
bldr.SetGasLimit(gas)
bldr.SetMemo(memo)
bldr.SetTimeoutHeight(timeout)
require.NoError(t, bldr.SetMsgs(msg))
}
func TestLegacyAminoJSONHandler_GetSignBytes(t *testing.T) {
legacytx.RegressionTestingAminoCodec = codec.NewLegacyAmino()
var (
chainID = "test-chain"
accNum uint64 = 7
seqNum uint64 = 7
)
testcases := []struct {
name string
signer string
malleate func(*wrapper)
expectedSignBz []byte
}{
{
"signer which is also fee payer (no tips)", addr1.String(),
func(w *wrapper) {},
legacytx.StdSignBytes(chainID, accNum, seqNum, timeout, legacytx.StdFee{Amount: coins, Gas: gas}, []sdk.Msg{msg}, memo),
},
{
"explicit fee payer", addr1.String(),
func(w *wrapper) { w.SetFeePayer(addr2) },
legacytx.StdSignBytes(chainID, accNum, seqNum, timeout, legacytx.StdFee{Amount: coins, Gas: gas, Payer: addr2.String()}, []sdk.Msg{msg}, memo),
},
{
"explicit fee granter", addr1.String(),
func(w *wrapper) { w.SetFeeGranter(addr2) },
legacytx.StdSignBytes(chainID, accNum, seqNum, timeout, legacytx.StdFee{Amount: coins, Gas: gas, Granter: addr2.String()}, []sdk.Msg{msg}, memo),
},
{
"explicit fee payer and fee granter", addr1.String(),
func(w *wrapper) {
w.SetFeePayer(addr2)
w.SetFeeGranter(addr2)
},
legacytx.StdSignBytes(chainID, accNum, seqNum, timeout, legacytx.StdFee{Amount: coins, Gas: gas, Payer: addr2.String(), Granter: addr2.String()}, []sdk.Msg{msg}, memo),
},
}
handler := signModeLegacyAminoJSONHandler{}
for _, tc := range testcases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
bldr := newBuilder(nil)
buildTx(t, bldr)
tx := bldr.GetTx()
tc.malleate(bldr)
signingData := signing.SignerData{
Address: tc.signer,
ChainID: chainID,
AccountNumber: accNum,
Sequence: seqNum,
}
signBz, err := handler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, signingData, tx)
require.NoError(t, err)
require.Equal(t, tc.expectedSignBz, signBz)
})
}
bldr := newBuilder(nil)
buildTx(t, bldr)
tx := bldr.GetTx()
signingData := signing.SignerData{
Address: addr1.String(),
ChainID: chainID,
AccountNumber: accNum,
Sequence: seqNum,
PubKey: pubkey1,
}
// expect error with wrong sign mode
_, err := handler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_DIRECT, signingData, tx)
require.Error(t, err)
// expect error with extension options
bldr = newBuilder(nil)
buildTx(t, bldr)
any, err := cdctypes.NewAnyWithValue(testdata.NewTestMsg())
require.NoError(t, err)
bldr.tx.Body.ExtensionOptions = []*cdctypes.Any{any}
tx = bldr.GetTx()
_, err = handler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, signingData, tx)
require.Error(t, err)
// expect error with non-critical extension options
bldr = newBuilder(nil)
buildTx(t, bldr)
bldr.tx.Body.NonCriticalExtensionOptions = []*cdctypes.Any{any}
tx = bldr.GetTx()
_, err = handler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, signingData, tx)
require.Error(t, err)
}
func TestLegacyAminoJSONHandler_AllGetSignBytesComparison(t *testing.T) {
var (
chainID = "test-chain"
accNum uint64
seqNum uint64 = 7
)
modeHandler := aminojson.NewSignModeHandler(aminojson.SignModeHandlerOptions{})
mode, _ := signing.APISignModeToInternal(modeHandler.Mode())
legacyAmino := codec.NewLegacyAmino()
legacytx.RegressionTestingAminoCodec = legacyAmino
legacy.RegisterAminoMsg(legacyAmino, &types.MsgUpdateParams{}, "cosmos-sdk/x/auth/MsgUpdateParams")
testcases := []struct {
name string
signer string
malleate func(*wrapper)
expectedSignBz []byte
}{
{
"signer which is also fee payer (no tips)", addr1.String(),
func(w *wrapper) {},
legacytx.StdSignBytes(chainID, accNum, seqNum, timeout, legacytx.StdFee{Amount: coins, Gas: gas}, []sdk.Msg{msg}, memo),
},
{
"explicit fee payer", addr1.String(),
func(w *wrapper) { w.SetFeePayer(addr2) },
legacytx.StdSignBytes(chainID, accNum, seqNum, timeout, legacytx.StdFee{Amount: coins, Gas: gas, Payer: addr2.String()}, []sdk.Msg{msg}, memo),
},
{
"explicit fee granter", addr1.String(),
func(w *wrapper) { w.SetFeeGranter(addr2) },
legacytx.StdSignBytes(chainID, accNum, seqNum, timeout, legacytx.StdFee{Amount: coins, Gas: gas, Granter: addr2.String()}, []sdk.Msg{msg}, memo),
},
{
"explicit fee payer and fee granter", addr1.String(),
func(w *wrapper) {
w.SetFeePayer(addr2)
w.SetFeeGranter(addr2)
},
legacytx.StdSignBytes(chainID, accNum, seqNum, timeout, legacytx.StdFee{Amount: coins, Gas: gas, Payer: addr2.String(), Granter: addr2.String()}, []sdk.Msg{msg}, memo),
},
}
handler := signModeLegacyAminoJSONHandler{}
for _, tc := range testcases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
bldr := newBuilder(nil)
buildTx(t, bldr)
tx := bldr.GetTx()
tc.malleate(bldr)
signingData := signing.SignerData{
Address: tc.signer,
ChainID: chainID,
AccountNumber: accNum,
Sequence: seqNum,
PubKey: pubkey1,
}
signBz, err := handler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, signingData, tx)
require.NoError(t, err)
// compare with new signing
newSignBz, err := signing.GetSignBytesAdapter(context.Background(), txsigning.NewHandlerMap(modeHandler), mode, signingData, tx)
require.NoError(t, err)
require.Equal(t, string(tc.expectedSignBz), string(signBz))
require.Equal(t, string(tc.expectedSignBz), string(newSignBz))
})
}
legacytx.RegressionTestingAminoCodec = nil
}
func TestLegacyAminoJSONHandler_DefaultMode(t *testing.T) {
handler := signModeLegacyAminoJSONHandler{}
require.Equal(t, signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, handler.DefaultMode())
}
func TestLegacyAminoJSONHandler_Modes(t *testing.T) {
handler := signModeLegacyAminoJSONHandler{}
require.Equal(t, []signingtypes.SignMode{signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON}, handler.Modes())
}

View File

@ -136,16 +136,18 @@ func mkTxResult(txConfig client.TxConfig, resTx *coretypes.ResultTx, resBlock *c
if err != nil {
return nil, err
}
p, ok := txb.(intoAny)
p, ok := txb.(*gogoTxWrapper)
if !ok {
return nil, fmt.Errorf("expecting a type implementing intoAny, got: %T", txb)
return nil, fmt.Errorf("unexpected type, wnted gogoTxWrapper, got: %T", txb)
}
any := p.AsAny()
return sdk.NewResponseResultTx(resTx, any, resBlock.Block.Time.Format(time.RFC3339)), nil
}
// Deprecated: this interface is used only internally for scenario we are
// deprecating (StdTxConfig support)
type intoAny interface {
AsAny() *codectypes.Any
tx, err := p.AsTx()
if err != nil {
return nil, err
}
anyTx, err := codectypes.NewAnyWithValue(tx)
if err != nil {
return nil, err
}
return sdk.NewResponseResultTx(resTx, anyTx, resBlock.Block.Time.Format(time.RFC3339)), nil
}

View File

@ -2,7 +2,6 @@ package tx
import (
"context"
"fmt"
"strings"
gogogrpc "github.com/cosmos/gogoproto/grpc"
@ -60,7 +59,7 @@ func (s txServer) GetTxsEvent(ctx context.Context, req *txtypes.GetTxsEventReque
for i, tx := range result.Txs {
protoTx, ok := tx.Tx.GetCachedValue().(*txtypes.Tx)
if !ok {
return nil, status.Errorf(codes.Internal, "expected %T, got %T", txtypes.Tx{}, tx.Tx.GetCachedValue())
return nil, status.Errorf(codes.Internal, "getting cached value failed expected %T, got %T", txtypes.Tx{}, tx.Tx.GetCachedValue())
}
txsList[i] = protoTx
@ -139,13 +138,6 @@ func (s txServer) GetTx(ctx context.Context, req *txtypes.GetTxRequest) (*txtype
}, nil
}
// protoTxProvider is a type which can provide a proto transaction. It is a
// workaround to get access to the wrapper TxBuilder's method GetProtoTx().
// ref: https://github.com/cosmos/cosmos-sdk/issues/10347
type protoTxProvider interface {
GetProtoTx() *txtypes.Tx
}
// GetBlockWithTxs returns a block with decoded txs.
func (s txServer) GetBlockWithTxs(ctx context.Context, req *txtypes.GetBlockWithTxsRequest) (*txtypes.GetBlockWithTxsResponse, error) {
if req == nil {
@ -186,11 +178,11 @@ func (s txServer) GetBlockWithTxs(ctx context.Context, req *txtypes.GetBlockWith
if err != nil {
return err
}
p, ok := txb.(protoTxProvider)
if !ok {
return sdkerrors.ErrTxDecode.Wrapf("could not cast %T to %T", txb, txtypes.Tx{})
p, err := txb.(interface{ AsTx() (*txtypes.Tx, error) }).AsTx()
if err != nil {
return err
}
txs = append(txs, p.GetProtoTx())
txs = append(txs, p)
return nil
}
if req.Pagination != nil && req.Pagination.Reverse {
@ -223,14 +215,28 @@ func (s txServer) BroadcastTx(ctx context.Context, req *txtypes.BroadcastTxReque
}
// TxEncode implements the ServiceServer.TxEncode RPC method.
func (s txServer) TxEncode(ctx context.Context, req *txtypes.TxEncodeRequest) (*txtypes.TxEncodeResponse, error) {
func (s txServer) TxEncode(_ context.Context, req *txtypes.TxEncodeRequest) (*txtypes.TxEncodeResponse, error) {
if req.Tx == nil {
return nil, status.Error(codes.InvalidArgument, "invalid empty tx")
}
txBuilder := &wrapper{tx: req.Tx}
bodyBytes, err := s.clientCtx.Codec.Marshal(req.Tx.Body)
if err != nil {
return nil, err
}
encodedBytes, err := s.clientCtx.TxConfig.TxEncoder()(txBuilder)
authInfoBytes, err := s.clientCtx.Codec.Marshal(req.Tx.AuthInfo)
if err != nil {
return nil, err
}
raw := &txtypes.TxRaw{
BodyBytes: bodyBytes,
AuthInfoBytes: authInfoBytes,
Signatures: req.Tx.Signatures,
}
encodedBytes, err := s.clientCtx.Codec.Marshal(raw)
if err != nil {
return nil, err
}
@ -241,7 +247,7 @@ func (s txServer) TxEncode(ctx context.Context, req *txtypes.TxEncodeRequest) (*
}
// TxEncodeAmino implements the ServiceServer.TxEncodeAmino RPC method.
func (s txServer) TxEncodeAmino(ctx context.Context, req *txtypes.TxEncodeAminoRequest) (*txtypes.TxEncodeAminoResponse, error) {
func (s txServer) TxEncodeAmino(_ context.Context, req *txtypes.TxEncodeAminoRequest) (*txtypes.TxEncodeAminoResponse, error) {
if req.AminoJson == "" {
return nil, status.Error(codes.InvalidArgument, "invalid empty tx json")
}
@ -263,7 +269,7 @@ func (s txServer) TxEncodeAmino(ctx context.Context, req *txtypes.TxEncodeAminoR
}
// TxDecode implements the ServiceServer.TxDecode RPC method.
func (s txServer) TxDecode(ctx context.Context, req *txtypes.TxDecodeRequest) (*txtypes.TxDecodeResponse, error) {
func (s txServer) TxDecode(_ context.Context, req *txtypes.TxDecodeRequest) (*txtypes.TxDecodeResponse, error) {
if req.TxBytes == nil {
return nil, status.Error(codes.InvalidArgument, "invalid empty tx bytes")
}
@ -273,18 +279,17 @@ func (s txServer) TxDecode(ctx context.Context, req *txtypes.TxDecodeRequest) (*
return nil, err
}
txWrapper, ok := txb.(*wrapper)
if ok {
return &txtypes.TxDecodeResponse{
Tx: txWrapper.tx,
}, nil
tx, err := txb.(interface{ AsTx() (*txtypes.Tx, error) }).AsTx() // TODO: maybe we can break the Tx interface to add this also
if err != nil {
return nil, err
}
return nil, fmt.Errorf("expected %T, got %T", &wrapper{}, txb)
return &txtypes.TxDecodeResponse{
Tx: tx,
}, nil
}
// TxDecodeAmino implements the ServiceServer.TxDecodeAmino RPC method.
func (s txServer) TxDecodeAmino(ctx context.Context, req *txtypes.TxDecodeAminoRequest) (*txtypes.TxDecodeAminoResponse, error) {
func (s txServer) TxDecodeAmino(_ context.Context, req *txtypes.TxDecodeAminoRequest) (*txtypes.TxDecodeAminoResponse, error) {
if req.AminoBinary == nil {
return nil, status.Error(codes.InvalidArgument, "invalid empty tx bytes")
}

View File

@ -3,6 +3,8 @@ package tx
import (
"fmt"
txv1beta1 "cosmossdk.io/api/cosmos/tx/v1beta1"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
@ -55,15 +57,15 @@ func SignatureDataToModeInfoAndSig(data signing.SignatureData) (*tx.ModeInfo, []
// ModeInfoAndSigToSignatureData converts a ModeInfo and raw bytes signature to a SignatureData or returns
// an error
func ModeInfoAndSigToSignatureData(modeInfo *tx.ModeInfo, sig []byte) (signing.SignatureData, error) {
switch modeInfo := modeInfo.Sum.(type) {
case *tx.ModeInfo_Single_:
func ModeInfoAndSigToSignatureData(modeInfoPb *txv1beta1.ModeInfo, sig []byte) (signing.SignatureData, error) {
switch modeInfo := modeInfoPb.Sum.(type) {
case *txv1beta1.ModeInfo_Single_:
return &signing.SingleSignatureData{
SignMode: modeInfo.Single.Mode,
SignMode: signing.SignMode(modeInfo.Single.Mode),
Signature: sig,
}, nil
case *tx.ModeInfo_Multi_:
case *txv1beta1.ModeInfo_Multi_:
multi := modeInfo.Multi
sigs, err := decodeMultisignatures(sig)
@ -80,7 +82,10 @@ func ModeInfoAndSigToSignatureData(modeInfo *tx.ModeInfo, sig []byte) (signing.S
}
return &signing.MultiSignatureData{
BitArray: multi.Bitarray,
BitArray: &cryptotypes.CompactBitArray{
ExtraBitsStored: multi.Bitarray.ExtraBitsStored,
Elems: multi.Bitarray.Elems,
},
Signatures: sigv2s,
}, nil

View File

@ -318,7 +318,9 @@ func (s *TxConfigTestSuite) TestWrapTxBuilder() {
err := txBuilder.SetMsgs(msg)
s.Require().NoError(err)
newTxBldr, err := s.TxConfig.WrapTxBuilder(txBuilder.GetTx())
tx := txBuilder.GetTx()
newTxBldr, err := s.TxConfig.WrapTxBuilder(tx)
s.Require().NoError(err)
s.Require().Equal(txBuilder, newTxBldr)
txBuilder.SetFeePayer(tx.FeePayer()) // NOTE: fee payer will be populated even if empty.
s.Require().Equal(txBuilder.GetTx(), newTxBldr.GetTx())
}

View File

@ -11,6 +11,7 @@ import (
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/suite"
_ "cosmossdk.io/api/cosmos/crypto/secp256k1"
"cosmossdk.io/core/genesis"
"cosmossdk.io/math"
storetypes "cosmossdk.io/store/types"

View File

@ -30,6 +30,8 @@ require (
)
require (
buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.32.0-20230509103710-5e5b9fdd0180.1 // indirect
buf.build/gen/go/tendermint/tendermint/protocolbuffers/go v1.32.0-20231117195010-33ed361a9051.1 // indirect
cosmossdk.io/x/accounts v0.0.0-00010101000000-000000000000 // indirect
cosmossdk.io/x/bank v0.0.0-00010101000000-000000000000 // indirect
cosmossdk.io/x/tx v0.13.0 // indirect
@ -168,6 +170,7 @@ replace github.com/cosmos/cosmos-sdk => ../../.
// TODO remove post spinning out all modules
replace (
cosmossdk.io/api => ../../api
cosmossdk.io/core => ../../core
cosmossdk.io/depinject => ../../depinject
cosmossdk.io/x/accounts => ../accounts

View File

@ -1,7 +1,9 @@
buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.32.0-20230509103710-5e5b9fdd0180.1 h1:7LKjxs607BNfGhtKLf+bi3SDJgpiGuTgOvemojsH8Hc=
buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.32.0-20230509103710-5e5b9fdd0180.1/go.mod h1:5GqIYthcy/ASmnKcaT26APpxMhZirnIHXHKki69zjWI=
buf.build/gen/go/tendermint/tendermint/protocolbuffers/go v1.32.0-20231117195010-33ed361a9051.1 h1:VooqQ3rklp3PwMTAE890M76w/8Z01OPa7RdgU9posFE=
buf.build/gen/go/tendermint/tendermint/protocolbuffers/go v1.32.0-20231117195010-33ed361a9051.1/go.mod h1:9KmeMJUsSG3IiIwK63Lh1ipZJrwd7KHrWZseJeHukcs=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cosmossdk.io/api v0.7.3-0.20231113122742-912390d5fc4a h1:Zr++x1RCJWi+K8bTZsQKdjtL4SzyHBLGM3Fcn75iWf0=
cosmossdk.io/api v0.7.3-0.20231113122742-912390d5fc4a/go.mod h1:7B/5XWh1HYwJk3DzWeNoxOSI+nGx1m5UyYfHLFuKzkw=
cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s=
cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0=
cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0=

View File

@ -97,6 +97,7 @@ func (d *Decoder) Decode(txBytes []byte) (*DecodedTx, error) {
var signers [][]byte
var msgs []proto.Message
seenSigners := map[string]struct{}{}
for _, anyMsg := range body.Messages {
msg, signerErr := anyutil.Unpack(anyMsg, fileResolver, d.signingCtx.TypeResolver())
if signerErr != nil {
@ -107,7 +108,14 @@ func (d *Decoder) Decode(txBytes []byte) (*DecodedTx, error) {
if signerErr != nil {
return nil, errors.Wrap(ErrTxDecode, signerErr.Error())
}
signers = append(signers, ss...)
for _, s := range ss {
_, seen := seenSigners[string(s)]
if seen {
continue
}
signers = append(signers, s)
seenSigners[string(s)] = struct{}{}
}
}
return &DecodedTx{

View File

@ -21,6 +21,8 @@ require (
)
require (
buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.32.0-20230509103710-5e5b9fdd0180.1 // indirect
buf.build/gen/go/tendermint/tendermint/protocolbuffers/go v1.32.0-20231117195010-33ed361a9051.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
@ -34,3 +36,5 @@ require (
google.golang.org/grpc v1.61.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
replace cosmossdk.io/api => ../../api

View File

@ -1,5 +1,7 @@
cosmossdk.io/api v0.7.2 h1:BO3i5fvKMKvfaUiMkCznxViuBEfyWA/k6w2eAF6q1C4=
cosmossdk.io/api v0.7.2/go.mod h1:IcxpYS5fMemZGqyYtErK7OqvdM0C8kdW3dq8Q/XIG38=
buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.32.0-20230509103710-5e5b9fdd0180.1 h1:7LKjxs607BNfGhtKLf+bi3SDJgpiGuTgOvemojsH8Hc=
buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.32.0-20230509103710-5e5b9fdd0180.1/go.mod h1:5GqIYthcy/ASmnKcaT26APpxMhZirnIHXHKki69zjWI=
buf.build/gen/go/tendermint/tendermint/protocolbuffers/go v1.32.0-20231117195010-33ed361a9051.1 h1:VooqQ3rklp3PwMTAE890M76w/8Z01OPa7RdgU9posFE=
buf.build/gen/go/tendermint/tendermint/protocolbuffers/go v1.32.0-20231117195010-33ed361a9051.1/go.mod h1:9KmeMJUsSG3IiIwK63Lh1ipZJrwd7KHrWZseJeHukcs=
cosmossdk.io/core v0.11.0 h1:vtIafqUi+1ZNAE/oxLOQQ7Oek2n4S48SWLG8h/+wdbo=
cosmossdk.io/core v0.11.0/go.mod h1:LaTtayWBSoacF5xNzoF8tmLhehqlA9z1SWiPuNC6X1w=
cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0=

View File

@ -71,6 +71,10 @@ func TestE2EJSONTestcases(t *testing.T) {
AuthInfoBytes: authInfoBz,
})
require.NoError(t, err)
decodeWant, err := hex.DecodeString(tc.Cbor)
require.NoError(t, err)
t.Log("got: " + string(signDoc))
t.Log("want " + string(decodeWant))
require.Equal(t, tc.Cbor, hex.EncodeToString(signDoc))
})
}

File diff suppressed because one or more lines are too long

View File

@ -142,7 +142,7 @@
"@type": "/cosmos.gov.v1.MsgVote",
"proposal_id": 1,
"voter": "cosmos1ulav3hsenupswqfkw2y3sup5kgtqwnvqa8eyhs",
"option": "VOTE_OPTION_YES",
"option": "VOTE_OPTION_ONE",
"metadata": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Also it ends in a single ampersand @"
}
]
@ -193,7 +193,7 @@
{ "title": "Message (1/1)", "content": "/cosmos.gov.v1.MsgVote", "indent": 1 },
{ "title": "Proposal id", "content": "1", "indent": 2 },
{ "title": "Voter", "content": "cosmos1ulav3hsenupswqfkw2y3sup5kgtqwnvqa8eyhs", "indent": 2 },
{ "title": "Option", "content": "VOTE_OPTION_YES", "indent": 2 },
{ "title": "Option", "content": "VOTE_OPTION_ONE", "indent": 2 },
{
"title": "Metadata",
"content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Also it ends in a single ampersand @",
@ -226,7 +226,7 @@
"@type": "/cosmos.gov.v1.MsgVote",
"proposal_id": 1,
"voter": "cosmos1ulav3hsenupswqfkw2y3sup5kgtqwnvqa8eyhs",
"option": "VOTE_OPTION_YES",
"option": "VOTE_OPTION_ONE",
"metadata": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Also it ends in a single ampersand @"
}
],
@ -339,7 +339,7 @@
{ "title": "Message (2/2)", "content": "/cosmos.gov.v1.MsgVote", "indent": 1 },
{ "title": "Proposal id", "content": "1", "indent": 2 },
{ "title": "Voter", "content": "cosmos1ulav3hsenupswqfkw2y3sup5kgtqwnvqa8eyhs", "indent": 2 },
{ "title": "Option", "content": "VOTE_OPTION_YES", "indent": 2 },
{ "title": "Option", "content": "VOTE_OPTION_ONE", "indent": 2 },
{
"title": "Metadata",
"content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Also it ends in a single ampersand @",