Skip to main content

lwk_test_util/
lib.rs

1use elements_miniscript::elements::{self, BlockHeader};
2
3use elements::bitcoin::bip32::Xpriv;
4use elements::bitcoin::Network;
5use elements::confidential::{AssetBlindingFactor, ValueBlindingFactor};
6use elements::encode::Decodable;
7use elements::hex::{FromHex, ToHex};
8use elements::pset::PartiallySignedTransaction;
9use elements::{AssetId, TxOutWitness, Txid};
10use elements::{Block, TxOutSecrets};
11use elements_miniscript::descriptor::checksum::desc_checksum;
12use pulldown_cmark::{CodeBlockKind, Event, Tag};
13use rand::{thread_rng, Rng};
14use std::str::FromStr;
15
16mod registry;
17mod test_env;
18mod waterfalls;
19pub use test_env::{TestEnv, TestEnvBuilder};
20
21const DEFAULT_FEE_RATE: f32 = 100.0;
22
23pub const TEST_MNEMONIC: &str =
24    "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
25pub const TEST_MNEMONIC_XPUB: &str =
26"tpubD6NzVbkrYhZ4XYa9MoLt4BiMZ4gkt2faZ4BcmKu2a9te4LDpQmvEz2L2yDERivHxFPnxXXhqDRkUNnQCpZggCyEZLBktV7VaSmwayqMJy1s";
27pub const TEST_MNEMONIC_SLIP77: &str =
28    "9c8e4f05c7711a98c838be228bcb84924d4570ca53f35fa1c793e58841d47023";
29
30pub const DEFAULT_SPECULOS_MNEMONIC: &str = "glory promote mansion idle axis finger extra february uncover one trip resource lawn turtle enact monster seven myth punch hobby comfort wild raise skin";
31
32// Constants for pegins
33// test vector created with:
34// ```
35// $ elements-cli getnetworkinfo | jq .version
36// 230201
37// $ elements-cli getblockchaininfo | jq .blocks
38// 2976078
39// elements-cli getsidechaininfo | jq '.current_fedpegscripts[0]'`
40// "5b21020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b678172612102675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af992102896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d4821029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c2102a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc401021031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb2103079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b2103111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2210318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa08401742103230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de121035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a62103bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c2103cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d175462103d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d4248282103ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a5fae736402c00fb269522103aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79210291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807210386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb53ae68"
41// $ elements-cli getpeginaddress
42// {
43// "mainchain_address": "bc1qyya0twwz58kgfslpdgsygeq0r4nngl9tkt89v6phk8nqrwyenwrq5h0dk8",
44// "claim_script": "0014a15906e643f2c9635527ab8658d370e8eaf149b5"
45// }
46// ```
47pub const FED_PEG_SCRIPT: &str = "5b21020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b678172612102675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af992102896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d4821029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c2102a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc401021031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb2103079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b2103111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2210318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa08401742103230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de121035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a62103bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c2103cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d175462103d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d4248282103ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a5fae736402c00fb269522103aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79210291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807210386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb53ae68";
48pub const FED_PEG_SCRIPT_ASM: &str = "OP_PUSHNUM_11 OP_PUSHBYTES_33 020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b67817261 OP_PUSHBYTES_33 02675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af99 OP_PUSHBYTES_33 02896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d48 OP_PUSHBYTES_33 029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c OP_PUSHBYTES_33 02a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc4010 OP_PUSHBYTES_33 031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb OP_PUSHBYTES_33 03079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b OP_PUSHBYTES_33 03111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2 OP_PUSHBYTES_33 0318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa0840174 OP_PUSHBYTES_33 03230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de1 OP_PUSHBYTES_33 035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a6 OP_PUSHBYTES_33 03bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c OP_PUSHBYTES_33 03cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d17546 OP_PUSHBYTES_33 03d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d424828 OP_PUSHBYTES_33 03ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a OP_PUSHNUM_15 OP_CHECKMULTISIG OP_IFDUP OP_NOTIF OP_PUSHBYTES_2 c00f OP_CSV OP_VERIFY OP_PUSHNUM_2 OP_PUSHBYTES_33 03aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79 OP_PUSHBYTES_33 0291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807 OP_PUSHBYTES_33 0386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb OP_PUSHNUM_3 OP_CHECKMULTISIG OP_ENDIF";
49pub const FED_PEG_DESC: &str = "wsh(or_d(multi(11,020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b67817261,02675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af99,02896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d48,029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c,02a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc4010,031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb,03079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b,03111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2,0318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa0840174,03230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de1,035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a6,03bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c,03cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d17546,03d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d424828,03ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a),and_v(v:older(4032),multi(2,03aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79,0291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807,0386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb))))#7jwwklk4";
50
51pub const PEGIN_TEST_DESC: &str = "ct(slip77(ab5824f4477b4ebb00a132adfd8eb0b7935cf24f6ac151add5d1913db374ce92),elwpkh([759db348/84'/1'/0']tpubDCRMaF33e44pcJj534LXVhFbHibPbJ5vuLhSSPFAw57kYURv4tzXFL6LSnd78bkjqdmE3USedkbpXJUPA1tdzKfuYSL7PianceqAhwL2UkA/<0;1>/*))";
52pub const PEGIN_TEST_ADDR: &str = "tb1qqkq6czql4zqwsylgrfzttjrn5wjeqmwfq5yn80p39amxtnkng9lsyjwm6v"; // tweak_index = 0
53
54/// Descriptor with 11 txs on testnet
55pub const TEST_DESCRIPTOR: &str = "ct(slip77(ab5824f4477b4ebb00a132adfd8eb0b7935cf24f6ac151add5d1913db374ce92),elwpkh([759db348/84'/1'/0']tpubDCRMaF33e44pcJj534LXVhFbHibPbJ5vuLhSSPFAw57kYURv4tzXFL6LSnd78bkjqdmE3USedkbpXJUPA1tdzKfuYSL7PianceqAhwL2UkA/<0;1>/*))#cch6wrnp";
56
57pub fn liquid_block_1() -> Block {
58    let raw = include_bytes!(
59        "../test_data/afafbbdfc52a45e51a3b634f391f952f6bdfd14ef74b34925954b4e20d0ad639.raw"
60    );
61    Block::consensus_decode(&raw[..]).unwrap()
62}
63
64pub fn liquid_block_header_2_963_520() -> BlockHeader {
65    let hex = include_str!("../test_data/block_header_2_963_520.hex");
66    let bytes = Vec::<u8>::from_hex(hex).unwrap();
67    BlockHeader::consensus_decode(&bytes[..]).unwrap()
68}
69
70pub fn add_checksum(desc: &str) -> String {
71    if desc.find('#').is_some() {
72        desc.into()
73    } else {
74        format!("{}#{}", desc, desc_checksum(desc).unwrap())
75    }
76}
77
78pub fn compute_fee_rate_without_discount_ct(pset: &PartiallySignedTransaction) -> f32 {
79    let vsize = pset.extract_tx().unwrap().vsize();
80    let fee_satoshi = pset.outputs().last().unwrap().amount.unwrap();
81    1000.0 * (fee_satoshi as f32 / vsize as f32)
82}
83
84pub fn compute_fee_rate(pset: &PartiallySignedTransaction) -> f32 {
85    let vsize = pset.extract_tx().unwrap().discount_vsize();
86    let fee_satoshi = pset.outputs().last().unwrap().amount.unwrap();
87    1000.0 * (fee_satoshi as f32 / vsize as f32)
88}
89
90pub fn assert_fee_rate(fee_rate: f32, expected: Option<f32>) {
91    let expected = expected.unwrap_or(DEFAULT_FEE_RATE);
92    let toll = 0.45;
93    assert!(fee_rate > expected * (1.0 - toll));
94    assert!(fee_rate < expected * (1.0 + toll));
95}
96
97pub fn parse_code_from_markdown(markdown_input: &str, code_kind: &str) -> Vec<String> {
98    let parser = pulldown_cmark::Parser::new(markdown_input);
99    let mut result = vec![];
100    let mut str = String::new();
101    let mut active = false;
102
103    for el in parser {
104        match el {
105            Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(current)))
106                if code_kind == current.as_ref() =>
107            {
108                active = true
109            }
110            Event::Text(t) => {
111                if active {
112                    str.push_str(t.as_ref())
113                }
114            }
115            Event::End(Tag::CodeBlock(CodeBlockKind::Fenced(current)))
116                if code_kind == current.as_ref() =>
117            {
118                result.push(str.clone());
119                str.clear();
120                active = false;
121            }
122            _ => (),
123        }
124    }
125
126    result
127}
128
129/// Serialize and deserialize a PSET
130///
131/// This allows us to catch early (de)serialization issues,
132/// which can be hit in practice since PSETs are passed around as b64 strings.
133pub fn pset_rt(pset: &PartiallySignedTransaction) -> PartiallySignedTransaction {
134    PartiallySignedTransaction::from_str(&pset.to_string()).unwrap()
135}
136
137pub fn regtest_policy_asset() -> AssetId {
138    AssetId::from_str("5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225").unwrap()
139}
140
141pub fn init_logging() {
142    let _ = env_logger::try_init();
143}
144
145#[allow(dead_code)]
146pub fn prune_proofs(pset: &PartiallySignedTransaction) -> PartiallySignedTransaction {
147    let mut pset = pset.clone();
148    for i in pset.inputs_mut() {
149        if let Some(utxo) = &mut i.witness_utxo {
150            utxo.witness = TxOutWitness::default();
151        }
152        if let Some(tx) = &mut i.non_witness_utxo {
153            tx.output
154                .iter_mut()
155                .for_each(|o| o.witness = Default::default());
156        }
157    }
158    for o in pset.outputs_mut() {
159        o.value_rangeproof = None;
160        o.asset_surjection_proof = None;
161        o.blind_value_proof = None;
162        o.blind_asset_proof = None;
163    }
164    pset
165}
166
167pub fn generate_mnemonic() -> String {
168    let mut bytes = [0u8; 16];
169    thread_rng().fill(&mut bytes);
170    bip39::Mnemonic::from_entropy(&bytes).unwrap().to_string()
171}
172
173pub fn generate_slip77() -> String {
174    let mut bytes = [0u8; 32];
175    thread_rng().fill(&mut bytes);
176    bytes.to_hex()
177}
178
179pub fn generate_view_key() -> String {
180    let mut bytes = [0u8; 32];
181    thread_rng().fill(&mut bytes);
182    bytes.to_hex()
183}
184
185pub fn generate_xprv() -> Xpriv {
186    let mut seed = [0u8; 16];
187    thread_rng().fill(&mut seed);
188    Xpriv::new_master(Network::Regtest, &seed).unwrap()
189}
190
191pub fn n_issuances(details: &lwk_common::PsetDetails) -> usize {
192    details.issuances.iter().filter(|e| e.is_issuance()).count()
193}
194
195pub fn n_reissuances(details: &lwk_common::PsetDetails) -> usize {
196    details
197        .issuances
198        .iter()
199        .filter(|e| e.is_reissuance())
200        .count()
201}
202
203pub fn asset_blinding_factor_test_vector() -> AssetBlindingFactor {
204    AssetBlindingFactor::from_hex(
205        "0000000000000000000000000000000000000000000000000000000000000001",
206    )
207    .unwrap()
208}
209
210pub fn value_blinding_factor_test_vector() -> ValueBlindingFactor {
211    ValueBlindingFactor::from_hex(
212        "0000000000000000000000000000000000000000000000000000000000000002",
213    )
214    .unwrap()
215}
216
217pub fn txid_test_vector() -> Txid {
218    Txid::from_str("0000000000000000000000000000000000000000000000000000000000000003").unwrap()
219}
220
221pub fn tx_out_secrets_test_vector() -> TxOutSecrets {
222    elements::TxOutSecrets::new(
223        regtest_policy_asset(),
224        asset_blinding_factor_test_vector(),
225        1000,
226        value_blinding_factor_test_vector(),
227    )
228}
229
230pub fn tx_out_secrets_test_vector_bytes() -> Vec<u8> {
231    Vec::<u8>::from_hex(include_str!("../test_data/tx_out_secrets_test_vector.hex")).unwrap()
232}
233
234pub fn update_test_vector_bytes() -> Vec<u8> {
235    Vec::<u8>::from_hex(include_str!("../test_data/update_test_vector.hex")).unwrap()
236}
237
238pub fn update_test_vector_v1_bytes() -> Vec<u8> {
239    Vec::<u8>::from_hex(include_str!("../test_data/update_test_vector_v1.hex")).unwrap()
240}
241
242pub fn update_test_vector_2_bytes() -> Vec<u8> {
243    include_bytes!("../test_data/update_test_vector.bin").to_vec()
244}
245
246/// An update (serialized v1) with 63 transactions on liquid testnet wallet defined by [`wollet_descriptor_many_transactions`]
247pub fn update_test_vector_many_transactions() -> Vec<u8> {
248    include_bytes!("../test_data/update_many_txs.bin").to_vec()
249}
250
251/// An update (serialized v2) after [`update_test_vector_many_transactions`]
252pub fn update_v2_test_vector_after_many_transactions() -> Vec<u8> {
253    include_bytes!("../test_data/update_v2_after_many_txs.bin").to_vec()
254}
255
256/// First of 3 consecutive updates for merge testing
257/// Contains initial wallet sync (tip only)
258pub fn update_merge_test_1() -> Vec<u8> {
259    include_bytes!("../test_data/merge_updates/update_merge_1.bin").to_vec()
260}
261
262/// Second of 3 consecutive updates for merge testing
263/// Contains wallet funding transaction
264pub fn update_merge_test_2() -> Vec<u8> {
265    include_bytes!("../test_data/merge_updates/update_merge_2.bin").to_vec()
266}
267
268/// Third of 3 consecutive updates for merge testing
269/// Contains spending transaction
270pub fn update_merge_test_3() -> Vec<u8> {
271    include_bytes!("../test_data/merge_updates/update_merge_3.bin").to_vec()
272}
273
274/// Descriptor used for the merge test updates
275pub fn update_merge_test_descriptor() -> String {
276    include_str!("../test_data/merge_updates/descriptor.txt").to_string()
277}
278
279pub fn update_test_vector_encrypted_bytes() -> Vec<u8> {
280    Vec::<u8>::from_hex(include_str!(
281        "../test_data/update_test_vector_encrypted.hex"
282    ))
283    .unwrap()
284}
285
286pub fn update_test_vector_encrypted_base64() -> String {
287    include_str!("../test_data/update_test_vector/update.base64").to_string()
288}
289
290pub fn update_test_vector_encrypted_bytes2() -> Vec<u8> {
291    include_bytes!("../test_data/update_test_vector/000000000000").to_vec()
292}
293
294pub fn wollet_descriptor_string2() -> String {
295    include_str!("../test_data/update_test_vector/desc").to_string()
296}
297
298pub fn wollet_descriptor_string() -> String {
299    include_str!("../test_data/update_test_vector/desc2").to_string()
300}
301
302pub fn wollet_descriptor_many_transactions() -> &'static str {
303    "ct(slip77(ac53739ddde9fdf6bba3dbc51e989b09aa8c9cdce7b7d7eddd49cec86ddf71f7),elwpkh([93970d14/84'/1'/0']tpubDC3BrFCCjXq4jAceV8k6UACxDDJCFb1eb7R7BiKYUGZdNagEhNfJoYtUrRdci9JFs1meiGGModvmNm8PrqkrEjJ6mpt6gA1DRNU8vu7GqXH/<0;1>/*))#u0y4axgs"
304}
305
306/// A 3 of 5 descriptor and a vector of partially signed transactions to combine 1 sig each
307pub fn psets_to_combine() -> (String, Vec<PartiallySignedTransaction>) {
308    let c = |s: &str| PartiallySignedTransaction::from_str(s).unwrap();
309    let ps = vec![
310        c(include_str!("../test_data/pset_combine/s1_pset.base64")),
311        c(include_str!("../test_data/pset_combine/s2_pset.base64")),
312        c(include_str!("../test_data/pset_combine/s3_pset.base64")),
313        c(include_str!("../test_data/pset_combine/s4_pset.base64")),
314        c(include_str!("../test_data/pset_combine/s5_pset.base64")),
315    ];
316    let d = include_str!("../test_data/pset_combine/desc");
317    (d.to_string(), ps)
318}
319
320pub fn descriptor_pset_usdt_no_contracts() -> &'static str {
321    include_str!("../test_data/pset_usdt/desc")
322}
323
324/// Pset created with descriptor [`descriptor_pset_usdt_no_contracts`] containing mainnet USDt but without contract info
325pub fn pset_usdt_no_contracts() -> &'static str {
326    include_str!("../test_data/pset_usdt/pset_usdt_no_contracts.base64")
327}
328
329pub fn pset_usdt_with_contract() -> &'static str {
330    include_str!("../test_data/pset_usdt/pset_usdt_with_contract.base64")
331}
332
333#[cfg(test)]
334mod test {
335
336    use crate::parse_code_from_markdown;
337
338    #[test]
339    fn test_parse_code_from_markdown() {
340        let mkdown = r#"
341```python
342python
343code
344```
345```rust
346rust
347code
348```
349
350```python
351some more
352python code
353"#;
354        let res = parse_code_from_markdown(mkdown, "python");
355        assert_eq!(
356            res,
357            vec![
358                "python\ncode\n".to_string(),
359                "some more\npython code\n".to_string()
360            ]
361        );
362
363        let res = parse_code_from_markdown(mkdown, "rust");
364        assert_eq!(res, vec!["rust\ncode\n".to_string()])
365    }
366}