naom/utils/
transaction_utils.rs

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
12/// Builds a P2SH address
13///
14/// ### Arguments
15///
16/// * `script` - Script to build address for
17pub 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
28/// Builds an address from a public key and a specified network version
29///
30/// ### Arguments
31///
32/// * `pub_key` - A public key to build an address from
33/// * `address_version` - Network version to use for the address
34pub 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
42/// Builds an address from a public key
43///
44/// ### Arguments
45///
46/// * `pub_key` - A public key to build an address from
47pub fn construct_address(pub_key: &PublicKey) -> String {
48    hex::encode(sha3_256::digest(pub_key.as_ref()))
49}
50
51/// Builds an old (network version 0) address from a public key
52///
53/// ### Arguments
54///
55/// * `pub_key` - A public key to build an address from
56pub fn construct_address_v0(pub_key: &PublicKey) -> String {
57    let first_pubkey_bytes = {
58        // We used sodiumoxide serialization before with a 64 bit length prefix.
59        // Make clear what we are using as this was not intended.
60        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
69/// Builds an address from a public key using the
70/// temporary address scheme present on the wallet
71///
72/// TODO: Deprecate after addresses retire
73///
74/// ### Arguments
75///
76/// * `pub_key` - A public key to build an address from
77pub 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
83/// Decodes a base64 encoded string as hex, invalid character pairs are decoded up to the
84/// first character. If the decoding up to the first character fails, a default value of 0
85/// is used.
86///
87/// TODO: Deprecate after addresses retire
88///
89/// ### Arguments
90///
91/// * `s`   - Base64 encoded string
92pub 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
103/// Constructs signable string for OutPoint
104///
105/// ### Arguments
106///
107/// * `out_point`   - OutPoint value
108pub fn get_out_point_signable_string(out_point: &OutPoint) -> String {
109    format!("{}-{}", out_point.n, out_point.t_hash)
110}
111
112/// Constructs signable hash for a TxIn
113///
114/// ### Arguments
115///
116/// * `previous_out`   - Previous transaction used as input
117pub 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
123/// Constructs signable string for an Asset
124///
125/// ### Arguments
126///
127/// * `asset`   - Asset to sign
128pub 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
140/// Constructs signable asset hash for a TxIn
141///
142/// ### Arguments
143///
144/// * `asset`   - Asset to sign
145pub 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
151/// Constructs signable string for a StackEntry
152///
153/// ### Arguments
154///
155/// * `entry`   - StackEntry to obtain signable string for
156pub 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
168/// Constructs signable string for Script stack
169///
170/// ### Arguments
171///
172/// * `stack`   - StackEntry vector
173pub 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
181/// Constructs signable string for TxIn
182///
183/// ### Arguments
184///
185/// * `tx_in`   - TxIn value
186pub 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
195/// Constructs address for a TxIn collection
196///
197/// ### Arguments
198///
199/// * `tx_ins`   - TxIn collection
200pub 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
209/// Get all the hash to remove from UTXO set for the utxo_entries
210///
211/// ### Arguments
212///
213/// * `utxo_entries` - The entries to to provide an update for.
214pub 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
223/// Get all the OutPoint and Transaction from the (hash,transactions)
224///
225/// ### Arguments
226///
227/// * `txs` - The entries to to provide an update for.
228pub 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
236/// Get all the OutPoint and Transaction from the (hash,transactions)
237///
238/// ### Arguments
239///
240/// * `txs` - The entries to to provide an update for.
241pub 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
247/// Get all the OutPoint and TxOut from the (hash,transactions)
248///
249/// ### Arguments
250///
251/// * `txs` - The entries to to provide an update for.
252pub 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
260/// Get all the OutPoint and TxOut from the (hash,transactions)
261///
262/// ### Arguments
263///
264/// * `txs` - The entries to to provide an update for.
265pub 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
271/// Constructs the UTXO set for the current state of the blockchain
272///
273/// ### Arguments
274///
275/// * `current_utxo` - The current UTXO set to be updated.
276pub 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
285/// Constructs a search-valid hash for a transaction to be added to the blockchain
286///
287/// ### Arguments
288///
289/// * `tx`  - Transaction to hash
290pub 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
301/// Constructs a valid TxIn for a new create asset transaction
302///
303/// ### Arguments
304///
305/// * `block_num`   - Block number
306/// * `asset`       - Asset to create
307/// * `public_key`  - Public key to sign with
308/// * `secret_key`  - Corresponding private key
309pub 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
324/// Constructs a transaction for the creation of a new smart data asset
325///
326/// ### Arguments
327///
328/// * `block_num`           - Block number
329/// * `drs`                 - Digital rights signature for the new asset
330/// * `public_key`          - Public key for the output address
331/// * `secret_key`          - Corresponding secret key for signing data
332/// * `amount`              - Amount of the asset to generate
333pub 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
353/// Constructs a receipt data asset for use in accepting payments
354/// TODO: On compute, figure out a way to ease flow of receipts without issue for users
355///
356/// ### Arguments
357///
358/// * `block_num`           - Block number
359/// * `public_key`          - Public key for the output address
360/// * `secret_key`          - Corresponding secret key for signing data
361/// * `amount`              - Amount of receipt assets to create
362pub 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
384/// Constructs a transaction to pay a receiver
385///
386/// TODO: Check whether the `amount` is valid in the TxIns
387/// TODO: Call this a charity tx or something, as a payment is an exchange of goods
388///
389/// ### Arguments
390///
391/// * `tx_ins`              - Input/s to pay from
392/// * `receiver_address`    - Address to send to
393/// * `drs_block_hash`      - Hash of the block containing the original DRS. Only for data trades
394/// * `asset`               - Asset to send
395/// * `locktime`            - Block height below which the payment is restricted. "0" means no locktime
396pub 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
413/// Constructs a P2SH transaction to pay a receiver
414///
415/// ### Arguments
416///
417/// * `tx_ins`              - Input/s to pay from
418/// * `script`              - Script to validate
419/// * `drs_block_hash`      - Hash of the block containing the original DRS. Only for data trades
420/// * `asset`               - Asset to send
421/// * `locktime`            - Block height below which the payment is restricted. "0" means no locktime
422pub 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
441/// Constructs a P2SH transaction to burn tokens
442///
443/// ### Arguments
444///
445/// * `tx_ins`  - Input/s to pay from
446pub 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
459/// Constructs a transaction to pay a receiver
460/// If TxIn collection does not add up to the exact amount to pay,
461/// payer will always need to provide a return payment in tx_outs,
462/// otherwise the excess will be burnt and unusable.
463///
464/// TODO: Check whether the `amount` is valid in the TxIns
465/// TODO: Call this a charity tx or something, as a payment is an exchange of goods
466///
467/// ### Arguments
468///
469/// * `tx_ins`     - Address/es to pay from
470/// * `tx_outs`    - Address/es to send to
471pub 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
479/// Constructs a core receipt-based payment transaction
480///
481/// ### Arguments
482///
483/// * `from_address`    - Address receiving asset from
484/// * `to_address`      - Address sending asset to
485/// * `asset`           - Asset to send
486/// * `tx_ins`          - TxIns for outgoing transaction
487/// * `out`             - The TxOut for this send
488/// * `druid`           - DRUID to match on
489pub 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
505/// Constructs the "send" half of a receipt-based payment
506/// transaction
507///
508/// ### Arguments
509///
510/// * `receiver_address`    - Own address to receive receipt to
511/// * `amount`              - Amount of token to send
512/// * `locktime`            - Block height to lock the current transaction to
513pub 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
532/// Constructs the "receive" half of a receipt-based payment
533/// transaction
534///
535/// ### Arguments
536///
537/// * `tx_ins`              - Inputs to receipt data asset
538/// * `sender_address`      - Address of sender
539/// * `sender_send_addr`    - Input hash used by sender to send tokens
540/// * `own_address`         - Own address to receive tokens to
541/// * `amount`              - Number of tokens expected
542/// * `locktime`            - Block height below which the payment receipt is restricted. "0" means no locktime
543/// * `druid`               - The matching DRUID value
544pub 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, // this will need to change
558    };
559    tx_outs.push(out);
560    construct_rb_tx_core(tx_ins, tx_outs, druid, expectation)
561}
562
563/// Constructs a set of TxIns for a payment
564///
565/// ### Arguments
566///
567/// * `tx_values`   - Series of values required for TxIn construction
568pub 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
591/// Constructs the TxIn for a P2SH redemption. The redeemer must supply a script that
592/// matches the scriptPubKey of the output being spent.
593///
594/// ### Arguments
595///
596/// * `tx_values`   - Series of values required for TxIn construction
597/// * `script`      - Script to be used in the scriptSig
598pub 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
610/// Constructs a dual double entry tx
611///
612/// ### Arguments
613///
614/// * `druid`                           - DRUID value to match with the other party
615/// * `tx_ins`                          - Addresses to pay from
616/// * `send_asset_drs_hash`             - Hash of the block containing the DRS for the sent asset. Only applicable to data trades
617/// * `participants`                    - Participants in trade
618/// * `(send_address, receive_address)` - Send and receive addresses as a tuple
619/// * `(send_asset, receive_asset)`     - Send and receive assets as a tuple
620pub 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/*---- TESTS ----*/
638
639#[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    // Creates a valid creation transaction
649    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    // Creates a valid payment transaction
670    fn test_construct_a_valid_payment_tx() {
671        test_construct_a_valid_payment_tx_common(None);
672    }
673
674    #[test]
675    // Creates a valid payment transaction
676    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    // Creates a valid payment transaction
682    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        // TODO: Add assertion for full tx validity
748    }
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        // TODO: Add assertion for full tx validity
789    }
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    /// Checks the validity of the metadata on-spend for receipts
812    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    // Creates a valid UTXO set
847    fn test_construct_valid_utxo_set() {
848        test_construct_valid_utxo_set_common(None);
849    }
850
851    #[test]
852    // Creates a valid UTXO set
853    fn test_construct_valid_utxo_set_v0() {
854        test_construct_valid_utxo_set_common(Some(NETWORK_VERSION_V0));
855    }
856
857    #[test]
858    // Creates a valid UTXO set
859    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        // Second tx referencing first
889        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        // BTreemap
907        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        // Check that only one entry remains
914        assert_eq!(btree.len(), 1);
915        assert_ne!(btree.get(&tx_2_out_p), None);
916    }
917
918    #[test]
919    // Creates a valid DDE transaction
920    fn test_construct_a_valid_dde_tx() {
921        test_construct_a_valid_dde_tx_common(None);
922    }
923
924    #[test]
925    // Creates a valid DDE transaction
926    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    // Creates a valid DDE transaction
932    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        // DDE params
969        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        // Actual DDE
978        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    // Creates a valid receipt based tx pair
987    fn test_construct_a_valid_receipt_tx_pair() {
988        // Arrange
989        //
990        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        // Act
1003        //
1004        let send_tx = {
1005            let tx_ins = {
1006                // constructors with enough money for amount and excess, caller responsibility.
1007                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                // constructors with enough money for amount and excess, caller responsibility.
1036                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            // create the sender that match the receiver.
1046            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
1058        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    // Test valid address construction; should correlate with test on wallet
1076    fn test_construct_valid_addresses() {
1077        test_construct_valid_addresses_common(None);
1078    }
1079
1080    #[test]
1081    // Test valid address construction; should correlate with test on wallet
1082    fn test_construct_valid_addresses_v0() {
1083        test_construct_valid_addresses_common(Some(NETWORK_VERSION_V0));
1084    }
1085
1086    #[test]
1087    // Test valid address construction; should correlate with test on wallet
1088    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        //
1094        // Arrange
1095        //
1096        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        //
1107        // Act
1108        //
1109        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        //
1115        // Assert
1116        //
1117        let expected_pub_addresses = match address_version {
1118            // Old Address structure
1119            Some(NETWORK_VERSION_V0) => vec![
1120                "13bd3351b78beb2d0dadf2058dcc926c",
1121                "abc7c0448465c4507faf2ee588728824",
1122                "6ae52e3870884ab66ec49d3bb359c0bf",
1123            ],
1124            // Temporary address structure present on wallet
1125            Some(NETWORK_VERSION_TEMP) => vec![
1126                "6c6b6e8e9df8c63d22d9eb687b9671dd1ce5d89f195bb2316e1b1444848cd2b3",
1127                "8ac2fdcb0688abb2727d63ed230665b275a1d3a28373baa92a9afa5afd610e9f",
1128                "0becdaaf6a855f04961208ee992651c11df0be91c08629dfc079d05d2915ec22",
1129            ],
1130            // Current address structure
1131            _ => vec![
1132                "5423e6bd848e0ce5cd794e55235c23138d8833633cd2d7de7f4a10935178457b",
1133                "77516e2d91606250e625546f86702510d2e893e4a27edfc932fdba03c955cc1b",
1134                "4cfd64a6692021fc417368a866d33d94e1c806747f61ac85e0b3935e7d5ed925",
1135            ],
1136        };
1137        assert_eq!(actual_pub_addresses, expected_pub_addresses);
1138    }
1139
1140    #[test]
1141    // Test TxIn signable hash construction; should correlate with test on wallet
1142    fn test_construct_valid_tx_in_signable_hash() {
1143        //
1144        // Arrange
1145        //
1146        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        //
1153        // Act
1154        //
1155        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        //
1167        // Assert
1168        //
1169        assert_eq!(actual, expected);
1170    }
1171
1172    #[test]
1173    // Test TxIn signable asset hash construction; should correlate with test on wallet
1174    fn test_construct_valid_tx_in_signable_asset_hash() {
1175        //
1176        // Arrange
1177        //
1178        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        //
1188        // Act
1189        //
1190        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        //
1202        // Assert
1203        //
1204        assert_eq!(actual, expected);
1205    }
1206
1207    #[test]
1208    // Test valid TxIn address construction; should correlate with test on wallet
1209    fn test_construct_valid_tx_ins_address() {
1210        //
1211        // Arrange
1212        //
1213        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        //
1238        // Act
1239        //
1240        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        //
1259        // Assert
1260        //
1261        assert_eq!(actual, expected);
1262    }
1263}