Use as_parts to encode Block<Unchecked> without cloning
What changed, and why it matters
This is a routine performance improvement: it removes an unnecessary data copy when encoding an unchecked Bitcoin block. There is no security issue visible in the change.
No security action needed; this is a benign optimization.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit adds Block<Unchecked>::as_parts() returning references to the header and transactions, then rewrites the Encodable implementation for Block<Unchecked> to encode those references directly instead of cloning the whole block, calling assume_checked(None), and delegating to the checked block’s encoder. The encoded bytes are identical; only an internal clone is removed. API snapshot files are updated to expose the new public method.
Changed components
primitives/src/block.rsbitcoin/src/blockdata/block.rsInspect captured patch +14 / −4
diff --git a/api/primitives/all-features.txt b/api/primitives/all-features.txt
index c1361632..df151532 100644
--- a/api/primitives/all-features.txt
+++ b/api/primitives/all-features.txt
@@ -1465,6 +1465,7 @@ pub fn bitcoin_primitives::block::Block<V>::fmt(&self, f: &mut core::fmt::Format
pub fn bitcoin_primitives::block::Block<bitcoin_primitives::block::Checked>::cached_witness_root(&self) -> core::option::Option<bitcoin_primitives::WitnessMerkleNode>
pub fn bitcoin_primitives::block::Block<bitcoin_primitives::block::Checked>::header(&self) -> &bitcoin_primitives::block::Header
pub fn bitcoin_primitives::block::Block<bitcoin_primitives::block::Checked>::transactions(&self) -> &[bitcoin_primitives::transaction::Transaction]
+pub fn bitcoin_primitives::block::Block<bitcoin_primitives::block::Unchecked>::as_parts(&self) -> (&bitcoin_primitives::block::Header, &[bitcoin_primitives::transaction::Transaction])
pub fn bitcoin_primitives::block::Block<bitcoin_primitives::block::Unchecked>::assume_checked(self, witness_root: core::option::Option<bitcoin_primitives::WitnessMerkleNode>) -> bitcoin_primitives::block::Block<bitcoin_primitives::block::Checked>
pub fn bitcoin_primitives::block::Block<bitcoin_primitives::block::Unchecked>::check_merkle_root(&self) -> bool
pub fn bitcoin_primitives::block::Block<bitcoin_primitives::block::Unchecked>::check_witness_commitment(&self) -> (bool, core::option::Option<bitcoin_primitives::WitnessMerkleNode>)
diff --git a/api/primitives/alloc-only.txt b/api/primitives/alloc-only.txt
index dc3dc482..c06410f8 100644
--- a/api/primitives/alloc-only.txt
+++ b/api/primitives/alloc-only.txt
@@ -1276,6 +1276,7 @@ pub fn bitcoin_primitives::block::Block<V>::fmt(&self, f: &mut core::fmt::Format
pub fn bitcoin_primitives::block::Block<bitcoin_primitives::block::Checked>::cached_witness_root(&self) -> core::option::Option<bitcoin_primitives::WitnessMerkleNode>
pub fn bitcoin_primitives::block::Block<bitcoin_primitives::block::Checked>::header(&self) -> &bitcoin_primitives::block::Header
pub fn bitcoin_primitives::block::Block<bitcoin_primitives::block::Checked>::transactions(&self) -> &[bitcoin_primitives::transaction::Transaction]
+pub fn bitcoin_primitives::block::Block<bitcoin_primitives::block::Unchecked>::as_parts(&self) -> (&bitcoin_primitives::block::Header, &[bitcoin_primitives::transaction::Transaction])
pub fn bitcoin_primitives::block::Block<bitcoin_primitives::block::Unchecked>::assume_checked(self, witness_root: core::option::Option<bitcoin_primitives::WitnessMerkleNode>) -> bitcoin_primitives::block::Block<bitcoin_primitives::block::Checked>
pub fn bitcoin_primitives::block::Block<bitcoin_primitives::block::Unchecked>::check_merkle_root(&self) -> bool
pub fn bitcoin_primitives::block::Block<bitcoin_primitives::block::Unchecked>::check_witness_commitment(&self) -> (bool, core::option::Option<bitcoin_primitives::WitnessMerkleNode>)
diff --git a/bitcoin/src/blockdata/block.rs b/bitcoin/src/blockdata/block.rs
index bc2ed355..7a6e1d4c 100644
--- a/bitcoin/src/blockdata/block.rs
+++ b/bitcoin/src/blockdata/block.rs
@@ -236,10 +236,14 @@ fn block_base_size(transactions: &[Transaction]) -> usize {
impl Encodable for Block<Unchecked> {
#[inline]
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
- // TODO: Should we be able to encode without cloning?
- // This is ok, we decode as unchecked anyway.
- let block = self.clone().assume_checked(None);
- block.consensus_encode(w)
+ let (header, transactions) = self.as_parts();
+ let mut len = 0;
+ len += header.consensus_encode(w)?;
+ len += w.emit_compact_size(transactions.len())?;
+ for tx in transactions.iter() {
+ len += tx.consensus_encode(w)?;
+ }
+ Ok(len)
}
}
diff --git a/primitives/src/block.rs b/primitives/src/block.rs
index 26a2a62e..c748cbbc 100644
--- a/primitives/src/block.rs
+++ b/primitives/src/block.rs
@@ -113,6 +113,10 @@ impl Block<Unchecked> {
#[inline]
pub fn into_parts(self) -> (Header, Vec<Transaction>) { (self.header, self.transactions) }
+ /// Returns the constituent parts of the block by reference.
+ #[inline]
+ pub fn as_parts(&self) -> (&Header, &[Transaction]) { (&self.header, &self.transactions) }
+
/// Validates (or checks) a block.
///
/// We define valid as:
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.