mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-02 17:31:39 +00:00
319 lines
8.6 KiB
Go
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
|
|
}
|