diff --git a/simapp/mint_fn.go b/simapp/mint_fn.go index e34aca38f4..9f2ee70ca5 100644 --- a/simapp/mint_fn.go +++ b/simapp/mint_fn.go @@ -17,7 +17,7 @@ import ( type MintBankKeeper interface { MintCoins(ctx context.Context, moduleName string, coins sdk.Coins) error - SendCoinsFromModuleToModule(ctx context.Context, senderModule string, recipientModule string, amt sdk.Coins) error + SendCoinsFromModuleToModule(ctx context.Context, senderModule, recipientModule string, amt sdk.Coins) error } // ProvideExampleMintFn returns the function used in x/mint's endblocker to mint new tokens. diff --git a/x/auth/ante/expected_keepers.go b/x/auth/ante/expected_keepers.go index 9c8612e8eb..f0f2db6bcc 100644 --- a/x/auth/ante/expected_keepers.go +++ b/x/auth/ante/expected_keepers.go @@ -30,5 +30,5 @@ type FeegrantKeeper interface { type ConsensusKeeper interface { Params(context.Context, *consensustypes.QueryParamsRequest) (*consensustypes.QueryParamsResponse, error) - GetCometInfo(context.Context, *consensustypes.QueryGetCometInfoRequest) (*consensustypes.QueryGetCometInfoResponse, error) + GetCometInfo(ctx context.Context, request *consensustypes.QueryGetCometInfoRequest) (*consensustypes.QueryGetCometInfoResponse, error) } diff --git a/x/auth/ante/testutil/expected_keepers_mocks.go b/x/auth/ante/testutil/expected_keepers_mocks.go index 3b1c898e01..35952cd580 100644 --- a/x/auth/ante/testutil/expected_keepers_mocks.go +++ b/x/auth/ante/testutil/expected_keepers_mocks.go @@ -196,18 +196,18 @@ func (m *MockConsensusKeeper) EXPECT() *MockConsensusKeeperMockRecorder { } // GetCometInfo mocks base method. -func (m *MockConsensusKeeper) GetCometInfo(arg0 context.Context, arg1 *types0.QueryGetCometInfoRequest) (*types0.QueryGetCometInfoResponse, error) { +func (m *MockConsensusKeeper) GetCometInfo(ctx context.Context, request *types0.QueryGetCometInfoRequest) (*types0.QueryGetCometInfoResponse, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetCometInfo", arg0, arg1) + ret := m.ctrl.Call(m, "GetCometInfo", ctx, request) ret0, _ := ret[0].(*types0.QueryGetCometInfoResponse) ret1, _ := ret[1].(error) return ret0, ret1 } // GetCometInfo indicates an expected call of GetCometInfo. -func (mr *MockConsensusKeeperMockRecorder) GetCometInfo(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockConsensusKeeperMockRecorder) GetCometInfo(ctx, request interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCometInfo", reflect.TypeOf((*MockConsensusKeeper)(nil).GetCometInfo), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCometInfo", reflect.TypeOf((*MockConsensusKeeper)(nil).GetCometInfo), ctx, request) } // Params mocks base method. diff --git a/x/auth/tx/config.go b/x/auth/tx/config.go index 90fbb8e39f..2b7675cd25 100644 --- a/x/auth/tx/config.go +++ b/x/auth/tx/config.go @@ -190,7 +190,11 @@ func NewTxConfigWithOptions(protoCodec codec.Codec, configOptions ConfigOptions) } if configOptions.ProtoDecoder == nil { - dec, err := txdecode.NewDecoder(txdecode.Options{SigningContext: configOptions.SigningContext}) + dec, err := txdecode.NewDecoder(txdecode.Options{ + SigningContext: configOptions.SigningContext, + ProtoCodec: protoCodec, + }, + ) if err != nil { return nil, err } diff --git a/x/auth/tx/gogotx.go b/x/auth/tx/gogotx.go index 4a5fa52c23..2aebf06635 100644 --- a/x/auth/tx/gogotx.go +++ b/x/auth/tx/gogotx.go @@ -74,7 +74,7 @@ func newWrapperFromDecodedTx(addrCodec address.Codec, cdc codec.BinaryCodec, dec // reflectMsgs reflectMsgs := make([]protoreflect.Message, len(msgs)) - for i, msg := range decodedTx.Messages { + for i, msg := range decodedTx.DynamicMessages { reflectMsgs[i] = msg.ProtoReflect() } diff --git a/x/auth/tx/testutil/suite.go b/x/auth/tx/testutil/suite.go index 4ed2cd7c3c..1689fca401 100644 --- a/x/auth/tx/testutil/suite.go +++ b/x/auth/tx/testutil/suite.go @@ -322,5 +322,12 @@ func (s *TxConfigTestSuite) TestWrapTxBuilder() { newTxBldr, err := s.TxConfig.WrapTxBuilder(tx) s.Require().NoError(err) txBuilder.SetFeePayer(tx.FeePayer()) // NOTE: fee payer will be populated even if empty. - s.Require().Equal(txBuilder.GetTx(), newTxBldr.GetTx()) + tx1 := txBuilder.GetTx() + tx2 := newTxBldr.GetTx() + + tx1Bytes, err := s.TxConfig.TxEncoder()(tx1) + s.Require().NoError(err) + tx2Bytes, err := s.TxConfig.TxEncoder()(tx2) + s.Require().NoError(err) + s.Require().Equal(tx1Bytes, tx2Bytes) } diff --git a/x/tx/decode/decode.go b/x/tx/decode/decode.go index 45a1c9ce96..994d54cf48 100644 --- a/x/tx/decode/decode.go +++ b/x/tx/decode/decode.go @@ -3,10 +3,14 @@ package decode import ( "crypto/sha256" "errors" + "fmt" + "reflect" + "strings" - "github.com/cosmos/cosmos-proto/anyutil" + gogoproto "github.com/cosmos/gogoproto/proto" "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/protoadapt" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/types/dynamicpb" v1beta1 "cosmossdk.io/api/cosmos/tx/v1beta1" "cosmossdk.io/core/transaction" @@ -16,7 +20,8 @@ import ( // DecodedTx contains the decoded transaction, its signers, and other flags. type DecodedTx struct { - Messages []proto.Message + DynamicMessages []proto.Message + Messages []gogoproto.Message Tx *v1beta1.Tx TxRaw *v1beta1.TxRaw Signers [][]byte @@ -30,14 +35,20 @@ type DecodedTx struct { var _ transaction.Tx = &DecodedTx{} +type gogoProtoCodec interface { + Unmarshal([]byte, gogoproto.Message) error +} + // Decoder contains the dependencies required for decoding transactions. type Decoder struct { signingCtx *signing.Context + codec gogoProtoCodec } // Options are options for creating a Decoder. type Options struct { SigningContext *signing.Context + ProtoCodec gogoProtoCodec } // NewDecoder creates a new Decoder for decoding transactions. @@ -45,9 +56,12 @@ func NewDecoder(options Options) (*Decoder, error) { if options.SigningContext == nil { return nil, errors.New("signing context is required") } - + if options.ProtoCodec == nil { + return nil, errors.New("proto codec is required for unmarshalling gogoproto messages") + } return &Decoder{ signingCtx: options.SigningContext, + codec: options.ProtoCodec, }, nil } @@ -105,16 +119,41 @@ func (d *Decoder) Decode(txBytes []byte) (*DecodedTx, error) { Signatures: raw.Signatures, } - var signers [][]byte - var msgs []proto.Message + var ( + signers [][]byte + dynamicMsgs []proto.Message + msgs []gogoproto.Message + ) seenSigners := map[string]struct{}{} for _, anyMsg := range body.Messages { - msg, signerErr := anyutil.Unpack(anyMsg, fileResolver, d.signingCtx.TypeResolver()) - if signerErr != nil { - return nil, errorsmod.Wrap(ErrTxDecode, signerErr.Error()) + typeURL := strings.TrimPrefix(anyMsg.TypeUrl, "/") + + // unmarshal into dynamic message + msgDesc, err := fileResolver.FindDescriptorByName(protoreflect.FullName(typeURL)) + if err != nil { + return nil, fmt.Errorf("protoFiles does not have descriptor %s: %w", anyMsg.TypeUrl, err) + } + dynamicMsg := dynamicpb.NewMessageType(msgDesc.(protoreflect.MessageDescriptor)).New().Interface() + err = anyMsg.UnmarshalTo(dynamicMsg) + if err != nil { + return nil, err + } + dynamicMsgs = append(dynamicMsgs, dynamicMsg) + + // unmarshal into gogoproto message + gogoType := gogoproto.MessageType(typeURL) + if gogoType == nil { + return nil, fmt.Errorf("cannot find type: %s", anyMsg.TypeUrl) + } + msg := reflect.New(gogoType.Elem()).Interface().(gogoproto.Message) + err = d.codec.Unmarshal(anyMsg.Value, msg) + if err != nil { + return nil, err } msgs = append(msgs, msg) - ss, signerErr := d.signingCtx.GetSigners(msg) + + // fetch signers with dynamic message + ss, signerErr := d.signingCtx.GetSigners(dynamicMsg) if signerErr != nil { return nil, errorsmod.Wrap(ErrTxDecode, signerErr.Error()) } @@ -130,6 +169,7 @@ func (d *Decoder) Decode(txBytes []byte) (*DecodedTx, error) { return &DecodedTx{ Messages: msgs, + DynamicMessages: dynamicMsgs, Tx: theTx, TxRaw: &raw, TxBodyHasUnknownNonCriticals: txBodyHasUnknownNonCriticals, @@ -157,12 +197,7 @@ func (dtx *DecodedTx) GetMessages() ([]transaction.Msg, error) { return nil, errors.New("messages not available or are nil") } - msgs := make([]transaction.Msg, len(dtx.Messages)) - for i, msg := range dtx.Messages { - msgs[i] = protoadapt.MessageV1Of(msg) - } - - return msgs, nil + return dtx.Messages, nil } func (dtx *DecodedTx) GetSenders() ([][]byte, error) { diff --git a/x/tx/decode/decode_test.go b/x/tx/decode/decode_test.go index a54702cbe9..60be253876 100644 --- a/x/tx/decode/decode_test.go +++ b/x/tx/decode/decode_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/cosmos/cosmos-proto/anyutil" + gogoproto "github.com/cosmos/gogoproto/proto" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" @@ -21,6 +22,12 @@ import ( "cosmossdk.io/x/tx/signing" ) +type mockCodec struct{} + +func (m mockCodec) Unmarshal(bytes []byte, message gogoproto.Message) error { + return gogoproto.Unmarshal(bytes, message) +} + func TestDecode(t *testing.T) { accSeq := uint64(2) @@ -46,9 +53,13 @@ func TestDecode(t *testing.T) { require.NoError(t, err) decoder, err := decode.NewDecoder(decode.Options{ SigningContext: signingCtx, + ProtoCodec: mockCodec{}, }) require.NoError(t, err) + gogoproto.RegisterType(&bankv1beta1.MsgSend{}, string((&bankv1beta1.MsgSend{}).ProtoReflect().Descriptor().FullName())) + gogoproto.RegisterType(&testpb.A{}, string((&testpb.A{}).ProtoReflect().Descriptor().FullName())) + testCases := []struct { name string msg proto.Message @@ -131,6 +142,7 @@ func TestDecodeTxBodyPanic(t *testing.T) { } dec, err := decode.NewDecoder(decode.Options{ SigningContext: signingCtx, + ProtoCodec: mockCodec{}, }) if err != nil { t.Fatal(err) diff --git a/x/tx/decode/fuzz_test.go b/x/tx/decode/fuzz_test.go index 38262a2007..0177fc530e 100644 --- a/x/tx/decode/fuzz_test.go +++ b/x/tx/decode/fuzz_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/cosmos/cosmos-proto/anyutil" + gogoproto "github.com/cosmos/gogoproto/proto" fuzz "github.com/google/gofuzz" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" @@ -17,6 +18,12 @@ import ( "cosmossdk.io/x/tx/signing" ) +type mockCodec struct{} + +func (m mockCodec) Unmarshal(bytes []byte, message gogoproto.Message) error { + return gogoproto.Unmarshal(bytes, message) +} + var ( accSeq = uint64(2) @@ -107,6 +114,7 @@ func FuzzDecode(f *testing.F) { } dec, err := NewDecoder(Options{ SigningContext: signingCtx, + ProtoCodec: mockCodec{}, }) if err != nil { return