Repository: rust-bitcoin/rust-bitcoin
Pull request #1239 (2022-09-05 02:24:07)

Introduce `bitcoin-internals` crate
Add a new crate `bitcoin-internals` to be used for internal code needed by multiple soon-to-be-created crates. Add the `write_err` macro to `bitcoin-internals`, nothing else.

### Notes

This PR uses a `path` dependency which means `rust-bitcoin` cannot be released in its current state, will need to be changed once we release the `bitcoin-internals` crate on `crates.io`.

Please note also the testing strategy imposed by this PR - each new crate to have its own `contrib/test.sh` run from the top level `test.sh`.

Please excuse the mid-series import statement refactor, I had a moment of weakness and got uncontrollably triggered by the messy imports in one file, I managed to keep myself together on the others :)

tcharding: Would `bitcoin-internal` be preferred?
Kixunil: `internals` sounds better than `internal` to me but I'm not native, so if there's a convention to not use plural for cases like these feel free to remove it.
Kixunil: Also rebase since #1238 was merged.
tcharding: > I'm not sure if we want to change it to workspace but shouldn't be bad.

What benefit is there in _not_ having a workspace? I assumed we would use a workspace soon as we started crate smashing. 
Kixunil: I thought flatter crate but long term we will mostly work in subdirs anyway. I think the only benefit is a smaller diff. Let's do it.
Kixunil: I request this to be handled with high priority as it'd unblock a bunch of my hex work and simplify PRs such as #1254 
apoelstra: Needs rebase. I'm happy to prioritize this over the weekend.
tcharding: Bizarre, that's two times now in a week that github has shown merge conflicts but I've been able to rebase without conflicts.
tcharding: > I'm happy to prioritize this over the weekend.

Sorry, Australian weekend started before I saw this.
tcharding: Make that three times, what is going on?
apoelstra: lol, another rebase request.

I have gone over this -- other than @Kixunil's nits this looks great. concept and utACK. I am a bit tempted to nit the name (should it be `bitcoin-macros` or something?) but I think `internals` is good and future-proof against whatever else we might throw in here.
Kixunil: I don't think crate missing from `members` is a nit. AFAIK some things won't work without it.
apoelstra: Good point regarding "missing workspace is not a nit".

I thought `hex` was going into `bitcoin-encodings` with base64.
tcharding: `bitcoin-macros` seems both too restrictive and too general:
- too general: does it include macros we want to be part of the public API?
- too specific: any other non-macro things that are used across the stack but we don't want to be part of the public API that we hope to soon guarantee

`bitcoin-unstable` but "unstable" is already used for unstable features? 
tcharding: > I thought hex was going into bitcoin-encodings with base64.

I thought that too (but I thought it was going to be called `bitcoin-str`).

Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 0a371932..5298527d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,62 +1,3 @@
-[package]
-name = "bitcoin"
-version = "0.29.0"
-authors = ["Andrew Poelstra <apoelstra@wpsoftware.net>"]
-license = "CC0-1.0"
-homepage = "https://github.com/rust-bitcoin/rust-bitcoin/"
-repository = "https://github.com/rust-bitcoin/rust-bitcoin/"
-documentation = "https://docs.rs/bitcoin/"
-description = "General purpose library for using and interoperating with Bitcoin and other cryptocurrencies."
-keywords = [ "crypto", "bitcoin" ]
-readme = "README.md"
-exclude = ["./test_data"]
-edition = "2018"
-
-# Please don't forget to add relevant features to docs.rs below
-[features]
-default = [ "std", "secp-recovery" ]
-rand = ["secp256k1/rand-std"]
-serde = ["actual-serde", "bitcoin_hashes/serde", "secp256k1/serde"]
-secp-lowmemory = ["secp256k1/lowmemory"]
-secp-recovery = ["secp256k1/recovery"]
-
-# At least one of std, no-std must be enabled.
-#
-# The no-std feature doesn't disable std - you need to turn off the std feature for that by disabling default.
-# Instead no-std enables additional features required for this crate to be usable without std.
-# As a result, both can be enabled without conflict.
-std = ["secp256k1/std", "bitcoin_hashes/std", "bech32/std"]
-no-std = ["hashbrown", "core2/alloc", "bitcoin_hashes/alloc", "secp256k1/alloc"]
-
-[package.metadata.docs.rs]
-features = [ "std", "secp-recovery", "base64", "rand", "serde", "bitcoinconsensus" ]
-rustdoc-args = ["--cfg", "docsrs"]
-
-[dependencies]
-bech32 = { version = "0.9.0", default-features = false }
-bitcoin_hashes = { version = "0.11.0", default-features = false }
-secp256k1 = { version = "0.24.0", default-features = false, features = ["bitcoin_hashes"] }
-core2 = { version = "0.3.0", optional = true, default-features = false }
-
-base64 = { version = "0.13.0", optional = true }
-bitcoinconsensus = { version = "0.20.2-0.5.0", optional = true }
-# Do NOT use this as a feature! Use the `serde` feature instead.
-actual-serde = { package = "serde", version = "1", default-features = false, features = [ "derive", "alloc" ], optional = true }
-hashbrown = { version = "0.8", optional = true }
-
-[dev-dependencies]
-serde_json = "<1.0.45"
-serde_test = "1"
-secp256k1 = { version = "0.24.0", features = [ "recovery", "rand-std" ] }
-bincode = "1.3.1"
-
-[[example]]
-name = "bip32"
-
-[[example]]
-name = "handshake"
-required-features = ["std"]
-
-[[example]]
-name = "ecdsa-psbt"
-required-features = ["std", "bitcoinconsensus"]
+[workspace]
+members = ["bitcoin", "internals"]
+exclude = ["embedded", "fuzz"]
diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml
new file mode 100644
index 00000000..e8717845
--- /dev/null
+++ b/bitcoin/Cargo.toml
@@ -0,0 +1,63 @@
+[package]
+name = "bitcoin"
+version = "0.29.0"
+authors = ["Andrew Poelstra <apoelstra@wpsoftware.net>"]
+license = "CC0-1.0"
+homepage = "https://github.com/rust-bitcoin/rust-bitcoin/"
+repository = "https://github.com/rust-bitcoin/rust-bitcoin/"
+documentation = "https://docs.rs/bitcoin/"
+description = "General purpose library for using and interoperating with Bitcoin and other cryptocurrencies."
+keywords = [ "crypto", "bitcoin" ]
+readme = "README.md"
+exclude = ["./test_data"]
+edition = "2018"
+
+# Please don't forget to add relevant features to docs.rs below
+[features]
+default = [ "std", "secp-recovery" ]
+rand = ["secp256k1/rand-std"]
+serde = ["actual-serde", "bitcoin_hashes/serde", "secp256k1/serde"]
+secp-lowmemory = ["secp256k1/lowmemory"]
+secp-recovery = ["secp256k1/recovery"]
+
+# At least one of std, no-std must be enabled.
+#
+# The no-std feature doesn't disable std - you need to turn off the std feature for that by disabling default.
+# Instead no-std enables additional features required for this crate to be usable without std.
+# As a result, both can be enabled without conflict.
+std = ["secp256k1/std", "bitcoin_hashes/std", "bech32/std", "bitcoin-internals/std"]
+no-std = ["hashbrown", "core2/alloc", "bitcoin_hashes/alloc", "secp256k1/alloc"]
+
+[package.metadata.docs.rs]
+features = [ "std", "secp-recovery", "base64", "rand", "serde", "bitcoinconsensus" ]
+rustdoc-args = ["--cfg", "docsrs"]
+
+[dependencies]
+bitcoin-internals = { path = "../internals" }
+bech32 = { version = "0.9.0", default-features = false }
+bitcoin_hashes = { version = "0.11.0", default-features = false }
+secp256k1 = { version = "0.24.0", default-features = false, features = ["bitcoin_hashes"] }
+core2 = { version = "0.3.0", optional = true, default-features = false }
+
+base64 = { version = "0.13.0", optional = true }
+bitcoinconsensus = { version = "0.20.2-0.5.0", optional = true }
+# Do NOT use this as a feature! Use the `serde` feature instead.
+actual-serde = { package = "serde", version = "1", default-features = false, features = [ "derive", "alloc" ], optional = true }
+hashbrown = { version = "0.8", optional = true }
+
+[dev-dependencies]
+serde_json = "<1.0.45"
+serde_test = "1"
+secp256k1 = { version = "0.24.0", features = [ "recovery", "rand-std" ] }
+bincode = "1.3.1"
+
+[[example]]
+name = "bip32"
+
+[[example]]
+name = "handshake"
+required-features = ["std"]
+
+[[example]]
+name = "ecdsa-psbt"
+required-features = ["std", "bitcoinconsensus"]
diff --git a/examples/bip32.rs b/bitcoin/examples/bip32.rs
similarity index 100%
rename from examples/bip32.rs
rename to bitcoin/examples/bip32.rs
diff --git a/examples/ecdsa-psbt.rs b/bitcoin/examples/ecdsa-psbt.rs
similarity index 100%
rename from examples/ecdsa-psbt.rs
rename to bitcoin/examples/ecdsa-psbt.rs
diff --git a/examples/handshake.rs b/bitcoin/examples/handshake.rs
similarity index 100%
rename from examples/handshake.rs
rename to bitcoin/examples/handshake.rs
diff --git a/src/address.rs b/bitcoin/src/address.rs
similarity index 99%
rename from src/address.rs
rename to bitcoin/src/address.rs
index 60aaaa80..5534e263 100644
--- a/src/address.rs
+++ b/bitcoin/src/address.rs
@@ -27,6 +27,7 @@
 use core::str::FromStr;
 
 use bech32;
