Skip to content

Commit ac639f6

Browse files
committed
Add support for writing integration tests that fail
1 parent c80aa7c commit ac639f6

File tree

2 files changed

+62
-29
lines changed

2 files changed

+62
-29
lines changed

tests/setup/test_util.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
2525
use bitcoin::secp256k1;
2626
use miniscript::descriptor::{SinglePub, SinglePubKey};
2727
use miniscript::{
28-
Descriptor, DescriptorPublicKey, Miniscript, ScriptContext, TranslatePk, Translator,
28+
Descriptor, DescriptorPublicKey, Error, Miniscript, ScriptContext, TranslatePk, Translator,
2929
};
3030
use rand::RngCore;
3131

@@ -248,13 +248,15 @@ impl<'a> Translator<String, DescriptorPublicKey, ()> for StrTranslatorLoose<'a>
248248

249249
#[allow(dead_code)]
250250
// https://github.com/rust-lang/rust/issues/46379. The code is pub fn and integration test, but still shows warnings
251-
pub fn parse_test_desc(desc: &str, pubdata: &PubData) -> Descriptor<DescriptorPublicKey> {
251+
pub fn parse_test_desc(
252+
desc: &str,
253+
pubdata: &PubData,
254+
) -> Result<Descriptor<DescriptorPublicKey>, Error> {
252255
let desc = subs_hash_frag(desc, pubdata);
253-
let desc =
254-
Descriptor::<String>::from_str(&desc).expect("only parsing valid and sane descriptors");
256+
let desc = Descriptor::<String>::from_str(&desc)?;
255257
let mut translator = StrDescPubKeyTranslator(0, pubdata);
256258
let desc: Result<_, ()> = desc.translate_pk(&mut translator);
257-
desc.expect("Translate must succeed")
259+
Ok(desc.expect("Translate must succeed"))
258260
}
259261

260262
// substitute hash fragments in the string as the per rules

tests/test_desc.rs

Lines changed: 55 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
//!
66
77
use std::collections::BTreeMap;
8+
use std::{error, fmt};
89

910
use actual_rand as rand;
1011
use bitcoin::blockdata::witness::Witness;
@@ -18,8 +19,9 @@ use bitcoin::{
1819
use bitcoind::bitcoincore_rpc::{json, Client, RpcApi};
1920
use miniscript::miniscript::iter;
2021
use miniscript::psbt::{PsbtExt, PsbtInputExt};
21-
use miniscript::{Descriptor, Miniscript, MiniscriptKey, ScriptContext, ToPublicKey};
22+
use miniscript::{Descriptor, Error, Miniscript, MiniscriptKey, ScriptContext, ToPublicKey};
2223
mod setup;
24+
// use crate::test_util::{self, TestData};
2325

2426
use rand::RngCore;
2527
use setup::test_util::{self, TestData};
@@ -43,7 +45,31 @@ fn get_vout(cl: &Client, txid: Txid, value: u64, spk: Script) -> (OutPoint, TxOu
4345
unreachable!("Only call get vout on functions which have the expected outpoint");
4446
}
4547

46-
pub fn test_desc_satisfy(cl: &Client, testdata: &TestData, desc: &str) -> Witness {
48+
// Currently this is not being used, since miniscript::Error is being propagated
49+
// But if the types of errors grow in future, we can extend this enum and use it.
50+
#[derive(Debug, PartialEq)]
51+
pub enum DescError {
52+
/// PSBT was not able to finalize
53+
PsbtFinalizeError,
54+
InvalidDescriptor,
55+
}
56+
57+
impl fmt::Display for DescError {
58+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59+
match *self {
60+
DescError::PsbtFinalizeError => f.write_str("PSBT was not able to finalize"),
61+
DescError::InvalidDescriptor => f.write_str("Invalid descriptor"),
62+
}
63+
}
64+
}
65+
66+
impl error::Error for DescError {}
67+
68+
pub fn test_desc_satisfy(
69+
cl: &Client,
70+
testdata: &TestData,
71+
descriptor: &str,
72+
) -> Result<Witness, DescError> {
4773
let secp = secp256k1::Secp256k1::new();
4874
let sks = &testdata.secretdata.sks;
4975
let xonly_keypairs = &testdata.secretdata.x_only_keypairs;
@@ -55,20 +81,29 @@ pub fn test_desc_satisfy(cl: &Client, testdata: &TestData, desc: &str) -> Witnes
5581
.unwrap();
5682
assert_eq!(blocks.len(), 1);
5783

58-
let desc = test_util::parse_test_desc(&desc, &testdata.pubdata);
84+
let mut desc;
85+
let desc_res = test_util::parse_test_desc(&descriptor, &testdata.pubdata);
86+
match desc_res {
87+
Ok(temp_desc) => {
88+
desc = temp_desc;
89+
}
90+
Err(_) => {
91+
return Err(DescError::InvalidDescriptor);
92+
}
93+
}
5994
let derived_desc = desc.derived_descriptor(&secp, 0).unwrap();
6095
// Next send some btc to each address corresponding to the miniscript
96+
let mut rest;
97+
match derived_desc.address(bitcoin::Network::Regtest) {
98+
Ok(addr) => {
99+
rest = addr;
100+
}
101+
Err(_) => {
102+
return Err(DescError::InvalidDescriptor);
103+
}
104+
}
61105
let txid = cl
62-
.send_to_address(
63-
&derived_desc.address(bitcoin::Network::Regtest).unwrap(),
64-
btc(1),
65-
None,
66-
None,
67-
None,
68-
None,
69-
None,
70-
None,
71-
)
106+
.send_to_address(&rest, btc(1), None, None, None, None, None, None)
72107
.unwrap();
73108
// Wait for the funds to mature.
74109
let blocks = cl
@@ -274,11 +309,7 @@ pub fn test_desc_satisfy(cl: &Client, testdata: &TestData, desc: &str) -> Witnes
274309
// Finalize the transaction using psbt
275310
// Let miniscript do it's magic!
276311
if let Err(e) = psbt.finalize_mut(&secp) {
277-
// All miniscripts should satisfy
278-
panic!(
279-
"Could not satisfy non-malleably: error{} desc:{} ",
280-
e[0], desc
281-
);
312+
return Err(DescError::PsbtFinalizeError);
282313
}
283314
let tx = psbt.extract(&secp).expect("Extraction error");
284315

@@ -298,7 +329,7 @@ pub fn test_desc_satisfy(cl: &Client, testdata: &TestData, desc: &str) -> Witnes
298329
// Assert that the confirmations are > 0.
299330
let num_conf = cl.get_transaction(&txid, None).unwrap().info.confirmations;
300331
assert!(num_conf > 0);
301-
tx.input[0].witness.clone()
332+
return Ok(tx.input[0].witness.clone());
302333
}
303334

304335
// Find all secret corresponding to the known public keys in ms
@@ -340,30 +371,30 @@ fn test_descs(cl: &Client, testdata: &TestData) {
340371
// X!: X-only key with corresponding secret key unknown
341372

342373
// Test 1: Simple spend with internal key
343-
let wit = test_desc_satisfy(cl, testdata, "tr(X)");
374+
let wit = test_desc_satisfy(cl, testdata, "tr(X)").unwrap();
344375
assert!(wit.len() == 1);
345376

346377
// Test 2: Same as above, but with leaves
347-
let wit = test_desc_satisfy(cl, testdata, "tr(X,{pk(X1!),pk(X2!)})");
378+
let wit = test_desc_satisfy(cl, testdata, "tr(X,{pk(X1!),pk(X2!)})").unwrap();
348379
assert!(wit.len() == 1);
349380

350381
// Test 3: Force to spend with script spend. Unknown internal key and only one known script path
351382
// X! -> Internal key unknown
352383
// Leaf 1 -> pk(X1) with X1 known
353384
// Leaf 2-> and_v(v:pk(X2),pk(X3!)) with partial witness only to X2 known
354-
let wit = test_desc_satisfy(cl, testdata, "tr(X!,{pk(X1),and_v(v:pk(X2),pk(X3!))})");
385+
let wit = test_desc_satisfy(cl, testdata, "tr(X!,{pk(X1),and_v(v:pk(X2),pk(X3!))})").unwrap();
355386
assert!(wit.len() == 3); // control block, script and signature
356387

357388
// Test 4: Force to spend with script spend. Unknown internal key and multiple script paths
358389
// Should select the one with minimum weight
359390
// X! -> Internal key unknown
360391
// Leaf 1 -> pk(X1!) with X1 unknown
361392
// Leaf 2-> and_v(v:pk(X2),pk(X3)) X2 and X3 known
362-
let wit = test_desc_satisfy(cl, testdata, "tr(X!,{pk(X1),and_v(v:pk(X2),pk(X3))})");
393+
let wit = test_desc_satisfy(cl, testdata, "tr(X!,{pk(X1),and_v(v:pk(X2),pk(X3))})").unwrap();
363394
assert!(wit.len() == 3); // control block, script and one signatures
364395

365396
// Test 5: When everything is available, we should select the key spend path
366-
let wit = test_desc_satisfy(cl, testdata, "tr(X,{pk(X1),and_v(v:pk(X2),pk(X3!))})");
397+
let wit = test_desc_satisfy(cl, testdata, "tr(X,{pk(X1),and_v(v:pk(X2),pk(X3!))})").unwrap();
367398
assert!(wit.len() == 1); // control block, script and signature
368399

369400
// Test 6: Test the new multi_a opcodes

0 commit comments

Comments
 (0)