soroban_cli/commands/tx/
xdr.rs

1use crate::xdr::{
2    Limits, Operation, ReadXdr, Transaction, TransactionEnvelope, TransactionV1Envelope,
3};
4use std::ffi::OsString;
5use std::fs::File;
6use std::io::{stdin, Read};
7use std::io::{Cursor, IsTerminal};
8use std::path::Path;
9use stellar_xdr::curr::Limited;
10
11#[derive(Debug, thiserror::Error)]
12pub enum Error {
13    #[error("failed to decode XDR: {0}")]
14    XDRDecode(#[from] stellar_xdr::curr::Error),
15    #[error(transparent)]
16    Io(#[from] std::io::Error),
17    #[error("only transaction v1 is supported")]
18    OnlyTransactionV1Supported,
19    #[error("too many operations, limited to 100 operations in a transaction")]
20    TooManyOperations,
21    #[error("no transaction provided")]
22    NoStdin,
23}
24
25pub fn tx_envelope_from_input(input: &Option<OsString>) -> Result<TransactionEnvelope, Error> {
26    let read: &mut dyn Read = if let Some(input) = input {
27        let exist = Path::new(input).try_exists();
28        if let Ok(true) = exist {
29            &mut File::open(input)?
30        } else {
31            &mut Cursor::new(input.clone().into_encoded_bytes())
32        }
33    } else {
34        if stdin().is_terminal() {
35            return Err(Error::NoStdin);
36        }
37        &mut stdin()
38    };
39
40    let mut lim = Limited::new(SkipWhitespace::new(read), Limits::none());
41    Ok(TransactionEnvelope::read_xdr_base64_to_end(&mut lim)?)
42}
43
44// TODO: use SkipWhitespace from rs-stellar-xdr once it's updated to 23.0
45pub struct SkipWhitespace<R: Read> {
46    pub inner: R,
47}
48
49impl<R: Read> SkipWhitespace<R> {
50    pub fn new(inner: R) -> Self {
51        SkipWhitespace { inner }
52    }
53}
54
55impl<R: Read> Read for SkipWhitespace<R> {
56    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
57        let n = self.inner.read(buf)?;
58
59        let mut written = 0;
60        for read in 0..n {
61            if !buf[read].is_ascii_whitespace() {
62                buf[written] = buf[read];
63                written += 1;
64            }
65        }
66
67        Ok(written)
68    }
69}
70//
71
72pub fn unwrap_envelope_v1(tx_env: TransactionEnvelope) -> Result<Transaction, Error> {
73    let TransactionEnvelope::Tx(TransactionV1Envelope { tx, .. }) = tx_env else {
74        return Err(Error::OnlyTransactionV1Supported);
75    };
76    Ok(tx)
77}
78
79pub fn add_op(tx_env: TransactionEnvelope, op: Operation) -> Result<TransactionEnvelope, Error> {
80    let mut tx = unwrap_envelope_v1(tx_env)?;
81    let mut ops = tx.operations.to_vec();
82    ops.push(op);
83    tx.operations = ops.try_into().map_err(|_| Error::TooManyOperations)?;
84    Ok(tx.into())
85}