diff --git a/CHANGELOG.md b/CHANGELOG.md index 6eb06693e1..88971a218e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -170,12 +170,10 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (crypto) [#15070](https://github.com/cosmos/cosmos-sdk/pull/15070) `GenerateFromPassword` and `Cost` from `bcrypt.go` now take a `uint32` instead of a `int` type. * (x/capability) [#15344](https://github.com/cosmos/cosmos-sdk/pull/15344) Capability module was removed and is now housed in [IBC-GO](https://github.com/cosmos/ibc-go). * [#15299](https://github.com/cosmos/cosmos-sdk/pull/15299) remove `StdTx` transaction and signing APIs. No SDK version has actually supported `StdTx` since before Stargate. -* (codec) [#15600](https://github.com/cosmos/cosmos-sdk/pull/15600) add support for getting signers to `codec.Codec` and protoregistry support to `InterfaceRegistry`: - * `Codec` is now a private interface and has the methods `InterfaceRegistry`, `GetMsgAnySigners`, `GetMsgV1Signers`, and `GetMsgV2Signers` which will fail when using `AminoCodec`. - All implementations of `Codec` by other users must now embed an official implementation from the `codec` package. - * `InterfaceRegistry` is now a private interface and implements `protodesc.Resolver` plus the `RangeFiles` method - All implementations of `InterfaceRegistry` by other users must now embed the official implementation. - * `AminoCodec` is marked as deprecated. +* (codec) [#15600](https://github.com/cosmos/cosmos-sdk/pull/15600) [#15873](https://github.com/cosmos/cosmos-sdk/pull/15873) add support for getting signers to `codec.Codec` and `InterfaceRegistry`: + * `Codec` has new methods `InterfaceRegistry`, `GetMsgAnySigners`, `GetMsgV1Signers`, and `GetMsgV2Signers` as well as unexported methods. All implementations of `Codec` by other users must now embed an official implementation from the `codec` package. + * `InterfaceRegistry` is has unexported methods and implements `protodesc.Resolver` plus the `RangeFiles` and `SigningContext` methods. All implementations of `InterfaceRegistry` by other users must now embed the official implementation. + * `AminoCodec` is marked as deprecated and no longer implements `Codec. * (x/crisis) [#15852](https://github.com/cosmos/cosmos-sdk/pull/15852) Crisis keeper now takes a instance of the address codec to be able to decode user addresses * (x/slashing) [#15875](https://github.com/cosmos/cosmos-sdk/pull/15875) `x/slashing.NewAppModule` now requires an `InterfaceRegistry` parameter. * (client) [#15822](https://github.com/cosmos/cosmos-sdk/pull/15822) The return type of the interface method `TxConfig.SignModeHandler` has been changed to `x/tx/signing.HandlerMap`. diff --git a/codec/amino_codec.go b/codec/amino_codec.go index 2b1e028645..c4bc0bd0d1 100644 --- a/codec/amino_codec.go +++ b/codec/amino_codec.go @@ -1,12 +1,7 @@ package codec import ( - "fmt" - "github.com/cosmos/gogoproto/proto" - protov2 "google.golang.org/protobuf/proto" - - "github.com/cosmos/cosmos-sdk/codec/types" ) // Deprecated: AminoCodec defines a codec that utilizes Codec for both binary and JSON @@ -16,7 +11,10 @@ type AminoCodec struct { *LegacyAmino } -var _ Codec = &AminoCodec{} +var ( + _ BinaryCodec = &AminoCodec{} + _ JSONCodec = &AminoCodec{} +) // Deprecated: NewAminoCodec returns a reference to a new AminoCodec. // Use NewLegacyAmino instead. @@ -131,21 +129,3 @@ func (ac *AminoCodec) MarshalInterfaceJSON(i proto.Message) ([]byte, error) { func (ac *AminoCodec) UnmarshalInterfaceJSON(bz []byte, ptr interface{}) error { return ac.LegacyAmino.UnmarshalJSON(bz, ptr) } - -func (ac *AminoCodec) GetMsgAnySigners(*types.Any) ([]string, protov2.Message, error) { - return nil, nil, fmt.Errorf("amino codec does not support getting msg signers") -} - -func (ac *AminoCodec) GetMsgV1Signers(proto.Message) ([]string, protov2.Message, error) { - return nil, nil, fmt.Errorf("amino codec does not support getting msg signers") -} - -func (ac *AminoCodec) GetMsgV2Signers(protov2.Message) ([]string, error) { - return nil, fmt.Errorf("amino codec does not support getting msg signers") -} - -func (ac *AminoCodec) InterfaceRegistry() types.InterfaceRegistry { - panic("amino codec does not support interface registry") -} - -func (ac *AminoCodec) mustEmbedCodec() {} diff --git a/codec/codec.go b/codec/codec.go index 7bdaffa69b..6256f78a80 100644 --- a/codec/codec.go +++ b/codec/codec.go @@ -26,15 +26,15 @@ type ( // GetMsgAnySigners returns the signers of the given message encoded in a protobuf Any // as well as the decoded google.golang.org/protobuf/proto.Message that was used to // extract the signers so that this can be used in other contexts. - GetMsgAnySigners(msg *types.Any) ([]string, protov2.Message, error) + GetMsgAnySigners(msg *types.Any) ([][]byte, protov2.Message, error) // GetMsgV2Signers returns the signers of the given message. - GetMsgV2Signers(msg protov2.Message) ([]string, error) + GetMsgV2Signers(msg protov2.Message) ([][]byte, error) // GetMsgV1Signers returns the signers of the given message plus the // decoded google.golang.org/protobuf/proto.Message that was used to extract the // signers so that this can be used in other contexts. - GetMsgV1Signers(msg proto.Message) ([]string, protov2.Message, error) + GetMsgV1Signers(msg proto.Message) ([][]byte, protov2.Message, error) // mustEmbedCodec requires that all implementations of Codec embed an official implementation from the codec // package. This allows new methods to be added to the Codec interface without breaking backwards compatibility. diff --git a/codec/codec_common_test.go b/codec/codec_common_test.go index 6c793fe172..de5552dd02 100644 --- a/codec/codec_common_test.go +++ b/codec/codec_common_test.go @@ -87,7 +87,11 @@ func testMarshalingTestCase(require *require.Assertions, tc testCase, m mustMars } } -func testMarshaling(t *testing.T, cdc codec.Codec) { +func testMarshaling(t *testing.T, cdc interface { + codec.BinaryCodec + codec.JSONCodec +}, +) { any, err := types.NewAnyWithValue(&testdata.Dog{Name: "rufus"}) require.NoError(t, err) diff --git a/codec/proto_codec.go b/codec/proto_codec.go index 965954ae2d..732a222d58 100644 --- a/codec/proto_codec.go +++ b/codec/proto_codec.go @@ -13,8 +13,6 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" - "cosmossdk.io/x/tx/signing" - "github.com/cosmos/cosmos-sdk/codec/types" ) @@ -29,7 +27,6 @@ type ProtoCodecMarshaler interface { // encoding. type ProtoCodec struct { interfaceRegistry types.InterfaceRegistry - getSignersCtx *signing.GetSignersContext } var ( @@ -39,16 +36,8 @@ var ( // NewProtoCodec returns a reference to a new ProtoCodec func NewProtoCodec(interfaceRegistry types.InterfaceRegistry) *ProtoCodec { - getSignersCtx, err := signing.NewGetSignersContext( - signing.GetSignersOptions{ - ProtoFiles: interfaceRegistry, - }) - if err != nil { - panic(err) - } return &ProtoCodec{ interfaceRegistry: interfaceRegistry, - getSignersCtx: getSignersCtx, } } @@ -277,7 +266,7 @@ func (pc *ProtoCodec) InterfaceRegistry() types.InterfaceRegistry { return pc.interfaceRegistry } -func (pc ProtoCodec) GetMsgAnySigners(msg *types.Any) ([]string, proto.Message, error) { +func (pc ProtoCodec) GetMsgAnySigners(msg *types.Any) ([][]byte, proto.Message, error) { msgv2, err := anyutil.Unpack(&anypb.Any{ TypeUrl: msg.TypeUrl, Value: msg.Value, @@ -286,17 +275,17 @@ func (pc ProtoCodec) GetMsgAnySigners(msg *types.Any) ([]string, proto.Message, return nil, nil, err } - signers, err := pc.getSignersCtx.GetSigners(msgv2) + signers, err := pc.interfaceRegistry.SigningContext().GetSigners(msgv2) return signers, msgv2, err } -func (pc *ProtoCodec) GetMsgV2Signers(msg proto.Message) ([]string, error) { - return pc.getSignersCtx.GetSigners(msg) +func (pc *ProtoCodec) GetMsgV2Signers(msg proto.Message) ([][]byte, error) { + return pc.interfaceRegistry.SigningContext().GetSigners(msg) } -func (pc *ProtoCodec) GetMsgV1Signers(msg gogoproto.Message) ([]string, proto.Message, error) { +func (pc *ProtoCodec) GetMsgV1Signers(msg gogoproto.Message) ([][]byte, proto.Message, error) { if msgV2, ok := msg.(proto.Message); ok { - signers, err := pc.getSignersCtx.GetSigners(msgV2) + signers, err := pc.interfaceRegistry.SigningContext().GetSigners(msgV2) return signers, msgV2, err } a, err := types.NewAnyWithValue(msg) diff --git a/codec/proto_codec_test.go b/codec/proto_codec_test.go index f0591dad4e..7e02f91aad 100644 --- a/codec/proto_codec_test.go +++ b/codec/proto_codec_test.go @@ -13,6 +13,7 @@ import ( "google.golang.org/grpc/encoding" "google.golang.org/grpc/status" protov2 "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoregistry" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/types" @@ -174,11 +175,16 @@ func BenchmarkProtoCodecMarshalLengthPrefixed(b *testing.B) { } func TestGetSigners(t *testing.T) { - interfaceRegistry := types.NewInterfaceRegistry() + interfaceRegistry, err := types.NewInterfaceRegistryWithOptions(types.InterfaceRegistryOptions{ + AddressCodec: testAddressCodec{}, + ValidatorAddressCodec: testAddressCodec{}, + ProtoFiles: protoregistry.GlobalFiles, + }) + require.NoError(t, err) cdc := codec.NewProtoCodec(interfaceRegistry) - testAddr := sdk.AccAddress([]byte("test")) + testAddr := sdk.AccAddress("test") testAddrStr := testAddr.String() - testAddr2 := sdk.AccAddress([]byte("test2")) + testAddr2 := sdk.AccAddress("test2") testAddrStr2 := testAddr2.String() msgSendV1 := banktypes.NewMsgSend(testAddr, testAddr2, sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(1)))) @@ -190,17 +196,27 @@ func TestGetSigners(t *testing.T) { signers, msgSendV2Copy, err := cdc.GetMsgV1Signers(msgSendV1) require.NoError(t, err) - require.Equal(t, []string{testAddrStr}, signers) + require.Equal(t, [][]byte{testAddr}, signers) require.True(t, protov2.Equal(msgSendV2, msgSendV2Copy)) signers, err = cdc.GetMsgV2Signers(msgSendV2) require.NoError(t, err) - require.Equal(t, []string{testAddrStr}, signers) + require.Equal(t, [][]byte{testAddr}, signers) msgSendAny, err := types.NewAnyWithValue(msgSendV1) require.NoError(t, err) signers, msgSendV2Copy, err = cdc.GetMsgAnySigners(msgSendAny) require.NoError(t, err) - require.Equal(t, []string{testAddrStr}, signers) + require.Equal(t, [][]byte{testAddr}, signers) require.True(t, protov2.Equal(msgSendV2, msgSendV2Copy)) } + +type testAddressCodec struct{} + +func (t testAddressCodec) StringToBytes(text string) ([]byte, error) { + return sdk.AccAddressFromBech32(text) +} + +func (t testAddressCodec) BytesToString(bz []byte) (string, error) { + return sdk.AccAddress(bz).String(), nil +} diff --git a/codec/types/interface_registry.go b/codec/types/interface_registry.go index 57a098e0cb..6d216b36f5 100644 --- a/codec/types/interface_registry.go +++ b/codec/types/interface_registry.go @@ -9,6 +9,10 @@ import ( "google.golang.org/protobuf/reflect/protodesc" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/reflect/protoregistry" + + "cosmossdk.io/core/address" + + "cosmossdk.io/x/tx/signing" ) // AnyUnpacker is an interface which allows safely unpacking types packed @@ -65,6 +69,8 @@ type InterfaceRegistry interface { // the entire FileDescriptorSet. RangeFiles(f func(protoreflect.FileDescriptor) bool) + SigningContext() *signing.Context + // mustEmbedInterfaceRegistry requires that all implementations of InterfaceRegistry embed an official implementation // from this package. This allows new methods to be added to the InterfaceRegistry interface without breaking // backwards compatibility. @@ -101,28 +107,60 @@ type interfaceRegistry struct { interfaceImpls map[reflect.Type]interfaceMap implInterfaces map[reflect.Type]reflect.Type typeURLMap map[string]reflect.Type + signingCtx *signing.Context } type interfaceMap = map[string]reflect.Type // NewInterfaceRegistry returns a new InterfaceRegistry func NewInterfaceRegistry() InterfaceRegistry { - protoFiles, err := proto.MergedRegistry() + registry, err := NewInterfaceRegistryWithOptions(InterfaceRegistryOptions{ + ProtoFiles: protoregistry.GlobalFiles, + AddressCodec: failingAddressCodec{}, + ValidatorAddressCodec: failingAddressCodec{}, + }) if err != nil { panic(err) } - return NewInterfaceRegistryWithProtoFiles(protoFiles) + return registry } -// NewInterfaceRegistryWithProtoFiles returns a new InterfaceRegistry with the specified *protoregistry.Files instance. -func NewInterfaceRegistryWithProtoFiles(files *protoregistry.Files) InterfaceRegistry { +// InterfaceRegistryOptions are options for creating a new InterfaceRegistry. +type InterfaceRegistryOptions struct { + // ProtoFiles is the set of files to use for the registry. It is required. + ProtoFiles *protoregistry.Files + + // AddressCodec is the address codec to use for the registry. It is required. + AddressCodec address.Codec + + // ValidatorAddressCodec is the validator address codec to use for the registry. It is required. + ValidatorAddressCodec address.Codec +} + +// NewInterfaceRegistryWithOptions returns a new InterfaceRegistry with the given options. +func NewInterfaceRegistryWithOptions(options InterfaceRegistryOptions) (InterfaceRegistry, error) { + if options.ProtoFiles == nil { + return nil, fmt.Errorf("proto files must be provided") + } + + signingCtx, err := signing.NewContext(signing.Options{ + FileResolver: options.ProtoFiles, + TypeResolver: nil, + AddressCodec: options.AddressCodec, + ValidatorAddressCodec: options.ValidatorAddressCodec, + }) + if err != nil { + return nil, err + } + return &interfaceRegistry{ interfaceNames: map[string]reflect.Type{}, interfaceImpls: map[reflect.Type]interfaceMap{}, implInterfaces: map[reflect.Type]reflect.Type{}, typeURLMap: map[string]reflect.Type{}, - Files: files, - } + Files: options.ProtoFiles, + signingCtx: signingCtx, + }, nil } func (registry *interfaceRegistry) RegisterInterface(protoName string, iface interface{}, impls ...proto.Message) { @@ -314,6 +352,10 @@ func (registry *interfaceRegistry) Resolve(typeURL string) (proto.Message, error return msg, nil } +func (registry *interfaceRegistry) SigningContext() *signing.Context { + return registry.signingCtx +} + func (registry *interfaceRegistry) mustEmbedInterfaceRegistry() {} // UnpackInterfaces is a convenience function that calls UnpackInterfaces @@ -324,3 +366,13 @@ func UnpackInterfaces(x interface{}, unpacker AnyUnpacker) error { } return nil } + +type failingAddressCodec struct{} + +func (f failingAddressCodec) StringToBytes(string) ([]byte, error) { + return nil, fmt.Errorf("InterfaceRegistry requires a proper address codec implementation to do address conversion") +} + +func (f failingAddressCodec) BytesToString([]byte) (string, error) { + return "", fmt.Errorf("InterfaceRegistry requires a proper address codec implementation to do address conversion") +} diff --git a/go.mod b/go.mod index b1859b435b..cfa645c51a 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( cosmossdk.io/log v1.1.0 cosmossdk.io/math v1.0.0 cosmossdk.io/store v0.1.0-alpha.1.0.20230328185921-37ba88872dbc - cosmossdk.io/x/tx v0.5.5 + cosmossdk.io/x/tx v0.6.1 github.com/99designs/keyring v1.2.1 github.com/armon/go-metrics v0.4.1 github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 @@ -162,6 +162,8 @@ require ( // Below are the long-lived replace of the Cosmos SDK replace ( + // TODO: remove after release 0.6.2 + cosmossdk.io/x/tx => ./x/tx // use cosmos fork of keyring github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0 // dgrijalva/jwt-go is deprecated and doesn't receive security updates. diff --git a/go.sum b/go.sum index ae0747ab4a..83d41875bf 100644 --- a/go.sum +++ b/go.sum @@ -51,8 +51,6 @@ cosmossdk.io/math v1.0.0 h1:ro9w7eKx23om2tZz/VM2Pf+z2WAbGX1yDQQOJ6iGeJw= cosmossdk.io/math v1.0.0/go.mod h1:Ygz4wBHrgc7g0N+8+MrnTfS9LLn9aaTGa9hKopuym5k= cosmossdk.io/store v0.1.0-alpha.1.0.20230328185921-37ba88872dbc h1:9piuA+NYmhe+SyMPtMoboLw/djgDbrI3dD5TG020Tnk= cosmossdk.io/store v0.1.0-alpha.1.0.20230328185921-37ba88872dbc/go.mod h1:UFF5rmjN7WYVfxo6ArdY/l1+yyWMURBWOmSJypGqFHQ= -cosmossdk.io/x/tx v0.5.5 h1:9XG3KOrqObt7Rw7KhT7fiqRd6EepUfmA9ERa8CHj1WM= -cosmossdk.io/x/tx v0.5.5/go.mod h1:Oh3Kh+IPOfMEILNxVd2e8SLqRrIjYHpdGBfDg4ghU/k= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= diff --git a/runtime/module.go b/runtime/module.go index 81cda6e1ed..562a24aed3 100644 --- a/runtime/module.go +++ b/runtime/module.go @@ -24,6 +24,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/std" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/types/msgservice" ) @@ -97,7 +98,22 @@ func ProvideApp() ( _, _ = fmt.Fprintln(os.Stderr, err.Error()) } - interfaceRegistry := codectypes.NewInterfaceRegistryWithProtoFiles(protoFiles) + interfaceRegistry, err := codectypes.NewInterfaceRegistryWithOptions(codectypes.InterfaceRegistryOptions{ + ProtoFiles: protoFiles, + AddressCodec: globalAccAddressCodec{}, + ValidatorAddressCodec: globalValAddressCodec{}, + }) + if err != nil { + return nil, nil, nil, nil, nil, nil, nil, nil, nil, err + } + + // validate the signing context to make sure that messages are properly configured + // with cosmos.msg.v1.signer + err = interfaceRegistry.SigningContext().Validate() + if err != nil { + return nil, nil, nil, nil, nil, nil, nil, nil, nil, err + } + amino := codec.NewLegacyAmino() std.RegisterInterfaces(interfaceRegistry) @@ -212,3 +228,33 @@ func ProvideTransientStoreService(key depinject.ModuleKey, app *AppBuilder) stor func ProvideEventService() event.Service { return EventService{} } + +// globalAccAddressCodec is a temporary address codec that we will use until we +// can populate it with the correct bech32 prefixes without depending on the global. +type globalAccAddressCodec struct{} + +func (g globalAccAddressCodec) StringToBytes(text string) ([]byte, error) { + if text == "" { + return nil, nil + } + return sdk.AccAddressFromBech32(text) +} + +func (g globalAccAddressCodec) BytesToString(bz []byte) (string, error) { + if bz == nil { + return "", nil + } + return sdk.AccAddress(bz).String(), nil +} + +// globalValAddressCodec is a temporary address codec that we will use until we +// can populate it with the correct bech32 prefixes without depending on the global. +type globalValAddressCodec struct{} + +func (g globalValAddressCodec) StringToBytes(text string) ([]byte, error) { + return sdk.ValAddressFromBech32(text) +} + +func (g globalValAddressCodec) BytesToString(bz []byte) (string, error) { + return sdk.ValAddress(bz).String(), nil +} diff --git a/simapp/app.go b/simapp/app.go index 500b4ad33a..9e4d2f6e6c 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -14,6 +14,8 @@ import ( "cosmossdk.io/client/v2/autocli" "cosmossdk.io/core/appmodule" "cosmossdk.io/log" + authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec" + abci "github.com/cometbft/cometbft/abci/types" dbm "github.com/cosmos/cosmos-db" "github.com/cosmos/gogoproto/proto" @@ -40,7 +42,6 @@ import ( "github.com/cosmos/cosmos-sdk/client/grpc/cmtservice" nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node" "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/codec/address" "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/runtime" runtimeservices "github.com/cosmos/cosmos-sdk/runtime/services" @@ -625,7 +626,10 @@ func (app *SimApp) AutoCliOpts() autocli.AppOptions { } } - return autocli.AppOptions{Modules: modules, AddressCodec: address.NewBech32Codec(sdk.Bech32MainPrefix)} + return autocli.AppOptions{ + Modules: modules, + AddressCodec: authcodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix()), + } } // DefaultGenesis returns a default genesis from the registered AppModuleBasic's. diff --git a/simapp/export.go b/simapp/export.go index 8198300e05..c489d01109 100644 --- a/simapp/export.go +++ b/simapp/export.go @@ -5,9 +5,10 @@ import ( "fmt" "log" - storetypes "cosmossdk.io/store/types" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + storetypes "cosmossdk.io/store/types" + servertypes "github.com/cosmos/cosmos-sdk/server/types" sdk "github.com/cosmos/cosmos-sdk/types" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" diff --git a/simapp/go.mod b/simapp/go.mod index b676c347c5..ec55cf1ab2 100644 --- a/simapp/go.mod +++ b/simapp/go.mod @@ -38,7 +38,7 @@ require ( cloud.google.com/go/storage v1.30.0 // indirect cosmossdk.io/collections v0.1.0 // indirect cosmossdk.io/errors v1.0.0-beta.7 // indirect - cosmossdk.io/x/tx v0.5.5 // indirect + cosmossdk.io/x/tx v0.6.1 // indirect filippo.io/edwards25519 v1.0.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.1 // indirect @@ -205,6 +205,8 @@ replace ( cosmossdk.io/x/feegrant => ../x/feegrant cosmossdk.io/x/nft => ../x/nft cosmossdk.io/x/upgrade => ../x/upgrade + // TODO: remove after release 0.6.2 + cosmossdk.io/x/tx => ../x/tx ) // Below are the long-lived replace of the SimApp diff --git a/simapp/go.sum b/simapp/go.sum index 616ad0fdbb..cbafd47091 100644 --- a/simapp/go.sum +++ b/simapp/go.sum @@ -200,8 +200,8 @@ cosmossdk.io/log v1.1.0 h1:v0ogPHYeTzPcBTcPR1A3j1hkei4pZama8kz8LKlCMv0= cosmossdk.io/log v1.1.0/go.mod h1:6zjroETlcDs+mm62gd8Ig7mZ+N+fVOZS91V17H+M4N4= cosmossdk.io/math v1.0.0 h1:ro9w7eKx23om2tZz/VM2Pf+z2WAbGX1yDQQOJ6iGeJw= cosmossdk.io/math v1.0.0/go.mod h1:Ygz4wBHrgc7g0N+8+MrnTfS9LLn9aaTGa9hKopuym5k= -cosmossdk.io/x/tx v0.5.5 h1:9XG3KOrqObt7Rw7KhT7fiqRd6EepUfmA9ERa8CHj1WM= -cosmossdk.io/x/tx v0.5.5/go.mod h1:Oh3Kh+IPOfMEILNxVd2e8SLqRrIjYHpdGBfDg4ghU/k= +cosmossdk.io/x/tx v0.6.1 h1:WwPkb7bfxGqrCKgPZuL6oW/9R51ufeFzWaXwIva6dVk= +cosmossdk.io/x/tx v0.6.1/go.mod h1:ps6ZlSuAjZFvHjZb7Kqp51Kf/UHQ/nEdEbjzXaYUhIk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= diff --git a/simapp/simd/cmd/root.go b/simapp/simd/cmd/root.go index 2d77344904..ba900be9dc 100644 --- a/simapp/simd/cmd/root.go +++ b/simapp/simd/cmd/root.go @@ -81,13 +81,12 @@ func NewRootCmd() *cobra.Command { // This needs to go after ReadFromClientConfig, as that function // sets the RPC client needed for SIGN_MODE_TEXTUAL. - opts, err := txmodule.NewSignModeOptionsWithMetadataQueryFn(txmodule.NewGRPCCoinMetadataQueryFn(initClientCtx)) - if err != nil { - return err + txConfigOpts := tx.ConfigOptions{ + TextualCoinMetadataQueryFn: txmodule.NewGRPCCoinMetadataQueryFn(initClientCtx), } txConfigWithTextual := tx.NewTxConfigWithOptions( codec.NewProtoCodec(encodingConfig.InterfaceRegistry), - opts, + txConfigOpts, ) initClientCtx = initClientCtx.WithTxConfig(txConfigWithTextual) diff --git a/tests/go.mod b/tests/go.mod index 9c39300a5e..da7df64a1f 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -13,7 +13,7 @@ require ( cosmossdk.io/x/evidence v0.1.0 cosmossdk.io/x/feegrant v0.0.0-20230117113717-50e7c4a4ceff cosmossdk.io/x/nft v0.0.0-20230113085233-fae3332d62fc - cosmossdk.io/x/tx v0.5.5 + cosmossdk.io/x/tx v0.6.1 cosmossdk.io/x/upgrade v0.0.0-20230127052425-54c8e1568335 github.com/cometbft/cometbft v0.37.1 github.com/cosmos/cosmos-db v1.0.0-rc.1 @@ -200,6 +200,9 @@ replace ( cosmossdk.io/x/upgrade => ../x/upgrade ) +// temporary replace +replace cosmossdk.io/x/tx => ../x/tx + // Below are the long-lived replace for tests. replace ( // We always want to test against the latest version of the simapp. diff --git a/tests/go.sum b/tests/go.sum index 33d2f831b8..1769a1e0c4 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -202,8 +202,6 @@ cosmossdk.io/log v1.1.0 h1:v0ogPHYeTzPcBTcPR1A3j1hkei4pZama8kz8LKlCMv0= cosmossdk.io/log v1.1.0/go.mod h1:6zjroETlcDs+mm62gd8Ig7mZ+N+fVOZS91V17H+M4N4= cosmossdk.io/math v1.0.0 h1:ro9w7eKx23om2tZz/VM2Pf+z2WAbGX1yDQQOJ6iGeJw= cosmossdk.io/math v1.0.0/go.mod h1:Ygz4wBHrgc7g0N+8+MrnTfS9LLn9aaTGa9hKopuym5k= -cosmossdk.io/x/tx v0.5.5 h1:9XG3KOrqObt7Rw7KhT7fiqRd6EepUfmA9ERa8CHj1WM= -cosmossdk.io/x/tx v0.5.5/go.mod h1:Oh3Kh+IPOfMEILNxVd2e8SLqRrIjYHpdGBfDg4ghU/k= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= diff --git a/tests/integration/tx/decode_test.go b/tests/integration/tx/decode_test.go index d3f38376b6..43094b0170 100644 --- a/tests/integration/tx/decode_test.go +++ b/tests/integration/tx/decode_test.go @@ -12,6 +12,7 @@ import ( "cosmossdk.io/x/evidence" feegrantmodule "cosmossdk.io/x/feegrant/module" "cosmossdk.io/x/tx/decode" + txsigning "cosmossdk.io/x/tx/signing" "cosmossdk.io/x/upgrade" "github.com/cosmos/cosmos-sdk/codec/legacy" codectypes "github.com/cosmos/cosmos-sdk/codec/types" @@ -114,7 +115,12 @@ func TestDecode(t *testing.T) { tx := txBuilder.GetTx() txBytes, err := encCfg.TxConfig.TxEncoder()(tx) require.NoError(t, err) - decodeCtx, err := decode.NewDecoder(decode.Options{}) + signContext, err := txsigning.NewContext(txsigning.Options{ + AddressCodec: dummyAddressCodec{}, + ValidatorAddressCodec: dummyAddressCodec{}, + }) + require.NoError(t, err) + decodeCtx, err := decode.NewDecoder(decode.Options{SigningContext: signContext}) require.NoError(t, err) decodedTx, err := decodeCtx.Decode(txBytes) require.NoError(t, err) @@ -139,3 +145,13 @@ func TestDecode(t *testing.T) { }) } } + +type dummyAddressCodec struct{} + +func (d dummyAddressCodec) StringToBytes(text string) ([]byte, error) { + return []byte(text), nil +} + +func (d dummyAddressCodec) BytesToString(bz []byte) (string, error) { + return string(bz), nil +} diff --git a/tools/rosetta/go.mod b/tools/rosetta/go.mod index 74ce91bfc6..6095063389 100644 --- a/tools/rosetta/go.mod +++ b/tools/rosetta/go.mod @@ -23,7 +23,7 @@ require ( cosmossdk.io/depinject v1.0.0-alpha.3 // indirect cosmossdk.io/errors v1.0.0-beta.7 // indirect cosmossdk.io/store v0.1.0-alpha.1.0.20230328185921-37ba88872dbc // indirect - cosmossdk.io/x/tx v0.5.5 // indirect + cosmossdk.io/x/tx v0.6.1 // indirect filippo.io/edwards25519 v1.0.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.1 // indirect @@ -139,5 +139,8 @@ require ( sigs.k8s.io/yaml v1.3.0 // indirect ) -// this seems to be required -replace github.com/cosmos/cosmos-sdk => ../.. +// TODO: remove after merge of https://github.com/cosmos/cosmos-sdk/pull/15873 and tagging releases +replace ( + github.com/cosmos/cosmos-sdk => ../.. + cosmossdk.io/x/tx => ../../x/tx +) \ No newline at end of file diff --git a/tools/rosetta/go.sum b/tools/rosetta/go.sum index 2c447c6392..e7983738a9 100644 --- a/tools/rosetta/go.sum +++ b/tools/rosetta/go.sum @@ -51,8 +51,8 @@ cosmossdk.io/math v1.0.0 h1:ro9w7eKx23om2tZz/VM2Pf+z2WAbGX1yDQQOJ6iGeJw= cosmossdk.io/math v1.0.0/go.mod h1:Ygz4wBHrgc7g0N+8+MrnTfS9LLn9aaTGa9hKopuym5k= cosmossdk.io/store v0.1.0-alpha.1.0.20230328185921-37ba88872dbc h1:9piuA+NYmhe+SyMPtMoboLw/djgDbrI3dD5TG020Tnk= cosmossdk.io/store v0.1.0-alpha.1.0.20230328185921-37ba88872dbc/go.mod h1:UFF5rmjN7WYVfxo6ArdY/l1+yyWMURBWOmSJypGqFHQ= -cosmossdk.io/x/tx v0.5.5 h1:9XG3KOrqObt7Rw7KhT7fiqRd6EepUfmA9ERa8CHj1WM= -cosmossdk.io/x/tx v0.5.5/go.mod h1:Oh3Kh+IPOfMEILNxVd2e8SLqRrIjYHpdGBfDg4ghU/k= +cosmossdk.io/x/tx v0.6.1 h1:WwPkb7bfxGqrCKgPZuL6oW/9R51ufeFzWaXwIva6dVk= +cosmossdk.io/x/tx v0.6.1/go.mod h1:ps6ZlSuAjZFvHjZb7Kqp51Kf/UHQ/nEdEbjzXaYUhIk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= @@ -172,6 +172,8 @@ github.com/cosmos/cosmos-db v1.0.0-rc.1 h1:SjnT8B6WKMW9WEIX32qMhnEEKcI7ZP0+G1Sa9 github.com/cosmos/cosmos-db v1.0.0-rc.1/go.mod h1:Dnmk3flSf5lkwCqvvjNpoxjpXzhxnCAFzKHlbaForso= github.com/cosmos/cosmos-proto v1.0.0-beta.3 h1:VitvZ1lPORTVxkmF2fAp3IiA61xVwArQYKXTdEcpW6o= github.com/cosmos/cosmos-proto v1.0.0-beta.3/go.mod h1:t8IASdLaAq+bbHbjq4p960BvcTqtwuAxid3b/2rOD6I= +github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20230424095137-b73c17cb9cc8 h1:zIl1WnrW5ZP1VwhpbwVBZtCntkNKYNIkg4233/dZ3BU= +github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20230424095137-b73c17cb9cc8/go.mod h1:JicgV9n3SAu5uuoyDvQ2gSHYLyFvyRrIUYB5T2Q4HRw= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= diff --git a/x/auth/ante/sigverify_test.go b/x/auth/ante/sigverify_test.go index 6309f72633..e5538ee03e 100644 --- a/x/auth/ante/sigverify_test.go +++ b/x/auth/ante/sigverify_test.go @@ -132,11 +132,13 @@ func TestSigVerification(t *testing.T) { enabledSignModes := []signing.SignMode{signing.SignMode_SIGN_MODE_DIRECT, signing.SignMode_SIGN_MODE_TEXTUAL, signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON} // Since TEXTUAL is not enabled by default, we create a custom TxConfig // here which includes it. - opts, err := txmodule.NewSignModeOptionsWithMetadataQueryFn(txmodule.NewGRPCCoinMetadataQueryFn(suite.clientCtx)) - require.NoError(t, err) + txConfigOpts := authtx.ConfigOptions{ + TextualCoinMetadataQueryFn: txmodule.NewGRPCCoinMetadataQueryFn(suite.clientCtx), + EnabledSignModes: enabledSignModes, + } suite.clientCtx.TxConfig = authtx.NewTxConfigWithOptions( codec.NewProtoCodec(suite.encCfg.InterfaceRegistry), - opts, + txConfigOpts, ) suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() @@ -163,11 +165,13 @@ func TestSigVerification(t *testing.T) { gasLimit := testdata.NewTestGasLimit() spkd := ante.NewSetPubKeyDecorator(suite.accountKeeper) - opts, err = txmodule.NewSignModeOptionsWithMetadataQueryFn(txmodule.NewBankKeeperCoinMetadataQueryFn(suite.txBankKeeper)) - require.NoError(t, err) + txConfigOpts = authtx.ConfigOptions{ + TextualCoinMetadataQueryFn: txmodule.NewBankKeeperCoinMetadataQueryFn(suite.txBankKeeper), + EnabledSignModes: enabledSignModes, + } anteTxConfig := authtx.NewTxConfigWithOptions( codec.NewProtoCodec(suite.encCfg.InterfaceRegistry), - opts, + txConfigOpts, ) svd := ante.NewSigVerificationDecorator(suite.accountKeeper, anteTxConfig.SignModeHandler()) antehandler := sdk.ChainAnteDecorators(spkd, svd) diff --git a/x/auth/client/cli/tx_multisign.go b/x/auth/client/cli/tx_multisign.go index 1d1e0b2a5a..6d03db5949 100644 --- a/x/auth/client/cli/tx_multisign.go +++ b/x/auth/client/cli/tx_multisign.go @@ -10,14 +10,12 @@ import ( "google.golang.org/protobuf/types/known/anypb" errorsmod "cosmossdk.io/errors" - "cosmossdk.io/x/tx/decode" - txsigning "cosmossdk.io/x/tx/signing" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/types/registry" + txsigning "cosmossdk.io/x/tx/signing" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/keyring" kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" "github.com/cosmos/cosmos-sdk/crypto/types/multisig" @@ -117,11 +115,6 @@ func makeMultiSignCmd() func(cmd *cobra.Command, args []string) (err error) { txFactory = txFactory.WithAccountNumber(accnum).WithSequence(seq) } - decoder, err := decode.NewDecoder(decode.Options{ProtoFiles: registry.MergedProtoRegistry()}) - if err != nil { - return err - } - // read each signature and add it to the multisig if valid for i := 2; i < len(args); i++ { sigs, err := unmarshalSignatureJSON(clientCtx, args[i]) @@ -148,20 +141,12 @@ func makeMultiSignCmd() func(cmd *cobra.Command, args []string) (err error) { Value: anyPk.Value, }, } - txBytes, err := txCfg.TxEncoder()(txBuilder.GetTx()) - if err != nil { - return err - } - decodedTx, err := decoder.Decode(txBytes) - if err != nil { - return err - } - txData := txsigning.TxData{ - Body: decodedTx.Tx.Body, - AuthInfo: decodedTx.Tx.AuthInfo, - AuthInfoBytes: decodedTx.TxRaw.AuthInfoBytes, - BodyBytes: decodedTx.TxRaw.BodyBytes, + builtTx := txBuilder.GetTx() + adaptableTx, ok := builtTx.(signing.V2AdaptableTx) + if !ok { + return fmt.Errorf("expected Tx to be signing.V2AdaptableTx, got %T", builtTx) } + txData := adaptableTx.GetSigningTxData() err = signing.VerifySignature(cmd.Context(), sig.PubKey, txSignerData, sig.Data, txCfg.SignModeHandler(), txData) @@ -343,24 +328,13 @@ func makeBatchMultisignCmd() func(cmd *cobra.Command, args []string) error { }, } - txBytes, err := txCfg.TxEncoder()(txBldr.GetTx()) - if err != nil { - return err - } - decodeCtx, err := decode.NewDecoder(decode.Options{ProtoFiles: registry.MergedProtoRegistry()}) - if err != nil { - return err - } - decodedTx, err := decodeCtx.Decode(txBytes) - if err != nil { - return err - } - txData := txsigning.TxData{ - Body: decodedTx.Tx.Body, - AuthInfo: decodedTx.Tx.AuthInfo, - AuthInfoBytes: decodedTx.TxRaw.AuthInfoBytes, - BodyBytes: decodedTx.TxRaw.BodyBytes, + builtTx := txBldr.GetTx() + adaptableTx, ok := builtTx.(signing.V2AdaptableTx) + if !ok { + return fmt.Errorf("expected Tx to be signing.V2AdaptableTx, got %T", builtTx) } + txData := adaptableTx.GetSigningTxData() + for _, sig := range signatureBatch { err = signing.VerifySignature(cmd.Context(), sig[i].PubKey, txSignerData, sig[i].Data, txCfg.SignModeHandler(), txData) diff --git a/x/auth/client/cli/validate_sigs.go b/x/auth/client/cli/validate_sigs.go index a38283fd2d..42bc4fe276 100644 --- a/x/auth/client/cli/validate_sigs.go +++ b/x/auth/client/cli/validate_sigs.go @@ -6,14 +6,12 @@ import ( "github.com/spf13/cobra" "google.golang.org/protobuf/types/known/anypb" - "cosmossdk.io/x/tx/decode" txsigning "cosmossdk.io/x/tx/signing" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/registry" authclient "github.com/cosmos/cosmos-sdk/x/auth/client" authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" ) @@ -132,27 +130,12 @@ func printAndValidateSigs( }, } - txBytes, err := clientCtx.TxConfig.TxEncoder()(tx) - if err != nil { - cmd.PrintErrf("failed to encode transaction: %v", err) + adaptableTx, ok := tx.(authsigning.V2AdaptableTx) + if !ok { + cmd.PrintErrf("expected V2AdaptableTx, got %T", tx) return false } - decodeCtx, err := decode.NewDecoder(decode.Options{ProtoFiles: registry.MergedProtoRegistry()}) - if err != nil { - cmd.PrintErrf("failed to create decoder: %v", err) - return false - } - decodedTx, err := decodeCtx.Decode(txBytes) - if err != nil { - cmd.PrintErrf("failed to decode transaction: %v", err) - return false - } - txData := txsigning.TxData{ - Body: decodedTx.Tx.Body, - AuthInfo: decodedTx.Tx.AuthInfo, - AuthInfoBytes: decodedTx.TxRaw.AuthInfoBytes, - BodyBytes: decodedTx.TxRaw.BodyBytes, - } + txData := adaptableTx.GetSigningTxData() err = authsigning.VerifySignature(cmd.Context(), pubKey, txSignerData, sig.Data, signModeHandler, txData) if err != nil { diff --git a/x/auth/keeper/bech32_codec.go b/x/auth/codec/bech32_codec.go similarity index 98% rename from x/auth/keeper/bech32_codec.go rename to x/auth/codec/bech32_codec.go index dd0b2ab02e..bf29ae4cec 100644 --- a/x/auth/keeper/bech32_codec.go +++ b/x/auth/codec/bech32_codec.go @@ -1,4 +1,4 @@ -package keeper +package codec import ( "errors" diff --git a/x/auth/keeper/keeper.go b/x/auth/keeper/keeper.go index f4dc162d33..b66df333ba 100644 --- a/x/auth/keeper/keeper.go +++ b/x/auth/keeper/keeper.go @@ -16,6 +16,7 @@ import ( cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec" "github.com/cosmos/cosmos-sdk/x/auth/types" ) @@ -65,6 +66,7 @@ type AccountKeeper struct { storeService store.KVStoreService cdc codec.BinaryCodec permAddrs map[string]types.PermissionsForAddress + bech32Prefix string // The prototypical AccountI constructor. proto func() sdk.AccountI @@ -98,7 +100,8 @@ func NewAccountKeeper( sb := collections.NewSchemaBuilder(storeService) return AccountKeeper{ - Codec: NewBech32Codec(bech32Prefix), + Codec: authcodec.NewBech32Codec(bech32Prefix), + bech32Prefix: bech32Prefix, storeService: storeService, proto: proto, cdc: cdc, @@ -256,12 +259,7 @@ func (ak AccountKeeper) GetCodec() codec.BinaryCodec { return ak.cdc } // add getter for bech32Prefix func (ak AccountKeeper) getBech32Prefix() (string, error) { - bech32Codec, ok := ak.Codec.(bech32Codec) - if !ok { - return "", fmt.Errorf("unable cast addressCdc to bech32Codec; expected %T got %T", bech32Codec, ak.Codec) - } - - return bech32Codec.bech32Prefix, nil + return ak.bech32Prefix, nil } // SetParams sets the auth module's parameters. diff --git a/x/auth/module.go b/x/auth/module.go index 684a4d8511..84ef5489b2 100644 --- a/x/auth/module.go +++ b/x/auth/module.go @@ -10,6 +10,7 @@ import ( "github.com/spf13/cobra" "cosmossdk.io/depinject" + authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec" "cosmossdk.io/core/address" "cosmossdk.io/core/appmodule" @@ -207,7 +208,7 @@ func init() { // ProvideAddressCodec provides an address.Codec to the container for any // modules that want to do address string <> bytes conversion. func ProvideAddressCodec(config *modulev1.Module) address.Codec { - return keeper.NewBech32Codec(config.Bech32Prefix) + return authcodec.NewBech32Codec(config.Bech32Prefix) } type ModuleInputs struct { diff --git a/x/auth/tx/config.go b/x/auth/tx/config.go index 861289d145..448c9ebb02 100644 --- a/x/auth/tx/config.go +++ b/x/auth/tx/config.go @@ -9,11 +9,12 @@ import ( "cosmossdk.io/x/tx/signing/aminojson" "cosmossdk.io/x/tx/signing/direct" "cosmossdk.io/x/tx/signing/directaux" - + "cosmossdk.io/x/tx/signing/textual" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" + authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec" ) type config struct { @@ -25,12 +26,47 @@ type config struct { protoCodec codec.ProtoCodecMarshaler } +// ConfigOptions define the configuration of a TxConfig when calling NewTxConfigWithOptions. +// An empty struct is a valid configuration and will result in a TxConfig with default values. +type ConfigOptions struct { + // If SigningHandler is specified it will be used instead constructing one. + // This option supersedes all below options, whose sole purpose are to configure the creation of + // txsigning.HandlerMap. + SigningHandler *txsigning.HandlerMap + // EnabledSignModes is the list of sign modes that will be enabled in the txsigning.HandlerMap. + EnabledSignModes []signingtypes.SignMode + // If SigningContext is specified it will be used when constructing sign mode handlers. If nil, one will be created + // with the options specified in SigningOptions. + SigningContext *txsigning.Context + // SigningOptions are the options that will be used when constructing a txsigning.Context and sign mode handlers. + // If nil defaults will be used. + SigningOptions *txsigning.Options + // TextualCoinMetadataQueryFn is the function that will be used to query coin metadata when constructing + // textual sign mode handler. This is required if SIGN_MODE_TEXTUAL is enabled. + TextualCoinMetadataQueryFn textual.CoinMetadataQueryFn + // CustomSignModes are the custom sign modes that will be added to the txsigning.HandlerMap. + CustomSignModes []txsigning.SignModeHandler +} + +// DefaultSignModes are the default sign modes enabled for protobuf transactions. +var DefaultSignModes = []signingtypes.SignMode{ + signingtypes.SignMode_SIGN_MODE_DIRECT, + signingtypes.SignMode_SIGN_MODE_DIRECT_AUX, + signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, + // We currently don't add SIGN_MODE_TEXTUAL as part of the default sign + // modes, as it's not released yet (including the Ledger app). However, + // textual's sign mode handler is already available in this package. If you + // want to use textual for **TESTING** purposes, feel free to create a + // handler that includes SIGN_MODE_TEXTUAL. + // ref: Tracking issue for SIGN_MODE_TEXTUAL https://github.com/cosmos/cosmos-sdk/issues/11970 +} + // NewTxConfig returns a new protobuf TxConfig using the provided ProtoCodec and sign modes. The // first enabled sign mode will become the default sign mode. // -// NOTE: Use NewTxConfigWithHandler to provide a custom signing handler in case the sign mode -// is not supported by default (eg: SignMode_SIGN_MODE_EIP_191). Use NewTxConfigWithOptions -// to enable SIGN_MODE_TEXTUAL (for testing purposes for now). +// NOTE: Use NewTxConfigWithOptions to provide a custom signing handler in case the sign mode +// is not supported by default (eg: SignMode_SIGN_MODE_EIP_191), or to enable SIGN_MODE_TEXTUAL +// (for testing purposes for now). // // We prefer to use depinject to provide client.TxConfig, but we permit this constructor usage. Within the SDK, // this constructor is primarily used in tests, but also sees usage in app chains like: @@ -38,55 +74,100 @@ type config struct { func NewTxConfig(protoCodec codec.ProtoCodecMarshaler, enabledSignModes []signingtypes.SignMode, customSignModes ...txsigning.SignModeHandler, ) client.TxConfig { - typeResolver := protoregistry.GlobalTypes - protoFiles := protoCodec.InterfaceRegistry() - signersContext, err := txsigning.NewGetSignersContext(txsigning.GetSignersOptions{ProtoFiles: protoFiles}) - if err != nil { - panic(err) - } - - signModeOptions := &SignModeOptions{} - for _, m := range enabledSignModes { - switch m { - case signingtypes.SignMode_SIGN_MODE_DIRECT: - signModeOptions.Direct = &direct.SignModeHandler{} - case signingtypes.SignMode_SIGN_MODE_DIRECT_AUX: - signModeOptions.DirectAux = &directaux.SignModeHandlerOptions{ - FileResolver: protoFiles, - TypeResolver: typeResolver, - SignersContext: signersContext, - } - case signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON: - aminoJSONEncoder := aminojson.NewAminoJSON() - signModeOptions.AminoJSON = &aminojson.SignModeHandlerOptions{ - FileResolver: protoFiles, - TypeResolver: typeResolver, - Encoder: &aminoJSONEncoder, - } - case signingtypes.SignMode_SIGN_MODE_TEXTUAL: - panic("cannot use NewTxConfig with SIGN_MODE_TEXTUAL enabled; please use NewTxConfigWithOptions") - } - } - - return NewTxConfigWithHandler(protoCodec, makeSignModeHandler(*signModeOptions, customSignModes...)) + return NewTxConfigWithOptions(protoCodec, ConfigOptions{ + EnabledSignModes: enabledSignModes, + CustomSignModes: customSignModes, + }) } -func NewTxConfigWithOptions(protoCodec codec.ProtoCodecMarshaler, signModeOptions SignModeOptions, - customSignModes ...txsigning.SignModeHandler, -) client.TxConfig { - return NewTxConfigWithHandler(protoCodec, makeSignModeHandler(signModeOptions, customSignModes...)) -} - -// NewTxConfigWithHandler returns a new protobuf TxConfig using the provided ProtoCodec and signing handler. -func NewTxConfigWithHandler(protoCodec codec.ProtoCodecMarshaler, handler *txsigning.HandlerMap) client.TxConfig { - return &config{ - handler: handler, +// NewTxConfigWithOptions returns a new protobuf TxConfig using the provided ProtoCodec, ConfigOptions and +// custom sign mode handlers. If ConfigOptions is an empty struct then default values will be used. +func NewTxConfigWithOptions(protoCodec codec.ProtoCodecMarshaler, configOptions ConfigOptions) client.TxConfig { + txConfig := &config{ decoder: DefaultTxDecoder(protoCodec), encoder: DefaultTxEncoder(), jsonDecoder: DefaultJSONTxDecoder(protoCodec), jsonEncoder: DefaultJSONTxEncoder(protoCodec), protoCodec: protoCodec, } + + opts := &configOptions + if opts.SigningHandler != nil { + txConfig.handler = opts.SigningHandler + return txConfig + } + + signingOpts := opts.SigningOptions + if signingOpts == nil { + signingOpts = &txsigning.Options{} + } + if signingOpts.TypeResolver == nil { + signingOpts.TypeResolver = protoregistry.GlobalTypes + } + if signingOpts.FileResolver == nil { + signingOpts.FileResolver = protoCodec.InterfaceRegistry() + } + if len(opts.EnabledSignModes) == 0 { + opts.EnabledSignModes = DefaultSignModes + } + + if opts.SigningContext == nil { + sdkConfig := sdk.GetConfig() + if signingOpts.AddressCodec == nil { + signingOpts.AddressCodec = authcodec.NewBech32Codec(sdkConfig.GetBech32AccountAddrPrefix()) + } + if signingOpts.ValidatorAddressCodec == nil { + signingOpts.ValidatorAddressCodec = authcodec.NewBech32Codec(sdkConfig.GetBech32ValidatorAddrPrefix()) + } + var err error + opts.SigningContext, err = txsigning.NewContext(*signingOpts) + if err != nil { + panic(err) + } + } + + lenSignModes := len(configOptions.EnabledSignModes) + handlers := make([]txsigning.SignModeHandler, lenSignModes+len(opts.CustomSignModes)) + for i, m := range configOptions.EnabledSignModes { + var err error + switch m { + case signingtypes.SignMode_SIGN_MODE_DIRECT: + handlers[i] = &direct.SignModeHandler{} + case signingtypes.SignMode_SIGN_MODE_DIRECT_AUX: + handlers[i], err = directaux.NewSignModeHandler(directaux.SignModeHandlerOptions{ + TypeResolver: signingOpts.TypeResolver, + SignersContext: opts.SigningContext, + }) + if err != nil { + panic(err) + } + case signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON: + aminoJSONEncoder := aminojson.NewAminoJSON() + handlers[i] = aminojson.NewSignModeHandler(aminojson.SignModeHandlerOptions{ + FileResolver: signingOpts.FileResolver, + TypeResolver: signingOpts.TypeResolver, + Encoder: &aminoJSONEncoder, + }) + case signingtypes.SignMode_SIGN_MODE_TEXTUAL: + handlers[i], err = textual.NewSignModeHandler(textual.SignModeOptions{ + CoinMetadataQuerier: opts.TextualCoinMetadataQueryFn, + FileResolver: signingOpts.FileResolver, + TypeResolver: signingOpts.TypeResolver, + }) + if opts.TextualCoinMetadataQueryFn == nil { + panic("cannot enable SIGN_MODE_TEXTUAL without a TextualCoinMetadataQueryFn") + } + if err != nil { + panic(err) + } + } + } + for i, m := range opts.CustomSignModes { + handlers[i+lenSignModes] = m + } + + txConfig.handler = txsigning.NewHandlerMap(handlers...) + return txConfig } func (g config) NewTxBuilder() client.TxBuilder { diff --git a/x/auth/tx/config/config.go b/x/auth/tx/config/config.go index 5a3ce85b32..69629e14cd 100644 --- a/x/auth/tx/config/config.go +++ b/x/auth/tx/config/config.go @@ -7,17 +7,14 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/codes" grpcstatus "google.golang.org/grpc/status" - "google.golang.org/protobuf/reflect/protoregistry" bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1" txconfigv1 "cosmossdk.io/api/cosmos/tx/config/v1" "cosmossdk.io/core/appmodule" "cosmossdk.io/depinject" txsigning "cosmossdk.io/x/tx/signing" - "cosmossdk.io/x/tx/signing/aminojson" - "cosmossdk.io/x/tx/signing/direct" - "cosmossdk.io/x/tx/signing/directaux" "cosmossdk.io/x/tx/signing/textual" + authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" @@ -35,7 +32,7 @@ import ( func init() { appmodule.Register(&txconfigv1.Config{}, appmodule.Provide(ProvideModule), - appmodule.Provide(ProvideSignModeOptions), + appmodule.Provide(ProvideProtoRegistry), ) } @@ -43,7 +40,7 @@ type ModuleInputs struct { depinject.In Config *txconfigv1.Config ProtoCodecMarshaler codec.ProtoCodecMarshaler - SignModeOptions tx.SignModeOptions + ProtoFileResolver txsigning.ProtoFileResolver // BankKeeper is the expected bank keeper to be passed to AnteHandlers BankKeeper authtypes.BankKeeper `optional:"true"` AccountKeeper ante.AccountKeeper `optional:"true"` @@ -58,22 +55,29 @@ type ModuleOutputs struct { BaseAppOption runtime.BaseAppOption } -// ProvideSignModeOptions provides the default x/tx SignModeOptions for the SDK. -func ProvideSignModeOptions(bk BankKeeper) tx.SignModeOptions { - opts, err := NewSignModeOptionsWithMetadataQueryFn(NewBankKeeperCoinMetadataQueryFn(bk)) - if err != nil { - panic(err) - } - return opts +func ProvideProtoRegistry() txsigning.ProtoFileResolver { + return registry.MergedProtoRegistry() } func ProvideModule(in ModuleInputs) ModuleOutputs { - var txConfig client.TxConfig var customSignModeHandlers []txsigning.SignModeHandler if in.CustomSignModeHandlers != nil { customSignModeHandlers = in.CustomSignModeHandlers() } - txConfig = tx.NewTxConfigWithOptions(in.ProtoCodecMarshaler, in.SignModeOptions, customSignModeHandlers...) + sdkConfig := sdk.GetConfig() + txConfigOptions := tx.ConfigOptions{ + SigningOptions: &txsigning.Options{ + FileResolver: in.ProtoFileResolver, + // From static config? But this is already in auth config. + // - Provide codecs there as types? + // - Provide static prefix there exported from config? + // - Just do as below? + AddressCodec: authcodec.NewBech32Codec(sdkConfig.GetBech32AccountAddrPrefix()), + ValidatorAddressCodec: authcodec.NewBech32Codec(sdkConfig.GetBech32ValidatorAddrPrefix()), + }, + CustomSignModes: customSignModeHandlers, + } + txConfig := tx.NewTxConfigWithOptions(in.ProtoCodecMarshaler, txConfigOptions) baseAppOption := func(app *baseapp.BaseApp) { // AnteHandlers @@ -197,38 +201,6 @@ func NewGRPCCoinMetadataQueryFn(grpcConn grpc.ClientConnInterface) textual.CoinM } } -// NewSignModeOptionsWithMetadataQueryFn creates a new SignModeOptions instance -func NewSignModeOptionsWithMetadataQueryFn(fn textual.CoinMetadataQueryFn) (tx.SignModeOptions, error) { - protoFiles := registry.MergedProtoRegistry() - typeResolver := protoregistry.GlobalTypes - signersContext, err := txsigning.NewGetSignersContext(txsigning.GetSignersOptions{ProtoFiles: protoFiles}) - if err != nil { - return tx.SignModeOptions{}, err - } - - aminoJSONEncoder := aminojson.NewAminoJSON() - signModeOptions := tx.SignModeOptions{ - Direct: &direct.SignModeHandler{}, - DirectAux: &directaux.SignModeHandlerOptions{ - FileResolver: protoFiles, - TypeResolver: typeResolver, - SignersContext: signersContext, - }, - AminoJSON: &aminojson.SignModeHandlerOptions{ - FileResolver: protoFiles, - TypeResolver: typeResolver, - Encoder: &aminoJSONEncoder, - }, - Textual: &textual.SignModeOptions{ - CoinMetadataQuerier: fn, - FileResolver: protoFiles, - TypeResolver: typeResolver, - }, - } - - return signModeOptions, nil -} - // metadataExists parses the error, and only propagates the error if it's // different than a "not found" error. func metadataExists(err error) error { diff --git a/x/auth/tx/mode_handler.go b/x/auth/tx/mode_handler.go deleted file mode 100644 index ff0b3440a4..0000000000 --- a/x/auth/tx/mode_handler.go +++ /dev/null @@ -1,65 +0,0 @@ -package tx - -import ( - txsigning "cosmossdk.io/x/tx/signing" - "cosmossdk.io/x/tx/signing/aminojson" - "cosmossdk.io/x/tx/signing/direct" - "cosmossdk.io/x/tx/signing/directaux" - "cosmossdk.io/x/tx/signing/textual" - signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing" -) - -type SignModeOptions struct { - // Textual are options for SIGN_MODE_TEXTUAL - Textual *textual.SignModeOptions - // DirectAux are options for SIGN_MODE_DIRECT_AUX - DirectAux *directaux.SignModeHandlerOptions - // AminoJSON are options for SIGN_MODE_LEGACY_AMINO_JSON - AminoJSON *aminojson.SignModeHandlerOptions - // Direct is the SignModeHandler for SIGN_MODE_DIRECT since it takes options - Direct *direct.SignModeHandler -} - -// DefaultSignModes are the default sign modes enabled for protobuf transactions. -var DefaultSignModes = []signingtypes.SignMode{ - signingtypes.SignMode_SIGN_MODE_DIRECT, - signingtypes.SignMode_SIGN_MODE_DIRECT_AUX, - signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, - // We currently don't add SIGN_MODE_TEXTUAL as part of the default sign - // modes, as it's not released yet (including the Ledger app). However, - // textual's sign mode handler is already available in this package. If you - // want to use textual for **TESTING** purposes, feel free to create a - // handler that includes SIGN_MODE_TEXTUAL. - // ref: Tracking issue for SIGN_MODE_TEXTUAL https://github.com/cosmos/cosmos-sdk/issues/11970 -} - -// makeSignModeHandler returns the default protobuf SignModeHandler supporting -// SIGN_MODE_DIRECT, SIGN_MODE_DIRECT_AUX and SIGN_MODE_LEGACY_AMINO_JSON. -func makeSignModeHandler( - opts SignModeOptions, - customSignModes ...txsigning.SignModeHandler, -) *txsigning.HandlerMap { - var handlers []txsigning.SignModeHandler - if opts.Direct != nil { - handlers = append(handlers, opts.Direct) - } - if opts.Textual != nil { - h, err := textual.NewSignModeHandler(*opts.Textual) - if err != nil { - panic(err) - } - handlers = append(handlers, h) - } - if opts.DirectAux != nil { - h, err := directaux.NewSignModeHandler(*opts.DirectAux) - if err != nil { - panic(err) - } - handlers = append(handlers, h) - } - if opts.AminoJSON != nil { - handlers = append(handlers, aminojson.NewSignModeHandler(*opts.AminoJSON)) - } - handlers = append(handlers, customSignModes...) - return txsigning.NewHandlerMap(handlers...) -} diff --git a/x/gov/migrations/v3/convert.go b/x/gov/migrations/v3/convert.go index 1c9879c0cf..fd7f0374a1 100644 --- a/x/gov/migrations/v3/convert.go +++ b/x/gov/migrations/v3/convert.go @@ -163,7 +163,7 @@ func convertToNewVotes(oldVotes v1beta1.Votes) (v1.Votes, error) { newWVOs = v1.NewNonSplitVoteOption(v1.VoteOption(oldVote.Option)) default: - return nil, fmt.Errorf("vote does not have neither Options nor Option") + return nil, fmt.Errorf("vote does not have neither InterfaceRegistryOptions nor Option") } newVotes[i] = &v1.Vote{ diff --git a/x/gov/types/v1/gov.pb.go b/x/gov/types/v1/gov.pb.go index 8384b8f1df..91f414b768 100644 --- a/x/gov/types/v1/gov.pb.go +++ b/x/gov/types/v1/gov.pb.go @@ -2929,7 +2929,7 @@ func (m *Vote) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field InterfaceRegistryOptions", wireType) } var msglen int for shift := uint(0); ; shift += 7 { diff --git a/x/gov/types/v1/tx.pb.go b/x/gov/types/v1/tx.pb.go index 3ab042b178..02471136d8 100644 --- a/x/gov/types/v1/tx.pb.go +++ b/x/gov/types/v1/tx.pb.go @@ -2814,7 +2814,7 @@ func (m *MsgVoteWeighted) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field InterfaceRegistryOptions", wireType) } var msglen int for shift := uint(0); ; shift += 7 { diff --git a/x/gov/types/v1beta1/gov.pb.go b/x/gov/types/v1beta1/gov.pb.go index 1acd514b71..993fa5a836 100644 --- a/x/gov/types/v1beta1/gov.pb.go +++ b/x/gov/types/v1beta1/gov.pb.go @@ -2373,7 +2373,7 @@ func (m *Vote) Unmarshal(dAtA []byte) error { } case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field InterfaceRegistryOptions", wireType) } var msglen int for shift := uint(0); ; shift += 7 { diff --git a/x/gov/types/v1beta1/tx.pb.go b/x/gov/types/v1beta1/tx.pb.go index 80d6fe1f1f..d44670d925 100644 --- a/x/gov/types/v1beta1/tx.pb.go +++ b/x/gov/types/v1beta1/tx.pb.go @@ -1538,7 +1538,7 @@ func (m *MsgVoteWeighted) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field InterfaceRegistryOptions", wireType) } var msglen int for shift := uint(0); ; shift += 7 { diff --git a/x/tx/CHANGELOG.md b/x/tx/CHANGELOG.md index 5a8fafb2a1..325c4c3f3d 100644 --- a/x/tx/CHANGELOG.md +++ b/x/tx/CHANGELOG.md @@ -31,6 +31,12 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## Unreleased +### Improvements + +* [#15873](https://github.com/cosmos/cosmos-sdk/pull/15873): add `Validate` method and only check for errors when `Validate` is explicitly called. + +## v0.6.0 + ### API Breaking * [#15709](https://github.com/cosmos/cosmos-sdk/pull/15709): diff --git a/x/tx/signing/context.go b/x/tx/signing/context.go index c8afe2e838..52a3eaab7f 100644 --- a/x/tx/signing/context.go +++ b/x/tx/signing/context.go @@ -4,15 +4,14 @@ import ( "errors" "fmt" - "cosmossdk.io/core/address" cosmos_proto "github.com/cosmos/cosmos-proto" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protodesc" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/reflect/protoregistry" - "google.golang.org/protobuf/types/dynamicpb" msgv1 "cosmossdk.io/api/cosmos/msg/v1" + "cosmossdk.io/core/address" ) // Context is a context for retrieving the list of signers from a @@ -78,7 +77,7 @@ func NewContext(options Options) (*Context, error) { getSignersFuncs: map[protoreflect.FullName]getSignersFunc{}, } - return c, c.init() + return c, nil } type getSignersFunc func(proto.Message) ([][]byte, error) @@ -92,26 +91,25 @@ func getSignersFieldNames(descriptor protoreflect.MessageDescriptor) ([]string, return signersFields, nil } -// init performs a dry run of getting all msg's signers. This has 2 benefits: +// Validate performs a dry run of getting all msg's signers. This has 2 benefits: // - it will error if any Msg has forgotten the "cosmos.msg.v1.signer" // annotation // - it will pre-populate the context's internal cache for getSignersFuncs // so that calling it in antehandlers will be faster. -func (c *Context) init() error { +func (c *Context) Validate() error { var errs []error c.fileResolver.RangeFiles(func(fd protoreflect.FileDescriptor) bool { for i := 0; i < fd.Services().Len(); i++ { sd := fd.Services().Get(i) - // We use the heuristic that services named "Msg" are exactly the - // ones that need the proto annotation check. - if sd.Name() != "Msg" { + + // Skip services that are not annotated with the "cosmos.msg.v1.service" option. + if ext := proto.GetExtension(sd.Options(), msgv1.E_Service); ext == nil || !ext.(bool) { continue } for j := 0; j < sd.Methods().Len(); j++ { md := sd.Methods().Get(j).Input() - msg := dynamicpb.NewMessage(md) - _, err := c.GetSigners(msg) + _, err := c.getGetSignersFn(md) if err != nil { errs = append(errs, err) } @@ -284,9 +282,7 @@ func (c *Context) getAddressCodec(field protoreflect.FieldDescriptor) address.Co return addrCdc } -// GetSigners returns the signers for a given message. -func (c *Context) GetSigners(msg proto.Message) ([][]byte, error) { - messageDescriptor := msg.ProtoReflect().Descriptor() +func (c *Context) getGetSignersFn(messageDescriptor protoreflect.MessageDescriptor) (getSignersFunc, error) { f, ok := c.getSignersFuncs[messageDescriptor.FullName()] if !ok { var err error @@ -297,6 +293,16 @@ func (c *Context) GetSigners(msg proto.Message) ([][]byte, error) { c.getSignersFuncs[messageDescriptor.FullName()] = f } + return f, nil +} + +// GetSigners returns the signers for a given message. +func (c *Context) GetSigners(msg proto.Message) ([][]byte, error) { + f, err := c.getGetSignersFn(msg.ProtoReflect().Descriptor()) + if err != nil { + return nil, err + } + return f(msg) }