regiusmark/tx/
mod.rs

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                // Tx version (2 bytes)
203                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        // The TxType is part of the header and needs to be pushed into the buffer first
297        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,  // Key that signs blocks
325    pub wallet: ScriptHash, // Hot wallet that receives rewards
326    pub script: Script,     // Hot wallet previous script
327}
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}