Files
sonr/types/txns/encoding.go
T
Prad NukalaandGitHub 13e6c3e84d Master (#1262)
* clear

* feat: Add everything

* fix: Commenht
2025-10-03 14:45:52 -04:00

319 lines
8.6 KiB
Go

package txns
import (
"fmt"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
ethtypes "github.com/ethereum/go-ethereum/core/types"
)
// Encoder interface for encoding transactions
type Encoder interface {
// EncodeTx encodes a transaction
EncodeTx(tx any) ([]byte, error)
// DecodeTx decodes a transaction
DecodeTx(data []byte) (any, error)
// GetEncodingType returns the encoding type
GetEncodingType() EncodingType
}
// CosmosProtobufEncoder encodes/decodes Cosmos transactions using Protobuf
type CosmosProtobufEncoder struct {
txConfig client.TxConfig
cdc codec.Codec
}
// NewCosmosProtobufEncoder creates a new Protobuf encoder for Cosmos
func NewCosmosProtobufEncoder(clientCtx client.Context) *CosmosProtobufEncoder {
return &CosmosProtobufEncoder{
txConfig: clientCtx.TxConfig,
cdc: clientCtx.Codec,
}
}
// EncodeTx implements Encoder interface
func (e *CosmosProtobufEncoder) EncodeTx(tx any) ([]byte, error) {
switch t := tx.(type) {
case client.TxBuilder:
return e.txConfig.TxEncoder()(t.GetTx())
case sdk.Tx:
return e.txConfig.TxEncoder()(t)
case *CosmosSignedTx:
return e.txConfig.TxEncoder()(t.TxBuilder.GetTx())
case *CosmosUnsignedTx:
return e.txConfig.TxEncoder()(t.TxBuilder.GetTx())
default:
return nil, fmt.Errorf("unsupported transaction type for Protobuf encoding: %T", tx)
}
}
// DecodeTx implements Encoder interface
func (e *CosmosProtobufEncoder) DecodeTx(data []byte) (any, error) {
tx, err := e.txConfig.TxDecoder()(data)
if err != nil {
return nil, fmt.Errorf("failed to decode Protobuf transaction: %w", err)
}
return tx, nil
}
// GetEncodingType implements Encoder interface
func (e *CosmosProtobufEncoder) GetEncodingType() EncodingType {
return EncodingTypeProtobuf
}
// CosmosAminoEncoder encodes/decodes Cosmos transactions using Amino
type CosmosAminoEncoder struct {
cdc *codec.LegacyAmino
}
// NewCosmosAminoEncoder creates a new Amino encoder for Cosmos
func NewCosmosAminoEncoder() *CosmosAminoEncoder {
// Create a legacy amino codec
cdc := codec.NewLegacyAmino()
sdk.RegisterLegacyAminoCodec(cdc)
return &CosmosAminoEncoder{
cdc: cdc,
}
}
// EncodeTx implements Encoder interface
func (e *CosmosAminoEncoder) EncodeTx(tx any) ([]byte, error) {
switch t := tx.(type) {
case sdk.Tx:
return e.cdc.Marshal(t)
case *CosmosSignedTx:
return e.cdc.Marshal(t.TxBuilder.GetTx())
case *CosmosUnsignedTx:
return e.cdc.Marshal(t.TxBuilder.GetTx())
default:
return nil, fmt.Errorf("unsupported transaction type for Amino encoding: %T", tx)
}
}
// DecodeTx implements Encoder interface
func (e *CosmosAminoEncoder) DecodeTx(data []byte) (any, error) {
var tx sdk.Tx
err := e.cdc.Unmarshal(data, &tx)
if err != nil {
return nil, fmt.Errorf("failed to decode Amino transaction: %w", err)
}
return tx, nil
}
// GetEncodingType implements Encoder interface
func (e *CosmosAminoEncoder) GetEncodingType() EncodingType {
return EncodingTypeAmino
}
// EVMRLPEncoder encodes/decodes EVM transactions using RLP
type EVMRLPEncoder struct{}
// NewEVMRLPEncoder creates a new RLP encoder for EVM
func NewEVMRLPEncoder() *EVMRLPEncoder {
return &EVMRLPEncoder{}
}
// EncodeTx implements Encoder interface
func (e *EVMRLPEncoder) EncodeTx(tx any) ([]byte, error) {
switch t := tx.(type) {
case *ethtypes.Transaction:
return t.MarshalBinary()
case *EVMSignedTx:
return t.Transaction.MarshalBinary()
case *EVMUnsignedTx:
return t.Transaction.MarshalBinary()
default:
return nil, fmt.Errorf("unsupported transaction type for RLP encoding: %T", tx)
}
}
// DecodeTx implements Encoder interface
func (e *EVMRLPEncoder) DecodeTx(data []byte) (any, error) {
var tx ethtypes.Transaction
err := tx.UnmarshalBinary(data)
if err != nil {
return nil, fmt.Errorf("failed to decode RLP transaction: %w", err)
}
return &tx, nil
}
// GetEncodingType implements Encoder interface
func (e *EVMRLPEncoder) GetEncodingType() EncodingType {
return EncodingTypeRLP
}
// EncoderRegistry manages different encoders
type EncoderRegistry struct {
encoders map[string]Encoder
}
// NewEncoderRegistry creates a new encoder registry
func NewEncoderRegistry() *EncoderRegistry {
return &EncoderRegistry{
encoders: make(map[string]Encoder),
}
}
// RegisterEncoder registers an encoder
func (r *EncoderRegistry) RegisterEncoder(name string, encoder Encoder) {
r.encoders[name] = encoder
}
// GetEncoder retrieves an encoder by name
func (r *EncoderRegistry) GetEncoder(name string) (Encoder, error) {
encoder, exists := r.encoders[name]
if !exists {
return nil, fmt.Errorf("encoder not found: %s", name)
}
return encoder, nil
}
// GetEncoderByType retrieves an encoder by encoding type and transaction type
func (r *EncoderRegistry) GetEncoderByType(
encodingType EncodingType,
txType TransactionType,
) (Encoder, error) {
key := fmt.Sprintf("%s-%s", txType, encodingType)
return r.GetEncoder(key)
}
// DefaultEncoderRegistry creates a registry with default encoders
func DefaultEncoderRegistry(clientCtx client.Context) *EncoderRegistry {
registry := NewEncoderRegistry()
// Register Cosmos encoders
registry.RegisterEncoder(
fmt.Sprintf("%s-%s", TransactionTypeCosmos, EncodingTypeProtobuf),
NewCosmosProtobufEncoder(clientCtx),
)
registry.RegisterEncoder(
fmt.Sprintf("%s-%s", TransactionTypeCosmos, EncodingTypeAmino),
NewCosmosAminoEncoder(),
)
// Register EVM encoder
registry.RegisterEncoder(
fmt.Sprintf("%s-%s", TransactionTypeEVM, EncodingTypeRLP),
NewEVMRLPEncoder(),
)
return registry
}
// TransactionData represents decoded transaction data
type TransactionData struct {
Type TransactionType `json:"type"`
Encoding EncodingType `json:"encoding"`
Hash string `json:"hash"`
Size int `json:"size"`
Raw any `json:"raw"`
Metadata any `json:"metadata,omitempty"`
}
// DecodeTransaction decodes a transaction and returns structured data
func DecodeTransaction(
data []byte,
encodingType EncodingType,
txType TransactionType,
clientCtx client.Context,
) (*TransactionData, error) {
registry := DefaultEncoderRegistry(clientCtx)
encoder, err := registry.GetEncoderByType(encodingType, txType)
if err != nil {
return nil, fmt.Errorf("failed to get encoder: %w", err)
}
decodedTx, err := encoder.DecodeTx(data)
if err != nil {
return nil, fmt.Errorf("failed to decode transaction: %w", err)
}
// Calculate hash based on transaction type
var hash string
switch txType {
case TransactionTypeCosmos:
if cosmosTx, ok := decodedTx.(sdk.Tx); ok {
// Calculate Cosmos transaction hash
txBytes, err := encoder.EncodeTx(cosmosTx)
if err == nil {
hash = fmt.Sprintf("%x", txBytes[:32]) // Simple hash for demo
}
}
case TransactionTypeEVM:
if evmTx, ok := decodedTx.(*ethtypes.Transaction); ok {
hash = evmTx.Hash().Hex()
}
}
return &TransactionData{
Type: txType,
Encoding: encodingType,
Hash: hash,
Size: len(data),
Raw: decodedTx,
}, nil
}
// EncodeTransaction encodes a transaction using the specified encoding
func EncodeTransaction(
tx any,
encodingType EncodingType,
txType TransactionType,
clientCtx client.Context,
) ([]byte, error) {
registry := DefaultEncoderRegistry(clientCtx)
encoder, err := registry.GetEncoderByType(encodingType, txType)
if err != nil {
return nil, fmt.Errorf("failed to get encoder: %w", err)
}
return encoder.EncodeTx(tx)
}
// ConvertEncoding converts a transaction from one encoding to another
func ConvertEncoding(
data []byte,
fromEncoding, toEncoding EncodingType,
txType TransactionType,
clientCtx client.Context,
) ([]byte, error) {
// Decode with source encoding
decoded, err := DecodeTransaction(data, fromEncoding, txType, clientCtx)
if err != nil {
return nil, fmt.Errorf("failed to decode transaction: %w", err)
}
// Encode with target encoding
return EncodeTransaction(decoded.Raw, toEncoding, txType, clientCtx)
}
// ValidateTransactionEncoding validates that transaction data is properly encoded
func ValidateTransactionEncoding(
data []byte,
encodingType EncodingType,
txType TransactionType,
clientCtx client.Context,
) error {
_, err := DecodeTransaction(data, encodingType, txType, clientCtx)
if err != nil {
return fmt.Errorf("invalid transaction encoding: %w", err)
}
return nil
}
// GetTransactionSize returns the size of an encoded transaction
func GetTransactionSize(
tx any,
encodingType EncodingType,
txType TransactionType,
clientCtx client.Context,
) (int, error) {
data, err := EncodeTransaction(tx, encodingType, txType, clientCtx)
if err != nil {
return 0, fmt.Errorf("failed to encode transaction: %w", err)
}
return len(data), nil
}