Add a `bitcoin_io` crate

In order to support standard (de)serialization of structs, the
`rust-bitcoin` ecosystem uses the standard `std::io::{Read,Write}`
traits. This works great for environments with `std`, however sadly
the `std::io` module has not yet been added to the `core` crate.

Thus, in `no-std`, the `rust-bitcoin` ecosystem has historically
used the `core2` crate to provide copies of the `std::io` module
without any major dependencies. Sadly, its one dependency,
`memchr`, recently broke our MSRV.

Worse, because we didn't want to take on any excess dependencies
for `std` builds, `rust-bitcoin` has had to have
mutually-exclusive `std` and `no-std` builds. This breaks general
assumptions about how features work in Rust, causing substantial
pain for applications far downstream of `rust-bitcoin` crates.

Here, we add a new `bitcoin_io` crate, making it an unconditional
dependency and using its `io` module in the in-repository crates
in place of `std::io` and `core2::io`. As it is not substantial
additional code, the `hashes` io implementations are no longer
feature-gated.

This doesn't actually accomplish anything on its own, only adding
the new crate which still depends on `core2`.
This commit is contained in:
Matt Corallo 2023-10-04 05:51:26 +00:00
parent 4fb91277f0
commit 27c7c4e26a
13 changed files with 110 additions and 42 deletions

View File

