Skip to content

Commit

Permalink
Merge #10: Extended public keys support
Browse files Browse the repository at this point in the history
dcb88a0 transactions: move tests to use DescriptorPublicKey::XPub (Antoine Poinsot)
007a8a4 Cargo.toml: just use bitcoin's secp256k1 (Antoine Poinsot)
426c3c4 scripts: take generalistic MiniscriptKey instead of PublicKey (Antoine Poinsot)
d51654e Update to the latest rust-bitcoin (Antoine Poinsot)
1e24858 Cargo: rename the crate to revault_tx (Antoine Poinsot)

Pull request description:

  This:
  - Renames the crate
  - Updates to rust-bitcoin 0.24 🎉
  - Updates rust-miniscript to rust-bitcoin/rust-miniscript#131 and move our Miniscripts to use `DescriptorPublicKey`s

ACKs for top commit:
  JSwambo:
    Ack dcb88a0

Tree-SHA512: ca0908a459b6129cb2292ad70ff7efd40f0d778302d9c8fcfffa2f89f2f069121853b91927e7b16cc0dc0c530a7da2373ab57b3bcad96168cdf6ec3510e4e6ad
  • Loading branch information
darosior committed Oct 2, 2020
2 parents d0e3a36 + dcb88a0 commit f895005
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 150 deletions.
7 changes: 3 additions & 4 deletions Cargo.toml
@@ -1,15 +1,14 @@
[package]
name = "revault"
name = "revault_tx"
version = "0.0.1"
authors = ["Antoine Poinsot <darosior@protonmail.com>"]
edition = "2018"


[dependencies]
bitcoin = { git = "https://github.com/darosior/rust-bitcoin", branch = "hack_sighashcache" }
bitcoin = "0.24"
bitcoinconsensus = "0.19.0-1"
miniscript = { git = "https://github.com/darosior/rust-miniscript", branch = "hack_sighashcache", features = ["compiler"] }
secp256k1 = { version = "0.17.2" }
miniscript = { git = "https://github.com/rust-bitcoin/rust-miniscript", branch = "master", features = ["compiler"] }

[dev-dependencies]
rand = "0.7.3"
145 changes: 74 additions & 71 deletions src/scripts.rs
Expand Up @@ -10,8 +10,7 @@

use crate::error::Error;

use bitcoin::PublicKey;
use miniscript::{policy::concrete::Policy, Descriptor, Segwitv0};
use miniscript::{policy::concrete::Policy, Descriptor, MiniscriptKey, Segwitv0};

// FIXME: use extended pubkeys everywhere after https://github.com/rust-bitcoin/rust-miniscript/pull/116

Expand All @@ -21,9 +20,9 @@ use miniscript::{policy::concrete::Policy, Descriptor, Segwitv0};
///
/// # Examples
/// ```rust
/// use revault::scripts;
/// use revault_tx::scripts;
/// use bitcoin;
/// use secp256k1;
/// use bitcoin::secp256k1;
///
/// let secp = secp256k1::Secp256k1::new();
/// let secret_key = secp256k1::SecretKey::from_slice(&[0xcd; 32]).expect("32 bytes, within curve order");
Expand All @@ -32,7 +31,7 @@ use miniscript::{policy::concrete::Policy, Descriptor, Segwitv0};
/// key: secp256k1::PublicKey::from_secret_key(&secp, &secret_key),
/// };
/// let vault_descriptor =
/// scripts::vault_descriptor(&[public_key, public_key]).expect("Compiling descriptor");
/// scripts::vault_descriptor(vec![public_key, public_key]).expect("Compiling descriptor");
///
/// println!("Vault descriptor redeem script: {}", vault_descriptor.witness_script());
/// ```
Expand All @@ -41,21 +40,18 @@ use miniscript::{policy::concrete::Policy, Descriptor, Segwitv0};
/// - If the passed slice contains less than 2 public keys.
/// - If the policy compilation to miniscript failed, which should not happen (tm) and would be a
/// bug.
pub fn vault_descriptor(participants: &[PublicKey]) -> Result<Descriptor<PublicKey>, Error> {
pub fn vault_descriptor<Pk: MiniscriptKey>(participants: Vec<Pk>) -> Result<Descriptor<Pk>, Error> {
if participants.len() < 2 {
return Err(Error::ScriptCreation(
"Vault: bad parameters. We need more than one participant.".to_string(),
));
}

let pubkeys = participants
.iter()
.copied()
.into_iter()
.map(Policy::Key)
.collect::<Vec<Policy<PublicKey>>>();
.collect::<Vec<Policy<Pk>>>();

// Note that this will be more optimal once
// https://github.com/rust-bitcoin/rust-miniscript/pull/113 is merged
let policy = Policy::Threshold(pubkeys.len(), pubkeys);

// This handles the non-safe or malleable cases.
Expand All @@ -64,7 +60,7 @@ pub fn vault_descriptor(participants: &[PublicKey]) -> Result<Descriptor<PublicK
"Vault policy compilation error: {}",
compile_err
))),
Ok(miniscript) => Ok(Descriptor::<PublicKey>::Wsh(miniscript)),
Ok(miniscript) => Ok(Descriptor::<Pk>::Wsh(miniscript)),
}
}

