Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 58 additions & 4 deletions crates/core/src/xdr/codec.rs
Original file line number Diff line number Diff line change
@@ -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<Self>;

/// Serialize the value to a base64-encoded XDR payload.
fn to_xdr_base64(&self) -> PrismResult<String>;
}

impl XdrCodec for TransactionMeta {
fn from_xdr_base64(xdr_base64: &str) -> PrismResult<Self> {
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<String> {
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.
///
Expand All @@ -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<Vec<u8>> {
// 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}"))
)?;
Expand Down Expand Up @@ -67,6 +88,8 @@ fn hex_decode(input: &str) -> Result<Vec<u8>, String> {
#[cfg(test)]
mod tests {
use super::*;
use std::convert::TryInto;
use stellar_xdr::curr::{ LedgerEntryChanges, OperationMeta };

#[test]
fn test_decode_tx_hash_valid() {
Expand All @@ -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::<OperationMeta>::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::<OperationMeta>::new()
.try_into()
.expect("empty operation list should fit"),
});

let encoded = XdrCodec::to_xdr_base64(&meta).expect("encode transaction meta");
let decoded = <TransactionMeta as XdrCodec>::from_xdr_base64(&encoded)
.expect("decode transaction meta");

assert_eq!(decoded, meta);
}
}