diff --git a/crates/core/src/xdr/codec.rs b/crates/core/src/xdr/codec.rs index a8baaa14..3dbc066f 100644 --- a/crates/core/src/xdr/codec.rs +++ b/crates/core/src/xdr/codec.rs @@ -1,10 +1,34 @@ -//! XDR codec — thin wrapper over `stellar-xdr` with convenience methods. +//! XDR codec - thin wrapper over `stellar-xdr` with convenience methods. //! //! Handles serialization/deserialization of transaction envelopes, results, //! ledger entries, SCVal, and SCSpecEntry types. use crate::types::error::{ PrismError, PrismResult }; use base64::{ engine::general_purpose::STANDARD, Engine as _ }; +use stellar_xdr::curr::{ Limits, ReadXdr, TransactionMeta, WriteXdr }; + +/// Convenience trait for working with base64-encoded Stellar XDR values. +pub trait XdrCodec: Sized { + /// Deserialize a base64-encoded XDR payload into the target type. + fn from_xdr_base64(xdr_base64: &str) -> PrismResult; + + /// Serialize the value to a base64-encoded XDR payload. + fn to_xdr_base64(&self) -> PrismResult; +} + +impl XdrCodec for TransactionMeta { + fn from_xdr_base64(xdr_base64: &str) -> PrismResult { + TransactionMeta::from_xdr_base64(xdr_base64, Limits::none()).map_err(|e| + PrismError::XdrError(format!("Failed to decode TransactionMeta XDR: {e}")) + ) + } + + fn to_xdr_base64(&self) -> PrismResult { + WriteXdr::to_xdr_base64(self, Limits::none()).map_err(|e| + PrismError::XdrError(format!("Failed to encode TransactionMeta XDR: {e}")) + ) + } +} /// Decode a base64-encoded XDR transaction result. /// @@ -14,9 +38,6 @@ use base64::{ engine::general_purpose::STANDARD, Engine as _ }; /// # Returns /// The raw decoded bytes, ready for further parsing. pub fn decode_xdr_base64(xdr_base64: &str) -> PrismResult> { - // TODO: Implement full XDR decoding pipeline - let bytes = base64_decode(xdr_base64) - .map_err(|e| PrismError::XdrError(format!("Base64 decode failed: {e}")))?; let bytes = base64_decode(xdr_base64).map_err(|e| PrismError::XdrError(format!("Base64 decode failed: {e}")) )?; @@ -67,6 +88,8 @@ fn hex_decode(input: &str) -> Result, String> { #[cfg(test)] mod tests { use super::*; + use std::convert::TryInto; + use stellar_xdr::curr::{ LedgerEntryChanges, OperationMeta }; #[test] fn test_decode_tx_hash_valid() { @@ -92,4 +115,35 @@ mod tests { let result = decode_xdr_base64("!!!"); assert!(result.is_err()); } + + #[test] + fn test_transaction_meta_xdr_codec_roundtrip() { + let meta = TransactionMeta::V0( + Vec::::new() + .try_into() + .expect("empty operation list should fit in XDR VecM"), + ); + + let encoded = XdrCodec::to_xdr_base64(&meta).expect("encode transaction meta"); + let decoded = TransactionMeta::from_xdr_base64(&encoded).expect("decode transaction meta"); + + assert_eq!(decoded, meta); + } + + #[test] + fn test_transaction_meta_xdr_codec_decodes_v1() { + let meta = TransactionMeta::V1(stellar_xdr::curr::TransactionMetaV1 { + tx_changes: LedgerEntryChanges::try_from(vec![]) + .expect("empty ledger entry changes should fit"), + operations: Vec::::new() + .try_into() + .expect("empty operation list should fit"), + }); + + let encoded = XdrCodec::to_xdr_base64(&meta).expect("encode transaction meta"); + let decoded = ::from_xdr_base64(&encoded) + .expect("decode transaction meta"); + + assert_eq!(decoded, meta); + } }