Skip to content

Commit 20c50e3

Browse files
committed
Merge rust-bitcoin#4248: Alloc-free (AKA zero-copy) control block
9ea2e92 Don't use references to `TaprootMerkleBranchBuf` (Martin Habovstiak) c528f52 Change `Deref::Target` of `TaprootMerkleBranchBuf` (Martin Habovstiak) 04a4efb Introduce unsized `TaprootMerkleBranch` (Martin Habovstiak) 370c259 Add `as_mut_slice` to `TaprootMerkleBranchBuf` (Martin Habovstiak) 33d7565 Push `merkle_branch` module one level deeper. (Martin Habovstiak) 277045b Add `Buf` suffix to `TaprootMerkleBranch` (Martin Habovstiak) Pull request description: This implements a bunch of changes needed to make `ControlBlock` alloc-free. In particular, this allows constructing `Witness` without the intermediate allocation. It is also a step towards having `P2TrSpend` public. Closes rust-bitcoin#1614 This also intentionally does **not** address decoding of `ControlBlock` from `Witness` since I'm not sure about the API. Rationale for doing the `Buf` rename: while doing it with `Script` was very painful it shouldn't be here since it's not used that often and also we can just backport the first commit with deprecated type alias. I was thinking of having `TaprootMerkleBr` but it'd be inconsistent and the name is silly. (Also if anyone is wondering why I did this: I was too exhausted to do more important stuff but felt like doing something nice and easy like this.) ACKs for top commit: tcharding: ACK 9ea2e92 apoelstra: ACK 9ea2e92; successfully ran local tests Tree-SHA512: c5e3ea61d10fbe0cbce5e900943e3cef77a175a62043c500b3ff6df57a96f00692d80fb1c4dd75bca9a704201baab6ddfcc430b12c7ecabc43968198466fed9d
2 parents e0be90d + 9ea2e92 commit 20c50e3

File tree

5 files changed

+439
-52
lines changed

5 files changed

+439
-52
lines changed

bitcoin/src/blockdata/witness.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ use crate::prelude::Vec;
1414
#[cfg(doc)]
1515
use crate::script::ScriptExt as _;
1616
use crate::taproot::{
17-
self, LeafScript, LeafVersion, TAPROOT_ANNEX_PREFIX, TAPROOT_CONTROL_BASE_SIZE,
18-
TAPROOT_LEAF_MASK,
17+
self, ControlBlock, LeafScript, LeafVersion, TAPROOT_ANNEX_PREFIX, TAPROOT_CONTROL_BASE_SIZE,
18+
TAPROOT_LEAF_MASK, TaprootMerkleBranch,
1919
};
2020
use crate::Script;
2121

@@ -135,6 +135,15 @@ crate::internal_macros::define_extension_trait! {
135135
witness
136136
}
137137

