1use core::convert::TryInto;
4use sodalite::SIGN_LEN;
5use sp_std::{prelude::*, vec::Vec};
6
7use crate::{
8 network::Network,
9 secret_key::SecretKey,
10 types::{
11 DecoratedSignature, PublicKey, TransactionEnvelope, TransactionSignaturePayload,
12 TransactionSignaturePayloadTaggedTransaction,
13 },
14 utils::{
15 base64,
16 sha256::{sha256, BinarySha256Hash},
17 },
18 xdr::{
19 compound_types::{LimitedVarArray, LimitedVarOpaque},
20 xdr_codec::XdrCodec,
21 },
22 StellarSdkError,
23};
24
25impl TransactionEnvelope {
26 fn get_signatures(&mut self) -> &mut LimitedVarArray<DecoratedSignature, 20> {
27 match self {
28 TransactionEnvelope::EnvelopeTypeTxV0(envelope) => &mut envelope.signatures,
29 TransactionEnvelope::EnvelopeTypeTx(envelope) => &mut envelope.signatures,
30 TransactionEnvelope::EnvelopeTypeTxFeeBump(envelope) => &mut envelope.signatures,
31 _ => unreachable!("Invalid transaction envelope type"),
32 }
33 }
34
35 pub fn create_base64_signature(&self, network: &Network, keypair: &SecretKey) -> Vec<u8> {
42 let transaction_hash = self.get_hash(network);
43 let signature = keypair.create_signature(transaction_hash);
44 base64::encode(signature)
45 }
46
47 pub fn sign(&mut self, network: &Network, keypairs: Vec<&SecretKey>) -> Result<(), StellarSdkError> {
53 let transaction_hash = self.get_hash(network);
54
55 let signatures = self.get_signatures();
56
57 for keypair in keypairs.iter() {
58 let signature = keypair.create_signature(transaction_hash);
59 let hint = keypair.get_public().get_signature_hint();
60
61 signatures
62 .push(DecoratedSignature { hint, signature: LimitedVarOpaque::new(Vec::from(signature)).unwrap() })
63 .map_err(|_| StellarSdkError::TooManySignatures)?;
64 }
65
66 Ok(())
67 }
68
69 pub fn add_base64_signature<T: AsRef<[u8]>>(
75 &mut self,
76 network: &Network,
77 base64_signature: T,
78 public_key: &PublicKey,
79 ) -> Result<(), StellarSdkError> {
80 let signature = match base64::decode(base64_signature) {
81 Err(err) => Err(StellarSdkError::InvalidBase64Encoding(err))?,
82 Ok(signature) => {
83 if signature.len() != SIGN_LEN {
84 return Err(StellarSdkError::InvalidSignatureLength {
85 found_length: signature.len(),
86 expected_length: SIGN_LEN,
87 })
88 };
89 signature
90 },
91 };
92
93 let transaction_hash = self.get_hash(network);
94 if !public_key.verify_signature(transaction_hash, signature[..].try_into().unwrap()) {
95 return Err(StellarSdkError::PublicKeyCantVerify)
96 }
97
98 let signatures = self.get_signatures();
99
100 signatures
101 .push(DecoratedSignature {
102 hint: public_key.get_signature_hint(),
103 signature: LimitedVarOpaque::new(signature).unwrap(),
104 })
105 .map_err(|_| StellarSdkError::TooManySignatures)?;
106
107 Ok(())
108 }
109
110 pub fn get_hash(&self, network: &Network) -> BinarySha256Hash {
111 let network_id = network.get_id().clone();
112
113 let tagged_transaction = match self {
114 TransactionEnvelope::EnvelopeTypeTxV0(envelope) => {
115 let transaction = envelope.tx.clone().into();
116 TransactionSignaturePayloadTaggedTransaction::EnvelopeTypeTx(transaction)
117 },
118
119 TransactionEnvelope::EnvelopeTypeTx(envelope) =>
120 TransactionSignaturePayloadTaggedTransaction::EnvelopeTypeTx(envelope.tx.clone()),
121
122 TransactionEnvelope::EnvelopeTypeTxFeeBump(envelope) =>
123 TransactionSignaturePayloadTaggedTransaction::EnvelopeTypeTxFeeBump(envelope.tx.clone()),
124
125 _ => unimplemented!("This type of transaction envelope is not supported"),
126 };
127
128 let signature_payload = TransactionSignaturePayload { network_id, tagged_transaction };
129
130 sha256(signature_payload.to_xdr())
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use sp_std::{prelude::*, vec::Vec};
137
138 use crate::{
139 types::{
140 AlphaNum4, Asset, ManageSellOfferOp, Memo, MuxedAccount, Operation, OperationBody, PaymentOp,
141 Preconditions, Price, PublicKey, TimeBounds, Transaction, TransactionEnvelope, TransactionExt,
142 TransactionMeta, TransactionV1Envelope, Uint256,
143 },
144 xdr::compound_types::LimitedVarArray,
145 XdrCodec,
146 };
147
148 use crate::{network::TEST_NETWORK, secret_key::SecretKey};
149
150 const ENVELOPE: &[u8; 408] = b"AAAAAgAAAAC9xFYU1gQJeH4apEfzJkMCsW5DL4GEWRpyVjQHOlWVzgAAAZA\
151 CGsQoAAQytgAAAAAAAAAAAAAAAgAAAAAAAAADAAAAAVhMUEcAAAAAxxJMrxQQOx9raxDm3\
152 lINsLvksi7tj1BCQXzWTtqigbgAAAAAAAAAAAbK5N8CprKDAExLQAAAAAAAAAAAAAAAAAA\
153 AAAMAAAAAAAAAAVhMUEcAAAAAxxJMrxQQOx9raxDm3lINsLvksi7tj1BCQXzWTtqigbgAA\
154 AAAlV2+xQAEaBMAJiWgAAAAAAAAAAAAAAAAAAAAATpVlc4AAABAaX11e1dGcDkXrFT5s3Q\
155 N6x3v4kQqJ/1VIjqO00y6OStd70/aYiXR35e4289RvmBTudJ5Q05PaRsD8p1qa17VDQ==";
156
157 const META: &[u8; 2060] = b"AAAAAgAAAAIAAAADAiOf2gAAAAAAAAAAvcRWFNYECXh+GqRH8yZDArFuQy+Bh\
158 FkaclY0BzpVlc4AAAABMLFdwgIaxCgABDK1AAAAAQAAAAAAAAAAAAAAAAEAAAAAAAAAAAAA\
159 AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAECI5/aAAAAAAAAAAC9xFYU1gQJeH4apEf\
160 zJkMCsW5DL4GEWRpyVjQHOlWVzgAAAAEwsV3CAhrEKAAEMrYAAAABAAAAAAAAAAAAAAAAAQ\
161 AAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAUAAAADAiOf2gAAA\
162 AAAAAAAvcRWFNYECXh+GqRH8yZDArFuQy+BhFkaclY0BzpVlc4AAAABMLFdwgIaxCgABDK2\
163 AAAAAQAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
164 AAAECI5/aAAAAAAAAAAC9xFYU1gQJeH4apEfzJkMCsW5DL4GEWRpyVjQHOlWVzgAAAAEwsV\
165 3CAhrEKAAEMrYAAAACAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAADxs5AUAAAAAAAAAA\
166 AAAAAAAAAAAAAAAAAIjn9oAAAACAAAAAL3EVhTWBAl4fhqkR/MmQwKxbkMvgYRZGnJWNAc6\
167 VZXOAAAAACWxxV0AAAABWExQRwAAAADHEkyvFBA7H2trEObeUg2wu+SyLu2PUEJBfNZO2qK\
168 BuAAAAAAAAAAABsrk3wKmsoMATEtAAAAAAAAAAAAAAAAAAAAAAwIjn9gAAAABAAAAAL3EVh\
169 TWBAl4fhqkR/MmQwKxbkMvgYRZGnJWNAc6VZXOAAAAAVhMUEcAAAAAxxJMrxQQOx9raxDm3\
170 lINsLvksi7tj1BCQXzWTtqigbgAAAAADaUL/n//////////AAAAAQAAAAEAAAAAAAAAAAAA\
171 AAAAAAAAAAAAAAAAAAAAAAABAiOf2gAAAAEAAAAAvcRWFNYECXh+GqRH8yZDArFuQy+BhFk\
172 aclY0BzpVlc4AAAABWExQRwAAAADHEkyvFBA7H2trEObeUg2wu+SyLu2PUEJBfNZO2qKBuA\
173 AAAAANpQv+f/////////8AAAABAAAAAQAAAAAAAAAAAAAAAAbK5N8AAAAAAAAAAAAAAAUAA\
174 AADAiOf2gAAAAAAAAAAvcRWFNYECXh+GqRH8yZDArFuQy+BhFkaclY0BzpVlc4AAAABMLFd\
175 wgIaxCgABDK2AAAAAgAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAQAAAAA8bOQFAAAAAAAAAAA\
176 AAAAAAAAAAAAAAAECI5/aAAAAAAAAAAC9xFYU1gQJeH4apEfzJkMCsW5DL4GEWRpyVjQHOl\
177 WVzgAAAAEwsV3CAhrEKAAEMrYAAAADAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAADxs5\
178 AUAAAAAlV2+wgAAAAAAAAAAAAAAAAIjn9oAAAACAAAAAL3EVhTWBAl4fhqkR/MmQwKxbkMv\
179 gYRZGnJWNAc6VZXOAAAAACWxxV4AAAAAAAAAAVhMUEcAAAAAxxJMrxQQOx9raxDm3lINsLv\
180 ksi7tj1BCQXzWTtqigbgAAAAAlV2+wgAEaBMAJiWgAAAAAAAAAAAAAAAAAAAAAwIjn9oAAA\
181 ABAAAAAL3EVhTWBAl4fhqkR/MmQwKxbkMvgYRZGnJWNAc6VZXOAAAAAVhMUEcAAAAAxxJMr\
182 xQQOx9raxDm3lINsLvksi7tj1BCQXzWTtqigbgAAAAADaUL/n//////////AAAAAQAAAAEA\
183 AAAAAAAAAAAAAAAGyuTfAAAAAAAAAAAAAAABAiOf2gAAAAEAAAAAvcRWFNYECXh+GqRH8yZ\
184 DArFuQy+BhFkaclY0BzpVlc4AAAABWExQRwAAAADHEkyvFBA7H2trEObeUg2wu+SyLu2PUE\
185 JBfNZO2qKBuAAAAAANpQv+f/////////8AAAABAAAAAQAAAAARQQaGAAAAAAbK5N8AAAAAA\
186 AAAAAAAAAA=";
187
188 fn binary_public(public: &str) -> Uint256 {
189 PublicKey::from_encoding(public).unwrap().into_binary()
190 }
191
192 #[test]
193 fn xdr_encode_decode_transaction_envelope() {
194 let envelope = TransactionEnvelope::from_base64_xdr(ENVELOPE).unwrap();
195 assert_eq!(ENVELOPE, &envelope.to_base64_xdr()[..]);
196
197 let meta = TransactionMeta::from_base64_xdr(META).unwrap();
198 assert_eq!(META, &meta.to_base64_xdr()[..]);
199 }
200
201 #[test]
202 fn decode_complex_transaction() {
203 let envelope = "AAAAAgAAAAAH0lW2BMK5GhjjJ6rrG4xbz7f80vEjTkNnIN8\
204 9rLn0sgAABdwCIrMOAAEgvAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAAAAA\
205 AAwAAAAFVU0RUAAAAAKEzZhDpuPmzRl/ENBVUCh5Bd0GLgQQB9u7uGKat9lNZAAAAAUx\
206 UQwAAAAAAurdHYVeUhyvmUCg+kMeAmFcY6Zi+jZFh958xlu6ItVYAAAAAdRFqtAAAMc8\
207 AHoSAAAAAACZVXrUAAAAAAAAAAwAAAAFVU0RUAAAAAKEzZhDpuPmzRl/ENBVUCh5Bd0G\
208 LgQQB9u7uGKat9lNZAAAAAUxUQwAAAAAAurdHYVeUhyvmUCg+kMeAmFcY6Zi+jZFh958\
209 xlu6ItVYAAAAAjDnTwAAA+Q8AmJaAAAAAACZcaYkAAAAAAAAAAwAAAAFVU0RUAAAAAKE\
210 zZhDpuPmzRl/ENBVUCh5Bd0GLgQQB9u7uGKat9lNZAAAAAUxUQwAAAAAAurdHYVeUhyv\
211 mUCg+kMeAmFcY6Zi+jZFh958xlu6ItVYAAAAAjDeJ0AAA+RMAmJaAAAAAACZeXm4AAAA\
212 AAAAAAwAAAAFVU0RUAAAAAKEzZhDpuPmzRl/ENBVUCh5Bd0GLgQQB9u7uGKat9lNZAAA\
213 AAUxUQwAAAAAAurdHYVeUhyvmUCg+kMeAmFcY6Zi+jZFh958xlu6ItVYAAAAAjDU/4AA\
214 A+RcAmJaAAAAAACZmh6EAAAAAAAAAAwAAAAFVU0RUAAAAAKEzZhDpuPmzRl/ENBVUCh5\
215 Bd0GLgQQB9u7uGKat9lNZAAAAAUxUQwAAAAAAurdHYVeUhyvmUCg+kMeAmFcY6Zi+jZF\
216 h958xlu6ItVYAAAAAjDL18AAA+RsAmJaAAAAAACZxT/UAAAAAAAAAAwAAAAFMVEMAAAA\
217 AALq3R2FXlIcr5lAoPpDHgJhXGOmYvo2RYfefMZbuiLVWAAAAAVVTRFQAAAAAoTNmEOm\
218 4+bNGX8Q0FVQKHkF3QYuBBAH27u4Ypq32U1kAAAAAABd0sAAAPjkAAABkAAAAACZbMjU\
219 AAAAAAAAAAwAAAAFMVEMAAAAAALq3R2FXlIcr5lAoPpDHgJhXGOmYvo2RYfefMZbuiLV\
220 WAAAAAVVTRFQAAAAAoTNmEOm4+bNGX8Q0FVQKHkF3QYuBBAH27u4Ypq32U1kAAAAAAHA\
221 x4AAAPjsAAABkAAAAACZbrG4AAAAAAAAAAwAAAAFMVEMAAAAAALq3R2FXlIcr5lAoPpD\
222 HgJhXGOmYvo2RYfefMZbuiLVWAAAAAVVTRFQAAAAAoTNmEOm4+bNGX8Q0FVQKHkF3QYu\
223 BBAH27u4Ypq32U1kAAAAAAOThwAAAD48AAAAZAAAAACZbrG8AAAAAAAAAAwAAAAFMVEM\
224 AAAAAALq3R2FXlIcr5lAoPpDHgJhXGOmYvo2RYfefMZbuiLVWAAAAAVVTRFQAAAAAoTN\
225 mEOm4+bNGX8Q0FVQKHkF3QYuBBAH27u4Ypq32U1kAAAAAAOThwAAAPj0AAABkAAAAACZ\
226 j93QAAAAAAAAAAwAAAAFMVEMAAAAAALq3R2FXlIcr5lAoPpDHgJhXGOmYvo2RYfefMZb\
227 uiLVWAAAAAVVTRFQAAAAAoTNmEOm4+bNGX8Q0FVQKHkF3QYuBBAH27u4Ypq32U1kAAAA\
228 AAOThwAAAHx8AAAAyAAAAACZj93UAAAAAAAAAAwAAAAFMVEMAAAAAALq3R2FXlIcr5lA\
229 oPpDHgJhXGOmYvo2RYfefMZbuiLVWAAAAAVVTRFQAAAAAoTNmEOm4+bNGX8Q0FVQKHkF\
230 3QYuBBAH27u4Ypq32U1kAAAAAAOThXAAADHMAAAAUAAAAACZj93YAAAAAAAAAAwAAAAF\
231 MVEMAAAAAALq3R2FXlIcr5lAoPpDHgJhXGOmYvo2RYfefMZbuiLVWAAAAAVVTRFQAAAA\
232 AoTNmEOm4+bNGX8Q0FVQKHkF3QYuBBAH27u4Ypq32U1kAAAAAAOThXAAAD5AAAAAZAAA\
233 AACZqElgAAAAAAAAAAwAAAAFMVEMAAAAAALq3R2FXlIcr5lAoPpDHgJhXGOmYvo2RYfe\
234 fMZbuiLVWAAAAAVVTRFQAAAAAoTNmEOm4+bNGX8Q0FVQKHkF3QYuBBAH27u4Ypq32U1k\
235 AAAAAAOThwAAAPkEAAABkAAAAACZqao4AAAAAAAAAAwAAAAFMVEMAAAAAALq3R2FXlIc\
236 r5lAoPpDHgJhXGOmYvo2RYfefMZbuiLVWAAAAAVVTRFQAAAAAoTNmEOm4+bNGX8Q0FVQ\
237 KHkF3QYuBBAH27u4Ypq32U1kAAAAAAOThwAAAHyEAAAAyAAAAACZqbFIAAAAAAAAAAwA\
238 AAAFMVEMAAAAAALq3R2FXlIcr5lAoPpDHgJhXGOmYvo2RYfefMZbuiLVWAAAAAVVTRFQ\
239 AAAAAoTNmEOm4+bNGX8Q0FVQKHkF3QYuBBAH27u4Ypq32U1kAAAAAAOThXAAAPkMAAAB\
240 kAAAAACZw6OsAAAAAAAAAAay59LIAAABAdMmVEkeQO1ygJEUCpGk5qUzfhHWUD3qikrA\
241 7ZXRpe2n5JsRoJot88+Fc+ayFPJoIsKsP457TwyzTorPwuUGxBQ==";
242
243 let envelope = TransactionEnvelope::from_base64_xdr(envelope);
244 assert!(envelope.is_ok());
245 let envelope = match envelope.unwrap() {
246 TransactionEnvelope::EnvelopeTypeTx(envelope) => envelope,
247 _ => unreachable!(),
248 };
249
250 let transaction = envelope.tx;
251
252 let expected_transaction = Transaction {
253 source_account: MuxedAccount::KeyTypeEd25519(binary_public(
254 "GAD5EVNWATBLSGQY4MT2V2Y3RRN47N742LYSGTSDM4QN6PNMXH2LF7WV",
255 )),
256 fee: 1500,
257 seq_num: 153882209995006140,
258 cond: Preconditions::PrecondTime(TimeBounds { min_time: 0, max_time: 0 }),
259 memo: Memo::MemoNone,
260 operations: LimitedVarArray::new(vec![
261 Operation {
262 source_account: None,
263 body: OperationBody::ManageSellOffer(ManageSellOfferOp {
264 selling: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
265 asset_code: b"USDT".clone(),
266 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
267 "GCQTGZQQ5G4PTM2GL7CDIFKUBIPEC52BROAQIAPW53XBRJVN6ZJVTG6V",
268 )),
269 }),
270 buying: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
271 asset_code: b"LTC\0".clone(),
272 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
273 "GC5LOR3BK6KIOK7GKAUD5EGHQCMFOGHJTC7I3ELB66PTDFXORC2VM5LP",
274 )),
275 }),
276 amount: 1964075700,
277 price: Price { n: 12751, d: 2000000 },
278 offer_id: 643129013,
279 }),
280 },
281 Operation {
282 source_account: None,
283 body: OperationBody::ManageSellOffer(ManageSellOfferOp {
284 selling: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
285 asset_code: b"USDT".clone(),
286 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
287 "GCQTGZQQ5G4PTM2GL7CDIFKUBIPEC52BROAQIAPW53XBRJVN6ZJVTG6V",
288 )),
289 }),
290 buying: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
291 asset_code: b"LTC\0".clone(),
292 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
293 "GC5LOR3BK6KIOK7GKAUD5EGHQCMFOGHJTC7I3ELB66PTDFXORC2VM5LP",
294 )),
295 }),
296 amount: 2352600000,
297 price: Price { n: 63759, d: 10000000 },
298 offer_id: 643590537,
299 }),
300 },
301 Operation {
302 source_account: None,
303 body: OperationBody::ManageSellOffer(ManageSellOfferOp {
304 selling: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
305 asset_code: b"USDT".clone(),
306 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
307 "GCQTGZQQ5G4PTM2GL7CDIFKUBIPEC52BROAQIAPW53XBRJVN6ZJVTG6V",
308 )),
309 }),
310 buying: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
311 asset_code: b"LTC\0".clone(),
312 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
313 "GC5LOR3BK6KIOK7GKAUD5EGHQCMFOGHJTC7I3ELB66PTDFXORC2VM5LP",
314 )),
315 }),
316 amount: 2352450000,
317 price: Price { n: 63763, d: 10000000 },
318 offer_id: 643718766,
319 }),
320 },
321 Operation {
322 source_account: None,
323 body: OperationBody::ManageSellOffer(ManageSellOfferOp {
324 selling: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
325 asset_code: b"USDT".clone(),
326 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
327 "GCQTGZQQ5G4PTM2GL7CDIFKUBIPEC52BROAQIAPW53XBRJVN6ZJVTG6V",
328 )),
329 }),
330 buying: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
331 asset_code: b"LTC\0".clone(),
332 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
333 "GC5LOR3BK6KIOK7GKAUD5EGHQCMFOGHJTC7I3ELB66PTDFXORC2VM5LP",
334 )),
335 }),
336 amount: 2352300000,
337 price: Price { n: 63767, d: 10000000 },
338 offer_id: 644253601,
339 }),
340 },
341 Operation {
342 source_account: None,
343 body: OperationBody::ManageSellOffer(ManageSellOfferOp {
344 selling: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
345 asset_code: b"USDT".clone(),
346 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
347 "GCQTGZQQ5G4PTM2GL7CDIFKUBIPEC52BROAQIAPW53XBRJVN6ZJVTG6V",
348 )),
349 }),
350 buying: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
351 asset_code: b"LTC\0".clone(),
352 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
353 "GC5LOR3BK6KIOK7GKAUD5EGHQCMFOGHJTC7I3ELB66PTDFXORC2VM5LP",
354 )),
355 }),
356 amount: 2352150000,
357 price: Price { n: 63771, d: 10000000 },
358 offer_id: 644960245,
359 }),
360 },
361 Operation {
362 source_account: None,
363 body: OperationBody::ManageSellOffer(ManageSellOfferOp {
364 selling: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
365 asset_code: b"LTC\0".clone(),
366 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
367 "GC5LOR3BK6KIOK7GKAUD5EGHQCMFOGHJTC7I3ELB66PTDFXORC2VM5LP",
368 )),
369 }),
370 buying: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
371 asset_code: b"USDT".clone(),
372 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
373 "GCQTGZQQ5G4PTM2GL7CDIFKUBIPEC52BROAQIAPW53XBRJVN6ZJVTG6V",
374 )),
375 }),
376 amount: 1537200,
377 price: Price { n: 15929, d: 100 },
378 offer_id: 643510837,
379 }),
380 },
381 Operation {
382 source_account: None,
383 body: OperationBody::ManageSellOffer(ManageSellOfferOp {
384 selling: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
385 asset_code: b"LTC\0".clone(),
386 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
387 "GC5LOR3BK6KIOK7GKAUD5EGHQCMFOGHJTC7I3ELB66PTDFXORC2VM5LP",
388 )),
389 }),
390 buying: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
391 asset_code: b"USDT".clone(),
392 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
393 "GCQTGZQQ5G4PTM2GL7CDIFKUBIPEC52BROAQIAPW53XBRJVN6ZJVTG6V",
394 )),
395 }),
396 amount: 7352800,
397 price: Price { n: 15931, d: 100 },
398 offer_id: 643542126,
399 }),
400 },
401 Operation {
402 source_account: None,
403 body: OperationBody::ManageSellOffer(ManageSellOfferOp {
404 selling: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
405 asset_code: b"LTC\0".clone(),
406 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
407 "GC5LOR3BK6KIOK7GKAUD5EGHQCMFOGHJTC7I3ELB66PTDFXORC2VM5LP",
408 )),
409 }),
410 buying: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
411 asset_code: b"USDT".clone(),
412 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
413 "GCQTGZQQ5G4PTM2GL7CDIFKUBIPEC52BROAQIAPW53XBRJVN6ZJVTG6V",
414 )),
415 }),
416 amount: 15000000,
417 price: Price { n: 3983, d: 25 },
418 offer_id: 643542127,
419 }),
420 },
421 Operation {
422 source_account: None,
423 body: OperationBody::ManageSellOffer(ManageSellOfferOp {
424 selling: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
425 asset_code: b"LTC\0".clone(),
426 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
427 "GC5LOR3BK6KIOK7GKAUD5EGHQCMFOGHJTC7I3ELB66PTDFXORC2VM5LP",
428 )),
429 }),
430 buying: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
431 asset_code: b"USDT".clone(),
432 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
433 "GCQTGZQQ5G4PTM2GL7CDIFKUBIPEC52BROAQIAPW53XBRJVN6ZJVTG6V",
434 )),
435 }),
436 amount: 15000000,
437 price: Price { n: 15933, d: 100 },
438 offer_id: 644085620,
439 }),
440 },
441 Operation {
442 source_account: None,
443 body: OperationBody::ManageSellOffer(ManageSellOfferOp {
444 selling: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
445 asset_code: b"LTC\0".clone(),
446 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
447 "GC5LOR3BK6KIOK7GKAUD5EGHQCMFOGHJTC7I3ELB66PTDFXORC2VM5LP",
448 )),
449 }),
450 buying: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
451 asset_code: b"USDT".clone(),
452 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
453 "GCQTGZQQ5G4PTM2GL7CDIFKUBIPEC52BROAQIAPW53XBRJVN6ZJVTG6V",
454 )),
455 }),
456 amount: 15000000,
457 price: Price { n: 7967, d: 50 },
458 offer_id: 644085621,
459 }),
460 },
461 Operation {
462 source_account: None,
463 body: OperationBody::ManageSellOffer(ManageSellOfferOp {
464 selling: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
465 asset_code: b"LTC\0".clone(),
466 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
467 "GC5LOR3BK6KIOK7GKAUD5EGHQCMFOGHJTC7I3ELB66PTDFXORC2VM5LP",
468 )),
469 }),
470 buying: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
471 asset_code: b"USDT".clone(),
472 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
473 "GCQTGZQQ5G4PTM2GL7CDIFKUBIPEC52BROAQIAPW53XBRJVN6ZJVTG6V",
474 )),
475 }),
476 amount: 14999900,
477 price: Price { n: 3187, d: 20 },
478 offer_id: 644085622,
479 }),
480 },
481 Operation {
482 source_account: None,
483 body: OperationBody::ManageSellOffer(ManageSellOfferOp {
484 selling: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
485 asset_code: b"LTC\0".clone(),
486 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
487 "GC5LOR3BK6KIOK7GKAUD5EGHQCMFOGHJTC7I3ELB66PTDFXORC2VM5LP",
488 )),
489 }),
490 buying: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
491 asset_code: b"USDT".clone(),
492 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
493 "GCQTGZQQ5G4PTM2GL7CDIFKUBIPEC52BROAQIAPW53XBRJVN6ZJVTG6V",
494 )),
495 }),
496 amount: 14999900,
497 price: Price { n: 3984, d: 25 },
498 offer_id: 644485720,
499 }),
500 },
501 Operation {
502 source_account: None,
503 body: OperationBody::ManageSellOffer(ManageSellOfferOp {
504 selling: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
505 asset_code: b"LTC\0".clone(),
506 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
507 "GC5LOR3BK6KIOK7GKAUD5EGHQCMFOGHJTC7I3ELB66PTDFXORC2VM5LP",
508 )),
509 }),
510 buying: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
511 asset_code: b"USDT".clone(),
512 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
513 "GCQTGZQQ5G4PTM2GL7CDIFKUBIPEC52BROAQIAPW53XBRJVN6ZJVTG6V",
514 )),
515 }),
516 amount: 15000000,
517 price: Price { n: 15937, d: 100 },
518 offer_id: 644508302,
519 }),
520 },
521 Operation {
522 source_account: None,
523 body: OperationBody::ManageSellOffer(ManageSellOfferOp {
524 selling: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
525 asset_code: b"LTC\0".clone(),
526 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
527 "GC5LOR3BK6KIOK7GKAUD5EGHQCMFOGHJTC7I3ELB66PTDFXORC2VM5LP",
528 )),
529 }),
530 buying: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
531 asset_code: b"USDT".clone(),
532 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
533 "GCQTGZQQ5G4PTM2GL7CDIFKUBIPEC52BROAQIAPW53XBRJVN6ZJVTG6V",
534 )),
535 }),
536 amount: 15000000,
537 price: Price { n: 7969, d: 50 },
538 offer_id: 644508754,
539 }),
540 },
541 Operation {
542 source_account: None,
543 body: OperationBody::ManageSellOffer(ManageSellOfferOp {
544 selling: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
545 asset_code: b"LTC\0".clone(),
546 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
547 "GC5LOR3BK6KIOK7GKAUD5EGHQCMFOGHJTC7I3ELB66PTDFXORC2VM5LP",
548 )),
549 }),
550 buying: Asset::AssetTypeCreditAlphanum4(AlphaNum4 {
551 asset_code: b"USDT".clone(),
552 issuer: PublicKey::PublicKeyTypeEd25519(binary_public(
553 "GCQTGZQQ5G4PTM2GL7CDIFKUBIPEC52BROAQIAPW53XBRJVN6ZJVTG6V",
554 )),
555 }),
556 amount: 14999900,
557 price: Price { n: 15939, d: 100 },
558 offer_id: 644933867,
559 }),
560 },
561 ])
562 .unwrap(),
563 ext: TransactionExt::V0,
564 };
565
566 assert_eq!(transaction, expected_transaction);
567 }
568
569 #[test]
570 fn sign_simple_transaction() {
571 let secret = "SCDSVACTNFNSD5LQZ5LWUWEY3UIAML2J7ALPFCD6ZX4D3TVJV7X243N3";
572 let keypair = SecretKey::from_encoding(secret);
573 assert!(keypair.is_ok());
574 let keypair = keypair.unwrap();
575
576 let dest_public = PublicKey::from_encoding("GDMTKCJQ322RDTGOBLIPVEUCO3EIEJLXDV4JTWLXU6AFOYTMSJ45WZY5").unwrap();
577
578 let mut transaction_envelope = TransactionEnvelope::EnvelopeTypeTx(TransactionV1Envelope {
579 tx: Transaction {
580 source_account: MuxedAccount::KeyTypeEd25519(keypair.get_public().clone().into_binary()),
581 fee: 10000,
582 seq_num: 59481002082305,
583 cond: Preconditions::PrecondTime(TimeBounds { min_time: 0, max_time: 0 }),
584 memo: Memo::MemoNone,
585 operations: LimitedVarArray::new(vec![Operation {
586 source_account: None,
587 body: OperationBody::Payment(PaymentOp {
588 destination: MuxedAccount::KeyTypeEd25519(dest_public.into_binary()),
589 asset: Asset::AssetTypeNative,
590 amount: 10_000_000,
591 }),
592 }])
593 .unwrap(),
594 ext: TransactionExt::V0,
595 },
596 signatures: LimitedVarArray::new(Vec::new()).unwrap(),
597 });
598
599 let expected_xdr = b"AAAAAgAAAABRVWJF9F/Kd+p+e65fn2mnDGH\
600 5BnlL9yXAMBTaJbnUcQAAJxAAADYZAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAA\
601 AAAAAAAAAAAEAAAAAAAAAAQAAAADZNQkw3rURzM4K0PqSgnbIgiV3HXiZ2Xe\
602 ngFdibJJ52wAAAAAAAAAAAJiWgAAAAAAAAAAA";
603 assert_eq!(transaction_envelope.to_base64_xdr().as_slice(), &expected_xdr[..]);
604
605 let signing_result = transaction_envelope.sign(&TEST_NETWORK, vec![&keypair]);
606 assert!(signing_result.is_ok());
607
608 let expected_signed_xdr = b"AAAAAgAAAABRVWJF9F/Kd+p+e65\
609 fn2mnDGH5BnlL9yXAMBTaJbnUcQAAJxAAADYZAAAAAQAAAAEAAAAAAAAAAA\
610 AAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAADZNQkw3rURzM4K0PqSgnbIg\
611 iV3HXiZ2XengFdibJJ52wAAAAAAAAAAAJiWgAAAAAAAAAABJbnUcQAAAEAv\
612 CLQxbuE/zeBYq5Q/17d1hvcQME5uHUJ9SE8L8E/PQHa00jfGpFrtsG+XQV0\
613 DI0AnnqQhBhHKl1l5LNpIoxIA";
614
615 assert_eq!(transaction_envelope.to_base64_xdr().as_slice(), &expected_signed_xdr[..]);
616 }
617}