Repository: rust-bitcoin/rust-bitcoin
Pull request #1092 (2022-07-10 22:59:13)

Create configuration conditional "bench"
Currently we are unable to build with all features enabled with a non-nightly toolchain, this is because of the use of

    `#![cfg_attr(all(test, feature = "unstable"), feature(test))]`

which causes the following error when building:

 error[E0554]: `#![feature]` may not be used on the stable release channel

The "unstable" feature is used to guard bench mark modules, this is widely suggested online but there is a better way.

When running the bench marks use the following incantation:

    `RUSTFLAGS='--cfg=bench' cargo bench`

This creates a configuration conditional "bench" that can be used to guard the bench mark modules.

```
#[cfg(bench)]
mod benches {
    ...
}
```


Kixunil: Perhaps `#[cfg(bench)] extern crate test;`?
apoelstra: Very nice! Would be nice to also mention this in the README -- and we should move all the crates in the ecosystem over to this scheme.
tcharding: Changes in force push:
- Made `test.sh` comment be: `# Bench if told to, only works with non-stable toolchain (nightly, beta).`
tcharding: > Perhaps #[cfg(bench)] extern crate test;?

Forgot to say the other day, you were right @Kixunil, thanks man!

tcharding: Woops, readme suggestion still to come ...

Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 72cc0852..3213e694 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,7 +15,6 @@ edition = "2018"
 # Please don't forget to add relevant features to docs.rs below
 [features]
 default = [ "std", "secp-recovery" ]
-unstable = []
 rand = ["secp256k1/rand-std"]
 serde = ["actual-serde", "bitcoin_hashes/serde", "secp256k1/serde"]
 secp-lowmemory = ["secp256k1/lowmemory"]
@@ -35,8 +34,8 @@ rustdoc-args = ["--cfg", "docsrs"]
 
 [dependencies]
 bech32 = { version = "0.8.1", default-features = false }
-bitcoin_hashes = { version = "0.11.0", default-features = false }
-secp256k1 = { version = "0.23.0", default-features = false }
+bitcoin_hashes = { version = "0.10.0", default-features = false }
+secp256k1 = { version = "0.22.0", default-features = false }
 core2 = { version = "0.3.0", optional = true, default-features = false }
 
 base64 = { version = "0.13.0", optional = true }
@@ -48,7 +47,7 @@ hashbrown = { version = "0.8", optional = true }
 [dev-dependencies]
 serde_json = "<1.0.45"
 serde_test = "1"
-secp256k1 = { version = "0.23.0", features = [ "recovery", "rand-std" ] }
+secp256k1 = { version = "0.22.0", features = [ "recovery", "rand-std" ] }
 bincode = "1.3.1"
 
 [[example]]