138+
/// Finishes constructing the P2TR script spend witness by pushing the required items.
139+
fn push_p2tr_script_spend(&mut self, script: &Script, control_block: &ControlBlock<impl AsRef<TaprootMerkleBranch>>, annex: Option<&[u8]>) {
140+
self.push(script.as_bytes());
141+
self.push(&*control_block.encode_to_arrayvec());
142+
if let Some(annex) = annex {
143+
self.push(annex);
144+
}
145+
}
146+
138147
/// Pushes, as a new element on the witness, an ECDSA signature.
139148
///
140149
/// Pushes the DER encoded signature + sighash_type, requires an allocation.
Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
use core::borrow::{Borrow, BorrowMut};
2+
use internals::slice::SliceExt;
3+
4+
use super::{DecodeError, InvalidMerkleBranchSizeError, InvalidMerkleTreeDepthError, TaprootMerkleBranchBuf, TapNodeHash, TAPROOT_CONTROL_MAX_NODE_COUNT, TAPROOT_CONTROL_NODE_SIZE};
5+
6+
pub use privacy_boundary::TaprootMerkleBranch;
7+
8+
/// Makes sure only the allowed conversions are accessible to external code.
9+
mod privacy_boundary {
10+
use super::*;
11+
12+
/// The Merkle proof for inclusion of a tree in a Taproot tree hash.
13+
#[repr(transparent)]
14+
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
15+
pub struct TaprootMerkleBranch([TapNodeHash]);
16+
17+
impl TaprootMerkleBranch {
18+
/// Returns a reference to the slice of hashes.
19+
#[inline]
20+
pub const fn as_slice(&self) -> &[TapNodeHash] { &self.0 }
21+
22+
/// Returns a reference to the mutable slice of hashes.
23+
#[inline]
24+
pub fn as_mut_slice(&mut self) -> &mut [TapNodeHash] { &mut self.0 }
25+
26+
pub(super) const fn from_hashes_unchecked(hashes: &[TapNodeHash]) -> &Self {
27+
unsafe {
28+
&*(hashes as *const _ as *const Self)
29+
}
30+
}
31+
32+
pub(super) fn from_mut_hashes_unchecked(hashes: &mut [TapNodeHash]) -> &mut Self {
33+
unsafe {
34+
&mut *(hashes as *mut _ as *mut Self)
35+
}
36+
}
37+
}
38+
}
39+
40+
impl TaprootMerkleBranch {
41+
/// Returns an empty branch.
42+
pub const fn new() -> &'static Self {
43+
Self::from_hashes_unchecked(&[])
44+
}
45+
46+
/// Returns the number of nodes in this Merkle proof.
47+
#[inline]
48+
pub fn len(&self) -> usize { self.as_slice().len() }
49+
50+
/// Checks if this Merkle proof is empty.
51+
#[inline]
52+
pub fn is_empty(&self) -> bool { self.as_slice().is_empty() }
53+
54+
/// Creates an iterator over the node hashes.
55+
#[inline]
56+
pub fn iter(&self) -> core::slice::Iter<'_, TapNodeHash> { self.into_iter() }
57+
58+
/// Creates an iterator over the mutable node hashes.
59+
#[inline]
60+
pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, TapNodeHash> { self.into_iter() }
61+
62+
/// Casts `TaprootMerkleBranch` to a byte slice.
63+
pub(crate) fn as_bytes(&self) -> &[u8] {
64+
let ptr = self.as_slice().as_ptr();
65+
let num_bytes = self.len() * TAPROOT_CONTROL_NODE_SIZE;
66+
// SAFETY:
67+
// The pointer points to memory that's borrowed and the returned slice has the same
68+
// lifetime. The alignment is of the types is the same (as checked in the test), the
69+
// length is within the bounds - as computed above by multiplication.
70+
unsafe { core::slice::from_raw_parts(ptr.cast::<u8>(), num_bytes) }
71+
}
72+
73+
/// Serializes to a writer.
74+
///
75+
/// # Returns
76+
///
77+
/// The number of bytes written to the writer.
78+
pub fn encode<Write: io::Write + ?Sized>(&self, writer: &mut Write) -> io::Result<usize> {
79+
let bytes = self.as_bytes();
80+
writer.write_all(bytes)?;
81+
Ok(bytes.len())
82+
}
83+
84+
/// Zero-copy decodes `bytes` as Taproot Merkle branch.
85+
///
86+
/// Note that "decoding" is quite trivial: it only performs appropriate bound checks and casts
87+
/// the reference.
88+
pub fn decode(bytes: &[u8]) -> Result<&Self, DecodeError> {
89+
let (nodes, remainder) = bytes.bitcoin_as_chunks();
90+
if remainder.is_empty() {
91+
Self::decode_exact(nodes).map_err(Into::into)
92+
} else {
93+
Err(InvalidMerkleBranchSizeError(bytes.len()).into())
94+
}
95+
}
96+
97+
/// Decodes a byte slice that is statically known to be multiple of 32.
98+
///
99+
/// This can be used as a building block for other ways of decoding.
100+
fn decode_exact(nodes: &[[u8; TAPROOT_CONTROL_NODE_SIZE]]) -> Result<&Self, InvalidMerkleTreeDepthError> {
101+
// SAFETY:
102+
// The lifetime of the returned reference is the same as the lifetime of the input
103+
// reference, the size of `TapNodeHash` is equal to `TAPROOT_CONTROL_NODE_SIZE` and the
104+
// alignment of `TapNodeHash` is equal to the alignment of `u8` (see tests below).
105+
Self::from_hashes(unsafe { &*(nodes as *const _ as *const [TapNodeHash]) })
106+
}
107+
108+
fn from_hashes(nodes: &[TapNodeHash]) -> Result<&Self, InvalidMerkleTreeDepthError>{
109+
if nodes.len() <= TAPROOT_CONTROL_MAX_NODE_COUNT {
110+
Ok(Self::from_hashes_unchecked(nodes))
111+
} else {
112+
Err(InvalidMerkleTreeDepthError(nodes.len()))
113+
}
114+
}
115+
}
116+
117+
impl Default for &'_ TaprootMerkleBranch {
118+
fn default() -> Self {
119+
TaprootMerkleBranch::new()
120+
}
121+
}
122+
123+
impl AsRef<TaprootMerkleBranch> for TaprootMerkleBranch {
124+
fn as_ref(&self) -> &TaprootMerkleBranch {
125+
self
126+
}
127+
}
128+
129+
impl AsMut<TaprootMerkleBranch> for TaprootMerkleBranch {
130+
fn as_mut(&mut self) -> &mut TaprootMerkleBranch {
131+
self
132+
}
133+
}
134+
135+
impl AsRef<TaprootMerkleBranch> for TaprootMerkleBranchBuf {
136+
fn as_ref(&self) -> &TaprootMerkleBranch {
137+
// TaprootMerkleBranchBuf maintains the invariant that the node count is in range.
138+
TaprootMerkleBranch::from_hashes_unchecked(self.as_slice())
139+
}
140+
}
141+
142+
impl AsMut<TaprootMerkleBranch> for TaprootMerkleBranchBuf {
143+
fn as_mut(&mut self) -> &mut TaprootMerkleBranch {
144+
// TaprootMerkleBranchBuf maintains the invariant that the node count is in range.
145+
TaprootMerkleBranch::from_mut_hashes_unchecked(self.as_mut_slice())
146+
}
147+
}
148+
149+
impl Borrow<TaprootMerkleBranch> for TaprootMerkleBranchBuf {
150+
#[inline]
151+
fn borrow(&self) -> &TaprootMerkleBranch { self.as_ref() }
152+
}
153+
154+
impl BorrowMut<TaprootMerkleBranch> for TaprootMerkleBranchBuf {
155+
#[inline]
156+
fn borrow_mut(&mut self) -> &mut TaprootMerkleBranch { self.as_mut() }
157+
}
158+
159+
impl<'a> TryFrom<&'a [TapNodeHash]> for &'a TaprootMerkleBranch {
160+
type Error = InvalidMerkleTreeDepthError;
161+
162+
fn try_from(value: &'a [TapNodeHash]) -> Result<Self, Self::Error> {
163+
TaprootMerkleBranch::from_hashes(value)
164+
}
165+
}
166+
167+
macro_rules! impl_from_array {
168+
($($len:expr),* $(,)?) => {
169+
$(
170+
impl AsRef<TaprootMerkleBranch> for [TapNodeHash; $len] {
171+
fn as_ref(&self) -> &TaprootMerkleBranch {
172+
#[allow(unused_comparisons)]
173+
const _: () = { assert!($len <= TAPROOT_CONTROL_MAX_NODE_COUNT) };
174+
// There's a static check to ensure correct macro usage above.
175+
TaprootMerkleBranch::from_hashes_unchecked(self)
176+
}
177+
}
178+
179+
impl AsMut<TaprootMerkleBranch> for [TapNodeHash; $len] {
180+
fn as_mut(&mut self) -> &mut TaprootMerkleBranch {
181+
#[allow(unused_comparisons)]
182+
const _: () = { assert!($len <= TAPROOT_CONTROL_MAX_NODE_COUNT) };
183+
// There's a static check to ensure correct macro usage above.
184+
TaprootMerkleBranch::from_mut_hashes_unchecked(self)
185+
}
186+
}
187+
188+
impl Borrow<TaprootMerkleBranch> for [TapNodeHash; $len] {
189+
fn borrow(&self) -> &TaprootMerkleBranch {
190+
self.as_ref()
191+
}
192+
}
193+
194+
impl BorrowMut<TaprootMerkleBranch> for [TapNodeHash; $len] {
195+
fn borrow_mut(&mut self) -> &mut TaprootMerkleBranch {
196+
self.as_mut()
197+
}
198+
}
199+
200+
impl<'a> From<&'a [TapNodeHash; $len]> for &'a TaprootMerkleBranch {
201+
#[inline]
202+
fn from(branch: &'a [TapNodeHash; $len]) -> Self {
203+
branch.as_ref()
204+
}
205+
}
206+
207+
impl<'a> From<&'a mut [TapNodeHash; $len]> for &'a mut TaprootMerkleBranch {
208+
#[inline]
209+
fn from(branch: &'a mut [TapNodeHash; $len]) -> Self {
210+
branch.as_mut()
211+
}
212+
}
213+
)*
214+
}
215+
}
216+
217+
// Implement for all values [0, 128] inclusive.
218+
//
219+
// The reason zero is included is that `TaprootMerkleBranchBuf` doesn't contain the hash of the node
220+
// that's being proven - it's not needed because the script is already right before control block.
221+
impl_from_array!(
222+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
223+
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
224+
50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
225+
74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
226+
98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
227+
117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128
228+
);
229+
230+
impl AsRef<[TapNodeHash]> for TaprootMerkleBranch {
231+
#[inline]
232+
fn as_ref(&self) -> &[TapNodeHash] { self.as_slice() }
233+
}
234+
235+
impl AsMut<[TapNodeHash]> for TaprootMerkleBranch {
236+
#[inline]
237+
fn as_mut(&mut self) -> &mut [TapNodeHash] { self.as_mut_slice() }
238+
}
239+
240+
impl Borrow<[TapNodeHash]> for TaprootMerkleBranch {
241+
#[inline]
242+
fn borrow(&self) -> &[TapNodeHash] { self.as_ref() }
243+
}
244+
245+
impl BorrowMut<[TapNodeHash]> for TaprootMerkleBranch {
246+
#[inline]
247+
fn borrow_mut(&mut self) -> &mut [TapNodeHash] { self.as_mut() }
248+
}
249+
250+
impl alloc::borrow::ToOwned for TaprootMerkleBranch {
251+
// It could be argued that this should've been a stack-allocated type.
252+
// However such type would be huge and this trait interacts with `Cow`.
253+
// If someone wants to pass it around they're better off just always copying rather than using
254+
// `Cow`.
255+
type Owned = TaprootMerkleBranchBuf;
256+
257+
fn to_owned(&self) -> Self::Owned {
258+
self.into()
259+
}
260+
}
261+
262+
impl<'a> IntoIterator for &'a TaprootMerkleBranch {
263+
type IntoIter = core::slice::Iter<'a, TapNodeHash>;
264+
type Item = &'a TapNodeHash;
265+
266+
fn into_iter(self) -> Self::IntoIter {
267+
self.as_slice().iter()
268+
}
269+
}
270+
271+
impl<'a> IntoIterator for &'a mut TaprootMerkleBranch {
272+
type IntoIter = core::slice::IterMut<'a, TapNodeHash>;
273+
type Item = &'a mut TapNodeHash;
274+
275+
#[inline]
276+
fn into_iter(self) -> Self::IntoIter { self.as_mut_slice().iter_mut() }
277+
}
278+
279+
#[cfg(test)]
280+
mod tests {
281+
#[test]
282+
fn alignment() {
283+
assert!(core::mem::align_of_val(super::TaprootMerkleBranch::new()) == core::mem::align_of::<u8>());
284+
}
285+
286+
const _: () = {
287+
assert!(core::mem::size_of::<super::TapNodeHash>() == super::TAPROOT_CONTROL_NODE_SIZE);
288+
assert!(core::mem::align_of::<super::TapNodeHash>() == core::mem::align_of::<u8>());
289+
};
290+
}

0 commit comments

Comments
 (0)