stellar_base/operations/
allow_trust.rs

1use crate::account::TrustLineFlags;
2use crate::asset::{xdr_code_to_string, CreditAssetType};
3use crate::crypto::{MuxedAccount, PublicKey};
4use crate::error::{Error, Result};
5use crate::operations::Operation;
6use crate::xdr;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct AllowTrustOperation {
10    source_account: Option<MuxedAccount>,
11    trustor: PublicKey,
12    asset: CreditAssetType,
13    authorize: TrustLineFlags,
14}
15
16#[derive(Debug, Default)]
17pub struct AllowTrustOperationBuilder {
18    source_account: Option<MuxedAccount>,
19    trustor: Option<PublicKey>,
20    asset: Option<CreditAssetType>,
21    authorize: Option<TrustLineFlags>,
22}
23
24impl AllowTrustOperation {
25    /// Retrieves the operation source account.
26    pub fn source_account(&self) -> &Option<MuxedAccount> {
27        &self.source_account
28    }
29
30    /// Retrieves a reference to the operation source account.
31    pub fn source_account_mut(&mut self) -> &mut Option<MuxedAccount> {
32        &mut self.source_account
33    }
34
35    /// Retrieves the operation trustor.
36    pub fn trustor(&self) -> &PublicKey {
37        &self.trustor
38    }
39
40    /// Retrieves a mutable reference to the operation trustor.
41    pub fn trustor_mut(&mut self) -> &mut PublicKey {
42        &mut self.trustor
43    }
44
45    /// Retrieves the operation asset.
46    pub fn asset(&self) -> &CreditAssetType {
47        &self.asset
48    }
49
50    /// Retrieves a mutable reference to the operation asset.
51    pub fn asset_mut(&mut self) -> &mut CreditAssetType {
52        &mut self.asset
53    }
54
55    /// Retrieves the operation authorize flags.
56    pub fn authorize_flags(&self) -> &TrustLineFlags {
57        &self.authorize
58    }
59
60    /// Retrieves a mutable reference to the operation authorize flags.
61    pub fn authorize_flags_mut(&mut self) -> &mut TrustLineFlags {
62        &mut self.authorize
63    }
64
65    /// Returns the xdr operation body.
66    pub fn to_xdr_operation_body(&self) -> Result<xdr::OperationBody> {
67        let trustor = self.trustor.to_xdr_account_id()?;
68        let asset = match &self.asset {
69            CreditAssetType::CreditAlphaNum4(code) => {
70                let asset_code = xdr::AssetCode4::try_from(code.as_bytes())
71                    .map_err(|_| Error::InvalidAssetCode)?;
72                xdr::AssetCode::CreditAlphanum4(asset_code)
73            }
74            CreditAssetType::CreditAlphaNum12(code) => {
75                let asset_code = xdr::AssetCode12::try_from(code.as_bytes())
76                    .map_err(|_| Error::InvalidAssetCode)?;
77                xdr::AssetCode::CreditAlphanum12(asset_code)
78            }
79        };
80        let authorize = self.authorize.bits() as u32;
81
82        let inner = xdr::AllowTrustOp {
83            trustor,
84            asset,
85            authorize,
86        };
87        Ok(xdr::OperationBody::AllowTrust(inner))
88    }
89
90    /// Creates from xdr operation body.
91    pub fn from_xdr_operation_body(
92        source_account: Option<MuxedAccount>,
93        x: &xdr::AllowTrustOp,
94    ) -> Result<AllowTrustOperation> {
95        let trustor = PublicKey::from_xdr_account_id(&x.trustor)?;
96        let asset = match &x.asset {
97            xdr::AssetCode::CreditAlphanum4(code) => {
98                let code = xdr_code_to_string(&code.0);
99                CreditAssetType::CreditAlphaNum4(code)
100            }
101            xdr::AssetCode::CreditAlphanum12(code) => {
102                let code = xdr_code_to_string(&code.0);
103                CreditAssetType::CreditAlphaNum12(code)
104            }
105        };
106        let authorize =
107            TrustLineFlags::from_bits(x.authorize).ok_or(Error::InvalidTrustLineFlags)?;
108
109        Ok(AllowTrustOperation {
110            source_account,
111            trustor,
112            asset,
113            authorize,
114        })
115    }
116}
117
118impl AllowTrustOperationBuilder {
119    pub fn new() -> AllowTrustOperationBuilder {
120        Default::default()
121    }
122
123    pub fn with_source_account<S>(mut self, source: S) -> AllowTrustOperationBuilder
124    where
125        S: Into<MuxedAccount>,
126    {
127        self.source_account = Some(source.into());
128        self
129    }
130
131    pub fn with_trustor(mut self, trustor: PublicKey) -> AllowTrustOperationBuilder {
132        self.trustor = Some(trustor);
133        self
134    }
135
136    pub fn with_asset(mut self, asset: CreditAssetType) -> AllowTrustOperationBuilder {
137        self.asset = Some(asset);
138        self
139    }
140
141    pub fn with_authorize_flags(mut self, authorize: TrustLineFlags) -> AllowTrustOperationBuilder {
142        self.authorize = Some(authorize);
143        self
144    }
145
146    pub fn build(self) -> Result<Operation> {
147        let trustor = self
148            .trustor
149            .ok_or_else(|| Error::InvalidOperation("missing allow trust trustor".to_string()))?;
150
151        let asset = self
152            .asset
153            .ok_or_else(|| Error::InvalidOperation("missing allow trust asset".to_string()))?;
154
155        let authorize = self.authorize.ok_or_else(|| {
156            Error::InvalidOperation("missing allow trust authorize flags".to_string())
157        })?;
158
159        Ok(Operation::AllowTrust(AllowTrustOperation {
160            source_account: self.source_account,
161            trustor,
162            asset,
163            authorize,
164        }))
165    }
166}
167
168#[cfg(test)]
169mod tests {
170    use crate::account::TrustLineFlags;
171    use crate::asset::CreditAssetType;
172
173    use crate::network::Network;
174    use crate::operations::tests::*;
175    use crate::operations::Operation;
176    use crate::transaction::{Transaction, TransactionEnvelope, MIN_BASE_FEE};
177    use crate::xdr::{XDRDeserialize, XDRSerialize};
178
179    #[test]
180    fn test_allow_trust() {
181        let kp = keypair0();
182        let kp1 = keypair1();
183
184        let op = Operation::new_allow_trust()
185            .with_trustor(kp1.public_key())
186            .with_asset(CreditAssetType::CreditAlphaNum4("ABCD".to_string()))
187            .with_authorize_flags(TrustLineFlags::AUTHORIZED)
188            .build()
189            .unwrap();
190
191        let mut tx = Transaction::builder(kp.public_key(), 3556091187167235, MIN_BASE_FEE)
192            .add_operation(op)
193            .into_transaction()
194            .unwrap();
195        tx.sign(kp.as_ref(), &Network::new_test()).unwrap();
196        let envelope = tx.to_envelope();
197        let xdr = envelope.xdr_base64().unwrap();
198        let expected = "AAAAAgAAAADg3G3hclysZlFitS+s5zWyiiJD5B0STWy5LXCj6i5yxQAAAGQADKI/AAAAAwAAAAAAAAAAAAAAAQAAAAAAAAAHAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAUFCQ0QAAAABAAAAAAAAAAHqLnLFAAAAQNhV5kJkZryrHEq8jgx9O76dchfHSkS99FTAcR6D2cjSoy6dbPuGsiPpTbwbMMV+lYTigEmv5vTVV+rWcLfr0Q0=";
199        assert_eq!(expected, xdr);
200        let back = TransactionEnvelope::from_xdr_base64(&xdr).unwrap();
201        assert_eq!(envelope, back);
202    }
203
204    #[test]
205    fn test_allow_trust_with_source_account() {
206        let kp = keypair0();
207        let kp1 = keypair1();
208        let kp2 = keypair2();
209
210        let op = Operation::new_allow_trust()
211            .with_source_account(kp2.public_key())
212            .with_trustor(kp1.public_key())
213            .with_asset(CreditAssetType::CreditAlphaNum4("ABCD".to_string()))
214            .with_authorize_flags(TrustLineFlags::AUTHORIZED)
215            .build()
216            .unwrap();
217        let mut tx = Transaction::builder(kp.public_key(), 3556091187167235, MIN_BASE_FEE)
218            .add_operation(op)
219            .into_transaction()
220            .unwrap();
221        tx.sign(kp.as_ref(), &Network::new_test()).unwrap();
222        let envelope = tx.to_envelope();
223        let xdr = envelope.xdr_base64().unwrap();
224        let expected = "AAAAAgAAAADg3G3hclysZlFitS+s5zWyiiJD5B0STWy5LXCj6i5yxQAAAGQADKI/AAAAAwAAAAAAAAAAAAAAAQAAAAEAAAAAfhHLNNY19eGrAtSgLD3VpaRm2AjNjxIBWQg9zS4VWZgAAAAHAAAAACXK8doPx27P6IReQlRRuweSSUiUfjqgyswxiu3Sh2R+AAAAAUFCQ0QAAAABAAAAAAAAAAHqLnLFAAAAQOGeHFq7smaQekF9Cu+duxVIGbMiGvT5CccilyMmB7WELOn85XuYIY6qfDKCnjgH47ga1Yve8qnA5hdD2A+iAgA=";
225        assert_eq!(expected, xdr);
226        let back = TransactionEnvelope::from_xdr_base64(&xdr).unwrap();
227        assert_eq!(envelope, back);
228    }
229}