Expand All @@ -88,9 +84,9 @@ pub fn vault_descriptor(participants: &[PublicKey]) -> Result<Descriptor<PublicK
///
/// # Examples
/// ```rust
/// use revault::scripts;
/// use revault_tx::scripts;
/// use bitcoin;
/// use secp256k1;
/// use bitcoin::secp256k1;
///
/// let secp = secp256k1::Secp256k1::new();
/// let secret_key = secp256k1::SecretKey::from_slice(&[0xcd; 32]).expect("32 bytes, within curve order");
Expand All @@ -100,11 +96,11 @@ pub fn vault_descriptor(participants: &[PublicKey]) -> Result<Descriptor<PublicK
/// };
/// let unvault_descriptor = scripts::unvault_descriptor(
/// // Non-managers
/// &[public_key, public_key, public_key],
/// vec![public_key, public_key, public_key],
/// // Managers
/// &[public_key, public_key],
/// vec![public_key, public_key],
/// // Cosigners
/// &[public_key, public_key, public_key],
/// vec![public_key, public_key, public_key],
/// // CSV
/// 42
/// ).expect("Compiling descriptor");
Expand All @@ -117,12 +113,12 @@ pub fn vault_descriptor(participants: &[PublicKey]) -> Result<Descriptor<PublicK
/// not the same as the number of cosigners public key.
/// - If the policy compilation to miniscript failed, which should not happen (tm) and would be a
/// bug.
pub fn unvault_descriptor(
non_managers: &[PublicKey],
managers: &[PublicKey],
cosigners: &[PublicKey],
pub fn unvault_descriptor<Pk: MiniscriptKey>(
non_managers: Vec<Pk>,
managers: Vec<Pk>,
cosigners: Vec<Pk>,
csv_value: u32,
) -> Result<Descriptor<PublicKey>, Error> {
) -> Result<Descriptor<Pk>, Error> {
if non_managers.is_empty() || managers.is_empty() || cosigners.len() != non_managers.len() {
return Err(Error::ScriptCreation(
"Unvault: bad parameters. There must be a non-zero \
Expand All @@ -138,24 +134,21 @@ pub fn unvault_descriptor(
}

let mut pubkeys = managers
.iter()
.copied()
.into_iter()
.map(Policy::Key)
.collect::<Vec<Policy<PublicKey>>>();
.collect::<Vec<Policy<Pk>>>();
let spenders_thres = Policy::Threshold(pubkeys.len(), pubkeys);

pubkeys = non_managers
.iter()
.copied()
.into_iter()
.map(Policy::Key)
.collect::<Vec<Policy<PublicKey>>>();
.collect::<Vec<Policy<Pk>>>();
let non_spenders_thres = Policy::Threshold(pubkeys.len(), pubkeys);

pubkeys = cosigners
.iter()
.copied()
.into_iter()
.map(Policy::Key)
.collect::<Vec<Policy<PublicKey>>>();
.collect::<Vec<Policy<Pk>>>();
let cosigners_thres = Policy::Threshold(pubkeys.len(), pubkeys);

let cosigners_and_csv = Policy::And(vec![cosigners_thres, Policy::Older(csv_value)]);
Expand All @@ -171,7 +164,7 @@ pub fn unvault_descriptor(
"Unvault policy compilation error: {}",
compile_err
))),
Ok(miniscript) => Ok(Descriptor::<PublicKey>::Wsh(miniscript)),
Ok(miniscript) => Ok(Descriptor::<Pk>::Wsh(miniscript)),
}
}

