1#![no_std]
6#![deny(missing_docs)]
7#![deny(unsafe_code)]
8#![deny(rustdoc::broken_intra_doc_links)]
9
10#[cfg(test)]
11extern crate alloc;
12
13#[cfg(feature = "std")]
14extern crate std;
15
16use core::mem;
17
18use memuse::{self, DynamicUsage};
19use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
20
21pub mod arbitrary;
22pub mod fingerprint;
23pub mod hardened_only;
24pub mod registered;
25
26#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
31pub struct AccountId(u32);
32
33memuse::impl_no_dynamic_usage!(AccountId);
34
35impl TryFrom<u32> for AccountId {
36 type Error = TryFromIntError;
37
38 fn try_from(id: u32) -> Result<Self, Self::Error> {
39 if id < (1 << 31) {
42 Ok(Self(id))
43 } else {
44 Err(TryFromIntError(()))
45 }
46 }
47}
48
49impl From<AccountId> for u32 {
50 fn from(id: AccountId) -> Self {
51 id.0
52 }
53}
54
55impl From<AccountId> for ChildIndex {
56 fn from(id: AccountId) -> Self {
57 ChildIndex::hardened(id.0)
59 }
60}
61
62impl ConditionallySelectable for AccountId {
63 fn conditional_select(a0: &Self, a1: &Self, c: Choice) -> Self {
64 AccountId(u32::conditional_select(&a0.0, &a1.0, c))
65 }
66}
67
68impl AccountId {
69 pub const ZERO: Self = Self(0);
71
72 pub fn next(&self) -> Option<Self> {
74 Self::try_from(self.0 + 1).ok()
75 }
76
77 pub const fn const_from_u32(value: u32) -> Self {
82 if value < (1 << 31) {
83 Self(value)
84 } else {
85 panic!("Account IDs must be in the range 0..2^31");
86 }
87 }
88}
89
90#[derive(Clone, Copy, Debug)]
92pub struct TryFromIntError(());
93
94impl core::fmt::Display for TryFromIntError {
95 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
96 write!(f, "out of range integral type conversion attempted")
97 }
98}
99
100#[cfg(feature = "std")]
101impl std::error::Error for TryFromIntError {}
102
103pub(crate) fn with_ikm<F, T>(context_string: &[u8], seed: &[u8], f: F) -> T
106where
107 F: FnOnce(&[&[u8]]) -> T,
108{
109 let context_len =
110 u8::try_from(context_string.len()).expect("context string should be at most 252 bytes");
111 assert!((1..=252).contains(&context_len));
112
113 let seed_len = u8::try_from(seed.len()).expect("seed should be at most 252 bytes");
114 assert!((32..=252).contains(&seed_len));
115
116 let ikm = &[&[context_len], context_string, &[seed_len], seed];
117
118 f(ikm)
119}
120
121#[derive(Clone, Copy, Debug, PartialEq, Eq)]
127pub struct ChildIndex(u32);
128
129impl ConstantTimeEq for ChildIndex {
130 fn ct_eq(&self, other: &Self) -> Choice {
131 self.0.ct_eq(&other.0)
132 }
133}
134
135impl ChildIndex {
136 pub fn from_index(i: u32) -> Option<Self> {
140 if i >= (1 << 31) {
141 Some(ChildIndex(i))
142 } else {
143 None
144 }
145 }
146
147 pub const fn hardened(value: u32) -> Self {
153 assert!(value < (1 << 31));
154 Self(value + (1 << 31))
155 }
156
157 pub fn index(&self) -> u32 {
159 self.0
160 }
161
162 pub const PRIVATE_USE: Self = Self::hardened(0x7fff_ffff);
164}
165
166#[derive(Clone, Copy, Debug, PartialEq, Eq)]
169pub struct ChainCode([u8; 32]);
170
171impl ConstantTimeEq for ChainCode {
172 fn ct_eq(&self, other: &Self) -> Choice {
173 self.0.ct_eq(&other.0)
174 }
175}
176
177impl ChainCode {
178 pub fn new(c: [u8; 32]) -> Self {
180 Self(c)
181 }
182
183 pub fn as_bytes(&self) -> &[u8; 32] {
186 &self.0
187 }
188}
189
190#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
192pub struct DiversifierIndex([u8; 11]);
193
194impl Default for DiversifierIndex {
195 fn default() -> Self {
196 DiversifierIndex::new()
197 }
198}
199
200macro_rules! di_from {
201 ($n:ident) => {
202 impl From<$n> for DiversifierIndex {
203 fn from(j: $n) -> Self {
204 let mut j_bytes = [0; 11];
205 j_bytes[..mem::size_of::<$n>()].copy_from_slice(&j.to_le_bytes());
206 DiversifierIndex(j_bytes)
207 }
208 }
209 };
210}
211di_from!(u32);
212di_from!(u64);
213di_from!(usize);
214
215impl From<[u8; 11]> for DiversifierIndex {
216 fn from(j_bytes: [u8; 11]) -> Self {
217 DiversifierIndex(j_bytes)
218 }
219}
220
221impl TryFrom<u128> for DiversifierIndex {
222 type Error = TryFromIntError;
223
224 fn try_from(value: u128) -> Result<Self, Self::Error> {
225 if (value >> 88) == 0 {
226 Ok(Self(value.to_le_bytes()[..11].try_into().unwrap()))
227 } else {
228 Err(TryFromIntError(()))
229 }
230 }
231}
232
233macro_rules! di_try_into {
234 ($n:ident) => {
235 impl TryFrom<DiversifierIndex> for $n {
236 type Error = core::num::TryFromIntError;
237
238 fn try_from(di: DiversifierIndex) -> Result<Self, Self::Error> {
239 u128::from(di).try_into()
240 }
241 }
242 };
243}
244di_try_into!(u32);
245di_try_into!(u64);
246di_try_into!(usize);
247
248impl From<DiversifierIndex> for u128 {
249 fn from(di: DiversifierIndex) -> Self {
250 let mut u128_bytes = [0u8; 16];
251 u128_bytes[0..11].copy_from_slice(&di.0[..]);
252 u128::from_le_bytes(u128_bytes)
253 }
254}
255
256impl PartialOrd for DiversifierIndex {
257 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
258 Some(self.cmp(other))
259 }
260}
261
262impl Ord for DiversifierIndex {
263 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
264 self.0
265 .iter()
266 .rev()
267 .zip(other.0.iter().rev())
268 .find_map(|(a, b)| match a.cmp(b) {
269 core::cmp::Ordering::Equal => None,
270 ineq => Some(ineq),
271 })
272 .unwrap_or(core::cmp::Ordering::Equal)
273 }
274}
275
276impl DiversifierIndex {
277 pub fn new() -> Self {
279 DiversifierIndex([0; 11])
280 }
281
282 pub fn as_bytes(&self) -> &[u8; 11] {
284 &self.0
285 }
286
287 pub fn increment(&mut self) -> Result<(), DiversifierIndexOverflowError> {
289 for k in 0..11 {
290 self.0[k] = self.0[k].wrapping_add(1);
291 if self.0[k] != 0 {
292 return Ok(());
294 }
295 }
296 Err(DiversifierIndexOverflowError)
298 }
299}
300
301#[derive(Clone, Copy, Debug)]
303pub struct DiversifierIndexOverflowError;
304
305impl core::fmt::Display for DiversifierIndexOverflowError {
306 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
307 write!(f, "DiversifierIndex increment overflowed")
308 }
309}
310
311#[cfg(feature = "std")]
312impl std::error::Error for DiversifierIndexOverflowError {}
313
314#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
323pub enum Scope {
324 External,
327 Internal,
330}
331
332memuse::impl_no_dynamic_usage!(Scope);
333
334#[cfg(test)]
335mod tests {
336 use super::{AccountId, DiversifierIndex};
337
338 use assert_matches::assert_matches;
339
340 #[test]
341 fn account_id_next() {
342 let zero = AccountId::ZERO;
343 assert_eq!(zero.next(), AccountId::try_from(1).ok());
344
345 let max_id = AccountId::try_from((1 << 31) - 1).unwrap();
346 assert_eq!(max_id.next(), None);
347 }
348
349 #[test]
350 fn diversifier_index_to_u32() {
351 let two = DiversifierIndex([
352 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
353 ]);
354 assert_eq!(u32::try_from(two), Ok(2));
355 assert_eq!(DiversifierIndex::from(2_u32), two);
356
357 let max_u32 = DiversifierIndex([
358 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
359 ]);
360 assert_eq!(u32::try_from(max_u32), Ok(u32::MAX));
361 assert_eq!(DiversifierIndex::from(u32::MAX), max_u32);
362
363 let too_big = DiversifierIndex([
364 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
365 ]);
366 assert_matches!(u32::try_from(too_big), Err(_));
367 }
368
369 #[test]
370 fn diversifier_index_to_u64() {
371 let two = DiversifierIndex([
372 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
373 ]);
374 assert_eq!(u64::try_from(two), Ok(2));
375 assert_eq!(DiversifierIndex::from(2_u64), two);
376
377 let max_u64 = DiversifierIndex([
378 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00,
379 ]);
380 assert_eq!(u64::try_from(max_u64), Ok(u64::MAX));
381 assert_eq!(DiversifierIndex::from(u64::MAX), max_u64);
382
383 let too_big = DiversifierIndex([
384 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
385 ]);
386 assert_matches!(u64::try_from(too_big), Err(_));
387 }
388
389 #[test]
390 fn diversifier_index_to_u128() {
391 let two = DiversifierIndex([
392 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
393 ]);
394 assert_eq!(u128::from(two), 2);
395 assert_eq!(DiversifierIndex::try_from(2_u128).unwrap(), two);
396
397 let max_di = DiversifierIndex([
398 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
399 ]);
400 assert_eq!(u128::from(max_di), 0x00ff_ffff_ffff_ffff_ffff_ffff);
401 assert_eq!(
402 DiversifierIndex::try_from(0x00ff_ffff_ffff_ffff_ffff_ffff_u128).unwrap(),
403 max_di,
404 );
405
406 assert!(DiversifierIndex::try_from(0x0100_0000_0000_0000_0000_0000_u128).is_err());
407 }
408
409 #[test]
410 fn diversifier_index_increment() {
411 let mut di = DiversifierIndex::new();
412 assert_eq!(di, 0_u32.into());
413
414 assert_matches!(di.increment(), Ok(_));
415 assert_eq!(di, 1_u32.into());
416
417 assert_matches!(di.increment(), Ok(_));
418 assert_eq!(di, 2_u32.into());
419
420 let mut di = DiversifierIndex::from(0xff_u32);
421 assert_eq!(di, 0x00ff_u32.into());
422
423 assert_matches!(di.increment(), Ok(_));
424 assert_eq!(di, 0x0100_u32.into());
425
426 assert_matches!(di.increment(), Ok(_));
427 assert_eq!(di, 0x0101_u32.into());
428
429 let mut di = DiversifierIndex::try_from(0x00ff_ffff_ffff_ffff_ffff_fffe_u128).unwrap();
430 assert_eq!(u128::from(di), 0x00ff_ffff_ffff_ffff_ffff_fffe_u128);
431
432 assert_matches!(di.increment(), Ok(_));
433 assert_eq!(u128::from(di), 0x00ff_ffff_ffff_ffff_ffff_ffff_u128);
434
435 assert_matches!(di.increment(), Err(_));
436 }
437
438 #[test]
439 fn diversifier_index_ord() {
440 assert!(DiversifierIndex::from(1u64) < DiversifierIndex::from(2u64));
441 assert!(DiversifierIndex::from(u64::MAX - 1) < DiversifierIndex::from(u64::MAX));
442 assert!(DiversifierIndex::from(3u64) == DiversifierIndex::from(3u64));
443 assert!(DiversifierIndex::from(u64::MAX) == DiversifierIndex::from(u64::MAX));
444 }
445}