Skip to content

Commit 712ffbe

Browse files
committed
Add integration tests from c++ version
Updated to core 0.21
1 parent dd322eb commit 712ffbe

File tree

8 files changed

+479
-5
lines changed

8 files changed

+479
-5
lines changed

.github/workflows/rust.yml

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,14 @@ jobs:
4848
DO_BENCH: true
4949
run: ./contrib/test.sh
5050

51-
Tests:
51+
UnitTests:
5252
name: Tests
5353
runs-on: ubuntu-latest
5454
strategy:
5555
matrix:
5656
rust:
5757
- 1.29.0
5858
- beta
59-
- stable
6059
steps:
6160
- name: Checkout Crate
6261
uses: actions/checkout@v2
@@ -75,3 +74,24 @@ jobs:
7574
cargo update --verbose --package "serde_derive" --precise "1.0.98"
7675
- name: Running cargo
7776
run: ./contrib/test.sh
77+
78+
AllTests:
79+
name: Unit + Int tests
80+
runs-on: ubuntu-latest
81+
strategy:
82+
matrix:
83+
rust:
84+
- stable
85+
steps:
86+
- name: Checkout Crate
87+
uses: actions/checkout@v2
88+
- name: Checkout Toolchain
89+
uses: actions-rs/toolchain@v1
90+
with:
91+
profile: minimal
92+
toolchain: ${{ matrix.rust }}
93+
override: true
94+
- name: Running cargo
95+
env:
96+
BITCOINVERSION: 0.21.0
97+
run: ./contrib/test.sh

integration_test/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[package]
2-
name = "integration_test"
2+
name = "integration_tests_miniscript"
33
version = "0.1.0"
44
authors = ["Steven Roose <steven@stevenroose.org>", "Sanket K <sanket1729@gmail.com>"]
55

integration_test/bitcoin-core-0.20.0.sha256sum

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
7955542df199c6ce4ca0bb3966dcf9cc71199c592fec38508dad58301a3298d0 ./bitcoin-0.21.0/bin/bitcoind

integration_test/random_ms.txt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
and_b(lltvln:after(1231488000),s:pk(03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a))
2+
uuj:and_v(v:multi(2,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a,025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),after(1231488000))
3+
or_b(un:multi(2,03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),al:older(16))
4+
j:and_v(vdv:after(1567547623),older(16))
5+
t:and_v(vu:hash256(H),v:sha256(H))
6+
t:andor(multi(3,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13),v:older(4194305),v:sha256(H))
7+
or_d(multi(1,02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9),or_b(multi(3,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01,032fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a),su:after(500000)))
8+
or_d(sha256(H),and_n(un:after(499999999),older(4194305)))
9+
and_v(or_i(v:multi(2,02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb),v:multi(2,03e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc)),sha256(H))
10+
j:and_b(multi(2,0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),s:or_i(older(1),older(4252898)))
11+
and_b(older(16),s:or_d(sha256(H),n:after(1567547623)))
12+
j:and_v(v:ripemd160(H),or_d(sha256(H),older(16)))
13+
and_b(hash256(H),a:and_b(hash256(H),a:older(1)))
14+
thresh(2,multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),a:multi(1,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),ac:pk_k(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01))
15+
and_n(sha256(H),t:or_i(v:older(4252898),v:older(16)))
16+
or_d(d:and_v(v:older(4252898),v:older(4252898)),sha256(H))
17+
c:and_v(or_c(sha256(H),v:multi(1,02c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db)),pk_k(03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe))
18+
c:and_v(or_c(multi(2,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00,02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),v:ripemd160(H)),pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))
19+
and_v(andor(hash256(H),v:hash256(H),v:older(50000)),after(1231488000))
20+
andor(hash256(H),j:and_v(v:ripemd160(H),older(4194305)),ripemd160(H))
21+
or_i(c:and_v(v:after(500000),pk_k(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),sha256(H))
22+
thresh(2,c:pk_h(025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc),s:sha256(H),a:ripemd160(H))
23+
and_n(sha256(H),uc:and_v(v:older(16),pk_k(03fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ce)))
24+
and_n(c:pk_k(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),and_b(l:older(15),a:older(16)))
25+
c:or_i(and_v(v:older(16),pk_h(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)),pk_h(026a245bf6dc698504c89a20cfded60853152b695336c28063b61c65cbd269e6b4))
26+
or_d(c:pk_h(02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13),andor(c:pk_k(024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),older(2016),after(1567547623)))
27+
c:andor(ripemd160(H),pk_h(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e),and_v(v:hash256(H),pk_h(03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a)))
28+
c:andor(u:ripemd160(H),pk_h(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),or_i(pk_h(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01),pk_h(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)))
29+
c:or_i(andor(c:pk_h(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),pk_h(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01),pk_h(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),pk_k(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e))
30+
multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00)
31+
multi(1,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00)
32+
thresh(2,multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),a:multi(1,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00))
33+
thresh(2,multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),a:multi(1,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),ac:pk_k(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01))
34+
c:pk_k(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01)

