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:
parent
0cf0c285ea
commit
e846eca366
@ -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) {
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
@ -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))
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)))
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
|
||||
@ -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{}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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=
|
||||
|
||||
@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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())
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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"
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -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
302
x/auth/tx/gogotx.go
Normal 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
1
x/auth/tx/gogotx_test.go
Normal file
@ -0,0 +1 @@
|
||||
package tx
|
||||
@ -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
|
||||
|
||||
@ -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())
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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")
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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())
|
||||
}
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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=
|
||||
|
||||
@ -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{
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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=
|
||||
|
||||
@ -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
@ -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 @",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user