saito_core/core/consensus/
transaction.rs

1use ahash::AHashSet;
2use std::fmt::{Display, Formatter};
3use std::io::{Error, ErrorKind};
4
5use crate::core::consensus::blockchain::Blockchain;
6use log::{debug, error, trace, warn};
7use num_derive::FromPrimitive;
8use num_traits::FromPrimitive;
9use primitive_types::U256;
10use rayon::prelude::*;
11use serde::{Deserialize, Serialize};
12
13use crate::core::consensus::hop::{Hop, HOP_SIZE};
14use crate::core::consensus::slip::{Slip, SlipType, SLIP_SIZE};
15use crate::core::consensus::wallet::Wallet;
16use crate::core::defs::{
17    Currency, PrintForLog, SaitoHash, SaitoPrivateKey, SaitoPublicKey, SaitoSignature,
18    SaitoUTXOSetKey, Timestamp, UtxoSet, UTXO_KEY_LENGTH,
19};
20use crate::core::io::network::Network;
21use crate::core::util::crypto::{hash, sign, verify, verify_signature};
22use crate::iterate;
23
24pub const TRANSACTION_SIZE: usize = 93;
25
26#[derive(Serialize, Deserialize, Debug, Copy, PartialEq, Clone, FromPrimitive)]
27pub enum TransactionType {
28    Normal = 0,
29    /// Paying for the network
30    Fee = 1,
31    GoldenTicket = 2,
32    ATR = 3,
33    /// VIP transactions deprecated on mainnet
34    Vip = 4,
35    SPV = 5,
36    /// Issues funds for an address at the start of the network
37    Issuance = 6,
38    BlockStake = 7,
39    Bound = 8,
40}
41
42#[serde_with::serde_as]
43#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
44pub struct Transaction {
45    // the bulk of the consensus transaction data
46    pub timestamp: Timestamp,
47    pub from: Vec<Slip>,
48    pub to: Vec<Slip>,
49    // #[serde(with = "serde_bytes")] TODO : check this for performance
50    pub data: Vec<u8>,
51    pub transaction_type: TransactionType,
52    pub txs_replacements: u32,
53    #[serde_as(as = "[_; 64]")]
54    pub signature: SaitoSignature,
55    pub path: Vec<Hop>,
56
57    // hash used for merkle_root (does not include signature)
58    pub hash_for_signature: Option<SaitoHash>,
59
60    /// total nolan in input slips
61    pub total_in: Currency,
62    /// total nolan in output slips
63    pub total_out: Currency,
64    /// total fees
65    pub total_fees: Currency,
66    /// total work to creator
67    pub total_work_for_me: Currency,
68    /// cumulative fees for this tx-in-block
69    pub cumulative_fees: Currency,
70}
71
72impl Display for Transaction {
73    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
74        writeln!(f, "++++++++++++++++++++++++++++++++++++++++++++++++++")?;
75        writeln!(
76            f,
77            "Tx : {{ type : {:?}, data_size : {:?}, timestamp : {:?}, signature : {:?}, hash : {:?}, total_in : {:?}, total_out : {:?}, total_fees : {:?}, total_work_for_me : {:?}, cumulative_fees : {:?}, from slips : count : {:?} }}",
78            self.transaction_type,
79            self.data.len(),
80            self.timestamp,
81            self.signature.to_hex(),
82            self.hash_for_signature.unwrap_or_default().to_hex(),
83            self.total_in,
84            self.total_out,
85            self.total_fees,
86            self.total_work_for_me,
87            self.cumulative_fees,
88            self.from.len()
89        )?;
90        if !self.from.is_empty() {
91            writeln!(f, "---------------------------------------------")?;
92        }
93        writeln!(f, " from slips : count : {:?}", self.from.len())?;
94        for slip in self.from.iter() {
95            writeln!(f, "{}", slip)?;
96        }
97        if !self.to.is_empty() {
98            writeln!(f, "---------------------------------------------")?;
99        }
100        writeln!(f, " to slips : count : {:?}", self.to.len())?;
101        for slip in self.to.iter() {
102            writeln!(f, "{}", slip)?;
103        }
104        if !self.path.is_empty() {
105            writeln!(f, "---------------------------------------------")?;
106        }
107        writeln!(f, " path :  length : {:?}", self.path.len())?;
108        for hop in self.path.iter() {
109            writeln!(f, "{}", hop)?;
110        }
111        writeln!(f, "}}")?;
112        writeln!(f, "++++++++++++++++++++++++++++++++++++++++++++++++++")
113    }
114}
115
116impl Default for Transaction {
117    fn default() -> Self {
118        Self {
119            timestamp: 0,
120            from: vec![],
121            to: vec![],
122            data: vec![],
123            transaction_type: TransactionType::Normal,
124            txs_replacements: 1,
125            signature: [0; 64],
126            hash_for_signature: None,
127            path: vec![],
128            total_in: 0,
129            total_out: 0,
130            total_fees: 0,
131            total_work_for_me: 0,
132            cumulative_fees: 0,
133        }
134    }
135}
136
137impl Transaction {
138    pub fn add_hop(
139        &mut self,
140        my_private_key: &SaitoPrivateKey,
141        my_public_key: &SaitoPublicKey,
142        to_public_key: &SaitoPublicKey,
143    ) {
144        assert_ne!(my_public_key, to_public_key, "cannot add hop to self");
145        let hop = Hop::generate(my_private_key, my_public_key, to_public_key, self);
146        self.path.push(hop);
147    }
148
149    /// add input slip
150    ///
151    /// # Arguments
152    ///
153    /// * `input_slip`:
154    ///
155    /// returns: ()
156    ///
157    /// # Examples
158    ///
159    /// ```
160    ///
161    /// ```
162    pub fn add_from_slip(&mut self, input_slip: Slip) {
163        if self.from.len() < u8::MAX as usize {
164            self.from.push(input_slip);
165        } else {
166            warn!("cannot add more input slips to the transaction");
167        }
168    }
169
170    /// add output slip
171    ///
172    /// # Arguments
173    ///
174    /// * `output_slip`:
175    ///
176    /// returns: ()
177    ///
178    /// # Examples
179    ///
180    /// ```
181    ///
182    /// ```
183    pub fn add_to_slip(&mut self, output_slip: Slip) {
184        if self.to.len() < u8::MAX as usize {
185            self.to.push(output_slip);
186        } else {
187            warn!("cannot add more output slips to the transaction");
188        }
189    }
190
191    /// this function exists largely for testing. It attempts to attach the requested fee
192    /// to the transaction if possible. If not possible it reverts back to a transaction
193    /// with 1 zero-fee input and 1 zero-fee output.
194    ///
195    /// # Arguments
196    ///
197    /// * `wallet_lock`:
198    /// * `to_publickey`:
199    /// * `with_payment`:
200    /// * `with_fee`:
201    ///
202    /// returns: Transaction
203    ///
204    /// # Examples
205    ///
206    /// ```
207    ///
208    /// ```
209    pub fn create(
210        wallet: &mut Wallet,
211        to_public_key: SaitoPublicKey,
212        with_payment: Currency,
213        with_fee: Currency,
214        _force_merge: bool,
215        network: Option<&Network>,
216        latest_block_id: u64,
217        genesis_period: u64,
218    ) -> Result<Transaction, Error> {
219        Self::create_with_multiple_payments(
220            wallet,
221            vec![to_public_key],
222            vec![with_payment],
223            with_fee,
224            network,
225            latest_block_id,
226            genesis_period,
227        )
228    }
229
230    pub fn create_with_multiple_payments(
231        wallet: &mut Wallet,
232        mut keys: Vec<SaitoPublicKey>,
233        mut payments: Vec<Currency>,
234        mut with_fee: Currency,
235        network: Option<&Network>,
236        latest_block_id: u64,
237        genesis_period: u64,
238    ) -> Result<Transaction, Error> {
239        let total_payment: Currency = payments.iter().sum();
240        trace!(
241            "generating transaction : payments = {:?}, fee = {:?}",
242            total_payment,
243            with_fee
244        );
245
246        if payments.len() != keys.len() {
247            error!("keys and payments provided to the transaction is not similar in count. payments : {:?} keys : {:?}",payments.len(),keys.len());
248            return Err(Error::from(ErrorKind::InvalidInput));
249        }
250
251        let available_balance = wallet.get_available_balance();
252
253        if with_fee > available_balance {
254            with_fee = 0;
255        }
256
257        let total_requested = total_payment + with_fee;
258        trace!(
259            "in generate transaction. available: {} and payment: {} and fee: {}",
260            available_balance,
261            total_payment,
262            with_fee
263        );
264        if available_balance < total_requested {
265            debug!(
266                "not enough funds to create transaction. required : {:?} available : {:?}",
267                total_requested, available_balance
268            );
269            return Err(Error::from(ErrorKind::NotFound));
270        }
271
272        let mut transaction = Transaction::default();
273        if total_requested == 0 {
274            let slip = Slip {
275                public_key: wallet.public_key,
276                amount: 0,
277                ..Default::default()
278            };
279            transaction.add_from_slip(slip);
280        } else {
281            let (input_slips, output_slips) =
282                wallet.generate_slips(total_requested, network, latest_block_id, genesis_period);
283
284            for input in input_slips {
285                transaction.add_from_slip(input);
286            }
287            for output in output_slips {
288                transaction.add_to_slip(output);
289            }
290        }
291        for _i in 0..keys.len() {
292            let key = keys.pop().unwrap();
293            let payment = payments.pop().unwrap();
294
295            let output = Slip {
296                public_key: key,
297                amount: payment,
298                ..Default::default()
299            };
300            transaction.add_to_slip(output);
301        }
302
303        Ok(transaction)
304    }
305
306    ///
307    ///
308    /// # Arguments
309    ///
310    /// * `to_publickey`:
311    /// * `with_amount`:
312    ///
313    /// returns: Transaction
314    ///
315    /// # Examples
316    ///
317    /// ```
318    ///
319    /// ```
320    pub fn create_issuance_transaction(
321        to_public_key: SaitoPublicKey,
322        with_amount: Currency,
323    ) -> Transaction {
324        trace!("generate issuance transaction : amount = {:?}", with_amount);
325        let mut transaction = Transaction::default();
326        transaction.transaction_type = TransactionType::Issuance;
327        let mut output = Slip::default();
328        output.public_key = to_public_key;
329        output.amount = with_amount;
330        output.slip_type = SlipType::Normal;
331        transaction.add_to_slip(output);
332        transaction
333    }
334
335    /// create rebroadcast transaction
336    ///
337    /// # Arguments
338    ///
339    /// * `transaction_to_rebroadcast`:
340    /// * `output_slip_to_rebroadcast`:
341    /// * `with_fee`:
342    /// * `with_staking_subsidy`:
343    ///
344    /// returns: Transaction
345    ///
346    /// # Examples
347    ///
348    /// ```
349    ///
350    /// ```
351    pub fn create_rebroadcast_transaction(
352        transaction_to_rebroadcast: &Transaction,
353        to_slip: Slip,
354        from_slip: Slip,
355    ) -> Transaction {
356        debug!(
357            "creating rebroadcast transaction \nfrom : {} \nto : {} \ntx_to_rebroadcast: {}",
358            from_slip, to_slip, transaction_to_rebroadcast
359        );
360        let mut transaction = Transaction::default();
361
362        transaction.transaction_type = TransactionType::ATR;
363
364        // if this is the FIRST time we are rebroadcasting, we copy the
365        // original transaction into the message field in serialized
366        // form. this preserves the original message and its signature
367        // in perpetuity.
368        //
369        // if this is the SECOND or subsequent rebroadcast, we do not
370        // copy the ATR tx (no need for a meta-tx) and rather just update
371        // the message field with the original transaction (which is
372        // by definition already in the previous TX message space.
373        if transaction_to_rebroadcast.transaction_type == TransactionType::ATR {
374            transaction.data = transaction_to_rebroadcast.data.to_vec();
375        } else {
376            transaction.data = transaction_to_rebroadcast.serialize_for_net().to_vec();
377        }
378
379        transaction.add_from_slip(from_slip);
380
381        // add the output slip
382        assert_eq!(to_slip.slip_type, SlipType::ATR);
383        transaction.add_to_slip(to_slip);
384
385        transaction.generate_total_fees(0, 0);
386
387        // signature is the ORIGINAL signature. this transaction
388        // will fail its signature check and then get analysed as
389        // a rebroadcast transaction because of its transaction type.
390        transaction.signature = transaction_to_rebroadcast.signature;
391
392        debug!("generated rebroadcast transaction: {}", transaction);
393
394        transaction
395    }
396
397    //
398    // Builds an ATR rebroadcast transaction for a 3-slip NFT group:
399    // [Bound, Normal, Bound]
400    //
401    pub fn create_rebroadcast_bound_transaction(
402        transaction_to_rebroadcast: &Transaction,
403        slip1: Slip, // first Bound slip
404        slip2: Slip, // Normal slip (amount already includes payout)
405        slip3: Slip, // second Bound slip
406    ) -> Transaction {
407        let mut tx = Transaction::default();
408        tx.transaction_type = TransactionType::ATR;
409
410        // if this is the FIRST time we are rebroadcasting, we copy the
411        // original transaction into the message field in serialized
412        // form. this preserves the original message and its signature
413        // in perpetuity.
414        //
415        // if this is the SECOND or subsequent rebroadcast, we do not
416        // copy the ATR tx (no need for a meta-tx) and rather just update
417        // the message field with the original transaction (which is
418        // by definition already in the previous TX message space.
419        tx.data = if transaction_to_rebroadcast.transaction_type == TransactionType::ATR {
420            transaction_to_rebroadcast.data.clone()
421        } else {
422            transaction_to_rebroadcast.serialize_for_net()
423        };
424
425        // attach the three “input” slips
426        tx.add_from_slip(slip1.clone());
427        tx.add_from_slip(slip2.clone());
428        tx.add_from_slip(slip3.clone());
429
430        //
431        // attach the three output slips
432        // same Bound slips, but payload has slip_type=ATR
433        //
434        tx.add_to_slip(slip1);
435        {
436            let mut output2 = slip2.clone();
437            output2.slip_type = SlipType::ATR;
438            tx.add_to_slip(output2);
439        }
440        tx.add_to_slip(slip3);
441
442        tx.generate_total_fees(0, 0);
443
444        //
445        // signature is the ORIGINAL signature. this transaction
446        // will fail its signature check and then get analysed as
447        // a rebroadcast transaction because of its transaction type.
448        //
449        tx.signature = transaction_to_rebroadcast.signature;
450
451        tx
452    }
453
454    //
455    // removes utxoset entries when block is deleted
456    //
457    pub async fn delete(&self, utxoset: &mut UtxoSet) -> bool {
458        self.from.iter().for_each(|input| {
459            input.delete(utxoset);
460        });
461        self.to.iter().for_each(|output| {
462            output.delete(utxoset);
463        });
464
465        true
466    }
467
468    /// Deserialize from bytes to a Transaction.
469    /// [len of inputs - 4 bytes - u32]
470    /// [len of outputs - 4 bytes - u32]
471    /// [len of message - 4 bytes - u32]
472    /// [len of path - 4 bytes - u32]
473    /// [signature - 64 bytes - Secp25k1 sig]
474    /// [timestamp - 8 bytes - u64]
475    /// [transaction type - 1 byte]
476    /// [input][input][input]...
477    /// [output][output][output]...
478    /// [message]
479    /// [hop][hop][hop]...
480    pub fn deserialize_from_net(bytes: &[u8]) -> Result<Transaction, Error> {
481        // trace!(
482        //     "deserializing tx from buffer with length : {:?}",
483        //     bytes.len()
484        // );
485        if bytes.len() < TRANSACTION_SIZE {
486            return Err(Error::from(ErrorKind::InvalidData));
487        }
488        let inputs_len: u32 = u32::from_be_bytes(
489            bytes[0..4]
490                .try_into()
491                .or(Err(Error::from(ErrorKind::InvalidData)))?,
492        );
493        if inputs_len > u8::MAX as u32 {
494            return Err(Error::from(ErrorKind::InvalidData));
495        }
496        let outputs_len: u32 = u32::from_be_bytes(
497            bytes[4..8]
498                .try_into()
499                .or(Err(Error::from(ErrorKind::InvalidData)))?,
500        );
501        if outputs_len > u8::MAX as u32 {
502            return Err(Error::from(ErrorKind::InvalidData));
503        }
504        let message_len: usize = u32::from_be_bytes(
505            bytes[8..12]
506                .try_into()
507                .or(Err(Error::from(ErrorKind::InvalidData)))?,
508        ) as usize;
509        let path_len: usize = u32::from_be_bytes(
510            bytes[12..16]
511                .try_into()
512                .or(Err(Error::from(ErrorKind::InvalidData)))?,
513        ) as usize;
514        let signature: SaitoSignature = bytes[16..80]
515            .try_into()
516            .or(Err(Error::from(ErrorKind::InvalidData)))?;
517        let timestamp: Timestamp = Timestamp::from_be_bytes(
518            bytes[80..88]
519                .try_into()
520                .or(Err(Error::from(ErrorKind::InvalidData)))?,
521        );
522        let replaces_txs = u32::from_be_bytes(
523            bytes[88..92]
524                .try_into()
525                .or(Err(Error::from(ErrorKind::InvalidData)))?,
526        );
527        let transaction_type: TransactionType =
528            FromPrimitive::from_u8(bytes[92]).ok_or(Error::from(ErrorKind::InvalidData))?;
529        let start_of_inputs = TRANSACTION_SIZE;
530        let start_of_outputs = start_of_inputs + inputs_len as usize * SLIP_SIZE;
531        let start_of_message = start_of_outputs + outputs_len as usize * SLIP_SIZE;
532        let start_of_path = start_of_message + message_len;
533        let mut inputs: Vec<Slip> = vec![];
534        for n in 0..inputs_len {
535            let start_of_data: usize = start_of_inputs + n as usize * SLIP_SIZE;
536            let end_of_data: usize = start_of_data + SLIP_SIZE;
537            let input = Slip::deserialize_from_net(&bytes[start_of_data..end_of_data].to_vec())?;
538            inputs.push(input);
539        }
540        let mut outputs: Vec<Slip> = vec![];
541        for n in 0..outputs_len {
542            let start_of_data: usize = start_of_outputs + n as usize * SLIP_SIZE;
543            let end_of_data: usize = start_of_data + SLIP_SIZE;
544            let output = Slip::deserialize_from_net(&bytes[start_of_data..end_of_data].to_vec())?;
545            outputs.push(output);
546        }
547        let message = bytes[start_of_message..start_of_message + message_len]
548            .try_into()
549            .or(Err(Error::from(ErrorKind::InvalidData)))?;
550        let mut path: Vec<Hop> = vec![];
551        for n in 0..path_len {
552            let start_of_data: usize = start_of_path + n * HOP_SIZE;
553            let end_of_data: usize = start_of_data + HOP_SIZE;
554            let hop = Hop::deserialize_from_net(&bytes[start_of_data..end_of_data].to_vec())?;
555            path.push(hop);
556        }
557
558        let mut transaction = Transaction::default();
559        transaction.timestamp = timestamp;
560        transaction.from = inputs;
561        transaction.to = outputs;
562        transaction.data = message;
563        transaction.txs_replacements = replaces_txs;
564        transaction.transaction_type = transaction_type;
565        transaction.signature = signature;
566        transaction.path = path;
567        Ok(transaction)
568    }
569
570    pub fn is_fee_transaction(&self) -> bool {
571        self.transaction_type == TransactionType::Fee
572    }
573    pub fn is_staking_transaction(&self) -> bool {
574        self.transaction_type == TransactionType::BlockStake
575    }
576
577    pub fn is_atr_transaction(&self) -> bool {
578        self.transaction_type == TransactionType::ATR
579    }
580
581    pub fn is_normal_transaction(&self) -> bool {
582        self.transaction_type == TransactionType::Normal
583    }
584
585    pub fn is_golden_ticket(&self) -> bool {
586        self.transaction_type == TransactionType::GoldenTicket
587    }
588
589    pub fn is_issuance_transaction(&self) -> bool {
590        self.transaction_type == TransactionType::Issuance
591    }
592
593    // generates
594    //
595    // when the block is created, block.generate() is called to fill in all the
596    // dynamic data related to the block creator. that function in turn calls tx.generate()
597    // to ensure that transaction data is generated properly. this includes:
598    //
599    // tx.hash -> needed to generate merkle root
600    // tx.fees -> needed to calculate payouts
601    // tx.work -> needed to confirm adequate routing work
602    //
603    pub fn generate(&mut self, public_key: &SaitoPublicKey, tx_index: u64, block_id: u64) -> bool {
604        // ensure hash exists for signing
605        self.generate_hash_for_signature();
606
607        // nolan_in, nolan_out, total fees
608        self.generate_total_fees(tx_index, block_id);
609
610        // routing work for asserted public_key (creator)
611        self.generate_total_work(public_key);
612
613        true
614    }
615
616    // calculate cumulative fee share in block
617    pub fn generate_cumulative_fees(&mut self, cumulative_fees: Currency) -> Currency {
618        self.cumulative_fees = cumulative_fees + self.total_fees;
619        self.cumulative_fees
620    }
621
622    // calculate total fees in block
623    pub fn generate_total_fees(&mut self, tx_index: u64, block_id: u64) {
624        // calculate nolan in / out, fees
625        // generate utxoset key for every slip
626        let nolan_in = self
627            .from
628            .iter_mut()
629            .map(|slip| {
630                slip.generate_utxoset_key();
631                if let SlipType::Bound = slip.slip_type {
632                    // we are not counting the value in Bound slips
633                    return 0;
634                }
635                slip.amount
636            })
637            .sum::<Currency>();
638
639        let nolan_out = self
640            .to
641            .iter_mut()
642            .enumerate()
643            .map(|(index, slip)| {
644                if slip.slip_type != SlipType::ATR || slip.slip_type != SlipType::Bound {
645                    slip.block_id = block_id;
646                    slip.tx_ordinal = tx_index;
647                    slip.slip_index = index as u8;
648                }
649                slip.generate_utxoset_key();
650                if let SlipType::Bound = slip.slip_type {
651                    // we are not counting the value in Bound slips
652                    return 0;
653                }
654                slip.amount
655            })
656            .sum::<Currency>();
657
658        self.total_in = nolan_in;
659        self.total_out = nolan_out;
660        self.total_fees = 0;
661
662        //
663        // note that this is not validation code, permitting this. we may have
664        // some transactions that do insert NOLAN, such as during testing of
665        // monetary policy. All sanity checks need to be in the validate()
666        // function.
667        //
668        if nolan_in > nolan_out {
669            self.total_fees = nolan_in - nolan_out;
670        }
671    }
672    /// calculate cumulative routing work in block
673    pub fn generate_total_work(&mut self, public_key: &SaitoPublicKey) {
674        //
675        // if there is no routing path, then the transaction contains
676        // no usable work for producing a block, and any payout associated
677        // with the transaction will simply be issued to the creator of
678        // the transaction itself.
679        //
680        if self.path.is_empty() {
681            self.total_work_for_me = 0;
682            return;
683        }
684
685        // something is wrong if we are not the last routing node
686        let last_hop = &self.path[self.path.len() - 1];
687        if last_hop.to.ne(public_key) {
688            self.total_work_for_me = 0;
689            warn!(
690                "tx : {:?} last hop : {} is not current node : {}",
691                self.signature.to_hex(),
692                last_hop.to.to_base58(),
693                public_key.to_base58()
694            );
695            return;
696        }
697
698        let total_fees = self.total_fees;
699        let mut routing_work_available_to_public_key = total_fees;
700
701        //
702        // first hop gets ALL the routing work, so we start
703        // halving from the 2nd hop in the routing path
704        //
705        for i in 1..self.path.len() {
706            // TODO : check if this check required here since txs already validated at this point
707            if self.path[i].from != self.path[i - 1].to {
708                self.total_work_for_me = 0;
709                warn!(
710                    "tx : {:?} from and to not matching. to : {:?} from : {:?}",
711                    self.signature.to_hex(),
712                    self.path[i - 1].to.to_hex(),
713                    self.path[i].from.to_hex()
714                );
715                return;
716            }
717
718            // otherwise halve the work
719            let half_of_routing_work: Currency = routing_work_available_to_public_key / 2;
720            routing_work_available_to_public_key -= half_of_routing_work;
721        }
722        self.total_work_for_me = routing_work_available_to_public_key;
723    }
724
725    //
726    // generate hash used for signing the tx
727    //
728    pub fn generate_hash_for_signature(&mut self) {
729        if let TransactionType::SPV = self.transaction_type {
730            self.hash_for_signature = Some(self.signature[0..32].to_vec().try_into().unwrap());
731        } else {
732            self.hash_for_signature = Some(hash(&self.serialize_for_signature()));
733        }
734    }
735
736    pub fn get_winning_routing_node(&self, random_hash: SaitoHash) -> SaitoPublicKey {
737        //
738        // if there are no routing paths, we return the sender of
739        // the payment, as they're got all the routing work by
740        // definition. this is the edge-case where sending a tx
741        // can make you money.
742        //
743        if self.path.is_empty() {
744            return if !self.from.is_empty() {
745                self.from[0].public_key
746            } else {
747                //
748                // if there is no routing path and there are no FROM UTXO then this is a
749                // fee transaction or issuance transaction. in these cases we choose to
750                // graveyard the payout as this should encourage winners to move their
751                // payouts, which reinforces the longest chain.
752                [0; 33]
753            };
754        }
755
756        // no winning transaction should have no fees unless the
757        // entire block has no fees, in which case we have a block
758        // without any fee-paying transactions.
759        //
760        // burn these fees for the sake of safety.
761        //
762        if self.total_fees == 0 {
763            return [0; 33];
764        }
765
766        //
767        // if we have a routing path, we calculate the total amount
768        // of routing work that it is possible for this transaction
769        // to contain (2x the fee).
770        //
771        // aggregate routing work is only calculated in this function
772        // as it is only needed when determining payouts. it should
773        // not be confused with total_work which represents the amount
774        // of work available in the transaction itself.
775        //
776        let mut aggregate_routing_work: Currency = self.total_fees;
777        let mut routing_work_this_hop: Currency = aggregate_routing_work;
778        let mut work_by_hop: Vec<Currency> = vec![];
779        work_by_hop.push(aggregate_routing_work);
780
781        for _i in 1..self.path.len() {
782            let new_routing_work_this_hop: Currency = routing_work_this_hop / 2;
783            aggregate_routing_work += new_routing_work_this_hop;
784            routing_work_this_hop = new_routing_work_this_hop;
785            work_by_hop.push(aggregate_routing_work);
786        }
787
788        //
789        // find winning routing node
790        //
791        let x = U256::from_big_endian(&random_hash);
792        let z = U256::from_big_endian(&aggregate_routing_work.to_be_bytes());
793        let zy = x.div_mod(z).1;
794        let winning_routing_work_in_nolan: Currency = zy.low_u64();
795
796        for i in 0..work_by_hop.len() {
797            if winning_routing_work_in_nolan <= work_by_hop[i] {
798                return self.path[i].to;
799            }
800        }
801
802        unreachable!("winning routing node should've been found before this");
803    }
804
805    /// Runs when the chain is re-organized
806    pub fn on_chain_reorganization(&self, utxoset: &mut UtxoSet, longest_chain: bool) {
807        trace!(
808            "tx reorg : {:?} with {} inputs and {} outputs",
809            self.signature.to_hex(),
810            self.from.len(),
811            self.to.len()
812        );
813        let mut input_slip_spendable = true;
814        let mut output_slip_spendable = false;
815
816        if longest_chain {
817            input_slip_spendable = false;
818            output_slip_spendable = true;
819        }
820
821        self.from
822            .iter()
823            .for_each(|input| input.on_chain_reorganization(utxoset, input_slip_spendable));
824        self.to
825            .iter()
826            .for_each(|output| output.on_chain_reorganization(utxoset, output_slip_spendable));
827    }
828
829    /// [len of inputs - 4 bytes - u32]
830    /// [len of outputs - 4 bytes - u32]
831    /// [len of message - 4 bytes - u32]
832    /// [len of path - 4 bytes - u32]
833    /// [signature - 64 bytes - Secp25k1 sig]
834    /// [timestamp - 8 bytes - u64]
835    /// [transaction type - 1 byte]
836    /// [input][input][input]...
837    /// [output][output][output]...
838    /// [message]
839    /// [hop][hop][hop]...
840    pub fn serialize_for_net(&self) -> Vec<u8> {
841        self.serialize_for_net_with_hop(None)
842    }
843
844    pub(crate) fn serialize_for_net_with_hop(&self, opt_hop: Option<Hop>) -> Vec<u8> {
845        let mut path_len = self.path.len();
846        if opt_hop.is_some() {
847            path_len += 1;
848        }
849        if self.from.len() > u8::MAX as usize {
850            error!("ERROR: transaction has too many inputs");
851            return vec![];
852        }
853        if self.to.len() > u8::MAX as usize {
854            error!("ERROR: transaction has too many outputs");
855            return vec![];
856        }
857        let inputs = self
858            .from
859            .iter()
860            .map(|slip| slip.serialize_for_net())
861            .collect::<Vec<_>>()
862            .concat();
863        let outputs = self
864            .to
865            .iter()
866            .map(|slip| slip.serialize_for_net())
867            .collect::<Vec<_>>()
868            .concat();
869        let hops = self
870            .path
871            .iter()
872            .map(|hop| hop.serialize_for_net())
873            .collect::<Vec<_>>()
874            .concat();
875
876        let mut buffer: Vec<u8> = [
877            (self.from.len() as u32).to_be_bytes().as_slice(),
878            (self.to.len() as u32).to_be_bytes().as_slice(),
879            (self.data.len() as u32).to_be_bytes().as_slice(),
880            (path_len as u32).to_be_bytes().as_slice(),
881            self.signature.as_slice(),
882            self.timestamp.to_be_bytes().as_slice(),
883            self.txs_replacements.to_be_bytes().as_slice(),
884            (self.transaction_type as u8).to_be_bytes().as_slice(),
885            inputs.as_slice(),
886            outputs.as_slice(),
887            self.data.as_slice(),
888            hops.as_slice(),
889        ]
890        .concat();
891
892        if let Some(hop) = opt_hop {
893            buffer.extend(hop.serialize_for_net());
894        }
895        buffer
896    }
897
898    /// Returns the size of the serialized transaction buffer without serializing
899    pub fn get_serialized_size(&self) -> usize {
900        TRANSACTION_SIZE
901            + (SLIP_SIZE * self.from.len())
902            + (SLIP_SIZE * self.to.len())
903            + (HOP_SIZE * self.path.len())
904            + self.data.len()
905    }
906
907    pub fn serialize_for_signature(&self) -> Vec<u8> {
908        // fastest known way that isn't bincode ??
909
910        let inputs = self
911            .from
912            .iter()
913            .map(|slip| slip.serialize_input_for_signature())
914            .collect::<Vec<_>>()
915            .concat();
916
917        let outputs = self
918            .to
919            .iter()
920            .map(|slip| slip.serialize_output_for_signature())
921            .collect::<Vec<_>>()
922            .concat();
923
924        [
925            self.timestamp.to_be_bytes().as_slice(),
926            inputs.as_slice(),
927            outputs.as_slice(),
928            self.txs_replacements.to_be_bytes().as_slice(),
929            (self.transaction_type as u32).to_be_bytes().as_slice(),
930            self.data.as_slice(),
931        ]
932        .concat()
933    }
934
935    pub fn sign(&mut self, private_key: &SaitoPrivateKey) {
936        // we set slip ordinals when signing
937        for (i, output) in self.to.iter_mut().enumerate() {
938            output.slip_index = i as u8;
939        }
940
941        let buffer = self.serialize_for_signature();
942        let hash_for_signature = hash(&buffer);
943        self.hash_for_signature = Some(hash_for_signature);
944        self.signature = sign(&buffer, private_key);
945    }
946
947    pub fn validate(
948        &self,
949        utxoset: &UtxoSet,
950        blockchain: &Blockchain,
951        validate_against_utxo: bool,
952    ) -> bool {
953        //
954        // there are various types of transactions which have different validation
955        // requirements. the most significant difference is between transactions that
956        // are implicit or created by the block producer (ATR / Fee) and transactions
957        // that are created by users and must be cryptographically signed, etc...
958
959        //
960        // Fee Transactions are validated in block.validate() because they must match
961        // the fee transaction that block.generate_consensus_values() would create given
962        // the contents of the block. for this reason, and because there can only be
963        // a single fee transaction per block, we do not need to do further work to
964        // validate them here.
965        //
966
967        if self.from.len() > u8::MAX as usize {
968            error!("ERROR: transaction has too many inputs");
969            return false;
970        }
971        if self.to.len() > u8::MAX as usize {
972            error!("ERROR: transaction has too many outputs");
973            return false;
974        }
975
976        if self
977            .from
978            .iter()
979            .map(|slip| slip.utxoset_key)
980            .collect::<Vec<_>>()
981            .len()
982            != self.from.len()
983        {
984            error!("ERROR: transaction : {} has duplicate inputs", self);
985            return false;
986        }
987
988        // Fee Transactions are validated in the block class. There can only
989        // be one per block, and they are checked by ensuring the transaction hash
990        // matches our self-generated safety check. We do not need to validate
991        // their input slips as their input slips are records of what to do
992        // when reversing/unwinding the chain and have been spent previously.
993        if self.transaction_type == TransactionType::Fee {
994            return true;
995        }
996
997        //
998        // SPV transactions are "ghost" transactions which are included in SPV/lite-
999        // blocks. these transactions are not permitted to create outputs, and are
1000        // not processed by full-nodes, so cannot be included in valid full-blocks
1001        // or consensus.
1002        //
1003        if self.transaction_type == TransactionType::SPV {
1004            if self.total_fees > 0 {
1005                error!("ERROR: SPV transaction contains invalid hash");
1006                return false;
1007            }
1008
1009            return true;
1010        }
1011
1012        //
1013        // BlockStake transactions are a special class of transactions that are
1014        // affixed to blocks in order to propose them. This is used to add a form
1015        // of "social slashing" -- attackers who wish to spend their own money in
1016        // a "joyride" attack can be slashed as needed if the network must be
1017        // forked to deal with problems created by malicious participants at low
1018        // levels of fee-throughput.
1019        //
1020        if let TransactionType::BlockStake = self.transaction_type {
1021            let mut total_stakes = 0;
1022
1023            for slip in self.to.iter() {
1024                if !matches!(slip.slip_type, SlipType::BlockStake)
1025                    && !matches!(slip.slip_type, SlipType::Normal)
1026                {
1027                    error!("staking transaction outputs are not staking");
1028                    return false;
1029                }
1030
1031                if matches!(slip.slip_type, SlipType::BlockStake) {
1032                    total_stakes += slip.amount;
1033                }
1034            }
1035
1036            if total_stakes < blockchain.social_stake_requirement {
1037                error!(
1038                    "Not enough funds staked. expected: {:?}, staked: {:?}",
1039                    blockchain.social_stake_requirement, total_stakes
1040                );
1041                return false;
1042            }
1043
1044            let mut unique_keys: AHashSet<SaitoUTXOSetKey> = Default::default();
1045
1046            for slip in self.from.iter() {
1047                if slip.utxoset_key == [0; UTXO_KEY_LENGTH] {
1048                    return false;
1049                }
1050                if !blockchain.is_slip_unlocked(&slip.utxoset_key) {
1051                    return false;
1052                }
1053                let utxo_slip = Slip::parse_slip_from_utxokey(&slip.utxoset_key).unwrap();
1054                if utxo_slip.amount != slip.amount {
1055                    return false;
1056                }
1057
1058                unique_keys.insert(slip.utxoset_key);
1059            }
1060
1061            if unique_keys.len() != self.from.len() {
1062                // same utxo is used twice in the transaction
1063                return false;
1064            }
1065
1066            return true;
1067        }
1068
1069        //
1070        // User-Originated Transactions
1071        //
1072        // most transactions are identifiable by the public_key that
1073        // has signed their input transaction, but some transactions
1074        // do not have senders as they are auto-generated as part of
1075        // the block itself.
1076        //
1077        // ATR transactions
1078        // FEE transactions
1079        // ISSUANCE transactions
1080        //
1081        // the following validation rules cover user-originated txs
1082        // where we expect that the inputs are coming from valid
1083        // SAITO tokens that exist on the network.
1084        //
1085        // the first set of validation criteria is applied only to
1086        // validation criteria for the remaining classes of txs are
1087        // further down iin this function.
1088        //
1089        let transaction_type = self.transaction_type;
1090
1091        if self.transaction_type != TransactionType::ATR
1092            && self.transaction_type != TransactionType::Issuance
1093        {
1094            //
1095            // must have sender
1096            //
1097            if self.from.is_empty() {
1098                error!("ERROR 582039: less than 1 input in transaction");
1099                return false;
1100            }
1101
1102            //
1103            // must have valid signature
1104            //
1105            if let Some(hash_for_signature) = &self.hash_for_signature {
1106                let sig: SaitoSignature = self.signature;
1107                let public_key: SaitoPublicKey = self.from[0].public_key;
1108                if !verify_signature(hash_for_signature, &sig, &public_key) {
1109                    error!(
1110                        "tx verification failed : hash = {:?}, sig = {:?}, pub_key = {:?}",
1111                        hash_for_signature.to_hex(),
1112                        sig.to_hex(),
1113                        public_key.to_base58()
1114                    );
1115                    return false;
1116                }
1117            } else {
1118                //
1119                // we reach here if we have not already calculated the hash
1120                // that is checked by the signature. while we could auto-gen
1121                // it here, we choose to throw an error to raise visibility of
1122                // unexpected behavior.
1123                //
1124                error!("ERROR 757293: there is no hash for signature in a transaction");
1125                return false;
1126            }
1127
1128            //
1129            // validate routing path sigs
1130            //
1131            // it strengthens censorship-resistance and anti-MEV properties in the network
1132            // if we refuse to let nodes include transactions that have not been routed to
1133            // them. nonetheless, while we may add this restriction, it will also mean that
1134            // the server will need to cryptographically sign the transactions that it is
1135            // sending to itself, so for now we accept transactions WITHOUT routing paths
1136            // but require that any transaction WITH a routing path must have a cryptograph-
1137            // ically valid path.
1138            //
1139            if !self.validate_routing_path() {
1140                error!("ERROR 482033: routing paths do not validate, transaction invalid");
1141                return false;
1142            }
1143
1144            //
1145            // validate tokens are not created out of thin air
1146            //
1147            if self.total_out > self.total_in && self.transaction_type != TransactionType::Fee {
1148                error!("ERROR 802394: transaction spends more than it has available");
1149                return false;
1150            }
1151        }
1152
1153        //
1154        // fee transactions
1155        //
1156        if self.transaction_type == TransactionType::Fee {}
1157
1158        //
1159        // atr transactions
1160        //
1161        if self.transaction_type == TransactionType::ATR {}
1162
1163        //
1164        // normal transactions
1165        //
1166        if self.transaction_type == TransactionType::Normal {}
1167
1168        //
1169        // golden ticket transactions
1170        //
1171        if self.transaction_type == TransactionType::GoldenTicket {}
1172
1173        //
1174        // NFT transactions validation for Bound type
1175        //
1176        // NFTs can circulate on the network either as BoundTransactions, which are
1177        // they type used to CREATE and SEND NFTs, or as ATR transactions which is
1178        // what happens if the ATR mechanism rebroadcasts a BoundTransaction in
1179        // order to keep it on the network.
1180        //
1181        // in the User-Originated Transaction sector above, we have already validated
1182        // the routing paths, and fee amounts, of our BoundTransactions, so here we
1183        // validate the NF-related requirements -- the organization of the slips in
1184        // the transaction and whether the inputs/outputs match the NFT.
1185        //
1186        if self.transaction_type == TransactionType::Bound {
1187            //
1188            // this could either be a NEW nft that we have just created, or an NFT
1189            // that already existed and is being sent from one address to another.
1190            // our validation rules are slightly different depending on which case
1191            // we have, so we check first to see which is which.
1192            //
1193
1194            //
1195            // classify as “new NFT” if exactly 1 Normal input and ≥3 outputs
1196            //
1197            let is_this_a_new_nft = self.from.len() == 1
1198                && self.from[0].slip_type == SlipType::Normal
1199                && self.to.len() >= 3;
1200
1201            //
1202            // for new NFTs we check:
1203            //
1204            // - at least three output slips
1205            // - slip1 is bound
1206            // - slip2 is normal
1207            // - slip3 is bound
1208            // - slip3.amount = 0
1209            // - slips 4,5,6 etc are normal
1210            //
1211            if is_this_a_new_nft {
1212                //
1213                // at least 3 output slips
1214                //
1215                if self.to.len() < 3 {
1216                    error!(
1217                        "Bound Transaction Invalid: fewer than 3 outputs, found {}.",
1218                        self.to.len()
1219                    );
1220                    return false;
1221                }
1222
1223                //
1224                // slip1 + slip3 = bound
1225                //
1226                if self.to[0].slip_type != SlipType::Bound
1227                    || self.to[2].slip_type != SlipType::Bound
1228                {
1229                    error!(
1230                        "Create-bound transaction: slip1 or slip3 not bound slips {:?}",
1231                        self.to.len()
1232                    );
1233                    return false;
1234                }
1235
1236                //
1237                // slip2 = normal
1238                //
1239                if self.to[1].slip_type != SlipType::Normal {
1240                    error!(
1241                        "Create-bound transaction: slip2 nor normal slip {:?}",
1242                        self.to.len()
1243                    );
1244                    return false;
1245                }
1246
1247                //
1248                // slip3 = zero amount
1249                //
1250                if self.to[2].amount != 0 {
1251                    error!(
1252                        "Create-bound transaction: output 2 (tracking slip) amount is not zero (found {}).",
1253                        self.to[2].amount
1254                    );
1255                    return false;
1256                }
1257
1258                //
1259                // any additional slips are not BoundSlips
1260                //
1261                // outputs[3..] = Normal
1262                //
1263                for slip in self.from.iter().skip(3) {
1264                    if slip.slip_type != SlipType::Normal {
1265                        error!(
1266                            "Bound Transaction: created tx has unexpected non-normal slip (found {:?}).",
1267                            slip.slip_type
1268                        );
1269                        return false;
1270                    }
1271                }
1272
1273                //
1274                // This section ensures that the bound slip (output[2]) truly encodes
1275                // the unique UTXO that was consumed to mint this NFT. We decode the 33‑byte
1276                // public_key on output[2] to extract:
1277                //
1278                //  - rec_block_id   – the original block_id (bytes 0..8)
1279                //  - rec_tx_ord     – the original transaction ordinal (bytes 8..16)
1280                //  - rec_slip_id    – the original slip_index (byte 16)
1281                //
1282                // We then compare these directly against the values on the slip we burned
1283                // (self.from[0]). If any differ, the NFT‑UUID was forged or tampered with.
1284                //
1285
1286                // Extract the 33‑byte “UUID” from the third output slip
1287                let uuid_pk = self.to[2].public_key;
1288
1289                // 1) Decode original block_id (8 bytes, big-endian)
1290                let rec_block_id = u64::from_be_bytes(uuid_pk[0..8].try_into().unwrap());
1291
1292                // 2) Decode original transaction ordinal (next 8 bytes)
1293                let rec_tx_ord = u64::from_be_bytes(uuid_pk[8..16].try_into().unwrap());
1294
1295                // 3) Decode original slip_index (1 byte)
1296                let rec_slip_id = uuid_pk[16];
1297
1298                // The slip we actually consumed to mint this NFT
1299                let original_input = &self.from[0];
1300
1301                // Directly verify each identifier
1302                if rec_block_id != original_input.block_id
1303                    || rec_tx_ord != original_input.tx_ordinal
1304                    || rec_slip_id != original_input.slip_index
1305                {
1306                    error!("Create‑bound TX: NFT UUID identifiers do not match the consumed UTXO");
1307                    return false;
1308                }
1309
1310            //
1311            // otherwise, this is an existing NFT which is being transferred between
1312            // network addresses, in which case we have a slightly different set of
1313            // checks.
1314            //
1315            // - at least three input slips
1316            // - at least three output slips
1317            // - input slip1 is bound
1318            // - input slip2 is normal
1319            // - input slip3 is bound
1320            // - output slip1 is bound
1321            // - output slip2 is normal
1322            // - output slip3 is bound
1323            // - input slips 4,5,6 etc are normal
1324            // - output slips 4,5,6 etc are normal
1325            //
1326            // - input slip1 publickey matches output slip1 publickey
1327            // - input slip3 publickey matches output slip3 publickey
1328            // - input slip1 amount matches output slip1 amount
1329            // - input slip3 amount matches output slip3 amount
1330            // - slip1, slip2, slip3 are identical block_id, tx_id, and sequential slip_id
1331            //
1332            } else {
1333                //
1334                // at least 3 input slips
1335                //
1336                if self.from.len() < 3 {
1337                    error!(
1338                        "Send bound transaction Invalid: fewer than 3 inputs, found {}.",
1339                        self.from.len()
1340                    );
1341                    return false;
1342                }
1343                //
1344                // at least 3 output slips
1345                //
1346                if self.to.len() < 3 {
1347                    error!(
1348                        "Send-bound transaction Invalid: fewer than 3 outputs, found {}.",
1349                        self.to.len()
1350                    );
1351                    return false;
1352                }
1353
1354                //
1355                // input slip1 + slip3 = bound
1356                //
1357                if self.from[0].slip_type != SlipType::Bound
1358                    || self.from[2].slip_type != SlipType::Bound
1359                {
1360                    error!(
1361                        "Send-bound transaction: Input slip1 {:?} or slip3 not bound slips {:?}",
1362                        self.from[0], self.from[2]
1363                    );
1364                    return false;
1365                }
1366
1367                //
1368                // input slip2 = normal
1369                //
1370                if self.from[1].slip_type != SlipType::Normal {
1371                    error!(
1372                        "Send-bound transaction: Input slip2 not normal slip {:?}",
1373                        self.from[1]
1374                    );
1375                    return false;
1376                }
1377
1378                //
1379                // output slip1 + slip3 = bound
1380                //
1381                if self.to[0].slip_type != SlipType::Bound
1382                    || self.to[2].slip_type != SlipType::Bound
1383                {
1384                    error!(
1385                        "Send-bound transaction: Output slip1 {:?} or slip3 not bound slips {:?}",
1386                        self.to[0], self.to[2]
1387                    );
1388                    return false;
1389                }
1390
1391                //
1392                // output slip2 = normal
1393                //
1394                if self.to[1].slip_type != SlipType::Normal {
1395                    error!(
1396                        "Send-bound transaction: Output slip2 not normal slip {:?}",
1397                        self.to[1]
1398                    );
1399                    return false;
1400                }
1401
1402                //
1403                // any additional input slips are not BoundSlips
1404                //
1405                // inputs[3..] = Normal
1406                //
1407                for slip in self.from.iter().skip(3) {
1408                    if slip.slip_type != SlipType::Normal {
1409                        error!(
1410                            "Send-bound Transaction: created tx has unexpected non-normal slip (found {:?}).",
1411                            slip
1412                        );
1413                        return false;
1414                    }
1415                }
1416
1417                //
1418                // any additional output slips are not BoundSlips
1419                //
1420                // outputs[3..] = Normal
1421                //
1422                for slip in self.to.iter().skip(3) {
1423                    if slip.slip_type != SlipType::Normal {
1424                        error!(
1425                            "Send-bound Transaction: created tx has unexpected non-normal slip (found {:?}).",
1426                            slip
1427                        );
1428                        return false;
1429                    }
1430                }
1431
1432                //
1433                // input slip1 publickey matches output slip1 publickey
1434                //
1435                if self.from[0].public_key != self.to[0].public_key {
1436                    error!(
1437                        "Send-bound Transaction: NFT slip #1 has modified publickey. Input slip1 {:?}, 
1438                        output slip1: {:?}",
1439                        self.from[0],
1440                        self.to[0]
1441                    );
1442                    return false;
1443                }
1444
1445                //
1446                // input slip3 publickey matches output slip3 publickey
1447                //
1448                if self.from[2].public_key != self.to[2].public_key {
1449                    error!(
1450                        "Send-bound Transaction: NFT slip #3 has modified publickey {:?}",
1451                        self.from[2]
1452                    );
1453                    return false;
1454                }
1455
1456                //
1457                // input slip1 amount matches output slip1 amount
1458                //
1459                if self.from[0].amount != self.to[0].amount {
1460                    error!(
1461                        "Send-bound Transaction: NFT slip #3 has modified amount {:?}",
1462                        self.from[0]
1463                    );
1464                    return false;
1465                }
1466
1467                //
1468                // input slip3 amount matches output slip3 amount
1469                //
1470                if self.from[2].amount != self.to[2].amount {
1471                    error!(
1472                        "Send-bound Transaction: NFT slip #3 has modified amount {:?}",
1473                        self.from[2]
1474                    );
1475                    return false;
1476                }
1477
1478                //
1479                // input slip3 is 0 amount
1480                //
1481                if self.from[2].amount != 0 {
1482                    error!(
1483                        "Send-bound Transaction: NFT slip #3 has modified amount {:?}",
1484                        self.from[2]
1485                    );
1486                    return false;
1487                }
1488
1489                //
1490                // FROM slips have the same block_id, transaction_id and sequential slip_ids
1491                //
1492                // this is to prevent funny business of sometone trying to attach a totally
1493                // separate and un-bound normal slip as if it were the appropriate one.
1494                //
1495
1496                let block_id0 = self.from[0].block_id;
1497                let block_id1 = self.from[1].block_id;
1498                let block_id2 = self.from[2].block_id;
1499
1500                if block_id0 != block_id1 || block_id1 != block_id2 {
1501                    error!(
1502                        "Send-bound TX: input slips have mismatched block_id ({} / {} / {}).",
1503                        block_id0, block_id1, block_id2
1504                    );
1505                    return false;
1506                }
1507
1508                let tx_ordinal0 = self.from[0].tx_ordinal;
1509                let tx_ordinal1 = self.from[1].tx_ordinal;
1510                let tx_ordinal2 = self.from[2].tx_ordinal;
1511
1512                if tx_ordinal0 != tx_ordinal1 || tx_ordinal1 != tx_ordinal2 {
1513                    error!(
1514                        "Send-bound TX: input slips have mismatched tx_ordinal ({} / {} / {}).",
1515                        tx_ordinal0, tx_ordinal1, tx_ordinal2
1516                    );
1517                    return false;
1518                }
1519
1520                let slip_index0 = self.from[0].slip_index;
1521                let slip_index1 = self.from[1].slip_index;
1522                let slip_index2 = self.from[2].slip_index;
1523
1524                if slip_index1 != slip_index0 + 1 || slip_index2 != slip_index1 + 1 {
1525                    error!(
1526                        "Send-bound TX: input slips slip_index are not sequential ({} / {} / {}).",
1527                        slip_index0, slip_index1, slip_index2
1528                    );
1529                    return false;
1530                }
1531            }
1532        } else {
1533            //
1534            // the only other type of transaction that is permitted to have Bound Slips
1535            // are ATR transactions, in the case that the ATR transactions are rebroad-
1536            // casting a
1537            //
1538            if self.transaction_type != TransactionType::ATR {
1539                if self
1540                    .from
1541                    .iter()
1542                    .any(|slip| slip.slip_type == SlipType::Bound)
1543                    || self.to.iter().any(|slip| slip.slip_type == SlipType::Bound)
1544                {
1545                    error!("Non-ATR and Non-Bound Transaction has Bound UTXO");
1546                    return false;
1547                }
1548            }
1549        }
1550
1551        //
1552        // All Transactions
1553        //
1554        // The following validation criteria apply to all transactions, including
1555        // those auto-generated and included in blocks such as ATR transactions
1556        // and fee transactions.
1557        //
1558
1559        //
1560        // all transactions must have outputs
1561        //
1562        if self.to.is_empty() {
1563            error!("ERROR 582039: less than 1 output in transaction");
1564            return false;
1565        }
1566
1567        //
1568        // spent transaction slips must be spendable (in hashmap)
1569        //
1570        return if validate_against_utxo {
1571            let inputs_validate = self.validate_against_utxoset(utxoset);
1572            inputs_validate
1573        } else {
1574            true
1575        };
1576    }
1577
1578    pub fn validate_against_utxoset(&self, utxoset: &UtxoSet) -> bool {
1579        if self.transaction_type == TransactionType::Fee {
1580            return true;
1581        }
1582        // if inputs exist, they must validate against the UTXOSET
1583        // if they claim to spend tokens. if the slip has no spendable
1584        // tokens it will pass this check, which is conducted inside
1585        // the slip-level validation logic.
1586        iterate!(self.from, 10).all(|input| input.validate(utxoset))
1587    }
1588
1589    pub fn validate_routing_path(&self) -> bool {
1590        self.path.iter().enumerate().all(|(index, hop)| {
1591            let bytes: Vec<u8> = [self.signature.as_slice(), hop.to.as_slice()].concat();
1592
1593            // check sig is valid
1594            if !verify(bytes.as_slice(), &hop.sig, &hop.from) {
1595                warn!("signature is not valid");
1596                return false;
1597            }
1598
1599            if hop.from == hop.to {
1600                return false;
1601            }
1602            // check path is continuous
1603            if index > 0 && hop.from != self.path[index - 1].to {
1604                warn!(
1605                    "from {:?}: {:?} not matching with previous to {:?}: {:?}. path length = {:?}",
1606                    index,
1607                    hop.from.to_hex(),
1608                    index - 1,
1609                    self.path[index - 1].to.to_hex(),
1610                    self.path.len()
1611                );
1612                for hop in self.path.iter() {
1613                    debug!("hop : {:?} --> {:?}", hop.from.to_hex(), hop.to.to_hex());
1614                }
1615                return false;
1616            }
1617            true
1618        })
1619    }
1620    pub fn is_in_path(&self, public_key: &SaitoPublicKey) -> bool {
1621        if self.is_from(public_key) {
1622            return true;
1623        }
1624        for hop in &self.path {
1625            if hop.from.eq(public_key) {
1626                return true;
1627            }
1628        }
1629        false
1630    }
1631    pub fn is_from(&self, public_key: &SaitoPublicKey) -> bool {
1632        iterate!(self.from, 10).any(|input| input.public_key.eq(public_key))
1633    }
1634    pub fn is_to(&self, public_key: &SaitoPublicKey) -> bool {
1635        iterate!(self.to, 10).any(|slip| slip.public_key.eq(public_key))
1636    }
1637
1638    //
1639    // Returns true if the given slice of slips at `i` forms a
1640    // Bound–Normal–Bound triple (an NFT group).
1641    //
1642    pub fn is_nft(&self, slips: &[Slip], i: usize) -> bool {
1643        if i + 2 >= slips.len() {
1644            return false;
1645        }
1646        let a = &slips[i];
1647        let b = &slips[i + 1];
1648        let c = &slips[i + 2];
1649        a.slip_type == SlipType::Bound
1650            && c.slip_type == SlipType::Bound
1651            && b.slip_type != SlipType::Bound
1652    }
1653}
1654
1655#[cfg(test)]
1656mod tests {
1657    use crate::core::defs::{PrintForLog, SaitoPrivateKey, SaitoPublicKey};
1658    use crate::core::util::crypto::generate_keys;
1659
1660    use super::*;
1661
1662    #[test]
1663    fn transaction_new_test() {
1664        let tx = Transaction::default();
1665        assert_eq!(tx.timestamp, 0);
1666        assert_eq!(tx.from, vec![]);
1667        assert_eq!(tx.to, vec![]);
1668        assert_eq!(tx.data, Vec::<u8>::new());
1669        assert_eq!(tx.transaction_type, TransactionType::Normal);
1670        assert_eq!(tx.signature, [0; 64]);
1671        assert_eq!(tx.hash_for_signature, None);
1672        assert_eq!(tx.total_in, 0);
1673        assert_eq!(tx.total_out, 0);
1674        assert_eq!(tx.total_fees, 0);
1675        assert_eq!(tx.cumulative_fees, 0);
1676    }
1677
1678    #[test]
1679    fn transaction_sign_test() {
1680        let mut tx = Transaction::default();
1681        let keys = generate_keys();
1682        let wallet = Wallet::new(keys.1, keys.0);
1683
1684        tx.to = vec![Slip::default()];
1685        tx.sign(&wallet.private_key);
1686
1687        assert_eq!(tx.to[0].slip_index, 0);
1688        assert_ne!(tx.signature, [0; 64]);
1689        assert_ne!(tx.hash_for_signature, Some([0; 32]));
1690    }
1691
1692    #[test]
1693    fn serialize_for_signature_test() {
1694        let tx = Transaction::default();
1695        assert_eq!(
1696            tx.serialize_for_signature(),
1697            vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]
1698        );
1699    }
1700
1701    #[test]
1702    fn serialize_for_signature_with_data_test() {
1703        let mut tx = Transaction::default();
1704        tx.timestamp = 1637034582;
1705        tx.transaction_type = TransactionType::ATR;
1706        tx.data = vec![
1707            123, 34, 116, 101, 115, 116, 34, 58, 34, 116, 101, 115, 116, 34, 125,
1708        ];
1709
1710        let mut input_slip = Slip::default();
1711        input_slip.public_key = <SaitoPublicKey>::from_hex(
1712            "dcf6cceb74717f98c3f7239459bb36fdcd8f350eedbfccfbebf7c0b0161fcd8bcc",
1713        )
1714        .unwrap();
1715        input_slip.block_id = 0;
1716        input_slip.tx_ordinal = 0;
1717        input_slip.amount = 123;
1718        input_slip.slip_index = 10;
1719        input_slip.slip_type = SlipType::ATR;
1720
1721        let mut output_slip = Slip::default();
1722        output_slip.public_key = <SaitoPublicKey>::from_hex(
1723            "dcf6cceb74717f98c3f7239459bb36fdcd8f350eedbfccfbebf7c0b0161fcd8bcc",
1724        )
1725        .unwrap();
1726        output_slip.block_id = 0;
1727        output_slip.tx_ordinal = 0;
1728        output_slip.amount = 345;
1729        output_slip.slip_index = 23;
1730        output_slip.slip_type = SlipType::Normal;
1731
1732        tx.from.push(input_slip);
1733        tx.to.push(output_slip);
1734
1735        // assert_eq!(
1736        //     tx.serialize_for_signature(),
1737        //     vec![
1738        //         0, 0, 1, 125, 38, 221, 98, 138, 220, 246, 204, 235, 116, 113, 127, 152, 195, 247,
1739        //         35, 148, 89, 187, 54, 253, 205, 143, 53, 14, 237, 191, 204, 251, 235, 247, 192,
1740        //         176, 22, 31, 205, 139, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 123, 10,
1741        //         1, 220, 246, 204, 235, 116, 113, 127, 152, 195, 247, 35, 148, 89, 187, 54, 253,
1742        //         205, 143, 53, 14, 237, 191, 204, 251, 235, 247, 192, 176, 22, 31, 205, 139, 204, 0,
1743        //         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 89, 23, 0, 0, 0, 0, 1, 0, 0, 0, 3, 123,
1744        //         34, 116, 101, 115, 116, 34, 58, 34, 116, 101, 115, 116, 34, 125,
1745        //     ]
1746        // );
1747    }
1748
1749    #[test]
1750    fn tx_sign_with_data() {
1751        let mut tx = Transaction::default();
1752        tx.timestamp = 1637034582;
1753        tx.transaction_type = TransactionType::ATR;
1754        tx.data = vec![
1755            123, 34, 116, 101, 115, 116, 34, 58, 34, 116, 101, 115, 116, 34, 125,
1756        ];
1757
1758        let mut input_slip = Slip::default();
1759        input_slip.public_key = SaitoPublicKey::from_hex(
1760            "dcf6cceb74717f98c3f7239459bb36fdcd8f350eedbfccfbebf7c0b0161fcd8bcc",
1761        )
1762        .unwrap();
1763        input_slip.block_id = 0;
1764        input_slip.tx_ordinal = 0;
1765        input_slip.amount = 123;
1766        input_slip.slip_index = 10;
1767        input_slip.slip_type = SlipType::ATR;
1768
1769        let mut output_slip = Slip::default();
1770        output_slip.public_key = SaitoPublicKey::from_hex(
1771            "dcf6cceb74717f98c3f7239459bb36fdcd8f350eedbfccfbebf7c0b0161fcd8bcc",
1772        )
1773        .unwrap();
1774        output_slip.block_id = 0;
1775        output_slip.tx_ordinal = 0;
1776        output_slip.amount = 345;
1777        output_slip.slip_index = 23;
1778        output_slip.slip_type = SlipType::Normal;
1779
1780        tx.from.push(input_slip);
1781        tx.to.push(output_slip);
1782
1783        tx.sign(
1784            &SaitoPrivateKey::from_hex(
1785                "854702489d49c7fb2334005b903580c7a48fe81121ff16ee6d1a528ad32f235d",
1786            )
1787            .unwrap(),
1788        );
1789
1790        assert_eq!(tx.signature.len(), 64);
1791        // assert_eq!(
1792        //     tx.signature,
1793        //     [
1794        //         203, 125, 72, 56, 0, 215, 56, 221, 191, 48, 192, 230, 105, 221, 214, 165, 246, 220,
1795        //         45, 225, 64, 217, 69, 164, 26, 143, 154, 162, 121, 162, 244, 203, 30, 194, 204,
1796        //         166, 141, 17, 201, 156, 108, 170, 210, 112, 200, 93, 223, 59, 21, 157, 35, 107,
1797        //         104, 186, 159, 190, 28, 159, 119, 29, 99, 200, 241, 99
1798        //     ]
1799        // );
1800    }
1801
1802    #[test]
1803    fn transaction_generate_cumulative_fees_test() {
1804        let mut tx = Transaction::default();
1805        tx.generate_cumulative_fees(1_0000);
1806        assert_eq!(tx.cumulative_fees, 1_0000);
1807    }
1808
1809    #[test]
1810    fn serialize_for_net_and_deserialize_from_net_test() {
1811        let mock_input = Slip::default();
1812        let mock_output = Slip::default();
1813        let mock_hop = Hop::default();
1814
1815        let mut mock_tx = Transaction::default();
1816        let mut mock_path: Vec<Hop> = vec![];
1817        mock_path.push(mock_hop);
1818        let ctimestamp = 0;
1819
1820        mock_tx.timestamp = ctimestamp;
1821        mock_tx.add_from_slip(mock_input);
1822        mock_tx.add_to_slip(mock_output);
1823        mock_tx.data = vec![104, 101, 108, 108, 111];
1824        mock_tx.transaction_type = TransactionType::Normal;
1825        mock_tx.signature = [1; 64];
1826        mock_tx.path = mock_path;
1827
1828        let serialized_tx = mock_tx.serialize_for_net();
1829
1830        let deserialized_tx = Transaction::deserialize_from_net(&serialized_tx).unwrap();
1831        assert_eq!(mock_tx, deserialized_tx);
1832    }
1833    #[test]
1834    fn slip_count_test() {
1835        let mock_input = Slip::default();
1836        let mock_output = Slip::default();
1837        let mock_hop = Hop::default();
1838
1839        let mut mock_tx = Transaction::default();
1840        for i in 0..1000 {
1841            let mut mock_input = Slip::default();
1842            mock_input.amount = i;
1843            mock_tx.from.push(mock_input);
1844        }
1845        for i in 0..1000 {
1846            let mut mock_output = Slip::default();
1847            mock_output.amount = i;
1848            mock_tx.to.push(mock_output);
1849        }
1850
1851        let serialized_tx = mock_tx.serialize_for_net();
1852        assert_eq!(serialized_tx.len(), 0);
1853    }
1854}