Skip to main content

Chain

Trait Chain 

Source
pub trait Chain: Send + Sync {
    // Required methods
    fn id(&self) -> &'static str;
    fn parse(&self, raw: &[u8]) -> Result<ParsedTx, ParseError>;
    fn curve(&self) -> CurveType;

    // Provided methods
    fn supports_version(&self, version: u8) -> bool { ... }
    fn assemble_signed(
        &self,
        raw: &[u8],
        signature: &[u8],
    ) -> Result<Vec<u8>, ParseError> { ... }
}
Expand description

Trait for blockchain transaction parsers.

Each supported blockchain implements this trait to parse raw transaction bytes into a unified ParsedTx structure that can be evaluated by the policy engine.

§Implementor Requirements

Implementations must:

  • Parse raw transaction bytes according to the chain’s encoding format
  • Extract recipient address, amount, and token information
  • Compute the transaction hash that will be signed
  • Detect token operations (ERC-20, SPL, TRC-20, etc.)
  • Set appropriate TxType for policy evaluation
  • Return appropriate errors for malformed or unsupported transactions

§Thread Safety

Implementations must be Send + Sync to allow concurrent parsing in the server’s async runtime. This is typically achieved by:

  • Not storing mutable state in the parser
  • Using interior mutability with proper synchronization if state is needed

§Example: Using with Trait Objects

use txgate_chain::Chain;
use txgate_core::{ParsedTx, error::ParseError};
use txgate_crypto::CurveType;

// Create a registry of chain parsers
struct ChainRegistry {
    chains: Vec<Box<dyn Chain>>,
}

impl ChainRegistry {
    fn find(&self, id: &str) -> Option<&dyn Chain> {
        self.chains.iter().find(|c| c.id() == id).map(|c| c.as_ref())
    }
}

Required Methods§

Source

fn id(&self) -> &'static str

Returns the chain identifier (e.g., “ethereum”, “bitcoin”, “solana”).

This identifier is used for:

  • Chain lookup in the registry
  • Logging and metrics
  • Configuration matching
  • Correlation with key storage
§Naming Convention

Chain identifiers should be:

  • Lowercase
  • Alphanumeric with hyphens for multi-word names
  • Consistent with industry conventions

Examples: "ethereum", "bitcoin", "solana", "polygon", "arbitrum-one"

§Example
use txgate_chain::Chain;

struct EthereumParser;

impl Chain for EthereumParser {
    fn id(&self) -> &'static str {
        "ethereum"
    }
}

let parser = EthereumParser;
assert_eq!(parser.id(), "ethereum");
Source

fn parse(&self, raw: &[u8]) -> Result<ParsedTx, ParseError>

Parse raw transaction bytes into a ParsedTx.

This is the core method of the trait. It transforms chain-specific transaction bytes into a normalized format for policy evaluation.

§Arguments
  • raw - The raw transaction bytes in the chain’s native format
§Returns
  • Ok(ParsedTx) - Successfully parsed transaction with all fields populated
  • Err(ParseError) - Parsing failed
§Errors

This method returns a ParseError in the following cases:

§Transaction Hash

The returned ParsedTx.hash must be the exact hash that will be signed. This is critical for security:

  • For Ethereum legacy: Keccak256 of RLP-encoded transaction without signature
  • For Ethereum EIP-155: Includes chain_id in the signing hash
  • For Ethereum typed: Domain-separated hash with type prefix
  • For Bitcoin: Double SHA256 of serialized transaction
  • For Solana: SHA256 of serialized message
§Example
use txgate_chain::Chain;
use txgate_core::{ParsedTx, TxType, error::ParseError};

struct SimpleParser;

impl Chain for SimpleParser {
    fn id(&self) -> &'static str { "test" }

    fn parse(&self, raw: &[u8]) -> Result<ParsedTx, ParseError> {
        if raw.is_empty() {
            return Err(ParseError::MalformedTransaction {
                context: "empty transaction data".to_string(),
            });
        }
        // Parse the transaction...
        Ok(ParsedTx::default())
    }
}
Source

fn curve(&self) -> CurveType

Returns the elliptic curve used by this chain.

This is used to select the appropriate signer for the chain. Most EVM-compatible chains use secp256k1, while Solana uses Ed25519.

§Curve Selection
§Example
use txgate_chain::Chain;
use txgate_crypto::CurveType;

struct SolanaParser;

impl Chain for SolanaParser {
    fn id(&self) -> &'static str { "solana" }
    fn curve(&self) -> CurveType {
        CurveType::Ed25519
    }
}

let parser = SolanaParser;
assert_eq!(parser.curve(), CurveType::Ed25519);

Provided Methods§

Source

fn supports_version(&self, version: u8) -> bool

Check if this parser supports a specific transaction version/type.

This method allows parsers to declare which transaction versions they can handle. The registry can use this to select the appropriate parser for a given transaction.

§Arguments
  • version - Chain-specific version identifier
§Returns
  • true if this parser can handle the version
  • false otherwise
§Default Implementation

The default implementation returns true for all versions, which is suitable for parsers that handle all known transaction types.

§Version Semantics

The meaning of version is chain-specific:

  • Ethereum: Transaction type (0=legacy, 1=EIP-2930, 2=EIP-1559, 3=EIP-4844)
  • Bitcoin: Transaction version field
  • Solana: Message version (0=legacy, 128+=versioned)
§Example
use txgate_chain::Chain;

struct LegacyEthereumParser;

impl Chain for LegacyEthereumParser {
    fn id(&self) -> &'static str { "ethereum-legacy" }

    fn supports_version(&self, version: u8) -> bool {
        // Only support legacy transactions (type 0)
        version == 0
    }
}

let parser = LegacyEthereumParser;
assert!(parser.supports_version(0));  // Legacy
assert!(!parser.supports_version(2)); // EIP-1559 not supported
Source

fn assemble_signed( &self, raw: &[u8], signature: &[u8], ) -> Result<Vec<u8>, ParseError>

Assemble a signed transaction from raw bytes and a signature.

Takes the original raw transaction bytes and the 65-byte signature (r[32] || s[32] || recovery_id[1]) and returns the fully encoded signed transaction ready for network broadcast.

§Default Implementation

Returns ParseError::AssemblyFailed — not all chains support assembly. Chains that can assemble signed transactions should override this method.

§Arguments
  • raw - The raw transaction bytes (as passed to parse())
  • signature - 65-byte signature: r(32) || s(32) || recovery_id(1)
§Returns
  • Ok(Vec<u8>) - The assembled signed transaction bytes
  • Err(ParseError::AssemblyFailed) - Assembly is not supported or failed
§Errors

Returns ParseError::AssemblyFailed if the chain does not support transaction assembly, or if the assembly process fails.

Implementors§