1use sodiumoxide::crypto::sign::{PUBLICKEYBYTES, SIGNATUREBYTES};
2use std::{
3 borrow::Cow,
4 io::Cursor,
5 ops::{Deref, DerefMut},
6};
7
8use crate::{
9 asset::Asset,
10 crypto::{double_sha256, Digest, KeyPair, PublicKey, ScriptHash, SigPair},
11 script::Script,
12 serializer::*,
13};
14
15#[macro_use]
16mod util;
17
18pub mod tx_pool;
19
20pub use self::tx_pool::*;
21
22#[repr(u8)]
23#[derive(Copy, Clone, Debug, PartialEq)]
24pub enum TxType {
25 OWNER = 0x00,
26 MINT = 0x01,
27 REWARD = 0x02,
28 TRANSFER = 0x03,
29}
30
31pub trait SerializeTx {
32 fn serialize(&self, v: &mut Vec<u8>);
33}
34
35pub trait DeserializeTx<T> {
36 fn deserialize(cur: &mut Cursor<&[u8]>, tx: Tx) -> Option<T>;
37}
38
39#[derive(Clone, Debug, PartialEq)]
40pub struct TxId(Digest);
41
42impl TxId {
43 pub fn from_digest(txid: Digest) -> Self {
44 TxId(txid)
45 }
46}
47
48impl AsRef<[u8]> for TxId {
49 fn as_ref(&self) -> &[u8] {
50 &self.0
51 }
52}
53
54#[derive(Clone, Debug, PartialEq)]
55pub struct TxPrecompData<'a> {
56 tx: Cow<'a, TxVariant>,
57 txid: TxId,
58 bytes: Vec<u8>,
59 sig_tx_suffix: usize,
60}
61
62impl<'a> TxPrecompData<'a> {
63 pub fn from_tx<T>(tx: T) -> Self
64 where
65 T: Into<Cow<'a, TxVariant>>,
66 {
67 let tx = tx.into();
68 let mut bytes = Vec::with_capacity(4096);
69 tx.serialize(&mut bytes);
70 let sigs_len = 1 + (tx.sigs().len() * (PUBLICKEYBYTES + SIGNATUREBYTES));
71 let sig_tx_suffix = bytes.len() - sigs_len;
72
73 let txid = TxId(double_sha256(&bytes));
74 Self {
75 tx,
76 txid,
77 bytes,
78 sig_tx_suffix,
79 }
80 }
81
82 #[inline]
83 pub fn take(self) -> TxVariant {
84 self.tx.into_owned()
85 }
86
87 #[inline]
88 pub fn tx(&self) -> &TxVariant {
89 &self.tx
90 }
91
92 #[inline]
93 pub fn txid(&self) -> &TxId {
94 &self.txid
95 }
96
97 #[inline]
98 pub fn bytes(&self) -> &[u8] {
99 &self.bytes
100 }
101
102 #[inline]
103 pub fn bytes_without_sigs(&self) -> &[u8] {
104 &self.bytes[..self.sig_tx_suffix]
105 }
106}
107
108impl<'a> Into<Cow<'a, TxPrecompData<'a>>> for TxPrecompData<'a> {
109 fn into(self) -> Cow<'a, TxPrecompData<'a>> {
110 Cow::Owned(self)
111 }
112}
113
114impl<'a> Into<Cow<'a, TxPrecompData<'a>>> for &'a TxPrecompData<'a> {
115 fn into(self) -> Cow<'a, TxPrecompData<'a>> {
116 Cow::Borrowed(self)
117 }
118}
119
120#[derive(Clone, Debug, PartialEq)]
121pub enum TxVariant {
122 V0(TxVariantV0),
123}
124
125impl TxVariant {
126 #[inline]
127 pub fn precompute(self) -> TxPrecompData<'static> {
128 TxPrecompData::from_tx(Cow::Owned(self))
129 }
130
131 #[inline]
132 pub fn timestamp(&self) -> u64 {
133 match self {
134 TxVariant::V0(tx) => tx.timestamp,
135 }
136 }
137
138 #[inline]
139 pub fn sigs(&self) -> &[SigPair] {
140 match self {
141 TxVariant::V0(tx) => &tx.signature_pairs,
142 }
143 }
144
145 #[inline]
146 pub fn sigs_mut(&mut self) -> &mut Vec<SigPair> {
147 match self {
148 TxVariant::V0(tx) => &mut tx.signature_pairs,
149 }
150 }
151
152 pub fn script(&self) -> Option<&Script> {
153 match self {
154 TxVariant::V0(var) => match var {
155 TxVariantV0::OwnerTx(tx) => Some(&tx.script),
156 TxVariantV0::MintTx(tx) => Some(&tx.script),
157 TxVariantV0::RewardTx(_) => None,
158 TxVariantV0::TransferTx(tx) => Some(&tx.script),
159 },
160 }
161 }
162
163 #[inline]
164 pub fn sign(&self, key_pair: &KeyPair) -> SigPair {
165 let mut buf = Vec::with_capacity(4096);
166 self.serialize_without_sigs(&mut buf);
167 key_pair.sign(&buf)
168 }
169
170 #[inline]
171 pub fn append_sign(&mut self, key_pair: &KeyPair) {
172 let pair = self.sign(key_pair);
173 self.sigs_mut().push(pair);
174 }
175
176 pub fn serialize(&self, buf: &mut Vec<u8>) {
177 self.serialize_without_sigs(buf);
178 match self {
179 TxVariant::V0(var) => {
180 macro_rules! serialize_sigs {
181 ($name:expr) => {{
182 buf.push($name.signature_pairs.len() as u8);
183 for sig in &$name.signature_pairs {
184 buf.push_sig_pair(sig)
185 }
186 }};
187 }
188
189 match var {
190 TxVariantV0::OwnerTx(tx) => serialize_sigs!(tx),
191 TxVariantV0::MintTx(tx) => serialize_sigs!(tx),
192 TxVariantV0::RewardTx(tx) => serialize_sigs!(tx),
193 TxVariantV0::TransferTx(tx) => serialize_sigs!(tx),
194 }
195 }
196 };
197 }
198
199 pub fn serialize_without_sigs(&self, buf: &mut Vec<u8>) {
200 match self {
201 TxVariant::V0(var) => {
202 buf.push_u16(0x00);
204
205 match var {
206 TxVariantV0::OwnerTx(tx) => tx.serialize(buf),
207 TxVariantV0::MintTx(tx) => tx.serialize(buf),
208 TxVariantV0::RewardTx(tx) => tx.serialize(buf),
209 TxVariantV0::TransferTx(tx) => tx.serialize(buf),
210 }
211 }
212 };
213 }
214
215 pub fn deserialize(cur: &mut Cursor<&[u8]>) -> Option<TxVariant> {
216 let tx_ver = cur.take_u16().ok()?;
217 match tx_ver {
218 0x00 => {
219 let (base, tx_type) = Tx::deserialize_header(cur)?;
220 let mut tx = match tx_type {
221 TxType::OWNER => TxVariantV0::OwnerTx(OwnerTx::deserialize(cur, base)?),
222 TxType::MINT => TxVariantV0::MintTx(MintTx::deserialize(cur, base)?),
223 TxType::REWARD => TxVariantV0::RewardTx(RewardTx::deserialize(cur, base)?),
224 TxType::TRANSFER => {
225 TxVariantV0::TransferTx(TransferTx::deserialize(cur, base)?)
226 }
227 };
228 tx.signature_pairs = {
229 let len = cur.take_u8().ok()?;
230 let mut sigs = Vec::with_capacity(len as usize);
231 for _ in 0..len {
232 sigs.push(cur.take_sig_pair().ok()?)
233 }
234 sigs
235 };
236 Some(TxVariant::V0(tx))
237 }
238 _ => None,
239 }
240 }
241}
242
243impl<'a> Into<Cow<'a, TxVariant>> for TxVariant {
244 fn into(self) -> Cow<'a, TxVariant> {
245 Cow::Owned(self)
246 }
247}
248
249impl<'a> Into<Cow<'a, TxVariant>> for &'a TxVariant {
250 fn into(self) -> Cow<'a, TxVariant> {
251 Cow::Borrowed(self)
252 }
253}
254
255#[derive(Clone, Debug, PartialEq)]
256pub enum TxVariantV0 {
257 OwnerTx(OwnerTx),
258 MintTx(MintTx),
259 RewardTx(RewardTx),
260 TransferTx(TransferTx),
261}
262
263impl Deref for TxVariantV0 {
264 type Target = Tx;
265
266 fn deref(&self) -> &Self::Target {
267 match self {
268 TxVariantV0::OwnerTx(tx) => &tx.base,
269 TxVariantV0::MintTx(tx) => &tx.base,
270 TxVariantV0::RewardTx(tx) => &tx.base,
271 TxVariantV0::TransferTx(tx) => &tx.base,
272 }
273 }
274}
275
276impl DerefMut for TxVariantV0 {
277 fn deref_mut(&mut self) -> &mut Tx {
278 match self {
279 TxVariantV0::OwnerTx(tx) => &mut tx.base,
280 TxVariantV0::MintTx(tx) => &mut tx.base,
281 TxVariantV0::RewardTx(tx) => &mut tx.base,
282 TxVariantV0::TransferTx(tx) => &mut tx.base,
283 }
284 }
285}
286
287#[derive(Clone, Debug, PartialEq)]
288pub struct Tx {
289 pub timestamp: u64,
290 pub fee: Asset,
291 pub signature_pairs: Vec<SigPair>,
292}
293
294impl Tx {
295 fn serialize_header(&self, v: &mut Vec<u8>) {
296 v.push_u64(self.timestamp);
298 v.push_asset(self.fee);
299 }
300
301 fn deserialize_header(cur: &mut Cursor<&[u8]>) -> Option<(Tx, TxType)> {
302 let tx_type = match cur.take_u8().ok()? {
303 t if t == TxType::OWNER as u8 => TxType::OWNER,
304 t if t == TxType::MINT as u8 => TxType::MINT,
305 t if t == TxType::REWARD as u8 => TxType::REWARD,
306 t if t == TxType::TRANSFER as u8 => TxType::TRANSFER,
307 _ => return None,
308 };
309 let timestamp = cur.take_u64().ok()?;
310 let fee = cur.take_asset().ok()?;
311 let tx = Tx {
312 timestamp,
313 fee,
314 signature_pairs: Vec::new(),
315 };
316
317 Some((tx, tx_type))
318 }
319}
320
321#[derive(Clone, Debug, PartialEq)]
322pub struct OwnerTx {
323 pub base: Tx,
324 pub minter: PublicKey, pub wallet: ScriptHash, pub script: Script, }
328
329impl SerializeTx for OwnerTx {
330 fn serialize(&self, v: &mut Vec<u8>) {
331 v.push(TxType::OWNER as u8);
332 self.serialize_header(v);
333 v.push_pub_key(&self.minter);
334 v.push_digest(&self.wallet.0);
335 v.push_bytes(&self.script);
336 }
337}
338
339impl DeserializeTx<OwnerTx> for OwnerTx {
340 fn deserialize(cur: &mut Cursor<&[u8]>, tx: Tx) -> Option<OwnerTx> {
341 let minter = cur.take_pub_key().ok()?;
342 let wallet = ScriptHash(cur.take_digest().ok()?);
343 let script = cur.take_bytes().ok()?.into();
344 Some(OwnerTx {
345 base: tx,
346 minter,
347 wallet,
348 script,
349 })
350 }
351}
352
353#[derive(Clone, Debug, PartialEq)]
354pub struct MintTx {
355 pub base: Tx,
356 pub to: ScriptHash,
357 pub amount: Asset,
358 pub attachment: Vec<u8>,
359 pub attachment_name: String,
360 pub script: Script,
361}
362
363impl SerializeTx for MintTx {
364 fn serialize(&self, v: &mut Vec<u8>) {
365 v.push(TxType::MINT as u8);
366 self.serialize_header(v);
367 v.push_digest(&self.to.0);
368 v.push_asset(self.amount);
369 v.push_bytes(&self.attachment);
370 v.push_bytes(self.attachment_name.as_bytes());
371 v.push_bytes(&self.script);
372 }
373}
374
375impl DeserializeTx<MintTx> for MintTx {
376 fn deserialize(cur: &mut Cursor<&[u8]>, tx: Tx) -> Option<Self> {
377 let to = ScriptHash(cur.take_digest().ok()?);
378 let amount = cur.take_asset().ok()?;
379 let attachment = cur.take_bytes().ok()?;
380 let attachment_name = {
381 let bytes = cur.take_bytes().ok()?;
382 String::from_utf8(bytes).ok()?
383 };
384 let script = Script::from(cur.take_bytes().ok()?);
385 Some(Self {
386 base: tx,
387 to,
388 amount,
389 attachment,
390 attachment_name,
391 script,
392 })
393 }
394}
395
396#[derive(Clone, Debug, PartialEq)]
397pub struct RewardTx {
398 pub base: Tx,
399 pub to: ScriptHash,
400 pub rewards: Asset,
401}
402
403impl SerializeTx for RewardTx {
404 fn serialize(&self, v: &mut Vec<u8>) {
405 debug_assert_eq!(self.base.signature_pairs.len(), 0);
406 v.push(TxType::REWARD as u8);
407 self.serialize_header(v);
408 v.push_digest(&self.to.0);
409 v.push_asset(self.rewards);
410 }
411}
412
413impl DeserializeTx<RewardTx> for RewardTx {
414 fn deserialize(cur: &mut Cursor<&[u8]>, tx: Tx) -> Option<RewardTx> {
415 let key = ScriptHash(cur.take_digest().ok()?);
416 let rewards = cur.take_asset().ok()?;
417
418 Some(RewardTx {
419 base: tx,
420 to: key,
421 rewards,
422 })
423 }
424}
425
426#[derive(Clone, Debug, PartialEq)]
427pub struct TransferTx {
428 pub base: Tx,
429 pub from: ScriptHash,
430 pub to: ScriptHash,
431 pub script: Script,
432 pub amount: Asset,
433 pub memo: Vec<u8>,
434}
435
436impl SerializeTx for TransferTx {
437 fn serialize(&self, v: &mut Vec<u8>) {
438 v.push(TxType::TRANSFER as u8);
439 self.serialize_header(v);
440 v.push_digest(&self.from.0);
441 v.push_digest(&self.to.0);
442 v.push_bytes(&self.script);
443 v.push_asset(self.amount);
444 v.push_bytes(&self.memo);
445 }
446}
447
448impl DeserializeTx<TransferTx> for TransferTx {
449 fn deserialize(cur: &mut Cursor<&[u8]>, tx: Tx) -> Option<TransferTx> {
450 let from = ScriptHash(cur.take_digest().ok()?);
451 let to = ScriptHash(cur.take_digest().ok()?);
452 let script = cur.take_bytes().ok()?.into();
453 let amount = cur.take_asset().ok()?;
454 let memo = cur.take_bytes().ok()?;
455 Some(TransferTx {
456 base: tx,
457 from,
458 to,
459 script,
460 amount,
461 memo,
462 })
463 }
464}
465
466tx_deref!(OwnerTx);
467tx_deref!(MintTx);
468tx_deref!(RewardTx);
469tx_deref!(TransferTx);
470
471#[cfg(test)]
472mod tests {
473 use super::*;
474 use crate::{
475 crypto,
476 script::{Builder, OpFrame},
477 };
478
479 macro_rules! cmp_base_tx {
480 ($id:ident, $ts:expr, $fee:expr) => {
481 assert_eq!($id.timestamp, $ts);
482 assert_eq!($id.fee.to_string(), $fee);
483 };
484 }
485
486 #[test]
487 fn serialize_tx_with_empty_sigs() {
488 let to = crypto::KeyPair::gen();
489 let reward_tx = TxVariant::V0(TxVariantV0::RewardTx(RewardTx {
490 base: Tx {
491 timestamp: 123,
492 fee: get_asset("123.00000 MARK"),
493 signature_pairs: vec![],
494 },
495 to: to.0.into(),
496 rewards: get_asset("1.50000 MARK"),
497 }));
498
499 let mut v = vec![];
500 reward_tx.serialize(&mut v);
501
502 let mut c = Cursor::<&[u8]>::new(&v);
503 TxVariant::deserialize(&mut c).unwrap();
504 }
505
506 #[test]
507 fn serialize_tx_with_sigs() {
508 let minter = crypto::KeyPair::gen();
509 let wallet = crypto::KeyPair::gen();
510 let mut owner_tx = TxVariant::V0(TxVariantV0::OwnerTx(OwnerTx {
511 base: Tx {
512 timestamp: 1230,
513 fee: get_asset("123.00000 MARK"),
514 signature_pairs: vec![],
515 },
516 minter: minter.0.clone(),
517 wallet: wallet.0.clone().into(),
518 script: wallet.0.clone().into(),
519 }));
520
521 owner_tx.append_sign(&minter);
522 owner_tx.append_sign(&wallet);
523
524 let mut v = vec![];
525 owner_tx.serialize(&mut v);
526
527 let mut c = Cursor::<&[u8]>::new(&v);
528 let dec = TxVariant::deserialize(&mut c).unwrap();
529 assert_eq!(owner_tx, dec);
530 assert_eq!(dec.sigs().len(), 2);
531 assert_eq!(owner_tx.sigs()[0], dec.sigs()[0]);
532 assert_eq!(owner_tx.sigs()[1], dec.sigs()[1]);
533 }
534
535 #[test]
536 fn serialize_owner() {
537 let minter = crypto::KeyPair::gen();
538 let wallet = crypto::KeyPair::gen();
539 let owner_tx = OwnerTx {
540 base: Tx {
541 timestamp: 1230,
542 fee: get_asset("123.00000 MARK"),
543 signature_pairs: vec![],
544 },
545 minter: minter.0,
546 wallet: wallet.0.clone().into(),
547 script: wallet.0.into(),
548 };
549
550 let mut v = vec![];
551 owner_tx.serialize(&mut v);
552
553 let mut c = Cursor::<&[u8]>::new(&v);
554 let (base, tx_type) = Tx::deserialize_header(&mut c).unwrap();
555 let dec = OwnerTx::deserialize(&mut c, base).unwrap();
556 assert_eq!(owner_tx, dec);
557
558 cmp_base_tx!(dec, 1230, "123.00000 MARK");
559 assert_eq!(tx_type, TxType::OWNER);
560 assert_eq!(owner_tx.minter, dec.minter);
561 assert_eq!(owner_tx.wallet, dec.wallet);
562 }
563
564 #[test]
565 fn serialize_mint() {
566 let wallet = crypto::KeyPair::gen();
567 let mint_tx = MintTx {
568 base: Tx {
569 timestamp: 1234,
570 fee: get_asset("123.00000 MARK"),
571 signature_pairs: vec![],
572 },
573 to: wallet.0.clone().into(),
574 amount: get_asset("10.00000 MARK"),
575 attachment: vec![1, 2, 3],
576 attachment_name: "abc.pdf".to_owned(),
577 script: wallet.0.into(),
578 };
579
580 let mut v = vec![];
581 mint_tx.serialize(&mut v);
582
583 let mut c = Cursor::<&[u8]>::new(&v);
584 let (base, tx_type) = Tx::deserialize_header(&mut c).unwrap();
585 let dec = MintTx::deserialize(&mut c, base).unwrap();
586
587 cmp_base_tx!(dec, 1234, "123.00000 MARK");
588 assert_eq!(tx_type, TxType::MINT);
589 assert_eq!(mint_tx.to, dec.to);
590 assert_eq!(mint_tx.amount, dec.amount);
591 assert_eq!(mint_tx, dec);
592 }
593
594 #[test]
595 fn serialize_reward() {
596 let to = crypto::KeyPair::gen();
597 let reward_tx = RewardTx {
598 base: Tx {
599 timestamp: 123,
600 fee: get_asset("123.00000 MARK"),
601 signature_pairs: vec![],
602 },
603 to: to.0.into(),
604 rewards: get_asset("1.50000 MARK"),
605 };
606
607 let mut v = vec![];
608 reward_tx.serialize(&mut v);
609
610 let mut c = Cursor::<&[u8]>::new(&v);
611 let (base, tx_type) = Tx::deserialize_header(&mut c).unwrap();
612 let dec = RewardTx::deserialize(&mut c, base).unwrap();
613
614 cmp_base_tx!(dec, 123, "123.00000 MARK");
615 assert_eq!(tx_type, TxType::REWARD);
616 assert_eq!(reward_tx.to, dec.to);
617 assert_eq!(reward_tx.rewards, dec.rewards);
618 }
619
620 #[test]
621 fn serialize_transfer() {
622 let from = crypto::KeyPair::gen();
623 let to = crypto::KeyPair::gen();
624 let transfer_tx = TransferTx {
625 base: Tx {
626 timestamp: 1234567890,
627 fee: get_asset("1.23000 MARK"),
628 signature_pairs: vec![],
629 },
630 from: from.0.into(),
631 to: to.0.into(),
632 script: vec![1, 2, 3, 4].into(),
633 amount: get_asset("1.00456 MARK"),
634 memo: Vec::from(String::from("Hello world!").as_bytes()),
635 };
636
637 let mut v = vec![];
638 transfer_tx.serialize(&mut v);
639
640 let mut c = Cursor::<&[u8]>::new(&v);
641 let (base, tx_type) = Tx::deserialize_header(&mut c).unwrap();
642 let dec = TransferTx::deserialize(&mut c, base).unwrap();
643
644 cmp_base_tx!(dec, 1234567890, "1.23000 MARK");
645 assert_eq!(tx_type, TxType::TRANSFER);
646 assert_eq!(transfer_tx.from, dec.from);
647 assert_eq!(transfer_tx.to, dec.to);
648 assert_eq!(transfer_tx.script, vec![1, 2, 3, 4].into());
649 assert_eq!(transfer_tx.amount.to_string(), dec.amount.to_string());
650 assert_eq!(transfer_tx.memo, dec.memo);
651 }
652
653 #[test]
654 fn tx_eq() {
655 let tx_a = Tx {
656 timestamp: 1000,
657 fee: get_asset("10.00000 MARK"),
658 signature_pairs: vec![KeyPair::gen().sign(b"hello world")],
659 };
660 let tx_b = tx_a.clone();
661 assert_eq!(tx_a, tx_b);
662
663 let mut tx_b = tx_a.clone();
664 tx_b.timestamp = tx_b.timestamp + 1;
665 assert_ne!(tx_a, tx_b);
666
667 let mut tx_b = tx_a.clone();
668 tx_b.fee = get_asset("10.00000 MARK");
669 assert_eq!(tx_a, tx_b);
670
671 let mut tx_b = tx_a.clone();
672 tx_b.fee = get_asset("100.00000 MARK");
673 assert_ne!(tx_a, tx_b);
674
675 let mut tx_b = tx_a.clone();
676 tx_b.fee = get_asset("1.00000 MARK");
677 assert_ne!(tx_a, tx_b);
678
679 let mut tx_b = tx_a.clone();
680 tx_b.signature_pairs
681 .push(KeyPair::gen().sign(b"hello world"));
682 assert_ne!(tx_a, tx_b);
683 }
684
685 #[test]
686 fn transfer_tx_eq() {
687 let tx_a = TransferTx {
688 base: Tx {
689 timestamp: 1000,
690 fee: get_asset("10.00000 MARK"),
691 signature_pairs: vec![KeyPair::gen().sign(b"hello world")],
692 },
693 from: KeyPair::gen().0.into(),
694 to: KeyPair::gen().0.into(),
695 script: Builder::new().push(OpFrame::True).build(),
696 amount: get_asset("1.00000 MARK"),
697 memo: vec![1, 2, 3],
698 };
699
700 let tx_b = tx_a.clone();
701 assert_eq!(tx_a, tx_b);
702
703 let mut tx_b = tx_a.clone();
704 tx_b.base.fee = get_asset("10.00000 MARK");
705 assert_eq!(tx_a, tx_b);
706
707 let mut tx_b = tx_a.clone();
708 tx_b.base.fee = get_asset("1.00000 MARK");
709 assert_ne!(tx_a, tx_b);
710
711 let mut tx_b = tx_a.clone();
712 tx_b.from = KeyPair::gen().0.into();
713 assert_ne!(tx_a, tx_b);
714
715 let mut tx_b = tx_a.clone();
716 tx_b.to = KeyPair::gen().0.into();
717 assert_ne!(tx_a, tx_b);
718
719 let mut tx_b = tx_a.clone();
720 tx_b.script = Builder::new().push(OpFrame::False).build();
721 assert_ne!(tx_a, tx_b);
722
723 let mut tx_b = tx_a.clone();
724 tx_b.amount = get_asset("10.00000 MARK");
725 assert_ne!(tx_a, tx_b);
726
727 let mut tx_b = tx_a.clone();
728 tx_b.memo = vec![1, 2, 3, 4];
729 assert_ne!(tx_a, tx_b);
730 }
731
732 #[test]
733 fn precomp_data_sig_split() {
734 let tx = TxVariant::V0(TxVariantV0::TransferTx(TransferTx {
735 base: Tx {
736 timestamp: 1000,
737 fee: get_asset("10.00000 MARK"),
738 signature_pairs: vec![KeyPair::gen().sign(b"hello world")],
739 },
740 from: KeyPair::gen().0.into(),
741 to: KeyPair::gen().0.into(),
742 script: Builder::new().push(OpFrame::True).build(),
743 amount: get_asset("1.00000 MARK"),
744 memo: vec![1, 2, 3],
745 }));
746
747 let mut buf = Vec::with_capacity(4096);
748 tx.serialize_without_sigs(&mut buf);
749 assert_eq!(tx.precompute().bytes_without_sigs(), buf.as_slice());
750 }
751
752 fn get_asset(s: &str) -> Asset {
753 s.parse().unwrap()
754 }
755}