@ -38,9 +38,9 @@ dependencies = [
"bech32", "bech32",
"bincode", "bincode",
"bitcoin-internals", "bitcoin-internals",
"bitcoin-io",
"bitcoin_hashes", "bitcoin_hashes",
"bitcoinconsensus", "bitcoinconsensus",
"core2",
"hex-conservative", "hex-conservative",
"hex_lit", "hex_lit",
"mutagen", "mutagen",
@ -69,11 +69,18 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "bitcoin-io"
version = "0.1.0"
dependencies = [
"core2",
]
[[package]] [[package]]
name = "bitcoin_hashes" name = "bitcoin_hashes"
version = "0.13.0" version = "0.13.0"
dependencies = [ dependencies = [
"core2", "bitcoin-io",
"hex-conservative", "hex-conservative",
"schemars", "schemars",
"serde", "serde",

View File

@ -37,9 +37,9 @@ dependencies = [
"bech32", "bech32",
"bincode", "bincode",
"bitcoin-internals", "bitcoin-internals",
"bitcoin-io",
"bitcoin_hashes", "bitcoin_hashes",
"bitcoinconsensus", "bitcoinconsensus",
"core2",
"hex-conservative", "hex-conservative",
"hex_lit", "hex_lit",
"mutagen", "mutagen",
@ -68,11 +68,18 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "bitcoin-io"
version = "0.1.0"
dependencies = [
"core2",
]
[[package]] [[package]]
name = "bitcoin_hashes" name = "bitcoin_hashes"
version = "0.13.0" version = "0.13.0"
dependencies = [ dependencies = [
"core2", "bitcoin-io",
"hex-conservative", "hex-conservative",
"schemars", "schemars",
"serde", "serde",

View File

@ -1,5 +1,5 @@
[workspace] [workspace]
members = ["bitcoin", "hashes", "internals", "fuzz"] members = ["bitcoin", "hashes", "internals", "fuzz", "io"]
[patch.crates-io.bitcoin] [patch.crates-io.bitcoin]
path = "bitcoin" path = "bitcoin"
@ -9,3 +9,6 @@ path = "hashes"
[patch.crates-io.bitcoin-internals] [patch.crates-io.bitcoin-internals]
path = "internals" path = "internals"
[patch.crates-io.bitcoin-io]
path = "io"

View File

@ -26,8 +26,8 @@ bitcoinconsensus-std = ["bitcoinconsensus/std", "std"]
# The no-std feature doesn't disable std - you need to turn off the std feature for that by disabling default. # 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. # Instead no-std enables additional features required for this crate to be usable without std.
# As a result, both can be enabled without conflict. # As a result, both can be enabled without conflict.
std = ["secp256k1/std", "hashes/std", "bech32/std", "internals/std", "hex/std"] std = ["secp256k1/std", "bitcoin-io/std", "hashes/std", "bech32/std", "internals/std", "hex/std"]
no-std = ["core2", "hashes/alloc", "hashes/core2", "bech32/alloc", "secp256k1/alloc", "hex/alloc", "hex/core2"] no-std = ["hashes/alloc", "hashes/core2", "bitcoin-io/core2", "bitcoin-io/alloc", "bech32/alloc", "secp256k1/alloc", "hex/alloc", "hex/core2"]
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true all-features = true
@ -41,12 +41,12 @@ hashes = { package = "bitcoin_hashes", version = "0.13.0", default-features = fa
secp256k1 = { version = "0.28.0", default-features = false, features = ["hashes"] } secp256k1 = { version = "0.28.0", default-features = false, features = ["hashes"] }
hex_lit = "0.1.1" hex_lit = "0.1.1"
bitcoin-io = { version = "0.1", default-features = false }
base64 = { version = "0.21.3", optional = true } base64 = { version = "0.21.3", optional = true }
# Only use this feature for no-std builds, otherwise use bitcoinconsensus-std. # Only use this feature for no-std builds, otherwise use bitcoinconsensus-std.
bitcoinconsensus = { version = "0.20.2-0.5.0", default-features = false, optional = true } bitcoinconsensus = { version = "0.20.2-0.5.0", default-features = false, optional = true }
# There is no reason to use this dependency directly, it is activated by the "no-std" feature.
core2 = { version = "0.3.2", default-features = false, features = ["alloc"], optional = true }
# Do NOT use this as a feature! Use the `serde` feature instead. # Do NOT use this as a feature! Use the `serde` feature instead.
actual-serde = { package = "serde", version = "1.0.103", default-features = false, features = [ "derive", "alloc" ], optional = true } actual-serde = { package = "serde", version = "1.0.103", default-features = false, features = [ "derive", "alloc" ], optional = true }

View File

@ -17,7 +17,6 @@ panic-halt = "0.2.0"
alloc-cortex-m = "0.4.1" alloc-cortex-m = "0.4.1"
bitcoin = { path="../", default-features = false, features = ["no-std", "secp-lowmemory"] } bitcoin = { path="../", default-features = false, features = ["no-std", "secp-lowmemory"] }
[[bin]] [[bin]]
name = "embedded" name = "embedded"
test = false test = false
@ -33,3 +32,6 @@ path = "../../hashes"
[patch.crates-io.bitcoin-internals] [patch.crates-io.bitcoin-internals]
path = "../../internals" path = "../../internals"
[patch.crates-io.bitcoin-io]
path = "../../io"

View File

@ -23,7 +23,7 @@
//! deserialization. //! deserialization.
//! * `secp-lowmemory` - optimizations for low-memory devices. //! * `secp-lowmemory` - optimizations for low-memory devices.
//! * `no-std` - enables additional features required for this crate to be usable //! * `no-std` - enables additional features required for this crate to be usable
//! without std. Does **not** disable `std`. Depends on `core2`. //! without std. Does **not** disable `std`.
//! * `bitcoinconsensus-std` - enables `std` in `bitcoinconsensus` and communicates it //! * `bitcoinconsensus-std` - enables `std` in `bitcoinconsensus` and communicates it
//! to this crate so it knows how to implement //! to this crate so it knows how to implement
//! `std::error::Error`. At this time there's a hack to //! `std::error::Error`. At this time there's a hack to
@ -68,9 +68,6 @@ pub extern crate bech32;
/// Bitcoin's libbitcoinconsensus with Rust binding. /// Bitcoin's libbitcoinconsensus with Rust binding.
pub extern crate bitcoinconsensus; pub extern crate bitcoinconsensus;
#[cfg(not(feature = "std"))]
extern crate core2;
/// Rust implementation of cryptographic hash function algorithems. /// Rust implementation of cryptographic hash function algorithems.
pub extern crate hashes; pub extern crate hashes;
@ -118,16 +115,8 @@ pub mod taproot;
// May depend on crate features and we don't want to bother with it // May depend on crate features and we don't want to bother with it
#[allow(unused)] #[allow(unused)]
#[cfg(feature = "std")] use bitcoin_io::error::Error as StdError;
use std::error::Error as StdError; use bitcoin_io::io;
#[cfg(feature = "std")]
use std::io;
#[allow(unused)]
#[cfg(not(feature = "std"))]
use core2::error::Error as StdError;
#[cfg(not(feature = "std"))]
use core2::io;
pub use crate::address::{Address, AddressType}; pub use crate::address::{Address, AddressType};
pub use crate::amount::{Amount, Denomination, SignedAmount}; pub use crate::amount::{Amount, Denomination, SignedAmount};
@ -163,6 +152,8 @@ pub use crate::taproot::{
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
mod io_extras { mod io_extras {
use crate::io;
/// A writer which will move data into the void. /// A writer which will move data into the void.
pub struct Sink { pub struct Sink {
_priv: (), _priv: (),
@ -171,12 +162,12 @@ mod io_extras {
/// Creates an instance of a writer which will successfully consume all data. /// Creates an instance of a writer which will successfully consume all data.
pub const fn sink() -> Sink { Sink { _priv: () } } pub const fn sink() -> Sink { Sink { _priv: () } }
impl core2::io::Write for Sink { impl io::Write for Sink {
#[inline] #[inline]
fn write(&mut self, buf: &[u8]) -> core2::io::Result<usize> { Ok(buf.len()) } fn write(&mut self, buf: &[u8]) -> io::Result<usize> { Ok(buf.len()) }
#[inline] #[inline]
fn flush(&mut self) -> core2::io::Result<()> { Ok(()) } fn flush(&mut self) -> io::Result<()> { Ok(()) }
} }
} }

View File

@ -14,11 +14,11 @@ exclude = ["tests", "contrib"]
[features] [features]
default = ["std"] default = ["std"]
std = ["alloc", "hex/std"] std = ["alloc", "hex/std", "bitcoin-io/std"]
alloc = ["hex/alloc"] alloc = ["hex/alloc"]
serde-std = ["serde/std"] serde-std = ["serde/std"]
# If you want I/O you must enable either "std" or "core2". # If you want I/O you must enable either "std" or "core2".
core2 = ["actual-core2", "hex/core2"] core2 = ["bitcoin-io/core2", "hex/core2"]
# Smaller (but slower) implementation of sha256, sha512 and ripemd160 # Smaller (but slower) implementation of sha256, sha512 and ripemd160
small-hash = [] small-hash = []
@ -29,13 +29,12 @@ rustdoc-args = ["--cfg", "docsrs"]
[dependencies] [dependencies]
hex = { package = "hex-conservative", version = "0.1.1", default-features = false } hex = { package = "hex-conservative", version = "0.1.1", default-features = false }
bitcoin-io = { version = "0.1", default-features = false, optional = true }
schemars = { version = "0.8.3", optional = true } schemars = { version = "0.8.3", optional = true }
# Only enable this if you explicitly do not want to use "std", otherwise enable "serde-std". # Only enable this if you explicitly do not want to use "std", otherwise enable "serde-std".
serde = { version = "1.0", default-features = false, optional = true } serde = { version = "1.0", default-features = false, optional = true }
# Do NOT use this feature! Use the "core2" feature instead.
actual-core2 = { package = "core2", version = "0.3.2", default-features = false, optional = true }
[dev-dependencies] [dev-dependencies]
serde_test = "1.0" serde_test = "1.0"
serde_json = "1.0" serde_json = "1.0"

View File

@ -19,7 +19,7 @@ cortex-m-semihosting = "0.3.3"
panic-halt = "0.2.0" panic-halt = "0.2.0"
alloc-cortex-m = { version = "0.4.1", optional = true } alloc-cortex-m = { version = "0.4.1", optional = true }
bitcoin_hashes = { path="../", default-features = false, features = ["core2"] } bitcoin_hashes = { path="../", default-features = false, features = ["core2"] }
core2 = { version = "0.3.0", default_features = false } bitcoin-io = { path = "../../io", default_features = false }
[[bin]] [[bin]]
name = "embedded" name = "embedded"
@ -33,3 +33,6 @@ lto = true # better optimizations
[patch.crates-io.bitcoin-internals] [patch.crates-io.bitcoin-internals]
path = "../../internals" path = "../../internals"
[patch.crates-io.bitcoin-io]
path = "../../io"

View File

@ -10,7 +10,7 @@ extern crate bitcoin_hashes;
#[cfg(feature = "alloc")] use alloc::string::ToString; #[cfg(feature = "alloc")] use alloc::string::ToString;
use bitcoin_hashes::{sha256, Hash, HashEngine}; use bitcoin_hashes::{sha256, Hash, HashEngine};
use core2::io::Write; use bitcoin_io::io::Write;
use core::str::FromStr; use core::str::FromStr;
use cortex_m_rt::entry; use cortex_m_rt::entry;
use cortex_m_semihosting::{debug, hprintln}; use cortex_m_semihosting::{debug, hprintln};

View File

@ -21,3 +21,6 @@ serde_json = "1.0"
[patch.crates-io.bitcoin-internals] [patch.crates-io.bitcoin-internals]
path = "../../../internals" path = "../../../internals"
[patch.crates-io.bitcoin-io]
path = "../../../io"

View File

@ -81,8 +81,6 @@
// Exclude clippy lints we don't think are valuable // Exclude clippy lints we don't think are valuable
#![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134 #![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134
#[cfg(all(not(test), not(feature = "std"), feature = "core2"))]
extern crate actual_core2 as core2;
#[cfg(all(feature = "alloc", not(feature = "std")))] #[cfg(all(feature = "alloc", not(feature = "std")))]
extern crate alloc; extern crate alloc;
#[cfg(any(test, feature = "std"))] #[cfg(any(test, feature = "std"))]
@ -119,7 +117,7 @@ pub mod serde_macros;
pub mod cmp; pub mod cmp;
pub mod hash160; pub mod hash160;
pub mod hmac; pub mod hmac;
#[cfg(any(test, feature = "std", feature = "core2"))] #[cfg(feature = "bitcoin-io")]
mod impls; mod impls;
pub mod ripemd160; pub mod ripemd160;
pub mod sha1; pub mod sha1;
@ -131,12 +129,10 @@ pub mod sha512_256;
pub mod siphash24; pub mod siphash24;
use core::{borrow, fmt, hash, ops}; use core::{borrow, fmt, hash, ops};
// You get I/O if you enable "std" or "core2" (as well as during testing).
#[cfg(any(test, feature = "std"))]
use std::io;
#[cfg(all(not(test), not(feature = "std"), feature = "core2"))] // You get I/O if you enable "std" or "core2" (as well as during testing).
use core2::io; #[cfg(feature = "bitcoin-io")]
use bitcoin_io::io;
pub use hmac::{Hmac, HmacEngine}; pub use hmac::{Hmac, HmacEngine};
/// A hashing engine which bytes can be serialized into. /// A hashing engine which bytes can be serialized into.

25
io/Cargo.toml Normal file
View File

@ -0,0 +1,25 @@
[package]
name = "bitcoin-io"
version = "0.1.0"
authors = ["Matt Corallo <birchneutea@mattcorallo.com>"]
license = "CC0-1.0"
repository = "https://github.com/rust-bitcoin/rust-bitcoin"
documentation = "https://docs.rs/bitcoin-io/"
description = "Simple I/O traits for no-std (and std) environments"
categories = ["no-std"]
keywords = [ "io", "no-std" ]
readme = "README.md"
edition = "2018"
exclude = ["tests", "contrib"]
[features]
default = ["std"]
std = []
alloc = ["core2/alloc"]
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
core2 = { version = "0.3", default-features = false, optional = true }

32
io/src/lib.rs Normal file
View File

@ -0,0 +1,32 @@
//! Rust-Bitcoin IO Library
//!
//! Because the core `std::io` module is not yet exposed in `no-std` Rust, building `no-std`
//! applications which require reading and writing objects via standard traits is not generally
//! possible. While there is ongoing work to improve this situation, this module is not likely to
//! be available for applications with broad rustc version support for some time.
//!
//! Thus, this library exists to export a minmal version of `std::io`'s traits which `no-std`
//! applications may need. With the `std` feature, these traits are also implemented for the
//! `std::io` traits, allowing standard objects to be used wherever the traits from this crate are
//! required.
//!
//! This traits are not one-for-one drop-ins, but are as close as possible while still implementing
//! `std::io`'s traits without unnecessary complexity.
// Experimental features we need.
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(all(not(feature = "std"), not(feature = "core2")))]
compile_error!("At least one of std or core2 must be enabled");
#[cfg(feature = "std")]
pub use std::io;
#[cfg(feature = "std")]
pub use std::error;
#[cfg(not(feature = "std"))]
pub use core2::io;
#[cfg(not(feature = "std"))]
pub use core2::error;