diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml new file mode 100644 index 00000000..b512ea29 --- /dev/null +++ b/.github/workflows/fuzz.yml @@ -0,0 +1,68 @@ +# Automatically generated by fuzz/generate-files.sh +name: Fuzz + +on: + push: + branches: + - master + - 'test-ci/**' + pull_request: + +jobs: + fuzz: + if: ${{ !github.event.act }} + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + fuzz_target: [ +roundtrip_miniscript_str, +roundtrip_miniscript_script, +parse_descriptor, +roundtrip_semantic, +parse_descriptor_secret, +roundtrip_descriptor, +roundtrip_concrete, +compile_descriptor, + ] + steps: + - name: Install test dependencies + run: sudo apt-get update -y && sudo apt-get install -y binutils-dev libunwind8-dev libcurl4-openssl-dev libelf-dev libdw-dev cmake gcc libiberty-dev + - uses: actions/checkout@v2 + - uses: actions/cache@v2 + id: cache-fuzz + with: + path: | + ~/.cargo/bin + fuzz/target + target + key: cache-${{ matrix.target }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }} + - uses: actions-rs/toolchain@v1 + with: + toolchain: 1.58 + override: true + profile: minimal + - name: fuzz + run: | + if [[ "${{ matrix.fuzz_target }}" =~ ^bitcoin ]]; then + export RUSTFLAGS='--cfg=hashes_fuzz --cfg=secp256k1_fuzz' + fi + echo "Using RUSTFLAGS $RUSTFLAGS" + cd fuzz && ./fuzz.sh "${{ matrix.fuzz_target }}" + - run: echo "${{ matrix.fuzz_target }}" >executed_${{ matrix.fuzz_target }} + - uses: actions/upload-artifact@v2 + with: + name: executed_${{ matrix.fuzz_target }} + path: executed_${{ matrix.fuzz_target }} + + verify-execution: + if: ${{ !github.event.act }} + needs: fuzz + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/download-artifact@v2 + - name: Display structure of downloaded files + run: ls -R + - run: find executed_* -type f -exec cat {} + | sort > executed + - run: source ./fuzz/fuzz-util.sh && listTargetNames | sort | diff - executed diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9011cb07..2f418fca 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,10 +1,14 @@ -on: [push, pull_request] +on: + push: + branches: + - master + pull_request: {} name: Continuous integration jobs: lint_fuzz_stable: - name: Lint + Fuzz + name: Lint runs-on: ubuntu-latest steps: - name: Checkout Crate @@ -19,7 +23,6 @@ jobs: override: true - name: Running fuzzer env: - DO_FUZZ: true DO_LINT: true run: ./contrib/test.sh @@ -66,7 +69,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - rust: [stable, beta, nightly, 1.41.1] + rust: [stable, beta, nightly, 1.48.0] steps: - name: Checkout Crate uses: actions/checkout@v2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 2653b227..cc39a610 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,121 +1,4 @@ -# 9.0.0 - November 5, 2022 +# 0.2.0 - June 15, 2023 -- Fixed a bug dealing with dissatisfying pkh inside thresh -- Changed the signature of `Satisfier::lookup_raw_pkh_pk` API. Only custom implementations - of `Satisfier` need to be updated. The psbt APIs are unchanged. -- Fixed a bug related to display of `raw_pk_h`. These descriptors are experimental - and only usable by opting via `ExtParams` while parsing string. -# 8.0.0 - October 20, 2022 - -This release contains several significant API overhauls, as well as a bump -of our MSRV from 1.29 to 1.41. Users are encouraged to update their compiler -to 1.41 *before* updating to this version. - -It includes more Taproot support, but users should be aware that Taproot -support for Miniscript is **not** standardized and is subject to change in -the future. See [this gist](https://gist.github.com/sipa/06c5c844df155d4e5044c2c8cac9c05e) -for our thinking regarding this at the time of release. - -- Works with bitcoin crate 0.29 -- Correctly [return an error when `SortedMulti` is constructed with too many keys](https://github.com/rust-bitcoin/rust-miniscript/pull/366/) -- Cleanly separate [`experimental/insane miniscripts`](https://github.com/rust-bitcoin/rust-miniscript/pull/461) from sane miniscripts. -- allow disabling the checksum with [`alternate Display`](https://github.com/rust-bitcoin/rust-miniscript/pull/478) -- Correct [`max_satisfaction_size` of `from_multi_a` fragment](https://github.com/rust-bitcoin/rust-miniscript/pull/346/) -- [Add `PsbtInputExt` trait with `update_with_descriptor` method](https://github.com/rust-bitcoin/rust-miniscript/pull/339/) and [`PsbtOutputExt` trait](https://github.com/rust-bitcoin/rust-miniscript/pull/465/) -- Rename [several descriptor types](https://github.com/rust-bitcoin/rust-miniscript/pull/376/) to reduce redundancy -- [**Bump MSRV to 1.41** and edition to 2018](https://github.com/rust-bitcoin/rust-miniscript/pull/365/) -- Rename [`as_public` to `to_public` on some descriptor key types](https://github.com/rust-bitcoin/rust-miniscript/pull/377/) -- Split fully derived `DescriptorPublicKey`s [into their own type](https://github.com/rust-bitcoin/rust-miniscript/pull/345/) [followup](https://github.com/rust-bitcoin/rust-miniscript/pull/448/) -- [Remove the `DescriptorTrait`](https://github.com/rust-bitcoin/rust-miniscript/pull/386/) in favor of the `Descriptor` enum -- Fix signature costing [to account for ECDSA vs Schnorr](https://github.com/rust-bitcoin/rust-miniscript/pull/340/) -- **Add a Taproot-enabled compiler** [v1](https://github.com/rust-bitcoin/rust-miniscript/pull/291/) [v2](https://github.com/rust-bitcoin/rust-miniscript/pull/342/) [v3](https://github.com/rust-bitcoin/rust-miniscript/pull/418/) -- Rename [`stackelem` to `stack_elem`](https://github.com/rust-bitcoin/rust-miniscript/pull/411/) in the interpreter -- Add [`no-std`](https://github.com/rust-bitcoin/rust-miniscript/pull/277) -- Reworked the [`TranslatePk`](https://github.com/rust-bitcoin/rust-miniscript/pull/426) APIs. Add a Translator trait to cleanly allow downstream users without dealing with APIs that accept function pointers. Also provides `translate_assoc_clone` and `translate_assoc_fail` macros for helping in writing code. -- Updated [`MiniscriptKey trait`](https://github.com/rust-bitcoin/rust-miniscript/pull/434),https://github.com/rust-bitcoin/rust-miniscript/pull/439 to accept associated types for Sha256, Hash256, Ripemd160 and -Hash160. This allows users to write abstract miniscripts hashes as "sha256(H)" instead of specifying the entire hash in the string. -that updates the psbt with descriptor bip32 paths. -- Re-name [`as_public`](https://github.com/rust-bitcoin/rust-miniscript/pull/377) APIs -> `to_public` -- Significantly improve the [timelock](https://github.com/rust-bitcoin/rust-miniscript/pull/414) code with new rust-bitcoin APIs. -- rust-miniscript minor implementation detail: `PkH` fragment now has `Pk` generic instead of `Pk::Hash`. This only concerns users -that operate with `MiniscriptKey = bitcoin::PublicKey` or users that use custom implementation of `MiniscriptKey`. Users that use -`DescriptorPublicKey` need not be concerned. See [PR](https://github.com/rust-bitcoin/rust-miniscript/pull/431) for details. - - To elaborate, "pkh(<20-byte-hex>)" is no longer parsed by the `MiniscriptKey = bitcoin::PublicKey`. -This is consistent with the descriptor spec as defined. Parsing from `bitcoin::Script` for pkh<20-byte-hex> is still supported, but the library would not analyze them. These raw descriptors are still in spec discussions. Rust-miniscript will support them once they are completely specified. - -# 7.0.0 - April 20, 2022 - -- Support for `tr` descriptors with miniscript leaves and multi_a fragment -- Changes to MiniscriptKey and ToPublicKey traits for x-only keys support -- Add `PsbtExt` trait for psbt operations - - `Psbt::update_desc` adds information from a descriptor to a psbt. This figures - out the type of the descriptor and adds corresponding redeem script/witness script - and tap tree information -- Add `derived_descriptor` API to Descriptor so that users no longer need to use -`translate` APIs. See examples/`xpub_descriptor` for usage -- Update `DescriptorTrait`: `script_code` and `explicit_script` can now fail because - of taproot descriptors -- Add `PreTaprootDescriptor` and `PreTaprootDescriptorTrait` to support non-failing versions - of `script_code` and `explicit_script` for non taproot descriptors -- Overhaul the interpreter API to provide simpler APIs `iter(prevouts)` and `iter_assume_sig()` - so that it no longer takes a closure input. -- Add interpreter support for taproot transactions. -- Works with rust-bitcoin 0.28.0-rc.1 -# 6.0.1 - Aug 5, 2021 - -- The `lift` method on a Miniscript node was fixed. It would previously mix up - the `X` and `Y` argument of an `andor` fragment. - -# 6.0.0 - Jul 29, 2021 - -- bump `rust-bitcoin` to 0.27 -- several bugfixes - -# 5.0.0 - Jan 14, 2021 - -- Remove `PkCtx` from the API -- Move descriptors into their own types, with an enum containing all of them -- Move descriptor functionality into a trait -- Remove `FromStr` bound from `MiniscriptKey`and `MiniscriptKey::Hash` -- Various `DescriptorPublicKey` improvements -- Allow hardened paths in `DescriptorPublicKey`, remove direct `ToPublicKey` implementation -- Change `Option` to `Result` in all APIs -- bump `rust-bitcoin` to 0.26 - -# 4.0.0 - Nov 23, 2020 - -- Add support for parsing secret keys -- Add sortedmulti descriptor -- Added standardness and other sanity checks -- Cleaned up `Error` type and return values of most of the API -- Overhauled `satisfied_constraints` module into a new `Iterpreter` API - -# 3.0.0 - Oct 13, 2020 - -- **Bump MSRV to 1.29** - -# 2.0.0 - Oct 1, 2020 - -- Changes to the miniscript type system to detect an invalid - combination of heightlocks and timelocks - - Lift miniscripts can now fail. Earlier it always succeeded and gave - the resulting Semantic Policy - - Compiler will not compile policies that contain at least one - unspendable path -- Added support for Descriptor PublicKeys(xpub) -- Added a generic psbt finalizer and extractor -- Updated Satisfaction API for checking time/height before setting satisfaction -- Added a policy entailment API for more miniscript semantic analysis - -# 1.0.0 - July 6, 2020 - -- Added the following aliases to miniscript for ease of operations - - Rename `pk` to `pk_k` - - Rename `thresh_m` to `multi` - - Add alias `pk(K)` = `c:pk_k(K)` - - Add alias `pkh(K)` = `c:pk_h(K)` -- Fixed Miniscript parser bugs when decoding Hashlocks -- Added scriptContext(`Legacy` and `Segwitv0`) to Miniscript. -- Miscellaneous fixes against DoS attacks for heavy nesting. -- Fixed Satisfier bug that caused flipping of arguments for `and_v` and `and_n` and `and_or` +- Still rapid iteration, very unstable. diff --git a/Cargo.toml b/Cargo.toml index 43961a09..aa131afb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "elements-miniscript" -version = "0.1.0" +version = "0.2.0" authors = ["Andrew Poelstra , Sanket Kanjalkar "] repository = "https://github.com/ElementsProject/elements-miniscript" description = "Elements Miniscript: Miniscript, but for elements" @@ -14,15 +14,14 @@ edition = "2018" compiler = [] trace = [] -unstable = [] serde = ["actual-serde", "bitcoin/serde", "elements/serde"] rand = ["bitcoin/rand"] base64 = ["bitcoin/base64"] [dependencies] -bitcoin = "0.29.1" -elements = "0.21.0" -bitcoin-miniscript = { package = "miniscript", version = "9.0", git = "https://github.com/rust-bitcoin/rust-miniscript", rev = "1351c20a5ead4f308e18bce88cc01983c7d0e876" } +bitcoin = "0.30.0" +elements = "0.22.0" +bitcoin-miniscript = { package = "miniscript", version = "10.0" } # Do NOT use this as a feature! Use the `serde` feature instead. actual-serde = { package = "serde", version = "1.0", optional = true } @@ -31,8 +30,8 @@ actual-serde = { package = "serde", version = "1.0", optional = true } serde_json = "1.0" actual-rand = { package = "rand", version = "0.8.4"} serde_test = "1.0.147" -bitcoin = { version = "0.29.2", features = ["base64"] } -secp256k1 = {version = "0.24.0", features = ["rand-std"]} +bitcoin = { version = "0.30.0", features = ["base64"] } +secp256k1 = {version = "0.27.0", features = ["rand-std"]} actual-base64 = { package = "base64", version = "0.13.0" } @@ -58,4 +57,7 @@ required-features = ["compiler"] [[example]] name = "psbt_sign_finalize" -required-features = ["base64"] \ No newline at end of file +required-features = ["base64"] + +[workspace] +members = ["bitcoind-tests", "fuzz"] diff --git a/README.md b/README.md index de868eee..d2db55d4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![Build](https://github.com/ElementsProject/elements-miniscript/workflows/Continuous%20integration/badge.svg) -**Minimum Supported Rust Version:** 1.41.1 +**Minimum Supported Rust Version:** 1.48.0 *This crate uses "2018" edition @@ -35,19 +35,19 @@ The cargo feature `std` is enabled by default. At least one of the features `std Enabling the `no-std` feature does not disable `std`. To disable the `std` feature you must disable default features. The `no-std` feature only enables additional features required for this crate to be usable without `std`. Both can be enabled without conflict. +## Benchmarking + +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.41.1**. +This library should always compile with any combination of features on **Rust 1.48.0**. Some dependencies do not play nicely with our MSRV, if you are running the tests you may need to pin as follows: ``` -cargo update --package url --precise 2.2.2 -cargo update --package form_urlencoded --precise 1.0.1 -cargo update -p once_cell --precise 1.13.1 -cargo update -p bzip2 --precise 0.4.2 -cargo update -p which --precise 4.3.0 +cargo update -p serde --precise 1.0.156 ``` ## Contributing diff --git a/bitcoind-tests/Cargo.toml b/bitcoind-tests/Cargo.toml index 449114f9..8c79c914 100644 --- a/bitcoind-tests/Cargo.toml +++ b/bitcoind-tests/Cargo.toml @@ -10,4 +10,4 @@ edition = "2018" elements-miniscript = {path = "../"} elementsd = {version = "0.6.0"} actual-rand = { package = "rand", version = "0.8.4"} -secp256k1 = {version = "0.24.0", features = ["rand-std"]} +secp256k1 = {version = "0.27.0", features = ["rand-std"]} diff --git a/bitcoind-tests/tests/setup/mod.rs b/bitcoind-tests/tests/setup/mod.rs index 387883e5..af7cb92b 100644 --- a/bitcoind-tests/tests/setup/mod.rs +++ b/bitcoind-tests/tests/setup/mod.rs @@ -3,7 +3,7 @@ pub extern crate elements_miniscript; use std::str::FromStr; use miniscript::elements; use elements::encode::{deserialize, serialize_hex}; -use elements::hashes::hex::FromHex; +use elements::hex::FromHex; use elements::BlockHash; pub use elements_miniscript as miniscript; use elementsd::bitcoincore_rpc::jsonrpc::serde_json::{json, Value}; @@ -18,8 +18,8 @@ pub mod test_util; pub fn setup(validate_pegin: bool) -> (ElementsD, Option, elements::BlockHash) { // Lookup bitcoind binary path let curr_dir = std::env::current_dir().unwrap(); - let bitcoind_path = curr_dir.clone().join("bin/bitcoind"); - let elementsd_path = curr_dir.clone().join("bin/elementsd"); + let bitcoind_path = curr_dir.join("bin/bitcoind"); + let elementsd_path = curr_dir.join("bin/elementsd"); std::env::set_var("BITCOIND_EXE", bitcoind_path); std::env::set_var("ELEMENTSD_EXE", elementsd_path); @@ -28,7 +28,7 @@ pub fn setup(validate_pegin: bool) -> (ElementsD, Option, elements::Bl if validate_pegin { let bitcoind_exe = bitcoind::exe_path().unwrap(); let bitcoind_conf = bitcoind::Conf::default(); - bitcoind = Some(bitcoind::BitcoinD::with_conf(&bitcoind_exe, &bitcoind_conf).unwrap()); + bitcoind = Some(bitcoind::BitcoinD::with_conf(bitcoind_exe, &bitcoind_conf).unwrap()); } let mut conf = elementsd::Conf::new(bitcoind.as_ref()); diff --git a/bitcoind-tests/tests/setup/test_util.rs b/bitcoind-tests/tests/setup/test_util.rs index facb324c..f73820e9 100644 --- a/bitcoind-tests/tests/setup/test_util.rs +++ b/bitcoind-tests/tests/setup/test_util.rs @@ -21,10 +21,9 @@ use std::collections::HashMap; use std::str::FromStr; use miniscript::{elements, bitcoin}; -use elements::hashes::hex::ToHex; +use elements::hex::{FromHex, ToHex}; use elements::hashes::{hash160, ripemd160, sha256, Hash}; use elements::secp256k1_zkp as secp256k1; -use elements::hashes::hex::FromHex; use elements::{confidential, encode, AddressParams, BlockHash}; use miniscript::descriptor::{SinglePub, SinglePubKey}; use miniscript::extensions::param::ExtParamTranslator; @@ -41,7 +40,7 @@ pub static PARAMS: AddressParams = AddressParams::ELEMENTS; #[derive(Clone, Debug)] pub struct PubData { pub pks: Vec, - pub x_only_pks: Vec, + pub x_only_pks: Vec, pub sha256: sha256::Hash, pub hash256: hash256::Hash, pub ripemd160: ripemd160::Hash, @@ -60,7 +59,7 @@ pub struct PubData { #[derive(Debug, Clone)] pub struct SecretData { pub sks: Vec, - pub x_only_keypairs: Vec, + pub x_only_keypairs: Vec, pub sha256_pre: [u8; 32], pub hash256_pre: [u8; 32], pub ripemd160_pre: [u8; 32], @@ -78,8 +77,8 @@ fn setup_keys( ) -> ( Vec, Vec, - Vec, - Vec, + Vec, + Vec, ) { let secp_sign = secp256k1::Secp256k1::signing_only(); let mut sk = [0; 32]; @@ -102,9 +101,9 @@ fn setup_keys( let mut x_only_keypairs = vec![]; let mut x_only_pks = vec![]; - for i in 0..n { - let keypair = bitcoin::KeyPair::from_secret_key(&secp_sign, &sks[i]); - let (xpk, _parity) = bitcoin::XOnlyPublicKey::from_keypair(&keypair); + for sk in &sks { + let keypair = bitcoin::key::KeyPair::from_secret_key(&secp_sign, sk); + let (xpk, _parity) = bitcoin::key::XOnlyPublicKey::from_keypair(&keypair); x_only_keypairs.push(keypair); x_only_pks.push(xpk); } @@ -115,13 +114,13 @@ impl TestData { // generate a fixed data for n keys pub(crate) fn new_fixed_data(n: usize, genesis_hash: BlockHash) -> Self { let (sks, pks, x_only_keypairs, x_only_pks) = setup_keys(n); - let sha256_pre = [0x12 as u8; 32]; + let sha256_pre = [0x12; 32]; let sha256 = sha256::Hash::hash(&sha256_pre); - let hash256_pre = [0x34 as u8; 32]; + let hash256_pre = [0x34; 32]; let hash256 = hash256::Hash::hash(&hash256_pre); - let hash160_pre = [0x56 as u8; 32]; + let hash160_pre = [0x56; 32]; let hash160 = hash160::Hash::hash(&hash160_pre); - let ripemd160_pre = [0x78 as u8; 32]; + let ripemd160_pre = [0x78; 32]; let ripemd160 = ripemd160::Hash::hash(&ripemd160_pre); let msg = CsfsMsg::from_slice(&[0x9a; 32]).unwrap(); @@ -183,8 +182,7 @@ pub fn parse_insane_ms( let ms = Miniscript::::from_str_insane(&ms).expect("only parsing valid minsicripts"); let mut translator = StrTranslatorLoose(0, pubdata); - let ms = ms.translate_pk(&mut translator).unwrap(); - ms + ms.translate_pk(&mut translator).unwrap() } /// Translate Abstract Str to Consensus Extensions @@ -194,9 +192,9 @@ impl<'a> ExtParamTranslator for StrExtTranslator<'a> { fn ext(&mut self, e: &String) -> Result { if e.starts_with("msg") { Ok(CovExtArgs::CsfsMsg(self.1.msg.clone())) - } else if e.starts_with("X") { + } else if e.starts_with('X') { let csfs_pk = CovExtArgs::XOnlyKey(CsfsKey(self.1.x_only_pks[self.0])); - self.0 = self.0 + 1; + self.0 += 1; Ok(csfs_pk) } else if e.starts_with("spk") { let default = elements::Script::from_str( @@ -234,15 +232,15 @@ struct StrDescPubKeyTranslator<'a>(usize, &'a PubData); impl<'a> Translator for StrDescPubKeyTranslator<'a> { fn pk(&mut self, pk_str: &String) -> Result { - let avail = !pk_str.ends_with("!"); + let avail = !pk_str.ends_with('!'); if avail { - self.0 = self.0 + 1; - if pk_str.starts_with("K") { + self.0 += 1; + if pk_str.starts_with('K') { Ok(DescriptorPublicKey::Single(SinglePub { origin: None, key: SinglePubKey::FullKey(self.1.pks[self.0]), })) - } else if pk_str.starts_with("X") { + } else if pk_str.starts_with('X') { Ok(DescriptorPublicKey::Single(SinglePub { origin: None, key: SinglePubKey::XOnly(self.1.x_only_pks[self.0]), @@ -287,15 +285,15 @@ struct StrTranslatorLoose<'a>(usize, &'a PubData); impl<'a> Translator for StrTranslatorLoose<'a> { fn pk(&mut self, pk_str: &String) -> Result { - let avail = !pk_str.ends_with("!"); + let avail = !pk_str.ends_with('!'); if avail { - self.0 = self.0 + 1; - if pk_str.starts_with("K") { + self.0 += 1; + if pk_str.starts_with('K') { Ok(DescriptorPublicKey::Single(SinglePub { origin: None, key: SinglePubKey::FullKey(self.1.pks[self.0]), })) - } else if pk_str.starts_with("X") { + } else if pk_str.starts_with('X') { Ok(DescriptorPublicKey::Single(SinglePub { origin: None, key: SinglePubKey::XOnly(self.1.x_only_pks[self.0]), @@ -360,7 +358,7 @@ fn subs_hash_frag(ms: &str, pubdata: &PubData) -> String { ); let ms = ms.replace( "hash256(H)", - &format!("hash256({})", &pubdata.hash256.into_inner().to_hex()), + &format!("hash256({})", &pubdata.hash256.to_hex()), ); let ms = ms.replace( "ripemd160(H)", @@ -382,6 +380,5 @@ fn subs_hash_frag(ms: &str, pubdata: &PubData) -> String { "ripemd160(H!)", &format!("ripemd160({})", rand_hash20.to_hex()), ); - let ms = ms.replace("hash160(H!)", &format!("hash160({})", rand_hash20.to_hex())); - ms + ms.replace("hash160(H!)", &format!("hash160({})", rand_hash20.to_hex())) } diff --git a/bitcoind-tests/tests/test_arith.rs b/bitcoind-tests/tests/test_arith.rs index d232fad9..071c0628 100644 --- a/bitcoind-tests/tests/test_arith.rs +++ b/bitcoind-tests/tests/test_arith.rs @@ -59,7 +59,7 @@ pub fn test_desc_satisfy(cl: &ElementsD, testdata: &TestData, desc: &str) -> Vec // Spend one input and spend one output for simplicity. let mut psbt = Psbt::new_v2(); // figure out the outpoint from the txid - let (outpoint, witness_utxo) = get_vout(&cl, txid, 100_000_000, derived_desc.script_pubkey()); + let (outpoint, witness_utxo) = get_vout(cl, txid, 100_000_000, derived_desc.script_pubkey()); let txin = TxIn { previous_output: outpoint, is_pegin: false, @@ -103,7 +103,7 @@ pub fn test_desc_satisfy(cl: &ElementsD, testdata: &TestData, desc: &str) -> Vec Descriptor::TrExt(ref tr) => { let hash_ty = sighash::SchnorrSigHashType::Default; - let prevouts = [witness_utxo.clone()]; + let prevouts = [witness_utxo]; let prevouts = sighash::Prevouts::All(&prevouts); // ------------------ script spend ------------- let x_only_keypairs_reqd: Vec<(secp256k1::KeyPair, TapLeafHash)> = tr @@ -112,7 +112,7 @@ pub fn test_desc_satisfy(cl: &ElementsD, testdata: &TestData, desc: &str) -> Vec let leaf_hash = TapLeafHash::from_script(&ms.encode(), LeafVersion::default()); ms.iter_pk().filter_map(move |pk| { let i = x_only_pks.iter().position(|&x| x.to_public_key() == pk); - i.map(|idx| (xonly_keypairs[idx].clone(), leaf_hash)) + i.map(|idx| (xonly_keypairs[idx], leaf_hash)) }) }) .collect(); @@ -138,7 +138,7 @@ pub fn test_desc_satisfy(cl: &ElementsD, testdata: &TestData, desc: &str) -> Vec (x_only_pk, leaf_hash), elements::SchnorrSig { sig, - hash_ty: hash_ty, + hash_ty, }, ); } @@ -173,7 +173,7 @@ pub fn test_desc_satisfy(cl: &ElementsD, testdata: &TestData, desc: &str) -> Vec let txid = cl.send_raw_transaction(&tx); // Finally mine the blocks and await confirmations - let _blocks = cl.generate(1); + cl.generate(1); // Get the required transactions from the node mined in the blocks. // Check whether the transaction is mined in blocks // Assert that the confirmations are > 0. diff --git a/bitcoind-tests/tests/test_cpp.rs b/bitcoind-tests/tests/test_cpp.rs index ec9cc800..330546df 100644 --- a/bitcoind-tests/tests/test_cpp.rs +++ b/bitcoind-tests/tests/test_cpp.rs @@ -92,10 +92,9 @@ pub fn test_from_cpp_ms(cl: &ElementsD, testdata: &TestData) { let mut psbt = Psbt::new_v2(); psbt.global.tx_data.fallback_locktime = Some( LockTime::from_time(1_603_866_330) - .expect("valid timestamp") - .into(), + .expect("valid timestamp"), ); // 10/28/2020 @ 6:25am (UTC) - let (outpoint, witness_utxo) = get_vout(&cl, txid, 100_000_000); + let (outpoint, witness_utxo) = get_vout(cl, txid, 100_000_000); let txin = TxIn { previous_output: outpoint, is_pegin: false, @@ -167,7 +166,7 @@ pub fn test_from_cpp_ms(cl: &ElementsD, testdata: &TestData) { testdata.secretdata.sha256_pre.to_vec(), ); psbts[i].inputs_mut()[0].hash256_preimages.insert( - sha256d::Hash::from_inner(testdata.pubdata.hash256.into_inner()), + sha256d::Hash::from_byte_array(testdata.pubdata.hash256.to_byte_array()), testdata.secretdata.hash256_pre.to_vec(), ); println!("{}", ms); diff --git a/bitcoind-tests/tests/test_csfs.rs b/bitcoind-tests/tests/test_csfs.rs index 6561bbd6..e414bf3f 100644 --- a/bitcoind-tests/tests/test_csfs.rs +++ b/bitcoind-tests/tests/test_csfs.rs @@ -60,7 +60,7 @@ pub fn test_desc_satisfy(cl: &ElementsD, testdata: &TestData, desc: &str) -> Vec // Spend one input and spend one output for simplicity. let mut psbt = Psbt::new_v2(); // figure out the outpoint from the txid - let (outpoint, witness_utxo) = get_vout(&cl, txid, 100_000_000, derived_desc.script_pubkey()); + let (outpoint, witness_utxo) = get_vout(cl, txid, 100_000_000, derived_desc.script_pubkey()); let txin = TxIn { previous_output: outpoint, is_pegin: false, @@ -113,7 +113,7 @@ pub fn test_desc_satisfy(cl: &ElementsD, testdata: &TestData, desc: &str) -> Vec let leaf_hash = TapLeafHash::from_script(&ms.encode(), LeafVersion::default()); ms.iter_pk().filter_map(move |pk| { let i = x_only_pks.iter().position(|&x| x.to_public_key() == pk); - i.map(|idx| (xonly_keypairs[idx].clone(), leaf_hash)) + i.map(|idx| (xonly_keypairs[idx], leaf_hash)) }) }) .collect(); @@ -139,7 +139,7 @@ pub fn test_desc_satisfy(cl: &ElementsD, testdata: &TestData, desc: &str) -> Vec (x_only_pk, leaf_hash), elements::SchnorrSig { sig, - hash_ty: hash_ty, + hash_ty, }, ); } @@ -162,7 +162,7 @@ pub fn test_desc_satisfy(cl: &ElementsD, testdata: &TestData, desc: &str) -> Vec impl<'a> Satisfier for CsfsSatisfier<'a> { fn lookup_csfs_sig( &self, - pk: &bitcoin::XOnlyPublicKey, + pk: &bitcoin::key::XOnlyPublicKey, msg: &miniscript::extensions::CsfsMsg, ) -> Option { let xpk = pk.to_x_only_pubkey(); @@ -171,7 +171,7 @@ pub fn test_desc_satisfy(cl: &ElementsD, testdata: &TestData, desc: &str) -> Vec // Create a signature let keypair = &self.0.secretdata.x_only_keypairs[i]; - let msg = secp256k1::Message::from_slice(&msg.as_inner()[..]).unwrap(); + let msg = secp256k1::Message::from_slice(msg.as_inner()).unwrap(); let mut aux_rand = [0u8; 32]; rand::thread_rng().fill_bytes(&mut aux_rand); @@ -182,7 +182,7 @@ pub fn test_desc_satisfy(cl: &ElementsD, testdata: &TestData, desc: &str) -> Vec fn lookup_price_oracle_sig( &self, - pk: &bitcoin::XOnlyPublicKey, + pk: &bitcoin::key::XOnlyPublicKey, time: u64, ) -> Option<(secp256k1::schnorr::Signature, i64, u64)> { let xpk = pk.to_x_only_pubkey(); @@ -205,7 +205,7 @@ pub fn test_desc_satisfy(cl: &ElementsD, testdata: &TestData, desc: &str) -> Vec } let psbt_sat = PsbtInputSatisfier::new(&psbt, 0); - let csfs_sat = CsfsSatisfier(&testdata); + let csfs_sat = CsfsSatisfier(testdata); let mut tx = psbt.extract_tx().unwrap(); let txouts = vec![psbt.inputs()[0].witness_utxo.clone().unwrap()]; @@ -227,7 +227,7 @@ pub fn test_desc_satisfy(cl: &ElementsD, testdata: &TestData, desc: &str) -> Vec let txid = cl.send_raw_transaction(&tx); // Finally mine the blocks and await confirmations - let _blocks = cl.generate(1); + cl.generate(1); // Get the required transactions from the node mined in the blocks. // Check whether the transaction is mined in blocks // Assert that the confirmations are > 0. diff --git a/bitcoind-tests/tests/test_desc.rs b/bitcoind-tests/tests/test_desc.rs index 0f2a22f9..76357153 100644 --- a/bitcoind-tests/tests/test_desc.rs +++ b/bitcoind-tests/tests/test_desc.rs @@ -93,7 +93,7 @@ pub fn test_desc_satisfy( // Spend one input and spend one output for simplicity. let mut psbt = Psbt::new_v2(); // figure out the outpoint from the txid - let (outpoint, witness_utxo) = get_vout(&cl, txid, 100_000_000, derived_desc.script_pubkey()); + let (outpoint, witness_utxo) = get_vout(cl, txid, 100_000_000, derived_desc.script_pubkey()); let txin = TxIn { previous_output: outpoint, is_pegin: false, @@ -141,7 +141,7 @@ pub fn test_desc_satisfy( let internal_key_present = x_only_pks .iter() .position(|&x| x.to_public_key() == *tr.internal_key()); - let internal_keypair = internal_key_present.map(|idx| xonly_keypairs[idx].clone()); + let internal_keypair = internal_key_present.map(|idx| xonly_keypairs[idx]); let prevouts = [witness_utxo]; let prevouts = sighash::Prevouts::All(&prevouts); @@ -150,7 +150,7 @@ pub fn test_desc_satisfy( let internal_keypair = internal_keypair .add_xonly_tweak( &secp, - &Scalar::from_be_bytes(tr.spend_info().tap_tweak().into_inner()) + &Scalar::from_be_bytes(tr.spend_info().tap_tweak().to_byte_array()) .expect("valid scalar"), ) .expect("Tweaking failed"); @@ -169,7 +169,7 @@ pub fn test_desc_satisfy( secp.sign_schnorr_with_aux_rand(&msg, &internal_keypair, &aux_rand); psbt.inputs_mut()[0].tap_key_sig = Some(SchnorrSig { sig: schnorr_sig, - hash_ty: hash_ty, + hash_ty, }); } else { // No internal key @@ -181,7 +181,7 @@ pub fn test_desc_satisfy( let leaf_hash = TapLeafHash::from_script(&ms.encode(), LeafVersion::default()); ms.iter_pk().filter_map(move |pk| { let i = x_only_pks.iter().position(|&x| x.to_public_key() == pk); - i.map(|idx| (xonly_keypairs[idx].clone(), leaf_hash)) + i.map(|idx| (xonly_keypairs[idx], leaf_hash)) }) }) .collect(); @@ -207,7 +207,7 @@ pub fn test_desc_satisfy( (x_only_pk, leaf_hash), elements::SchnorrSig { sig, - hash_ty: hash_ty, + hash_ty, }, ); } @@ -216,7 +216,7 @@ pub fn test_desc_satisfy( // Non-tr descriptors // Ecdsa sigs let sks_reqd = match derived_desc { - Descriptor::Bare(bare) => find_sks_ms(&bare.as_inner(), testdata), + Descriptor::Bare(bare) => find_sks_ms(bare.as_inner(), testdata), Descriptor::Pkh(pk) => find_sk_single_key(*pk.as_inner(), testdata), Descriptor::Wpkh(pk) => find_sk_single_key(*pk.as_inner(), testdata), Descriptor::Sh(sh) => match sh.as_inner() { @@ -225,7 +225,7 @@ pub fn test_desc_satisfy( let ms = Miniscript::from_ast(smv.sorted_node()).unwrap(); find_sks_ms(&ms, testdata) } - miniscript::descriptor::WshInner::Ms(ref ms) => find_sks_ms(&ms, testdata), + miniscript::descriptor::WshInner::Ms(ref ms) => find_sks_ms(ms, testdata), }, miniscript::descriptor::ShInner::Wpkh(pk) => { find_sk_single_key(*pk.as_inner(), testdata) @@ -234,14 +234,14 @@ pub fn test_desc_satisfy( let ms = Miniscript::from_ast(smv.sorted_node()).unwrap(); find_sks_ms(&ms, testdata) } - miniscript::descriptor::ShInner::Ms(ms) => find_sks_ms(&ms, testdata), + miniscript::descriptor::ShInner::Ms(ms) => find_sks_ms(ms, testdata), }, Descriptor::Wsh(wsh) => match wsh.as_inner() { miniscript::descriptor::WshInner::SortedMulti(ref smv) => { let ms = Miniscript::from_ast(smv.sorted_node()).unwrap(); find_sks_ms(&ms, testdata) } - miniscript::descriptor::WshInner::Ms(ref ms) => find_sks_ms(&ms, testdata), + miniscript::descriptor::WshInner::Ms(ref ms) => find_sks_ms(ms, testdata), }, Descriptor::Tr(_tr) => unreachable!("Tr checked earlier"), Descriptor::TrExt(_tr) => unreachable!("Extensions not tested here"), @@ -272,7 +272,7 @@ pub fn test_desc_satisfy( testdata.secretdata.sha256_pre.to_vec(), ); psbt.inputs_mut()[0].hash256_preimages.insert( - sha256d::Hash::from_inner(testdata.pubdata.hash256.into_inner()), + sha256d::Hash::from_byte_array(testdata.pubdata.hash256.to_byte_array()), testdata.secretdata.hash256_pre.to_vec(), ); psbt.inputs_mut()[0].hash160_preimages.insert( @@ -286,7 +286,7 @@ pub fn test_desc_satisfy( println!("Testing descriptor: {}", definite_desc); // Finalize the transaction using psbt // Let miniscript do it's magic! - if let Err(_) = psbt.finalize_mut(&secp, testdata.pubdata.genesis_hash) { + if psbt.finalize_mut(&secp, testdata.pubdata.genesis_hash).is_err() { return Err(DescError::PsbtFinalizeError); } let tx = psbt @@ -299,7 +299,7 @@ pub fn test_desc_satisfy( let txid = cl.send_raw_transaction(&tx); // Finally mine the blocks and await confirmations - let _blocks = cl.generate(1); + cl.generate(1); // Get the required transactions from the node mined in the blocks. // Check whether the transaction is mined in blocks // Assert that the confirmations are > 0. @@ -307,7 +307,7 @@ pub fn test_desc_satisfy( .as_u64() .unwrap(); assert!(num_conf > 0); - return Ok(tx.input[0].witness.script_witness.clone()); + Ok(tx.input[0].witness.script_witness.clone()) } // Find all secret corresponding to the known public keys in ms diff --git a/bitcoind-tests/tests/test_introspect.rs b/bitcoind-tests/tests/test_introspect.rs index f95a4d93..e9c78cba 100644 --- a/bitcoind-tests/tests/test_introspect.rs +++ b/bitcoind-tests/tests/test_introspect.rs @@ -59,7 +59,7 @@ pub fn test_desc_satisfy(cl: &ElementsD, testdata: &TestData, desc: &str) -> Vec // Spend one input and spend one output for simplicity. let mut psbt = Psbt::new_v2(); // figure out the outpoint from the txid - let (outpoint, witness_utxo) = get_vout(&cl, txid, 100_000_000, derived_desc.script_pubkey()); + let (outpoint, witness_utxo) = get_vout(cl, txid, 100_000_000, derived_desc.script_pubkey()); let txin = TxIn { previous_output: outpoint, is_pegin: false, @@ -103,7 +103,7 @@ pub fn test_desc_satisfy(cl: &ElementsD, testdata: &TestData, desc: &str) -> Vec Descriptor::TrExt(ref tr) => { let hash_ty = sighash::SchnorrSigHashType::Default; - let prevouts = [witness_utxo.clone()]; + let prevouts = [witness_utxo]; let prevouts = sighash::Prevouts::All(&prevouts); // ------------------ script spend ------------- let x_only_keypairs_reqd: Vec<(secp256k1::KeyPair, TapLeafHash)> = tr @@ -112,7 +112,7 @@ pub fn test_desc_satisfy(cl: &ElementsD, testdata: &TestData, desc: &str) -> Vec let leaf_hash = TapLeafHash::from_script(&ms.encode(), LeafVersion::default()); ms.iter_pk().filter_map(move |pk| { let i = x_only_pks.iter().position(|&x| x.to_public_key() == pk); - i.map(|idx| (xonly_keypairs[idx].clone(), leaf_hash)) + i.map(|idx| (xonly_keypairs[idx], leaf_hash)) }) }) .collect(); @@ -138,7 +138,7 @@ pub fn test_desc_satisfy(cl: &ElementsD, testdata: &TestData, desc: &str) -> Vec (x_only_pk, leaf_hash), elements::SchnorrSig { sig, - hash_ty: hash_ty, + hash_ty, }, ); } @@ -173,7 +173,7 @@ pub fn test_desc_satisfy(cl: &ElementsD, testdata: &TestData, desc: &str) -> Vec let txid = cl.send_raw_transaction(&tx); // Finally mine the blocks and await confirmations - let _blocks = cl.generate(1); + cl.generate(1); // Get the required transactions from the node mined in the blocks. // Check whether the transaction is mined in blocks // Assert that the confirmations are > 0. diff --git a/clippy.toml b/clippy.toml index 799264ef..11d46a73 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1 +1 @@ -msrv = "1.41.1" +msrv = "1.48.0" diff --git a/contrib/test.sh b/contrib/test.sh index 72b683cf..3af30079 100755 --- a/contrib/test.sh +++ b/contrib/test.sh @@ -7,10 +7,9 @@ FEATURES="compiler serde rand base64" cargo --version rustc --version -# Pin dependencies required to build with Rust 1.41.1 -if cargo --version | grep "1\.41\.0"; then +# Pin dependencies required to build with Rust 1.48 +if cargo --version | grep "1\.48"; then cargo update -p serde --precise 1.0.156 - cargo update -p syn --precise 1.0.107 fi # Format if told to @@ -20,17 +19,6 @@ then cargo fmt -- --check fi -# Fuzz if told to -if [ "$DO_FUZZ" = true ] -then - cd fuzz - cargo test --verbose - ./travis-fuzz.sh - - # Exit out of the fuzzer, do not run other tests. - exit 0 -fi - # Test bitcoind integration tests if told to (this only works with the stable toolchain) if [ "$DO_BITCOIND_TESTS" = true ]; then cd bitcoind-tests @@ -68,7 +56,7 @@ fi # Bench if told to (this only works with the nightly toolchain) if [ "$DO_BENCH" = true ] then - cargo bench --features="unstable compiler" + RUSTFLAGS=--cfg=miniscript_bench cargo bench --features="compiler" fi # Build the docs if told to (this only works with the nightly toolchain) diff --git a/examples/htlc.rs b/examples/htlc.rs index b285ed56..74663b7b 100644 --- a/examples/htlc.rs +++ b/examples/htlc.rs @@ -14,7 +14,6 @@ //! Example: Create an HTLC with miniscript using the policy compiler -use bitcoin; extern crate elements_miniscript as miniscript; use std::str::FromStr; diff --git a/examples/psbt_sign_finalize.rs b/examples/psbt_sign_finalize.rs index f4849f18..1d5a8a4b 100644 --- a/examples/psbt_sign_finalize.rs +++ b/examples/psbt_sign_finalize.rs @@ -4,8 +4,7 @@ use elements::bitcoin::PrivateKey; use elements::encode::{serialize, serialize_hex}; use elements::hashes::Hash; use elements::sighash::SigHashCache; -use elements::{confidential, AssetId, PackedLockTime, TxOutWitness}; -use miniscript::elements::hashes::hex::FromHex; +use elements::{confidential, AssetId, LockTime, TxOutWitness}; use miniscript::elements::pset::PartiallySignedTransaction as Psbt; use miniscript::elements::{ self, pset, secp256k1_zkp as secp256k1, Address, AddressParams, OutPoint, Script, Sequence, @@ -21,7 +20,7 @@ fn main() { let secp256k1 = secp256k1::Secp256k1::new(); let s = "elwsh(t:or_c(pk(027a3565454fe1b749bccaef22aff72843a9c3efefd7b16ac54537a0c23f0ec0de),v:thresh(1,pkh(032d672a1a91cc39d154d366cd231983661b0785c7f27bc338447565844f4a6813),a:pkh(03417129311ed34c242c012cd0a3e0b9bca0065f742d0dfb63c78083ea6a02d4d9),a:pkh(025a687659658baeabdfc415164528065be7bcaade19342241941e556557f01e28))))#tdp6ld3e"; - let bridge_descriptor = Descriptor::from_str(&s).unwrap(); + let bridge_descriptor = Descriptor::from_str(s).unwrap(); //let bridge_descriptor = Descriptor::::from_str(&s).expect("parse descriptor string"); assert!(bridge_descriptor.sanity_check().is_ok()); println!( @@ -74,7 +73,7 @@ fn main() { let spend_tx = Transaction { version: 2, - lock_time: PackedLockTime(5000), + lock_time: LockTime::from_height(5000).unwrap(), input: vec![], output: vec![], }; @@ -89,7 +88,7 @@ fn main() { let amount = 100000000; let outpoint = elements::OutPoint { - txid: elements::Txid::from_hex( + txid: elements::Txid::from_str( "7a3565454fe1b749bccaef22aff72843a9c3efefd7b16ac54537a0c23f0ec0de", ) .unwrap(), @@ -100,13 +99,15 @@ fn main() { // In practice, you would have to get the outpoint and witness utxo from the blockchain. // something like this: - // let depo_tx = elements::Transction::from_hex("...").unwrap(); + // let depo_tx = elements::Transction::from_str("...").unwrap(); // let (outpoint, witness_utxo) = get_vout(&depo_tx, bridge_descriptor.script_pubkey()); - let mut txin = TxIn::default(); - txin.previous_output = outpoint; + let txin = TxIn { + previous_output: outpoint, + sequence: Sequence::from_height(26), //Sequence::MAX; // + ..TxIn::default() + }; - txin.sequence = Sequence::from_height(26); //Sequence::MAX; // psbt.add_input(pset::Input::from_txin(txin)); psbt.add_output(pset::Output::from_txout(bitcoin_asset_txout( @@ -131,7 +132,7 @@ fn main() { .update_with_descriptor_unchecked(&bridge_descriptor) .unwrap(); - psbt.inputs_mut()[0].witness_utxo = Some(witness_utxo.clone()); + psbt.inputs_mut()[0].witness_utxo = Some(witness_utxo); let tx = &psbt.extract_tx().unwrap(); let mut sighash_cache = SigHashCache::new(tx); @@ -166,7 +167,7 @@ fn main() { println!("{:#?}", psbt); let serialized = serialize(&psbt); - println!("{}", base64::encode(&serialized)); + println!("{}", base64::encode(serialized)); psbt.finalize_mut(&secp256k1, genesis_hash).unwrap(); println!("{:#?}", psbt); @@ -193,7 +194,7 @@ fn bitcoin_asset_txout(spk: Script, amt: u64) -> TxOut { script_pubkey: spk, value: confidential::Value::Explicit(amt), asset: confidential::Asset::Explicit( - AssetId::from_hex("088f6b381694259fd20599e71f7eb46e392f36b43cc20d131d95c8a4b8cc1aa8") + AssetId::from_str("088f6b381694259fd20599e71f7eb46e392f36b43cc20d131d95c8a4b8cc1aa8") .unwrap(), ), nonce: confidential::Nonce::Null, diff --git a/examples/sign_multisig.rs b/examples/sign_multisig.rs index 80f01ed1..6da5d93b 100644 --- a/examples/sign_multisig.rs +++ b/examples/sign_multisig.rs @@ -19,7 +19,7 @@ extern crate elements_miniscript as miniscript; use std::collections::HashMap; use std::str::FromStr; -use elements::{secp256k1_zkp, PackedLockTime, Sequence}; +use elements::{secp256k1_zkp, LockTime, Sequence}; fn main() { // Avoid repeatedly typing a pretty-common descriptor type @@ -28,7 +28,7 @@ fn main() { // Transaction which spends some output let mut tx = elements::Transaction { version: 2, - lock_time: PackedLockTime::ZERO, + lock_time: LockTime::ZERO, input: vec![elements::TxIn { previous_output: elements::OutPoint::default(), script_sig: elements::Script::new(), diff --git a/examples/taproot.rs b/examples/taproot.rs index e2ca4827..aad423ab 100644 --- a/examples/taproot.rs +++ b/examples/taproot.rs @@ -3,7 +3,7 @@ extern crate elements_miniscript as miniscript; use std::collections::HashMap; use std::str::FromStr; -use bitcoin::util::address::WitnessVersion; +use bitcoin::address::WitnessVersion; use miniscript::descriptor::DescriptorType; use miniscript::policy::Concrete; use miniscript::{ @@ -15,18 +15,18 @@ use secp256k1::{rand, KeyPair}; // for a detailed explanation of the policy and it's compilation struct StrPkTranslator { - pk_map: HashMap, + pk_map: HashMap, } -impl Translator for StrPkTranslator { - fn pk(&mut self, pk: &String) -> Result { +impl Translator for StrPkTranslator { + fn pk(&mut self, pk: &String) -> Result { self.pk_map.get(pk).copied().ok_or(()) } // We don't need to implement these methods as we are not using them in the policy // Fail if we encounter any hash fragments. // See also translate_hash_clone! macro - translate_hash_fail!(String, bitcoin::XOnlyPublicKey, ()); + translate_hash_fail!(String, bitcoin::key::XOnlyPublicKey, ()); } fn main() { @@ -89,7 +89,7 @@ fn main() { let secp = secp256k1::Secp256k1::new(); let key_pair = KeyPair::new(&secp, &mut rand::thread_rng()); // Random unspendable XOnlyPublicKey provided for compilation to Taproot Descriptor - let (unspendable_pubkey, _parity) = bitcoin::XOnlyPublicKey::from_keypair(&key_pair); + let (unspendable_pubkey, _parity) = bitcoin::key::XOnlyPublicKey::from_keypair(&key_pair); pk_map.insert("UNSPENDABLE_KEY".to_string(), unspendable_pubkey); let pubkeys = hardcoded_xonlypubkeys(); @@ -120,7 +120,7 @@ fn main() { assert_eq!(addr, expected_addr); } -fn hardcoded_xonlypubkeys() -> Vec { +fn hardcoded_xonlypubkeys() -> Vec { let serialized_keys: [[u8; 32]; 4] = [ [ 22, 37, 41, 4, 57, 254, 191, 38, 14, 184, 200, 133, 111, 226, 145, 183, 245, 112, 100, @@ -139,9 +139,9 @@ fn hardcoded_xonlypubkeys() -> Vec { 168, 39, 134, 58, 19, 181, 3, 63, 235, 103, 155, 213, ], ]; - let mut keys: Vec = vec![]; - for idx in 0..4 { - keys.push(bitcoin::XOnlyPublicKey::from_slice(&serialized_keys[idx][..]).unwrap()); + let mut keys: Vec = vec![]; + for key in &serialized_keys { + keys.push(bitcoin::key::XOnlyPublicKey::from_slice(&key[..]).unwrap()); } keys } diff --git a/examples/verify_tx.rs b/examples/verify_tx.rs index 219e8b09..751025ee 100644 --- a/examples/verify_tx.rs +++ b/examples/verify_tx.rs @@ -12,6 +12,8 @@ // If not, see . // +#![allow(clippy::single_match)] // this example looks nicer with match than `if let` + //! Example: Verifying a signed transaction. extern crate elements_miniscript as miniscript; @@ -27,7 +29,7 @@ use crate::miniscript::interpreter::KeySigPair; // secp256k1 re-exported from ru fn main() { // some random liquid tx from mempool(Dec 3rd 2020) // txid: f23c8973027aa8c1e86580a729833914f5b1fa710367db07f1f5515aa3729f16 - let tx_bytes : Vec = elements::hashes::hex::FromHex::from_hex( + let tx_bytes : Vec = elements::hex::FromHex::from_hex( "02000000010232228caa1ed022bba919d55646169e6094fcff7580cfd51767c52ad1f1f7be1501000000171600142e7c87cd55d9163b58a0e98cb0c076e8fbc916c5feffffff28b3920a83b2507ca5f3993c84cf7b69021be6887e86e1ec8855ac432199de6b0100000017160014edfc058e1c059532d7aa90cfd47d1bf4f7d2d9b3feffffff030ae977a025f8432195181d77bc6523b7769b8b4d4c3cf8f5a45bdb940c7dfb561e0878ac4e6ab21b09f64a262d0342e88928cfa68bc9e8d004109f9caca1b305d37e0217abccf88d3488db1e40f79dfaf14ca816431441095bcb2570c9a0dcca01456c17a914b5687f4af9ba5dfb00ed22cb32a857e607bc844d870b892d93c6accf6253edd8340a6c27caaee3d5dc067e626d3c1647dd895299404a0936e562586398b4e95fb34c108afd4a9de4268bd5cd41749833e7a9265e383a0e02658aa567bb9656cd06b563855dfd63fb3e8b37b7991f86895c536af5692e819d17a9149f03346fa3e2a4fbeed0e5c379f2f2ba28025bc187016d521c38ec1ea15734ae22b7c46064412829c0d0579f0a713d1c04ede979026f0100000000000001060000e6bf1000000002473044022020d643f80c3cab90a76dfca198bdba119db97cf4fd8d8adfa2609cdbcf594f570220324c54a8e3170d06cea60511e5e8a84d1f41b21fc252181d69aa8b7d190c26a90121029e831ee2345cc698c730b713b82b99e8c16ffe5ddd51c9b36607bb1fa68b1395000000024730440220645f22a912344b4f8d165814c3513e9bfa06d42066fa4d392f783a57444eff0202200b98199177d9e13c9f3872f433154be430e82cc2e312791a47bea2a91167ff4c01210303a640e79b1e941f2d7867e4eabffd79f30daaabc80fe1c4d974775fc29339810063020003c7b8cf924dff6cef0c9f9b507abc741e431e1a793206b5fca623748f583e6ad3f3f7a4fd1b27d78bf3b4debc9a2c849b5f85722cab872a9c08f19e6eb59ceef74fcf9a7c38acd6fdb7821c33bc731011b6440e8aae7beff3ee6ffc1009c849bcfd4e10603300000000000000012361c401dc40cd92cf93029e64c20c463d17c6e15d2997ab43e8c4b1c412b99a1a337c3cff34c6e7269580727c27404a131606aa85dd8bdf6d46b6ddb23948a0507e31666b531e753eb8fece006050bc7b7e2e06db86833f9adc4f6b77c7b88537617c3accea246a8fe303a23697801181efa8bbcded7690aeac04b58e32f4cd94279cff059a5c9f679d973007042fa1e5e4268a884dbce7acba614d57e1f7a56f81354779ab353c04806ad280417f656a10bcbc683348608d7f6e08367d9c21318cfe6f03a0a60bb8269d667a3ddd57f77f8774364e6a946a6163896db4a8b7e2446d229d8d3718f9a8d7b57a2dfe43baa79bce705512635c775602404d55a2a61fee93c00f1e0cc466bf2cf0bc070591e228de578bc51a38b1927ae29f0e7daad95380f5be599f50367ae1028e57f2e1d9b35d45f0fd860491c2c03a46500f2fabf3c8c7cc9ede16af01eb3d42633ddb8ef87bc043c136f727fffcb4aebdb19f674864e2d2a20aa0d61f7e635c582a79320ff2dbbe8ee74c4c51ee09e03e89581378fcc4d2a6f44524add57be3d31d3054468662a805fb82bd047e5b52e13415ea92c57532940985ded222477f5bed2bdedbbe1f2471b0a85c32a70b0d07a5c734ce90e62b51babee95c63f2cd5ba7b6716a9ecb2f44e5f30f2bc07ea7fef5257aff812558385b23966608191436cbeb1f9fc6a907c1841e1dfcea0e5719749969d447bbb9b332389b2e046d107a873c1b557319250c6469ea03c7bd2c61e6a31963921f1854c5546479044dc992f09c2e781c782fa7dc116074787495bedd991b530a7a8b49bd0f85e32ea19bdfac28152bf63eef1a8bd9294b51eaba8b5bc4db1d89031e4dafffff9bae4329fce27eac3db83e6d5d22ec4e04850b7af49329828e087f2c998882249cb74234002c6ea20641f6b4dd0199e4d6d932e75cf0e59a07b25724826fdfedae239d8275c992456c53a2d4c469f08120554e86c4693c32284616279eeb048d434288f6f73dec16d3d37ef0ed2a98e5c11cf6ac046137bbd8b76c1ebb00e8804133e0e647751ab6b395cc49c92d5084bbac242cbd8369c8f56548ca92b8a1da586e12f6163d2d0b42b4774e6c8eac23240c51ec79d71dbd1a69441265c6d5e4974e3ef6358b30106649744c9799f6ec1de0cc98929f02be8396c4d807e87a767e3e2903633da774ff39141e14aba0c01fe3331d7b888360722ebb1c3e1fdb1b3226ecd2003cfe2400ec9ef29f431d6e5182eb98c8aa4384b60dca8dd0d28ea52fdb6b181c4788b6d0d2ce9c5dad4dad259a83eda33c21704d4230660f8962c69d290e77e60f16916d5e1bf811eadd054d464fe33232541070371a69b120402a9598ceeb9c8ffe0622c90fcffcf271eda70128ed21c5c1990c3361c11f7f697eb56a32bc15c5e79596274043eb80da990fb40e31a0d8a83d62e53e9d9f3f786de418ee3c9a1adc54ebb43b74db84d550dc8fb64aa5a0947f23d9e07fc370e141217b0766186315ad088c2ccbb426e3a184ad74b5469ff24b596670617bb967aaf8976438415d851bc89a98887e2388161c967b8ae9e5814af518097c4e0604b6ecf755527ade18ae0a50234a95f4b45c1bf68ada13991928dc88b61abf23445ec95d3f06830852bba27e09a7747b1c06a1d4948214be7e0bb47f2c14f6ac02715a250232ed15767da55717adb239149d8021f33bb14316622947d98d30506aaabadf90add3cd639f5e558450642ac7b4457da522efb710124518a0a3f41b6798127d209768cc526ecc9a6f70edbe3c3b9f45536295c2bcccc384b0d13bd6d741e8d1d01ac83e09cf35a9e7ff71ffbe2926d8b3c5d5eb131fe9b023d427c4c8c3e4310df6b3c45c2de0092df59e98176b1bcce17889b3667854785a668e3c08aec571506e5074ddc5e53eba12a5c2adde7a0fb7c1e5b86b1463e7634980985dff039d90abdb0d11cc701bc8122e22000d3eb4ee0ef422a08e85bf13d5bc856e79c0eb00f415a8a76d6d7bcb087b090954e0476c81ad1ba3ef89b8b5338c78a8c0b5b25cda36ff35c307900066fcf775cf71069a1e396b0efc611b21ecde8b5f080252d16690767137307aade91486ff23fd4ebd206d0b9bbe9f2221003967fb76e51925954fd5df40d0c331277c35bc7bd40c05ed32a4cf577ccd5219b113df6454258cf1cdf123e8ffb7fab3debb05ac9f3a3874221bc6d08db4c0e2676257ba26d2b780c6102014f980987c6826cdab6a713cbfeae703af2dd8838378dec58b406ce2e796a560e1bf1f7f4d09f32c0cad9ad2fc3720ce3963273b6768250c3119eb4d9c0c722cf64733432fa76abf07e845d55f089df83dfb784cd7bae06e1fa26b7464667945774f2c61afac025d4f5dcc909cfaf064ff9b622b680eab1e5ba31b8374fd2402ffae1481cb8ef180060c8ac01aac5a4f9df8b5e84b7872cdeda17cc5b37a4438b6c31b917a88336cbbd535f95710f1d4bda8dee8f9613c0e62e9914dbc8f2f812783b7b8a154c74c17ceb1df61880f61a9038ade4a15542a2258d0533988303e1d22910448fa3d8aa29660efc1af8d8c470da4071960ab55a83627da6c1d955e337fff0f18e6e4b0516ef21beabdd3174ffe816ac945ab7c54a60a56898fce13b782820ad6b6e4f72a82122366695b4d5c332490fe8caf98b3e77749ac06fcaa0a1be7e283201e7dd0ec43cb728da2597af4a4d1d20836d1d443c6fd24d57881ec9b9d0af6b99f04511ba2f526c1647f0c67328f7ea7952bbeaf41198ba56ddeca73e8f1e028d64c9e8bc9515aa9f16830add7c705d6aa4fbf20ba5aef213088d6e4ef4453dfe0b0c531ea19ba772969881e5945e353b43ce2940d9b6898855092f6426b32ff0f1037e1d9799e30d93b1b1df71b70bdc52fcd9db0cc87c55894244da38cc6534dae903d84f7b636cac2d26ee2ee3c60a60c9482dcf3ad679936b6fb4e6a16bd3275f6452a0a47dc0f5acdcf9fe17c9fb34ac669ff277964d5d77062d4dc69ffb3f376665972fc8300df0d3eca41c769891013cb14d70b7600300d030e3ad5d6f9394786551857c5beb49c0eab658b6253ad647365912062b1895ceb0e5ddcfbc73034414bb7eef9bc7a201f735d67edfc7c07c89f3ea8a98bdef324f606ed2b4b3989d0d019b73cd8ac9bdf062c53a40c13ca97f848e152756c388bc54d4ec86104962ea8df3a9430ee4f1bf6eb57862faf2cd49e21db5982dfb5386a23b47b4d010b4ad79f600161ba2d5307b9199977493c1e8a696d2aed4b85d7d52074438c76203baaa50bd96f5d2c9063b4e3033f090021c996261344e44aece16bf926614a5163b70d7f6a03d13462e0858b1911763700503b96505f471bf084d966dd7cd8603e3ffb9b22c572526526d8568e6bb55fa77bfe334ffee4edab2a58719aab70e9e8e8102eadb5391d6a188006a42daee2b2ca71da99b80360fadc4710e7a33d8c3173cc69b5824d93b5021718a276b0ab706029d4378df2fd1183cff056d463a2984c5aa397b9cafd1d3e4a209c04e499130980265d5e2930638bd12656145667bf0dfa09bb61c5c6801d158fead98b3ccc6619c5ff643bc629a57cd6e9b535033d29d4ebdfcff607c50b6ff979fd22fc1a07d796672d98d8b2be6c6c4a4f4911179d18cb29c4c8c490a091d334975033e2ae1a0db38f77ee252f22d4555d0202b39671b6ff29d564cf7b0252ed748cb1a5770bb5aa913d37a7e6e86814e1f34baa5ce474fdaca3bb316cebc4e7893c6452d856a4245434b26b52a37d8aab8f5da3a325e313858e40a9405307de7f4e80d25b3cddea6c75f8595a1c235005a3f0fc948019cfe9e68e3cd0a2affb2f073374d0a03ac55ce9644a92b28409173f763e6a1f7946c1d16ac14a792bd5aea57e404d8a684bdea0b430572d2e5034c949e6c9e74a989d0f91dd740b174e4853e2e693c011d2d29e50bef9fee62079eafe87516448c5a045a16eb569314c9410d4363619ea19aee6460e72440d1cc2a368429c99f10dc8707662d64262113d8eda7842a6233e9ec5c1f7d8c71dc6efedc4bfed53fa9646171caa26daaa0bf4ce50d7b02864ffa0d860014e4d08425f0c1fb840d69a55ec84a833b45dd4572d05d2bcd7dbc6a78974dd2ed0bd52a0343fbd5421c05900da0b7c4e9e42e59183bd2f6e5058482fdd165e346b3ca22f35934538e97110321709394ee446087d97630aa17305fd5d1fd697a4ff3477fc7d02a65afd16e2a5aeccf9402c9dc90f4550140b6bebe6554203e44b9b02472a47c524f4a9554dc25484eb8f173a878a975cdabdfb7993b5e097dc04ef93a7833e51a61b64a6b62743252bf60bf665b9c398688eb24db0d8d2acea848859864f426c08649d29aa7e1d1027318d55624ef750572616ca5b5a118afb83b6e0c872c29a5388f954fcaf8f6d0e67396bd54b9115abe02ffdf52878cd55cb0c5d97cedf41794880ca2b88f960a37d0241ad3e268b3ed142285cfb464c9b0917234df19aa37daa95b935b9f035687d1ac0b7334855e5bf0473175d54c31cf520735d04f5b4ac272cb57999fbb8cf49bd5de6b44616c6a103f53dfc86f874cd847c5adda25c50f3615c0c9c63b34c32142ed62e49f72921fd35753e0ec9333add42838b383684e5246c154cd3ff6310933a36798a4020a027e8f0f951f0dc2dcdb26f0922c69a8dee7183332aa534fa25523469e7bca5917f37fc822b4e7ea5123ef1b8250b2d3dfc9c24ade46394ade3694d528db41108801c3618fd98878e837794023e5ca756e584fd2853829c53380bbb8a281998db43e3ee44e4e6cd08a46c958fee413af351467b7c8275f4c74d4111614eb90b3b10c044c88e1c00f3c2b7959b71bbabb25afa0f564b1449ca585066cd77721fbde395a2c6337be3d546a58498c5f21abf8177c7299fca76e4ae951847858f50fb07cfb10744b770f115e35f57dc70858cafe3a9f66fa406b8a5da94a07699672eefb557fa6adbea267961fd9e4af73a1efdea261a249a573258dae670de24a3d96f36cd146d02370921cacdc155b30a80ad5b43c5db9390ee94e5fcb33b574f82e039371d25fe1128effcb536d5d53727ef341043dd7b07db4c41c117356b3c5379e486f19a4bdef1d3afd4cd0f5fced5a2fba436ee2b84d0b326cc4fe19e69ce05906d3e36c62d9b9a4695bd3ec0d0e5c5e8578dc36094fe879702cf95577a2616244fa3dd7f07c998836793f367d973f640138896f18461b877df9c5a86b046bfab5ac5692d85c5ae632ac51dd726aea59ff471e3f50579204ba6c5868530205faaf81821a57d462ffc697bdd82ba50edd8d3d7973235b57be2d0f9a84ded1d0f3cb68555c2300987f2d14768d2f0214abaf29a06d5d37c5296b5c2db62b2169e1a7bd425cef00db2745d5e246f66191c522f58f0c14fb1d1c8fe6b5b95a45570334125c190a3a9a3e77deb9ae7ca45d973fc64a89d2bb6420286ce01649d5de80ccf89d34bc0c2d9e49d8ec713d2c83286bded0d30a8c42740678c6538d8a7e491b2d1007df7d5037016018c6bd36a21247c13c1cccad168a6a0c2dab4c01185b6d61c31f0f2410b83c16a58d7cdcc0f2522fbe62fb27b1094f77229bff259433e9265d5656931b6d47210bd1e1a66a6c6a3dc9c6a2912a8fc1f05eecf40b751555e569ac2f92d1e5b540037c0cf5c9e658a17fb9dabdd23a087ebb90411f5e75f70bc7786855a2adbebd889d1946ecd6be827e2a624e256c36226ec87f2b92f978be8ffa98169f8dd4947390509d2397322da63757dff25a86f9872414209c50983c570558fc1a630d3caa8f6a59a16cd024f00b2323d2884187ec92feb19e078690f51940229c6076dea7064e71fcc6c55ffd643ccff5b63020003f6512e0b2e6dd0d8cf0a8cde59f4ae60632a902889a27e469b0e844d8477b471a779f720b7650e56645f8a044bebcf4660405a5185e75d13d0c15a221fc707860cfc260b9f9ab3c98e616cc574bd05a494faea98c1e06f9c7ff9643b72692a4afd4e1060330000000000000001cda9b801f96b13ea639d611a15e8f3e899ccf6718b7add1e0309d6c16a3f9b5699cea2fe90fb96147e22f3b025013bb9e7e824614a2c2c2e2e9a420a98b91d71170d89e7aebb414266337a636ec3031021a51701dda9113e9c1ce1291fa9ccab9dd8f30ab98d9c5ba12894af05b42573202c8a5a35d4a49dea9873c2f9aa51ee263a10e49cced91debeff76caca98e07cb7e7705fb647f3ea846cafa1ea4e8449022aedc5210ac18c0a2585a435cc2249cc997aa7e2e180fcee7ee92b1dfee29b3fb125c3d78938b8e6cec68f9fa16643460617013d0bf65b453a124fb843326020adba9ea96bb1832cefd6ac4126329187329b1f31f713b097d34cd2be2c42b70db5e5153a7c5508496f4608c8b7c1c3d8fa7dad87c76c06c278caf0a429d49f4d4fe367894ed493b1e91f331e0533b8364241527adbb867a29bb47fa8e8b5156d5c0ec9042c900f5b9bbe352488b0c671bcd381f090b7fd5ce469d21e01cf5b3f01fcdd51b3fb0eefb4145de49a9444ab161522dbd465169f6d9814b2646142fcc9020aee9742246442a63486e80aa341c81be71f8568080f7d700308f0f79dd8295345da0bd852f5aca16e5fc0e686d7642ef70f46f6d836f3a5bd2b14919fcd5e55ed2dfa8d6920681cc26d1a31f68bf6580c6693b8b877630746cf3a4bea53b4862872941f535345e69a7e7f0108cc72b741a952f4aee5436e918c6fae307277cf20b1fc4035f9235b82c18a24e3159563afd240b0cbe671f2138e6d3b9a4efb5e073b5428e4073a3610c79643b441165121d2d85a311f334b751fac62afec8e1bab82138c5ccd2154f1e00d11e00fae9fc1621ed68e1a32c71b7bd98d5d6328bf7bb3ab5227391eed36335fccd1f83a4b6854a2ddeca1942f8478391be3468693cec629918dd66f00f3a520b495c7f6eaa22aa90a3ee22f29c9ff4e473b739e7a7581411ae966f0d5aa9f157d33066f868a39a0af75199fe886d62c8112ba5242becc11cbbf038c28c41674c643d24448ec7336c98dec3ce09db944476df456e661dec9ffe471243b16888496d17093cb21f0cf5360c884107ed584a24ea151f6f91ddbf92848afe9ca936aa68f992d13aa42e470284454e3b7fe31ec28c33480cbd17d44b941f4f99fe269aa51319cdae2f454d548a41d801061beb6466cf3c27c0efdbf4ea556d57dfd9f52798214f73c3506525426a59820d037285f61465a141e394de9970b6c9b6297ada4d4d65fc5b9921a8f9e6aad8a9ab2d587f193b741c8e5ed879498e7374bf1744f6a72566745e21012cec8412ea8b8eab9712c740cc71c1c798c1d2df850426f69ce311ff1f537b87ae9a31935525eda54780f15dc73214043ece83f1629e36c55747af1ad8e84fa5e535422b09db9a3145022691e189c9ccbacae70ebea3f6b5f2e09376b6402767c85a2ede43761b4ee5edb436d9f711dfdd84e1ff0dbd35c607ea59d9d5c8adf7fa4bf08634e8cb5027da6bb8a8cd7ba1e87c40be3bd9c5554fae6bf7683d25a57320b63d20c66f0df976dbd688e8c4ccf0dbe755f0d31fd6d1b81c6f1ea0538f73c99cd239efbe92b32b946f07b8b53aba95f05d9e0686e188027061c0c912bf2c27cb83a46cff714e57e4f072bd1df08e23640bb64143ee7c866097f8d360800abbff28a94ef7b035563ce396c9c68ee5c7751c070c364e0fcfb015749b480a8bf4cd9626541e40af879213812612c2e04d688629239f609219a45657693c4cb81c85e156a0c9b377d6123340f009a265cec1a2fde0dfa4676b80dec9b6544712b202cec1467d8f440c2c2a1d13862fab29cb266e9185ed6bbb2c3f3e168bce4720ae079194948d41ed8bf7913fa1d9d8c0dbd39e4f7855f807f6192d3269fa0a98a3d6c27992798b70b59e04dd63918c7ca99054abe15fad0f567d9e993ca49eae695a820552575ee569c4aae328c5db0c3d04d1692ff561064345ed32571d072ea4a2613ce163022287b86b9797223619e556f978deb6711051eab698b43fcac2d62729addc0d699f775b516ed1952af9c5faad43601a4a23007ee2d6fbb10316bbb047e04596a57d5ee871496f85ea460f3f078e6c5a3f629aa6b67e4dbba3ace0a3ebf870a2800a5c7f27a866f6d58b33b1daf49c27a0e8057c169eb9be30d3a42e92676f45be824c12aee3f35a59e1cd23bb39e0ab5a87d43e5894e7aedfa7e74ee6554873db5646eb0e7d2b79f970c4210bd9be2b9294284aa26156ba9773c3422aac0489ba21878bf569c79cc548519221c3c90a706460a2d32e073564d7b6133ef25e14a6e327bc15e33555a299251ca49006225b18d8890b45b49e8c16aab18b6e7ba1e932750e81de679d7b6dff571a4cdf737e06eeec7cbac678b8a58e2142615b71cce7f26187a9c1490407b32c48783bf848a830a41ae89989fe68e50307044c34ac27f360b3679b1e4b76bf590cc131f2e25634c9979b4c98c392fa862cbda18e5c5ac0dd25f3ea6d9fe88691aa4d6399d4bc1da5218b95a8f8ab6683a1916eca043fbdb23137799f8e8e6e240ae4693e1896b942ae548a9e948ba77b2770ac8d98737bdb47d04e5c233da80d84c806e8585e73ab7d4f24ef5415a8ff01392e05b267159736f222a7e88652801cc7b9a3ce955494df7c556d1747255f9492da41ce8dc9a615c9b926f83436190e458f7b3994e425d560f35d3a79753e637c8f7dcfae559e8a76a956927c870e5f6da04a8fbb805253af55e52f3895811825d720e35e305c164ebad8a8f5535e6f1a9023bf6a6f1087361a93741a245b4b98920f539b21958c47b68e8f11bd764b10fba33c607fc70b126263a07216d72eef2d432083a6e373747e345cf6f9362a9bcd52e865062cb026484a21a5794dd4a1171560caec70cb607c324537007b74a36a1ec02033d8fe76196854c4158c47d0535a98eb15b74e347ec308728a32f7b75f06f3ee753cd0598ecc47a563978eba8535eaa012ae9555551a1c3f39c1fadbb66b08cf6f560a820fc4ae281f2bf57fb092f4d2c61568192483e86e65d5d58e86b1a873d06541227228209a98050728216b70185cb0243b893882db43e9a7aaeea366b2dbb13e9c3e707f1be4473d790d676695f1ae7bf1064c6f0a38e8706d37b652cff8e9e1979236a1482dcedb391f7f62da88843221043e472328e7b3fa57e7e06e4eaad1afe822f65002b18382ee5ccd890bd84f26ab467bef4265828e7331c31f119beadf4dca3773ff4a1f1b55b6377bfd9ff5277e740e2c60faa7111547f3ed0325a509582c4185de57a7f0b97b841736df3e69f7f73dfd831967ca615e29c4ffece4c229a3da65ce8e9e7bf12ae10b338290fd80f887cd11cc452ac0b89800c1694e38fbae1d83d7ca0112ad57797354315adceecef61c35fecf618144ed59f84c6f5b9be75de6aa218616bac4eade6dd148b9d692f5df6dcded45b3c3e4a3d679ac91a4eff540f608a86432a749aa119db5e3ea92ceec1ffea5c467bdd5dd3798dd8893e8e3972709025619cada7a880f675faff2a257e18164dbfb1752cfbd5dd8194cfca896298165c0dc4369724b59af981350c4514107c8ff09fa39eed4d76e636a21cdbb3e0c0e5e8f510d087a591135b52f974576432d3e3bf2f46b036c7e140547f96ebabfd0cb242102b3f66aee8560be27657d0900494c33eb8a02dd36b8d8b0e6df3d9931fceeefbc32590050838eec27fa778ae6347102442dd21e300b50faf8fdfabbc1161a6bfe5bc795fbcda8994091ee954edf66f1ba75c4acf7965b31d56db077396bb1e7e9c8286e63e54b34cf5ca11bd8998e40f2c4594c55d7fa8fc2cf9476612d567996307d21caf06e49daf16300bc1f70d70d145e64d92a0b307df6a3d3554d6ccd0c550ba193ff6b818c6f2b0008f17359e8617aa927b9f771203faf28c2a04116f8f048ab0228adad39c8aa3e8c8381b1e60886138face97ae7f3c02d2ef854fab2ef27425b23fcf05bcbeb3d752db854313084ddf7a8833369c17123c92d63c744df22ffaf10188b2f4f4cdb53e5f530279d4419b0858d44a8a8f76a26b78be327bd233bf12709ea04a4cc6b6ccfb20808519f316d293bb4d605e4cee8373202f4e95e28bfef9c42354672526a7a81391239783b64d74c2b04686438411614e7228d34e457151f71d742bc514aedfc96966ea35297d5efa513e8dbbc10e8e16eac2ae14848a20c165f59f1362b57b457f6786b0e1fd492ca47d6aba03774e58f4e09fb480156b9c96ddd13c441fc8202e40a12b30396482778ebc439b5abebcd140ad3848726b2908f6b594e0ac7b307857d68fad4ab05e4362140ad2e4b4606e365e894224dfc033f4c069dafdd7f819d340f6a202592243a27f67a38f20e5b07136f5b503c3ca209736c0a7b99749a9d36a82b2a6926a426c95b5f60c835ab85038469e1e5bce2b6bc2d47345d8f1beed8e678423ca3db106b554890e8a701e5d43f6358d957b8c799d1cf20da1f577df0261717227113e5d1127849bccc076947beab3e3aed964b83c94de9322591b67d6fad860d5a5c642a545aee2d1ee6756723a96d74ae2f431876fce6d8da50628fc51560760c97aa66a258f41a9d958a1e3bb0e27a58eda5f6806a115ac6aadab545aebe2d9c6c6c01cb2dc7e8b36c7b37609563f84f409b0813047fea9220872becbc9cd976d502576d1dc2a0a5612a7a1452e46b35149528ed2cb74b18a221c2281d7b766d28a30f763d7375f6242f4c361807efb711d21426696f77606cf1c37c4c162fe79e444c622297b0849c88995cf248a47c49d1932b6ee79c2c2f3dc77fe97389855c50cb797b0fddaa94c31d5b23f4b7f7d24131aa24d6d53e9a43a3e2c360c3254573355ddbe850299f7ad13f8d0676c8a39ee2a63ec700eb3b53d9abee536a6a2bef272527ede483a09913d8d839cf69c828654ccac0581552b342b0735f49cd5af77bca8e8926173e58320fd4738e574114736cd480900c001766cc72266f7c7ee34dd27a109ad4e932c28d0ce1292df46a0df42f23a48531da38ee14bae23d27e3370434efb85546c5e53c10e8407084e833a65a91d07df741174fbdd7e1e5475d273b1c30fc3db1227d32739889b45ac3ce208f82523d51e2c5a54b6c325ed1fbda4748b61090a838bdc5ebdbf1eb2280ad73028ed8ec40fda49cdf17ce0e158521ea5c99cb8fca79a91e7a4011e6aefa4235b4cdd5344356ed7fb31b1e575592fa2dd89c0a2ce151e07d049d67463d6fff3b2b830451fd89c596d9c807f57aba74fba0a24649e27c849376ab134c5c7f501fd1746b75866f3e74f854029e2e73455a60ec76df9df208b8ce5feccf2b6a2bb743e461924cbd22b91c9e89dcbe231474f26678dd91bda846034f4d55ae97aa1a569c5a3bbb399682ba6b066597a1be9d9a1612cf3faa7cd9b0dc9a3d47c9e382604d7004bb10b2e29fadfa2e374c806a1c7f95b169c4e461bcbe907d3a7ffa6af042f351fc908dd67c522802f0ef18eb57ecfc6f2e95322928e5b561d715a29667ee44382be6eba5ea8577fc926ba742d0d1f345263991610174ec46c5989d8546cd37904089d5327e0787d136d3b13412b536bbdc385990ed35cf6454874c9ce362acb0a8146f78b3f20140c34519c9be23051ad0e0620d50dc3319bfe072c46952cccfa44a62c7afeb9bd596673dcaead2a279175b9324c922a6c17959eef9e6b15a1d952bdbdd74e6eaca13735db8132e0c26fd8eaac1acc66feb76e62760e6746dba93492891a048babd276ae5c4ec2fdd0afc8c1e570a2be342e6d98c3ed004e627ce95e06770227f7b6976a0b2b8e2769e8a1061b15834582c581cc36c0e28909153881e4ee0f8770392a04b3459b1bea67a4f4a68270000" ).unwrap(); let transaction = @@ -86,7 +88,7 @@ fn main() { .unwrap(); // Get the previous confidential amount - let conf_val: Vec = elements::hashes::hex::FromHex::from_hex( + let conf_val: Vec = elements::hex::FromHex::from_hex( "080e8899a3c271573359a179b27b59af180b36461f959ee00f762d9c2d84192a06", ) .unwrap(); @@ -94,8 +96,10 @@ fn main() { let amount = confidential::Value::from_commitment(&conf_val).unwrap(); // We only need to set the amount in prevouts because segwit transactions only need the amount field // For taproot transactions, this must contain all the required prevouts - let mut spent_utxo = elements::TxOut::default(); - spent_utxo.value = amount; + let spent_utxo = elements::TxOut { + value: amount, + ..elements::TxOut::default() + }; // Create a spend utxos, since it is a segwit spend we don't really need all prevouts. Fill dummy data for this example instead let utxos = [spent_utxo, elements::TxOut::default()]; let env = TxEnv::new(&transaction, &utxos, 0).expect("Input len == witness utxo len"); diff --git a/examples/xpub_descriptors.rs b/examples/xpub_descriptors.rs index 73f02881..d0e600d8 100644 --- a/examples/xpub_descriptors.rs +++ b/examples/xpub_descriptors.rs @@ -45,7 +45,7 @@ fn p2wsh(secp: &Secp256k1) -> Address { let address = Descriptor::::from_str(&s) .unwrap() - .derived_descriptor(&secp) + .derived_descriptor(secp) .unwrap() .address(&elements::AddressParams::ELEMENTS) .unwrap(); diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index e96ad3ec..75c28e3e 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -1,54 +1,46 @@ [package] name = "descriptor-fuzz" +edition = "2018" version = "0.0.1" -authors = ["Automatically generated"] +authors = ["Generated by fuzz/generate-files.sh"] publish = false [package.metadata] cargo-fuzz = true -[features] -afl_fuzz = ["afl"] -honggfuzz_fuzz = ["honggfuzz"] - [dependencies] -honggfuzz = { version = "0.5", default-features = false, optional = true } -afl = { version = "0.8", optional = true } +honggfuzz = { version = "0.5.55", default-features = false } regex = { version = "1.4"} elements-miniscript = { path = "..", features = ["compiler"] } -# Prevent this from interfering with workspaces -[workspace] -members = ["."] - [[bin]] -name = "roundtrip_descriptor" -path = "fuzz_targets/roundtrip_descriptor.rs" +name = "roundtrip_miniscript_str" +path = "fuzz_targets/roundtrip_miniscript_str.rs" [[bin]] name = "roundtrip_miniscript_script" path = "fuzz_targets/roundtrip_miniscript_script.rs" [[bin]] -name = "roundtrip_miniscript_str" -path = "fuzz_targets/roundtrip_miniscript_str.rs" - -[[bin]] -name = "roundtrip_concrete" -path = "fuzz_targets/roundtrip_concrete.rs" +name = "parse_descriptor" +path = "fuzz_targets/parse_descriptor.rs" [[bin]] name = "roundtrip_semantic" path = "fuzz_targets/roundtrip_semantic.rs" [[bin]] -name = "compile_descriptor" -path = "fuzz_targets/compile_descriptor.rs" +name = "parse_descriptor_secret" +path = "fuzz_targets/parse_descriptor_secret.rs" [[bin]] -name = "parse_descriptor" -path = "fuzz_targets/parse_descriptor.rs" +name = "roundtrip_descriptor" +path = "fuzz_targets/roundtrip_descriptor.rs" [[bin]] -name = "parse_descriptor_secret" -path = "fuzz_targets/parse_descriptor_secret.rs" +name = "roundtrip_concrete" +path = "fuzz_targets/roundtrip_concrete.rs" + +[[bin]] +name = "compile_descriptor" +path = "fuzz_targets/compile_descriptor.rs" diff --git a/fuzz/README.md b/fuzz/README.md index f636c9d8..17db41d1 100644 --- a/fuzz/README.md +++ b/fuzz/README.md @@ -1,39 +1,115 @@ -# Fuzz Tests +# Fuzzing -Repository for fuzz testing Miniscript. +`bitcoin` and `bitcoin_hashes` have fuzzing harnesses setup for use with +honggfuzz. -## How to reproduce crashes? +To run the fuzz-tests as in CI -- briefly fuzzing every target -- simply +run -Travis should output a offending hex("048531e80700ae6400670000af5168" in the example) -which you can use as shown. Copy and paste the following code lines into file reporting crashes and -replace the hex with the offending hex. -Refer to file [roundtrip_concrete.rs](./fuzz_targets/roundtrip_concrete.rs) for an example. + ./fuzz.sh + +in this directory. + +To build honggfuzz, you must have libunwind on your system, as well as +libopcodes and libbfd from binutils **2.38** on your system. The most +recently-released binutils 2.39 has changed their API in a breaking way. + +On Nix, you can obtain these libraries by running + + nix-shell -p libopcodes_2_38 -p libunwind + +and then run fuzz.sh as above. + +# Fuzzing with weak cryptography + +You may wish to replace the hashing and signing code with broken crypto, +which will be faster and enable the fuzzer to do otherwise impossible +things such as forging signatures or finding preimages to hashes. + +Doing so may result in spurious bug reports since the broken crypto does +not respect the encoding or algebraic invariants upheld by the real crypto. We +would like to improve this but it's a nontrivial problem -- though not +beyond the abilities of a motivated student with a few months of time. +Please let us know if you are interested in taking this on! + +Meanwhile, to use the broken crypto, simply compile (and run the fuzzing +scripts) with + + RUSTFLAGS="--cfg=hashes_fuzz --cfg=secp256k1_fuzz" + +which will replace the hashing library with broken hashes, and the +secp256k1 library with broken cryptography. + +Needless to say, NEVER COMPILE REAL CODE WITH THESE FLAGS because if a +fuzzer can break your crypto, so can anybody. + +# Long-term fuzzing + +To see the full list of targets, the most straightforward way is to run + + source ./fuzz-util.sh + listTargetNames + +To run each of them for an hour, run + + ./cycle.sh + +To run a single fuzztest indefinitely, run + + HFUZZ_BUILD_ARGS='--features honggfuzz_fuzz' cargo hfuzz run + +This script uses the `chrt` utility to try to reduce the priority of the +jobs. If you would like to run for longer, the most straightforward way +is to edit `cycle.sh` before starting. To run the fuzz-tests in parallel, +you will need to implement a custom harness. + +# Adding fuzz tests + +All fuzz tests can be found in the `fuzz_target/` directory. Adding a new +one is as simple as copying an existing one and editing the `do_test` +function to do what you want. + +If your test clearly belongs to a specific crate, please put it in that +crate's directory. Otherwise you can put it directly in `fuzz_target/`. + +If you need to add dependencies, edit the file `generate-files.sh` to add +it to the generated `Cargo.toml`. + +Once you've added a fuzztest, regenerate the `Cargo.toml` and CI job by +running + + ./generate-files.sh + +Then to test your fuzztest, run + + ./fuzz.sh + +If it is working, you will see a rapid stream of data for many seconds +(you can hit Ctrl+C to stop it early). If not, you should quickly see +an error. + +# Reproducing Failures + +If a fuzztest fails, it will exit with a summary which looks something like ``` -#[cfg(test)] -mod tests { - fn extend_vec_from_hex(hex: &str, out: &mut Vec) { - let mut b = 0; - for (idx, c) in hex.as_bytes().iter().enumerate() { - b <<= 4; - match *c { - b'A'...b'F' => b |= c - b'A' + 10, - b'a'...b'f' => b |= c - b'a' + 10, - b'0'...b'9' => b |= c - b'0', - _ => panic!("Bad hex"), - } - if (idx & 1) == 1 { - out.push(b); - b = 0; - } - } - } - - #[test] - fn duplicate_crash() { - let mut a = Vec::new(); - extend_vec_from_hex("048531e80700ae6400670000af5168", &mut a); - super::do_test(&a); - } -} +... + fuzzTarget : hfuzz_target/x86_64-unknown-linux-gnu/release/hashes_sha256 +CRASH: +DESCRIPTION: +ORIG_FNAME: 00000000000000000000000000000000.00000000.honggfuzz.cov +FUZZ_FNAME: hfuzz_workspace/hashes_sha256/SIGABRT.PC.7ffff7c8abc7.STACK.18826d9b64.CODE.-6.ADDR.0.INSTR.mov____%eax,%ebp.fuzz +... +===================================================================== +fff400610004 ``` + +The final line is a hex-encoded version of the input that caused the crash. You +can test this directly by editing the `duplicate_crash` test to copy/paste the +hex output into the call to `Vec::::from_hex`. Then run the test with + + cargo test + +Note that if you set your `RUSTFLAGS` while fuzzing (see above) you must make +sure they are set the same way when running `cargo test`. + diff --git a/fuzz/cycle.sh b/fuzz/cycle.sh new file mode 100755 index 00000000..0b59827a --- /dev/null +++ b/fuzz/cycle.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +# Continuosly cycle over fuzz targets running each for 1 hour. +# It uses chrt SCHED_IDLE so that other process takes priority. +# +# For hfuzz options see https://github.com/google/honggfuzz/blob/master/docs/USAGE.md + +set -e +REPO_DIR=$(git rev-parse --show-toplevel) +# shellcheck source=./fuzz-util.sh +source "$REPO_DIR/fuzz/fuzz-util.sh" + +while : +do + for targetFile in $(listTargetFiles); do + targetName=$(targetFileToName "$targetFile") + echo "Fuzzing target $targetName ($targetFile)" + + # fuzz for one hour + HFUZZ_RUN_ARGS='--run_time 3600' chrt -i 0 cargo hfuzz run "$targetName" + # minimize the corpus + HFUZZ_RUN_ARGS="-i hfuzz_workspace/$targetName/input/ -P -M" chrt -i 0 cargo hfuzz run "$targetName" + done +done + diff --git a/fuzz/fuzz-util.sh b/fuzz/fuzz-util.sh new file mode 100755 index 00000000..804e0da9 --- /dev/null +++ b/fuzz/fuzz-util.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash + +REPO_DIR=$(git rev-parse --show-toplevel) + +listTargetFiles() { + pushd "$REPO_DIR/fuzz" > /dev/null || exit 1 + find fuzz_targets/ -type f -name "*.rs" + popd > /dev/null || exit 1 +} + +targetFileToName() { + echo "$1" \ + | sed 's/^fuzz_targets\///' \ + | sed 's/\.rs$//' \ + | sed 's/\//_/g' +} + +targetFileToHFuzzInputArg() { + baseName=$(basename "$1") + dirName="${baseName%.*}" + if [ -d "hfuzz_input/$dirName" ]; then + echo "HFUZZ_INPUT_ARGS=\"-f hfuzz_input/$FILE/input\"" + fi +} + +listTargetNames() { + for target in $(listTargetFiles); do + targetFileToName "$target" + done +} + +# Utility function to avoid CI failures on Windows +checkWindowsFiles() { + incorrectFilenames=$(find . -type f -name "*,*" -o -name "*:*" -o -name "*<*" -o -name "*>*" -o -name "*|*" -o -name "*\?*" -o -name "*\**" -o -name "*\"*" | wc -l) + if [ "$incorrectFilenames" -gt 0 ]; then + echo "Bailing early because there is a Windows-incompatible filename in the tree." + exit 2 + fi +} + +# Checks whether a fuzz case output some report, and dumps it in hex +checkReport() { + reportFile="hfuzz_workspace/$1/HONGGFUZZ.REPORT.TXT" + if [ -f "$reportFile" ]; then + cat "$reportFile" + for CASE in "hfuzz_workspace/$1/SIG"*; do + xxd -p -c10000 < "$CASE" + done + exit 1 + fi +} diff --git a/fuzz/fuzz.sh b/fuzz/fuzz.sh new file mode 100755 index 00000000..5fc65ae6 --- /dev/null +++ b/fuzz/fuzz.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -ex + +REPO_DIR=$(git rev-parse --show-toplevel) + +# shellcheck source=./fuzz-util.sh +source "$REPO_DIR/fuzz/fuzz-util.sh" + +# Check that input files are correct Windows file names +checkWindowsFiles + +if [ "$1" == "" ]; then + targetFiles="$(listTargetFiles)" +else + targetFiles=fuzz_targets/"$1".rs +fi + +cargo --version +rustc --version + +# Testing +cargo install --force honggfuzz --no-default-features +for targetFile in $targetFiles; do + targetName=$(targetFileToName "$targetFile") + echo "Fuzzing target $targetName ($targetFile)" + if [ -d "hfuzz_input/$targetName" ]; then + HFUZZ_INPUT_ARGS="-f hfuzz_input/$targetName/input\"" + else + HFUZZ_INPUT_ARGS="" + fi + HFUZZ_RUN_ARGS="--run_time 30 --exit_upon_crash -v $HFUZZ_INPUT_ARGS" cargo hfuzz run "$targetName" + + checkReport "$targetName" +done diff --git a/fuzz/fuzz_targets/compile_descriptor.rs b/fuzz/fuzz_targets/compile_descriptor.rs index 29a05e1d..0b34281c 100644 --- a/fuzz/fuzz_targets/compile_descriptor.rs +++ b/fuzz/fuzz_targets/compile_descriptor.rs @@ -1,11 +1,10 @@ extern crate elements_miniscript as miniscript; -use miniscript::Segwitv0; -use miniscript::{policy, Miniscript}; -use policy::Liftable; - use std::str::FromStr; +use miniscript::{policy, Miniscript, Segwitv0}; +use policy::Liftable; + type Script = Miniscript; type Policy = policy::Concrete; @@ -15,10 +14,7 @@ fn do_test(data: &[u8]) { // Compile if let Ok(desc) = pol.compile::() { // Lift - assert_eq!( - desc.clone().lift().unwrap().sorted(), - pol.clone().lift().unwrap().sorted() - ); + assert_eq!(desc.lift().unwrap().sorted(), pol.lift().unwrap().sorted()); // Try to roundtrip the output of the compiler let output = desc.to_string(); if let Ok(desc) = Script::from_str(&output) { @@ -31,23 +27,21 @@ fn do_test(data: &[u8]) { } } -#[cfg(feature = "afl")] -extern crate afl; -#[cfg(feature = "afl")] -fn main() { - afl::read_stdio_bytes(|data| { - do_test(&data); - }); -} - -#[cfg(feature = "honggfuzz")] -#[macro_use] -extern crate honggfuzz; -#[cfg(feature = "honggfuzz")] fn main() { loop { - fuzz!(|data| { + honggfuzz::fuzz!(|data| { do_test(data); }); } } + +#[cfg(test)] +mod tests { + use miniscript::elements::hex::FromHex; + + #[test] + fn duplicate_crash() { + let hex = Vec::::from_hex("00").unwrap(); + super::do_test(&hex); + } +} diff --git a/fuzz/fuzz_targets/parse_descriptor.rs b/fuzz/fuzz_targets/parse_descriptor.rs index d73580c7..c9bf3c24 100644 --- a/fuzz/fuzz_targets/parse_descriptor.rs +++ b/fuzz/fuzz_targets/parse_descriptor.rs @@ -1,32 +1,20 @@ extern crate elements_miniscript as miniscript; -use miniscript::DescriptorPublicKey; use std::str::FromStr; +use miniscript::DescriptorPublicKey; + fn do_test(data: &[u8]) { let data_str = String::from_utf8_lossy(data); if let Ok(dpk) = DescriptorPublicKey::from_str(&data_str) { - let output = dpk.to_string(); + let _output = dpk.to_string(); // assert_eq!(data_str.to_lowercase(), output.to_lowercase()); } } -#[cfg(feature = "afl")] -extern crate afl; -#[cfg(feature = "afl")] -fn main() { - afl::read_stdio_bytes(|data| { - do_test(&data); - }); -} - -#[cfg(feature = "honggfuzz")] -#[macro_use] -extern crate honggfuzz; -#[cfg(feature = "honggfuzz")] fn main() { loop { - fuzz!(|data| { + honggfuzz::fuzz!(|data| { do_test(data); }); } @@ -34,27 +22,11 @@ fn main() { #[cfg(test)] mod tests { - fn extend_vec_from_hex(hex: &str, out: &mut Vec) { - let mut b = 0; - for (idx, c) in hex.as_bytes().iter().enumerate() { - b <<= 4; - match *c { - b'A'...b'F' => b |= c - b'A' + 10, - b'a'...b'f' => b |= c - b'a' + 10, - b'0'...b'9' => b |= c - b'0', - _ => panic!("Bad hex"), - } - if (idx & 1) == 1 { - out.push(b); - b = 0; - } - } - } + use miniscript::elements::hex::FromHex; #[test] fn duplicate_crash() { - let mut a = Vec::new(); - extend_vec_from_hex("6d75736967", &mut a); - super::do_test(&a); + let hex = Vec::::from_hex("00").unwrap(); + super::do_test(&hex); } } diff --git a/fuzz/fuzz_targets/parse_descriptor_secret.rs b/fuzz/fuzz_targets/parse_descriptor_secret.rs index b598305e..d9cc9714 100644 --- a/fuzz/fuzz_targets/parse_descriptor_secret.rs +++ b/fuzz/fuzz_targets/parse_descriptor_secret.rs @@ -1,8 +1,9 @@ extern crate elements_miniscript as miniscript; -use miniscript::descriptor::DescriptorSecretKey; use std::str::FromStr; +use miniscript::descriptor::DescriptorSecretKey; + fn do_test(data: &[u8]) { let data_str = String::from_utf8_lossy(data); if let Ok(dsk) = DescriptorSecretKey::from_str(&data_str) { @@ -11,23 +12,21 @@ fn do_test(data: &[u8]) { } } -#[cfg(feature = "afl")] -extern crate afl; -#[cfg(feature = "afl")] -fn main() { - afl::read_stdio_bytes(|data| { - do_test(&data); - }); -} - -#[cfg(feature = "honggfuzz")] -#[macro_use] -extern crate honggfuzz; -#[cfg(feature = "honggfuzz")] fn main() { loop { - fuzz!(|data| { + honggfuzz::fuzz!(|data| { do_test(data); }); } } + +#[cfg(test)] +mod tests { + use miniscript::elements::hex::FromHex; + + #[test] + fn duplicate_crash() { + let hex = Vec::::from_hex("00").unwrap(); + super::do_test(&hex); + } +} diff --git a/fuzz/fuzz_targets/roundtrip_concrete.rs b/fuzz/fuzz_targets/roundtrip_concrete.rs index 75d0f4db..70cfd43e 100644 --- a/fuzz/fuzz_targets/roundtrip_concrete.rs +++ b/fuzz/fuzz_targets/roundtrip_concrete.rs @@ -1,8 +1,9 @@ extern crate elements_miniscript as miniscript; extern crate regex; +use std::str::FromStr; + use miniscript::policy; use regex::Regex; -use std::str::FromStr; type Policy = policy::Concrete; @@ -18,22 +19,9 @@ fn do_test(data: &[u8]) { } } -#[cfg(feature = "afl")] -extern crate afl; -#[cfg(feature = "afl")] -fn main() { - afl::read_stdio_bytes(|data| { - do_test(&data); - }); -} - -#[cfg(feature = "honggfuzz")] -#[macro_use] -extern crate honggfuzz; -#[cfg(feature = "honggfuzz")] fn main() { loop { - fuzz!(|data| { + honggfuzz::fuzz!(|data| { do_test(data); }); } @@ -41,27 +29,11 @@ fn main() { #[cfg(test)] mod tests { - fn extend_vec_from_hex(hex: &str, out: &mut Vec) { - let mut b = 0; - for (idx, c) in hex.as_bytes().iter().enumerate() { - b <<= 4; - match *c { - b'A'...b'F' => b |= c - b'A' + 10, - b'a'...b'f' => b |= c - b'a' + 10, - b'0'...b'9' => b |= c - b'0', - _ => panic!("Bad hex"), - } - if (idx & 1) == 1 { - out.push(b); - b = 0; - } - } - } + use miniscript::elements::hex::FromHex; #[test] fn duplicate_crash() { - let mut a = Vec::new(); - extend_vec_from_hex("048531e80700ae6400670000af5168", &mut a); - super::do_test(&a); + let hex = Vec::::from_hex("00").unwrap(); + super::do_test(&hex); } } diff --git a/fuzz/fuzz_targets/roundtrip_descriptor.rs b/fuzz/fuzz_targets/roundtrip_descriptor.rs index ea734c78..b9182860 100644 --- a/fuzz/fuzz_targets/roundtrip_descriptor.rs +++ b/fuzz/fuzz_targets/roundtrip_descriptor.rs @@ -1,10 +1,10 @@ extern crate elements_miniscript as miniscript; extern crate regex; -use miniscript::Descriptor; -use regex::Regex; use std::str::FromStr; +use miniscript::Descriptor; + fn do_test(data: &[u8]) { // This is how we test in rust-miniscript. It is difficult to enforce wrapping logic in fuzzer // for alias like t: and_v(1), likely and unlikely. @@ -18,22 +18,9 @@ fn do_test(data: &[u8]) { } } -#[cfg(feature = "afl")] -extern crate afl; -#[cfg(feature = "afl")] -fn main() { - afl::read_stdio_bytes(|data| { - do_test(&data); - }); -} - -#[cfg(feature = "honggfuzz")] -#[macro_use] -extern crate honggfuzz; -#[cfg(feature = "honggfuzz")] fn main() { loop { - fuzz!(|data| { + honggfuzz::fuzz!(|data| { do_test(data); }); } @@ -41,9 +28,11 @@ fn main() { #[cfg(test)] mod tests { - use super::*; + use miniscript::elements::hex::FromHex; + #[test] - fn test() { - do_test(b"elc:pk_h()"); + fn duplicate_crash() { + let hex = Vec::::from_hex("00").unwrap(); + super::do_test(&hex); } } diff --git a/fuzz/fuzz_targets/roundtrip_miniscript_script.rs b/fuzz/fuzz_targets/roundtrip_miniscript_script.rs index 19518586..051257c7 100644 --- a/fuzz/fuzz_targets/roundtrip_miniscript_script.rs +++ b/fuzz/fuzz_targets/roundtrip_miniscript_script.rs @@ -2,10 +2,7 @@ extern crate elements_miniscript as miniscript; use miniscript::bitcoin::PublicKey; use miniscript::elements::script; -use miniscript::CovenantExt; -use miniscript::Miniscript; -use miniscript::NoExt; -use miniscript::Segwitv0; +use miniscript::{Miniscript, NoExt, Segwitv0}; fn do_test(data: &[u8]) { // Try round-tripping as a script @@ -18,22 +15,9 @@ fn do_test(data: &[u8]) { } } -#[cfg(feature = "afl")] -extern crate afl; -#[cfg(feature = "afl")] -fn main() { - afl::read_stdio_bytes(|data| { - do_test(&data); - }); -} - -#[cfg(feature = "honggfuzz")] -#[macro_use] -extern crate honggfuzz; -#[cfg(feature = "honggfuzz")] fn main() { loop { - fuzz!(|data| { + honggfuzz::fuzz!(|data| { do_test(data); }); } @@ -41,27 +25,11 @@ fn main() { #[cfg(test)] mod tests { - fn extend_vec_from_hex(hex: &str, out: &mut Vec) { - let mut b = 0; - for (idx, c) in hex.as_bytes().iter().enumerate() { - b <<= 4; - match *c { - b'A'...b'F' => b |= c - b'A' + 10, - b'a'...b'f' => b |= c - b'a' + 10, - b'0'...b'9' => b |= c - b'0', - _ => panic!("Bad hex"), - } - if (idx & 1) == 1 { - out.push(b); - b = 0; - } - } - } + use miniscript::elements::hex::FromHex; #[test] - fn duplicate_crash3() { - let mut a = Vec::new(); - extend_vec_from_hex("1479002d00000020323731363342740000004000000000000000000000000000000000000063630004636363639c00000000000000000000", &mut a); - super::do_test(&a); + fn duplicate_crash() { + let hex = Vec::::from_hex("00").unwrap(); + super::do_test(&hex); } } diff --git a/fuzz/fuzz_targets/roundtrip_miniscript_str.rs b/fuzz/fuzz_targets/roundtrip_miniscript_str.rs index 517bf8cb..372320ce 100644 --- a/fuzz/fuzz_targets/roundtrip_miniscript_str.rs +++ b/fuzz/fuzz_targets/roundtrip_miniscript_str.rs @@ -1,13 +1,9 @@ extern crate elements_miniscript as miniscript; extern crate regex; -use regex::Regex; use std::str::FromStr; -use miniscript::CovenantExt; -use miniscript::Miniscript; -use miniscript::NoExt; -use miniscript::Segwitv0; +use miniscript::{Miniscript, NoExt, Segwitv0}; fn do_test(data: &[u8]) { let s = String::from_utf8_lossy(data); @@ -18,22 +14,9 @@ fn do_test(data: &[u8]) { } } -#[cfg(feature = "afl")] -extern crate afl; -#[cfg(feature = "afl")] -fn main() { - afl::read_stdio_bytes(|data| { - do_test(&data); - }); -} - -#[cfg(feature = "honggfuzz")] -#[macro_use] -extern crate honggfuzz; -#[cfg(feature = "honggfuzz")] fn main() { loop { - fuzz!(|data| { + honggfuzz::fuzz!(|data| { do_test(data); }); } @@ -41,27 +24,11 @@ fn main() { #[cfg(test)] mod tests { - fn extend_vec_from_hex(hex: &str, out: &mut Vec) { - let mut b = 0; - for (idx, c) in hex.as_bytes().iter().enumerate() { - b <<= 4; - match *c { - b'A'...b'F' => b |= c - b'A' + 10, - b'a'...b'f' => b |= c - b'a' + 10, - b'0'...b'9' => b |= c - b'0', - _ => panic!("Bad hex"), - } - if (idx & 1) == 1 { - out.push(b); - b = 0; - } - } - } + use miniscript::elements::hex::FromHex; #[test] fn duplicate_crash() { - let mut a = Vec::new(); - extend_vec_from_hex("1479002d00000020323731363342740000004000000000000000000000000000000000000063630004636363639c00000000000000000000", &mut a); - super::do_test(&a); + let hex = Vec::::from_hex("00").unwrap(); + super::do_test(&hex); } } diff --git a/fuzz/fuzz_targets/roundtrip_semantic.rs b/fuzz/fuzz_targets/roundtrip_semantic.rs index a232df14..7cda5cad 100644 --- a/fuzz/fuzz_targets/roundtrip_semantic.rs +++ b/fuzz/fuzz_targets/roundtrip_semantic.rs @@ -1,8 +1,9 @@ extern crate elements_miniscript as miniscript; -use miniscript::policy; use std::str::FromStr; +use miniscript::policy; + type Policy = policy::Semantic; fn do_test(data: &[u8]) { @@ -13,23 +14,21 @@ fn do_test(data: &[u8]) { } } -#[cfg(feature = "afl")] -extern crate afl; -#[cfg(feature = "afl")] -fn main() { - afl::read_stdio_bytes(|data| { - do_test(&data); - }); -} - -#[cfg(feature = "honggfuzz")] -#[macro_use] -extern crate honggfuzz; -#[cfg(feature = "honggfuzz")] fn main() { loop { - fuzz!(|data| { + honggfuzz::fuzz!(|data| { do_test(data); }); } } + +#[cfg(test)] +mod tests { + use miniscript::elements::hex::FromHex; + + #[test] + fn duplicate_crash() { + let hex = Vec::::from_hex("00").unwrap(); + super::do_test(&hex); + } +} diff --git a/fuzz/generate-files.sh b/fuzz/generate-files.sh new file mode 100755 index 00000000..4787693a --- /dev/null +++ b/fuzz/generate-files.sh @@ -0,0 +1,102 @@ +#!/usr/bin/env bash + +set -e + +REPO_DIR=$(git rev-parse --show-toplevel) + +# shellcheck source=./fuzz-util.sh +source "$REPO_DIR/fuzz/fuzz-util.sh" + +# 1. Generate fuzz/Cargo.toml +cat > "$REPO_DIR/fuzz/Cargo.toml" <> "$REPO_DIR/fuzz/Cargo.toml" < "$REPO_DIR/.github/workflows/fuzz.yml" <executed_\${{ matrix.fuzz_target }} + - uses: actions/upload-artifact@v2 + with: + name: executed_\${{ matrix.fuzz_target }} + path: executed_\${{ matrix.fuzz_target }} + + verify-execution: + if: \${{ !github.event.act }} + needs: fuzz + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/download-artifact@v2 + - name: Display structure of downloaded files + run: ls -R + - run: find executed_* -type f -exec cat {} + | sort > executed + - run: source ./fuzz/fuzz-util.sh && listTargetNames | sort | diff - executed +EOF + diff --git a/fuzz/travis-fuzz.sh b/fuzz/travis-fuzz.sh deleted file mode 100755 index db1b9eb3..00000000 --- a/fuzz/travis-fuzz.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -set -e -cargo install --force honggfuzz --no-default-features -for TARGET in fuzz_targets/*; do - FILENAME=$(basename $TARGET) - FILE="${FILENAME%.*}" - if [ -d hfuzz_input/$FILE ]; then - HFUZZ_INPUT_ARGS="-f hfuzz_input/$FILE/input" - fi - HFUZZ_BUILD_ARGS="--features honggfuzz_fuzz" HFUZZ_RUN_ARGS="-N1000000 --exit_upon_crash -v $HFUZZ_INPUT_ARGS" cargo hfuzz run $FILE - - if [ -f hfuzz_workspace/$FILE/HONGGFUZZ.REPORT.TXT ]; then - cat hfuzz_workspace/$FILE/HONGGFUZZ.REPORT.TXT - for CASE in hfuzz_workspace/$FILE/SIG*; do - cat $CASE | xxd -p - done - exit 1 - fi -done diff --git a/rustfmt.toml b/rustfmt.toml index 37abad59..b2a63aa4 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -44,7 +44,7 @@ enum_discrim_align_threshold = 0 match_arm_blocks = true match_arm_leading_pipes = "Never" force_multiline_blocks = false -fn_args_layout = "Tall" +fn_params_layout = "Tall" brace_style = "SameLineWhere" control_brace_style = "AlwaysSameLine" trailing_semicolon = true diff --git a/src/descriptor/checksum.rs b/src/descriptor/checksum.rs index 68ccd966..1b02e97f 100644 --- a/src/descriptor/checksum.rs +++ b/src/descriptor/checksum.rs @@ -78,6 +78,12 @@ pub struct Engine { clscount: u64, } +impl Default for Engine { + fn default() -> Self { + Self::new() + } +} + impl Engine { /// Construct an engine with no input pub fn new() -> Self { diff --git a/src/descriptor/csfs_cov/mod.rs b/src/descriptor/csfs_cov/mod.rs index 6ed21743..8b01647e 100644 --- a/src/descriptor/csfs_cov/mod.rs +++ b/src/descriptor/csfs_cov/mod.rs @@ -59,13 +59,13 @@ mod tests { use bitcoin; use elements::encode::serialize; - use elements::hashes::hex::ToHex; + use elements::hex::ToHex; use elements::opcodes::all::OP_PUSHNUM_1; use elements::secp256k1_zkp::ZERO_TWEAK; use elements::{ self, confidential, opcodes, script, secp256k1_zkp, AssetId, AssetIssuance, - EcdsaSigHashType, LockTime, OutPoint, PackedLockTime, Script, Sequence, Transaction, TxIn, - TxInWitness, TxOut, Txid, + EcdsaSigHashType, LockTime, OutPoint, Script, Sequence, Transaction, TxIn, TxInWitness, + TxOut, Txid, }; use super::cov::*; @@ -191,7 +191,7 @@ mod tests { // Now create a transaction spending this. let mut spend_tx = Transaction { version: 2, - lock_time: PackedLockTime::ZERO, + lock_time: LockTime::ZERO, input: vec![txin_from_txid_vout( "141f79c7c254ee3a9a9bc76b4f60564385b784bdfc1882b25154617801fe2237", 1, @@ -314,13 +314,15 @@ mod tests { // Outputs Pref test // 1. Correct case - let mut out = TxOut::default(); - out.script_pubkey = script::Builder::new() - .push_opcode(opcodes::all::OP_PUSHNUM_1) - .into_script() - .to_v0_p2wsh(); - out.value = confidential::Value::Explicit(99_000); - out.asset = confidential::Asset::Explicit(AssetId::from_slice(&BTC_ASSET).unwrap()); + let out = TxOut { + script_pubkey: script::Builder::new() + .push_opcode(opcodes::all::OP_PUSHNUM_1) + .into_script() + .to_v0_p2wsh(), + value: confidential::Value::Explicit(99_000), + asset: confidential::Asset::Explicit(AssetId::from_slice(&BTC_ASSET).unwrap()), + ..Default::default() + }; let desc = Descriptor::::from_str(&format!( "elcovwsh({},outputs_pref({}))", pks[0], @@ -330,13 +332,15 @@ mod tests { _satisfy_and_interpret(desc, sks[0]).unwrap(); // 2. Chaning the amount should fail the test - let mut out = TxOut::default(); - out.script_pubkey = script::Builder::new() - .push_opcode(opcodes::all::OP_PUSHNUM_1) - .into_script() - .to_v0_p2wsh(); - out.value = confidential::Value::Explicit(99_001); // Changed to +1 - out.asset = confidential::Asset::Explicit(AssetId::from_slice(&BTC_ASSET).unwrap()); + let out = TxOut { + script_pubkey: script::Builder::new() + .push_opcode(opcodes::all::OP_PUSHNUM_1) + .into_script() + .to_v0_p2wsh(), + value: confidential::Value::Explicit(99_001), // Changed to +1 + asset: confidential::Asset::Explicit(AssetId::from_slice(&BTC_ASSET).unwrap()), + ..Default::default() + }; let desc = Descriptor::::from_str(&format!( "elcovwsh({},outputs_pref({}))", pks[0], @@ -393,7 +397,7 @@ mod tests { // Now create a transaction spending this. let mut spend_tx = Transaction { version: 2, - lock_time: PackedLockTime::ZERO, + lock_time: LockTime::ZERO, input: vec![txin_from_txid_vout( "7c8e615c8da947fefd2d9b6f83f313a9b59d249c93a5f232287633195b461cb7", 0, @@ -485,7 +489,7 @@ mod tests { TxIn { previous_output: OutPoint { txid: Txid::from_str(txid).unwrap(), - vout: vout, + vout, }, sequence: Sequence::MAX, is_pegin: false, diff --git a/src/descriptor/csfs_cov/satisfy.rs b/src/descriptor/csfs_cov/satisfy.rs index c2cb03cd..6490c73e 100644 --- a/src/descriptor/csfs_cov/satisfy.rs +++ b/src/descriptor/csfs_cov/satisfy.rs @@ -133,7 +133,7 @@ impl<'tx, 'ptx, Pk: MiniscriptKey + ToPublicKey> Satisfier for LegacyCovSati } fn lookup_nlocktime(&self) -> Option { - Some(self.tx.lock_time.to_u32()) + Some(self.tx.lock_time.to_consensus_u32()) } fn lookup_sighashu32(&self) -> Option { diff --git a/src/descriptor/key.rs b/src/descriptor/key.rs index d08cc7c7..ab55a61a 100644 --- a/src/descriptor/key.rs +++ b/src/descriptor/key.rs @@ -1,11 +1,11 @@ -use std::borrow::Borrow; // SPDX-License-Identifier: CC0-1.0 +use std::borrow::Borrow; +use std::convert::TryInto; use std::str::FromStr; use std::{error, fmt}; -use bitcoin::util::bip32; -use bitcoin::{self, XpubIdentifier}; -use elements::hashes::hex::FromHex; +use bitcoin::hash_types::XpubIdentifier; +use bitcoin::{self, bip32}; use elements::hashes::{hash160, ripemd160, sha256, Hash, HashEngine}; use elements::secp256k1_zkp::{Secp256k1, Signing, Verification}; @@ -19,7 +19,7 @@ pub enum SinglePubKey { /// FullKey (compressed or uncompressed) FullKey(bitcoin::PublicKey), /// XOnlyPublicKey - XOnly(bitcoin::XOnlyPublicKey), + XOnly(bitcoin::key::XOnlyPublicKey), } /// The MiniscriptKey corresponding to Descriptors. This can @@ -388,7 +388,7 @@ fn maybe_fmt_master_id( ) -> fmt::Result { if let Some((ref master_id, ref master_deriv)) = *origin { fmt::Formatter::write_str(f, "[")?; - for byte in master_id.into_bytes().iter() { + for byte in master_id.as_bytes() { write!(f, "{:02x}", byte)?; } fmt_derivation_path(f, master_deriv)?; @@ -461,9 +461,10 @@ impl FromStr for DescriptorPublicKey { } else { let key = match key_part.len() { 64 => { - let x_only_key = bitcoin::XOnlyPublicKey::from_str(key_part).map_err(|_| { - DescriptorKeyParseError("Error while parsing simple xonly key") - })?; + let x_only_key = + bitcoin::key::XOnlyPublicKey::from_str(key_part).map_err(|_| { + DescriptorKeyParseError("Error while parsing simple xonly key") + })?; SinglePubKey::XOnly(x_only_key) } 66 | 130 => { @@ -548,7 +549,11 @@ impl DescriptorPublicKey { } SinglePubKey::XOnly(x_only_pk) => engine.input(&x_only_pk.serialize()), }; - bip32::Fingerprint::from(&XpubIdentifier::from_engine(engine)[..4]) + bip32::Fingerprint::from( + &XpubIdentifier::from_engine(engine)[..4] + .try_into() + .expect("4 byte slice"), + ) } } } @@ -751,7 +756,7 @@ fn parse_key_origin(s: &str) -> Result<(&str, Option), Descrip "Master fingerprint should be 8 characters long", )); } - let parent_fingerprint = bip32::Fingerprint::from_hex(origin_id_hex).map_err(|_| { + let parent_fingerprint = bip32::Fingerprint::from_str(origin_id_hex).map_err(|_| { DescriptorKeyParseError("Malformed master fingerprint, expected 8 hex chars") })?; let origin_path = raw_origin @@ -851,7 +856,6 @@ fn parse_xkey_deriv( // step all the vectors of indexes contain a single element. If it did though, one of the // vectors contains more than one element. // Now transform this list of vectors of steps into distinct derivation paths. - .into_iter() .fold(Ok(Vec::new()), |paths, index_list| { let mut paths = paths?; let mut index_list = index_list?.into_iter(); @@ -897,7 +901,7 @@ impl DescriptorXKey { /// # extern crate elements_miniscript as miniscript; /// # use std::str::FromStr; /// # fn body() -> Result<(), Box> { - /// use miniscript::bitcoin::util::bip32; + /// use miniscript::bitcoin::bip32; /// use miniscript::descriptor::DescriptorPublicKey; /// /// let ctx = miniscript::elements::secp256k1_zkp::Secp256k1::signing_only(); @@ -974,13 +978,13 @@ impl MiniscriptKey for DescriptorPublicKey { } fn is_x_only_key(&self) -> bool { - match self { + matches!( + self, DescriptorPublicKey::Single(SinglePub { key: SinglePubKey::XOnly(ref _key), .. - }) => true, - _ => false, - } + }), + ) } fn num_der_paths(&self) -> usize { @@ -1157,8 +1161,7 @@ impl Serialize for DescriptorPublicKey { mod test { use std::str::FromStr; - use bitcoin::secp256k1; - use bitcoin::util::bip32; + use bitcoin::{bip32, secp256k1}; use elements::secp256k1_zkp; #[cfg(feature = "serde")] use serde_test::{assert_tokens, Token}; diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index 1c07c6dc..2c14fcfb 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -20,7 +20,7 @@ use std::sync::Arc; pub mod pegin; -use bitcoin::util::address::WitnessVersion; +use bitcoin::address::WitnessVersion; use elements::hashes::{hash160, ripemd160, sha256}; use elements::{secp256k1_zkp as secp256k1, secp256k1_zkp, Script, TxIn}; use {bitcoin, elements}; @@ -200,10 +200,7 @@ impl DescriptorInfo { let descriptor = Descriptor::::from_str(s)?; let has_secret = descriptor.for_any_key(|pk| DescriptorSecretKey::from_str(pk).is_ok()); let ty = DescriptorType::from_str(s)?; - let is_pegin = match ty { - DescriptorType::Pegin | DescriptorType::LegacyPegin => true, - _ => false, - }; + let is_pegin = matches!(ty, DescriptorType::Pegin | DescriptorType::LegacyPegin); // Todo: add elements later if is_pegin { Ok(DescriptorInfo::Pegin { has_secret, ty }) @@ -861,7 +858,7 @@ impl Descriptor { } /// Convert all the public keys in the descriptor to [`bitcoin::PublicKey`] by deriving them or - /// otherwise converting them. All [`bitcoin::XOnlyPublicKey`]s are converted to by adding a + /// otherwise converting them. All [`bitcoin::key::XOnlyPublicKey`]s are converted to by adding a /// default(0x02) y-coordinate. /// /// This is a shorthand for: @@ -1140,7 +1137,7 @@ impl Descriptor { impl Descriptor { /// Convert all the public keys in the descriptor to [`bitcoin::PublicKey`] by deriving them or - /// otherwise converting them. All [`bitcoin::XOnlyPublicKey`]s are converted to by adding a + /// otherwise converting them. All [`bitcoin::key::XOnlyPublicKey`]s are converted to by adding a /// default(0x02) y-coordinate. /// /// # Examples @@ -1279,10 +1276,9 @@ mod tests { use std::str::FromStr; use bitcoin; - use bitcoin::util::bip32; - use bitcoin::PublicKey; - use elements::hashes::hex::{FromHex, ToHex}; + use bitcoin::{bip32, PublicKey}; use elements::hashes::{hash160, sha256}; + use elements::hex::{FromHex, ToHex}; use elements::opcodes::all::{OP_CLTV, OP_CSV}; use elements::script::Instruction; use elements::{opcodes, script, Sequence}; @@ -1298,7 +1294,7 @@ mod tests { use crate::{hex_script, Descriptor, Error, Miniscript, NoExt, Satisfier}; type StdDescriptor = Descriptor>; - const TEST_PK: &'static str = + const TEST_PK: &str = "elpk(020000000000000000000000000000000000000000000000000000000000000002)"; fn roundtrip_descriptor(s: &str) { @@ -1317,11 +1313,13 @@ mod tests { // helper function to create elements txin from scriptsig and witness fn elements_txin(script_sig: Script, witness: Vec>) -> elements::TxIn { - let mut txin_witness = elements::TxInWitness::default(); - txin_witness.script_witness = witness; + let txin_witness = elements::TxInWitness { + script_witness: witness, + ..Default::default() + }; elements::TxIn { previous_output: elements::OutPoint::default(), - script_sig: script_sig, + script_sig, sequence: Sequence::from_height(100), is_pegin: false, asset_issuance: elements::AssetIssuance::default(), @@ -1420,7 +1418,7 @@ mod tests { .push_opcode(opcodes::all::OP_DUP) .push_opcode(opcodes::all::OP_HASH160) .push_slice( - &hash160::Hash::from_hex("84e9ed95a38613f0527ff685a9928abe2d4754d4",).unwrap() + &hash160::Hash::from_str("84e9ed95a38613f0527ff685a9928abe2d4754d4",).unwrap() [..] ) .push_opcode(opcodes::all::OP_EQUALVERIFY) @@ -1445,7 +1443,7 @@ mod tests { script::Builder::new() .push_opcode(opcodes::all::OP_PUSHBYTES_0) .push_slice( - &hash160::Hash::from_hex("84e9ed95a38613f0527ff685a9928abe2d4754d4",).unwrap() + &hash160::Hash::from_str("84e9ed95a38613f0527ff685a9928abe2d4754d4",).unwrap() [..] ) .into_script() @@ -1468,7 +1466,7 @@ mod tests { script::Builder::new() .push_opcode(opcodes::all::OP_HASH160) .push_slice( - &hash160::Hash::from_hex("f1c3b9a431134cb90a500ec06e0067cfa9b8bba7",).unwrap() + &hash160::Hash::from_str("f1c3b9a431134cb90a500ec06e0067cfa9b8bba7",).unwrap() [..] ) .push_opcode(opcodes::all::OP_EQUAL) @@ -1493,7 +1491,7 @@ mod tests { script::Builder::new() .push_opcode(opcodes::all::OP_HASH160) .push_slice( - &hash160::Hash::from_hex("aa5282151694d3f2f32ace7d00ad38f927a33ac8",).unwrap() + &hash160::Hash::from_str("aa5282151694d3f2f32ace7d00ad38f927a33ac8",).unwrap() [..] ) .push_opcode(opcodes::all::OP_EQUAL) @@ -1517,7 +1515,7 @@ mod tests { script::Builder::new() .push_opcode(opcodes::all::OP_PUSHBYTES_0) .push_slice( - &sha256::Hash::from_hex( + &sha256::Hash::from_str( "\ f9379edc8983152dc781747830075bd5\ 3896e4b0ce5bff73777fd77d124ba085\ @@ -1545,7 +1543,7 @@ mod tests { script::Builder::new() .push_opcode(opcodes::all::OP_HASH160) .push_slice( - &hash160::Hash::from_hex("4bec5d7feeed99e1d0a23fe32a4afe126a7ff07e",).unwrap() + &hash160::Hash::from_str("4bec5d7feeed99e1d0a23fe32a4afe126a7ff07e",).unwrap() [..] ) .push_opcode(opcodes::all::OP_EQUAL) @@ -1640,7 +1638,7 @@ mod tests { let redeem_script = script::Builder::new() .push_opcode(opcodes::all::OP_PUSHBYTES_0) .push_slice( - &hash160::Hash::from_hex("d1b2a1faf62e73460af885c687dee3b7189cd8ab").unwrap()[..], + &hash160::Hash::from_str("d1b2a1faf62e73460af885c687dee3b7189cd8ab").unwrap()[..], ) .into_script(); let expected_ssig = script::Builder::new() @@ -1812,8 +1810,8 @@ mod tests { let satisfier = { let mut satisfier = HashMap::with_capacity(2); - satisfier.insert(a, (sig_a.clone(), ::elements::EcdsaSigHashType::All)); - satisfier.insert(b, (sig_b.clone(), ::elements::EcdsaSigHashType::All)); + satisfier.insert(a, (sig_a, ::elements::EcdsaSigHashType::All)); + satisfier.insert(b, (sig_b, ::elements::EcdsaSigHashType::All)); satisfier }; @@ -1889,7 +1887,7 @@ mod tests { let key = "[78412e3a/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*"; let expected = DescriptorPublicKey::XPub(DescriptorXKey { origin: Some(( - bip32::Fingerprint::from(&[0x78, 0x41, 0x2e, 0x3a][..]), + bip32::Fingerprint::from([0x78, 0x41, 0x2e, 0x3a]), (&[ bip32::ChildNumber::from_hardened_idx(44).unwrap(), bip32::ChildNumber::from_hardened_idx(0).unwrap(), @@ -1974,7 +1972,7 @@ mod tests { .unwrap(), ), origin: Some(( - bip32::Fingerprint::from(&[0x78, 0x41, 0x2e, 0x3a][..]), + bip32::Fingerprint::from([0x78, 0x41, 0x2e, 0x3a]), (&[ bip32::ChildNumber::from_hardened_idx(0).unwrap(), bip32::ChildNumber::from_normal_idx(42).unwrap(), diff --git a/src/descriptor/pegin/dynafed_pegin.rs b/src/descriptor/pegin/dynafed_pegin.rs index c874ea73..11804203 100644 --- a/src/descriptor/pegin/dynafed_pegin.rs +++ b/src/descriptor/pegin/dynafed_pegin.rs @@ -20,11 +20,12 @@ //! Unlike Pegin descriptors these are Miniscript, so dealing //! with these is easier. +use std::convert::TryFrom; use std::fmt; -use bitcoin::blockdata::script; +use bitcoin::blockdata::script::{self, PushBytes}; use bitcoin::hashes::Hash; -use bitcoin::{self, hashes, Script as BtcScript}; +use bitcoin::{self, hashes, ScriptBuf as BtcScript}; use elements::secp256k1_zkp; use crate::descriptor::checksum::{desc_checksum, verify_checksum}; @@ -186,9 +187,9 @@ impl Pegin { let witness_script = self .bitcoin_witness_script(secp) .expect("TODO after taproot"); - script::Builder::new() - .push_slice(&witness_script.to_v0_p2wsh()[..]) - .into_script() + let push_bytes = <&PushBytes>::try_from(witness_script.as_bytes()) + .expect("Witness script is not too larg"); + script::Builder::new().push_slice(push_bytes).into_script() } /// Computes the bitcoin "witness script" of the descriptor, i.e. the underlying diff --git a/src/descriptor/pegin/legacy_pegin.rs b/src/descriptor/pegin/legacy_pegin.rs index f9a23bb1..0b5b6945 100644 --- a/src/descriptor/pegin/legacy_pegin.rs +++ b/src/descriptor/pegin/legacy_pegin.rs @@ -22,13 +22,15 @@ //! Thus, as a simple solution we implement these as a separate //! struct with it's own API. +use std::convert::TryFrom; use std::fmt; use std::str::FromStr; use std::sync::Arc; +use bitcoin::blockdata::script::PushBytes; use bitcoin::blockdata::{opcodes, script}; use bitcoin::hashes::{hash160, ripemd160, sha256, Hash}; -use bitcoin::{self, hashes, Script as BtcScript}; +use bitcoin::{self, hashes, ScriptBuf as BtcScript}; use bitcoin_miniscript::TranslatePk as BtcTranslatePk; use elements::secp256k1_zkp; @@ -227,7 +229,7 @@ impl LegacyPegin { .push_int(self.fed_k as i64); for key in &self.fed_pks { - let tweaked_pk = tweak_key(key.as_untweaked(), secp, tweak.as_inner()); + let tweaked_pk = tweak_key(key.as_untweaked(), secp, tweak.as_byte_array()); builder = builder.push_key(&tweaked_pk); } let mut nearly_done = builder @@ -267,7 +269,7 @@ impl LegacyPegin { let insert_point = nearly_done.len() - 1; nearly_done.insert(insert_point, 0x68); - bitcoin::Script::from(nearly_done) + BtcScript::from(nearly_done) } /// Create a new descriptor with hard coded values for the @@ -436,9 +438,9 @@ impl LegacyPegin { Pk: ToPublicKey, { let witness_script = self.explicit_script(secp); - script::Builder::new() - .push_slice(&witness_script.to_v0_p2wsh()[..]) - .into_script() + let push_bytes = <&PushBytes>::try_from(witness_script.as_bytes()) + .expect("Witness script is not too larg"); + script::Builder::new().push_slice(push_bytes).into_script() } /// Computes the bitcoin "witness script" of the descriptor, i.e. the underlying /// script before any hashing is done. For `Bare`, `Pkh` and `Wpkh` this @@ -471,10 +473,9 @@ impl LegacyPegin { let unsigned_script_sig = self.bitcoin_unsigned_script_sig(secp); let mut sigs = vec![]; for key in &self.fed_pks { - let tweaked_pk = tweak_key(key.as_untweaked(), secp, tweak.as_inner()); - match satisfier.lookup_ecdsa_sig(&tweaked_pk) { - Some(sig) => sigs.push(sig.to_vec()), - None => {} + let tweaked_pk = tweak_key(key.as_untweaked(), secp, tweak.as_byte_array()); + if let Some(sig) = satisfier.lookup_ecdsa_sig(&tweaked_pk) { + sigs.push(sig.to_vec()); } } sigs.sort_by_key(|a| a.len()); @@ -486,9 +487,8 @@ impl LegacyPegin { } else { let mut emer_sigs = vec![]; for emer_key in &self.emer_pks { - match satisfier.lookup_ecdsa_sig(emer_key.as_untweaked()) { - Some(sig) => emer_sigs.push(sig.to_vec()), - None => {} + if let Some(sig) = satisfier.lookup_ecdsa_sig(emer_key.as_untweaked()) { + emer_sigs.push(sig.to_vec()); } } emer_sigs.sort_by_key(|a| a.len()); diff --git a/src/descriptor/tr.rs b/src/descriptor/tr.rs index 64bd8f8e..6ea64234 100644 --- a/src/descriptor/tr.rs +++ b/src/descriptor/tr.rs @@ -429,8 +429,7 @@ where type Item = (usize, &'a Miniscript); fn next(&mut self) -> Option { - while !self.stack.is_empty() { - let (depth, last) = self.stack.pop().expect("Size checked above"); + while let Some((depth, last)) = self.stack.pop() { match *last { TapTree::Tree(ref l, ref r) => { self.stack.push((depth + 1, r)); @@ -496,19 +495,19 @@ impl_from_tree!( Tr::new(expression::terminal(key, Pk::from_str)?, Some(ret)) } _ => { - return Err(Error::Unexpected(format!( + Err(Error::Unexpected(format!( "{}[#{} args] while parsing taproot descriptor", top.name, top.args.len() - ))); + ))) } } } else { - return Err(Error::Unexpected(format!( + Err(Error::Unexpected(format!( "{}[#{} args] while parsing taproot descriptor", top.name, top.args.len() - ))); + ))) } } ); @@ -554,7 +553,7 @@ fn parse_tr_tree(s: &str) -> Result, Error> { } } - let ret = if s.len() > 5 && &s[..5] == "eltr(" && s.as_bytes()[s.len() - 1] == b')' { + if s.len() > 5 && &s[..5] == "eltr(" && s.as_bytes()[s.len() - 1] == b')' { let rest = &s[5..s.len() - 1]; if !rest.contains(',') { let internal_key = expression::Tree { @@ -591,12 +590,11 @@ fn parse_tr_tree(s: &str) -> Result, Error> { } } else { Err(Error::Unexpected("invalid taproot descriptor".to_string())) - }; - ret + } } fn split_once(inp: &str, delim: char) -> Option<(&str, &str)> { - let ret = if inp.is_empty() { + if inp.is_empty() { None } else { let mut found = inp.len(); @@ -612,8 +610,7 @@ fn split_once(inp: &str, delim: char) -> Option<(&str, &str)> { } else { Some((&inp[..found], &inp[found + 1..])) } - }; - ret + } } impl Liftable for TapTree { diff --git a/src/expression.rs b/src/expression.rs index c86bb096..7b2c2f92 100644 --- a/src/expression.rs +++ b/src/expression.rs @@ -200,6 +200,7 @@ impl<'a> Tree<'a> { } /// Parses a tree from a string + #[allow(clippy::should_implement_trait)] // seems to be a false positive pub fn from_str(s: &'a str) -> Result, Error> { // Filter out non-ASCII because we byte-index strings all over the // place and Rust gets very upsbt when you splinch a string. diff --git a/src/extensions/arith.rs b/src/extensions/arith.rs index 43d2eeb5..3ae55bd8 100644 --- a/src/extensions/arith.rs +++ b/src/extensions/arith.rs @@ -4,14 +4,14 @@ use std::convert::TryInto; use std::str::FromStr; use std::{cmp, error, fmt}; -use bitcoin::XOnlyPublicKey; +use bitcoin::key::XOnlyPublicKey; use bitcoin_miniscript::MiniscriptKey; use elements::opcodes::all::*; use elements::sighash::Prevouts; use elements::{opcodes, script, secp256k1_zkp as secp256k1, SchnorrSig, Transaction}; use super::param::{ExtParamTranslator, TranslateExtParam}; -use super::{CovExtArgs, CsfsKey, ExtParam, IdxExpr, ParseableExt, TxEnv}; +use super::{CovExtArgs, CsfsKey, ExtParam, FromTokenIterError, IdxExpr, ParseableExt, TxEnv}; use crate::expression::{FromTree, Tree}; use crate::extensions::check_sig_price_oracle_1; use crate::miniscript::context::ScriptContextError; @@ -433,7 +433,7 @@ impl Expr { ); let sig = s.pop().ok_or(EvalError::MissingOracleSignature)?; let schnorr_sig_sl = sig.try_push().map_err(|_| EvalError::MalformedSig)?; - let schnorr_sig = secp256k1::schnorr::Signature::from_slice(&schnorr_sig_sl) + let schnorr_sig = secp256k1::schnorr::Signature::from_slice(schnorr_sig_sl) .map_err(|_| EvalError::MalformedSig)?; let secp = secp256k1::Secp256k1::verification_only(); @@ -457,7 +457,7 @@ impl Expr { fn satisfy( &self, env: &TxEnv, - s: &Satisfier, + s: &dyn Satisfier, ) -> Result<(i64, Satisfaction), EvalError> { match &self.inner { ExprInner::Const(c) => Ok((*c, Satisfaction::empty())), @@ -774,7 +774,7 @@ impl Expr { // But care must be taken when introducing new arms. if let Some(Tk::Bytes8(bytes)) = tks.get(e.checked_sub(1)?) { let mut le_bytes = [0u8; 8]; - le_bytes.copy_from_slice(&bytes); + le_bytes.copy_from_slice(bytes); let expr = Expr::from_inner(ExprInner::Const(i64::from_le_bytes(le_bytes))); Some((expr, e - 1)) } else if let Some(Tk::Invert) = tks.get(e.checked_sub(1)?) { @@ -943,23 +943,13 @@ impl Arith { return Err(TypeError::PriceOracle1WFirst); } // Note iter here has consumed the first element - if iter.any(|x| { - if let ExprInner::PriceOracle1(_, _) = x { - true - } else { - false - } - }) { + if iter.any(|x| matches!(x, ExprInner::PriceOracle1(..))) { return Err(TypeError::PriceOracle1Missing); } // All the elements in b should be PriceOracle1W - if b.iter_terminals().any(|x| { - if let ExprInner::PriceOracle1(_, _) = x { - true - } else { - false - } - }) { + if b.iter_terminals() + .any(|x| matches!(x, ExprInner::PriceOracle1(..))) + { return Err(TypeError::PriceOracle1Missing); } } @@ -1017,7 +1007,7 @@ impl Arith { pub fn satisfy_helper( &self, env: &TxEnv, - sat: &Satisfier, + sat: &dyn Satisfier, ) -> Result { let (res, sat_a, sat_b) = match &self.expr { ArithInner::Eq(a, b) => { @@ -1189,7 +1179,7 @@ impl FromTree for Expr { let r: Expr = FromTree::from_tree(&top.args[1])?; Ok(Expr::from_inner(frag(Box::new(l), Box::new(r)))) } - let res = match (top.name, top.args.len()) { + match (top.name, top.args.len()) { ("inp_v", 1) => Ok(Expr::from_inner(expression::unary(top, ExprInner::Input)?)), ("curr_inp_v", 0) => Ok(Expr::from_inner(ExprInner::CurrInputIdx)), ("out_v", 1) => Ok(Expr::from_inner(expression::unary(top, ExprInner::Output)?)), @@ -1207,12 +1197,12 @@ impl FromTree for Expr { "price_oracle1 expects 2 terminal arguments", ))); } - let pk = T::arg_from_str(&top.args[0].name, top.name, 0)?; - let t: u64 = expression::parse_num::(&top.args[1].name)?; + let pk = T::arg_from_str(top.args[0].name, top.name, 0)?; + let t: u64 = expression::parse_num::(top.args[1].name)?; if top.name == "price_oracle1" { - return Ok(Expr::from_inner(ExprInner::PriceOracle1(pk, t))); + Ok(Expr::from_inner(ExprInner::PriceOracle1(pk, t))) } else { - return Ok(Expr::from_inner(ExprInner::PriceOracle1W(pk, t))); + Ok(Expr::from_inner(ExprInner::PriceOracle1W(pk, t))) } } ("add", 2) => binary(top, ExprInner::Add), @@ -1238,8 +1228,7 @@ impl FromTree for Expr { top.name, top.args.len(), ))), - }; - res + } } } @@ -1257,7 +1246,7 @@ impl FromStr for Arith { fn from_str(s: &str) -> Result { let inner = ArithInner::from_str(s)?; - Ok(Arith::new(inner).map_err(|_| Error::Unexpected(String::from("Arith::new")))?) + Arith::new(inner).map_err(|_| Error::Unexpected(String::from("Arith::new"))) } } @@ -1360,14 +1349,17 @@ impl Extension for Arith { )) } - fn from_name_tree(name: &str, children: &[expression::Tree<'_>]) -> Result { + fn from_name_tree( + name: &str, + children: &[expression::Tree<'_>], + ) -> Result { let tree = Tree { name, args: children.to_vec(), // Cloning two references here, it is possible to avoid the to_vec() here, // but it requires lot of refactor. }; - let inner = ArithInner::from_tree(&tree).map_err(|_| ())?; - Arith::new(inner).map_err(|_e| {}) + let inner = ArithInner::from_tree(&tree).map_err(|_| FromTokenIterError)?; + Arith::new(inner).map_err(|_e| FromTokenIterError) } } @@ -1406,20 +1398,20 @@ impl ParseableExt for Arith { self.push_to_builder(builder) } - fn from_token_iter(tokens: &mut TokenIter<'_>) -> Result { + fn from_token_iter(tokens: &mut TokenIter<'_>) -> Result { let len = tokens.len(); - match Self::from_tokens(&tokens.as_inner_mut()) { + match Self::from_tokens(tokens.as_inner_mut()) { Some((res, last_pos)) => { - tokens.advance(len - last_pos).ok_or(())?; + tokens.advance(len - last_pos).ok_or(FromTokenIterError)?; Ok(res) } - None => Err(()), + None => Err(FromTokenIterError), } } - fn evaluate<'intp, 'txin>( - &'intp self, - stack: &mut interpreter::Stack<'txin>, + fn evaluate( + &self, + stack: &mut interpreter::Stack, txenv: Option<&TxEnv>, ) -> Result { let txenv = txenv @@ -1616,13 +1608,12 @@ where ExprInner::Negate(a) => Ok(Expr::from_inner(ExprInner::Negate(Box::new( a.translate_ext(t)?, )))), - ExprInner::PriceOracle1(pk, time) => Ok(Expr::from_inner(ExprInner::PriceOracle1( - t.ext(pk)?, - time.clone(), - ))), + ExprInner::PriceOracle1(pk, time) => { + Ok(Expr::from_inner(ExprInner::PriceOracle1(t.ext(pk)?, *time))) + } ExprInner::PriceOracle1W(pk, time) => Ok(Expr::from_inner(ExprInner::PriceOracle1W( t.ext(pk)?, - time.clone(), + *time, ))), } } @@ -1631,7 +1622,7 @@ where #[cfg(test)] mod tests { use bitcoin::hashes::Hash; - use bitcoin::XOnlyPublicKey; + use bitcoin::key::XOnlyPublicKey; use super::*; use crate::extensions::check_sig_price_oracle_1; diff --git a/src/extensions/csfs.rs b/src/extensions/csfs.rs index 9d935dba..ed12c7e9 100644 --- a/src/extensions/csfs.rs +++ b/src/extensions/csfs.rs @@ -4,13 +4,12 @@ use std::fmt; use std::str::FromStr; -use bitcoin::hashes::hex::{FromHex, ToHex}; -use bitcoin::XOnlyPublicKey; -use elements::hashes::hex; +use bitcoin::key::XOnlyPublicKey; +use elements::hex::{self, FromHex, ToHex}; use elements::{self, opcodes, secp256k1_zkp}; use super::param::{ExtParamTranslator, TranslateExtParam}; -use super::{ArgFromStr, CovExtArgs, ExtParam, ParseableExt, TxEnv}; +use super::{ArgFromStr, CovExtArgs, ExtParam, FromTokenIterError, ParseableExt, TxEnv}; use crate::miniscript::context::ScriptContextError; use crate::miniscript::lex::{Token as Tk, TokenIter}; use crate::miniscript::limits::MAX_STANDARD_P2WSH_STACK_ITEM_SIZE; @@ -99,17 +98,20 @@ impl Extension for CheckSigFromStack { )) } - fn from_name_tree(name: &str, children: &[expression::Tree<'_>]) -> Result { + fn from_name_tree( + name: &str, + children: &[expression::Tree<'_>], + ) -> Result { if children.len() == 2 && name == "csfs" { if !children[0].args.is_empty() || !children[1].args.is_empty() { - return Err(()); + return Err(FromTokenIterError); } - let pk = T::arg_from_str(children[0].name, name, 0).map_err(|_| ())?; - let msg = T::arg_from_str(children[1].name, name, 1).map_err(|_| ())?; + let pk = T::arg_from_str(children[0].name, name, 0).map_err(|_| FromTokenIterError)?; + let msg = T::arg_from_str(children[1].name, name, 1).map_err(|_| FromTokenIterError)?; Ok(Self { pk, msg }) } else { // Correct error handling while parsing fromtree - Err(()) + Err(FromTokenIterError) } } } @@ -201,7 +203,7 @@ impl ArgFromStr for CsfsMsg { /// Wrapper around XOnlyKeys used in CheckSigfromstack #[derive(Debug, Clone, Eq, Ord, PartialOrd, PartialEq, Hash)] -pub struct CsfsKey(pub bitcoin::XOnlyPublicKey); +pub struct CsfsKey(pub bitcoin::key::XOnlyPublicKey); impl fmt::Display for CsfsKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -216,7 +218,7 @@ impl ArgFromStr for CsfsKey { "Key must be at first position in csfs".to_string(), )); } - let k = bitcoin::XOnlyPublicKey::from_str(s)?; + let k = bitcoin::key::XOnlyPublicKey::from_str(s)?; Ok(Self(k)) } } @@ -237,7 +239,7 @@ impl CheckSigFromStack { /// Obtains the message as Vec pub fn as_msg(&self) -> &CsfsMsg { if let CovExtArgs::CsfsMsg(msg) = &self.msg { - &msg + msg } else { unreachable!( "Both constructors from_str and from_token_iter @@ -281,31 +283,31 @@ impl ParseableExt for CheckSigFromStack { .push_opcode(opcodes::all::OP_CHECKSIGFROMSTACK) } - fn from_token_iter(tokens: &mut TokenIter<'_>) -> Result { + fn from_token_iter(tokens: &mut TokenIter<'_>) -> Result { let frag = { - let sl = tokens.peek_slice(3).ok_or(())?; + let sl = tokens.peek_slice(3).ok_or(FromTokenIterError)?; if let (Tk::Bytes32(pk), Tk::Bytes32(msg)) = (&sl[1], &sl[0]) { if sl[2] == Tk::CheckSigFromStack { - let xpk = XOnlyPublicKey::from_slice(&pk).map_err(|_| ())?; - let msg = CsfsMsg::from_slice(msg).ok_or(())?; + let xpk = XOnlyPublicKey::from_slice(pk).map_err(|_| FromTokenIterError)?; + let msg = CsfsMsg::from_slice(msg).ok_or(FromTokenIterError)?; Self { pk: CovExtArgs::XOnlyKey(CsfsKey(xpk)), msg: CovExtArgs::CsfsMsg(msg), } } else { - return Err(()); + return Err(FromTokenIterError); } } else { - return Err(()); + return Err(FromTokenIterError); } }; tokens.advance(3).expect("Size checked previously"); Ok(frag) } - fn evaluate<'intp, 'txin>( - &'intp self, - stack: &mut interpreter::Stack<'txin>, + fn evaluate( + &self, + stack: &mut interpreter::Stack, _txenv: Option<&TxEnv>, ) -> Result { let sig = stack[0].try_push()?; @@ -314,7 +316,7 @@ impl ParseableExt for CheckSigFromStack { return Ok(false); } - let sig = secp256k1_zkp::schnorr::Signature::from_slice(&sig)?; + let sig = secp256k1_zkp::schnorr::Signature::from_slice(sig)?; // rust-secp-zkp API only signing/verification for 32 bytes messages. It is supported in upstream secp-zkp // but bindings are not exposed. // The interpreter will error on non 32 byte messages till it is fixed. @@ -322,7 +324,7 @@ impl ParseableExt for CheckSigFromStack { let secp = secp256k1_zkp::Secp256k1::verification_only(); - secp.verify_schnorr(&sig, &msg, &self.as_pk())?; + secp.verify_schnorr(&sig, &msg, self.as_pk())?; Ok(true) } } @@ -349,7 +351,7 @@ where #[cfg(test)] mod tests { - use bitcoin::XOnlyPublicKey; + use bitcoin::key::XOnlyPublicKey; use super::*; use crate::test_utils::{StrExtTranslator, StrXOnlyKeyTranslator}; @@ -388,7 +390,7 @@ mod tests { let mut t = StrXOnlyKeyTranslator::default(); t.pk_map.insert( "B".to_string(), - bitcoin::XOnlyPublicKey::from_str( + bitcoin::key::XOnlyPublicKey::from_str( "9064b3ac01fb4cb648e8899723ee4d50433920ae558c572e96d945805e0bc3ec", ) .unwrap(), @@ -401,7 +403,7 @@ mod tests { ext_t.ext_map.insert( "A".to_string(), CovExtArgs::XOnlyKey(CsfsKey( - bitcoin::XOnlyPublicKey::from_str( + bitcoin::key::XOnlyPublicKey::from_str( "26d137d15e2ae24f2d5158663d190d1269ad6b1a6ce330aa825ba502e7519d44", ) .unwrap(), diff --git a/src/extensions/index_ops.rs b/src/extensions/index_ops.rs index fea34396..00cd0d50 100644 --- a/src/extensions/index_ops.rs +++ b/src/extensions/index_ops.rs @@ -147,7 +147,7 @@ impl IdxExpr { /// Evaluate this expression pub fn eval(&self, env: &TxEnv) -> Result { match self { - IdxExpr::Const(i) => Ok(*i as usize), + IdxExpr::Const(i) => Ok(*i), IdxExpr::CurrIdx => Ok(env.idx), IdxExpr::Add(x, y) => Ok(x.eval(env)? + y.eval(env)?), IdxExpr::Sub(x, y) => Ok(x.eval(env)? - y.eval(env)?), diff --git a/src/extensions/introspect_ops.rs b/src/extensions/introspect_ops.rs index d5bc9faa..32c02929 100644 --- a/src/extensions/introspect_ops.rs +++ b/src/extensions/introspect_ops.rs @@ -4,16 +4,16 @@ use std::convert::TryFrom; use std::fmt; use std::str::FromStr; -use bitcoin::hashes::hex::{FromHex, ToHex}; use bitcoin::hashes::{sha256, Hash}; use elements::address::Payload; use elements::confidential::Asset; +use elements::hex::{FromHex, ToHex}; use elements::opcodes::all::*; use elements::{confidential, encode, script, Address, AddressParams}; use super::index_ops::IdxExpr; use super::param::{ExtParamTranslator, TranslateExtParam}; -use super::{ArgFromStr, CovExtArgs, EvalError, ExtParam, ParseableExt, TxEnv}; +use super::{ArgFromStr, CovExtArgs, EvalError, ExtParam, FromTokenIterError, ParseableExt, TxEnv}; use crate::expression::{FromTree, Tree}; use crate::miniscript::context::ScriptContextError; use crate::miniscript::lex::{Token as Tk, TokenIter}; @@ -184,8 +184,8 @@ impl AssetExpr { fn from_tree_parent(top: &Tree<'_>, parent: &str, pos: usize) -> Result { match (top.name, top.args.len()) { ("curr_inp_asset", 0) => Ok(AssetExpr::CurrInputAsset), - ("inp_asset", 1) => expression::unary(&top, AssetExpr::Input), - ("out_asset", 1) => expression::unary(&top, AssetExpr::Output), + ("inp_asset", 1) => expression::unary(top, AssetExpr::Input), + ("out_asset", 1) => expression::unary(top, AssetExpr::Output), (asset, 0) => Ok(AssetExpr::Const(T::arg_from_str(asset, parent, pos)?)), _ => Err(Error::Unexpected(format!( "{}({} args) while parsing Extension", @@ -256,8 +256,8 @@ impl ValueExpr { fn from_tree_parent(top: &Tree<'_>, parent: &str, pos: usize) -> Result { match (top.name, top.args.len()) { ("curr_inp_value", 0) => Ok(ValueExpr::CurrInputValue), - ("inp_value", 1) => expression::unary(&top, ValueExpr::Input), - ("out_value", 1) => expression::unary(&top, ValueExpr::Output), + ("inp_value", 1) => expression::unary(top, ValueExpr::Input), + ("out_value", 1) => expression::unary(top, ValueExpr::Output), (value, 0) => Ok(ValueExpr::Const(T::arg_from_str(value, parent, pos)?)), _ => Err(Error::Unexpected(format!( "{}({} args) while parsing Extension", @@ -328,8 +328,8 @@ impl SpkExpr { fn from_tree_parent(top: &Tree<'_>, parent: &str, pos: usize) -> Result { match (top.name, top.args.len()) { ("curr_inp_spk", 0) => Ok(SpkExpr::CurrInputSpk), - ("inp_spk", 1) => expression::unary(&top, SpkExpr::Input), - ("out_spk", 1) => expression::unary(&top, SpkExpr::Output), + ("inp_spk", 1) => expression::unary(top, SpkExpr::Input), + ("out_spk", 1) => expression::unary(top, SpkExpr::Output), (asset, 0) => Ok(SpkExpr::Const(T::arg_from_str(asset, parent, pos)?)), _ => Err(Error::Unexpected(format!( "{}({} args) while parsing Extension", @@ -381,24 +381,24 @@ impl FromTree for CovOps { fn from_tree(top: &Tree<'_>) -> Result { match (top.name, top.args.len()) { ("is_exp_asset", 1) => { - AssetExpr::from_tree_parent(&top.args[0], &top.name, 0).map(CovOps::IsExpAsset) + AssetExpr::from_tree_parent(&top.args[0], top.name, 0).map(CovOps::IsExpAsset) } ("is_exp_value", 1) => { - ValueExpr::from_tree_parent(&top.args[0], &top.name, 0).map(CovOps::IsExpValue) + ValueExpr::from_tree_parent(&top.args[0], top.name, 0).map(CovOps::IsExpValue) } ("asset_eq", 2) => { - let l = AssetExpr::from_tree_parent(&top.args[0], &top.name, 0)?; - let r = AssetExpr::from_tree_parent(&top.args[1], &top.name, 1)?; + let l = AssetExpr::from_tree_parent(&top.args[0], top.name, 0)?; + let r = AssetExpr::from_tree_parent(&top.args[1], top.name, 1)?; Ok(CovOps::AssetEq(l, r)) } ("value_eq", 2) => { - let l = ValueExpr::from_tree_parent(&top.args[0], &top.name, 0)?; - let r = ValueExpr::from_tree_parent(&top.args[1], &top.name, 1)?; + let l = ValueExpr::from_tree_parent(&top.args[0], top.name, 0)?; + let r = ValueExpr::from_tree_parent(&top.args[1], top.name, 1)?; Ok(CovOps::ValueEq(l, r)) } ("spk_eq", 2) => { - let l = SpkExpr::from_tree_parent(&top.args[0], &top.name, 0)?; - let r = SpkExpr::from_tree_parent(&top.args[1], &top.name, 1)?; + let l = SpkExpr::from_tree_parent(&top.args[0], top.name, 0)?; + let r = SpkExpr::from_tree_parent(&top.args[1], top.name, 1)?; Ok(CovOps::SpkEq(l, r)) } ("curr_idx_eq", 1) => { @@ -440,11 +440,7 @@ impl Extension for CovOps { fn extra_prop(&self) -> ExtData { ExtData { pk_cost: self.script_size(), // 1 opcodes, 1 key push, msg, 1 msg push - has_free_verify: if let CovOps::CurrIndEq(..) = self { - true - } else { - false - }, + has_free_verify: matches!(self, CovOps::CurrIndEq(..)), stack_elem_count_sat: Some(0), stack_elem_count_dissat: Some(0), max_sat_size: Some((0, 0)), @@ -474,13 +470,13 @@ impl Extension for CovOps { } } - fn from_name_tree(name: &str, children: &[Tree<'_>]) -> Result { + fn from_name_tree(name: &str, children: &[Tree<'_>]) -> Result { let tree = Tree { name, args: children.to_vec(), // Cloning references here, it is possible to avoid the to_vec() here, // but it requires lot of refactor. }; - Self::from_tree(&tree).map_err(|_| ()) + Self::from_tree(&tree).map_err(|_| FromTokenIterError) } fn segwit_ctx_checks(&self) -> Result<(), ScriptContextError> { @@ -571,8 +567,7 @@ impl ArgFromStr for confidential::Asset { )); } let asset_hex = Vec::::from_hex(s).map_err(|e| Error::Unexpected(e.to_string()))?; - Ok(elements::encode::deserialize(&asset_hex) - .map_err(|e| Error::Unexpected(e.to_string()))?) + elements::encode::deserialize(&asset_hex).map_err(|e| Error::Unexpected(e.to_string())) } } @@ -584,8 +579,7 @@ impl ArgFromStr for confidential::Value { )); } let asset_hex = Vec::::from_hex(s).map_err(|e| Error::Unexpected(e.to_string()))?; - Ok(elements::encode::deserialize(&asset_hex) - .map_err(|e| Error::Unexpected(e.to_string()))?) + elements::encode::deserialize(&asset_hex).map_err(|e| Error::Unexpected(e.to_string())) } } @@ -596,7 +590,7 @@ fn asset(pref: u8, comm: &[u8]) -> Option { if comm.len() != 32 { return None; } - bytes[1..].copy_from_slice(&comm); + bytes[1..].copy_from_slice(comm); encode::deserialize(&bytes).ok() } @@ -605,7 +599,7 @@ fn value(pref: u8, comm: &[u8]) -> Option { if comm.len() == 32 { let mut bytes = [0u8; 33]; bytes[0] = pref; - bytes[1..].copy_from_slice(&comm); + bytes[1..].copy_from_slice(comm); encode::deserialize(&bytes).ok() } else if comm.len() == 8 { let mut bytes = [0u8; 8]; @@ -642,7 +636,10 @@ fn spk(pref: i8, prog: &[u8]) -> Option { // This converts legacy programs to (-1, sha256::Hash(spk)) fn spk_to_components(s: &elements::Script) -> (i8, Vec) { if !s.is_witness_program() { - (-1, sha256::Hash::hash(s.as_bytes()).to_vec()) + ( + -1, + sha256::Hash::hash(s.as_bytes()).to_byte_array().to_vec(), + ) } else { // indirect way to get payload. // The address parameters don't really matter here @@ -664,7 +661,7 @@ impl AssetExpr { AssetExpr::Const(CovExtArgs::Asset(a)) => { match a { Asset::Null => unreachable!("Attempt to push Null asset"), - Asset::Explicit(a) => builder.push_slice(&a.into_inner()).push_int(1), // explicit prefix + Asset::Explicit(a) => builder.push_slice(a.into_inner().as_ref()).push_int(1), // explicit prefix Asset::Confidential(c) => { let ser = c.serialize(); builder.push_slice(&ser[1..]).push_int(ser[0] as i64) @@ -839,7 +836,7 @@ impl SpkExpr { SpkExpr::Const(CovExtArgs::Script(s)) => { let (ver, prog) = match &s.0 { SpkInner::Script(s) => spk_to_components(s), - SpkInner::Hashed(h) => (-1, h.to_vec()), + SpkInner::Hashed(h) => (-1, h.to_byte_array().to_vec()), }; builder.push_slice(&prog).push_int(ver as i64) } @@ -864,7 +861,7 @@ impl SpkExpr { let res = match self { SpkExpr::Const(CovExtArgs::Script(s)) => match &s.0 { SpkInner::Script(s) => spk_to_components(s), - SpkInner::Hashed(h) => (-1, h.to_vec()), + SpkInner::Hashed(h) => (-1, h.to_byte_array().to_vec()), }, SpkExpr::Const(_) => unreachable!( "Both constructors from_str and from_token_iter @@ -908,7 +905,7 @@ impl SpkExpr { } else if let Some(&[Tk::Bytes32(spk_vec), Tk::NumNeg1]) = tks.get(e.checked_sub(2)?..e) { let mut inner = [0u8; 32]; inner.copy_from_slice(spk_vec); - let hashed_spk = Spk(SpkInner::Hashed(sha256::Hash::from_inner(inner))); + let hashed_spk = Spk(SpkInner::Hashed(sha256::Hash::from_byte_array(inner))); Some((SpkExpr::Const(CovExtArgs::Script(hashed_spk)), e - 2)) } else if let Some(&[Tk::Push(ref spk_vec), Tk::Num(i)]) = tks.get(e.checked_sub(2)?..e) { let script = spk(i8::try_from(i).ok()?, spk_vec)?; @@ -1155,20 +1152,20 @@ impl ParseableExt for CovOps { self.push_to_builder(builder) } - fn from_token_iter(tokens: &mut TokenIter<'_>) -> Result { + fn from_token_iter(tokens: &mut TokenIter<'_>) -> Result { let len = tokens.len(); - match Self::from_tokens(&tokens.as_inner_mut()) { + match Self::from_tokens(tokens.as_inner_mut()) { Some((res, last_pos)) => { - tokens.advance(len - last_pos).ok_or(())?; + tokens.advance(len - last_pos).ok_or(FromTokenIterError)?; Ok(res) } - None => Err(()), + None => Err(FromTokenIterError), } } - fn evaluate<'intp, 'txin>( - &'intp self, - stack: &mut interpreter::Stack<'txin>, + fn evaluate( + &self, + stack: &mut interpreter::Stack, txenv: Option<&TxEnv>, ) -> Result { let txenv = txenv @@ -1218,7 +1215,7 @@ where #[cfg(test)] mod tests { - use bitcoin::XOnlyPublicKey; + use bitcoin::key::XOnlyPublicKey; use super::*; use crate::test_utils::{StrExtTranslator, StrXOnlyKeyTranslator}; diff --git a/src/extensions/mod.rs b/src/extensions/mod.rs index e60f5e10..6a9fafd4 100644 --- a/src/extensions/mod.rs +++ b/src/extensions/mod.rs @@ -35,6 +35,9 @@ pub use self::outputs_pref::LegacyOutputsPref; pub use self::param::{ArgFromStr, CovExtArgs, ExtParam, NoExtParam}; pub use self::tx_ver::LegacyVerEq; +/// Failed to extract a token from an iterator of tokens +pub struct FromTokenIterError; + /// Extensions to elements-miniscript. /// Refer to implementations(unimplemented!) for example and tutorials pub trait Extension: Clone + Eq + Ord + fmt::Debug + fmt::Display + hash::Hash { @@ -65,7 +68,7 @@ pub trait Extension: Clone + Eq + Ord + fmt::Debug + fmt::Display + hash::Hash { /// `Vec`. // Ideally, we would want a FromTree implementation here, but that is not possible // as we would need to create a new Tree by removing wrappers from root. - fn from_name_tree(_name: &str, children: &[Tree<'_>]) -> Result; + fn from_name_tree(_name: &str, children: &[Tree<'_>]) -> Result; } /// Support for parsing/serializing/satisfaction of extensions. @@ -81,7 +84,7 @@ pub trait ParseableExt: /// Parse the terminal from [`TokenIter`]. Implementers of this trait are responsible /// for making sure tokens is mutated correctly. If parsing is not successful, the tokens /// should not be consumed. - fn from_token_iter(_tokens: &mut TokenIter<'_>) -> Result; + fn from_token_iter(_tokens: &mut TokenIter<'_>) -> Result; /// Interpreter support /// Evaluate the fragment based on inputs from stack. If an implementation of this @@ -90,9 +93,9 @@ pub trait ParseableExt: /// Output Ok(true) when the ext fragment is satisfied. /// Output Ok(false) when the ext fragment is dissatisfied, /// Output Some(Err) when there is an error in interpreter value. - fn evaluate<'intp, 'txin>( - &'intp self, - stack: &mut Stack<'txin>, + fn evaluate( + &self, + stack: &mut Stack, txenv: Option<&TxEnv>, ) -> Result; @@ -140,9 +143,9 @@ impl Extension for NoExt { match *self {} } - fn from_name_tree(_name: &str, _children: &[Tree<'_>]) -> Result { + fn from_name_tree(_name: &str, _children: &[Tree<'_>]) -> Result { // No extensions should not parse any extensions from String - Err(()) + Err(FromTokenIterError) } fn segwit_ctx_checks(&self) -> Result<(), ScriptContextError> { @@ -167,9 +170,9 @@ impl ParseableExt for NoExt { match *self {} } - fn evaluate<'intp, 'txin>( - &'intp self, - _stack: &mut Stack<'txin>, + fn evaluate( + &self, + _stack: &mut Stack, _txenv: Option<&TxEnv>, ) -> Result { match *self {} @@ -179,9 +182,9 @@ impl ParseableExt for NoExt { match *self {} } - fn from_token_iter(_tokens: &mut TokenIter<'_>) -> Result { + fn from_token_iter(_tokens: &mut TokenIter<'_>) -> Result { // No extensions should return Err on parsing - Err(()) + Err(FromTokenIterError) } } @@ -255,7 +258,7 @@ macro_rules! try_from_arms { } else if let Ok(v) = as $trt>::$f($($args, )*) { Ok(CovenantExt::Introspect(v)) }else { - Err(()) + Err(FromTokenIterError) } }; } @@ -277,7 +280,7 @@ impl Extension for CovenantExt { all_arms_fn!(self, Extension, script_size,) } - fn from_name_tree(name: &str, children: &[Tree<'_>]) -> Result { + fn from_name_tree(name: &str, children: &[Tree<'_>]) -> Result { try_from_arms!(Extension, T, from_name_tree, name, children,) } @@ -303,9 +306,9 @@ impl ParseableExt for CovenantExt { all_arms_fn!(self, ParseableExt, dissatisfy, sat,) } - fn evaluate<'intp, 'txin>( + fn evaluate( &self, - stack: &mut Stack<'txin>, + stack: &mut Stack, txenv: Option<&TxEnv>, ) -> Result { all_arms_fn!(self, ParseableExt, evaluate, stack, txenv,) @@ -315,7 +318,7 @@ impl ParseableExt for CovenantExt { all_arms_fn!(self, ParseableExt, push_to_builder, builder,) } - fn from_token_iter(tokens: &mut TokenIter<'_>) -> Result { + fn from_token_iter(tokens: &mut TokenIter<'_>) -> Result { try_from_arms!(ParseableExt, CovExtArgs, from_token_iter, tokens,) } } @@ -422,7 +425,7 @@ pub fn check_sig_price_oracle_1( let sha_msg = elements::hashes::sha256::Hash::hash(&buf); let msg = elements::secp256k1_zkp::Message::from_slice(&sha_msg[..]).unwrap(); - secp.verify_schnorr(&sig, &msg, &pk).is_ok() + secp.verify_schnorr(sig, &msg, pk).is_ok() } /// [`secp256k1_zkp::Message`] for fragment `price_oracle_1`. diff --git a/src/extensions/outputs_pref.rs b/src/extensions/outputs_pref.rs index b035c2ec..344ece30 100644 --- a/src/extensions/outputs_pref.rs +++ b/src/extensions/outputs_pref.rs @@ -5,10 +5,10 @@ use std::fmt; use elements::encode::serialize; -use elements::hashes::hex::{FromHex, ToHex}; use elements::hashes::{sha256d, Hash}; +use elements::hex::{FromHex, ToHex}; -use super::{ParseableExt, TxEnv}; +use super::{FromTokenIterError, ParseableExt, TxEnv}; use crate::descriptor::CovError; use crate::miniscript::astelem::StackCtxOperations; use crate::miniscript::context::ScriptContextError; @@ -105,13 +105,17 @@ impl Extension for LegacyOutputsPref { + 6 /* line 2 */ } - fn from_name_tree(name: &str, children: &[expression::Tree<'_>]) -> Result { + fn from_name_tree( + name: &str, + children: &[expression::Tree<'_>], + ) -> Result { if children.len() == 1 && name == "outputs_pref" { - let pref = expression::terminal(&children[0], Vec::::from_hex).map_err(|_| ())?; + let pref = expression::terminal(&children[0], Vec::::from_hex) + .map_err(|_| FromTokenIterError)?; Ok(Self { pref }) } else { // Correct error handling while parsing fromtree - Err(()) + Err(FromTokenIterError) } } } @@ -208,9 +212,9 @@ impl ParseableExt for LegacyOutputsPref { builder.check_item_pref(4, &self.pref) } - fn from_token_iter(tokens: &mut TokenIter<'_>) -> Result { + fn from_token_iter(tokens: &mut TokenIter<'_>) -> Result { let outputs_pref = { - let sl = tokens.peek_slice(15).ok_or(())?; + let sl = tokens.peek_slice(15).ok_or(FromTokenIterError)?; if let Tk::Push(pref) = &sl[6] { if sl[0] == Tk::Cat && sl[1] == Tk::Cat @@ -228,19 +232,19 @@ impl ParseableExt for LegacyOutputsPref { { Self { pref: pref.clone() } } else { - return Err(()); + return Err(FromTokenIterError); } } else { - return Err(()); + return Err(FromTokenIterError); } }; tokens.advance(15).expect("Size checked previously"); Ok(outputs_pref) } - fn evaluate<'intp, 'txin>( - &'intp self, - stack: &mut interpreter::Stack<'txin>, + fn evaluate( + &self, + stack: &mut interpreter::Stack, _txenv: Option<&TxEnv>, ) -> Result { // Hash Outputs is at index 3 @@ -264,7 +268,7 @@ impl ParseableExt for LegacyOutputsPref { for _ in 0..max_elems { stack.pop().unwrap(); } - if sha256d::Hash::hash(&outputs_builder).as_inner() == hash_outputs { + if sha256d::Hash::hash(&outputs_builder).as_byte_array() == hash_outputs { stack.push(interpreter::Element::Satisfied); Ok(true) } else { diff --git a/src/extensions/param.rs b/src/extensions/param.rs index f15a22f2..76f0a8c9 100644 --- a/src/extensions/param.rs +++ b/src/extensions/param.rs @@ -2,9 +2,9 @@ use std::{fmt, hash}; -use bitcoin::hashes::hex::ToHex; use elements::confidential; use elements::encode::serialize; +use elements::hex::ToHex; use super::csfs::{CsfsKey, CsfsMsg}; use super::introspect_ops::Spk; @@ -105,7 +105,7 @@ impl From for CovExtArgs { impl CovExtArgs { /// Creates a new csfs key variant of [`CovExtArgs`] - pub fn csfs_key(key: bitcoin::XOnlyPublicKey) -> Self { + pub fn csfs_key(key: bitcoin::key::XOnlyPublicKey) -> Self { CovExtArgs::XOnlyKey(CsfsKey(key)) } @@ -196,7 +196,7 @@ where /// Translates one extension to another fn ext(&mut self, cov: &CovenantExt) -> Result, E> { match *cov { - CovenantExt::LegacyVerEq(ref v) => Ok(CovenantExt::LegacyVerEq(v.clone())), + CovenantExt::LegacyVerEq(ref v) => Ok(CovenantExt::LegacyVerEq(*v)), CovenantExt::LegacyOutputsPref(ref p) => Ok(CovenantExt::LegacyOutputsPref(p.clone())), CovenantExt::Csfs(ref c) => Ok(CovenantExt::Csfs(TranslateExtParam::translate_ext( c, self, diff --git a/src/extensions/tx_ver.rs b/src/extensions/tx_ver.rs index a22ab023..7b69ffca 100644 --- a/src/extensions/tx_ver.rs +++ b/src/extensions/tx_ver.rs @@ -6,7 +6,7 @@ use std::fmt; use elements::encode::serialize; -use super::{ParseableExt, TxEnv}; +use super::{FromTokenIterError, ParseableExt, TxEnv}; use crate::descriptor::CovError; use crate::miniscript::astelem::StackCtxOperations; use crate::miniscript::lex::{Token as Tk, TokenIter}; @@ -84,13 +84,17 @@ impl Extension for LegacyVerEq { 4 + 1 + 1 + 4 // opcodes + push opcodes + target size } - fn from_name_tree(name: &str, children: &[expression::Tree<'_>]) -> Result { + fn from_name_tree( + name: &str, + children: &[expression::Tree<'_>], + ) -> Result { if children.len() == 1 && name == "ver_eq" { - let n = expression::terminal(&children[0], expression::parse_num).map_err(|_| ())?; + let n = expression::terminal(&children[0], expression::parse_num) + .map_err(|_| FromTokenIterError)?; Ok(Self { n }) } else { // Correct error handling while parsing fromtree - Err(()) + Err(FromTokenIterError) } } } @@ -143,9 +147,9 @@ impl ParseableExt for LegacyVerEq { builder.check_item_eq(12, &serialize(&self.n)) } - fn from_token_iter(tokens: &mut TokenIter<'_>) -> Result { + fn from_token_iter(tokens: &mut TokenIter<'_>) -> Result { let ver = { - let sl = tokens.peek_slice(5).ok_or(())?; + let sl = tokens.peek_slice(5).ok_or(FromTokenIterError)?; if let Tk::PickPush4(ver) = sl[3] { if sl[0] == Tk::Depth && sl[1] == Tk::Num(12) @@ -154,19 +158,19 @@ impl ParseableExt for LegacyVerEq { { Self { n: ver } } else { - return Err(()); + return Err(FromTokenIterError); } } else { - return Err(()); + return Err(FromTokenIterError); } }; tokens.advance(5).expect("Size checked previously"); Ok(ver) } - fn evaluate<'intp, 'txin>( - &'intp self, - stack: &mut interpreter::Stack<'txin>, + fn evaluate( + &self, + stack: &mut interpreter::Stack, _txenv: Option<&TxEnv>, ) -> Result { // Version is at index 11 diff --git a/src/interpreter/error.rs b/src/interpreter/error.rs index e093ae52..49e9448b 100644 --- a/src/interpreter/error.rs +++ b/src/interpreter/error.rs @@ -4,7 +4,7 @@ use std::{error, fmt}; use elements::hashes::hash160; -use elements::hashes::hex::ToHex; +use elements::hex::ToHex; use elements::{secp256k1_zkp, taproot}; use {bitcoin, elements}; @@ -30,7 +30,7 @@ pub enum Error { /// General Interpreter error. CouldNotEvaluate, /// EcdsaSig related error - EcdsaSig(bitcoin::EcdsaSigError), + EcdsaSig(bitcoin::ecdsa::Error), /// We expected a push (including a `OP_1` but no other numeric pushes) ExpectedPush, /// The preimage to the hash function must be exactly 32 bytes. @@ -50,7 +50,7 @@ pub enum Error { /// ecdsa Signature failed to verify InvalidEcdsaSignature(bitcoin::PublicKey), /// Signature failed to verify - InvalidSchnorrSignature(bitcoin::XOnlyPublicKey), + InvalidSchnorrSignature(bitcoin::key::XOnlyPublicKey), /// Last byte of this signature isn't a standard sighash type NonStandardSigHash(Vec), /// Miniscript error @@ -285,7 +285,7 @@ pub enum PkEvalErrInner { /// Full Key FullKey(bitcoin::PublicKey), /// XOnly Key - XOnlyKey(bitcoin::XOnlyPublicKey), + XOnlyKey(bitcoin::key::XOnlyPublicKey), } impl From for PkEvalErrInner { diff --git a/src/interpreter/inner.rs b/src/interpreter/inner.rs index 51c755a8..c99219ae 100644 --- a/src/interpreter/inner.rs +++ b/src/interpreter/inner.rs @@ -2,9 +2,9 @@ // SPDX-License-Identifier: CC0-1.0 use bitcoin; -use bitcoin::util::taproot::TAPROOT_ANNEX_PREFIX; +use bitcoin::taproot::TAPROOT_ANNEX_PREFIX; use elements::hashes::{hash160, sha256, Hash}; -use elements::schnorr::TapTweak; +use elements::schnorr::TweakedPublicKey; use elements::taproot::ControlBlock; use elements::{self, script}; @@ -238,11 +238,11 @@ pub fn from_txdata<'txin, Ext: ParseableExt>( } } // ** pay to taproot **// - } else if is_v1_p2tr(&spk) { + } else if is_v1_p2tr(spk) { if !ssig_stack.is_empty() { Err(Error::NonEmptyScriptSig) } else { - let output_key = bitcoin::XOnlyPublicKey::from_slice(&spk[2..]) + let output_key = bitcoin::key::XOnlyPublicKey::from_slice(&spk[2..]) .map_err(|_| Error::XOnlyPublicKeyParseError)?; let has_annex = wit_stack .last() @@ -280,7 +280,7 @@ pub fn from_txdata<'txin, Ext: ParseableExt>( // This is fixed in rust-bitcoin. Should also be fixed in rust-elements if ctrl_blk.verify_taproot_commitment( &secp, - &output_key.dangerous_assume_tweaked(), + &TweakedPublicKey::new(output_key), &tap_script, ) { Ok(( @@ -434,18 +434,18 @@ impl ToNoChecks } impl ToNoChecks - for Miniscript + for Miniscript { fn to_no_checks_ms(&self) -> Miniscript { // specify the () error type as this cannot error struct TranslateXOnlyPk; - impl Translator for TranslateXOnlyPk { - fn pk(&mut self, pk: &bitcoin::XOnlyPublicKey) -> Result { + impl Translator for TranslateXOnlyPk { + fn pk(&mut self, pk: &bitcoin::key::XOnlyPublicKey) -> Result { Ok(BitcoinKey::XOnlyPublicKey(*pk)) } - translate_hash_clone!(bitcoin::XOnlyPublicKey, BitcoinKey, ()); + translate_hash_clone!(bitcoin::key::XOnlyPublicKey, BitcoinKey, ()); } self.real_translate_pk(&mut TranslateXOnlyPk) .expect("Translation should succeed") @@ -457,8 +457,8 @@ mod tests { use std::str::FromStr; - use elements::hashes::hex::FromHex; use elements::hashes::{hash160, sha256, Hash}; + use elements::hex::FromHex; use elements::{self, script, Script}; use super::*; diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index ad2c8e53..5a393100 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -8,6 +8,7 @@ //! assuming that the spent coin was descriptor controlled. //! +use std::borrow::Borrow; use std::fmt; use std::str::FromStr; @@ -49,7 +50,7 @@ pub enum KeySigPair { /// A Full public key and corresponding Ecdsa signature Ecdsa(bitcoin::PublicKey, ElementsSig), /// A x-only key and corresponding Schnorr signature - Schnorr(bitcoin::XOnlyPublicKey, elements::SchnorrSig), + Schnorr(bitcoin::key::XOnlyPublicKey, elements::SchnorrSig), } impl KeySigPair { @@ -61,8 +62,8 @@ impl KeySigPair { } } - /// Obtain a pair of ([`bitcoin::XOnlyPublicKey`], [`elements::SchnorrSig`]) from [`KeySigPair`] - pub fn as_schnorr(&self) -> Option<(bitcoin::XOnlyPublicKey, elements::SchnorrSig)> { + /// Obtain a pair of ([`bitcoin::key::XOnlyPublicKey`], [`elements::SchnorrSig`]) from [`KeySigPair`] + pub fn as_schnorr(&self) -> Option<(bitcoin::key::XOnlyPublicKey, elements::SchnorrSig)> { match self { KeySigPair::Ecdsa(_, _) => None, KeySigPair::Schnorr(pk, sig) => Some((*pk, *sig)), @@ -90,7 +91,7 @@ pub enum BitcoinKey { /// Full key Fullkey(bitcoin::PublicKey), /// Xonly key - XOnlyPublicKey(bitcoin::XOnlyPublicKey), + XOnlyPublicKey(bitcoin::key::XOnlyPublicKey), } impl BitcoinKey { @@ -118,8 +119,8 @@ impl From for BitcoinKey { } } -impl From for BitcoinKey { - fn from(xpk: bitcoin::XOnlyPublicKey) -> Self { +impl From for BitcoinKey { + fn from(xpk: bitcoin::key::XOnlyPublicKey) -> Self { BitcoinKey::XOnlyPublicKey(xpk) } } @@ -220,7 +221,7 @@ where None }, has_errored: false, - txenv: txenv, + txenv, sig_type: self.sig_type(), } } @@ -234,19 +235,19 @@ where /// - Insufficient sighash information is present /// - sighash single without corresponding output // TODO: Create a good first isse to change this to error - pub fn verify_sig( + pub fn verify_sig>( &self, secp: &secp256k1_zkp::Secp256k1, tx: &elements::Transaction, input_idx: usize, - prevouts: &sighash::Prevouts<'_>, + prevouts: &sighash::Prevouts<'_, T>, genesis_hash: elements::BlockHash, sig: &KeySigPair, ) -> bool { - fn get_prevout<'u>( - prevouts: &sighash::Prevouts<'u>, + fn get_prevout<'u, T: Borrow>( + prevouts: &'u sighash::Prevouts<'u, T>, input_index: usize, - ) -> Option<&'u elements::TxOut> { + ) -> Option<&'u T> { match prevouts { sighash::Prevouts::One(index, prevout) => { if input_index == *index { @@ -266,7 +267,7 @@ where cache.legacy_sighash(input_idx, script_pubkey, ecdsa_sig.1) } else if self.is_segwit_v0() { let amt = match get_prevout(prevouts, input_idx) { - Some(txout) => txout.value, + Some(txout) => txout.borrow().value, None => return false, }; cache.segwitv0_sighash(input_idx, script_pubkey, amt, ecdsa_sig.1) @@ -303,8 +304,9 @@ where // schnorr sigs in ecdsa descriptors return false; }; - let msg = sighash_msg - .map(|hash| secp256k1_zkp::Message::from_slice(&hash).expect("32 byte")); + let msg = sighash_msg.map(|hash| { + secp256k1_zkp::Message::from_slice(hash.as_ref()).expect("32 byte") + }); let success = msg.map(|msg| secp.verify_schnorr(&schnorr_sig.sig, &msg, xpk).is_ok()); success.unwrap_or(false) // unwrap_or_default checks for errors, while success would have checksig results @@ -698,7 +700,9 @@ where Terminal::After(ref n) => { debug_assert_eq!(node_state.n_evaluated, 0); debug_assert_eq!(node_state.n_satisfied, 0); - let res = self.stack.evaluate_after(&n.into(), self.lock_time); + let res = self + .stack + .evaluate_after(&LockTime::from(*n), self.lock_time); if res.is_some() { return res; } @@ -1212,7 +1216,7 @@ mod tests { Vec, secp256k1_zkp::Message, Secp256k1, - Vec, + Vec, Vec, Vec>, ) { @@ -1244,8 +1248,8 @@ mod tests { pks.push(pk); der_sigs.push(sigser); - let keypair = bitcoin::KeyPair::from_secret_key(&secp, &sk); - let (x_only_pk, _parity) = bitcoin::XOnlyPublicKey::from_keypair(&keypair); + let keypair = bitcoin::key::KeyPair::from_secret_key(&secp, &sk); + let (x_only_pk, _parity) = bitcoin::key::XOnlyPublicKey::from_keypair(&keypair); x_only_pks.push(x_only_pk); let schnorr_sig = secp.sign_schnorr_with_aux_rand(&msg, &keypair, &[0u8; 32]); let schnorr_sig = elements::SchnorrSig { @@ -1754,7 +1758,7 @@ mod tests { } fn x_only_no_checks_ms(ms: &str) -> Miniscript { - let elem: Miniscript = + let elem: Miniscript = Miniscript::from_str_ext(ms, &ExtParams::allow_all()).unwrap(); elem.to_no_checks_ms() } diff --git a/src/interpreter/stack.rs b/src/interpreter/stack.rs index a121a97b..deda9afa 100644 --- a/src/interpreter/stack.rs +++ b/src/interpreter/stack.rs @@ -193,7 +193,7 @@ impl<'txin> Stack<'txin> { // We don't really store information about which key error. fn bitcoin_key_from_slice(sl: &[u8], sig_type: SigType) -> Option { let key: BitcoinKey = match sig_type { - SigType::Schnorr => bitcoin::XOnlyPublicKey::from_slice(sl).ok()?.into(), + SigType::Schnorr => bitcoin::key::XOnlyPublicKey::from_slice(sl).ok()?.into(), SigType::Ecdsa => bitcoin::PublicKey::from_slice(sl).ok()?.into(), }; Some(key) diff --git a/src/lib.rs b/src/lib.rs index 1aefd52c..eed53e9f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,15 +89,9 @@ //! //! -#![cfg_attr(all(test, feature = "unstable"), feature(test))] -// Coding conventions -#![allow(bare_trait_objects)] -#![deny(non_camel_case_types)] -#![deny(non_snake_case)] -#![deny(unused_mut)] -#![deny(dead_code)] -#![deny(unused_imports)] -#![deny(missing_docs)] +#![cfg_attr(miniscript_bench, feature(test))] +#![allow(clippy::manual_range_contains)] // this lint is just stupid +#![allow(clippy::type_complexity)] // clippy doesn't like how many generics we use #[cfg(target_pointer_width = "16")] compile_error!( @@ -107,19 +101,19 @@ compile_error!( #[cfg(feature = "serde")] pub use actual_serde as serde; pub use {bitcoin, elements}; -#[cfg(all(test, feature = "unstable"))] +#[cfg(miniscript_bench)] extern crate test; // Miniscript imports // It can be confusing to code when we have two miniscript libraries // As a rule, only import the library here and pub use all the required // items. Should help in faster code development in the long run -pub(crate) use bitcoin_miniscript::expression::{FromTree as BtcFromTree, Tree as BtcTree}; -pub(crate) use bitcoin_miniscript::policy::semantic::Policy as BtcPolicy; -pub(crate) use bitcoin_miniscript::policy::Liftable as BtcLiftable; +use bitcoin_miniscript::expression::{FromTree as BtcFromTree, Tree as BtcTree}; +use bitcoin_miniscript::policy::semantic::Policy as BtcPolicy; +use bitcoin_miniscript::policy::Liftable as BtcLiftable; // re-export imports pub use bitcoin_miniscript::{hash256, ForEachKey, MiniscriptKey, SigType, ToPublicKey}; -pub(crate) use bitcoin_miniscript::{ +use bitcoin_miniscript::{ Descriptor as BtcDescriptor, Error as BtcError, Miniscript as BtcMiniscript, Satisfier as BtcSatisfier, Segwitv0 as BtcSegwitv0, Terminal as BtcTerminal, }; @@ -145,11 +139,11 @@ pub mod psbt; mod test_utils; mod util; -use std::{error, fmt, str}; +use std::{cmp, error, fmt, str}; use elements::hashes::sha256; use elements::secp256k1_zkp::Secp256k1; -use elements::{opcodes, script, secp256k1_zkp}; +use elements::{locktime, opcodes, script, secp256k1_zkp}; pub use crate::descriptor::{DefiniteDescriptorKey, Descriptor, DescriptorPublicKey}; pub use crate::extensions::{CovenantExt, Extension, NoExt, TxEnv}; @@ -179,7 +173,7 @@ mod contracthash { .inner .add_exp_tweak( secp, - &Scalar::from_be_bytes(hmac_result.into_inner()) + &Scalar::from_be_bytes(hmac_result.to_byte_array()) .expect("Result of hash must be a valid point"), ) .expect("HMAC cannot produce invalid tweak"); @@ -294,7 +288,7 @@ pub enum Error { /// rust-bitcoin script error Script(script::Error), /// rust-bitcoin address error - AddrError(bitcoin::util::address::Error), + AddrError(bitcoin::address::Error), /// A `CHECKMULTISIG` opcode was preceded by a number > 20 CmsTooManyKeys(u32), /// A tapscript multi_a cannot support more than MAX_BLOCK_WEIGHT/32 keys @@ -322,7 +316,7 @@ pub enum Error { /// Parsed a miniscript but there were more script opcodes after it Trailing(String), /// Failed to parse a push as a public key - BadPubkey(bitcoin::util::key::Error), + BadPubkey(bitcoin::key::Error), /// Could not satisfy a script (fragment) because of a missing hash preimage MissingHash(sha256::Hash), /// Could not satisfy a script (fragment) because of a missing signature @@ -433,14 +427,14 @@ impl From for Error { } #[doc(hidden)] -impl From for Error { - fn from(e: bitcoin::util::key::Error) -> Error { +impl From for Error { + fn from(e: bitcoin::key::Error) -> Error { Error::BadPubkey(e) } } -impl From for Error { - fn from(e: bitcoin::util::address::Error) -> Error { +impl From for Error { + fn from(e: bitcoin::address::Error) -> Error { Error::AddrError(e) } } @@ -461,7 +455,7 @@ impl fmt::Display for Error { Error::NonMinimalVerify(ref tok) => write!(f, "{} VERIFY", tok), Error::InvalidPush(ref push) => { write!(f, "invalid push ")?; - bitcoin::hashes::hex::format_hex(push, f) + elements::hex::format_hex(push, f) }, Error::Script(ref e) => fmt::Display::fmt(e, f), Error::AddrError(ref e) => fmt::Display::fmt(e, f), @@ -628,10 +622,69 @@ fn push_opcode_size(script_size: usize) -> usize { } } +/// An absolute locktime that implements `Ord`. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct AbsLockTime(locktime::LockTime); + +impl AbsLockTime { + /// Constructs an `AbsLockTime` from an nLockTime value or the argument to OP_CHEKCLOCKTIMEVERIFY. + pub fn from_consensus(n: u32) -> Self { + Self(locktime::LockTime::from_consensus(n)) + } + + /// Returns the inner `u32` value. This is the value used when creating this `LockTime` + /// i.e., `n OP_CHECKLOCKTIMEVERIFY` or nLockTime. + /// + /// This calls through to `locktime::LockTime::to_consensus_u32()` and the same usage warnings + /// apply. + pub fn to_consensus_u32(self) -> u32 { + self.0.to_consensus_u32() + } + + /// Returns the inner `u32` value. + /// + /// Equivalent to `AbsLockTime::to_consensus_u32()`. + pub fn to_u32(self) -> u32 { + self.to_consensus_u32() + } +} + +impl From for AbsLockTime { + fn from(lock_time: locktime::LockTime) -> Self { + Self(lock_time) + } +} + +impl From for locktime::LockTime { + fn from(lock_time: AbsLockTime) -> locktime::LockTime { + lock_time.0 + } +} + +impl cmp::PartialOrd for AbsLockTime { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl cmp::Ord for AbsLockTime { + fn cmp(&self, other: &Self) -> cmp::Ordering { + let this = self.0.to_consensus_u32(); + let that = other.0.to_consensus_u32(); + this.cmp(&that) + } +} + +impl fmt::Display for AbsLockTime { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + /// Helper function used by tests #[cfg(test)] fn hex_script(s: &str) -> elements::Script { - let v: Vec = elements::hashes::hex::FromHex::from_hex(s).unwrap(); + let v: Vec = elements::hex::FromHex::from_hex(s).unwrap(); elements::Script::from(v) } diff --git a/src/miniscript/analyzable.rs b/src/miniscript/analyzable.rs index 81bb2a4f..41d8ae68 100644 --- a/src/miniscript/analyzable.rs +++ b/src/miniscript/analyzable.rs @@ -217,10 +217,8 @@ impl Miniscript bool { - self.iter().any(|ms| match ms.node { - Terminal::RawPkH(_) => true, - _ => false, - }) + self.iter() + .any(|ms| matches!(ms.node, Terminal::RawPkH(..))) } /// Check whether the underlying Miniscript is safe under the current context diff --git a/src/miniscript/astelem.rs b/src/miniscript/astelem.rs index 340eb0c5..22cb2795 100644 --- a/src/miniscript/astelem.rs +++ b/src/miniscript/astelem.rs @@ -13,7 +13,7 @@ use std::str::FromStr; use std::sync::Arc; use bitcoin::hashes::hash160; -use elements::{opcodes, script, LockTime, Sequence}; +use elements::{opcodes, script, Sequence}; use super::limits::{MAX_SCRIPT_ELEMENT_SIZE, MAX_STANDARD_P2WSH_STACK_ITEM_SIZE}; use crate::extensions::ParseableExt; @@ -22,8 +22,8 @@ use crate::miniscript::types::{self, Property}; use crate::miniscript::ScriptContext; use crate::util::MsKeyBuilder; use crate::{ - errstr, expression, script_num_size, Error, ExtTranslator, Extension, ForEachKey, Miniscript, - MiniscriptKey, Terminal, ToPublicKey, TranslateExt, TranslatePk, Translator, + errstr, expression, script_num_size, AbsLockTime, Error, ExtTranslator, Extension, ForEachKey, + Miniscript, MiniscriptKey, Terminal, ToPublicKey, TranslateExt, TranslatePk, Translator, }; impl Terminal { @@ -219,7 +219,7 @@ impl Terminal = match *self { Terminal::PkK(ref p) => Terminal::PkK(p.clone()), Terminal::PkH(ref p) => Terminal::PkH(p.clone()), - Terminal::RawPkH(ref h) => Terminal::RawPkH(h.clone()), + Terminal::RawPkH(ref h) => Terminal::RawPkH(*h), Terminal::After(n) => Terminal::After(n), Terminal::Older(n) => Terminal::Older(n), Terminal::Sha256(ref x) => Terminal::Sha256(x.clone()), @@ -571,7 +571,7 @@ impl_from_tree!( } ("pk_h", 1) => expression::terminal(&top.args[0], |x| Pk::from_str(x).map(Terminal::PkH)), ("after", 1) => expression::terminal(&top.args[0], |x| { - expression::parse_num::(x).map(|x| Terminal::After(LockTime::from_consensus(x).into())) + expression::parse_num::(x).map(|x| Terminal::After(AbsLockTime::from_consensus(x))) }), ("older", 1) => expression::terminal(&top.args[0], |x| { expression::parse_num::(x).map(|x| Terminal::Older(Sequence::from_consensus(x))) @@ -790,7 +790,7 @@ impl Terminal builder .push_opcode(opcodes::all::OP_DUP) .push_opcode(opcodes::all::OP_HASH160) - .push_slice(hash) + .push_slice(hash.as_ref()) .push_opcode(opcodes::all::OP_EQUALVERIFY), Terminal::After(t) => builder .push_int(t.to_u32().into()) @@ -803,28 +803,28 @@ impl Terminal builder .push_opcode(opcodes::all::OP_SIZE) .push_int(32) .push_opcode(opcodes::all::OP_EQUALVERIFY) .push_opcode(opcodes::all::OP_HASH256) - .push_slice(&Pk::to_hash256(h)) + .push_slice(Pk::to_hash256(h).as_ref()) .push_opcode(opcodes::all::OP_EQUAL), Terminal::Ripemd160(ref h) => builder .push_opcode(opcodes::all::OP_SIZE) .push_int(32) .push_opcode(opcodes::all::OP_EQUALVERIFY) .push_opcode(opcodes::all::OP_RIPEMD160) - .push_slice(&Pk::to_ripemd160(h)) + .push_slice(Pk::to_ripemd160(h).as_ref()) .push_opcode(opcodes::all::OP_EQUAL), Terminal::Hash160(ref h) => builder .push_opcode(opcodes::all::OP_SIZE) .push_int(32) .push_opcode(opcodes::all::OP_EQUALVERIFY) .push_opcode(opcodes::all::OP_HASH160) - .push_slice(&Pk::to_hash160(h)) + .push_slice(Pk::to_hash160(h).as_ref()) .push_opcode(opcodes::all::OP_EQUAL), Terminal::True => builder.push_opcode(opcodes::OP_TRUE), Terminal::False => builder.push_opcode(opcodes::OP_FALSE), diff --git a/src/miniscript/decode.rs b/src/miniscript/decode.rs index 72cd4b98..a0c0e67c 100644 --- a/src/miniscript/decode.rs +++ b/src/miniscript/decode.rs @@ -12,7 +12,7 @@ use std::{error, fmt}; use elements::hashes::{hash160, ripemd160, sha256, Hash}; -use crate::elements::{LockTime, PackedLockTime, Sequence}; +use crate::elements::Sequence; use crate::extensions::ParseableExt; use crate::miniscript::lex::{Token as Tk, TokenIter}; use crate::miniscript::limits::{MAX_BLOCK_WEIGHT, MAX_PUBKEYS_PER_MULTISIG}; @@ -21,7 +21,9 @@ use crate::miniscript::types::{Property, Type}; use crate::miniscript::ScriptContext; #[cfg(doc)] use crate::Descriptor; -use crate::{bitcoin, hash256, Error, Extension, Miniscript, MiniscriptKey, NoExt, ToPublicKey}; +use crate::{ + bitcoin, hash256, AbsLockTime, Error, Extension, Miniscript, MiniscriptKey, NoExt, ToPublicKey, +}; fn return_none(_: usize) -> Option { None @@ -39,9 +41,9 @@ impl ParseableKey for bitcoin::PublicKey { } } -impl ParseableKey for bitcoin::XOnlyPublicKey { +impl ParseableKey for bitcoin::key::XOnlyPublicKey { fn from_slice(sl: &[u8]) -> Result { - bitcoin::XOnlyPublicKey::from_slice(sl).map_err(KeyParseError::XonlyKeyParseError) + bitcoin::key::XOnlyPublicKey::from_slice(sl).map_err(KeyParseError::XonlyKeyParseError) } } @@ -49,7 +51,7 @@ impl ParseableKey for bitcoin::XOnlyPublicKey { #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum KeyParseError { /// Bitcoin PublicKey parse error - FullKeyParseError(bitcoin::util::key::Error), + FullKeyParseError(bitcoin::key::Error), /// Xonly key parse Error XonlyKeyParseError(bitcoin::secp256k1::Error), } @@ -79,7 +81,7 @@ mod private { // Implement for those same types, but no others. impl Sealed for super::bitcoin::PublicKey {} - impl Sealed for super::bitcoin::XOnlyPublicKey {} + impl Sealed for super::bitcoin::key::XOnlyPublicKey {} } #[derive(Copy, Clone, Debug)] @@ -136,7 +138,7 @@ pub enum Terminal RawPkH(hash160::Hash), // timelocks /// `n CHECKLOCKTIMEVERIFY` - After(PackedLockTime), + After(AbsLockTime), /// `n CHECKSEQUENCEVERIFY` Older(Sequence), // hashlocks @@ -398,7 +400,7 @@ pub fn parse( Tk::CheckSequenceVerify, Tk::Num(n) => term.reduce0(Terminal::Older(Sequence::from_consensus(n)))?, Tk::CheckLockTimeVerify, Tk::Num(n) - => term.reduce0(Terminal::After(LockTime::from_consensus(n).into()))?, + => term.reduce0(Terminal::After(AbsLockTime::from_consensus(n)))?, // hashlocks Tk::Equal => match_token!( tokens, @@ -653,13 +655,12 @@ pub fn parse( } fn is_and_v(tokens: &mut TokenIter<'_>) -> bool { - match tokens.peek() { - None - | Some(&Tk::If) - | Some(&Tk::NotIf) - | Some(&Tk::Else) - | Some(&Tk::ToAltStack) - | Some(&Tk::Swap) => false, - _ => true, - } + !matches!( + tokens.peek(), + None | Some(&Tk::If) + | Some(&Tk::NotIf) + | Some(&Tk::Else) + | Some(&Tk::ToAltStack) + | Some(&Tk::Swap) + ) } diff --git a/src/miniscript/iter.rs b/src/miniscript/iter.rs index 08793a85..18e4cc15 100644 --- a/src/miniscript/iter.rs +++ b/src/miniscript/iter.rs @@ -62,30 +62,30 @@ impl Miniscript Option<&Miniscript> { match (n, &self.node) { - (0, &Terminal::Alt(ref node)) - | (0, &Terminal::Swap(ref node)) - | (0, &Terminal::Check(ref node)) - | (0, &Terminal::DupIf(ref node)) - | (0, &Terminal::Verify(ref node)) - | (0, &Terminal::NonZero(ref node)) - | (0, &Terminal::ZeroNotEqual(ref node)) - | (0, &Terminal::AndV(ref node, _)) - | (0, &Terminal::AndB(ref node, _)) - | (0, &Terminal::OrB(ref node, _)) - | (0, &Terminal::OrD(ref node, _)) - | (0, &Terminal::OrC(ref node, _)) - | (0, &Terminal::OrI(ref node, _)) - | (1, &Terminal::AndV(_, ref node)) - | (1, &Terminal::AndB(_, ref node)) - | (1, &Terminal::OrB(_, ref node)) - | (1, &Terminal::OrD(_, ref node)) - | (1, &Terminal::OrC(_, ref node)) - | (1, &Terminal::OrI(_, ref node)) - | (0, &Terminal::AndOr(ref node, _, _)) - | (1, &Terminal::AndOr(_, ref node, _)) - | (2, &Terminal::AndOr(_, _, ref node)) => Some(node), - - (n, &Terminal::Thresh(_, ref node_vec)) => node_vec.get(n).map(|x| &**x), + (0, Terminal::Alt(node)) + | (0, Terminal::Swap(node)) + | (0, Terminal::Check(node)) + | (0, Terminal::DupIf(node)) + | (0, Terminal::Verify(node)) + | (0, Terminal::NonZero(node)) + | (0, Terminal::ZeroNotEqual(node)) + | (0, Terminal::AndV(node, _)) + | (0, Terminal::AndB(node, _)) + | (0, Terminal::OrB(node, _)) + | (0, Terminal::OrD(node, _)) + | (0, Terminal::OrC(node, _)) + | (0, Terminal::OrI(node, _)) + | (1, Terminal::AndV(_, node)) + | (1, Terminal::AndB(_, node)) + | (1, Terminal::OrB(_, node)) + | (1, Terminal::OrD(_, node)) + | (1, Terminal::OrC(_, node)) + | (1, Terminal::OrI(_, node)) + | (0, Terminal::AndOr(node, _, _)) + | (1, Terminal::AndOr(_, node, _)) + | (2, Terminal::AndOr(_, _, node)) => Some(node), + + (n, Terminal::Thresh(_, node_vec)) => node_vec.get(n).map(|x| &**x), _ => None, } @@ -268,9 +268,9 @@ pub mod test { let preimage = vec![0xab; 32]; let sha256_hash = sha256::Hash::hash(&preimage); let sha256d_hash_rev = sha256d::Hash::hash(&preimage); - let mut sha256d_hash_bytes = sha256d_hash_rev.into_inner(); + let mut sha256d_hash_bytes = sha256d_hash_rev.to_byte_array(); sha256d_hash_bytes.reverse(); - let sha256d_hash = sha256d::Hash::from_inner(sha256d_hash_bytes); + let sha256d_hash = sha256d::Hash::from_byte_array(sha256d_hash_bytes); let hash160_hash = hash160::Hash::hash(&preimage); let ripemd160_hash = ripemd160::Hash::hash(&preimage); diff --git a/src/miniscript/mod.rs b/src/miniscript/mod.rs index 2c253652..213bb40b 100644 --- a/src/miniscript/mod.rs +++ b/src/miniscript/mod.rs @@ -203,25 +203,23 @@ where /// use elements_miniscript::bitcoin::secp256k1::XOnlyPublicKey; /// type Segwitv0Script = Miniscript; /// type TapScript = Miniscript; - /// use bitcoin::hashes::hex::FromHex; - /// fn main() { - /// // parse x-only miniscript in Taproot context - /// let tapscript_ms = TapScript::parse(&elements::Script::from(Vec::::from_hex( - /// "202788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac", - /// ).expect("Even length hex"))) - /// .expect("Xonly keys are valid only in taproot context"); - /// // tapscript fails decoding when we use them with compressed keys - /// let err = TapScript::parse(&elements::Script::from(Vec::::from_hex( - /// "21022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac", - /// ).expect("Even length hex"))) - /// .expect_err("Compressed keys cannot be used in Taproot context"); - /// // Segwitv0 succeeds decoding with full keys. - /// Segwitv0Script::parse(&elements::Script::from(Vec::::from_hex( - /// "21022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac", - /// ).expect("Even length hex"))) - /// .expect("Compressed keys are allowed in Segwit context"); + /// use elements::hex::FromHex; /// - /// } + /// // parse x-only miniscript in Taproot context + /// let tapscript_ms = TapScript::parse(&elements::Script::from(Vec::::from_hex( + /// "202788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac", + /// ).expect("Even length hex"))) + /// .expect("Xonly keys are valid only in taproot context"); + /// // tapscript fails decoding when we use them with compressed keys + /// let err = TapScript::parse(&elements::Script::from(Vec::::from_hex( + /// "21022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac", + /// ).expect("Even length hex"))) + /// .expect_err("Compressed keys cannot be used in Taproot context"); + /// // Segwitv0 succeeds decoding with full keys. + /// Segwitv0Script::parse(&elements::Script::from(Vec::::from_hex( + /// "21022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac", + /// ).expect("Even length hex"))) + /// .expect("Compressed keys are allowed in Segwit context"); /// ``` pub fn parse(script: &script::Script) -> Result, Error> { let ms = Self::parse_with_ext(script, &ExtParams::sane())?; @@ -522,8 +520,11 @@ serde_string_impl_pk!(Miniscript, "a miniscript", Ctx; ScriptContext => Ext2 ; E pub mod hash256 { use bitcoin::hashes::{hash_newtype, sha256d}; - #[rustfmt::skip] - hash_newtype!(Hash, sha256d::Hash, 32, doc = "A bitcoin block hash.", false); + hash_newtype! { + /// A hash256 of preimage. + #[hash_newtype(forward)] + pub struct Hash(sha256d::Hash); + } } #[cfg(test)] @@ -534,7 +535,8 @@ mod tests { use std::str::FromStr; use std::sync::Arc; - use bitcoin::{self, XOnlyPublicKey}; + use bitcoin::key::XOnlyPublicKey; + use bitcoin::{self}; use elements::hashes::{hash160, sha256, Hash}; use elements::taproot::TapLeafHash; use elements::{self, secp256k1_zkp, Sequence}; @@ -736,7 +738,7 @@ mod tests { ", ) .unwrap(); - let hash = hash160::Hash::from_inner([17; 20]); + let hash = hash160::Hash::from_byte_array([17; 20]); let pkk_ms: Miniscript = Miniscript { node: Terminal::Check(Arc::new(Miniscript { @@ -1088,7 +1090,7 @@ mod tests { ); assert_eq!( ms.unwrap_err().to_string(), - "unexpected «key hex decoding error»", + "unexpected «PublicKey hex should be 66 or 130 digits long, got: 64»", ); Tapscript::from_str_insane( "pk(2788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)", @@ -1160,7 +1162,8 @@ mod tests { .unwrap(); // script rtt test assert_eq!( - Miniscript::::parse_insane(&tap_ms.encode()).unwrap(), + Miniscript::::parse_insane(&tap_ms.encode()) + .unwrap(), tap_ms ); assert_eq!(tap_ms.script_size(), 104); diff --git a/src/miniscript/satisfy.rs b/src/miniscript/satisfy.rs index e3ddd285..80b82e69 100644 --- a/src/miniscript/satisfy.rs +++ b/src/miniscript/satisfy.rs @@ -77,7 +77,7 @@ pub trait Satisfier { None } - /// Given a raw `Pkh`, lookup corresponding [`bitcoin::XOnlyPublicKey`] + /// Given a raw `Pkh`, lookup corresponding [`bitcoin::key::XOnlyPublicKey`] fn lookup_raw_pkh_x_only_pk(&self, _: &hash160::Hash) -> Option { None } @@ -950,17 +950,17 @@ impl PartialOrd for Witness { impl Ord for Witness { fn cmp(&self, other: &Self) -> cmp::Ordering { match (self, other) { - (&Witness::Stack(ref v1), &Witness::Stack(ref v2)) => { + (Witness::Stack(v1), Witness::Stack(v2)) => { let w1 = witness_size(v1); let w2 = witness_size(v2); w1.cmp(&w2) } - (&Witness::Stack(_), _) => cmp::Ordering::Less, - (_, &Witness::Stack(_)) => cmp::Ordering::Greater, - (&Witness::Impossible, &Witness::Unavailable) => cmp::Ordering::Less, - (&Witness::Unavailable, &Witness::Impossible) => cmp::Ordering::Greater, - (&Witness::Impossible, &Witness::Impossible) => cmp::Ordering::Equal, - (&Witness::Unavailable, &Witness::Unavailable) => cmp::Ordering::Equal, + (Witness::Stack(_), _) => cmp::Ordering::Less, + (_, Witness::Stack(_)) => cmp::Ordering::Greater, + (Witness::Impossible, Witness::Unavailable) => cmp::Ordering::Less, + (Witness::Unavailable, Witness::Impossible) => cmp::Ordering::Greater, + (Witness::Impossible, Witness::Impossible) => cmp::Ordering::Equal, + (Witness::Unavailable, Witness::Unavailable) => cmp::Ordering::Equal, } } } @@ -1189,11 +1189,11 @@ impl Satisfaction { let mut sat_indices = (0..subs.len()).collect::>(); sat_indices.sort_by_key(|&i| { let stack_weight = match (&sats[i].stack, &ret_stack[i].stack) { - (&Witness::Unavailable, _) | (&Witness::Impossible, _) => i64::MAX, + (Witness::Unavailable, _) | (Witness::Impossible, _) => i64::MAX, // This can only be the case when we have PkH without the corresponding // Pubkey. - (_, &Witness::Unavailable) | (_, &Witness::Impossible) => i64::MIN, - (&Witness::Stack(ref s), &Witness::Stack(ref d)) => { + (_, Witness::Unavailable) | (_, Witness::Impossible) => i64::MIN, + (Witness::Stack(ref s), Witness::Stack(ref d)) => { witness_size(s) as i64 - witness_size(d) as i64 } }; @@ -1309,10 +1309,10 @@ impl Satisfaction { sat_indices.sort_by_key(|&i| { // For malleable satifactions, directly choose smallest weights match (&sats[i].stack, &ret_stack[i].stack) { - (&Witness::Unavailable, _) | (&Witness::Impossible, _) => i64::MAX, + (Witness::Unavailable, _) | (Witness::Impossible, _) => i64::MAX, // This is only possible when one of the branches has PkH - (_, &Witness::Unavailable) | (_, &Witness::Impossible) => i64::MIN, - (&Witness::Stack(ref s), &Witness::Stack(ref d)) => { + (_, Witness::Unavailable) | (_, Witness::Impossible) => i64::MIN, + (Witness::Stack(ref s), Witness::Stack(ref d)) => { witness_size(s) as i64 - witness_size(d) as i64 } } diff --git a/src/miniscript/types/extra_props.rs b/src/miniscript/types/extra_props.rs index cb023d66..bdbee9b0 100644 --- a/src/miniscript/types/extra_props.rs +++ b/src/miniscript/types/extra_props.rs @@ -6,7 +6,7 @@ use std::cmp; use std::iter::once; -use elements::{LockTime, PackedLockTime, Sequence}; +use elements::{LockTime, Sequence}; use super::{Error, ErrorKind, Property, ScriptContext}; use crate::miniscript::context::SigType; @@ -938,7 +938,7 @@ impl Property for ExtData { // Note that for CLTV this is a limitation not of Bitcoin but Miniscript. The // number on the stack would be a 5 bytes signed integer but Miniscript's B type // only consumes 4 bytes from the stack. - if t == PackedLockTime::ZERO { + if t == LockTime::ZERO.into() { return Err(Error { fragment: fragment.clone(), error: ErrorKind::InvalidTime, diff --git a/src/miniscript/types/mod.rs b/src/miniscript/types/mod.rs index a6da769c..40135028 100644 --- a/src/miniscript/types/mod.rs +++ b/src/miniscript/types/mod.rs @@ -11,7 +11,7 @@ pub mod malleability; use std::{error, fmt}; -use elements::{LockTime, PackedLockTime, Sequence}; +use elements::{LockTime, Sequence}; pub use self::correctness::{Base, Correctness, Input}; pub use self::extra_props::ExtData; @@ -432,7 +432,7 @@ pub trait Property: Sized { // Note that for CLTV this is a limitation not of Bitcoin but Miniscript. The // number on the stack would be a 5 bytes signed integer but Miniscript's B type // only consumes 4 bytes from the stack. - if t == PackedLockTime::ZERO { + if t == LockTime::ZERO.into() { return Err(Error { fragment: fragment.clone(), error: ErrorKind::InvalidTime, @@ -831,7 +831,7 @@ impl Property for Type { // Note that for CLTV this is a limitation not of Bitcoin but Miniscript. The // number on the stack would be a 5 bytes signed integer but Miniscript's B type // only consumes 4 bytes from the stack. - if t == PackedLockTime::ZERO { + if t == LockTime::ZERO.into() { return Err(Error { fragment: fragment.clone(), error: ErrorKind::InvalidTime, diff --git a/src/policy/compiler.rs b/src/policy/compiler.rs index ecb60d7f..d567da2c 100644 --- a/src/policy/compiler.rs +++ b/src/policy/compiler.rs @@ -24,10 +24,17 @@ type PolicyCache = BTreeMap<(Concrete, OrdF64, Option), BTreeMap>>; ///Ordered f64 for comparison -#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)] +#[derive(Copy, Clone, PartialEq, Debug)] pub(crate) struct OrdF64(pub f64); impl Eq for OrdF64 {} +// We could derive PartialOrd, but we can't derive Ord, and clippy wants us +// to derive both or neither. Better to be explicit. +impl PartialOrd for OrdF64 { + fn partial_cmp(&self, other: &OrdF64) -> Option { + self.0.partial_cmp(&other.0) + } +} impl Ord for OrdF64 { fn cmp(&self, other: &OrdF64) -> cmp::Ordering { // will panic if given NaN @@ -127,7 +134,7 @@ impl CompilationKey { CompilationKey { ty, expensive_verify, - dissat_prob: dissat_prob.and_then(|x| Some(OrdF64(x))), + dissat_prob: dissat_prob.map(OrdF64), } } } @@ -374,6 +381,7 @@ impl Property for CompilerExtData { }) } + #[allow(clippy::manual_map)] // clippy doesn't like our else { None } tail fn or_i(l: Self, r: Self) -> Result { let lprob = l .branch_prob @@ -415,11 +423,7 @@ impl Property for CompilerExtData { Ok(CompilerExtData { branch_prob: None, sat_cost: aprob * (a.sat_cost + b.sat_cost) + cprob * (adis + c.sat_cost), - dissat_cost: if let Some(cdis) = c.dissat_cost { - Some(adis + cdis) - } else { - None - }, + dissat_cost: c.dissat_cost.map(|cdis| adis + cdis), }) } @@ -529,8 +533,8 @@ impl AstElemExt { let comp_ext_data = CompilerExtData::type_check(&ast, lookup_ext)?; Ok(AstElemExt { ms: Arc::new(Miniscript { - ty: ty, - ext: ext, + ty, + ext, node: ast, phantom: PhantomData, }), @@ -668,7 +672,7 @@ fn insert_elem( return false; } - if let Err(_) = Ctx::check_local_validity(&elem.ms) { + if Ctx::check_local_validity(&elem.ms).is_err() { return false; } @@ -685,13 +689,13 @@ fn insert_elem( let existing_elem_cost = existing_elem.cost_1d(sat_prob, dissat_prob); existing_key.is_subtype(elem_key) && existing_elem_cost <= elem_cost }) - .fold(false, |acc, x| acc || x); + .any(|x| x); if !is_worse { // If the element is not worse any element in the map, remove elements // whose subtype is the current element and have worse cost. - *map = mem::replace(map, BTreeMap::new()) + *map = mem::take(map) .into_iter() - .filter(|&(ref existing_key, ref existing_elem)| { + .filter(|(existing_key, existing_elem)| { let existing_elem_cost = existing_elem.cost_1d(sat_prob, dissat_prob); !(elem_key.is_subtype(*existing_key) && existing_elem_cost >= elem_cost) }) @@ -724,8 +728,8 @@ fn insert_elem_closure( while !cast_stack.is_empty() { let current = cast_stack.pop_front().unwrap(); - for i in 0..casts.len() { - if let Ok(new_ext) = casts[i].cast(¤t) { + for cast in &casts { + if let Ok(new_ext) = cast.cast(¤t) { if insert_elem(map, new_ext.clone(), sat_prob, dissat_prob) { cast_stack.push_back(new_ext); } @@ -756,9 +760,9 @@ fn insert_best_wrapped( if dissat_prob.is_some() { let casts: [Cast; 10] = all_casts::(); - for i in 0..casts.len() { + for cast in &casts { for x in best_compilations(policy_cache, policy, sat_prob, None)?.values() { - if let Ok(new_ext) = casts[i].cast(x) { + if let Ok(new_ext) = cast.cast(x) { insert_elem_closure(map, new_ext, sat_prob, dissat_prob); } } @@ -781,7 +785,7 @@ where { //Check the cache for hits let ord_sat_prob = OrdF64(sat_prob); - let ord_dissat_prob = dissat_prob.and_then(|x| Some(OrdF64(x))); + let ord_dissat_prob = dissat_prob.map(OrdF64); if let Some(ret) = policy_cache.get(&(policy.clone(), ord_sat_prob, ord_dissat_prob)) { return Ok(ret.clone()); } @@ -880,12 +884,12 @@ where let rw = subs[1].0 as f64 / total; //and-or - if let (&Concrete::And(ref x), _) = (&subs[0].1, &subs[1].1) { + if let (Concrete::And(x), _) = (&subs[0].1, &subs[1].1) { let mut a1 = best_compilations( policy_cache, &x[0], lw * sat_prob, - Some(dissat_prob.unwrap_or(0 as f64) + rw * sat_prob), + Some(dissat_prob.unwrap_or(0.0) + rw * sat_prob), )?; let mut a2 = best_compilations(policy_cache, &x[0], lw * sat_prob, None)?; @@ -893,7 +897,7 @@ where policy_cache, &x[1], lw * sat_prob, - Some(dissat_prob.unwrap_or(0 as f64) + rw * sat_prob), + Some(dissat_prob.unwrap_or(0.0) + rw * sat_prob), )?; let mut b2 = best_compilations(policy_cache, &x[1], lw * sat_prob, None)?; @@ -903,12 +907,12 @@ where compile_tern!(&mut a1, &mut b2, &mut c, [lw, rw]); compile_tern!(&mut b1, &mut a2, &mut c, [lw, rw]); }; - if let (_, &Concrete::And(ref x)) = (&subs[0].1, &subs[1].1) { + if let (_, Concrete::And(x)) = (&subs[0].1, &subs[1].1) { let mut a1 = best_compilations( policy_cache, &x[0], rw * sat_prob, - Some(dissat_prob.unwrap_or(0 as f64) + lw * sat_prob), + Some(dissat_prob.unwrap_or(0.0) + lw * sat_prob), )?; let mut a2 = best_compilations(policy_cache, &x[0], rw * sat_prob, None)?; @@ -916,7 +920,7 @@ where policy_cache, &x[1], rw * sat_prob, - Some(dissat_prob.unwrap_or(0 as f64) + lw * sat_prob), + Some(dissat_prob.unwrap_or(0.0) + lw * sat_prob), )?; let mut b2 = best_compilations(policy_cache, &x[1], rw * sat_prob, None)?; @@ -928,12 +932,12 @@ where }; let dissat_probs = |w: f64| -> Vec> { - let mut dissat_set = Vec::new(); - dissat_set.push(Some(dissat_prob.unwrap_or(0 as f64) + w * sat_prob)); - dissat_set.push(Some(w * sat_prob)); - dissat_set.push(dissat_prob); - dissat_set.push(None); - dissat_set + vec![ + Some(dissat_prob.unwrap_or(0.0) + w * sat_prob), + Some(w * sat_prob), + dissat_prob, + None, + ] }; let mut l_comp = vec![]; @@ -975,11 +979,11 @@ where let mut best_es = Vec::with_capacity(n); let mut best_ws = Vec::with_capacity(n); - let mut min_value = (0 as usize, f64::INFINITY as f64); + let mut min_value = (0, f64::INFINITY); for (i, ast) in subs.iter().enumerate() { let sp = sat_prob * k_over_n; //Expressions must be dissatisfiable - let dp = Some(dissat_prob.unwrap_or(0 as f64) + (1.0 - k_over_n) * sat_prob); + let dp = Some(dissat_prob.unwrap_or(0.0) + (1.0 - k_over_n) * sat_prob); let be = best(types::Base::B, policy_cache, ast, sp, dp)?; let bw = best(types::Base::W, policy_cache, ast, sp, dp)?; @@ -1048,7 +1052,7 @@ where for k in ret.keys() { debug_assert_eq!(k.dissat_prob, ord_dissat_prob); } - if ret.len() == 0 { + if ret.is_empty() { // The only reason we are discarding elements out of compiler is because // compilations exceeded consensus and standardness limits or are non-malleable. // If there no possible compilations for any policies regardless of dissat @@ -1065,6 +1069,7 @@ where /// Helper function to compile different types of binary fragments. /// `sat_prob` and `dissat_prob` represent the sat and dissat probabilities of /// root or. `weights` represent the odds for taking each sub branch +#[allow(clippy::too_many_arguments)] fn compile_binary( policy_cache: &mut PolicyCache, policy: &Concrete, @@ -1099,6 +1104,7 @@ where /// Helper function to compile different order of and_or fragments. /// `sat_prob` and `dissat_prob` represent the sat and dissat probabilities of /// root and_or node. `weights` represent the odds for taking each sub branch +#[allow(clippy::too_many_arguments)] fn compile_tern( policy_cache: &mut PolicyCache, policy: &Concrete, @@ -1158,8 +1164,7 @@ where best_compilations(policy_cache, policy, sat_prob, dissat_prob)? .into_iter() .filter(|&(key, _)| { - key.ty.corr.base == types::Base::B - && key.dissat_prob == dissat_prob.and_then(|x| Some(OrdF64(x))) + key.ty.corr.base == types::Base::B && key.dissat_prob == dissat_prob.map(OrdF64) }) .map(|(_, val)| val) .min_by_key(|ext| OrdF64(ext.cost_1d(sat_prob, dissat_prob))) @@ -1180,11 +1185,11 @@ where { best_compilations(policy_cache, policy, sat_prob, dissat_prob)? .into_iter() - .filter(|&(ref key, ref val)| { + .filter(|(key, val)| { key.ty.corr.base == basic_type && key.ty.corr.unit && val.ms.ty.mall.dissat == types::Dissat::Unique - && key.dissat_prob == dissat_prob.and_then(|x| Some(OrdF64(x))) + && key.dissat_prob == dissat_prob.map(OrdF64) }) .map(|(_, val)| val) .min_by_key(|ext| OrdF64(ext.cost_1d(sat_prob, dissat_prob))) @@ -1251,7 +1256,7 @@ mod tests { // artificially create a policy that is problematic and try to compile let pol: SPolicy = Concrete::And(vec![ Concrete::Key("A".to_string()), - Concrete::And(vec![Concrete::after(9), Concrete::after(1000_000_000)]), + Concrete::And(vec![Concrete::after(9), Concrete::after(1_000_000_000)]), ]); assert!(pol.compile::().is_err()); @@ -1323,7 +1328,7 @@ mod tests { let (keys, sig) = pubkeys_and_a_sig(10); let key_pol: Vec = keys.iter().map(|k| Concrete::Key(*k)).collect(); - let policy: BPolicy = Concrete::Key(keys[0].clone()); + let policy: BPolicy = Concrete::Key(keys[0]); let ms: SegwitMiniScript = policy.compile().unwrap(); assert_eq!( ms.encode(), @@ -1407,14 +1412,11 @@ mod tests { let mut right_sat = HashMap::::new(); - for i in 0..5 { - left_sat.insert(keys[i], elements_sig); + for key in keys.iter().take(5) { + left_sat.insert(*key, elements_sig); } - for i in 5..8 { - right_sat.insert( - keys[i].to_pubkeyhash(SigType::Ecdsa), - (keys[i], elements_sig), - ); + for key in keys.iter().skip(5) { + right_sat.insert(key.to_pubkeyhash(SigType::Ecdsa), (*key, elements_sig)); } assert!(ms.satisfy(no_sat).is_err()); @@ -1523,7 +1525,7 @@ mod tests { (1, Concrete::Threshold(keys_b.len(), keys_b)), ]) .compile(); - let script_size = thresh_res.clone().and_then(|m| Ok(m.script_size())); + let script_size = thresh_res.clone().map(|m| m.script_size()); assert_eq!( thresh_res, Err(CompilerError::LimitsExceeded), @@ -1539,7 +1541,7 @@ mod tests { Concrete::Threshold(keys.len(), keys).compile(); let n_elements = thresh_res .clone() - .and_then(|m| Ok(m.max_satisfaction_witness_elements())); + .map(|m| m.max_satisfaction_witness_elements()); assert_eq!( thresh_res, Err(CompilerError::LimitsExceeded), @@ -1556,7 +1558,7 @@ mod tests { keys.iter().map(|pubkey| Concrete::Key(*pubkey)).collect(); let thresh_res: Result = Concrete::Threshold(keys.len() - 1, keys).compile(); - let ops_count = thresh_res.clone().and_then(|m| Ok(m.ext.ops.op_count())); + let ops_count = thresh_res.clone().map(|m| m.ext.ops.op_count()); assert_eq!( thresh_res, Err(CompilerError::LimitsExceeded), @@ -1568,7 +1570,7 @@ mod tests { let keys: Vec> = keys.iter().map(|pubkey| Concrete::Key(*pubkey)).collect(); let thresh_res = Concrete::Threshold(keys.len() - 1, keys).compile::(); - let ops_count = thresh_res.clone().and_then(|m| Ok(m.ext.ops.op_count())); + let ops_count = thresh_res.clone().map(|m| m.ext.ops.op_count()); assert_eq!( thresh_res, Err(CompilerError::LimitsExceeded), @@ -1608,7 +1610,7 @@ mod tests { } } -#[cfg(all(test, feature = "unstable"))] +#[cfg(miniscript_bench)] mod benches { use std::str::FromStr; diff --git a/src/policy/concrete.rs b/src/policy/concrete.rs index a3113d5e..d696b53a 100644 --- a/src/policy/concrete.rs +++ b/src/policy/concrete.rs @@ -7,7 +7,7 @@ use std::collections::HashSet; use std::{error, fmt, str}; -use elements::{LockTime, PackedLockTime, Sequence}; +use elements::{LockTime, Sequence}; #[cfg(feature = "compiler")] use { crate::descriptor::TapTree, @@ -29,7 +29,7 @@ use crate::expression::{self, FromTree}; use crate::miniscript::types::extra_props::TimelockInfo; #[cfg(all(doc, not(feature = "compiler")))] use crate::Descriptor; -use crate::{errstr, Error, ForEachKey, MiniscriptKey, Translator}; +use crate::{errstr, AbsLockTime, Error, ForEachKey, MiniscriptKey, Translator}; /// Maximum TapLeafs allowed in a compiled TapTree #[cfg(feature = "compiler")] @@ -47,7 +47,7 @@ pub enum Policy { /// A public key which must sign to satisfy the descriptor Key(Pk), /// An absolute locktime restriction - After(PackedLockTime), + After(AbsLockTime), /// A relative locktime restriction Older(Sequence), /// A SHA256 whose preimage must be provided to satisfy the descriptor @@ -72,9 +72,9 @@ where Pk: MiniscriptKey, { /// Construct a `Policy::After` from `n`. Helper function equivalent to - /// `Policy::After(PackedLockTime::from(LockTime::from_consensus(n)))`. + /// `Policy::After(LockTime::from(LockTime::from_consensus(n)))`. pub fn after(n: u32) -> Policy { - Policy::After(PackedLockTime::from(LockTime::from_consensus(n))) + Policy::After(AbsLockTime::from(LockTime::from_consensus(n))) } /// Construct a `Policy::Older` from `n`. Helper function equivalent to @@ -124,7 +124,7 @@ impl From> for Policy { PolicyArc::Unsatisfiable => Policy::Unsatisfiable, PolicyArc::Trivial => Policy::Trivial, PolicyArc::Key(pk) => Policy::Key(pk), - PolicyArc::After(t) => Policy::After(PackedLockTime::from(LockTime::from_consensus(t))), + PolicyArc::After(t) => Policy::After(AbsLockTime::from(LockTime::from_consensus(t))), PolicyArc::Older(t) => Policy::Older(Sequence::from_consensus(t)), PolicyArc::Sha256(hash) => Policy::Sha256(hash), PolicyArc::Hash256(hash) => Policy::Hash256(hash), @@ -157,7 +157,7 @@ impl From> for PolicyArc { Policy::Unsatisfiable => PolicyArc::Unsatisfiable, Policy::Trivial => PolicyArc::Trivial, Policy::Key(pk) => PolicyArc::Key(pk), - Policy::After(PackedLockTime(t)) => PolicyArc::After(t), + Policy::After(t) => PolicyArc::After(t.to_consensus_u32()), Policy::Older(Sequence(t)) => PolicyArc::Older(t), Policy::Sha256(hash) => PolicyArc::Sha256(hash), Policy::Hash256(hash) => PolicyArc::Hash256(hash), @@ -305,17 +305,15 @@ impl Policy { Policy::Or(ref subs) => { let total_odds: usize = subs.iter().map(|(ref k, _)| k).sum(); subs.iter() - .map(|(k, ref policy)| { + .flat_map(|(k, ref policy)| { policy.to_tapleaf_prob_vec(prob * *k as f64 / total_odds as f64) }) - .flatten() .collect::>() } Policy::Threshold(k, ref subs) if *k == 1 => { let total_odds = subs.len(); subs.iter() - .map(|policy| policy.to_tapleaf_prob_vec(prob / total_odds as f64)) - .flatten() + .flat_map(|policy| policy.to_tapleaf_prob_vec(prob / total_odds as f64)) .collect::>() } x => vec![(prob, x.clone())], @@ -333,10 +331,7 @@ impl Policy { let key_prob_map: HashMap<_, _> = self .to_tapleaf_prob_vec(1.0) .into_iter() - .filter(|(_, ref pol)| match *pol { - Concrete::Key(..) => true, - _ => false, - }) + .filter(|(_, pol)| matches!(*pol, Concrete::Key(..))) .map(|(prob, key)| (key, prob)) .collect(); @@ -359,7 +354,7 @@ impl Policy { } } match (internal_key, unspendable_key) { - (Some(ref key), _) => Ok((key.clone(), self.translate_unsatisfiable_pk(&key))), + (Some(ref key), _) => Ok((key.clone(), self.translate_unsatisfiable_pk(key))), (_, Some(key)) => Ok((key, self)), _ => Err(errstr("No viable internal key found.")), } @@ -763,7 +758,7 @@ impl Policy { )), Policy::Or(ref subs) => Ok(Policy::Or( subs.iter() - .map(|&(ref prob, ref sub)| Ok((*prob, sub._translate_pk(t)?))) + .map(|(prob, sub)| Ok((*prob, sub._translate_pk(t)?))) .collect::)>, E>>()?, )), } @@ -795,7 +790,7 @@ impl Policy { /// Get all keys in the policy pub fn keys(&self) -> Vec<&Pk> { - match &*self { + match self { Policy::Key(ref pk) => vec![pk], Policy::Threshold(_k, ref subs) => { subs.iter().flat_map(|sub| sub.keys()).collect::>() @@ -893,9 +888,7 @@ impl Policy { TimelockInfo::combine_threshold(subs.len(), iter) } Policy::Or(ref subs) => { - let iter = subs - .iter() - .map(|&(ref _p, ref sub)| sub.check_timelocks_helper()); + let iter = subs.iter().map(|(_p, sub)| sub.check_timelocks_helper()); TimelockInfo::combine_threshold(1, iter) } } @@ -924,7 +917,7 @@ impl Policy { Err(PolicyError::NonBinaryArgOr) } else { subs.iter() - .map(|&(ref _prob, ref sub)| sub.is_valid()) + .map(|(_prob, sub)| sub.is_valid()) .collect::, PolicyError>>()?; Ok(()) } @@ -940,7 +933,7 @@ impl Policy { } } Policy::After(n) => { - if n == PackedLockTime::ZERO { + if n == LockTime::ZERO.into() { Err(PolicyError::ZeroTime) } else if n.to_u32() > 2u32.pow(31) { Err(PolicyError::TimeTooFar) @@ -1001,7 +994,7 @@ impl Policy { Policy::Or(ref subs) => { let (all_safe, atleast_one_safe, all_non_mall) = subs .iter() - .map(|&(_, ref sub)| sub.is_safe_nonmalleable()) + .map(|(_, sub)| sub.is_safe_nonmalleable()) .fold((true, false, true), |acc, x| { (acc.0 && x.0, acc.1 || x.0, acc.2 && x.1) }); diff --git a/src/policy/mod.rs b/src/policy/mod.rs index 35711c17..298b50b9 100644 --- a/src/policy/mod.rs +++ b/src/policy/mod.rs @@ -25,7 +25,7 @@ pub use self::concrete::Policy as Concrete; pub use self::semantic::Policy as Semantic; use crate::descriptor::{CovError, Descriptor}; use crate::miniscript::{Miniscript, ScriptContext}; -use crate::{BtcPolicy, Error, Extension, MiniscriptKey, Terminal}; +use crate::{AbsLockTime, BtcPolicy, Error, Extension, MiniscriptKey, Terminal}; /// Policy entailment algorithm maximum number of terminals allowed const ENTAILMENT_MAX_TERMINALS: usize = 20; @@ -213,7 +213,7 @@ impl Liftable for Concrete { } Concrete::Or(ref subs) => { let semantic_subs: Result<_, Error> = - subs.iter().map(|&(ref _p, ref sub)| sub.lift()).collect(); + subs.iter().map(|(_p, sub)| sub.lift()).collect(); Semantic::Threshold(1, semantic_subs?) } Concrete::Threshold(k, ref subs) => { @@ -237,7 +237,9 @@ impl Liftable for BtcPolicy { BtcPolicy::Hash256(ref h) => Ok(Semantic::Hash256(h.clone())), BtcPolicy::Ripemd160(ref h) => Ok(Semantic::Ripemd160(h.clone())), BtcPolicy::Hash160(ref h) => Ok(Semantic::Hash160(h.clone())), - BtcPolicy::After(n) => Ok(Semantic::After(elements::PackedLockTime(n.to_u32()))), + BtcPolicy::After(n) => Ok(Semantic::After(AbsLockTime::from_consensus( + n.to_consensus_u32(), + ))), BtcPolicy::Older(n) => Ok(Semantic::Older(Sequence(n.to_consensus_u32()))), BtcPolicy::Threshold(k, ref subs) => { let new_subs: Result>, _> = @@ -467,13 +469,13 @@ mod tests { node_policies[6] ) ); - let descriptor = policy.compile_tr(Some(unspendable_key.clone())).unwrap(); + let descriptor = policy.compile_tr(Some(unspendable_key)).unwrap(); let mut sorted_policy_prob = node_policies .iter() .zip(node_probabilities.iter()) .collect::>(); - sorted_policy_prob.sort_by(|a, b| (a.1).partial_cmp(&b.1).unwrap()); + sorted_policy_prob.sort_by(|a, b| (a.1).partial_cmp(b.1).unwrap()); let sorted_policies = sorted_policy_prob .into_iter() .map(|(x, _prob)| x) @@ -547,7 +549,7 @@ mod tests { let pol = Concrete::::from_str("thresh(3,pk(A),pk(B),pk(C),pk(D),pk(E))").unwrap(); let desc = pol - .compile_tr_private_experimental(Some(unspendable_key.clone())) + .compile_tr_private_experimental(Some(unspendable_key)) .unwrap(); let expected_desc = Descriptor::Tr( Tr::::from_str( @@ -572,7 +574,7 @@ mod tests { } } -#[cfg(all(test, feature = "compiler", feature = "unstable"))] +#[cfg(all(miniscript_bench, feature = "compiler"))] mod benches { use core::str::FromStr; diff --git a/src/policy/semantic.rs b/src/policy/semantic.rs index 8ca7405e..cd6080fe 100644 --- a/src/policy/semantic.rs +++ b/src/policy/semantic.rs @@ -6,11 +6,11 @@ use std::str::FromStr; use std::{fmt, str}; -use elements::{LockTime, PackedLockTime, Sequence}; +use elements::{LockTime, Sequence}; use super::concrete::PolicyError; use super::ENTAILMENT_MAX_TERMINALS; -use crate::{errstr, expression, Error, ForEachKey, MiniscriptKey, Translator}; +use crate::{errstr, expression, AbsLockTime, Error, ForEachKey, MiniscriptKey, Translator}; /// Abstract policy which corresponds to the semantics of a Miniscript /// and which allows complex forms of analysis, e.g. filtering and @@ -27,7 +27,7 @@ pub enum Policy { /// Signature and public key matching a given hash is required Key(Pk), /// An absolute locktime restriction - After(PackedLockTime), + After(AbsLockTime), /// A relative locktime restriction Older(Sequence), /// A SHA256 whose preimage must be provided to satisfy the descriptor @@ -47,9 +47,9 @@ where Pk: MiniscriptKey, { /// Construct a `Policy::After` from `n`. Helper function equivalent to - /// `Policy::After(PackedLockTime::from(LockTime::from_consensus(n)))`. + /// `Policy::After(AbsLockTime::from(LockTime::from_consensus(n)))`. pub fn after(n: u32) -> Policy { - Policy::After(PackedLockTime::from(LockTime::from_consensus(n))) + Policy::After(AbsLockTime::from(LockTime::from_consensus(n))) } /// Construct a `Policy::Older` from `n`. Helper function equivalent to @@ -457,10 +457,7 @@ impl Policy { /// For checking if the normalized form is trivial, the caller /// is expected to normalize the policy first. pub fn is_trivial(&self) -> bool { - match *self { - Policy::Trivial => true, - _ => false, - } + matches!(*self, Policy::Trivial) } /// Helper function to detect a false/unsatisfiable policy @@ -468,10 +465,7 @@ impl Policy { /// For checking if the normalized form is unsatisfiable, the caller /// is expected to normalize the policy first. pub fn is_unsatisfiable(&self) -> bool { - match *self { - Policy::Unsatisfiable => true, - _ => false, - } + matches!(*self, Policy::Unsatisfiable) } /// Helper function to do the recursion in `timelocks`. @@ -513,7 +507,7 @@ impl Policy { | Policy::Ripemd160(..) | Policy::Hash160(..) => vec![], Policy::Older(..) => vec![], - Policy::After(t) => vec![t.0], + Policy::After(t) => vec![t.to_u32()], Policy::Threshold(_, ref subs) => subs.iter().fold(vec![], |mut acc, x| { acc.extend(x.real_absolute_timelocks()); acc diff --git a/src/psbt/finalizer.rs b/src/psbt/finalizer.rs index 27e96d56..01866149 100644 --- a/src/psbt/finalizer.rs +++ b/src/psbt/finalizer.rs @@ -8,7 +8,8 @@ //! `https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki` //! -use bitcoin::{self, PublicKey, XOnlyPublicKey}; +use bitcoin::key::XOnlyPublicKey; +use bitcoin::{self, PublicKey}; use elements::secp256k1_zkp::{self, Secp256k1}; use elements::taproot::LeafVersion; use elements::{self, confidential, Script, Sequence, Transaction, TxOut}; @@ -402,7 +403,7 @@ fn _finalize_inp( let psbt_sat = PsbtInputSatisfier::new(psbt, index); if util::is_v1_p2tr(spk) { - let cov_sat = TxEnv::new(&extracted_tx, spent_utxos, index) + let cov_sat = TxEnv::new(extracted_tx, spent_utxos, index) .ok_or(super::Error::InputError(InputError::MissingUtxo, index))?; // Deal with tr case separately, unfortunately we cannot infer the full descriptor for Tr let wit = construct_tap_witness(spk, &(psbt_sat, cov_sat), allow_mall) @@ -534,7 +535,7 @@ pub fn finalize( // mod tests { // use super::*; // use elements::encode::{deserialize, serialize}; -// use elements::hashes::hex::FromHex; +// use elements::hex::FromHex; // #[test] // fn test_inp_finalize_520bytes() { diff --git a/src/psbt/mod.rs b/src/psbt/mod.rs index 363c6a99..b308b301 100644 --- a/src/psbt/mod.rs +++ b/src/psbt/mod.rs @@ -13,15 +13,14 @@ use std::ops::Deref; use std::{error, fmt}; use bitcoin; -use bitcoin::util::bip32; +use bitcoin::bip32; use elements::hashes::{hash160, sha256d, Hash}; use elements::pset::PartiallySignedTransaction as Psbt; use elements::secp256k1_zkp::{self as secp256k1, Secp256k1, VerifyOnly}; use elements::sighash::SigHashCache; use elements::taproot::{self, ControlBlock, LeafVersion, TapLeafHash}; use elements::{ - self, pset as psbt, EcdsaSigHashType, LockTime, PackedLockTime, SchnorrSigHashType, Script, - Sequence, + self, pset as psbt, EcdsaSigHashType, LockTime, SchnorrSigHashType, Script, Sequence, }; use crate::extensions::{CovExtArgs, CovenantExt, ParseableExt}; @@ -107,7 +106,7 @@ pub enum InputError { /// Get the secp Errors directly SecpErr(elements::secp256k1_zkp::Error), /// Key errors - KeyErr(bitcoin::util::key::Error), + KeyErr(bitcoin::key::Error), /// Error doing an interpreter-check on a finalized psbt Interpreter(interpreter::Error), /// Redeem script does not match the p2sh hash @@ -258,8 +257,8 @@ impl From for InputError { } #[doc(hidden)] -impl From for InputError { - fn from(e: bitcoin::util::key::Error) -> InputError { +impl From for InputError { + fn from(e: bitcoin::key::Error) -> InputError { InputError::KeyErr(e) } } @@ -385,13 +384,12 @@ impl<'psbt, Pk: MiniscriptKey + ToPublicKey> Satisfier for PsbtInputSatisfie return false; } - let lock_time = LockTime::from( - self.psbt - .global - .tx_data - .fallback_locktime - .unwrap_or(PackedLockTime::ZERO), - ); + let lock_time = self + .psbt + .global + .tx_data + .fallback_locktime + .unwrap_or(LockTime::ZERO); >::check_after(&lock_time, n) } @@ -431,7 +429,9 @@ impl<'psbt, Pk: MiniscriptKey + ToPublicKey> Satisfier for PsbtInputSatisfie fn lookup_hash256(&self, h: &Pk::Hash256) -> Option { self.psbt.inputs()[self.index] .hash256_preimages - .get(&sha256d::Hash::from_inner(Pk::to_hash256(h).into_inner())) // upstream psbt operates on hash256 + .get(&sha256d::Hash::from_byte_array( + Pk::to_hash256(h).to_byte_array(), + )) // upstream psbt operates on hash256 .and_then(try_vec_as_preimage32) } @@ -627,7 +627,7 @@ pub trait PsbtExt { /// * `cache`: The [`SighashCache`] for used to cache/read previously cached computations /// * `tapleaf_hash`: If the output is taproot, compute the sighash for this particular leaf. /// - /// [`SighashCache`]: bitcoin::util::sighash::SighashCache + /// [`SighashCache`]: bitcoin::sighash::SighashCache fn sighash_msg>( &self, idx: usize, @@ -1084,10 +1084,10 @@ trait PsbtFields { fn redeem_script(&mut self) -> &mut Option