|
| 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