+use bitcoin_internals::write_err;
 use secp256k1::{Secp256k1, Verification, XOnlyPublicKey};
 
 use crate::blockdata::constants::{
@@ -38,7 +39,7 @@
 use crate::error::ParseIntError;
 use crate::hash_types::{PubkeyHash, ScriptHash};
 use crate::hashes::{sha256, Hash, HashEngine};
-use crate::internal_macros::{serde_string_impl, write_err};
+use crate::internal_macros::serde_string_impl;
 use crate::network::constants::Network;
 use crate::prelude::*;
 use crate::util::base58;
diff --git a/src/bip158.rs b/bitcoin/src/bip158.rs
similarity index 98%
rename from src/bip158.rs
rename to bitcoin/src/bip158.rs
index 25fa0d8a..7692d5a2 100644
--- a/src/bip158.rs
+++ b/bitcoin/src/bip158.rs
@@ -42,6 +42,8 @@
 use core::cmp::{self, Ordering};
 use core::fmt::{self, Display, Formatter};
 
+use bitcoin_internals::write_err;
+
 use crate::blockdata::block::Block;
 use crate::blockdata::script::Script;
 use crate::blockdata::transaction::OutPoint;
@@ -49,7 +51,6 @@
 use crate::consensus::{Decodable, Encodable};
 use crate::hash_types::{BlockHash, FilterHash, FilterHeader};
 use crate::hashes::{siphash24, Hash};
-use crate::internal_macros::write_err;
 use crate::io;
 use crate::prelude::*;
 use crate::util::endian;
@@ -273,7 +274,8 @@ pub fn match_any<I, R>(&self, reader: &mut R, query: I) -> Result<bool, Error>
         let reader = &mut decoder;
         // map hashes to [0, n_elements << grp]
         let nm = n_elements.0 * self.m;
-        let mut mapped = query.map(|e| map_to_range(self.filter.hash(e.borrow()), nm)).collect::<Vec<_>>();
+        let mut mapped =
+            query.map(|e| map_to_range(self.filter.hash(e.borrow()), nm)).collect::<Vec<_>>();
         // sort
         mapped.sort_unstable();
         if mapped.is_empty() {
@@ -317,7 +319,8 @@ pub fn match_all<I, R>(&self, reader: &mut R, query: I) -> Result<bool, Error>
         let reader = &mut decoder;
         // map hashes to [0, n_elements << grp]
         let nm = n_elements.0 * self.m;
-        let mut mapped = query.map(|e| map_to_range(self.filter.hash(e.borrow()), nm)).collect::<Vec<_>>();
+        let mut mapped =
+            query.map(|e| map_to_range(self.filter.hash(e.borrow()), nm)).collect::<Vec<_>>();
         // sort
         mapped.sort_unstable();
         mapped.dedup();
diff --git a/src/blockdata/block.rs b/bitcoin/src/blockdata/block.rs
similarity index 99%
rename from src/blockdata/block.rs
rename to bitcoin/src/blockdata/block.rs
index 80972fc3..8d4b75c7 100644
--- a/src/blockdata/block.rs
+++ b/bitcoin/src/blockdata/block.rs
@@ -179,7 +179,7 @@ impl BlockVersion {
     /// BIP-9 soft fork signal bits mask.
     const VERSION_BITS_MASK: u32 = 0x1FFF_FFFF;
     /// 32bit value starting with `001` to use version bits.
-    /// 
+    ///
     /// The value has the top three bits `001` which enables the use of version bits to signal for soft forks.
     const USE_VERSION_BITS: u32 = 0x2000_0000;
 
diff --git a/src/blockdata/constants.rs b/bitcoin/src/blockdata/constants.rs
similarity index 100%
rename from src/blockdata/constants.rs
rename to bitcoin/src/blockdata/constants.rs
diff --git a/src/blockdata/locktime/absolute.rs b/bitcoin/src/blockdata/locktime/absolute.rs
similarity index 99%
rename from src/blockdata/locktime/absolute.rs
rename to bitcoin/src/blockdata/locktime/absolute.rs
index aaece729..8543a808 100644
--- a/src/blockdata/locktime/absolute.rs
+++ b/bitcoin/src/blockdata/locktime/absolute.rs
@@ -11,14 +11,14 @@
 use core::cmp::{PartialOrd, Ordering};
 use core::convert::TryFrom;
 use core::str::FromStr;
-use crate::error::ParseIntError;
-use crate::parse;
+
+use bitcoin_internals::write_err;
 
 use crate::consensus::encode::{self, Decodable, Encodable};
+use crate::error::ParseIntError;
 use crate::io::{self, Read, Write};
 use crate::prelude::*;
-use crate::internal_macros::write_err;
-use crate::parse::impl_parse_str_through_int;
+use crate::parse::{self, impl_parse_str_through_int};
 
 /// The Threshold for deciding whether a lock time value is a height or a time (see [Bitcoin Core]).
 ///
diff --git a/src/blockdata/locktime/mod.rs b/bitcoin/src/blockdata/locktime/mod.rs
similarity index 100%
rename from src/blockdata/locktime/mod.rs
rename to bitcoin/src/blockdata/locktime/mod.rs
diff --git a/src/blockdata/locktime/relative.rs b/bitcoin/src/blockdata/locktime/relative.rs
similarity index 100%
rename from src/blockdata/locktime/relative.rs
rename to bitcoin/src/blockdata/locktime/relative.rs
diff --git a/src/blockdata/mod.rs b/bitcoin/src/blockdata/mod.rs
similarity index 100%
rename from src/blockdata/mod.rs
rename to bitcoin/src/blockdata/mod.rs
diff --git a/src/blockdata/opcodes.rs b/bitcoin/src/blockdata/opcodes.rs
similarity index 100%
rename from src/blockdata/opcodes.rs
rename to bitcoin/src/blockdata/opcodes.rs
diff --git a/src/blockdata/script.rs b/bitcoin/src/blockdata/script.rs
similarity index 99%
rename from src/blockdata/script.rs
rename to bitcoin/src/blockdata/script.rs
index 4ac3e71a..11be7899 100644
--- a/src/blockdata/script.rs
+++ b/bitcoin/src/blockdata/script.rs
@@ -20,7 +20,7 @@
 use core::ops::Index;
 use crate::internal_macros::debug_from_display;
 #[cfg(feature = "bitcoinconsensus")]
-use crate::internal_macros::write_err;
+use bitcoin_internals::write_err;
 
 #[cfg(feature = "serde")] use serde;
 
diff --git a/src/blockdata/transaction.rs b/bitcoin/src/blockdata/transaction.rs
similarity index 99%
rename from src/blockdata/transaction.rs
rename to bitcoin/src/blockdata/transaction.rs
index 7a0dd151..3b499df7 100644
--- a/src/blockdata/transaction.rs
+++ b/bitcoin/src/blockdata/transaction.rs
@@ -18,6 +18,8 @@
 use core::{fmt, str, default::Default};
 use core::convert::TryFrom;
 
+use bitcoin_internals::write_err;
+
 use crate::hashes::{self, Hash, sha256d};
 use crate::hashes::hex::FromHex;
 
@@ -30,7 +32,7 @@
 use crate::consensus::{encode, Decodable, Encodable};
 use crate::hash_types::{Sighash, Txid, Wtxid};
 use crate::VarInt;
-use crate::internal_macros::{impl_consensus_encoding, serde_struct_human_string_impl, write_err};
+use crate::internal_macros::{impl_consensus_encoding, serde_struct_human_string_impl};
 use crate::parse::impl_parse_str_through_int;
 
 #[cfg(doc)]
diff --git a/src/blockdata/witness.rs b/bitcoin/src/blockdata/witness.rs
similarity index 100%
rename from src/blockdata/witness.rs
rename to bitcoin/src/blockdata/witness.rs
diff --git a/src/consensus/encode.rs b/bitcoin/src/consensus/encode.rs
similarity index 99%
rename from src/consensus/encode.rs
rename to bitcoin/src/consensus/encode.rs
index e2997225..e1afd10a 100644
--- a/src/consensus/encode.rs
+++ b/bitcoin/src/consensus/encode.rs
@@ -20,9 +20,10 @@
 
 use core::{fmt, mem, u32, convert::From};
 
+use bitcoin_internals::write_err;
+
 use crate::hashes::{sha256d, Hash, sha256};
 use crate::hash_types::{BlockHash, FilterHash, TxMerkleNode, FilterHeader};
-use crate::internal_macros::write_err;
 use crate::io::{self, Cursor, Read};
 
 use crate::util::endian;
diff --git a/src/consensus/mod.rs b/bitcoin/src/consensus/mod.rs
similarity index 100%
rename from src/consensus/mod.rs
rename to bitcoin/src/consensus/mod.rs
diff --git a/src/consensus/params.rs b/bitcoin/src/consensus/params.rs
similarity index 100%
rename from src/consensus/params.rs
rename to bitcoin/src/consensus/params.rs
diff --git a/src/error.rs b/bitcoin/src/error.rs
similarity index 100%
rename from src/error.rs
rename to bitcoin/src/error.rs
diff --git a/src/hash_types.rs b/bitcoin/src/hash_types.rs
similarity index 100%
rename from src/hash_types.rs
rename to bitcoin/src/hash_types.rs
diff --git a/src/internal_macros.rs b/bitcoin/src/internal_macros.rs
similarity index 96%
rename from src/internal_macros.rs
rename to bitcoin/src/internal_macros.rs
index 2e8835ce..bc196c6c 100644
--- a/src/internal_macros.rs
+++ b/bitcoin/src/internal_macros.rs
@@ -626,26 +626,6 @@ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
 }
 pub(crate) use user_enum;
 
-/// Formats error. If `std` feature is OFF appends error source (delimited by `: `). We do this
-/// because `e.source()` is only available in std builds, without this macro the error source is
-/// lost for no-std builds.
-macro_rules! write_err {
-    ($writer:expr, $string:literal $(, $args:expr)*; $source:expr) => {
-        {
-            #[cfg(feature = "std")]
-            {
-                let _ = &$source;   // Prevents clippy warnings.
-                write!($writer, $string $(, $args)*)
-            }
-            #[cfg(not(feature = "std"))]
-            {
-                write!($writer, concat!($string, ": {}") $(, $args)*, $source)
-            }
-        }
-    }
-}
-pub(crate) use write_err;
-
 /// Asserts a boolean expression at compile time.
 macro_rules! const_assert {
     ($x:expr) => {{
diff --git a/src/lib.rs b/bitcoin/src/lib.rs
similarity index 100%
rename from src/lib.rs
rename to bitcoin/src/lib.rs
diff --git a/src/network/address.rs b/bitcoin/src/network/address.rs
similarity index 100%
rename from src/network/address.rs
rename to bitcoin/src/network/address.rs
diff --git a/src/network/constants.rs b/bitcoin/src/network/constants.rs
similarity index 100%
rename from src/network/constants.rs
rename to bitcoin/src/network/constants.rs
diff --git a/src/network/message.rs b/bitcoin/src/network/message.rs
similarity index 100%
rename from src/network/message.rs
rename to bitcoin/src/network/message.rs
diff --git a/src/network/message_blockdata.rs b/bitcoin/src/network/message_blockdata.rs
similarity index 100%
rename from src/network/message_blockdata.rs
rename to bitcoin/src/network/message_blockdata.rs
diff --git a/src/network/message_bloom.rs b/bitcoin/src/network/message_bloom.rs
similarity index 100%
rename from src/network/message_bloom.rs
rename to bitcoin/src/network/message_bloom.rs
diff --git a/src/network/message_compact_blocks.rs b/bitcoin/src/network/message_compact_blocks.rs
similarity index 100%
rename from src/network/message_compact_blocks.rs
rename to bitcoin/src/network/message_compact_blocks.rs
diff --git a/src/network/message_filter.rs b/bitcoin/src/network/message_filter.rs
similarity index 100%
rename from src/network/message_filter.rs
rename to bitcoin/src/network/message_filter.rs
diff --git a/src/network/message_network.rs b/bitcoin/src/network/message_network.rs
similarity index 100%
rename from src/network/message_network.rs
rename to bitcoin/src/network/message_network.rs
diff --git a/src/network/mod.rs b/bitcoin/src/network/mod.rs
similarity index 100%
rename from src/network/mod.rs
rename to bitcoin/src/network/mod.rs
diff --git a/src/parse.rs b/bitcoin/src/parse.rs
similarity index 99%
rename from src/parse.rs
rename to bitcoin/src/parse.rs
index 3b352d15..fd69cbf2 100644
--- a/src/parse.rs
+++ b/bitcoin/src/parse.rs
@@ -2,8 +2,9 @@
 use core::fmt;
 use core::str::FromStr;
 
+use bitcoin_internals::write_err;
+
 use crate::error::impl_std_error;
-use crate::internal_macros::write_err;
 use crate::prelude::*;
 
 /// Error with rich context returned when a string can't be parsed as an integer.
diff --git a/src/policy.rs b/bitcoin/src/policy.rs
similarity index 100%
rename from src/policy.rs
rename to bitcoin/src/policy.rs
diff --git a/src/serde_utils.rs b/bitcoin/src/serde_utils.rs
similarity index 100%
rename from src/serde_utils.rs
rename to bitcoin/src/serde_utils.rs
diff --git a/src/test_macros.rs b/bitcoin/src/test_macros.rs
similarity index 100%
rename from src/test_macros.rs
rename to bitcoin/src/test_macros.rs
diff --git a/src/util/amount.rs b/bitcoin/src/util/amount.rs
similarity index 100%
rename from src/util/amount.rs
rename to bitcoin/src/util/amount.rs
diff --git a/src/util/base58.rs b/bitcoin/src/util/base58.rs
similarity index 99%
rename from src/util/base58.rs
rename to bitcoin/src/util/base58.rs
index d548f116..7fcc78a8 100644
--- a/src/util/base58.rs
+++ b/bitcoin/src/util/base58.rs
@@ -11,11 +11,11 @@
 
 use core::{fmt, str, iter, slice};
 
+use bitcoin_internals::write_err;
 use crate::hashes::{sha256d, Hash, hex};
 use secp256k1;
 
 use crate::util::{endian, key};
-use crate::internal_macros::write_err;
 
 /// An error that might occur during base58 decoding
 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
diff --git a/src/util/bip143.rs b/bitcoin/src/util/bip143.rs
similarity index 100%
rename from src/util/bip143.rs
rename to bitcoin/src/util/bip143.rs
diff --git a/src/util/bip152.rs b/bitcoin/src/util/bip152.rs
similarity index 100%
rename from src/util/bip152.rs
rename to bitcoin/src/util/bip152.rs
diff --git a/src/util/bip32.rs b/bitcoin/src/util/bip32.rs
similarity index 99%
rename from src/util/bip32.rs
rename to bitcoin/src/util/bip32.rs
index 9f18916d..a6d1a17c 100644
--- a/src/util/bip32.rs
+++ b/bitcoin/src/util/bip32.rs
@@ -14,6 +14,7 @@
 use core::ops::Index;
 #[cfg(feature = "serde")] use serde;
 
+use bitcoin_internals::write_err;
 use crate::hash_types::XpubIdentifier;
 use crate::hashes::{sha512, Hash, HashEngine, Hmac, HmacEngine, hex};
 use secp256k1::{self, Secp256k1, XOnlyPublicKey};
@@ -21,7 +22,7 @@
 use crate::network::constants::Network;
 use crate::util::{base58, endian, key};
 use crate::util::key::{PublicKey, PrivateKey, KeyPair};
-use crate::internal_macros::{impl_array_newtype, impl_bytes_newtype, serde_string_impl, write_err};
+use crate::internal_macros::{impl_array_newtype, impl_bytes_newtype, serde_string_impl};
 
 /// A chain code
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
diff --git a/src/util/ecdsa.rs b/bitcoin/src/util/ecdsa.rs
similarity index 99%
rename from src/util/ecdsa.rs
rename to bitcoin/src/util/ecdsa.rs
index 50116723..682ab298 100644
--- a/src/util/ecdsa.rs
+++ b/bitcoin/src/util/ecdsa.rs
@@ -8,11 +8,11 @@
 use core::str::FromStr;
 use core::{fmt, iter};
 
+use bitcoin_internals::write_err;
 use secp256k1;
 
 use crate::prelude::*;
 use crate::hashes::hex::{self, FromHex};
-use crate::internal_macros::write_err;
 use crate::util::sighash::{EcdsaSighashType, NonStandardSighashType};
 
 /// An ECDSA signature with the corresponding hash type.
diff --git a/src/util/endian.rs b/bitcoin/src/util/endian.rs
similarity index 100%
rename from src/util/endian.rs
rename to bitcoin/src/util/endian.rs
diff --git a/src/util/hash.rs b/bitcoin/src/util/hash.rs
similarity index 100%
rename from src/util/hash.rs
rename to bitcoin/src/util/hash.rs
diff --git a/src/util/key.rs b/bitcoin/src/util/key.rs
similarity index 99%
rename from src/util/key.rs
rename to bitcoin/src/util/key.rs
index ffc1c2e5..c53d648f 100644
--- a/src/util/key.rs
+++ b/bitcoin/src/util/key.rs
@@ -11,6 +11,7 @@
 use core::{ops, str::FromStr};
 use core::fmt::{self, Write};
 
+use bitcoin_internals::write_err;
 pub use secp256k1::{self, Secp256k1, XOnlyPublicKey, KeyPair};
 
 use crate::io;
@@ -18,7 +19,6 @@
 use crate::hashes::{Hash, hash160, hex, hex::FromHex};
 use crate::hash_types::{PubkeyHash, WPubkeyHash};
 use crate::util::base58;
-use crate::internal_macros::write_err;
 
 /// A key-related error.
 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
diff --git a/src/util/merkleblock.rs b/bitcoin/src/util/merkleblock.rs
similarity index 100%
rename from src/util/merkleblock.rs
rename to bitcoin/src/util/merkleblock.rs
diff --git a/src/util/misc.rs b/bitcoin/src/util/misc.rs
similarity index 99%
rename from src/util/misc.rs
rename to bitcoin/src/util/misc.rs
index dea34063..fc1807ca 100644
--- a/src/util/misc.rs
+++ b/bitcoin/src/util/misc.rs
@@ -27,13 +27,13 @@ mod message_signing {
 
     use core::fmt;
 
+    use bitcoin_internals::write_err;
     use crate::hashes::sha256d;
     use secp256k1;
     use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
 
     use crate::util::key::PublicKey;
     use crate::address::{Address, AddressType};
-    use crate::internal_macros::write_err;
 
     /// An error used for dealing with Bitcoin Signed Messages.
     #[cfg_attr(docsrs, doc(cfg(feature = "secp-recovery")))]
diff --git a/src/util/mod.rs b/bitcoin/src/util/mod.rs
similarity index 98%
rename from src/util/mod.rs
rename to bitcoin/src/util/mod.rs
index 52f61ecd..72d66095 100644
--- a/src/util/mod.rs
+++ b/bitcoin/src/util/mod.rs
@@ -28,8 +28,9 @@
 use crate::io;
 use core::fmt;
 
+use bitcoin_internals::write_err;
+
 use crate::consensus::encode;
-use crate::internal_macros::write_err;
 
 /// A trait which allows numbers to act as fixed-size bit arrays
 pub trait BitArray {
diff --git a/src/util/psbt/error.rs b/bitcoin/src/util/psbt/error.rs
similarity index 99%
rename from src/util/psbt/error.rs
rename to bitcoin/src/util/psbt/error.rs
index 7965e0e6..4424a809 100644
--- a/src/util/psbt/error.rs
+++ b/bitcoin/src/util/psbt/error.rs
@@ -4,13 +4,14 @@
 
 use core::fmt;
 
+use bitcoin_internals::write_err;
+
 use crate::blockdata::transaction::Transaction;
 use crate::consensus::encode;
 use crate::util::psbt::raw;
 
 use crate::hashes;
 use crate::util::bip32::ExtendedPubKey;
-use crate::internal_macros::write_err;
 
 /// Enum for marking psbt hash error.
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
diff --git a/src/util/psbt/macros.rs b/bitcoin/src/util/psbt/macros.rs
similarity index 100%
rename from src/util/psbt/macros.rs
rename to bitcoin/src/util/psbt/macros.rs
diff --git a/src/util/psbt/map/global.rs b/bitcoin/src/util/psbt/map/global.rs
similarity index 100%
rename from src/util/psbt/map/global.rs
rename to bitcoin/src/util/psbt/map/global.rs
diff --git a/src/util/psbt/map/input.rs b/bitcoin/src/util/psbt/map/input.rs
similarity index 100%
rename from src/util/psbt/map/input.rs
rename to bitcoin/src/util/psbt/map/input.rs
diff --git a/src/util/psbt/map/mod.rs b/bitcoin/src/util/psbt/map/mod.rs
similarity index 100%
rename from src/util/psbt/map/mod.rs
rename to bitcoin/src/util/psbt/map/mod.rs
diff --git a/src/util/psbt/map/output.rs b/bitcoin/src/util/psbt/map/output.rs
similarity index 100%
rename from src/util/psbt/map/output.rs
rename to bitcoin/src/util/psbt/map/output.rs
diff --git a/src/util/psbt/mod.rs b/bitcoin/src/util/psbt/mod.rs
similarity index 99%
rename from src/util/psbt/mod.rs
rename to bitcoin/src/util/psbt/mod.rs
index 6e88bd39..99b328ad 100644
--- a/src/util/psbt/mod.rs
+++ b/bitcoin/src/util/psbt/mod.rs
@@ -207,7 +207,7 @@ mod display_from_str {
     use core::str::FromStr;
     use crate::consensus::encode::{Error, self};
     use base64::display::Base64Display;
-    use crate::internal_macros::write_err;
+    use bitcoin_internals::write_err;
 
     /// Error encountered during PSBT decoding from Base64 string.
     #[derive(Debug)]
diff --git a/src/util/psbt/raw.rs b/bitcoin/src/util/psbt/raw.rs
similarity index 100%
rename from src/util/psbt/raw.rs
rename to bitcoin/src/util/psbt/raw.rs
diff --git a/src/util/psbt/serialize.rs b/bitcoin/src/util/psbt/serialize.rs
similarity index 100%
rename from src/util/psbt/serialize.rs
rename to bitcoin/src/util/psbt/serialize.rs
diff --git a/src/util/schnorr.rs b/bitcoin/src/util/schnorr.rs
similarity index 99%
rename from src/util/schnorr.rs
rename to bitcoin/src/util/schnorr.rs
index 81729e17..7036b6a2 100644
--- a/src/util/schnorr.rs
+++ b/bitcoin/src/util/schnorr.rs
@@ -9,12 +9,13 @@
 
 use core::fmt;
 
+use bitcoin_internals::write_err;
+
 use crate::prelude::*;
 
 use secp256k1::{self, Secp256k1, Verification, constants};
 use crate::util::taproot::{TapBranchHash, TapTweakHash};
 use crate::SchnorrSighashType;
-use crate::internal_macros::write_err;
 
 /// Deprecated re-export of [`secp256k1::XOnlyPublicKey`]
 #[deprecated(since = "0.28.0", note = "Please use `util::key::XOnlyPublicKey` instead")]
diff --git a/src/util/sighash.rs b/bitcoin/src/util/sighash.rs
similarity index 100%
rename from src/util/sighash.rs
rename to bitcoin/src/util/sighash.rs
diff --git a/src/util/taproot.rs b/bitcoin/src/util/taproot.rs
similarity index 99%
rename from src/util/taproot.rs
rename to bitcoin/src/util/taproot.rs
index 7e7f9190..7e37e2e3 100644
--- a/src/util/taproot.rs
+++ b/bitcoin/src/util/taproot.rs
@@ -7,6 +7,7 @@
 
 use crate::prelude::*;
 use crate::io;
+use bitcoin_internals::write_err;
 use secp256k1::{self, Secp256k1, Scalar};
 
 use core::convert::TryFrom;
@@ -17,7 +18,6 @@
 use crate::schnorr::{TweakedPublicKey, UntweakedPublicKey, TapTweak};
 use crate::util::key::XOnlyPublicKey;
 use crate::Script;
-use crate::internal_macros::write_err;
 use crate::consensus::Encodable;
 
 /// The SHA-256 midstate value for the TapLeaf hash.
diff --git a/embedded/Cargo.toml b/embedded/Cargo.toml
index f8c870b3..ef6d962e 100644
--- a/embedded/Cargo.toml
+++ b/embedded/Cargo.toml
@@ -11,7 +11,7 @@ cortex-m-rt = "0.6.10"
 cortex-m-semihosting = "0.3.3"
 panic-halt = "0.2.0"
 alloc-cortex-m = "0.4.1"
-bitcoin = { path="../", default-features = false, features = ["no-std", "secp-lowmemory"] }
+bitcoin = { path="../bitcoin", default-features = false, features = ["no-std", "secp-lowmemory"] }
 
 [[bin]]
 name = "embedded"
diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml
index 927a3b37..555ec383 100644
--- a/fuzz/Cargo.toml
+++ b/fuzz/Cargo.toml
@@ -14,7 +14,7 @@ honggfuzz_fuzz = ["honggfuzz"]
 [dependencies]
 honggfuzz = { version = "0.5", optional = true, default-features = false }
 afl = { version = "0.4", optional = true }
-bitcoin = { path = ".." }
+bitcoin = { path = "../bitcoin" }
 
 # Prevent this from interfering with workspaces
 [workspace]
diff --git a/internals/Cargo.toml b/internals/Cargo.toml
new file mode 100644
index 00000000..f23271f6
--- /dev/null
+++ b/internals/Cargo.toml
@@ -0,0 +1,25 @@
+[package]
+name = "bitcoin-internals"
+version = "0.1.0"
+authors = ["Andrew Poelstra <apoelstra@wpsoftware.net>", "The Rust Bitcoin developers"]
+license = "CC0-1.0"
+repository = "https://github.com/rust-bitcoin/rust-bitcoin/"
+documentation = "https://docs.rs/bitcoin-internals"
+description = "Internal types and macros used by rust-bitcoin ecosystem"
+categories = ["cryptography::cryptocurrencies"]
+keywords = ["internal"]
+readme = "README.md"
+edition = "2018"
+
+# Please don't forget to add relevant features to docs.rs below.
+[features]
+default = []
+std = []
+
+[package.metadata.docs.rs]
+features = ["std"]
+rustdoc-args = ["--cfg", "docsrs"]
+
+[dependencies]
+
+[dev-dependencies]
diff --git a/internals/README.md b/internals/README.md
new file mode 100644
index 00000000..79aa2980
--- /dev/null
+++ b/internals/README.md
@@ -0,0 +1,7 @@
+Rust Bitcoin Internals
+======================
+
+This crate is only meant to be used internally by crates in the
+[rust-bitcoin](https://github.com/rust-bitcoin) ecosystem.
+
+This crate will never be stabilized, depend on it at your own risk.
diff --git a/internals/src/error.rs b/internals/src/error.rs
new file mode 100644
index 00000000..2f213377
--- /dev/null
+++ b/internals/src/error.rs
@@ -0,0 +1,29 @@
+// Written by the Rust Bitcoin developers.
+// SPDX-License-Identifier: CC0-1.0
+
+//! # Error
+//!
+//! Error handling macros and helpers.
+//!
+
+/// Formats error.
+///
+/// If `std` feature is OFF appends error source (delimited by `: `). We do this because
+/// `e.source()` is only available in std builds, without this macro the error source is lost for
+/// no-std builds.
+#[macro_export]
+macro_rules! write_err {
+    ($writer:expr, $string:literal $(, $args:expr)*; $source:expr) => {
+        {
+            #[cfg(feature = "std")]
+            {
+                let _ = &$source;   // Prevents clippy warnings.
+                write!($writer, $string $(, $args)*)
+            }
+            #[cfg(not(feature = "std"))]
+            {
+                write!($writer, concat!($string, ": {}") $(, $args)*, $source)
+            }
+        }
+    }
+}
diff --git a/internals/src/lib.rs b/internals/src/lib.rs
new file mode 100644
index 00000000..bad0d8d2
--- /dev/null
+++ b/internals/src/lib.rs
@@ -0,0 +1,24 @@
+// Written by the Rust Bitcoin developers.
+// SPDX-License-Identifier: CC0-1.0
+
+//! # Rust Bitcoin Internal
+//!
+//! This crate is only meant to be used internally by crates in the
+//! [rust-bitcoin](https://github.com/rust-bitcoin) ecosystem.
+//!
+
+#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
+// Experimental features we need.
+#![cfg_attr(docsrs, feature(doc_cfg))]
+// Coding conventions
+#![forbid(unsafe_code)]
+#![deny(non_upper_case_globals)]
+#![deny(non_camel_case_types)]
+#![deny(non_snake_case)]
+#![deny(unused_mut)]
+#![deny(dead_code)]
+#![deny(unused_imports)]
+#![deny(missing_docs)]
+#![deny(unused_must_use)]
+
+pub mod error;
diff --git a/rustfmt.toml b/rustfmt.toml
index 68b410f6..a80a8e64 100644
--- a/rustfmt.toml
+++ b/rustfmt.toml
@@ -1,9 +1,9 @@
 # Eventually this shoud be: ignore = []
 ignore = [
-       "src/blockdata",
-       "src/consensus",
-       "src/network",
-       "src/util",
+       "bitcoin/src/blockdata",
+       "bitcoin/src/consensus",
+       "bitcoin/src/network",
+       "bitcoin/src/util",
 ]
 
 hard_tabs = false


Test patch:
diff --git a/contrib/act/event.json b/bitcoin/contrib/act/event.json
similarity index 100%
rename from contrib/act/event.json
rename to bitcoin/contrib/act/event.json
diff --git a/bitcoin/contrib/test.sh b/bitcoin/contrib/test.sh
new file mode 100755
index 00000000..69805acc
--- /dev/null
+++ b/bitcoin/contrib/test.sh
@@ -0,0 +1,127 @@
+#!/bin/sh
+
+set -ex
+
+FEATURES="base64 bitcoinconsensus serde rand secp-recovery"
+
+if [ "$DO_COV" = true ]
+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
+
+# We should not have any duplicate dependencies. This catches mistakes made upgrading dependencies
+# in one crate and not in another (e.g. upgrade bitcoin_hashes in bitcoin but not in secp).
+cargo update -p serde --precise 1.0.142
+cargo update -p serde_test --precise 1.0.142
+cargo update -p serde_derive --precise 1.0.142
+duplicate_dependencies=$(cargo tree  --target=all --all-features --duplicates | wc -l)
+if [ "$duplicate_dependencies" -ne 0 ]; then
+    echo "Dependency tree is broken, contains duplicates"
+    cargo tree  --target=all --all-features --duplicates
+    exit 1
+fi
+
+if [ "$DO_LINT" = true ]
+then
+    cargo clippy --all-features --all-targets -- -D warnings
+    cargo clippy --example bip32 -- -D warnings
+    cargo clippy --example handshake -- -D warnings
+    cargo clippy --example ecdsa-psbt --features=bitcoinconsensus -- -D warnings
+fi
+
+echo "********* Testing std *************"
+# Test without any features other than std first
+cargo test --verbose --no-default-features --features="std"
+
+echo "********* Testing default *************"
+# Then test with the default features
+cargo test --verbose
+
+if [ "$DO_NO_STD" = true ]
+then
+    echo "********* Testing no-std build *************"
+    # Build no_std, to make sure that cfg(test) doesn't hide any issues
+    cargo build --verbose --features="no-std" --no-default-features
+
+    # Build std + no_std, to make sure they are not incompatible
+    cargo build --verbose --features="no-std"
+
+    # Test no_std
+    cargo test --verbose --features="no-std" --no-default-features
+
+    # Build all features
+    cargo build --verbose --features="no-std $FEATURES" --no-default-features
+
+    # Build specific features
+    for feature in ${FEATURES}
+    do
+        cargo build --verbose --features="no-std $feature"
+    done
+
+    cargo run --example bip32 7934c09359b234e076b9fa5a1abfd38e3dc2a9939745b7cc3c22a48d831d14bd
+    cargo run --no-default-features --features no-std --example bip32 7934c09359b234e076b9fa5a1abfd38e3dc2a9939745b7cc3c22a48d831d14bd
+fi
+
+# Test each feature
+for feature in ${FEATURES}
+do
+    echo "********* Testing $feature *************"
+    cargo test --verbose --features="$feature"
+done
+
+cargo run --example ecdsa-psbt --features=bitcoinconsensus
+
+# Build the docs if told to (this only works with the nightly toolchain)
+if [ "$DO_DOCS" = true ]; then
+    RUSTDOCFLAGS="--cfg docsrs" cargo +nightly rustdoc --features="$FEATURES" -- -D rustdoc::broken-intra-doc-links
+fi
+
+# Fuzz if told to
+if [ "$DO_FUZZ" = true ]
+then
+    (
+        cd fuzz
+        cargo test --verbose
+        ./travis-fuzz.sh
+    )
+fi
+
+# Bench if told to, only works with non-stable toolchain (nightly, beta).
+if [ "$DO_BENCH" = true ]
+then
+    if [ "$NIGHTLY" = false ]
+    then
+        if [ -n "$RUSTUP_TOOLCHAIN" ]
+        then
+            echo "RUSTUP_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
+if [ "$AS_DEPENDENCY" = true ]
+then
+    cargo new dep_test 2> /dev/null # Mute warning about workspace, fixed below.
+    cd dep_test
+    echo 'bitcoin = { path = "..", features = ["serde"] }\n\n' >> Cargo.toml
+    # Adding an empty workspace section excludes this crate from the rust-bitcoin workspace.
+    echo '[workspace]\n\n' >> Cargo.toml
+
+    cargo update -p serde --precise 1.0.142
+    cargo update -p serde_derive --precise 1.0.142
+
+    cargo test --verbose
+fi
diff --git a/src/util/uint.rs b/bitcoin/src/util/uint.rs
similarity index 100%
rename from src/util/uint.rs
rename to bitcoin/src/util/uint.rs
diff --git a/test_data/bip341_tests.json b/bitcoin/test_data/bip341_tests.json
similarity index 100%
rename from test_data/bip341_tests.json
rename to bitcoin/test_data/bip341_tests.json
diff --git a/test_data/blockfilters.json b/bitcoin/test_data/blockfilters.json
similarity index 100%
rename from test_data/blockfilters.json
rename to bitcoin/test_data/blockfilters.json
diff --git a/test_data/legacy_sighash.json b/bitcoin/test_data/legacy_sighash.json
similarity index 100%
rename from test_data/legacy_sighash.json
rename to bitcoin/test_data/legacy_sighash.json
diff --git a/test_data/mainnet_block_000000000000000000000c835b2adcaedc20fdf6ee440009c249452c726dafae.raw b/bitcoin/test_data/mainnet_block_000000000000000000000c835b2adcaedc20fdf6ee440009c249452c726dafae.raw
similarity index 100%
rename from test_data/mainnet_block_000000000000000000000c835b2adcaedc20fdf6ee440009c249452c726dafae.raw
rename to bitcoin/test_data/mainnet_block_000000000000000000000c835b2adcaedc20fdf6ee440009c249452c726dafae.raw
diff --git a/test_data/psbt1.hex b/bitcoin/test_data/psbt1.hex
similarity index 100%
rename from test_data/psbt1.hex
rename to bitcoin/test_data/psbt1.hex
diff --git a/test_data/psbt2.hex b/bitcoin/test_data/psbt2.hex
similarity index 100%
rename from test_data/psbt2.hex
rename to bitcoin/test_data/psbt2.hex
diff --git a/test_data/psbt_combined.hex b/bitcoin/test_data/psbt_combined.hex
similarity index 100%
rename from test_data/psbt_combined.hex
rename to bitcoin/test_data/psbt_combined.hex
diff --git a/test_data/testnet_block_000000000000045e0b1660b6445b5e5c5ab63c9a4f956be7e1e69be04fa4497b.raw b/bitcoin/test_data/testnet_block_000000000000045e0b1660b6445b5e5c5ab63c9a4f956be7e1e69be04fa4497b.raw
similarity index 100%
rename from test_data/testnet_block_000000000000045e0b1660b6445b5e5c5ab63c9a4f956be7e1e69be04fa4497b.raw
rename to bitcoin/test_data/testnet_block_000000000000045e0b1660b6445b5e5c5ab63c9a4f956be7e1e69be04fa4497b.raw
diff --git a/contrib/test.sh b/contrib/test.sh
index b46e8917..30483824 100755
--- a/contrib/test.sh
+++ b/contrib/test.sh
@@ -2,123 +2,14 @@
 
 set -ex
 
-FEATURES="base64 bitcoinconsensus serde rand secp-recovery"
+CRATES="bitcoin"
 
-if [ "$DO_COV" = true ]
-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
-
-# We should not have any duplicate dependencies. This catches mistakes made upgrading dependencies
-# in one crate and not in another (e.g. upgrade bitcoin_hashes in bitcoin but not in secp).
-cargo update -p serde --precise 1.0.142
-cargo update -p serde_test --precise 1.0.142
-cargo update -p serde_derive --precise 1.0.142
-duplicate_dependencies=$(cargo tree  --target=all --all-features --duplicates | wc -l)
-if [ "$duplicate_dependencies" -ne 0 ]; then
-    echo "Dependency tree is broken, contains duplicates"
-    cargo tree  --target=all --all-features --duplicates
-    exit 1
-fi
-
-if [ "$DO_LINT" = true ]
-then
-    cargo clippy --all-features --all-targets -- -D warnings
-    cargo clippy --example bip32 -- -D warnings
-    cargo clippy --example handshake -- -D warnings
-    cargo clippy --example ecdsa-psbt --features=bitcoinconsensus -- -D warnings
-fi
-
-echo "********* Testing std *************"
-# Test without any features other than std first
-cargo test --verbose --no-default-features --features="std"
-
-echo "********* Testing default *************"
-# Then test with the default features
-cargo test --verbose
-
-if [ "$DO_NO_STD" = true ]
-then
-    echo "********* Testing no-std build *************"
-    # Build no_std, to make sure that cfg(test) doesn't hide any issues
-    cargo build --verbose --features="no-std" --no-default-features
-
-    # Build std + no_std, to make sure they are not incompatible
-    cargo build --verbose --features="no-std"
-
-    # Test no_std
-    cargo test --verbose --features="no-std" --no-default-features
-
-    # Build all features
-    cargo build --verbose --features="no-std $FEATURES" --no-default-features
-
-    # Build specific features
-    for feature in ${FEATURES}
-    do
-        cargo build --verbose --features="no-std $feature"
-    done
-
-    cargo run --example bip32 7934c09359b234e076b9fa5a1abfd38e3dc2a9939745b7cc3c22a48d831d14bd
-    cargo run --no-default-features --features no-std --example bip32 7934c09359b234e076b9fa5a1abfd38e3dc2a9939745b7cc3c22a48d831d14bd
-fi
-
-# Test each feature
-for feature in ${FEATURES}
+for crate in ${CRATES}
 do
-    echo "********* Testing $feature *************"
-    cargo test --verbose --features="$feature"
-done
-
-cargo run --example ecdsa-psbt --features=bitcoinconsensus
-
-# Build the docs if told to (this only works with the nightly toolchain)
-if [ "$DO_DOCS" = true ]; then
-    RUSTDOCFLAGS="--cfg docsrs" cargo +nightly rustdoc --features="$FEATURES" -- -D rustdoc::broken-intra-doc-links
-fi
-
-# Fuzz if told to
-if [ "$DO_FUZZ" = true ]
-then
     (
-        cd fuzz
-        cargo test --verbose
-        ./travis-fuzz.sh
+        cd "$crate"
+        ./contrib/test.sh
     )
-fi
-
-# Bench if told to, only works with non-stable toolchain (nightly, beta).
-if [ "$DO_BENCH" = true ]
-then
-    if [ "$NIGHTLY" = false ]
-    then
-        if [ -n "$RUSTUP_TOOLCHAIN" ]
-        then
-            echo "RUSTUP_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
-if [ "$AS_DEPENDENCY" = true ]
-then
-    cargo new dep_test
-    cd dep_test
-    echo 'bitcoin = { path = "..", features = ["serde"] }' >> Cargo.toml
-    cargo update -p serde --precise 1.0.142
-    cargo update -p serde_derive --precise 1.0.142
+done
 
-    cargo test --verbose
-fi
+exit 0