integration_test/run.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ killall -9 bitcoind
1010

1111
echo $PATH
1212

13+
BLOCKFILTERARG=""
14+
if bitcoind -version | grep -q "v0\.\(19\|2\)"; then
15+
BLOCKFILTERARG="-blockfilterindex=1"
16+
fi
17+
1318
FALLBACKFEEARG=""
1419
if bitcoind -version | grep -q -e "v0\.2" -e "v2[2-9]"; then
1520
FALLBACKFEEARG="-fallbackfee=0.00001000"

integration_test/src/main.rs

Lines changed: 257 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,262 @@
22
//!
33
//! This is how some external user would use rust-miniscript
44
5+
extern crate bitcoincore_rpc;
6+
extern crate log;
7+
8+
extern crate bitcoin;
9+
extern crate miniscript;
10+
11+
use bitcoincore_rpc::{json, Auth, Client, RpcApi};
12+
13+
use bitcoin::secp256k1;
14+
use bitcoin::util::bip143;
15+
use bitcoin::util::psbt;
16+
use bitcoin::util::psbt::PartiallySignedTransaction as Psbt;
17+
use bitcoin::{Amount, OutPoint, Transaction, TxIn, TxOut, Txid};
18+
mod read_file;
19+
use miniscript::miniscript::iter;
20+
use miniscript::DescriptorTrait;
21+
use miniscript::MiniscriptKey;
22+
use miniscript::{Miniscript, Segwitv0};
23+
use std::collections::BTreeMap;
24+
25+
struct StdLogger;
26+
27+
impl log::Log for StdLogger {
28+
fn enabled(&self, metadata: &log::Metadata) -> bool {
29+
metadata.target().contains("jsonrpc") || metadata.target().contains("bitcoincore_rpc")
30+
}
31+
32+
fn log(&self, record: &log::Record) {
33+
if self.enabled(record.metadata()) {
34+
println!(
35+
"[{}][{}]: {}",
36+
record.level(),
37+
record.metadata().target(),
38+
record.args()
39+
);
40+
}
41+
}
42+
43+
fn flush(&self) {}
44+
}
45+
46+
static LOGGER: StdLogger = StdLogger;
47+
48+
/// Quickly create a BTC amount.
49+
fn btc<F: Into<f64>>(btc: F) -> Amount {
50+
Amount::from_btc(btc.into()).unwrap()
51+
}
52+
53+
fn get_rpc_url() -> String {
54+
return std::env::var("RPC_URL").expect("RPC_URL must be set");
55+
}
56+
57+
fn get_auth() -> bitcoincore_rpc::Auth {
58+
if let Ok(cookie) = std::env::var("RPC_COOKIE") {
59+
return Auth::CookieFile(cookie.into());
60+
} else if let Ok(user) = std::env::var("RPC_USER") {
61+
return Auth::UserPass(user, std::env::var("RPC_PASS").unwrap_or_default());
62+
} else {
63+
panic!("Either RPC_COOKIE or RPC_USER + RPC_PASS must be set.");
64+
};
65+
}
66+
67+
// Find the Outpoint by value.
68+
// Ideally, we should find by scriptPubkey, but this
69+
// works for temp test case
70+
fn get_vout(cl: &Client, txid: Txid, value: u64) -> (OutPoint, TxOut) {
71+
let tx = cl
72+
.get_transaction(&txid, None)
73+
.unwrap()
74+
.transaction()
75+
.unwrap();
76+
for (i, txout) in tx.output.into_iter().enumerate() {
77+
if txout.value == value {
78+
return (OutPoint::new(txid, i as u32), txout);
79+
}
80+
}
81+
unreachable!("Only call get vout on functions which have the expected outpoint");
82+
}
83+
584
fn main() {
6-
()
85+
log::set_logger(&LOGGER)
86+
.map(|()| log::set_max_level(log::LevelFilter::max()))
87+
.unwrap();
88+
89+
let rpc_url = format!("{}/wallet/testwallet", get_rpc_url());
90+
let auth = get_auth();
91+
92+
let cl = Client::new(&rpc_url, auth).unwrap();
93+
94+
// 0.21 does not create default wallet..
95+
cl.create_wallet("testwallet", None, None, None, None)
96+
.unwrap();
97+
98+
let testdata = read_file::TestData::new_fixed_data(50);
99+
let ms_vec = read_file::parse_miniscripts(&testdata.pubdata);
100+
let sks = testdata.secretdata.sks;
101+
let pks = testdata.pubdata.pks;
102+
// Generate some blocks
103+
let blocks = cl
104+
.generate_to_address(500, &cl.get_new_address(None, None).unwrap())
105+
.unwrap();
106+
assert_eq!(blocks.len(), 500);
107+
108+
// Next send some btc to each address corresponding to the miniscript
109+
let mut txids = vec![];
110+
for ms in ms_vec.iter() {
111+
let wsh = miniscript::Descriptor::new_wsh(ms.clone()).unwrap();
112+
let txid = cl
113+
.send_to_address(
114+
&wsh.address(bitcoin::Network::Regtest).unwrap(),
115+
btc(1),
116+
None,
117+
None,
118+
None,
119+
None,
120+
None,
121+
None,
122+
)
123+
.unwrap();
124+
txids.push(txid);
125+
}
126+
// Wait for the funds to mature.
127+
let blocks = cl
128+
.generate_to_address(50, &cl.get_new_address(None, None).unwrap())
129+
.unwrap();
130+
assert_eq!(blocks.len(), 50);
131+
// Create a PSBT for each transaction.
132+
// Spend one input and spend one output for simplicity.
133+
let mut psbts = vec![];
134+
for (ms, txid) in ms_vec.iter().zip(txids) {
135+
let mut psbt = Psbt {
136+
global: psbt::Global {
137+
unsigned_tx: Transaction {
138+
version: 2,
139+
lock_time: 1_603_866_330, // time at 10/28/2020 @ 6:25am (UTC)
140+
input: vec![],
141+
output: vec![],
142+
},
143+
unknown: BTreeMap::new(),
144+
proprietary: BTreeMap::new(),
145+
xpub: BTreeMap::new(),
146+
version: 0,
147+
},
148+
inputs: vec![],
149+
outputs: vec![],
150+
};
151+
// figure out the outpoint from the txid
152+
let (outpoint, witness_utxo) = get_vout(&cl, txid, btc(1.0).as_sat());
153+
let mut txin = TxIn::default();
154+
txin.previous_output = outpoint;
155+
// set the sequence to a non-final number for the locktime transactions to be
156+
// processed correctly.
157+
// We waited 50 blocks, keep 49 for safety
158+
txin.sequence = 49;
159+
psbt.global.unsigned_tx.input.push(txin);
160+
// Get a new script pubkey from the node so that
161+
// the node wallet tracks the receiving transaction
162+
// and we can check it by gettransaction RPC.
163+
let addr = cl
164+
.get_new_address(None, Some(json::AddressType::Bech32))
165+
.unwrap();
166+
psbt.global.unsigned_tx.output.push(TxOut {
167+
value: 99_999_000,
168+
script_pubkey: addr.script_pubkey(),
169+
});
170+
let mut input = psbt::Input::default();
171+
input.witness_utxo = Some(witness_utxo);
172+
input.witness_script = Some(ms.encode());
173+
psbt.inputs.push(input);
174+
psbt.outputs.push(psbt::Output::default());
175+
psbts.push(psbt);
176+
}
177+
178+
let mut spend_txids = vec![];
179+
// Sign the transactions with all keys
180+
// AKA the signer role of psbt
181+
for i in 0..psbts.len() {
182+
// Get all the pubkeys and the corresponding secret keys
183+
let ms: Miniscript<miniscript::bitcoin::PublicKey, Segwitv0> =
184+
Miniscript::parse_insane(psbts[i].inputs[0].witness_script.as_ref().unwrap()).unwrap();
185+
186+
let sks_reqd: Vec<_> = ms
187+
.iter_pk_pkh()
188+
.map(|pk_pkh| match pk_pkh {
189+
iter::PkPkh::PlainPubkey(pk) => sks[pks.iter().position(|&x| x == pk).unwrap()],
190+
iter::PkPkh::HashedPubkey(hash) => {
191+
sks[pks
192+
.iter()
193+
.position(|&pk| pk.to_pubkeyhash() == hash)
194+
.unwrap()]
195+
}
196+
})
197+
.collect();
198+
// Get the required sighash message
199+
let amt = btc(1).as_sat();
200+
let mut sighash_cache = bip143::SigHashCache::new(&psbts[i].global.unsigned_tx);
201+
let sighash_ty = bitcoin::SigHashType::All;
202+
let sighash = sighash_cache.signature_hash(0, &ms.encode(), amt, sighash_ty);
203+
204+
// requires both signing and verification because we check the tx
205+
// after we psbt extract it
206+
let secp = secp256k1::Secp256k1::new();
207+
let msg = secp256k1::Message::from_slice(&sighash[..]).unwrap();
208+
209+
// Finally construct the signature and add to psbt
210+
for sk in sks_reqd {
211+
let sig = secp.sign(&msg, &sk);
212+
let pk = pks[sks.iter().position(|&x| x == sk).unwrap()];
213+
let mut sig = sig.serialize_der().to_vec();
214+
sig.push(0x01u8); //sighash all flag
215+
psbts[i].inputs[0].partial_sigs.insert(pk, sig);
216+
}
217+
// Add the hash preimages to the psbt
218+
psbts[i].inputs[0].sha256_preimages.insert(
219+
testdata.pubdata.sha256,
220+
testdata.secretdata.sha256_pre.to_vec(),
221+
);
222+
psbts[i].inputs[0].hash256_preimages.insert(
223+
testdata.pubdata.hash256,
224+
testdata.secretdata.hash256_pre.to_vec(),
225+
);
226+
println!("{}", ms);
227+
psbts[i].inputs[0].hash160_preimages.insert(
228+
testdata.pubdata.hash160,
229+
testdata.secretdata.hash160_pre.to_vec(),
230+
);
231+
psbts[i].inputs[0].ripemd160_preimages.insert(
232+
testdata.pubdata.ripemd160,
233+
testdata.secretdata.ripemd160_pre.to_vec(),
234+
);
235+
// Finalize the transaction using psbt
236+
// Let miniscript do it's magic!
237+
if let Err(e) = miniscript::psbt::finalize_mall(&mut psbts[i], &secp) {
238+
// All miniscripts should satisfy
239+
panic!("Could not satisfy: error{} ms:{} at ind:{}", e, ms, i);
240+
} else {
241+
let tx = miniscript::psbt::extract(&psbts[i], &secp).unwrap();
242+
243+
// Send the transactions to bitcoin node for mining.
244+
// Regtest mode has standardness checks
245+
// Check whether the node accepts the transactions
246+
let txid = cl
247+
.send_raw_transaction(&tx)
248+
.expect(&format!("{} send tx failed for ms {}", i, ms));
249+
spend_txids.push(txid);
250+
}
251+
}
252+
// Finally mine the blocks and await confirmations
253+
let _blocks = cl
254+
.generate_to_address(10, &cl.get_new_address(None, None).unwrap())
255+
.unwrap();
256+
// Get the required transactions from the node mined in the blocks.
257+
for txid in spend_txids {
258+
// Check whether the transaction is mined in blocks
259+
// Assert that the confirmations are > 0.
260+
let num_conf = cl.get_transaction(&txid, None).unwrap().info.confirmations;
261+
assert!(num_conf > 0);
262+
}
7263
}

0 commit comments

Comments
 (0)