1use crate::constants::*;
2use crate::crypto::sha3_256;
3use crate::crypto::sign_ed25519::{self as sign, PublicKey, SecretKey};
4use crate::primitives::asset::{Asset, DataAsset, TokenAmount};
5use crate::primitives::druid::{DdeValues, DruidExpectation};
6use crate::primitives::transaction::*;
7use crate::script::lang::Script;
8use crate::script::{OpCodes, StackEntry};
9use bincode::serialize;
10use std::collections::BTreeMap;
11
12pub fn construct_p2sh_address(script: &Script) -> String {
18 let bytes = match serialize(script) {
19 Ok(bytes) => bytes,
20 Err(_) => vec![],
21 };
22 let mut addr = hex::encode(sha3_256::digest(&bytes));
23 addr.insert(ZERO, P2SH_PREPEND as char);
24 addr.truncate(STANDARD_ADDRESS_LENGTH);
25 addr
26}
27
28pub fn construct_address_for(pub_key: &PublicKey, address_version: Option<u64>) -> String {
35 match address_version {
36 Some(NETWORK_VERSION_V0) => construct_address_v0(pub_key),
37 Some(NETWORK_VERSION_TEMP) => construct_address_temp(pub_key),
38 _ => construct_address(pub_key),
39 }
40}
41
42pub fn construct_address(pub_key: &PublicKey) -> String {
48 hex::encode(sha3_256::digest(pub_key.as_ref()))
49}
50
51pub fn construct_address_v0(pub_key: &PublicKey) -> String {
57 let first_pubkey_bytes = {
58 let mut v = vec![32, 0, 0, 0, 0, 0, 0, 0];
61 v.extend_from_slice(pub_key.as_ref());
62 v
63 };
64 let mut first_hash = sha3_256::digest(&first_pubkey_bytes).to_vec();
65 first_hash.truncate(V0_ADDRESS_LENGTH);
66 hex::encode(first_hash)
67}
68
69pub fn construct_address_temp(pub_key: &PublicKey) -> String {
78 let base64_encoding = base64::encode(pub_key.as_ref());
79 let hex_decoded = decode_base64_as_hex(&base64_encoding);
80 hex::encode(sha3_256::digest(&hex_decoded))
81}
82
83pub fn decode_base64_as_hex(s: &str) -> Vec<u8> {
93 (ZERO..s.len())
94 .step_by(TWO)
95 .map(|i| {
96 u8::from_str_radix(&s[i..i + TWO], SIXTEEN as u32)
97 .or_else(|_| u8::from_str_radix(&s[i..i + ONE], SIXTEEN as u32))
98 .unwrap_or_default()
99 })
100 .collect()
101}
102
103pub fn get_out_point_signable_string(out_point: &OutPoint) -> String {
109 format!("{}-{}", out_point.n, out_point.t_hash)
110}
111
112pub fn construct_tx_in_signable_hash(previous_out: &OutPoint) -> String {
118 hex::encode(sha3_256::digest(
119 get_out_point_signable_string(previous_out).as_bytes(),
120 ))
121}
122
123pub fn get_asset_signable_string(asset: &Asset) -> String {
129 match asset {
130 Asset::Token(token_amount) => format!("Token:{}", token_amount.0),
131 Asset::Data(data_asset) => format!(
132 "Data:{}-{}",
133 hex::encode(&data_asset.data),
134 data_asset.amount
135 ),
136 Asset::Receipt(receipt) => format!("Receipt:{}", receipt.amount),
137 }
138}
139
140pub fn construct_tx_in_signable_asset_hash(asset: &Asset) -> String {
146 hex::encode(sha3_256::digest(
147 get_asset_signable_string(asset).as_bytes(),
148 ))
149}
150
151pub fn get_stack_entry_signable_string(entry: &StackEntry) -> String {
157 match entry {
158 StackEntry::Op(op) => format!("Op:{op}"),
159 StackEntry::Signature(signature) => {
160 format!("Signature:{}", hex::encode(signature.as_ref()))
161 }
162 StackEntry::PubKey(pub_key) => format!("PubKey:{}", hex::encode(pub_key.as_ref())),
163 StackEntry::Num(num) => format!("Num:{num}"),
164 StackEntry::Bytes(bytes) => format!("Bytes:{bytes}"),
165 }
166}
167
168pub fn get_script_signable_string(stack: &[StackEntry]) -> String {
174 stack
175 .iter()
176 .map(get_stack_entry_signable_string)
177 .collect::<Vec<String>>()
178 .join("-")
179}
180
181pub fn get_tx_in_address_signable_string(tx_in: &TxIn) -> String {
187 let out_point_signable_string = match &tx_in.previous_out {
188 Some(out_point) => get_out_point_signable_string(out_point),
189 None => "null".to_owned(),
190 };
191 let script_signable_string = get_script_signable_string(&tx_in.script_signature.stack);
192 format!("{out_point_signable_string}-{script_signable_string}")
193}
194
195pub fn construct_tx_ins_address(tx_ins: &[TxIn]) -> String {
201 let signable_tx_ins = tx_ins
202 .iter()
203 .map(get_tx_in_address_signable_string)
204 .collect::<Vec<String>>()
205 .join("-");
206 hex::encode(sha3_256::digest(signable_tx_ins.as_bytes()))
207}
208
209pub fn get_inputs_previous_out_point<'a>(
215 utxo_entries: impl Iterator<Item = &'a Transaction>,
216) -> impl Iterator<Item = &'a OutPoint> {
217 utxo_entries
218 .filter(|tx| !tx.is_create_tx())
219 .flat_map(|val| val.inputs.iter())
220 .map(|input| input.previous_out.as_ref().unwrap())
221}
222
223pub fn get_tx_with_out_point<'a>(
229 txs: impl Iterator<Item = (&'a String, &'a Transaction)>,
230) -> impl Iterator<Item = (OutPoint, &'a Transaction)> {
231 txs.map(|(hash, tx)| (hash, tx, &tx.outputs))
232 .flat_map(|(hash, tx, outs)| outs.iter().enumerate().map(move |(idx, _)| (hash, idx, tx)))
233 .map(|(hash, idx, tx)| (OutPoint::new(hash.clone(), idx as i32), tx))
234}
235
236pub fn get_tx_with_out_point_cloned<'a>(
242 txs: impl Iterator<Item = (&'a String, &'a Transaction)> + 'a,
243) -> impl Iterator<Item = (OutPoint, Transaction)> + 'a {
244 get_tx_with_out_point(txs).map(|(h, tx)| (h, tx.clone()))
245}
246
247pub fn get_tx_out_with_out_point<'a>(
253 txs: impl Iterator<Item = (&'a String, &'a Transaction)>,
254) -> impl Iterator<Item = (OutPoint, &'a TxOut)> {
255 txs.map(|(hash, tx)| (hash, tx.outputs.iter()))
256 .flat_map(|(hash, outs)| outs.enumerate().map(move |(idx, txo)| (hash, idx, txo)))
257 .map(|(hash, idx, txo)| (OutPoint::new(hash.clone(), idx as i32), txo))
258}
259
260pub fn get_tx_out_with_out_point_cloned<'a>(
266 txs: impl Iterator<Item = (&'a String, &'a Transaction)> + 'a,
267) -> impl Iterator<Item = (OutPoint, TxOut)> + 'a {
268 get_tx_out_with_out_point(txs).map(|(o, txo)| (o, txo.clone()))
269}
270
271pub fn update_utxo_set(current_utxo: &mut BTreeMap<OutPoint, Transaction>) {
277 let value_set: Vec<OutPoint> = get_inputs_previous_out_point(current_utxo.values())
278 .cloned()
279 .collect();
280 value_set.iter().for_each(move |t_hash| {
281 current_utxo.remove(t_hash);
282 });
283}
284
285pub fn construct_tx_hash(tx: &Transaction) -> String {
291 let bytes = match serialize(tx) {
292 Ok(bytes) => bytes,
293 Err(_) => vec![],
294 };
295 let mut hash = hex::encode(sha3_256::digest(&bytes));
296 hash.insert(ZERO, TX_PREPEND as char);
297 hash.truncate(TX_HASH_LENGTH);
298 hash
299}
300
301pub fn construct_create_tx_in(
310 block_num: u64,
311 asset: &Asset,
312 public_key: PublicKey,
313 secret_key: &SecretKey,
314) -> Vec<TxIn> {
315 let asset_hash = construct_tx_in_signable_asset_hash(asset);
316 let signature = sign::sign_detached(asset_hash.as_bytes(), secret_key);
317
318 vec![TxIn {
319 previous_out: None,
320 script_signature: Script::new_create_asset(block_num, asset_hash, signature, public_key),
321 }]
322}
323
324pub fn construct_create_tx(
334 block_num: u64,
335 drs: Vec<u8>,
336 public_key: PublicKey,
337 secret_key: &SecretKey,
338 amount: u64,
339) -> Transaction {
340 let asset = Asset::Data(DataAsset { data: drs, amount });
341 let receiver_address = construct_address(&public_key);
342
343 let tx_ins = construct_create_tx_in(block_num, &asset, public_key, secret_key);
344 let tx_out = TxOut {
345 value: asset,
346 script_public_key: Some(receiver_address),
347 ..Default::default()
348 };
349
350 construct_tx_core(tx_ins, vec![tx_out])
351}
352
353pub fn construct_receipt_create_tx(
363 block_num: u64,
364 public_key: PublicKey,
365 secret_key: &SecretKey,
366 amount: u64,
367 drs_tx_hash_spec: DrsTxHashSpec,
368 metadata: Option<String>,
369) -> Transaction {
370 let drs_tx_hash = drs_tx_hash_spec.get_drs_tx_hash();
371 let asset = Asset::receipt(amount, drs_tx_hash, metadata);
372 let receiver_address = construct_address(&public_key);
373
374 let tx_ins = construct_create_tx_in(block_num, &asset, public_key, secret_key);
375 let tx_out = TxOut {
376 value: asset,
377 script_public_key: Some(receiver_address),
378 ..Default::default()
379 };
380
381 construct_tx_core(tx_ins, vec![tx_out])
382}
383
384pub fn construct_payment_tx(
397 tx_ins: Vec<TxIn>,
398 receiver_address: String,
399 drs_block_hash: Option<String>,
400 asset: Asset,
401 locktime: u64,
402) -> Transaction {
403 let tx_out = TxOut {
404 value: asset,
405 locktime,
406 script_public_key: Some(receiver_address),
407 drs_block_hash,
408 };
409
410 construct_tx_core(tx_ins, vec![tx_out])
411}
412
413pub fn construct_p2sh_tx(
423 tx_ins: Vec<TxIn>,
424 script: &Script,
425 drs_block_hash: Option<String>,
426 asset: Asset,
427 locktime: u64,
428) -> Transaction {
429 let script_hash = construct_p2sh_address(script);
430
431 let tx_out = TxOut {
432 value: asset,
433 locktime,
434 script_public_key: Some(script_hash),
435 drs_block_hash,
436 };
437
438 construct_tx_core(tx_ins, vec![tx_out])
439}
440
441pub fn construct_burn_tx(tx_ins: Vec<TxIn>) -> Transaction {
447 let s = vec![StackEntry::Op(OpCodes::OP_BURN)];
448 let script = Script::from(s);
449 let script_hash = construct_p2sh_address(&script);
450
451 let tx_out = TxOut {
452 script_public_key: Some(script_hash),
453 ..Default::default()
454 };
455
456 construct_tx_core(tx_ins, vec![tx_out])
457}
458
459pub fn construct_tx_core(tx_ins: Vec<TxIn>, tx_outs: Vec<TxOut>) -> Transaction {
472 Transaction {
473 outputs: tx_outs,
474 inputs: tx_ins,
475 ..Default::default()
476 }
477}
478
479pub fn construct_rb_tx_core(
490 tx_ins: Vec<TxIn>,
491 tx_outs: Vec<TxOut>,
492 druid: String,
493 druid_expectation: Vec<DruidExpectation>,
494) -> Transaction {
495 let mut tx = construct_tx_core(tx_ins, tx_outs);
496 tx.druid_info = Some(DdeValues {
497 druid,
498 participants: 2,
499 expectations: druid_expectation,
500 });
501
502 tx
503}
504
505pub fn construct_rb_payments_send_tx(
514 tx_ins: Vec<TxIn>,
515 mut tx_outs: Vec<TxOut>,
516 receiver_address: String,
517 amount: TokenAmount,
518 locktime: u64,
519 druid: String,
520 expectation: Vec<DruidExpectation>,
521) -> Transaction {
522 let out = TxOut {
523 value: Asset::Token(amount),
524 locktime,
525 script_public_key: Some(receiver_address),
526 drs_block_hash: None,
527 };
528 tx_outs.push(out);
529 construct_rb_tx_core(tx_ins, tx_outs, druid, expectation)
530}
531
532pub fn construct_rb_receive_payment_tx(
545 tx_ins: Vec<TxIn>,
546 mut tx_outs: Vec<TxOut>,
547 sender_address: String,
548 locktime: u64,
549 druid: String,
550 expectation: Vec<DruidExpectation>,
551 drs_tx_hash: Option<String>,
552) -> Transaction {
553 let out = TxOut {
554 value: Asset::receipt(1, drs_tx_hash, None),
555 locktime,
556 script_public_key: Some(sender_address),
557 drs_block_hash: None, };
559 tx_outs.push(out);
560 construct_rb_tx_core(tx_ins, tx_outs, druid, expectation)
561}
562
563pub fn construct_payment_tx_ins(tx_values: Vec<TxConstructor>) -> Vec<TxIn> {
569 let mut tx_ins = Vec::new();
570
571 for entry in tx_values {
572 let signable_hash = construct_tx_in_signable_hash(&entry.previous_out);
573
574 let previous_out = Some(entry.previous_out);
575 let script_signature = Script::pay2pkh(
576 signable_hash,
577 entry.signatures[0],
578 entry.pub_keys[0],
579 entry.address_version,
580 );
581
582 tx_ins.push(TxIn {
583 previous_out,
584 script_signature,
585 });
586 }
587
588 tx_ins
589}
590
591pub fn construct_p2sh_redeem_tx_ins(tx_values: TxConstructor, script: Script) -> Vec<TxIn> {
599 let mut tx_ins = Vec::new();
600 let previous_out = Some(tx_values.previous_out);
601
602 tx_ins.push(TxIn {
603 previous_out,
604 script_signature: script,
605 });
606
607 tx_ins
608}
609
610pub fn construct_dde_tx(
621 druid: String,
622 tx_ins: Vec<TxIn>,
623 tx_outs: Vec<TxOut>,
624 participants: usize,
625 expectations: Vec<DruidExpectation>,
626) -> Transaction {
627 let mut tx = construct_tx_core(tx_ins, tx_outs);
628 tx.druid_info = Some(DdeValues {
629 druid,
630 participants,
631 expectations,
632 });
633
634 tx
635}
636
637#[cfg(test)]
640mod tests {
641 use super::*;
642 use crate::crypto::sign_ed25519::{self as sign, Signature};
643 use crate::primitives::asset::{AssetValues, ReceiptAsset};
644 use crate::script::OpCodes;
645 use crate::utils::script_utils::{tx_has_valid_p2sh_script, tx_outs_are_valid};
646
647 #[test]
648 fn test_construct_a_valid_create_tx() {
650 let (pk, sk) = sign::gen_keypair();
651 let receiver_address = construct_address(&pk);
652 let amount = 1;
653 let drs = vec![0, 8, 30, 20, 1];
654
655 let tx = construct_create_tx(0, drs.clone(), pk, &sk, amount);
656
657 assert!(tx.is_create_tx());
658 assert_eq!(tx.outputs.len(), 1);
659 assert_eq!(tx.druid_info, None);
660 assert_eq!(tx.outputs[0].drs_block_hash, None);
661 assert_eq!(tx.outputs[0].script_public_key, Some(receiver_address));
662 assert_eq!(
663 tx.outputs[0].value,
664 Asset::Data(DataAsset { data: drs, amount })
665 );
666 }
667
668 #[test]
669 fn test_construct_a_valid_payment_tx() {
671 test_construct_a_valid_payment_tx_common(None);
672 }
673
674 #[test]
675 fn test_construct_a_valid_payment_tx_v0() {
677 test_construct_a_valid_payment_tx_common(Some(NETWORK_VERSION_V0));
678 }
679
680 #[test]
681 fn test_construct_a_valid_payment_tx_temp() {
683 test_construct_a_valid_payment_tx_common(Some(NETWORK_VERSION_TEMP));
684 }
685
686 fn test_construct_valid_inputs(address_version: Option<u64>) -> (Vec<TxIn>, String) {
687 let (_pk, sk) = sign::gen_keypair();
688 let (pk, _sk) = sign::gen_keypair();
689 let t_hash = vec![0, 0, 0];
690 let signature = sign::sign_detached(&t_hash, &sk);
691 let drs_block_hash = hex::encode(vec![1, 2, 3, 4, 5, 6]);
692
693 let tx_const = TxConstructor {
694 previous_out: OutPoint::new(hex::encode(t_hash), 0),
695 signatures: vec![signature],
696 pub_keys: vec![pk],
697 address_version,
698 };
699
700 let tx_ins = construct_payment_tx_ins(vec![tx_const]);
701
702 (tx_ins, drs_block_hash)
703 }
704
705 #[test]
706 fn test_construct_a_valid_p2sh_tx() {
707 let token_amount = TokenAmount(400000);
708 let (tx_ins, drs_block_hash) = test_construct_valid_inputs(Some(NETWORK_VERSION_V0));
709 let mut script = Script::new_for_coinbase(10);
710 script.stack.push(StackEntry::Op(OpCodes::OP_DROP));
711
712 let p2sh_tx = construct_p2sh_tx(
713 tx_ins,
714 &script,
715 Some(drs_block_hash.clone()),
716 Asset::Token(token_amount),
717 0,
718 );
719
720 let spending_tx_hash = construct_tx_hash(&p2sh_tx);
721
722 let tx_const = TxConstructor {
723 previous_out: OutPoint::new(spending_tx_hash, 0),
724 signatures: vec![],
725 pub_keys: vec![],
726 address_version: Some(NETWORK_VERSION_V0),
727 };
728
729 let redeeming_tx_ins = construct_p2sh_redeem_tx_ins(tx_const, script.clone());
730 let redeeming_tx = construct_payment_tx(
731 redeeming_tx_ins,
732 hex::encode(vec![0; 32]),
733 Some(drs_block_hash),
734 Asset::Token(token_amount),
735 0,
736 );
737 let p2sh_script_pub_key = p2sh_tx.outputs[0].script_public_key.as_ref().unwrap();
738
739 assert_eq!(Asset::Token(token_amount), p2sh_tx.outputs[0].value);
740 assert_eq!(p2sh_script_pub_key.as_bytes()[0], P2SH_PREPEND);
741 assert_eq!(p2sh_script_pub_key.len(), STANDARD_ADDRESS_LENGTH);
742 assert!(tx_has_valid_p2sh_script(
743 &redeeming_tx.inputs[0].script_signature,
744 p2sh_tx.outputs[0].script_public_key.as_ref().unwrap()
745 ));
746
747 }
749
750 #[test]
751 fn test_construct_a_valid_burn_tx() {
752 let token_amount = TokenAmount(400000);
753 let (tx_ins, drs_block_hash) = test_construct_valid_inputs(Some(NETWORK_VERSION_V0));
754
755 let burn_tx = construct_burn_tx(tx_ins);
756
757 let spending_tx_hash = construct_tx_hash(&burn_tx);
758
759 let tx_const = TxConstructor {
760 previous_out: OutPoint::new(spending_tx_hash, 0),
761 signatures: vec![],
762 pub_keys: vec![],
763 address_version: Some(NETWORK_VERSION_V0),
764 };
765
766 let s = vec![StackEntry::Op(OpCodes::OP_BURN)];
767 let script = Script::from(s);
768
769 let redeeming_tx_ins = construct_p2sh_redeem_tx_ins(tx_const, script);
770 let redeeming_tx = construct_payment_tx(
771 redeeming_tx_ins,
772 hex::encode(vec![0; 32]),
773 Some(drs_block_hash),
774 Asset::Token(token_amount),
775 0,
776 );
777 let burn_script_pub_key = burn_tx.outputs[0].script_public_key.as_ref().unwrap();
778 println!("{:?}", burn_script_pub_key);
779
780 assert_eq!(burn_script_pub_key.as_bytes()[0], P2SH_PREPEND);
781 assert_eq!(burn_script_pub_key.len(), STANDARD_ADDRESS_LENGTH);
782 assert!(!redeeming_tx.inputs[0].script_signature.interpret());
783 assert!(!tx_has_valid_p2sh_script(
784 &redeeming_tx.inputs[0].script_signature,
785 burn_tx.outputs[0].script_public_key.as_ref().unwrap()
786 ));
787
788 }
790
791 fn test_construct_a_valid_payment_tx_common(address_version: Option<u64>) {
792 let (tx_ins, drs_block_hash) = test_construct_valid_inputs(address_version);
793
794 let token_amount = TokenAmount(400000);
795 let payment_tx = construct_payment_tx(
796 tx_ins,
797 hex::encode(vec![0; 32]),
798 Some(drs_block_hash),
799 Asset::Token(token_amount),
800 0,
801 );
802
803 assert_eq!(Asset::Token(token_amount), payment_tx.outputs[0].value);
804 assert_eq!(
805 payment_tx.outputs[0].script_public_key,
806 Some(hex::encode(vec![0; 32]))
807 );
808 }
809
810 #[test]
811 fn test_receipt_onspend_metadata() {
813 let (_pk, sk) = sign::gen_keypair();
814 let (pk, _sk) = sign::gen_keypair();
815 let t_hash = vec![0, 0, 0];
816 let signature = sign::sign_detached(&t_hash, &sk);
817 let drs_block_hash = hex::encode(vec![1, 2, 3, 4, 5, 6]);
818
819 let tx_const = TxConstructor {
820 previous_out: OutPoint::new(hex::encode(t_hash), 0),
821 signatures: vec![signature],
822 pub_keys: vec![pk],
823 address_version: Some(2),
824 };
825
826 let drs_tx_hash = "receipt_tx_hash".to_string();
827 let receipt_asset_valid = ReceiptAsset::new(1000, Some(drs_tx_hash.clone()), None);
828
829 let tx_ins = construct_payment_tx_ins(vec![tx_const]);
830 let payment_tx_valid = construct_payment_tx(
831 tx_ins,
832 hex::encode(vec![0; 32]),
833 Some(drs_block_hash),
834 Asset::Receipt(receipt_asset_valid),
835 0,
836 );
837
838 let mut btree = BTreeMap::new();
839 btree.insert(drs_tx_hash, 1000);
840 let tx_ins_spent = AssetValues::new(TokenAmount(0), btree);
841
842 assert!(tx_outs_are_valid(&payment_tx_valid.outputs, tx_ins_spent));
843 }
844
845 #[test]
846 fn test_construct_valid_utxo_set() {
848 test_construct_valid_utxo_set_common(None);
849 }
850
851 #[test]
852 fn test_construct_valid_utxo_set_v0() {
854 test_construct_valid_utxo_set_common(Some(NETWORK_VERSION_V0));
855 }
856
857 #[test]
858 fn test_construct_valid_utxo_set_temp() {
860 test_construct_valid_utxo_set_common(Some(NETWORK_VERSION_TEMP));
861 }
862
863 fn test_construct_valid_utxo_set_common(address_version: Option<u64>) {
864 let (pk, sk) = sign::gen_keypair();
865
866 let t_hash_1 = hex::encode(vec![0, 0, 0]);
867 let signed = sign::sign_detached(t_hash_1.as_bytes(), &sk);
868
869 let tx_1 = TxConstructor {
870 previous_out: OutPoint::new("".to_string(), 0),
871 signatures: vec![signed],
872 pub_keys: vec![pk],
873 address_version,
874 };
875
876 let token_amount = TokenAmount(400000);
877 let tx_ins_1 = construct_payment_tx_ins(vec![tx_1]);
878 let payment_tx_1 = construct_payment_tx(
879 tx_ins_1,
880 hex::encode(vec![0; 32]),
881 None,
882 Asset::Token(token_amount),
883 0,
884 );
885 let tx_1_hash = construct_tx_hash(&payment_tx_1);
886 let tx_1_out_p = OutPoint::new(tx_1_hash.clone(), 0);
887
888 let tx_2 = TxConstructor {
890 previous_out: OutPoint::new(tx_1_hash, 0),
891 signatures: vec![signed],
892 pub_keys: vec![pk],
893 address_version,
894 };
895 let tx_ins_2 = construct_payment_tx_ins(vec![tx_2]);
896 let tx_outs = vec![TxOut::new_token_amount(
897 hex::encode(vec![0; 32]),
898 token_amount,
899 None,
900 )];
901 let payment_tx_2 = construct_tx_core(tx_ins_2, tx_outs);
902
903 let tx_2_hash = construct_tx_hash(&payment_tx_2);
904 let tx_2_out_p = OutPoint::new(tx_2_hash, 0);
905
906 let mut btree = BTreeMap::new();
908 btree.insert(tx_1_out_p, payment_tx_1);
909 btree.insert(tx_2_out_p.clone(), payment_tx_2);
910
911 update_utxo_set(&mut btree);
912
913 assert_eq!(btree.len(), 1);
915 assert_ne!(btree.get(&tx_2_out_p), None);
916 }
917
918 #[test]
919 fn test_construct_a_valid_dde_tx() {
921 test_construct_a_valid_dde_tx_common(None);
922 }
923
924 #[test]
925 fn test_construct_a_valid_dde_tx_v0() {
927 test_construct_a_valid_dde_tx_common(Some(NETWORK_VERSION_V0));
928 }
929
930 #[test]
931 fn test_construct_a_valid_dde_tx_temp() {
933 test_construct_a_valid_dde_tx_common(Some(NETWORK_VERSION_TEMP));
934 }
935
936 fn test_construct_a_valid_dde_tx_common(address_version: Option<u64>) {
937 let (_pk, sk) = sign::gen_keypair();
938 let (pk, _sk) = sign::gen_keypair();
939 let t_hash = hex::encode(vec![0, 0, 0]);
940 let signature = sign::sign_detached(t_hash.as_bytes(), &sk);
941
942 let to_asset = "2222".to_owned();
943 let data = Asset::Data(DataAsset {
944 data: vec![0, 12, 3, 5, 6],
945 amount: 1,
946 });
947
948 let tx_const = TxConstructor {
949 previous_out: OutPoint::new(hex::encode(&t_hash), 0),
950 signatures: vec![signature],
951 pub_keys: vec![pk],
952 address_version,
953 };
954
955 let tx_ins = construct_payment_tx_ins(vec![tx_const]);
956 let tx_outs = vec![TxOut {
957 value: data.clone(),
958 script_public_key: Some(to_asset.clone()),
959 ..Default::default()
960 }];
961
962 let bytes = match serialize(&tx_ins) {
963 Ok(bytes) => bytes,
964 Err(_) => vec![],
965 };
966 let from_addr = hex::encode(bytes);
967
968 let druid = hex::encode(vec![1, 2, 3, 4, 5]);
970 let participants = 2;
971 let expects = vec![DruidExpectation {
972 from: from_addr,
973 to: to_asset,
974 asset: data.clone(),
975 }];
976
977 let dde = construct_dde_tx(druid.clone(), tx_ins, tx_outs, participants, expects);
979
980 assert_eq!(dde.druid_info.clone().unwrap().druid, druid);
981 assert_eq!(dde.outputs[0].clone().value, data);
982 assert_eq!(dde.druid_info.unwrap().participants, participants);
983 }
984
985 #[test]
986 fn test_construct_a_valid_receipt_tx_pair() {
988 let amount = TokenAmount(33);
991 let payment = TokenAmount(11);
992 let druid = "VALUE".to_owned();
993
994 let tx_input = construct_payment_tx_ins(vec![]);
995 let from_addr = construct_tx_ins_address(&tx_input);
996
997 let alice_addr = "1111".to_owned();
998 let bob_addr = "00000".to_owned();
999
1000 let sender_address_excess = "11112".to_owned();
1001
1002 let send_tx = {
1005 let tx_ins = {
1006 construct_payment_tx_ins(vec![])
1008 };
1009 let excess_tx_out =
1010 TxOut::new_token_amount(sender_address_excess, amount - payment, None);
1011
1012 let expectation = DruidExpectation {
1013 from: from_addr.clone(),
1014 to: alice_addr.clone(),
1015 asset: Asset::receipt(1, Some("drs_tx_hash".to_owned()), None),
1016 };
1017
1018 let mut tx = construct_rb_payments_send_tx(
1019 tx_ins,
1020 Vec::new(),
1021 bob_addr.clone(),
1022 payment,
1023 0,
1024 druid.clone(),
1025 vec![expectation],
1026 );
1027
1028 tx.outputs.push(excess_tx_out);
1029
1030 tx
1031 };
1032
1033 let recv_tx = {
1034 let tx_ins = {
1035 let tx_ins_constructor = vec![];
1037 construct_payment_tx_ins(tx_ins_constructor)
1038 };
1039 let expectation = DruidExpectation {
1040 from: from_addr,
1041 to: bob_addr,
1042 asset: Asset::Token(payment),
1043 };
1044
1045 construct_rb_receive_payment_tx(
1047 tx_ins,
1048 Vec::new(),
1049 alice_addr,
1050 0,
1051 druid.clone(),
1052 vec![expectation],
1053 Some("drs_tx_hash".to_owned()),
1054 )
1055 };
1056
1057 assert_eq!(
1059 send_tx
1060 .druid_info
1061 .as_ref()
1062 .map(|v| (&v.druid, v.participants)),
1063 Some((&druid, 2))
1064 );
1065 assert_eq!(
1066 recv_tx
1067 .druid_info
1068 .as_ref()
1069 .map(|v| (&v.druid, v.participants)),
1070 Some((&druid, 2))
1071 );
1072 }
1073
1074 #[test]
1075 fn test_construct_valid_addresses() {
1077 test_construct_valid_addresses_common(None);
1078 }
1079
1080 #[test]
1081 fn test_construct_valid_addresses_v0() {
1083 test_construct_valid_addresses_common(Some(NETWORK_VERSION_V0));
1084 }
1085
1086 #[test]
1087 fn test_construct_valid_addresses_temp() {
1089 test_construct_valid_addresses_common(Some(NETWORK_VERSION_TEMP));
1090 }
1091
1092 fn test_construct_valid_addresses_common(address_version: Option<u64>) {
1093 let pub_keys = vec![
1097 "5371832122a8e804fa3520ec6861c3fa554a7f6fb617e6f0768452090207e07c",
1098 "6e86cc1fc5efbe64c2690efbb966b9fe1957facc497dce311981c68dac88e08c",
1099 "8b835e00c57ebff6637ec32276f2c6c0df71129c8f0860131a78a4692a0b59dc",
1100 ]
1101 .iter()
1102 .map(|v| hex::decode(v).unwrap())
1103 .map(|v| PublicKey::from_slice(&v).unwrap())
1104 .collect::<Vec<PublicKey>>();
1105
1106 let actual_pub_addresses: Vec<String> = pub_keys
1110 .iter()
1111 .map(|pub_key| construct_address_for(pub_key, address_version))
1112 .collect();
1113
1114 let expected_pub_addresses = match address_version {
1118 Some(NETWORK_VERSION_V0) => vec![
1120 "13bd3351b78beb2d0dadf2058dcc926c",
1121 "abc7c0448465c4507faf2ee588728824",
1122 "6ae52e3870884ab66ec49d3bb359c0bf",
1123 ],
1124 Some(NETWORK_VERSION_TEMP) => vec![
1126 "6c6b6e8e9df8c63d22d9eb687b9671dd1ce5d89f195bb2316e1b1444848cd2b3",
1127 "8ac2fdcb0688abb2727d63ed230665b275a1d3a28373baa92a9afa5afd610e9f",
1128 "0becdaaf6a855f04961208ee992651c11df0be91c08629dfc079d05d2915ec22",
1129 ],
1130 _ => vec![
1132 "5423e6bd848e0ce5cd794e55235c23138d8833633cd2d7de7f4a10935178457b",
1133 "77516e2d91606250e625546f86702510d2e893e4a27edfc932fdba03c955cc1b",
1134 "4cfd64a6692021fc417368a866d33d94e1c806747f61ac85e0b3935e7d5ed925",
1135 ],
1136 };
1137 assert_eq!(actual_pub_addresses, expected_pub_addresses);
1138 }
1139
1140 #[test]
1141 fn test_construct_valid_tx_in_signable_hash() {
1143 let out_points = vec![
1147 OutPoint::new("000000".to_owned(), 0),
1148 OutPoint::new("000001".to_owned(), 0),
1149 OutPoint::new("000002".to_owned(), 0),
1150 ];
1151
1152 let actual: Vec<String> = out_points
1156 .iter()
1157 .map(construct_tx_in_signable_hash)
1158 .collect();
1159
1160 let expected: Vec<String> = vec![
1161 "927b3411743452e5e0d73e9e40a4fa3c842b3d00dabde7f9af7e44661ce02c88".to_owned(),
1162 "754dc248d1c847e8a10c6f8ded6ccad96381551ebb162583aea2a86b9bb78dfa".to_owned(),
1163 "5585c6f74d5c55f1ab457c31671822ba28c78c397cce1e11680b9f3852f96edb".to_owned(),
1164 ];
1165
1166 assert_eq!(actual, expected);
1170 }
1171
1172 #[test]
1173 fn test_construct_valid_tx_in_signable_asset_hash() {
1175 let assets = vec![
1179 Asset::token_u64(1),
1180 Asset::receipt(1, None, None),
1181 Asset::Data(DataAsset {
1182 data: vec![1, 2, 3],
1183 amount: 1,
1184 }),
1185 ];
1186
1187 let actual: Vec<String> = assets
1191 .iter()
1192 .map(construct_tx_in_signable_asset_hash)
1193 .collect();
1194
1195 let expected: Vec<String> = vec![
1196 "a5b2f5e8dcf824aee45b81294ff8049b680285b976cc6c8fa45eb070acfc5974".to_owned(),
1197 "ce86f26f7f44f92630031f83e8d2f26c58e88eae40583c8760082edc7407991f".to_owned(),
1198 "ab72cb41f1f18edfb9c5161029c9695de4d5eed1d323be18ddedfb66a2b32282".to_owned(),
1199 ];
1200
1201 assert_eq!(actual, expected);
1205 }
1206
1207 #[test]
1208 fn test_construct_valid_tx_ins_address() {
1210 let pub_keys = vec![
1214 "5e6d463ec66d7999769fa4de56f690dfb62e685b97032f5926b0cb6c93ba83c6",
1215 "58272ba93c1e79df280d4c417de47dbf6a7e330ba52793d7baa8e00ae5c34e59",
1216 "efa9dcba0f3282b3ed4a6aa1ccdb169d6685a30d7b2af7a2171a5682f3112359",
1217 ];
1218
1219 let signatures = vec![
1220 "660e4698d817d409feb209699b15935048c8b3c4ac86a23f25b05aa32fb8b87e7cd029b83220d31a0b2717bd63b47a320a7728355d7fae43a665d6e27743e20d",
1221 "fd107c9446cdcbd8fbb0d6b88c73067c9bd15de03fff677b0129acf1bd2d14a5ab8a63c7eb6fe8c5acc4b44b033744760847194a15b006368d178c85243d0605",
1222 "e1a436bbfcb3e411be1ce6088cdb4c39d7e79f8fe427943e74307e43864fd0f6ef26123f1439b92c075edd031d17feb4dd265c6fcc2e5ed571df48a03c396100",
1223 ];
1224
1225 let signable_data = vec![
1226 "927b3411743452e5e0d73e9e40a4fa3c842b3d00dabde7f9af7e44661ce02c88",
1227 "754dc248d1c847e8a10c6f8ded6ccad96381551ebb162583aea2a86b9bb78dfa",
1228 "5585c6f74d5c55f1ab457c31671822ba28c78c397cce1e11680b9f3852f96edb",
1229 ];
1230
1231 let previous_out_points = vec![
1232 OutPoint::new("000000".to_owned(), 0),
1233 OutPoint::new("000001".to_owned(), 0),
1234 OutPoint::new("000002".to_owned(), 0),
1235 ];
1236
1237 let tx_ins: Vec<TxIn> = (0..3)
1241 .map(|n| {
1242 let sig_data = signable_data[n].to_owned();
1243 let sig =
1244 Signature::from_slice(hex::decode(signatures[n]).unwrap().as_ref()).unwrap();
1245 let pk = PublicKey::from_slice(hex::decode(pub_keys[n]).unwrap().as_ref()).unwrap();
1246
1247 let script = Script::pay2pkh(sig_data, sig, pk, None);
1248 let out_p = previous_out_points[n].clone();
1249
1250 TxIn::new_from_input(out_p, script)
1251 })
1252 .collect();
1253
1254 let expected =
1255 "c8b62d379f07602956207ea473ce20d9752d24ad6e6cd43cb042d024d7c6a468".to_owned();
1256 let actual = construct_tx_ins_address(&tx_ins);
1257
1258 assert_eq!(actual, expected);
1262 }
1263}