Skip to content

Clean up the examples #387

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions examples/htlc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use miniscript::DescriptorTrait;
use std::str::FromStr;

fn main() {
//HTLC policy with 10:1 odds for happy(co-operative) case compared to uncooperative case
// HTLC policy with 10:1 odds for happy (co-operative) case compared to uncooperative case.
let htlc_policy = Concrete::<bitcoin::PublicKey>::from_str(&format!("or(10@and(sha256({secret_hash}),pk({redeem_identity})),1@and(older({expiry}),pk({refund_identity})))",
secret_hash = "1111111111111111111111111111111111111111111111111111111111111111",
redeem_identity = "022222222222222222222222222222222222222222222222222222222222222222",
Expand All @@ -39,31 +39,34 @@ fn main() {
)
.expect("Resource limits");

// Check whether the descriptor is safe
// This checks whether all spend paths are accessible in bitcoin network.
// It maybe possible that some of the spend require more than 100 elements in Wsh scripts
// Or they contain a combination of timelock and heightlock.
// Check whether the descriptor is safe. This checks whether all spend paths are accessible in
// the Bitcoin network. It may be possible that some of the spend paths require more than 100
// elements in Wsh scripts or they contain a combination of timelock and heightlock.
assert!(htlc_descriptor.sanity_check().is_ok());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would really like to get rid of these sanity checks. I think we lose of using rust if users have to call sanity check to double-check things. This feels like CPubKey.isVliad() type stuff

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think they don't belong in the example. They'd be useful for users who are manually constructing the Descriptor object I think.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be doing the sanity checks in all the constructors (incl. FromStr) instead?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tcharding yes, but we can't do it in the "implicit" constructor where the user just constructs the enum type themselves.

assert_eq!(
format!("{}", htlc_descriptor),
"wsh(andor(pk(022222222222222222222222222222222222222222222222222222222222222222),sha256(1111111111111111111111111111111111111111111111111111111111111111),and_v(v:pkh(51814f108670aced2d77c1805ddd6634bc9d4731),older(4444))))#s0qq76ng"
);

// Lift the descriptor into an abstract policy.
assert_eq!(
format!("{}", htlc_descriptor.lift().unwrap()),
"or(and(pkh(4377a5acd66dc5cb67148a24818d1e51fa183bd2),sha256(1111111111111111111111111111111111111111111111111111111111111111)),and(pkh(51814f108670aced2d77c1805ddd6634bc9d4731),older(4444)))"
);

// Get the scriptPpubkey for this Wsh descriptor.
assert_eq!(
format!("{:x}", htlc_descriptor.spk()),
"0020d853877af928a8d2a569c9c0ed14bd16f6a80ce9cccaf8a6150fd8f7f8867ae2"
);

// Encode the Wsh descriptor into a Bitcoin script.
assert_eq!(
format!("{:x}", htlc_descriptor.inner_script()),
"21022222222222222222222222222222222222222222222222222222222222222222ac6476a91451814f108670aced2d77c1805ddd6634bc9d473188ad025c11b26782012088a82011111111111111111111111111111111111111111111111111111111111111118768"
);

// Get the address for this Wsh descriptor.v
assert_eq!(
format!("{}", htlc_descriptor.address(Network::Bitcoin).unwrap()),
"bc1qmpfcw7he9z5d9ftfe8qw699azmm2sr8fen903fs4plv007yx0t3qxfmqv5"
Expand Down
37 changes: 16 additions & 21 deletions examples/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//

//! Example: Parsing a descriptor from a string
//! Example: Parsing a descriptor from a string.

use bitcoin;
use miniscript;
Expand All @@ -21,54 +21,49 @@ use miniscript::{descriptor::DescriptorType, Descriptor, DescriptorTrait};
use std::str::FromStr;

fn main() {
let my_descriptor = miniscript::Descriptor::<bitcoin::PublicKey>::from_str(
let desc = miniscript::Descriptor::<bitcoin::PublicKey>::from_str(
"wsh(c:pk_k(020202020202020202020202020202020202020202020202020202020202020202))",
)
.unwrap();

// Check whether the descriptor is safe
// This checks whether all spend paths are accessible in bitcoin network.
// It maybe possible that some of the spend require more than 100 elements in Wsh scripts
// Or they contain a combination of timelock and heightlock.
assert!(my_descriptor.sanity_check().is_ok());
// Check whether the descriptor is safe. This checks whether all spend paths are accessible in
// the Bitcoin network. It may be possible that some of the spend paths require more than 100
// elements in Wsh scripts or they contain a combination of timelock and heightlock.
assert!(desc.sanity_check().is_ok());

// Compute the script pubkey. As mentioned in the documentation, script_pubkey only fails
// for Tr descriptors that don't have some pre-computed data
// for Tr descriptors that don't have some pre-computed data.
assert_eq!(
format!("{:x}", my_descriptor.script_pubkey()),
format!("{:x}", desc.script_pubkey()),
"0020daef16dd7c946a3e735a6e43310cb2ce33dfd14a04f76bf8241a16654cb2f0f9"
);

// Another way to compute script pubkey
// We can also compute the type of descriptor
let desc_type = my_descriptor.desc_type();
// As another way to compute script pubkey; we can also compute the type of the descriptor.
let desc_type = desc.desc_type();
assert_eq!(desc_type, DescriptorType::Wsh);
// Since we know the type of descriptor, we can get the Wsh struct from Descriptor
// This allows us to call infallible methods for getting script pubkey
if let Descriptor::Wsh(wsh) = &my_descriptor {
// Since we know the type of descriptor, we can get the Wsh struct from Descriptor. This allows
// us to call infallible methods for getting script pubkey.
if let Descriptor::Wsh(wsh) = &desc {
assert_eq!(
format!("{:x}", wsh.spk()),
"0020daef16dd7c946a3e735a6e43310cb2ce33dfd14a04f76bf8241a16654cb2f0f9"
);
} else {
// We checked for the descriptor type earlier
}

// Get the inner script inside the descriptor
// Get the inner script inside the descriptor.
assert_eq!(
format!(
"{:x}",
my_descriptor
.explicit_script()
desc.explicit_script()
.expect("Wsh descriptors have inner scripts")
),
"21020202020202020202020202020202020202020202020202020202020202020202ac"
);

// In a similar fashion we can parse a wrapped segwit script.
let desc = miniscript::Descriptor::<bitcoin::PublicKey>::from_str(
"sh(wsh(c:pk_k(020202020202020202020202020202020202020202020202020202020202020202)))",
)
.unwrap();

assert!(desc.desc_type() == DescriptorType::ShWsh);
}
160 changes: 82 additions & 78 deletions examples/sign_multisig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,87 @@
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//

//! Example: Signing a 2-of-3 multisignature
//! Example: Signing a 2-of-3 multisignature.

use bitcoin;
use miniscript;

use bitcoin::blockdata::witness::Witness;
use bitcoin::secp256k1; // secp256k1 re-exported from rust-bitcoin
use bitcoin::secp256k1;
use miniscript::DescriptorTrait;
use std::collections::HashMap;
use std::str::FromStr;

fn main() {
// Avoid repeatedly typing a pretty-common descriptor type
type BitcoinDescriptor = miniscript::Descriptor<bitcoin::PublicKey>;
let mut tx = spending_transaction();
let pks = list_of_three_arbitrary_public_keys();
let sig = random_signature_from_the_blockchain();

// Transaction which spends some output
let mut tx = bitcoin::Transaction {
// Descriptor for the output being spent.
let s = format!("wsh(multi(2,{},{},{}))", pks[0], pks[1], pks[2],);
let descriptor = miniscript::Descriptor::<bitcoin::PublicKey>::from_str(&s).unwrap();

// Check weight for witness satisfaction cost ahead of time.
// 4 (scriptSig length of 0) + 1 (witness stack size) + 106 (serialized witnessScript)
// + 73*2 (signature length + signatures + sighash bytes) + 1 (dummy byte) = 258
assert_eq!(descriptor.max_satisfaction_weight().unwrap(), 258);

// Sometimes it is necessary to have additional information to get the
// `bitcoin::PublicKey` from the `MiniscriptKey` which can be supplied by
// the `to_pk_ctx` parameter. For example, when calculating the script
// pubkey of a descriptor with xpubs, the secp context and child information
// maybe required.

// Observe the script properties, just for fun.
assert_eq!(
format!("{:x}", descriptor.script_pubkey()),
"00200ed49b334a12c37f3df8a2974ad91ff95029215a2b53f78155be737907f06163"
);

assert_eq!(
format!(
"{:x}",
descriptor
.explicit_script()
.expect("wsh descriptors have unique inner script")
),
"52\
21020202020202020202020202020202020202020202020202020202020202020202\
21020102030405060708010203040506070801020304050607080000000000000000\
21030102030405060708010203040506070801020304050607080000000000000000\
53ae"
);

// Attempt to satisfy at age 0, height 0.
let original_txin = tx.input[0].clone();

let mut sigs = HashMap::<bitcoin::PublicKey, miniscript::bitcoin::EcdsaSig>::new();

// Doesn't work with no signatures.
assert!(descriptor.satisfy(&mut tx.input[0], &sigs).is_err());
assert_eq!(tx.input[0], original_txin);

// ...or one signature...
sigs.insert(pks[1], sig);
assert!(descriptor.satisfy(&mut tx.input[0], &sigs).is_err());
assert_eq!(tx.input[0], original_txin);

// ...but two signatures is ok.
sigs.insert(pks[2], sig);
assert!(descriptor.satisfy(&mut tx.input[0], &sigs).is_ok());
assert_ne!(tx.input[0], original_txin);
assert_eq!(tx.input[0].witness.len(), 4); // 0, sig, sig, witness script

// ...and even if we give it a third signature, only two are used.
sigs.insert(pks[0], sig);
assert!(descriptor.satisfy(&mut tx.input[0], &sigs).is_ok());
assert_ne!(tx.input[0], original_txin);
assert_eq!(tx.input[0].witness.len(), 4); // 0, sig, sig, witness script
}

// Transaction which spends some output.
fn spending_transaction() -> bitcoin::Transaction {
bitcoin::Transaction {
version: 2,
lock_time: 0,
input: vec![bitcoin::TxIn {
Expand All @@ -41,10 +105,12 @@ fn main() {
script_pubkey: bitcoin::Script::new(),
value: 100_000_000,
}],
};
}
}

fn list_of_three_arbitrary_public_keys() -> Vec<bitcoin::PublicKey> {
#[cfg_attr(feature="cargo-fmt", rustfmt_skip)]
let public_keys = vec![
vec![
bitcoin::PublicKey::from_slice(&[2; 33]).expect("key 1"),
bitcoin::PublicKey::from_slice(&[
0x02,
Expand All @@ -60,10 +126,13 @@ fn main() {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]).expect("key 3"),
];
let bitcoin_sig = bitcoin::EcdsaSig {
// copied at random off the blockchain; this is not actually a valid
// signature for this transaction; Miniscript does not verify
]
}

// Returns a signature copied at random off the blockchain; this is not actually
// a valid signature for this transaction; Miniscript does not verify the validity.
fn random_signature_from_the_blockchain() -> bitcoin::EcdsaSig {
bitcoin::EcdsaSig {
sig: secp256k1::ecdsa::Signature::from_str(
"3045\
0221\
Expand All @@ -73,70 +142,5 @@ fn main() {
)
.unwrap(),
hash_ty: bitcoin::EcdsaSighashType::All,
};

let descriptor_str = format!(
"wsh(multi(2,{},{},{}))",
public_keys[0], public_keys[1], public_keys[2],
);

// Descriptor for the output being spent
let my_descriptor =
BitcoinDescriptor::from_str(&descriptor_str[..]).expect("parse descriptor string");

// Check weight for witness satisfaction cost ahead of time.
// 4(scriptSig length of 0) + 1(witness stack size) + 106(serialized witnessScript)
// + 73*2(signature length + signatures + sighash bytes) + 1(dummy byte) = 258
assert_eq!(my_descriptor.max_satisfaction_weight().unwrap(), 258);

// Sometimes it is necessary to have additional information to get the bitcoin::PublicKey
// from the MiniscriptKey which can supplied by `to_pk_ctx` parameter. For example,
// when calculating the script pubkey of a descriptor with xpubs, the secp context and
// child information maybe required.

// Observe the script properties, just for fun
assert_eq!(
format!("{:x}", my_descriptor.script_pubkey()),
"00200ed49b334a12c37f3df8a2974ad91ff95029215a2b53f78155be737907f06163"
);

assert_eq!(
format!(
"{:x}",
my_descriptor
.explicit_script()
.expect("wsh descriptors have unique inner script")
),
"52\
21020202020202020202020202020202020202020202020202020202020202020202\
21020102030405060708010203040506070801020304050607080000000000000000\
21030102030405060708010203040506070801020304050607080000000000000000\
53ae"
);

// Attempt to satisfy at age 0, height 0
let original_txin = tx.input[0].clone();

let mut sigs = HashMap::<bitcoin::PublicKey, miniscript::bitcoin::EcdsaSig>::new();

// Doesn't work with no signatures
assert!(my_descriptor.satisfy(&mut tx.input[0], &sigs).is_err());
assert_eq!(tx.input[0], original_txin);

// ...or one signature...
sigs.insert(public_keys[1], bitcoin_sig);
assert!(my_descriptor.satisfy(&mut tx.input[0], &sigs).is_err());
assert_eq!(tx.input[0], original_txin);

// ...but two signatures is ok
sigs.insert(public_keys[2], bitcoin_sig);
assert!(my_descriptor.satisfy(&mut tx.input[0], &sigs).is_ok());
assert_ne!(tx.input[0], original_txin);
assert_eq!(tx.input[0].witness.len(), 4); // 0, sig, sig, witness script

// ...and even if we give it a third signature, only two are used
sigs.insert(public_keys[0], bitcoin_sig);
assert!(my_descriptor.satisfy(&mut tx.input[0], &sigs).is_ok());
assert_ne!(tx.input[0], original_txin);
assert_eq!(tx.input[0].witness.len(), 4); // 0, sig, sig, witness script
}
}
Loading