Skip to content
Open
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
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# Unreleased

- Upgrade `rust-simplicity` to 0.8.0 and align the minimum supported Rust version [#104](https://github.com/ElementsProject/elements-miniscript/pull/104).
- Support CMR-only Simplicity leaves in Taproot descriptors using `sim{asm(CMR)}` [#104](https://github.com/ElementsProject/elements-miniscript/pull/104).

# 0.4.0 - Oct 8, 2024

- Use rust-bitcoin 0.32.0 and rust-elements 0.25.0 [#90](https://github.com/ElementsProject/elements-miniscript/pull/90)
Expand All @@ -22,4 +27,3 @@
# 0.2.0 - June 15, 2023

- Still rapid iteration, very unstable.

27 changes: 12 additions & 15 deletions Cargo-latest.lock
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,12 @@ dependencies = [
"wasm-bindgen",
]

[[package]]
name = "ghost-cell"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8449d342b1c67f49169e92e71deb7b9b27f30062301a16dbc27a4cc8d2351b7"

[[package]]
name = "hex-conservative"
version = "0.1.2"
Expand Down Expand Up @@ -560,15 +566,6 @@ version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"

[[package]]
name = "santiago"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de36022292bc2086eb8f55bffa460fef3475e4459b478820711f4c421feb87ec"
dependencies = [
"regex",
]

[[package]]
name = "secp256k1"
version = "0.28.2"
Expand Down Expand Up @@ -689,26 +686,26 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"

[[package]]
name = "simplicity-lang"
version = "0.3.1"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75c8fb4a18e63fbce4cf16026c36a6c38066e4f4a09ce5e81be817d0e36d8f8"
checksum = "13ed081e3046d66c146d7201bcbf3b655ca3436cb83f6efc26d7895bd2b79d06"
dependencies = [
"bitcoin 0.32.7",
"bitcoin_hashes 0.14.0",
"byteorder",
"elements",
"getrandom",
"hex-conservative 0.1.2",
"ghost-cell",
"hex-conservative 0.2.1",
"miniscript",
"santiago",
"simplicity-sys",
]

[[package]]
name = "simplicity-sys"
version = "0.3.0"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd2cc5d458a8032d328ea85e824f54f61664ab84c3d42b3b7f8804fb9b81572"
checksum = "96d1ec5477c7650b8ef511aa56dccb28f2e8cdb6e87f260ecffdaf0ebfef2d3b"
dependencies = [
"bitcoin_hashes 0.14.0",
"cc",
Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ keywords = [ "crypto", "bitcoin", "miniscript", "script" ]
readme = "README.md"
homepage = "https://github.com/rust-bitcoin/rust-miniscript/"
edition = "2018"
rust-version = "1.63.0"
rust-version = "1.74.0"

[features]
compiler = []
Expand All @@ -23,7 +23,7 @@ base64 = ["bitcoin/base64", "elements/base64"]
bitcoin = "0.32.0"
elements = "0.25.0"
bitcoin-miniscript = { package = "miniscript", version = "12.0" }
simplicity = { package = "simplicity-lang", version = "0.3.0", optional = true }
simplicity = { package = "simplicity-lang", version = "=0.8.0", optional = true }

# Do NOT use this as a feature! Use the `serde` feature instead.
actual-serde = { package = "serde", version = "1.0", optional = true }
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
![Build](https://github.com/ElementsProject/elements-miniscript/workflows/Continuous%20integration/badge.svg)

**Minimum Supported Rust Version:** 1.63.0
**Minimum Supported Rust Version:** 1.74.0

*This crate uses "2018" edition

Expand Down Expand Up @@ -40,7 +40,7 @@ Enabling the `no-std` feature does not disable `std`. To disable the `std` featu
To run the benchmarks run `RUSTFLAGS=--cfg=miniscript_bench cargo +nightly bench --all-features`.

## Minimum Supported Rust Version (MSRV)
This library should always compile with any combination of features on **Rust 1.63.0**.
This library should always compile with any combination of features on **Rust 1.74.0**.


Some dependencies do not play nicely with our MSRV, if you are running the tests
Expand Down
2 changes: 1 addition & 1 deletion clippy.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
msrv = "1.63.0"
msrv = "1.74.0"
# PSBT API returns Self as an error type for an large-ish enum
large-error-threshold = 289
43 changes: 33 additions & 10 deletions src/confidential/elip151.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ impl<T: Extension + ParseableExt> ConfidentialDescriptor<DescriptorPublicKey, T>
mod test {
use super::*;
use crate::descriptor::checksum::desc_checksum;
use bitcoin::hashes::{sha256, HashEngine, sha256t::Tag};
use bitcoin::hashes::{sha256, sha256t::Tag, HashEngine};
use std::str::FromStr;

/// The SHA-256 initial midstate value for the [`Elip151Hash`].
Expand All @@ -124,7 +124,10 @@ mod test {

// Test empty hash
let expected = "dcd8403dcf5af960f69fa41d114931a840877dfb5378046018f78ea894a36ebd";
assert_eq!(Elip151Hash::from_engine(Elip151Tag::engine()).to_string(), expected);
assert_eq!(
Elip151Hash::from_engine(Elip151Tag::engine()).to_string(),
expected
);
assert_eq!(Elip151Hash::hash(&[]).to_string(), expected);
}

Expand Down Expand Up @@ -174,10 +177,22 @@ mod test {

let mut _i = 0;
for (desc, key) in [
(&format!("elwpkh({xpub}/<0;1>/*)"), "b3baf94d60cf8423cd257283575997a2c00664ced3e8de00f8726703142b1989"),
(&format!("elwpkh({xpub}/0/*)"), "de9c5fb624154624146a8aea0489b30f05c720eed6b493b1f3ab63405a11bf37"),
(&format!("elwsh(multi(2,{xpub}/<0;1>/*,{xpub}/0/<0;1>/*))"), "7fcc1b9a20bbf611d157016192a7d28e353033cfa6a4885b3c48fa5ff9ce1881"),
(&format!("elwsh(multi(2,{xpub}/<0;1>/*,{xpub}/0/<1;2>/*))"), "ff0a08050417f0ca95fb6ef7df979ae464739cb79b8c8f4b05408e0ac681a527"),
(
&format!("elwpkh({xpub}/<0;1>/*)"),
"b3baf94d60cf8423cd257283575997a2c00664ced3e8de00f8726703142b1989",
),
(
&format!("elwpkh({xpub}/0/*)"),
"de9c5fb624154624146a8aea0489b30f05c720eed6b493b1f3ab63405a11bf37",
),
(
&format!("elwsh(multi(2,{xpub}/<0;1>/*,{xpub}/0/<0;1>/*))"),
"7fcc1b9a20bbf611d157016192a7d28e353033cfa6a4885b3c48fa5ff9ce1881",
),
(
&format!("elwsh(multi(2,{xpub}/<0;1>/*,{xpub}/0/<1;2>/*))"),
"ff0a08050417f0ca95fb6ef7df979ae464739cb79b8c8f4b05408e0ac681a527",
),
] {
let conf_desc = confidential_descriptor(desc).unwrap();
let elip151_desc = add_checksum(&format!("ct(elip151,{})", desc));
Expand All @@ -202,9 +217,18 @@ mod test {
_i = 0;
let text = "Descriptors without wildcards are not supported in elip151".to_string();
for (invalid_desc, expected_err) in [
(&format!("elwpkh({xpub})"), Error::Unexpected(text.to_string())),
(&format!("elwpkh({pubkey})"), Error::Unexpected(text.to_string())),
(&format!("elwsh(multi(2,{xpub}/<0;1>/*,{xpub}/0/<0;1;2>/*))"), Error::MultipathDescLenMismatch),
(
&format!("elwpkh({xpub})"),
Error::Unexpected(text.to_string()),
),
(
&format!("elwpkh({pubkey})"),
Error::Unexpected(text.to_string()),
),
(
&format!("elwsh(multi(2,{xpub}/<0;1>/*,{xpub}/0/<0;1;2>/*))"),
Error::MultipathDescLenMismatch,
),
] {
let err = confidential_descriptor(invalid_desc).unwrap_err();
assert_eq!(err, expected_err);
Expand All @@ -215,6 +239,5 @@ mod test {
println!("** Invalid confidential descriptor: <code>{}</code>", add_checksum(&format!("ct(elip151,{})", invalid_desc)));
*/
}

}
}
90 changes: 69 additions & 21 deletions src/confidential/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ use elements::secp256k1_zkp;

use crate::descriptor::checksum::{self, verify_checksum};
use crate::descriptor::{
ConversionError, DefiniteDescriptorKey, DescriptorSecretKey, DescriptorPublicKey,
DescriptorXKey, Wildcard
ConversionError, DefiniteDescriptorKey, DescriptorPublicKey, DescriptorSecretKey,
DescriptorXKey, Wildcard,
};
use crate::expression::FromTree;
use crate::extensions::{CovExtArgs, CovenantExt, Extension, ParseableExt};
Expand Down Expand Up @@ -83,13 +83,16 @@ impl Key {
// Convert into a DefiniteDescriptorKey, note that we are deriving the xpub
// since there is not wildcard.
// Consider adding DescriptorPublicKey::to_definite_descriptor
let pk = pk.clone().at_derivation_index(0).expect("single or xpub without wildcards");
let pk = pk
.clone()
.at_derivation_index(0)
.expect("single or xpub without wildcards");
// Derive explicitly rather than relying on ToPublicKey::to_public_key,
// which panics on keys with hardened derivation steps.
let pk = pk.derive_public_key(secp)?;
Ok(bare::tweak_key(secp, spk, &pk))
}
},
}
Key::View(ref sk) => {
if sk.is_multipath() {
Err(Error::Unexpected("multipath blinding key".into()))
Expand All @@ -98,11 +101,13 @@ impl Key {
if pk.has_wildcard() {
Err(Error::Unexpected("wildcard blinding key".into()))
} else {
let pk = pk.at_derivation_index(0).expect("single or xprv without wildcards");
let pk = pk
.at_derivation_index(0)
.expect("single or xprv without wildcards");
Ok(bare::tweak_key(secp, spk, &pk))
}
}
},
}
}
}
}
Expand Down Expand Up @@ -130,7 +135,10 @@ impl<T: Extension + ParseableExt> Descriptor<DescriptorPublicKey, T> {
///
/// # Errors
/// - If index ≥ 2^31
pub fn at_derivation_index(&self, index: u32) -> Result<Descriptor<DefiniteDescriptorKey, T>, ConversionError> {
pub fn at_derivation_index(
&self,
index: u32,
) -> Result<Descriptor<DefiniteDescriptorKey, T>, ConversionError> {
let definite_key = match self.key.clone() {
Key::Slip77(k) => Key::Slip77(k),
Key::Bare(k) => Key::Bare(k.at_derivation_index(index)?.into_descriptor_public_key()),
Expand All @@ -157,12 +165,12 @@ impl<T: Extension + ParseableExt> Descriptor<DescriptorPublicKey, T> {
derivation_path,
wildcard: Wildcard::None,
})
},
}
DescriptorSecretKey::MultiXPrv(_) => return Err(ConversionError::MultiKey),
}),
};
let definite_descriptor = self.descriptor.at_derivation_index(index)?;
Ok(Descriptor{
Ok(Descriptor {
key: definite_key,
descriptor: definite_descriptor,
})
Expand Down Expand Up @@ -550,8 +558,20 @@ mod tests {
] {
let desc = Descriptor::<DefiniteDescriptorKey>::from_str(&desc_str).unwrap();
assert_eq!(desc.to_string(), desc_str);
assert_eq!(addr_conf, &desc.address(&secp, &elements::AddressParams::LIQUID).unwrap().to_string());
assert_eq!(addr_unconf, &desc.unconfidential_address(&elements::AddressParams::LIQUID).unwrap().to_string());
assert_eq!(
addr_conf,
&desc
.address(&secp, &elements::AddressParams::LIQUID)
.unwrap()
.to_string()
);
assert_eq!(
addr_unconf,
&desc
.unconfidential_address(&elements::AddressParams::LIQUID)
.unwrap()
.to_string()
);
}
}

Expand Down Expand Up @@ -580,10 +600,8 @@ mod tests {

let desc: Descriptor<DefiniteDescriptorKey, NoExt> = Descriptor {
key: Key::Bare(DescriptorPublicKey::from_str(&format!("{}/0h", xpub)).unwrap()),
descriptor: crate::Descriptor::new_wpkh(
DefiniteDescriptorKey::from_str(xpub).unwrap(),
)
.unwrap(),
descriptor: crate::Descriptor::new_wpkh(DefiniteDescriptorKey::from_str(xpub).unwrap())
.unwrap(),
};

assert_eq!(
Expand Down Expand Up @@ -628,20 +646,50 @@ mod tests {
let desc_bare = Descriptor::<DescriptorPublicKey>::from_str(&desc_bare_str).unwrap();
let definite_desc_view = desc_view.at_derivation_index(index).unwrap();
let definite_desc_bare = desc_bare.at_derivation_index(index).unwrap();
assert_eq!(definite_desc_view.address(&secp, params).unwrap().to_string(), conf_addr.to_string());
assert_eq!(definite_desc_bare.address(&secp, params).unwrap().to_string(), conf_addr.to_string());
assert_eq!(definite_desc_view.unconfidential_address(params).unwrap().to_string(), unconf_addr.to_string());
assert_eq!(definite_desc_bare.unconfidential_address(params).unwrap().to_string(), unconf_addr.to_string());
assert_eq!(
definite_desc_view
.address(&secp, params)
.unwrap()
.to_string(),
conf_addr.to_string()
);
assert_eq!(
definite_desc_bare
.address(&secp, params)
.unwrap()
.to_string(),
conf_addr.to_string()
);
assert_eq!(
definite_desc_view
.unconfidential_address(params)
.unwrap()
.to_string(),
unconf_addr.to_string()
);
assert_eq!(
definite_desc_bare
.unconfidential_address(params)
.unwrap()
.to_string(),
unconf_addr.to_string()
);

// It's not possible to get an address if the blinding key has a wildcard,
// because the descriptor blinding key is not *definite*,
// but we can't enforce this with the Descriptor generic.
let desc_view_str = format!("ct({}/*,elwpkh({}))#ls6mx2ac", xprv, xpub);
let desc_view = Descriptor::<DefiniteDescriptorKey>::from_str(&desc_view_str).unwrap();
assert_eq!(desc_view.address(&secp, params).unwrap_err(), Error::Unexpected("wildcard blinding key".into()));
assert_eq!(
desc_view.address(&secp, params).unwrap_err(),
Error::Unexpected("wildcard blinding key".into())
);

let desc_bare_str = format!("ct({}/*,elwpkh({}))#czkz0hwn", xpub, xpub);
let desc_bare = Descriptor::<DefiniteDescriptorKey>::from_str(&desc_bare_str).unwrap();
assert_eq!(desc_bare.address(&secp, params).unwrap_err(), Error::Unexpected("wildcard blinding key".into()));
assert_eq!(
desc_bare.address(&secp, params).unwrap_err(),
Error::Unexpected("wildcard blinding key".into())
);
}
}
8 changes: 2 additions & 6 deletions src/descriptor/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1018,9 +1018,7 @@ impl<K: InnerXKey> DescriptorXKey<K> {
let (compare_fingerprint, compare_path) = match self.origin {
Some((fingerprint, ref path)) => (
fingerprint,
path.into_iter()
.chain(&self.derivation_path)
.collect(),
path.into_iter().chain(&self.derivation_path).collect(),
),
None => (
self.xkey.xkey_fingerprint(secp),
Expand All @@ -1039,9 +1037,7 @@ impl<K: InnerXKey> DescriptorXKey<K> {
};

if &compare_fingerprint == fingerprint
&& compare_path
.into_iter()
.eq(&path_excluding_wildcard)
&& compare_path.into_iter().eq(&path_excluding_wildcard)
{
Some(path_excluding_wildcard)
} else {
Expand Down
Loading
Loading