cosmos-sdk/x/auth/tx/config.go
2025-01-22 11:15:49 +00:00

264 lines
9.4 KiB
Go

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
}