1#![doc = "Strongly typed, allocation-free Handshake protocol values."]
2
3use core::{cmp::Ordering, fmt};
4
5use thiserror::Error;
6
7macro_rules! semantic_bytes {
8 ($name:ident, $size:expr) => {
9 #[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
10 pub struct $name([u8; $size]);
11
12 impl Default for $name {
13 fn default() -> Self {
14 Self([0; $size])
15 }
16 }
17
18 impl $name {
19 pub const LENGTH: usize = $size;
20
21 pub const fn new(bytes: [u8; $size]) -> Self {
22 Self(bytes)
23 }
24
25 pub const fn into_bytes(self) -> [u8; $size] {
26 self.0
27 }
28
29 pub const fn as_bytes(&self) -> &[u8; $size] {
30 &self.0
31 }
32
33 pub fn from_hex(value: &str) -> Result<Self, HexValueError> {
34 let mut bytes = [0_u8; $size];
35 hex::decode_to_slice(value, &mut bytes).map_err(|_| HexValueError::InvalidHex {
36 expected_bytes: $size,
37 })?;
38 Ok(Self(bytes))
39 }
40 }
41
42 impl fmt::Debug for $name {
43 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
44 write!(formatter, "{}({})", stringify!($name), hex::encode(self.0))
45 }
46 }
47
48 impl fmt::Display for $name {
49 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
50 formatter.write_str(&hex::encode(self.0))
51 }
52 }
53
54 impl From<[u8; $size]> for $name {
55 fn from(bytes: [u8; $size]) -> Self {
56 Self(bytes)
57 }
58 }
59 };
60}
61
62macro_rules! semantic_integer {
63 ($name:ident, $inner:ty) => {
64 #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
65 pub struct $name($inner);
66
67 impl $name {
68 pub const fn new(value: $inner) -> Self {
69 Self(value)
70 }
71
72 pub const fn get(self) -> $inner {
73 self.0
74 }
75 }
76
77 impl From<$inner> for $name {
78 fn from(value: $inner) -> Self {
79 Self(value)
80 }
81 }
82 };
83}
84
85#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
86pub enum HexValueError {
87 #[error("invalid hexadecimal value; expected exactly {expected_bytes} bytes")]
88 InvalidHex { expected_bytes: usize },
89}
90
91semantic_bytes!(BlockHash, 32);
92semantic_bytes!(TransactionHash, 32);
93semantic_bytes!(NameHash, 32);
94semantic_bytes!(TreeRoot, 32);
95semantic_bytes!(MerkleRoot, 32);
96semantic_bytes!(WitnessRoot, 32);
97semantic_bytes!(ReservedRoot, 32);
98semantic_bytes!(PowMask, 32);
99semantic_bytes!(ShareHash, 32);
100semantic_bytes!(PowHash, 32);
101semantic_bytes!(ScriptHash, 32);
102semantic_bytes!(OfferId, 32);
103semantic_bytes!(PeerIdentity, 33);
104semantic_bytes!(RegistryFingerprint, 32);
105
106#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
113pub struct Outpoint {
114 pub transaction_hash: TransactionHash,
117 pub index: u32,
119}
120
121impl Outpoint {
122 pub const NULL: Self = Self {
123 transaction_hash: TransactionHash::new([0; 32]),
124 index: u32::MAX,
125 };
126
127 pub fn is_null(self) -> bool {
129 self.index == u32::MAX && self.transaction_hash.into_bytes() == [0; 32]
130 }
131
132 pub fn encode(self) -> [u8; 36] {
137 let mut encoded = [0_u8; 36];
138 encoded[..32].copy_from_slice(self.transaction_hash.as_bytes());
139 encoded[32..].copy_from_slice(&self.index.to_le_bytes());
140 encoded
141 }
142}
143
144impl Default for Outpoint {
145 fn default() -> Self {
146 Self::NULL
147 }
148}
149
150semantic_integer!(Height, u32);
151semantic_integer!(BlockTime, u64);
152semantic_integer!(Dollarydoos, u64);
153semantic_integer!(CompactTarget, u32);
154semantic_integer!(RequestId, u64);
155semantic_integer!(EventSequence, u64);
156semantic_integer!(PolicyGeneration, u64);
157
158impl RequestId {
159 pub const fn is_valid(self) -> bool {
160 self.0 != 0
161 }
162}
163
164#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
165pub enum ArithmeticError {
166 #[error("numeric overflow")]
167 Overflow,
168 #[error("numeric underflow")]
169 Underflow,
170}
171
172impl Dollarydoos {
173 pub fn checked_add(self, other: Self) -> Result<Self, ArithmeticError> {
174 self.0
175 .checked_add(other.0)
176 .map(Self)
177 .ok_or(ArithmeticError::Overflow)
178 }
179
180 pub fn checked_sub(self, other: Self) -> Result<Self, ArithmeticError> {
181 self.0
182 .checked_sub(other.0)
183 .map(Self)
184 .ok_or(ArithmeticError::Underflow)
185 }
186}
187
188#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
190pub struct Chainwork([u64; 4]);
191
192impl Chainwork {
193 pub const ZERO: Self = Self([0; 4]);
194
195 pub const fn from_limbs_le(limbs: [u64; 4]) -> Self {
196 Self(limbs)
197 }
198
199 pub const fn limbs_le(self) -> [u64; 4] {
200 self.0
201 }
202
203 pub fn from_be_bytes(bytes: [u8; 32]) -> Self {
204 let mut limbs = [0_u64; 4];
205 for (index, limb) in limbs.iter_mut().rev().enumerate() {
206 let start = index * 8;
207 *limb = u64::from_be_bytes(
208 bytes[start..start + 8]
209 .try_into()
210 .expect("eight-byte chunk"),
211 );
212 }
213 Self(limbs)
214 }
215
216 pub fn to_be_bytes(self) -> [u8; 32] {
217 let mut bytes = [0_u8; 32];
218 for (index, limb) in self.0.iter().rev().enumerate() {
219 let start = index * 8;
220 bytes[start..start + 8].copy_from_slice(&limb.to_be_bytes());
221 }
222 bytes
223 }
224
225 pub fn checked_add(self, other: Self) -> Result<Self, ArithmeticError> {
226 let mut output = [0_u64; 4];
227 let mut carry = false;
228 for (index, output_limb) in output.iter_mut().enumerate() {
229 let (sum, first_carry) = self.0[index].overflowing_add(other.0[index]);
230 let (sum, second_carry) = sum.overflowing_add(u64::from(carry));
231 *output_limb = sum;
232 carry = first_carry || second_carry;
233 }
234 if carry {
235 Err(ArithmeticError::Overflow)
236 } else {
237 Ok(Self(output))
238 }
239 }
240
241 pub fn checked_sub(self, other: Self) -> Result<Self, ArithmeticError> {
242 if self < other {
243 return Err(ArithmeticError::Underflow);
244 }
245 let mut output = [0_u64; 4];
246 let mut borrow = false;
247 for (index, output_limb) in output.iter_mut().enumerate() {
248 let (difference, first_borrow) = self.0[index].overflowing_sub(other.0[index]);
249 let (difference, second_borrow) = difference.overflowing_sub(u64::from(borrow));
250 *output_limb = difference;
251 borrow = first_borrow || second_borrow;
252 }
253 debug_assert!(!borrow);
254 Ok(Self(output))
255 }
256
257 pub fn checked_mul_u64(self, multiplier: u64) -> Result<Self, ArithmeticError> {
258 let mut output = [0_u64; 4];
259 let mut carry = 0_u128;
260 for (index, output_limb) in output.iter_mut().enumerate() {
261 let product = u128::from(self.0[index]) * u128::from(multiplier) + carry;
262 *output_limb = product as u64;
263 carry = product >> 64;
264 }
265 if carry == 0 {
266 Ok(Self(output))
267 } else {
268 Err(ArithmeticError::Overflow)
269 }
270 }
271
272 pub fn checked_div_u64(self, divisor: u64) -> Result<Self, ArithmeticError> {
273 if divisor == 0 {
274 return Err(ArithmeticError::Underflow);
275 }
276 let mut output = [0_u64; 4];
277 let mut remainder = 0_u128;
278 for index in (0..4).rev() {
279 let dividend = (remainder << 64) | u128::from(self.0[index]);
280 output[index] = (dividend / u128::from(divisor)) as u64;
281 remainder = dividend % u128::from(divisor);
282 }
283 Ok(Self(output))
284 }
285}
286
287impl Ord for Chainwork {
288 fn cmp(&self, other: &Self) -> Ordering {
289 for index in (0..4).rev() {
290 match self.0[index].cmp(&other.0[index]) {
291 Ordering::Equal => {}
292 ordering => return ordering,
293 }
294 }
295 Ordering::Equal
296 }
297}
298
299impl PartialOrd for Chainwork {
300 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
301 Some(self.cmp(other))
302 }
303}
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308
309 #[test]
310 fn semantic_hashes_do_not_interchange() {
311 let block = BlockHash::new([7; 32]);
312 let transaction = TransactionHash::new([7; 32]);
313 assert_eq!(block.as_bytes(), transaction.as_bytes());
314 assert_eq!(block.to_string().len(), 64);
315 }
316
317 #[test]
318 fn outpoint_null_and_fixed_transaction_encoding_are_exact() {
319 assert_eq!(Outpoint::default(), Outpoint::NULL);
320 assert!(Outpoint::NULL.is_null());
321 let outpoint = Outpoint {
322 transaction_hash: TransactionHash::new([0x42; 32]),
323 index: 0x1020_3040,
324 };
325 assert!(!outpoint.is_null());
326 assert_eq!(&outpoint.encode()[..32], &[0x42; 32]);
327 assert_eq!(&outpoint.encode()[32..], &[0x40, 0x30, 0x20, 0x10]);
328 }
329
330 #[test]
331 fn chainwork_addition_carries_and_detects_overflow() {
332 let left = Chainwork::from_limbs_le([u64::MAX, 1, 0, 0]);
333 let right = Chainwork::from_limbs_le([1, 2, 0, 0]);
334 assert_eq!(
335 left.checked_add(right).expect("fits").limbs_le(),
336 [0, 4, 0, 0]
337 );
338 assert_eq!(
339 Chainwork::from_limbs_le([u64::MAX; 4]).checked_add(right),
340 Err(ArithmeticError::Overflow)
341 );
342 }
343
344 #[test]
345 fn chainwork_numeric_order_and_checked_operations_are_256_bit() {
346 let lower = Chainwork::from_limbs_le([u64::MAX, 0, 0, 0]);
347 let higher = Chainwork::from_limbs_le([0, 1, 0, 0]);
348 assert!(higher > lower);
349 assert_eq!(
350 higher.checked_sub(lower).expect("fits").limbs_le(),
351 [1, 0, 0, 0]
352 );
353 assert_eq!(
354 Chainwork::from_limbs_le([u64::MAX, 0, 0, 0])
355 .checked_mul_u64(2)
356 .expect("fits")
357 .limbs_le(),
358 [u64::MAX - 1, 1, 0, 0]
359 );
360 assert_eq!(
361 Chainwork::from_limbs_le([0, 1, 0, 0])
362 .checked_div_u64(2)
363 .expect("fits")
364 .limbs_le(),
365 [1_u64 << 63, 0, 0, 0]
366 );
367 let bytes = [0x5a; 32];
368 assert_eq!(Chainwork::from_be_bytes(bytes).to_be_bytes(), bytes);
369 }
370
371 #[test]
372 fn amounts_never_use_floating_point() {
373 assert_eq!(
374 Dollarydoos::new(2)
375 .checked_add(Dollarydoos::new(3))
376 .expect("fits")
377 .get(),
378 5
379 );
380 assert_eq!(
381 Dollarydoos::new(0).checked_sub(Dollarydoos::new(1)),
382 Err(ArithmeticError::Underflow)
383 );
384 }
385}