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 Fee = 1,
31 GoldenTicket = 2,
32 ATR = 3,
33 Vip = 4,
35 SPV = 5,
36 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 pub timestamp: Timestamp,
47 pub from: Vec<Slip>,
48 pub to: Vec<Slip>,
49 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 pub hash_for_signature: Option<SaitoHash>,
59
60 pub total_in: Currency,
62 pub total_out: Currency,
64 pub total_fees: Currency,
66 pub total_work_for_me: Currency,
68 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 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 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 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 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 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 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 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 transaction.signature = transaction_to_rebroadcast.signature;
391
392 debug!("generated rebroadcast transaction: {}", transaction);
393
394 transaction
395 }
396
397 pub fn create_rebroadcast_bound_transaction(
402 transaction_to_rebroadcast: &Transaction,
403 slip1: Slip, slip2: Slip, slip3: Slip, ) -> Transaction {
407 let mut tx = Transaction::default();
408 tx.transaction_type = TransactionType::ATR;
409
410 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 tx.add_from_slip(slip1.clone());
427 tx.add_from_slip(slip2.clone());
428 tx.add_from_slip(slip3.clone());
429
430 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 tx.signature = transaction_to_rebroadcast.signature;
450
451 tx
452 }
453
454 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 pub fn deserialize_from_net(bytes: &[u8]) -> Result<Transaction, Error> {
481 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 pub fn generate(&mut self, public_key: &SaitoPublicKey, tx_index: u64, block_id: u64) -> bool {
604 self.generate_hash_for_signature();
606
607 self.generate_total_fees(tx_index, block_id);
609
610 self.generate_total_work(public_key);
612
613 true
614 }
615
616 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 pub fn generate_total_fees(&mut self, tx_index: u64, block_id: u64) {
624 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 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 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 if nolan_in > nolan_out {
669 self.total_fees = nolan_in - nolan_out;
670 }
671 }
672 pub fn generate_total_work(&mut self, public_key: &SaitoPublicKey) {
674 if self.path.is_empty() {
681 self.total_work_for_me = 0;
682 return;
683 }
684
685 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 for i in 1..self.path.len() {
706 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 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 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 if self.path.is_empty() {
744 return if !self.from.is_empty() {
745 self.from[0].public_key
746 } else {
747 [0; 33]
753 };
754 }
755
756 if self.total_fees == 0 {
763 return [0; 33];
764 }
765
766 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 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 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 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 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 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 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 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 if self.transaction_type == TransactionType::Fee {
994 return true;
995 }
996
997 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 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 return false;
1064 }
1065
1066 return true;
1067 }
1068
1069 let transaction_type = self.transaction_type;
1090
1091 if self.transaction_type != TransactionType::ATR
1092 && self.transaction_type != TransactionType::Issuance
1093 {
1094 if self.from.is_empty() {
1098 error!("ERROR 582039: less than 1 input in transaction");
1099 return false;
1100 }
1101
1102 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 error!("ERROR 757293: there is no hash for signature in a transaction");
1125 return false;
1126 }
1127
1128 if !self.validate_routing_path() {
1140 error!("ERROR 482033: routing paths do not validate, transaction invalid");
1141 return false;
1142 }
1143
1144 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 if self.transaction_type == TransactionType::Fee {}
1157
1158 if self.transaction_type == TransactionType::ATR {}
1162
1163 if self.transaction_type == TransactionType::Normal {}
1167
1168 if self.transaction_type == TransactionType::GoldenTicket {}
1172
1173 if self.transaction_type == TransactionType::Bound {
1187 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 if is_this_a_new_nft {
1212 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 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 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 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 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 let uuid_pk = self.to[2].public_key;
1288
1289 let rec_block_id = u64::from_be_bytes(uuid_pk[0..8].try_into().unwrap());
1291
1292 let rec_tx_ord = u64::from_be_bytes(uuid_pk[8..16].try_into().unwrap());
1294
1295 let rec_slip_id = uuid_pk[16];
1297
1298 let original_input = &self.from[0];
1300
1301 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 } else {
1333 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 if self.to.is_empty() {
1563 error!("ERROR 582039: less than 1 output in transaction");
1564 return false;
1565 }
1566
1567 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 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 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 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 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 }
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 }
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}