primitives: Remove arrayvec dependency
What changed, and why it matters
This commit removes a third-party dependency called `arrayvec` from the `bitcoin-primitives` crate and rewrites how a Bitcoin block header is printed as text. The old code built the text in a small fixed-size buffer from `arrayvec`; the new code streams the bytes directly through an internal formatting helper. There is no security fix here—this is a routine dependency-reduction refactor.
No security action required. Treat as a normal maintenance/refactor change. Reviewers may optionally verify that `fmt_hex_exact!` and `HeaderIter` preserve the same 160-character lower-case hex output as before, but the diff shows equivalent fields are encoded.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change drops the arrayvec crate from primitives/Cargo.toml and the lockfiles. The Display implementation for Header is converted from using arrayvec::ArrayString<160> plus manual write! of hex fragments to using the internal fmt_hex_exact! macro with a new HeaderIter wrapper around EncodableByteIter. The wrapper implements ExactSizeIterator so the macro can size the output exactly. The commit message frames this purely as minimizing external dependencies.
Changed components
primitives/src/block.rsprimitives/Cargo.tomlCargo-minimal.lockCargo-recent.lockInspect captured patch +24 / −20
diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock
index 847aea25..499b58e0 100644
--- a/Cargo-minimal.lock
+++ b/Cargo-minimal.lock
@@ -141,7 +141,6 @@ name = "bitcoin-primitives"
version = "1.0.0-rc.1"
dependencies = [
"arbitrary",
- "arrayvec",
"bincode",
"bitcoin-consensus-encoding",
"bitcoin-internals",
diff --git a/Cargo-recent.lock b/Cargo-recent.lock
index 045b2218..3c2594e6 100644
--- a/Cargo-recent.lock
+++ b/Cargo-recent.lock
@@ -140,7 +140,6 @@ name = "bitcoin-primitives"
version = "1.0.0-rc.1"
dependencies = [
"arbitrary",
- "arrayvec",
"bincode",
"bitcoin-consensus-encoding",
"bitcoin-internals",
diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml
index 0b280bbd..3ab99d60 100644
--- a/primitives/Cargo.toml
+++ b/primitives/Cargo.toml
@@ -14,7 +14,7 @@ exclude = ["tests", "contrib"]
[features]
default = ["std", "hex"]
-std = ["alloc", "hashes/std", "hex-stable?/std", "hex-unstable?/std", "internals/std", "units/std", "arrayvec/std"]
+std = ["alloc", "hashes/std", "hex-stable?/std", "hex-unstable?/std", "internals/std", "units/std"]
alloc = ["hashes/alloc", "hex-stable?/alloc", "hex-unstable?/alloc", "internals/alloc", "units/alloc"]
serde = ["dep:serde", "hashes/serde", "internals/serde", "units/serde", "alloc", "hex"]
arbitrary = ["dep:arbitrary", "units/arbitrary"]
@@ -25,7 +25,6 @@ encoding = { package = "bitcoin-consensus-encoding", path = "../consensus_encodi
hashes = { package = "bitcoin_hashes", path = "../hashes", version = "0.18.0", default-features = false }
internals = { package = "bitcoin-internals", path = "../internals", version = "0.4.1" }
units = { package = "bitcoin-units", path = "../units", version = "=1.0.0-rc.3", default-features = false, features = [ "encoding" ] }
-arrayvec = { version = "0.7.2", default-features = false }
arbitrary = { version = "1.4.1", optional = true }
hex-stable = { package = "hex-conservative", version = "1.0.0", default-features = false, optional = true }
diff --git a/primitives/src/block.rs b/primitives/src/block.rs
index 8bb15166..d2b65ff3 100644
--- a/primitives/src/block.rs
+++ b/primitives/src/block.rs
@@ -15,6 +15,8 @@ use core::marker::PhantomData;
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
use encoding::Encodable;
+#[cfg(feature = "hex")]
+use encoding::EncodableByteIter;
#[cfg(feature = "alloc")]
use encoding::{CompactSizeEncoder, Decodable, Decoder, Decoder2, Decoder6, Encoder2, SliceEncoder, VecDecoder};
use hashes::{sha256d, HashEngine as _};
@@ -491,23 +493,11 @@ impl Header {
#[cfg(feature = "hex")]
impl fmt::Display for Header {
+ #[allow(clippy::use_self)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- use fmt::Write as _;
- use hex_unstable::DisplayHex as _;
-
- let mut buf = arrayvec::ArrayString::<160>::new();
- write!(
- &mut buf,
- "{}{}{}{}{}{}",
- self.version.to_consensus().to_le_bytes().as_hex(),
- self.prev_blockhash.as_byte_array().as_hex(),
- self.merkle_root.as_byte_array().as_hex(),
- self.time.to_u32().to_le_bytes().as_hex(),
- self.bits.to_consensus().to_le_bytes().as_hex(),
- self.nonce.to_le_bytes().as_hex(),
- )
- .expect("total length of written objects is 160 characters");
- fmt::Display::fmt(&buf, f)
+ use hex_unstable::{fmt_hex_exact, Case};
+
+ fmt_hex_exact!(f, Header::SIZE, HeaderIter(EncodableByteIter::new(self)), Case::Lower)
}
}
@@ -525,6 +515,23 @@ impl fmt::Debug for Header {
}
}
+/// A wrapper around [`encoding::EncodableByteIter`]
+///
+/// This wrapper implements `ExactSizeIterator` for use with `fmt_hex_exact!`.
+#[cfg(feature = "hex")]
+struct HeaderIter<'a>(EncodableByteIter<'a, Header>);
+
+#[cfg(feature = "hex")]
+impl Iterator for HeaderIter<'_> {
+ type Item = u8;
+
+ fn next(&mut self) -> Option<Self::Item> { self.0.next() }
+
+ fn size_hint(&self) -> (usize, Option<usize>) { (Header::SIZE, Some(Header::SIZE)) }
+}
+#[cfg(feature = "hex")]
+impl ExactSizeIterator for HeaderIter<'_> {}
+
encoding::encoder_newtype! {
/// The encoder for the [`Header`] type.
pub struct HeaderEncoder(
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.