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
106semantic_integer!(Height, u32);
107semantic_integer!(BlockTime, u64);
108semantic_integer!(Dollarydoos, u64);
109semantic_integer!(CompactTarget, u32);
110semantic_integer!(RequestId, u64);
111semantic_integer!(EventSequence, u64);
112semantic_integer!(PolicyGeneration, u64);
113
114impl RequestId {
115 pub const fn is_valid(self) -> bool {
116 self.0 != 0
117 }
118}
119
120#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
121pub enum ArithmeticError {
122 #[error("numeric overflow")]
123 Overflow,
124 #[error("numeric underflow")]
125 Underflow,
126}
127
128impl Dollarydoos {
129 pub fn checked_add(self, other: Self) -> Result<Self, ArithmeticError> {
130 self.0
131 .checked_add(other.0)
132 .map(Self)
133 .ok_or(ArithmeticError::Overflow)
134 }
135
136 pub fn checked_sub(self, other: Self) -> Result<Self, ArithmeticError> {
137 self.0
138 .checked_sub(other.0)
139 .map(Self)
140 .ok_or(ArithmeticError::Underflow)
141 }
142}
143
144#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
146pub struct Chainwork([u64; 4]);
147
148impl Chainwork {
149 pub const ZERO: Self = Self([0; 4]);
150
151 pub const fn from_limbs_le(limbs: [u64; 4]) -> Self {
152 Self(limbs)
153 }
154
155 pub const fn limbs_le(self) -> [u64; 4] {
156 self.0
157 }
158
159 pub fn from_be_bytes(bytes: [u8; 32]) -> Self {
160 let mut limbs = [0_u64; 4];
161 for (index, limb) in limbs.iter_mut().rev().enumerate() {
162 let start = index * 8;
163 *limb = u64::from_be_bytes(
164 bytes[start..start + 8]
165 .try_into()
166 .expect("eight-byte chunk"),
167 );
168 }
169 Self(limbs)
170 }
171
172 pub fn to_be_bytes(self) -> [u8; 32] {
173 let mut bytes = [0_u8; 32];
174 for (index, limb) in self.0.iter().rev().enumerate() {
175 let start = index * 8;
176 bytes[start..start + 8].copy_from_slice(&limb.to_be_bytes());
177 }
178 bytes
179 }
180
181 pub fn checked_add(self, other: Self) -> Result<Self, ArithmeticError> {
182 let mut output = [0_u64; 4];
183 let mut carry = false;
184 for (index, output_limb) in output.iter_mut().enumerate() {
185 let (sum, first_carry) = self.0[index].overflowing_add(other.0[index]);
186 let (sum, second_carry) = sum.overflowing_add(u64::from(carry));
187 *output_limb = sum;
188 carry = first_carry || second_carry;
189 }
190 if carry {
191 Err(ArithmeticError::Overflow)
192 } else {
193 Ok(Self(output))
194 }
195 }
196
197 pub fn checked_sub(self, other: Self) -> Result<Self, ArithmeticError> {
198 if self < other {
199 return Err(ArithmeticError::Underflow);
200 }
201 let mut output = [0_u64; 4];
202 let mut borrow = false;
203 for (index, output_limb) in output.iter_mut().enumerate() {
204 let (difference, first_borrow) = self.0[index].overflowing_sub(other.0[index]);
205 let (difference, second_borrow) = difference.overflowing_sub(u64::from(borrow));
206 *output_limb = difference;
207 borrow = first_borrow || second_borrow;
208 }
209 debug_assert!(!borrow);
210 Ok(Self(output))
211 }
212
213 pub fn checked_mul_u64(self, multiplier: u64) -> Result<Self, ArithmeticError> {
214 let mut output = [0_u64; 4];
215 let mut carry = 0_u128;
216 for (index, output_limb) in output.iter_mut().enumerate() {
217 let product = u128::from(self.0[index]) * u128::from(multiplier) + carry;
218 *output_limb = product as u64;
219 carry = product >> 64;
220 }
221 if carry == 0 {
222 Ok(Self(output))
223 } else {
224 Err(ArithmeticError::Overflow)
225 }
226 }
227
228 pub fn checked_div_u64(self, divisor: u64) -> Result<Self, ArithmeticError> {
229 if divisor == 0 {
230 return Err(ArithmeticError::Underflow);
231 }
232 let mut output = [0_u64; 4];
233 let mut remainder = 0_u128;
234 for index in (0..4).rev() {
235 let dividend = (remainder << 64) | u128::from(self.0[index]);
236 output[index] = (dividend / u128::from(divisor)) as u64;
237 remainder = dividend % u128::from(divisor);
238 }
239 Ok(Self(output))
240 }
241}
242
243impl Ord for Chainwork {
244 fn cmp(&self, other: &Self) -> Ordering {
245 for index in (0..4).rev() {
246 match self.0[index].cmp(&other.0[index]) {
247 Ordering::Equal => {}
248 ordering => return ordering,
249 }
250 }
251 Ordering::Equal
252 }
253}
254
255impl PartialOrd for Chainwork {
256 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
257 Some(self.cmp(other))
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264
265 #[test]
266 fn semantic_hashes_do_not_interchange() {
267 let block = BlockHash::new([7; 32]);
268 let transaction = TransactionHash::new([7; 32]);
269 assert_eq!(block.as_bytes(), transaction.as_bytes());
270 assert_eq!(block.to_string().len(), 64);
271 }
272
273 #[test]
274 fn chainwork_addition_carries_and_detects_overflow() {
275 let left = Chainwork::from_limbs_le([u64::MAX, 1, 0, 0]);
276 let right = Chainwork::from_limbs_le([1, 2, 0, 0]);
277 assert_eq!(
278 left.checked_add(right).expect("fits").limbs_le(),
279 [0, 4, 0, 0]
280 );
281 assert_eq!(
282 Chainwork::from_limbs_le([u64::MAX; 4]).checked_add(right),
283 Err(ArithmeticError::Overflow)
284 );
285 }
286
287 #[test]
288 fn chainwork_numeric_order_and_checked_operations_are_256_bit() {
289 let lower = Chainwork::from_limbs_le([u64::MAX, 0, 0, 0]);
290 let higher = Chainwork::from_limbs_le([0, 1, 0, 0]);
291 assert!(higher > lower);
292 assert_eq!(
293 higher.checked_sub(lower).expect("fits").limbs_le(),
294 [1, 0, 0, 0]
295 );
296 assert_eq!(
297 Chainwork::from_limbs_le([u64::MAX, 0, 0, 0])
298 .checked_mul_u64(2)
299 .expect("fits")
300 .limbs_le(),
301 [u64::MAX - 1, 1, 0, 0]
302 );
303 assert_eq!(
304 Chainwork::from_limbs_le([0, 1, 0, 0])
305 .checked_div_u64(2)
306 .expect("fits")
307 .limbs_le(),
308 [1_u64 << 63, 0, 0, 0]
309 );
310 let bytes = [0x5a; 32];
311 assert_eq!(Chainwork::from_be_bytes(bytes).to_be_bytes(), bytes);
312 }
313
314 #[test]
315 fn amounts_never_use_floating_point() {
316 assert_eq!(
317 Dollarydoos::new(2)
318 .checked_add(Dollarydoos::new(3))
319 .expect("fits")
320 .get(),
321 5
322 );
323 assert_eq!(
324 Dollarydoos::new(0).checked_sub(Dollarydoos::new(1)),
325 Err(ArithmeticError::Underflow)
326 );
327 }
328}