Skip to content

Commit 0a3a79e

Browse files
committed
Add integration tests
1 parent 97f16a9 commit 0a3a79e

File tree

1 file changed

+262
-0
lines changed

1 file changed

+262
-0
lines changed

tests/test_arith.rs

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
//! # rust-miniscript integration test
2+
//!
3+
//! Arith expression fragment integration tests
4+
//!
5+
6+
use elements::pset::PartiallySignedTransaction as Psbt;
7+
use elements::sighash::SigHashCache;
8+
use elements::taproot::{LeafVersion, TapLeafHash};
9+
use elements::{
10+
self, confidential, pset as psbt, secp256k1_zkp as secp256k1, sighash, OutPoint, Script, TxIn,
11+
TxOut, Txid,
12+
};
13+
use elementsd::ElementsD;
14+
use miniscript::miniscript::iter;
15+
use miniscript::psbt::PsbtInputExt;
16+
use miniscript::{Descriptor, MiniscriptKey, ToPublicKey};
17+
use rand::RngCore;
18+
mod setup;
19+
use miniscript::descriptor::CovSatisfier;
20+
use miniscript::psbt::PsbtInputSatisfier;
21+
use setup::test_util::{self, TestData, PARAMS};
22+
use setup::Call;
23+
use {actual_rand as rand, elements_miniscript as miniscript};
24+
25+
// Find the Outpoint by value.
26+
// Ideally, we should find by scriptPubkey, but this
27+
// works for temp test case
28+
fn get_vout(cl: &ElementsD, txid: Txid, value: u64, spk: Script) -> (OutPoint, TxOut) {
29+
let tx = cl.get_transaction(&txid);
30+
for (i, txout) in tx.output.into_iter().enumerate() {
31+
if txout.value == confidential::Value::Explicit(value) && txout.script_pubkey == spk {
32+
return (OutPoint::new(txid, i as u32), txout);
33+
}
34+
}
35+
unreachable!("Only call get vout on functions which have the expected outpoint");
36+
}
37+
38+
pub fn test_desc_satisfy(cl: &ElementsD, testdata: &TestData, desc: &str) -> Vec<Vec<u8>> {
39+
/* Convert desc into elements one by adding a prefix*/
40+
let desc = format!("el{}", desc);
41+
//
42+
let secp = secp256k1::Secp256k1::new();
43+
let xonly_keypairs = &testdata.secretdata.x_only_keypairs;
44+
let x_only_pks = &testdata.pubdata.x_only_pks;
45+
// Generate some blocks
46+
cl.generate(1);
47+
48+
let desc = test_util::parse_test_desc(&desc, &testdata.pubdata);
49+
let derived_desc = desc.derived_descriptor(&secp, 0).unwrap();
50+
// Next send some btc to each address corresponding to the miniscript
51+
let txid = cl.send_to_address(
52+
&derived_desc.address(&PARAMS).unwrap(), // No blinding
53+
"1", // 1 BTC
54+
);
55+
// Wait for the funds to mature.
56+
cl.generate(2);
57+
// Create a PSBT for each transaction.
58+
// Spend one input and spend one output for simplicity.
59+
let mut psbt = Psbt::new_v2();
60+
// figure out the outpoint from the txid
61+
let (outpoint, witness_utxo) = get_vout(&cl, txid, 100_000_000, derived_desc.script_pubkey());
62+
let txin = TxIn {
63+
previous_output: outpoint,
64+
is_pegin: false,
65+
has_issuance: false,
66+
script_sig: Script::new(),
67+
sequence: 1,
68+
asset_issuance: Default::default(),
69+
witness: Default::default(),
70+
};
71+
psbt.add_input(psbt::Input::from_txin(txin));
72+
// Get a new script pubkey from the node so that
73+
// the node wallet tracks the receiving transaction
74+
// and we can check it by gettransaction RPC.
75+
let addr = cl.get_new_address();
76+
let out = TxOut {
77+
// Had to decrease 'value', so that fees can be increased
78+
// (Was getting insufficient fees error, for deep script trees)
79+
value: confidential::Value::Explicit(99_997_000),
80+
script_pubkey: addr.script_pubkey(),
81+
asset: witness_utxo.asset,
82+
nonce: confidential::Nonce::Null,
83+
witness: Default::default(),
84+
};
85+
psbt.add_output(psbt::Output::from_txout(out));
86+
// ELEMENTS: Add fee output
87+
let fee_out = TxOut::new_fee(3_000, witness_utxo.asset.explicit().unwrap());
88+
psbt.add_output(psbt::Output::from_txout(fee_out));
89+
90+
psbt.inputs_mut()[0]
91+
.update_with_descriptor_unchecked(&desc)
92+
.unwrap();
93+
psbt.inputs_mut()[0].witness_utxo = Some(witness_utxo.clone());
94+
95+
// --------------------------------------------
96+
// Sign the transactions with all keys
97+
// AKA the signer role of psbt
98+
// Get all the pubkeys and the corresponding secret keys
99+
100+
let unsigned_tx = &psbt.extract_tx().unwrap();
101+
let mut sighash_cache = SigHashCache::new(unsigned_tx);
102+
match derived_desc {
103+
Descriptor::TrExt(ref tr) => {
104+
let hash_ty = sighash::SchnorrSigHashType::Default;
105+
106+
let prevouts = [witness_utxo.clone()];
107+
let prevouts = sighash::Prevouts::All(&prevouts);
108+
// ------------------ script spend -------------
109+
let x_only_keypairs_reqd: Vec<(secp256k1::KeyPair, TapLeafHash)> = tr
110+
.iter_scripts()
111+
.flat_map(|(_depth, ms)| {
112+
let leaf_hash = TapLeafHash::from_script(&ms.encode(), LeafVersion::default());
113+
ms.iter_pk_pkh().filter_map(move |pk_pkh| match pk_pkh {
114+
iter::PkPkh::PlainPubkey(pk) => {
115+
let i = x_only_pks.iter().position(|&x| x.to_public_key() == pk);
116+
i.map(|idx| (xonly_keypairs[idx].clone(), leaf_hash))
117+
}
118+
iter::PkPkh::HashedPubkey(hash) => {
119+
let i = x_only_pks
120+
.iter()
121+
.position(|&x| x.to_public_key().to_pubkeyhash() == hash);
122+
i.map(|idx| (xonly_keypairs[idx].clone(), leaf_hash))
123+
}
124+
})
125+
})
126+
.collect();
127+
for (keypair, leaf_hash) in x_only_keypairs_reqd {
128+
let sighash_msg = sighash_cache
129+
.taproot_script_spend_signature_hash(
130+
0,
131+
&prevouts,
132+
leaf_hash,
133+
hash_ty,
134+
testdata.pubdata.genesis_hash,
135+
)
136+
.unwrap();
137+
let msg = secp256k1::Message::from_slice(&sighash_msg[..]).unwrap();
138+
let mut aux_rand = [0u8; 32];
139+
rand::thread_rng().fill_bytes(&mut aux_rand);
140+
let sig = secp.sign_schnorr_with_aux_rand(&msg, &keypair, &aux_rand);
141+
// FIXME: uncomment when == is supported for secp256k1::KeyPair. (next major release)
142+
// let x_only_pk = pks[xonly_keypairs.iter().position(|&x| x == keypair).unwrap()];
143+
// Just recalc public key
144+
let x_only_pk = secp256k1::XOnlyPublicKey::from_keypair(&keypair);
145+
psbt.inputs_mut()[0].tap_script_sigs.insert(
146+
(x_only_pk, leaf_hash),
147+
elements::SchnorrSig {
148+
sig,
149+
hash_ty: hash_ty,
150+
},
151+
);
152+
}
153+
}
154+
_ => {
155+
// Non-tr descriptors
156+
panic!("Only testing Tr covenant descriptor")
157+
}
158+
}
159+
// Add the hash preimages to the psbt
160+
psbt.inputs_mut()[0].sha256_preimages.insert(
161+
testdata.pubdata.sha256,
162+
testdata.secretdata.sha256_pre.to_vec(),
163+
);
164+
println!("Testing descriptor: {}", desc);
165+
// Finalize the transaction using psbt
166+
// Let miniscript do it's magic!
167+
let psbt_sat = PsbtInputSatisfier::new(&psbt, 0);
168+
let mut tx = psbt.extract_tx().unwrap();
169+
let utxos = [witness_utxo];
170+
let unsigned_tx = &tx.clone();
171+
let cov_sat = CovSatisfier::new_taproot(unsigned_tx, &utxos, 0);
172+
173+
derived_desc
174+
.satisfy(&mut tx.input[0], (psbt_sat, cov_sat))
175+
.expect("Satisfaction error");
176+
177+
// Send the transactions to bitcoin node for mining.
178+
// Regtest mode has standardness checks
179+
// Check whether the node accepts the transactions
180+
let txid = cl.send_raw_transaction(&tx);
181+
182+
// Finally mine the blocks and await confirmations
183+
let _blocks = cl.generate(1);
184+
// Get the required transactions from the node mined in the blocks.
185+
// Check whether the transaction is mined in blocks
186+
// Assert that the confirmations are > 0.
187+
let num_conf = cl.call("gettransaction", &[txid.to_string().into()])["confirmations"]
188+
.as_u64()
189+
.unwrap();
190+
assert!(num_conf > 0);
191+
tx.input[0].witness.script_witness.clone()
192+
}
193+
194+
fn test_descs(cl: &ElementsD, testdata: &TestData) {
195+
// K : Compressed key available
196+
// K!: Compressed key with corresponding secret key unknown
197+
// X: X-only key available
198+
// X!: X-only key with corresponding secret key unknown
199+
200+
// Test 1: Simple spend with internal key
201+
let wit = test_desc_satisfy(cl, testdata, "tr(X!,and_v(v:pk(X1),num_eq(8,8)))");
202+
assert!(wit.len() == 3);
203+
204+
let wit = test_desc_satisfy(cl, testdata, "tr(X!,and_v(v:pk(X1),geq(9,8)))");
205+
assert!(wit.len() == 3);
206+
207+
let wit = test_desc_satisfy(cl, testdata, "tr(X!,and_v(v:pk(X1),ge(9,8)))");
208+
assert!(wit.len() == 3);
209+
210+
test_desc_satisfy(
211+
cl,
212+
testdata,
213+
"tr(X!,and_v(v:pk(X1),ge(9223372036854775807,9223372036854775806)))",
214+
);
215+
216+
test_desc_satisfy(
217+
cl,
218+
testdata,
219+
"tr(X!,and_v(v:pk(X1),num_eq(inp_v(0),100000000)))",
220+
);
221+
test_desc_satisfy(
222+
cl,
223+
testdata,
224+
"tr(X!,and_v(v:pk(X1),num_eq(out_v(0),99997000)))",
225+
);
226+
test_desc_satisfy(cl, testdata, "tr(X!,and_v(v:pk(X1),num_eq(out_v(1),3000)))");
227+
test_desc_satisfy(
228+
cl,
229+
testdata,
230+
"tr(X!,and_v(v:pk(X1),num_eq(inp_v(0),add(out_v(0),out_v(1)))))",
231+
);
232+
test_desc_satisfy(cl, testdata, "tr(X!,and_v(v:pk(X1),leq(-10,-10)))");
233+
test_desc_satisfy(cl, testdata, "tr(X!,and_v(v:pk(X1),leq(-10,-10)))");
234+
235+
test_desc_satisfy(
236+
cl,
237+
testdata,
238+
"tr(X!,and_v(v:pk(X1),num_eq(add(6,2),mul(2,4))))",
239+
);
240+
test_desc_satisfy(
241+
cl,
242+
testdata,
243+
"tr(X!,and_v(v:pk(X1),num_eq(sub(3,3),div(0,9))))",
244+
);
245+
test_desc_satisfy(cl, testdata, "tr(X!,and_v(v:pk(X1),num_eq(mod(9,3),0)))");
246+
test_desc_satisfy(
247+
cl,
248+
testdata,
249+
"tr(X!,and_v(v:pk(X1),num_eq(bitand(0,134),0)))",
250+
);
251+
test_desc_satisfy(cl, testdata, "tr(X!,and_v(v:pk(X1),num_eq(bitor(1,3),3)))");
252+
test_desc_satisfy(cl, testdata, "tr(X!,and_v(v:pk(X1),num_eq(bitxor(1,3),2)))");
253+
test_desc_satisfy(cl, testdata, "tr(X!,and_v(v:pk(X1),num_eq(bitinv(0),-1)))");
254+
test_desc_satisfy(cl, testdata, "tr(X!,and_v(v:pk(X1),num_eq(neg(1),-1)))");
255+
}
256+
257+
#[test]
258+
fn test_arith() {
259+
let (cl, _, genesis_hash) = &setup::setup(false);
260+
let testdata = TestData::new_fixed_data(50, *genesis_hash);
261+
test_descs(cl, &testdata);
262+
}

0 commit comments

Comments
 (0)