diff --git a/README.md b/README.md
index 6ae301cb..316e467c 100644
--- a/README.md
+++ b/README.md
@@ -106,7 +106,14 @@ Please refer to the [`cargo` documentation](https://doc.rust-lang.org/stable/car
 We build docs with the nightly toolchain, you may wish to use the following
 shell alias to check your documentation changes build correctly.
 
-```alias build-docs='RUSTDOCFLAGS="--cfg docsrs" cargo +nightly rustdoc --features="$FEATURES" -- -D rustdoc::broken-intra-doc-links'```
+```
+alias build-docs='RUSTDOCFLAGS="--cfg docsrs" cargo +nightly rustdoc --features="$FEATURES" -- -D rustdoc::broken-intra-doc-links'
+```
+
+### Running benchmarks
+
+We use a custom Rust compiler configuration conditional to guard the bench mark code. To run the
+bench marks use: `RUSTFLAGS='--cfg=bench' cargo +nightly bench`.
 
 ## Pull Requests
 
diff --git a/src/blockdata/block.rs b/src/blockdata/block.rs
index 98b7fb52..0fd6155c 100644
--- a/src/blockdata/block.rs
+++ b/src/blockdata/block.rs
@@ -254,7 +254,7 @@ pub fn witness_root(&self) -> Option<WitnessMerkleNode> {
         let hashes = self.txdata.iter().enumerate().map(|(i, t)| {
             if i == 0 {
                 // Replace the first hash with zeroes.
-                Wtxid::all_zeros().as_hash()
+                Wtxid::default().as_hash()
             } else {
                 t.wtxid().as_hash()
             }
@@ -528,7 +528,7 @@ fn compact_roundrtip_test() {
     }
 }
 
-#[cfg(all(test, feature = "unstable"))]
+#[cfg(bench)]
 mod benches {
     use super::Block;
     use crate::EmptyWrite;
diff --git a/src/blockdata/constants.rs b/src/blockdata/constants.rs
index cb91160b..ea392a78 100644
--- a/src/blockdata/constants.rs
+++ b/src/blockdata/constants.rs
@@ -13,7 +13,7 @@
 use core::default::Default;
 
 use crate::hashes::hex::{self, HexIterator};
-use crate::hashes::{Hash, sha256d};
+use crate::hashes::sha256d;
 use crate::blockdata::opcodes;
 use crate::blockdata::script;
 use crate::blockdata::transaction::{OutPoint, Transaction, TxOut, TxIn};
@@ -114,7 +114,7 @@ pub fn genesis_block(network: Network) -> Block {
             Block {
                 header: BlockHeader {
                     version: 1,
-                    prev_blockhash: Hash::all_zeros(),
+                    prev_blockhash: Default::default(),
                     merkle_root,
                     time: 1231006505,
                     bits: 0x1d00ffff,
@@ -127,7 +127,7 @@ pub fn genesis_block(network: Network) -> Block {
             Block {
                 header: BlockHeader {
                     version: 1,
-                    prev_blockhash: Hash::all_zeros(),
+                    prev_blockhash: Default::default(),
                     merkle_root,
                     time: 1296688602,
                     bits: 0x1d00ffff,
@@ -140,7 +140,7 @@ pub fn genesis_block(network: Network) -> Block {
             Block {
                 header: BlockHeader {
                     version: 1,
-                    prev_blockhash: Hash::all_zeros(),
+                    prev_blockhash: Default::default(),
                     merkle_root,
                     time: 1598918400,
                     bits: 0x1e0377ae,
@@ -153,7 +153,7 @@ pub fn genesis_block(network: Network) -> Block {
             Block {
                 header: BlockHeader {
                     version: 1,
-                    prev_blockhash: Hash::all_zeros(),
+                    prev_blockhash: Default::default(),
                     merkle_root,
                     time: 1296688602,
                     bits: 0x207fffff,
@@ -194,6 +194,7 @@ pub fn using_genesis_block(network: Network) -> Self {
 
 #[cfg(test)]
 mod test {
+    use core::default::Default;
     use super::*;
     use crate::hashes::hex::{ToHex, FromHex};
     use crate::network::constants::Network;
@@ -205,7 +206,7 @@ fn bitcoin_genesis_first_transaction() {
 
         assert_eq!(gen.version, 1);
         assert_eq!(gen.input.len(), 1);
-        assert_eq!(gen.input[0].previous_output.txid, Hash::all_zeros());
+        assert_eq!(gen.input[0].previous_output.txid, Default::default());
         assert_eq!(gen.input[0].previous_output.vout, 0xFFFFFFFF);
         assert_eq!(serialize(&gen.input[0].script_sig),
                    Vec::from_hex("4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73").unwrap());
@@ -225,7 +226,7 @@ fn bitcoin_genesis_full_block() {
         let gen = genesis_block(Network::Bitcoin);
 
         assert_eq!(gen.header.version, 1);
-        assert_eq!(gen.header.prev_blockhash, Hash::all_zeros());
+        assert_eq!(gen.header.prev_blockhash, Default::default());
         assert_eq!(gen.header.merkle_root.to_hex(), "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b");
 
         assert_eq!(gen.header.time, 1231006505);
@@ -238,7 +239,7 @@ fn bitcoin_genesis_full_block() {
     fn testnet_genesis_full_block() {
         let gen = genesis_block(Network::Testnet);
         assert_eq!(gen.header.version, 1);
-        assert_eq!(gen.header.prev_blockhash, Hash::all_zeros());
+        assert_eq!(gen.header.prev_blockhash, Default::default());
         assert_eq!(gen.header.merkle_root.to_hex(), "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b");
         assert_eq!(gen.header.time, 1296688602);
         assert_eq!(gen.header.bits, 0x1d00ffff);
@@ -250,7 +251,7 @@ fn testnet_genesis_full_block() {
     fn signet_genesis_full_block() {
         let gen = genesis_block(Network::Signet);
         assert_eq!(gen.header.version, 1);
-        assert_eq!(gen.header.prev_blockhash, Hash::all_zeros());
+        assert_eq!(gen.header.prev_blockhash, Default::default());
         assert_eq!(gen.header.merkle_root.to_hex(), "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b");
         assert_eq!(gen.header.time, 1598918400);
         assert_eq!(gen.header.bits, 0x1e0377ae);
@@ -261,7 +262,7 @@ fn signet_genesis_full_block() {
     // The *_chain_hash tests are sanity/regression tests, they verify that the const byte array
     // representing the genesis block is the same as that created by hashing the genesis block.
     fn chain_hash_and_genesis_block(network: Network) {
-        use crate::hashes::sha256;
+        use hashes::{sha256, Hash};
 
         // The genesis block hash is a double-sha256 and it is displayed backwards.
         let genesis_hash = genesis_block(network).block_hash();
diff --git a/src/blockdata/transaction.rs b/src/blockdata/transaction.rs
index c92b73c3..271ce69d 100644
--- a/src/blockdata/transaction.rs
+++ b/src/blockdata/transaction.rs
@@ -60,7 +60,7 @@ pub fn new(txid: Txid, vout: u32) -> OutPoint {
     #[inline]
     pub fn null() -> OutPoint {
         OutPoint {
-            txid: Hash::all_zeros(),
+            txid: Default::default(),
             vout: u32::max_value(),
         }
     }
@@ -1724,7 +1724,7 @@ fn test_transaction_verify () {
     }
 }
 
-#[cfg(all(test, feature = "unstable"))]
+#[cfg(bench)]
 mod benches {
     use super::Transaction;
     use crate::EmptyWrite;
diff --git a/src/blockdata/witness.rs b/src/blockdata/witness.rs
index d2733c90..9b6da04a 100644
--- a/src/blockdata/witness.rs
+++ b/src/blockdata/witness.rs
@@ -441,7 +441,7 @@ fn test_serde() {
 }
 
 
-#[cfg(all(test, feature = "unstable"))]
+#[cfg(bench)]
 mod benches {
     use test::{Bencher, black_box};
     use super::Witness;
diff --git a/src/consensus/encode.rs b/src/consensus/encode.rs
index 2bd87848..70992d1b 100644
--- a/src/consensus/encode.rs
+++ b/src/consensus/encode.rs
@@ -1115,7 +1115,7 @@ macro_rules! round_trip_bytes {
 
             data.clear();
             data64.clear();
-            let len = thread_rng().gen_range(1..256);
+            let len = thread_rng().gen_range(1, 256);
             data.resize(len, 0u8);
             data64.resize(len, 0u64);
             let mut arr33 = [0u8; 33];
diff --git a/src/hash_types.rs b/src/hash_types.rs
index 3215bd8f..7e9a3822 100644
--- a/src/hash_types.rs
+++ b/src/hash_types.rs
@@ -9,7 +9,7 @@
 //! hash).
 //!
 
-use bitcoin_hashes::{sha256, sha256d, hash160, hash_newtype};
+use crate::hashes::{Hash, sha256, sha256d, hash160, hash_newtype};
 
 macro_rules! impl_hashencode {
     ($hashtype:ident) => {
diff --git a/src/lib.rs b/src/lib.rs
index 51fd3a28..f32839f5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -19,7 +19,6 @@
 //! * `std` - the usual dependency on `std` (default).
 //! * `secp-recovery` - enables calculating public key from a signature and message.
 //! * `base64` - (dependency), enables encoding of PSBTs and message signatures.
-//! * `unstable` - enables unstable features for testing.
 //! * `rand` - (dependency), makes it more convenient to generate random values.
 //! * `serde` - (dependency), implements `serde`-based serialization and
 //!                 deserialization.
@@ -31,9 +30,8 @@
 
 #![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
 
-// Experimental features we need
-#![cfg_attr(all(test, feature = "unstable"), feature(test))]
-
+// Experimental features we need.
+#![cfg_attr(bench, feature(test))]
 #![cfg_attr(docsrs, feature(doc_cfg))]
 
 // Coding conventions
@@ -56,12 +54,17 @@
                 than 16 bits, let us know if you want 16-bit support. Note that we do
                 NOT guarantee that we will implement it!");
 
+#[cfg(bench)] extern crate test;
+
 #[cfg(feature = "no-std")]
 #[macro_use]
 extern crate alloc;
 #[cfg(feature = "no-std")]
 extern crate core2;
 
+#[cfg(any(feature = "std", test))]
+extern crate core; // for Rust 1.29 and no-std tests
+
 // Re-exported dependencies.
 #[macro_use] pub extern crate bitcoin_hashes as hashes;
 pub extern crate secp256k1;
@@ -181,10 +184,10 @@ mod prelude {
     pub use std::collections::HashSet;
 }
 
-#[cfg(all(test, feature = "unstable"))] use tests::EmptyWrite;
+#[cfg(bench)] use bench::EmptyWrite;
 
-#[cfg(all(test, feature = "unstable"))]
-mod tests {
+#[cfg(bench)]
+mod bench {
     use core::fmt::Arguments;
     use crate::io::{IoSlice, Result, Write};
 
diff --git a/src/network/message_blockdata.rs b/src/network/message_blockdata.rs
index 3268f823..eebd9361 100644
--- a/src/network/message_blockdata.rs
+++ b/src/network/message_blockdata.rs
@@ -11,7 +11,7 @@
 
 use crate::io;
 
-use crate::hashes::{Hash as _, sha256d};
+use crate::hashes::sha256d;
 
 use crate::network::constants;
 use crate::consensus::encode::{self, Decodable, Encodable};
@@ -50,7 +50,7 @@ macro_rules! encode_inv {
             }
         }
         Ok(match *self {
-            Inventory::Error => encode_inv!(0, sha256d::Hash::all_zeros()),
+            Inventory::Error => encode_inv!(0, sha256d::Hash::default()),
             Inventory::Transaction(ref t) => encode_inv!(1, t),
             Inventory::Block(ref b) => encode_inv!(2, b),
             Inventory::WTx(w) => encode_inv!(5, w),
@@ -139,8 +139,9 @@ mod tests {
     use super::{Vec, GetHeadersMessage, GetBlocksMessage};
 
     use crate::hashes::hex::FromHex;
-    use crate::hashes::Hash;
+
     use crate::consensus::encode::{deserialize, serialize};
+    use core::default::Default;
 
     #[test]
     fn getblocks_message_test() {
@@ -153,7 +154,7 @@ fn getblocks_message_test() {
         assert_eq!(real_decode.version, 70002);
         assert_eq!(real_decode.locator_hashes.len(), 1);
         assert_eq!(serialize(&real_decode.locator_hashes[0]), genhash);
-        assert_eq!(real_decode.stop_hash, Hash::all_zeros());
+        assert_eq!(real_decode.stop_hash, Default::default());
 
         assert_eq!(serialize(&real_decode), from_sat);
     }
@@ -169,7 +170,7 @@ fn getheaders_message_test() {
         assert_eq!(real_decode.version, 70002);
         assert_eq!(real_decode.locator_hashes.len(), 1);
         assert_eq!(serialize(&real_decode.locator_hashes[0]), genhash);
-        assert_eq!(real_decode.stop_hash, Hash::all_zeros());
+        assert_eq!(real_decode.stop_hash, Default::default());
 
         assert_eq!(serialize(&real_decode), from_sat);
     }
diff --git a/src/util/address.rs b/src/util/address.rs
index 6b3d5409..c6b24eab 100644
--- a/src/util/address.rs
+++ b/src/util/address.rs
@@ -75,8 +75,6 @@ pub enum Error {
     ExcessiveScriptSize,
     /// Script is not a p2pkh, p2sh or witness program.
     UnrecognizedScript,
-    /// Address type is either invalid or not supported in rust-bitcoin.
-    UnknownAddressType(String),
 }
 
 impl fmt::Display for Error {
@@ -93,8 +91,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
             Error::InvalidSegwitV0ProgramLength(l) => write!(f, "a v0 witness program must be either of length 20 or 32 bytes: length={}", l),
             Error::UncompressedPubkey => write!(f, "an uncompressed pubkey was used where it is not allowed"),
             Error::ExcessiveScriptSize => write!(f, "script size exceed 520 bytes"),
-            Error::UnrecognizedScript => write!(f, "script is not a p2pkh, p2sh or witness program"),
-            Error::UnknownAddressType(ref s) => write!(f, "unknown address type: '{}' is either invalid or not supported in rust-bitcoin", s),
+            Error::UnrecognizedScript => write!(f, "script is not a p2pkh, p2sh or witness program")
         }
     }
 }
@@ -117,8 +114,7 @@ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
             | InvalidSegwitV0ProgramLength(_)
             | UncompressedPubkey
             | ExcessiveScriptSize
-            | UnrecognizedScript
-            | UnknownAddressType(_) => None,
+            | UnrecognizedScript => None,
         }
     }
 }
@@ -166,7 +162,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 }
 
 impl FromStr for AddressType {
-    type Err = Error;
+    type Err = ();
     fn from_str(s: &str) -> Result<Self, Self::Err> {
         match s {
             "p2pkh" => Ok(AddressType::P2pkh),
@@ -174,7 +170,7 @@ fn from_str(s: &str) -> Result<Self, Self::Err> {
             "p2wpkh" => Ok(AddressType::P2wpkh),
             "p2wsh" => Ok(AddressType::P2wsh),
             "p2tr" => Ok(AddressType::P2tr),
-            _ => Err(Error::UnknownAddressType(s.to_owned())),
+            _ => Err(()),
         }
     }
 }
@@ -1339,8 +1335,8 @@ fn test_qr_string() {
     #[test]
     fn test_valid_networks() {
         let legacy_payload = &[
-            Payload::PubkeyHash(PubkeyHash::all_zeros()),
-            Payload::ScriptHash(ScriptHash::all_zeros())
+            Payload::PubkeyHash(PubkeyHash::default()),
+            Payload::ScriptHash(ScriptHash::default())
         ];
         let segwit_payload = (0..=16).map(|version| {
             Payload::WitnessProgram {
@@ -1504,17 +1500,4 @@ fn test_fail_address_from_script() {
         assert_eq!(Address::from_script(&bad_p2wsh, Network::Bitcoin), expected);
         assert_eq!(Address::from_script(&invalid_segwitv0_script, Network::Bitcoin), Err(Error::InvalidSegwitV0ProgramLength(17)));
     }
-
-    #[test]
-    fn valid_address_parses_correctly() {
-        let addr = AddressType::from_str("p2tr").expect("false negative while parsing address");
-        assert_eq!(addr, AddressType::P2tr);
-    }
-
-    #[test]
-    fn invalid_address_parses_error() {
-        let got = AddressType::from_str("invalid");
-        let want = Err(Error::UnknownAddressType("invalid".to_string()));
-        assert_eq!(got, want);
-    }
 }
diff --git a/src/util/bip32.rs b/src/util/bip32.rs
index 5a380265..6a4f4dfa 100644
--- a/src/util/bip32.rs
+++ b/src/util/bip32.rs
@@ -590,15 +590,15 @@ pub fn ckd_priv<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>, i: ChildNumbe
 
         hmac_engine.input(&endian::u32_to_array_be(u32::from(i)));
         let hmac_result: Hmac<sha512::Hash> = Hmac::from_engine(hmac_engine);
-        let sk = secp256k1::SecretKey::from_slice(&hmac_result[..32]).expect("statistically impossible to hit");
-        let tweaked = sk.add_tweak(&self.private_key.into()).expect("statistically impossible to hit");
+        let mut sk = secp256k1::SecretKey::from_slice(&hmac_result[..32])?;
+        sk.add_assign(&self.private_key[..])?;
 
         Ok(ExtendedPrivKey {
             network: self.network,
             depth: self.depth + 1,
             parent_fingerprint: self.fingerprint(secp),
             child_number: i,
-            private_key: tweaked,
+            private_key: sk,
             chain_code: ChainCode::from(&hmac_result[32..])
         })
     }
@@ -731,14 +731,15 @@ pub fn ckd_pub<C: secp256k1::Verification>(
         i: ChildNumber,
     ) -> Result<ExtendedPubKey, Error> {
         let (sk, chain_code) = self.ckd_pub_tweak(i)?;
-        let tweaked = self.public_key.add_exp_tweak(secp, &sk.into())?;
+        let mut pk = self.public_key;
+        pk.add_exp_assign(secp, &sk[..])?;
 
         Ok(ExtendedPubKey {
             network: self.network,
             depth: self.depth + 1,
             parent_fingerprint: self.fingerprint(),
             child_number: i,
-            public_key: tweaked,
+            public_key: pk,
             chain_code,
         })
     }
diff --git a/src/util/hash.rs b/src/util/hash.rs
index 3f492e4d..08e40e90 100644
--- a/src/util/hash.rs
+++ b/src/util/hash.rs
@@ -114,7 +114,7 @@ fn both_merkle_root_functions_return_the_same_result() {
 
         let hashes_iter = block.txdata.iter().map(|obj| obj.txid().as_hash());
 
-        let mut hashes_array: [sha256d::Hash; 15] = [Hash::all_zeros(); 15];
+        let mut hashes_array: [sha256d::Hash; 15] = [Default::default(); 15];
         for (i, hash) in hashes_iter.clone().enumerate() {
             hashes_array[i] = hash;
         }
diff --git a/src/util/key.rs b/src/util/key.rs
index ebeffb25..8ebba6ea 100644
--- a/src/util/key.rs
+++ b/src/util/key.rs
@@ -172,72 +172,6 @@ pub fn to_bytes(&self) -> Vec<u8> {
         buf
     }
 
-    /// Serialize the public key into a `SortKey`.
-    ///
-    /// `SortKey` is not too useful by itself, but it can be used to sort a
-    /// `[PublicKey]` slice using `sort_unstable_by_key`, `sort_by_cached_key`,
-    /// `sort_by_key`, or any of the other `*_by_key` methods on slice.
-    /// Pass the method into the sort method directly. (ie. `PublicKey::to_sort_key`)
-    ///
-    /// This method of sorting is in line with Bitcoin Core's implementation of
-    /// sorting keys for output descriptors such as `sortedmulti()`.
-    ///
-    /// If every `PublicKey` in the slice is `compressed == true` then this will sort
-    /// the keys in a
-    /// [BIP67](https://github.com/bitcoin/bips/blob/master/bip-0067.mediawiki)
-    /// compliant way.
-    ///
-    /// # Example: Using with `sort_unstable_by_key`
-    ///
-    /// ```rust
-    /// use std::str::FromStr;
-    /// use bitcoin::PublicKey;
-    ///
-    /// let pk = |s| PublicKey::from_str(s).unwrap();
-    ///
-    /// let mut unsorted = [
-    ///     pk("04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc38e98ac269ffe028345c31ac8d0a365f29c8f7e7cfccac72f84e1acd02bc554f35"),
-    ///     pk("038f47dcd43ba6d97fc9ed2e3bba09b175a45fac55f0683e8cf771e8ced4572354"),
-    ///     pk("028bde91b10013e08949a318018fedbd896534a549a278e220169ee2a36517c7aa"),
-    ///     pk("04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc3816753d96001fd7cba3ce5372f5c9a0d63708183033538d07b1e532fc43aaacfa"),
-    ///     pk("032b8324c93575034047a52e9bca05a46d8347046b91a032eff07d5de8d3f2730b"),
-    ///     pk("045d753414fa292ea5b8f56e39cfb6a0287b2546231a5cb05c4b14ab4b463d171f5128148985b23eccb1e2905374873b1f09b9487f47afa6b1f2b0083ac8b4f7e8"),
-    ///     pk("0234dd69c56c36a41230d573d68adeae0030c9bc0bf26f24d3e1b64c604d293c68"),
-    /// ];
-    /// let sorted = [
-    ///     // These first 4 keys are in a BIP67 compatible sorted order
-    ///     // (since they are compressed)
-    ///     pk("0234dd69c56c36a41230d573d68adeae0030c9bc0bf26f24d3e1b64c604d293c68"),
-    ///     pk("028bde91b10013e08949a318018fedbd896534a549a278e220169ee2a36517c7aa"),
-    ///     pk("032b8324c93575034047a52e9bca05a46d8347046b91a032eff07d5de8d3f2730b"),
-    ///     pk("038f47dcd43ba6d97fc9ed2e3bba09b175a45fac55f0683e8cf771e8ced4572354"),
-    ///     // Uncompressed keys are not BIP67 compliant, but are sorted
-    ///     // after compressed keys in Bitcoin Core using `sortedmulti()`
-    ///     pk("045d753414fa292ea5b8f56e39cfb6a0287b2546231a5cb05c4b14ab4b463d171f5128148985b23eccb1e2905374873b1f09b9487f47afa6b1f2b0083ac8b4f7e8"),
-    ///     pk("04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc3816753d96001fd7cba3ce5372f5c9a0d63708183033538d07b1e532fc43aaacfa"),
-    ///     pk("04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc38e98ac269ffe028345c31ac8d0a365f29c8f7e7cfccac72f84e1acd02bc554f35"),
-    /// ];
-    ///
-    /// unsorted.sort_unstable_by_key(PublicKey::to_sort_key);
-    ///
-    /// assert_eq!(unsorted, sorted);
-    /// ```
-    pub fn to_sort_key(&self) -> SortKey {
-        if self.compressed {
-            let bytes = self.inner.serialize();
-            let mut res = [0; 32];
-            res[..].copy_from_slice(&bytes[1..33]);
-            SortKey(bytes[0], res, [0; 32])
-        } else {
-            let bytes = self.inner.serialize_uncompressed();
-            let mut res_left = [0; 32];
-            let mut res_right = [0; 32];
-            res_left[..].copy_from_slice(&bytes[1..33]);
-            res_right[..].copy_from_slice(&bytes[33..65]);
-            SortKey(bytes[0], res_left, res_right)
-        }
-    }
-
     /// Deserialize a public key from a slice
     pub fn from_slice(data: &[u8]) -> Result<PublicKey, Error> {
         let compressed = match data.len() {
@@ -264,10 +198,6 @@ pub fn from_private_key<C: secp256k1::Signing>(secp: &Secp256k1<C>, sk: &Private
     }
 }
 
-/// An opaque return type for PublicKey::to_sort_key
-#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
-pub struct SortKey(u8, [u8; 32], [u8; 32]);
-
 impl fmt::Display for PublicKey {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         if self.compressed {
@@ -545,10 +475,10 @@ fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
 #[cfg(test)]
 mod tests {
     use crate::io;
-    use super::{PrivateKey, PublicKey, SortKey};
+    use super::{PrivateKey, PublicKey};
     use secp256k1::Secp256k1;
     use std::str::FromStr;
-    use crate::hashes::hex::{FromHex, ToHex};
+    use crate::hashes::hex::ToHex;
     use crate::network::constants::Network::Testnet;
     use crate::network::constants::Network::Bitcoin;
     use crate::util::address::Address;
@@ -699,141 +629,4 @@ fn pubkey_read_write() {
         assert!(PublicKey::read_from(io::Cursor::new(&[0; 65][..])).is_err());
         assert!(PublicKey::read_from(io::Cursor::new(&[4; 64][..])).is_err());
     }
-
-    #[test]
-    fn pubkey_to_sort_key() {
-        let key1 = PublicKey::from_str("02ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8").unwrap();
-        let key2 = PublicKey {
-            inner: key1.inner,
-            compressed: false,
-        };
-        let expected1 = SortKey(
-            2,
-            <[u8; 32]>::from_hex(
-                "ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8",
-            ).unwrap(),
-            [0_u8; 32],
-        );
-        let expected2 = SortKey(
-            4,
-            <[u8; 32]>::from_hex(
-                "ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8",
-            ).unwrap(),
-            <[u8; 32]>::from_hex(
-                "1794e7f3d5e420641a3bc690067df5541470c966cbca8c694bf39aa16d836918",
-            ).unwrap(),
-        );
-        assert_eq!(key1.to_sort_key(), expected1);
-        assert_eq!(key2.to_sort_key(), expected2);
-    }
-
-    #[test]
-    fn pubkey_sort() {
-        struct Vector {
-            input: Vec<PublicKey>,
-            expect: Vec<PublicKey>,
-        }
-        let fmt = |v: Vec<_>| v.into_iter()
-            .map(|s| PublicKey::from_str(s).unwrap())
-            .collect::<Vec<_>>();
-        let vectors = vec![
-            // Start BIP67 vectors
-            // Vector 1
-            Vector {
-                input: fmt(vec![
-                    "02ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8",
-                    "02fe6f0a5a297eb38c391581c4413e084773ea23954d93f7753db7dc0adc188b2f",
-                ]),
-                expect: fmt(vec![
-                    "02fe6f0a5a297eb38c391581c4413e084773ea23954d93f7753db7dc0adc188b2f",
-                    "02ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8",
-                ]),
-            },
-            // Vector 2 (Already sorted, no action required)
-            Vector {
-                input: fmt(vec![
-                    "02632b12f4ac5b1d1b72b2a3b508c19172de44f6f46bcee50ba33f3f9291e47ed0",
-                    "027735a29bae7780a9755fae7a1c4374c656ac6a69ea9f3697fda61bb99a4f3e77",
-                    "02e2cc6bd5f45edd43bebe7cb9b675f0ce9ed3efe613b177588290ad188d11b404",
-                ]),
-                expect: fmt(vec![
-                    "02632b12f4ac5b1d1b72b2a3b508c19172de44f6f46bcee50ba33f3f9291e47ed0",
-                    "027735a29bae7780a9755fae7a1c4374c656ac6a69ea9f3697fda61bb99a4f3e77",
-                    "02e2cc6bd5f45edd43bebe7cb9b675f0ce9ed3efe613b177588290ad188d11b404",
-                ]),
-            },
-            // Vector 3
-            Vector {
-                input: fmt(vec![
-                    "030000000000000000000000000000000000004141414141414141414141414141",
-                    "020000000000000000000000000000000000004141414141414141414141414141",
-                    "020000000000000000000000000000000000004141414141414141414141414140",
-                    "030000000000000000000000000000000000004141414141414141414141414140",
-                ]),
-                expect: fmt(vec![
-                    "020000000000000000000000000000000000004141414141414141414141414140",
-                    "020000000000000000000000000000000000004141414141414141414141414141",
-                    "030000000000000000000000000000000000004141414141414141414141414140",
-                    "030000000000000000000000000000000000004141414141414141414141414141",
-                ]),
-            },
-            // Vector 4: (from bitcore)
-            Vector {
-                input: fmt(vec![
-                    "022df8750480ad5b26950b25c7ba79d3e37d75f640f8e5d9bcd5b150a0f85014da",
-                    "03e3818b65bcc73a7d64064106a859cc1a5a728c4345ff0b641209fba0d90de6e9",
-                    "021f2f6e1e50cb6a953935c3601284925decd3fd21bc445712576873fb8c6ebc18",
-                ]),
-                expect: fmt(vec![
-                    "021f2f6e1e50cb6a953935c3601284925decd3fd21bc445712576873fb8c6ebc18",
-                    "022df8750480ad5b26950b25c7ba79d3e37d75f640f8e5d9bcd5b150a0f85014da",
-                    "03e3818b65bcc73a7d64064106a859cc1a5a728c4345ff0b641209fba0d90de6e9",
-                ]),
-            },
-            // Non-BIP67 vectors
-            Vector {
-                input: fmt(vec![
-                    "02c690d642c1310f3a1ababad94e3930e4023c930ea472e7f37f660fe485263b88",
-                    "0234dd69c56c36a41230d573d68adeae0030c9bc0bf26f24d3e1b64c604d293c68",
-                    "041a181bd0e79974bd7ca552e09fc42ba9c3d5dbb3753741d6f0ab3015dbfd9a22d6b001a32f5f51ac6f2c0f35e73a6a62f59e848fa854d3d21f3f231594eeaa46",
-                    "032b8324c93575034047a52e9bca05a46d8347046b91a032eff07d5de8d3f2730b",
-                    "04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc3816753d96001fd7cba3ce5372f5c9a0d63708183033538d07b1e532fc43aaacfa",
-                    "028e1c947c8c0b8ed021088b8e981491ac7af2b8fabebea1abdb448424c8ed75b7",
-                    "045d753414fa292ea5b8f56e39cfb6a0287b2546231a5cb05c4b14ab4b463d171f5128148985b23eccb1e2905374873b1f09b9487f47afa6b1f2b0083ac8b4f7e8",
-                    "03004a8a3d242d7957c0b60fb7208d386fa6a0193aabd1f3f095ffd0ac097e447b",
-                    "04eb0db2d71ccbb0edd8fb35092cbcae2f7fa1f06d4c170804bf52007924b569a8d2d6f6bc8fd2b3caa3253fa1bb674443743bf7fb9f94f9c0b0831a252894cfa8",
-                    "04516cde23e14f2319423b7a4a7ae48b1dadceb5e9c123198d417d10895684c42eb05e210f90ccbc72448803a22312e3f122ff2939956ccef4f7316f836295ddd5",
-                    "038f47dcd43ba6d97fc9ed2e3bba09b175a45fac55f0683e8cf771e8ced4572354",
-                    "04c6bec3b07586a4b085a78cbb97e9bab6f1d3c9ebf299b65dec85213c5eacd44487de86017183120bb7ea3b6c6660c5037615fe1add2a73f800cbeeae22c60438",
-                    "03e1a1cfa9eaff604ae237b7af31ffe4c01be22eb96f3da0e62c5850dd4b4386c1",
-                    "028d3a2d9f1b1c5c75845944f93bc183ba23aecde53f1978b8aa1b77661be6114f",
-                    "028bde91b10013e08949a318018fedbd896534a549a278e220169ee2a36517c7aa",
-                    "04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc38e98ac269ffe028345c31ac8d0a365f29c8f7e7cfccac72f84e1acd02bc554f35",
-                ]),
-                expect: fmt(vec![
-                    "0234dd69c56c36a41230d573d68adeae0030c9bc0bf26f24d3e1b64c604d293c68",
-                    "028bde91b10013e08949a318018fedbd896534a549a278e220169ee2a36517c7aa",
-                    "028d3a2d9f1b1c5c75845944f93bc183ba23aecde53f1978b8aa1b77661be6114f",
-                    "028e1c947c8c0b8ed021088b8e981491ac7af2b8fabebea1abdb448424c8ed75b7",
-                    "02c690d642c1310f3a1ababad94e3930e4023c930ea472e7f37f660fe485263b88",
-                    "03004a8a3d242d7957c0b60fb7208d386fa6a0193aabd1f3f095ffd0ac097e447b",
-                    "032b8324c93575034047a52e9bca05a46d8347046b91a032eff07d5de8d3f2730b",
-                    "038f47dcd43ba6d97fc9ed2e3bba09b175a45fac55f0683e8cf771e8ced4572354",
-                    "03e1a1cfa9eaff604ae237b7af31ffe4c01be22eb96f3da0e62c5850dd4b4386c1",
-                    "041a181bd0e79974bd7ca552e09fc42ba9c3d5dbb3753741d6f0ab3015dbfd9a22d6b001a32f5f51ac6f2c0f35e73a6a62f59e848fa854d3d21f3f231594eeaa46",
-                    "04516cde23e14f2319423b7a4a7ae48b1dadceb5e9c123198d417d10895684c42eb05e210f90ccbc72448803a22312e3f122ff2939956ccef4f7316f836295ddd5",
-                    "045d753414fa292ea5b8f56e39cfb6a0287b2546231a5cb05c4b14ab4b463d171f5128148985b23eccb1e2905374873b1f09b9487f47afa6b1f2b0083ac8b4f7e8",
-                    // These two pubkeys are mirrored. This helps verify the sort past the x value.
-                    "04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc3816753d96001fd7cba3ce5372f5c9a0d63708183033538d07b1e532fc43aaacfa",
-                    "04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc38e98ac269ffe028345c31ac8d0a365f29c8f7e7cfccac72f84e1acd02bc554f35",
-                    "04c6bec3b07586a4b085a78cbb97e9bab6f1d3c9ebf299b65dec85213c5eacd44487de86017183120bb7ea3b6c6660c5037615fe1add2a73f800cbeeae22c60438",
-                    "04eb0db2d71ccbb0edd8fb35092cbcae2f7fa1f06d4c170804bf52007924b569a8d2d6f6bc8fd2b3caa3253fa1bb674443743bf7fb9f94f9c0b0831a252894cfa8",
-                ]),
-            },
-        ];
-        for mut vector in vectors {
-            vector.input.sort_by_cached_key(PublicKey::to_sort_key);
-            assert_eq!(vector.input, vector.expect);
-        }
-    }
 }
diff --git a/src/util/merkleblock.rs b/src/util/merkleblock.rs
index 55048b55..fc94a5b0 100644
--- a/src/util/merkleblock.rs
+++ b/src/util/merkleblock.rs
@@ -584,7 +584,7 @@ fn pmt_tests() {
 
                 // Check that it has the same merkle root as the original, and a valid one
                 assert_eq!(merkle_root_1, merkle_root_2);
-                assert_ne!(merkle_root_2, TxMerkleNode::all_zeros());
+                assert_ne!(merkle_root_2, TxMerkleNode::default());
 
                 // check that it contains the matched transactions (in the same order!)
                 assert_eq!(match_txid1, match_txid2);
@@ -715,7 +715,7 @@ fn merkleblock_construct_from_txids_not_found() {
     impl PartialMerkleTree {
         /// Flip one bit in one of the hashes - this should break the authentication
         fn damage(&mut self, rng: &mut ThreadRng) {
-            let n = rng.gen_range(0..self.hashes.len());
+            let n = rng.gen_range(0, self.hashes.len());
             let bit = rng.gen::<u8>();
             let hashes = &mut self.hashes;
             let mut hash = hashes[n].into_inner();
diff --git a/src/util/psbt/serialize.rs b/src/util/psbt/serialize.rs
index 441728a3..3b2cacc6 100644
--- a/src/util/psbt/serialize.rs
+++ b/src/util/psbt/serialize.rs
@@ -382,7 +382,7 @@ pub fn compose_taproot_builder<'map>(opcode: u8, depth_map: impl IntoIterator<It
     fn taptree_hidden() {
         let mut builder = compose_taproot_builder(0x51, &[2, 2, 2]);
         builder = builder.add_leaf_with_ver(3, Script::from_hex("b9").unwrap(), LeafVersion::from_consensus(0xC2).unwrap()).unwrap();
-        builder = builder.add_hidden_node(3, sha256::Hash::all_zeros()).unwrap();
+        builder = builder.add_hidden_node(3, sha256::Hash::default()).unwrap();
         assert!(TapTree::try_from(builder).is_err());
     }
 
diff --git a/src/util/schnorr.rs b/src/util/schnorr.rs
index 7c13cc26..ec09c47a 100644
--- a/src/util/schnorr.rs
+++ b/src/util/schnorr.rs
@@ -12,6 +12,7 @@
 use crate::prelude::*;
 
 use secp256k1::{self, Secp256k1, Verification, constants};
+use crate::hashes::Hash;
 use crate::util::taproot::{TapBranchHash, TapTweakHash};
 use crate::SchnorrSighashType;
 
@@ -100,10 +101,11 @@ impl TapTweak for UntweakedPublicKey {
     /// # Returns
     /// The tweaked key and its parity.
     fn tap_tweak<C: Verification>(self, secp: &Secp256k1<C>, merkle_root: Option<TapBranchHash>) -> (TweakedPublicKey, secp256k1::Parity) {
-        let tweak = TapTweakHash::from_key_and_tweak(self, merkle_root).to_scalar();
-        let (output_key, parity) = self.add_tweak(secp, &tweak).expect("Tap tweak failed");
+        let tweak_value = TapTweakHash::from_key_and_tweak(self, merkle_root).into_inner();
+        let mut output_key = self;
+        let parity = output_key.tweak_add_assign(secp, &tweak_value).expect("Tap tweak failed");
 
-        debug_assert!(self.tweak_add_check(secp, &output_key, parity, tweak));
+        debug_assert!(self.tweak_add_check(secp, &output_key, parity, tweak_value));
         (TweakedPublicKey(output_key), parity)
     }
 
@@ -128,11 +130,11 @@ impl TapTweak for UntweakedKeyPair {
     ///
     /// # Returns
     /// The tweaked key and its parity.
-    fn tap_tweak<C: Verification>(self, secp: &Secp256k1<C>, merkle_root: Option<TapBranchHash>) -> TweakedKeyPair {
-        let (pubkey, _parity) = crate::XOnlyPublicKey::from_keypair(&self);
-        let tweak = TapTweakHash::from_key_and_tweak(pubkey, merkle_root).to_scalar();
-        let tweaked = self.add_xonly_tweak(secp, &tweak).expect("Tap tweak failed");
-        TweakedKeyPair(tweaked)
+    fn tap_tweak<C: Verification>(mut self, secp: &Secp256k1<C>, merkle_root: Option<TapBranchHash>) -> TweakedKeyPair {
+        let pubkey = crate::XOnlyPublicKey::from_keypair(&self);
+        let tweak_value = TapTweakHash::from_key_and_tweak(pubkey, merkle_root).into_inner();
+        self.tweak_add_assign(secp, &tweak_value).expect("Tap tweak failed");
+        TweakedKeyPair(self)
     }
 
     fn dangerous_assume_tweaked(self) -> TweakedKeyPair {
diff --git a/src/util/sighash.rs b/src/util/sighash.rs
index f7832525..ce57cd71 100644
--- a/src/util/sighash.rs
+++ b/src/util/sighash.rs
@@ -567,7 +567,7 @@ pub fn segwit_encode_signing_data_to<Write: io::Write>(
         value: u64,
         sighash_type: EcdsaSighashType,
     ) -> Result<(), Error> {
-        let zero_hash = sha256d::Hash::all_zeros();
+        let zero_hash = sha256d::Hash::default();
 
         let (sighash, anyone_can_pay) = sighash_type.split_anyonecanpay_flag();
 
@@ -1125,10 +1125,11 @@ fn bip_341_sighash_tests() {
             };
 
             // tests
-            let keypair = secp256k1::KeyPair::from_secret_key(secp, &internal_priv_key);
-            let (internal_key, _parity) = XOnlyPublicKey::from_keypair(&keypair);
+            let keypair = secp256k1::KeyPair::from_secret_key(secp, internal_priv_key);
+            let internal_key = XOnlyPublicKey::from_keypair(&keypair);
             let tweak = TapTweakHash::from_key_and_tweak(internal_key, merkle_root);
-            let tweaked_keypair = keypair.add_xonly_tweak(secp, &tweak.to_scalar()).unwrap();
+            let mut tweaked_keypair = keypair;
+            tweaked_keypair.tweak_add_assign(secp, &tweak).unwrap();
             let mut sig_msg = Vec::new();
             cache.taproot_encode_signing_data_to(
                 &mut sig_msg,
diff --git a/src/util/taproot.rs b/src/util/taproot.rs
index 8d77a501..af39af08 100644
--- a/src/util/taproot.rs
+++ b/src/util/taproot.rs
@@ -7,7 +7,7 @@
 
 use crate::prelude::*;
 use crate::io;
-use secp256k1::{self, Secp256k1, Scalar};
+use secp256k1::{self, Secp256k1};
 
 use core::convert::TryFrom;
 use core::fmt;
@@ -79,12 +79,6 @@ pub fn from_key_and_tweak(
         }
         TapTweakHash::from_engine(eng)
     }
-
-    /// Converts a `TapTweakHash` into a `Scalar` ready for use with key tweaking API.
-    pub fn to_scalar(&self) -> Scalar {
-        // This is statistically extremely unlikely to panic.
-        Scalar::from_be_bytes(self.into_inner()).expect("hash value greater than curve order")
-    }
 }
 
 impl TapLeafHash {
@@ -842,12 +836,12 @@ pub fn verify_taproot_commitment<C: secp256k1::Verification>(
             );
         }
         // compute the taptweak
-        let tweak = TapTweakHash::from_key_and_tweak(self.internal_key, Some(curr_hash)).to_scalar();
+        let tweak = TapTweakHash::from_key_and_tweak(self.internal_key, Some(curr_hash));
         self.internal_key.tweak_add_check(
             secp,
             &output_key,
             self.output_key_parity,
-            tweak,
+            tweak.into_inner(),
         )
     }
 }


Test patch:
diff --git a/contrib/test.sh b/contrib/test.sh
index 4031b875..37b8cfd2 100755
--- a/contrib/test.sh
+++ b/contrib/test.sh
@@ -13,6 +13,14 @@ then
     export RUSTFLAGS="-C link-dead-code"
 fi
 
+cargo --version
+rustc --version
+
+# Work out if we are using a nightly toolchain.
+NIGHTLY=false
+if cargo --version | grep nightly; then
+    NIGHTLY=true
+fi
 
 echo "********* Testing std *************"
 # Test without any features other than std first
@@ -69,10 +77,20 @@ then
     )
 fi
 
-# Bench if told to
+# Bench if told to, only works with non-stable toolchain (nightly, beta).
 if [ "$DO_BENCH" = true ]
 then
-    cargo bench --features unstable
+    if [ "NIGHTLY" = false ]
+    then
+        if [ -n "TOOLCHAIN" ]
+        then
+            echo "TOOLCHAIN is set to a non-nightly toolchain but DO_BENCH requires a nightly toolchain"
+        else
+            echo "DO_BENCH requires a nightly toolchain"
+        fi
+        exit 1
+    fi
+    RUSTFLAGS='--cfg=bench' cargo bench
 fi
 
 # Use as dependency if told to