package tx import ( "errors" "fmt" apisigning "cosmossdk.io/api/cosmos/tx/signing/v1beta1" "cosmossdk.io/core/address" txdecode "cosmossdk.io/x/tx/decode" 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" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" ) type config struct { handler *txsigning.HandlerMap decoder sdk.TxDecoder encoder sdk.TxEncoder jsonDecoder sdk.TxDecoder jsonEncoder sdk.TxEncoder protoCodec codec.Codec signingContext *txsigning.Context txDecoder *txdecode.Decoder } // 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 []apisigning.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 // ProtoDecoder is the decoder that will be used to decode protobuf transactions. ProtoDecoder sdk.TxDecoder // ProtoEncoder is the encoder that will be used to encode protobuf transactions. ProtoEncoder sdk.TxEncoder // JSONDecoder is the decoder that will be used to decode json transactions. JSONDecoder sdk.TxDecoder // JSONEncoder is the encoder that will be used to encode json transactions. JSONEncoder sdk.TxEncoder } // DefaultSignModes are the default sign modes enabled for protobuf transactions. var DefaultSignModes = []apisigning.SignMode{ apisigning.SignMode_SIGN_MODE_DIRECT, apisigning.SignMode_SIGN_MODE_DIRECT_AUX, apisigning.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, // apisigning.SignMode_SIGN_MODE_TEXTUAL is not enabled by default, as it requires a x/bank keeper or gRPC connection. } // 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 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. // // 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: // https://github.com/evmos/evmos/blob/719363fbb92ff3ea9649694bd088e4c6fe9c195f/encoding/config.go#L37 func NewTxConfig(protoCodec codec.Codec, addressCodec, validatorAddressCodec address.Codec, enabledSignModes []apisigning.SignMode, customSignModes ...txsigning.SignModeHandler, ) client.TxConfig { txConfig, err := NewTxConfigWithOptions(protoCodec, ConfigOptions{ EnabledSignModes: enabledSignModes, CustomSignModes: customSignModes, SigningOptions: &txsigning.Options{ AddressCodec: addressCodec, ValidatorAddressCodec: validatorAddressCodec, }, }) if err != nil { panic(err) } return txConfig } // NewSigningOptions returns signing options used by x/tx. This includes account and // validator address prefix enabled codecs. func NewSigningOptions(addressCodec, validatorAddressCodec address.Codec) *txsigning.Options { return &txsigning.Options{ AddressCodec: addressCodec, ValidatorAddressCodec: validatorAddressCodec, } } // NewSigningHandlerMap returns a new txsigning.HandlerMap using the provided ConfigOptions. // It is recommended to use types.InterfaceRegistry in the field ConfigOptions.FileResolver as shown in // NewTxConfigWithOptions but this fn does not enforce it. func NewSigningHandlerMap(configOpts ConfigOptions) (*txsigning.HandlerMap, error) { var err error if configOpts.SigningOptions == nil { return nil, errors.New("signing options not provided") } if configOpts.SigningContext == nil { configOpts.SigningContext, err = txsigning.NewContext(*configOpts.SigningOptions) if err != nil { return nil, err } } signingOpts := configOpts.SigningOptions if len(configOpts.EnabledSignModes) == 0 { configOpts.EnabledSignModes = DefaultSignModes } lenSignModes := len(configOpts.EnabledSignModes) handlers := make([]txsigning.SignModeHandler, lenSignModes+len(configOpts.CustomSignModes)) for i, m := range configOpts.EnabledSignModes { var err error switch m { case apisigning.SignMode_SIGN_MODE_DIRECT: handlers[i] = &direct.SignModeHandler{} case apisigning.SignMode_SIGN_MODE_DIRECT_AUX: handlers[i], err = directaux.NewSignModeHandler(directaux.SignModeHandlerOptions{ TypeResolver: signingOpts.TypeResolver, SignersContext: configOpts.SigningContext, }) if err != nil { return nil, err } case apisigning.SignMode_SIGN_MODE_LEGACY_AMINO_JSON: handlers[i] = aminojson.NewSignModeHandler(aminojson.SignModeHandlerOptions{ FileResolver: signingOpts.FileResolver, TypeResolver: signingOpts.TypeResolver, }) case apisigning.SignMode_SIGN_MODE_TEXTUAL: handlers[i], err = textual.NewSignModeHandler(textual.SignModeOptions{ CoinMetadataQuerier: configOpts.TextualCoinMetadataQueryFn, FileResolver: signingOpts.FileResolver, TypeResolver: signingOpts.TypeResolver, }) if configOpts.TextualCoinMetadataQueryFn == nil { return nil, errors.New("cannot enable SIGN_MODE_TEXTUAL without a TextualCoinMetadataQueryFn") } if err != nil { return nil, err } } } for i, m := range configOpts.CustomSignModes { handlers[i+lenSignModes] = m } handler := txsigning.NewHandlerMap(handlers...) return handler, nil } // 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.Codec, configOptions ConfigOptions) (client.TxConfig, error) { txConfig := &config{ protoCodec: protoCodec, decoder: configOptions.ProtoDecoder, encoder: configOptions.ProtoEncoder, jsonDecoder: configOptions.JSONDecoder, jsonEncoder: configOptions.JSONEncoder, } var err error if configOptions.SigningContext == nil { if configOptions.SigningOptions == nil { return nil, errors.New("signing options not provided") } if configOptions.SigningOptions.FileResolver == nil { configOptions.SigningOptions.FileResolver = protoCodec.InterfaceRegistry() } configOptions.SigningContext, err = txsigning.NewContext(*configOptions.SigningOptions) if err != nil { return nil, err } } txConfig.txDecoder, err = txdecode.NewDecoder(txdecode.Options{ SigningContext: configOptions.SigningContext, ProtoCodec: protoCodec, }) if err != nil { return nil, err } if configOptions.ProtoDecoder == nil { txConfig.decoder = DefaultTxDecoder(configOptions.SigningOptions.AddressCodec, protoCodec, txConfig.txDecoder) } 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 { txConfig.handler = configOptions.SigningHandler return txConfig, nil } txConfig.handler, err = NewSigningHandlerMap(configOptions) if err != nil { return nil, err } return txConfig, nil } func (g config) NewTxBuilder() client.TxBuilder { 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) { gogoTx, ok := newTx.(*gogoTxWrapper) if !ok { return nil, fmt.Errorf("expected %T, got %T", &gogoTxWrapper{}, newTx) } return newBuilderFromDecodedTx(g.signingContext.AddressCodec(), g.txDecoder, g.protoCodec, gogoTx) } func (g config) SignModeHandler() *txsigning.HandlerMap { return g.handler } func (g config) TxEncoder() sdk.TxEncoder { return g.encoder } func (g config) TxDecoder() sdk.TxDecoder { return g.decoder } func (g config) TxJSONEncoder() sdk.TxEncoder { return g.jsonEncoder } func (g config) TxJSONDecoder() sdk.TxDecoder { return g.jsonDecoder } func (g config) SigningContext() *txsigning.Context { return g.signingContext }