Expand All @@ -182,12 +175,13 @@ pub fn unvault_descriptor(
/// # Errors
/// - If the policy compilation to miniscript failed, which should not happen (tm) and would be a
/// bug.
pub fn unvault_cpfp_descriptor(managers: &[PublicKey]) -> Result<Descriptor<PublicKey>, Error> {
pub fn unvault_cpfp_descriptor<Pk: MiniscriptKey>(
managers: Vec<Pk>,
) -> Result<Descriptor<Pk>, Error> {
let pubkeys = managers
.iter()
.copied()
.into_iter()
.map(Policy::Key)
.collect::<Vec<Policy<PublicKey>>>();
.collect::<Vec<Policy<Pk>>>();

let policy = Policy::Threshold(1, pubkeys);

Expand All @@ -197,7 +191,7 @@ pub fn unvault_cpfp_descriptor(managers: &[PublicKey]) -> Result<Descriptor<Publ
"Unvault CPFP policy compilation error: {}",
compile_err
))),
Ok(miniscript) => Ok(Descriptor::<PublicKey>::Wsh(miniscript)),
Ok(miniscript) => Ok(Descriptor::<Pk>::Wsh(miniscript)),
}
}

Expand All @@ -210,19 +204,19 @@ mod tests {
use bitcoin::PublicKey;

fn get_random_pubkey() -> PublicKey {
let secp = secp256k1::Secp256k1::new();
let secp = bitcoin::secp256k1::Secp256k1::new();
let mut rand_bytes = [0u8; 32];
// Make rustc happy..
let mut secret_key = Err(secp256k1::Error::InvalidSecretKey);
let mut secret_key = Err(bitcoin::secp256k1::Error::InvalidSecretKey);

while secret_key.is_err() {
rand::thread_rng().fill_bytes(&mut rand_bytes);
secret_key = secp256k1::SecretKey::from_slice(&rand_bytes);
secret_key = bitcoin::secp256k1::SecretKey::from_slice(&rand_bytes);
}

PublicKey {
compressed: true,
key: secp256k1::PublicKey::from_secret_key(&secp, &secret_key.unwrap()),
key: bitcoin::secp256k1::PublicKey::from_secret_key(&secp, &secret_key.unwrap()),
}
}

Expand Down Expand Up @@ -258,12 +252,19 @@ mod tests {
.map(|_| get_random_pubkey())
.collect::<Vec<PublicKey>>();

unvault_descriptor(&non_managers, &managers, &cosigners, 18).expect(&format!(
unvault_descriptor(
non_managers.clone(),
managers.clone(),
cosigners.clone(),
18,
)
.expect(&format!(
"Unvault descriptors creation error with ({}, {})",
n_managers, n_non_managers
));
vault_descriptor(
&managers
managers
.clone()
.iter()
.chain(non_managers.iter())
.copied()
Expand All @@ -273,7 +274,7 @@ mod tests {
"Vault descriptors creation error with ({}, {})",
n_managers, n_non_managers
));
unvault_cpfp_descriptor(&managers).expect(&format!(
unvault_cpfp_descriptor(managers).expect(&format!(
"Unvault CPFP descriptors creation error with ({}, {})",
n_managers, n_non_managers
));
Expand All @@ -283,17 +284,17 @@ mod tests {
#[test]
fn test_configuration_limits() {
assert_eq!(
vault_descriptor(&vec![get_random_pubkey()]),
vault_descriptor(vec![get_random_pubkey()]),
Err(Error::ScriptCreation(
"Vault: bad parameters. We need more than one participant.".to_string()
))
);

assert_eq!(
unvault_descriptor(
&vec![get_random_pubkey()],
&vec![get_random_pubkey()],
&vec![get_random_pubkey(), get_random_pubkey()],
vec![get_random_pubkey()],
vec![get_random_pubkey()],
vec![get_random_pubkey(), get_random_pubkey()],
6
),
Err(Error::ScriptCreation(
Expand All @@ -305,9 +306,9 @@ mod tests {

assert_eq!(
unvault_descriptor(
&vec![get_random_pubkey()],
&vec![get_random_pubkey()],
&vec![get_random_pubkey()],
vec![get_random_pubkey()],
vec![get_random_pubkey()],
vec![get_random_pubkey()],
4194305
),
Err(Error::ScriptCreation(
Expand All @@ -316,39 +317,41 @@ mod tests {
))
);

// TODO: fix upstream, this should fail to compile (cf https://github.com/rust-bitcoin/rust-miniscript/issues/132)

// Maximum N-of-N
let participants = (0..67)
.map(|_| get_random_pubkey())
.collect::<Vec<PublicKey>>();
vault_descriptor(&participants).expect("Should be OK: max allowed value");
vault_descriptor(participants).expect("Should be OK: max allowed value");
// Now hit the limit
let participants = (0..68)
.map(|_| get_random_pubkey())
.collect::<Vec<PublicKey>>();
assert_eq!(vault_descriptor(&participants), Err(Error::ScriptCreation("Vault policy compilation error: Atleast one spending path has more op codes executed than MAX_OPS_PER_SCRIPT".to_string())));
//let participants = (0..68)
//.map(|_| get_random_pubkey())
//.collect::<Vec<PublicKey>>();
//assert_eq!(vault_descriptor(&participants), Err(Error::ScriptCreation("Vault policy compilation error: Atleast one spending path has more op codes executed than MAX_OPS_PER_SCRIPT".to_string())));

// Maximum 1-of-N
let managers = (0..20)
.map(|_| get_random_pubkey())
.collect::<Vec<PublicKey>>();
unvault_cpfp_descriptor(&managers).expect("Should be OK, that's the maximum allowed value");
unvault_cpfp_descriptor(managers).expect("Should be OK, that's the maximum allowed value");
// Hit the limit
let managers = (0..21)
.map(|_| get_random_pubkey())
.collect::<Vec<PublicKey>>();
assert_eq!(unvault_cpfp_descriptor(&managers), Err(Error::ScriptCreation("Unvault CPFP policy compilation error: Atleast one spending path has more op codes executed than MAX_OPS_PER_SCRIPT".to_string())));
//let managers = (0..21)
//.map(|_| get_random_pubkey())
//.collect::<Vec<PublicKey>>();
//assert_eq!(unvault_cpfp_descriptor(&managers), Err(Error::ScriptCreation("Unvault CPFP policy compilation error: Atleast one spending path has more op codes executed than MAX_OPS_PER_SCRIPT".to_string())));

// Maximum non-managers for 2 managers (+ 1)
let managers = (0..2)
.map(|_| get_random_pubkey())
.collect::<Vec<PublicKey>>();
let non_managers = (0..21)
.map(|_| get_random_pubkey())
.collect::<Vec<PublicKey>>();
let cosigners = (0..21)
.map(|_| get_random_pubkey())
.collect::<Vec<PublicKey>>();
assert_eq!(unvault_descriptor(&non_managers, &managers, &cosigners, 32), Err(Error::ScriptCreation("Unvault policy compilation error: Atleast one spending path has more op codes executed than MAX_OPS_PER_SCRIPT".to_string())));
//let managers = (0..2)
//.map(|_| get_random_pubkey())
//.collect::<Vec<PublicKey>>();
//let non_managers = (0..21)
//.map(|_| get_random_pubkey())
//.collect::<Vec<PublicKey>>();
//let cosigners = (0..21)
//.map(|_| get_random_pubkey())
//.collect::<Vec<PublicKey>>();
//assert_eq!(unvault_descriptor(&non_managers, &managers, &cosigners, 32), Err(Error::ScriptCreation("Unvault policy compilation error: Atleast one spending path has more op codes executed than MAX_OPS_PER_SCRIPT".to_string())));
}

// TODO: extensively test all possibilities before reaching the limit
Expand Down

0 comments on commit f895005

Please sign in to comment.