1use crate::bitstream::{BitReader, BitWriter};
14use crate::error::Error;
15use crate::varint::{read_varint, write_varint};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub struct PathComponent {
20 pub hardened: bool,
22 pub value: u32,
24}
25
26impl PathComponent {
27 pub fn write(&self, w: &mut BitWriter) -> Result<(), Error> {
29 w.write_bits(u64::from(self.hardened), 1);
30 write_varint(w, self.value)?;
31 Ok(())
32 }
33
34 pub fn read(r: &mut BitReader) -> Result<Self, Error> {
36 let hardened = r.read_bits(1)? != 0;
37 let value = read_varint(r)?;
38 Ok(Self { hardened, value })
39 }
40}
41
42pub const MAX_PATH_COMPONENTS: usize = 15;
44
45#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct OriginPath {
48 pub components: Vec<PathComponent>,
50}
51
52impl OriginPath {
53 pub fn write(&self, w: &mut BitWriter) -> Result<(), Error> {
55 if self.components.len() > MAX_PATH_COMPONENTS {
56 return Err(Error::PathDepthExceeded {
57 got: self.components.len(),
58 max: MAX_PATH_COMPONENTS,
59 });
60 }
61 w.write_bits(self.components.len() as u64, 4);
62 for c in &self.components {
63 c.write(w)?;
64 }
65 Ok(())
66 }
67
68 pub fn read(r: &mut BitReader) -> Result<Self, Error> {
70 let depth = r.read_bits(4)? as usize;
71 let mut components = Vec::with_capacity(depth);
72 for _ in 0..depth {
73 components.push(PathComponent::read(r)?);
74 }
75 Ok(Self { components })
76 }
77}
78
79#[derive(Debug, Clone, PartialEq, Eq)]
82pub struct PathDecl {
83 pub n: u8,
85 pub paths: PathDeclPaths,
87}
88
89#[derive(Debug, Clone, PartialEq, Eq)]
91pub enum PathDeclPaths {
92 Shared(OriginPath),
94 Divergent(Vec<OriginPath>),
96}
97
98impl PathDecl {
99 pub fn write(&self, w: &mut BitWriter) -> Result<(), Error> {
111 if !(1..=32).contains(&(self.n as u32)) {
112 return Err(Error::KeyCountOutOfRange { n: self.n });
113 }
114 w.write_bits((self.n - 1) as u64, 5);
116 match &self.paths {
117 PathDeclPaths::Shared(p) => p.write(w)?,
118 PathDeclPaths::Divergent(paths) => {
119 if paths.len() != self.n as usize {
120 return Err(Error::DivergentPathCountMismatch {
121 n: self.n,
122 got: paths.len(),
123 });
124 }
125 for p in paths {
126 p.write(w)?;
127 }
128 }
129 }
130 Ok(())
131 }
132
133 pub fn read(r: &mut BitReader, divergent_mode: bool) -> Result<Self, Error> {
135 let n = (r.read_bits(5)? + 1) as u8;
136 let paths = if divergent_mode {
137 let mut paths = Vec::with_capacity(n as usize);
138 for _ in 0..n {
139 paths.push(OriginPath::read(r)?);
140 }
141 PathDeclPaths::Divergent(paths)
142 } else {
143 PathDeclPaths::Shared(OriginPath::read(r)?)
144 };
145 Ok(Self { n, paths })
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152
153 fn bip84() -> OriginPath {
154 OriginPath {
156 components: vec![
157 PathComponent {
158 hardened: true,
159 value: 84,
160 },
161 PathComponent {
162 hardened: true,
163 value: 0,
164 },
165 PathComponent {
166 hardened: true,
167 value: 0,
168 },
169 ],
170 }
171 }
172
173 #[test]
174 fn origin_path_round_trip_bip84() {
175 let p = bip84();
176 let mut w = BitWriter::new();
177 p.write(&mut w).unwrap();
178 let bytes = w.into_bytes();
179 let mut r = BitReader::new(&bytes);
180 assert_eq!(OriginPath::read(&mut r).unwrap(), p);
181 }
182
183 #[test]
184 fn origin_path_bit_cost_bip84() {
185 let p = bip84();
187 let mut w = BitWriter::new();
188 p.write(&mut w).unwrap();
189 assert_eq!(w.bit_len(), 26);
190 }
191
192 #[test]
193 fn origin_path_rejects_depth_too_large() {
194 let p = OriginPath {
195 components: (0..16)
196 .map(|_| PathComponent {
197 hardened: false,
198 value: 0,
199 })
200 .collect(),
201 };
202 let mut w = BitWriter::new();
203 assert!(matches!(
204 p.write(&mut w),
205 Err(Error::PathDepthExceeded { got: 16, max: 15 })
206 ));
207 }
208}
209
210#[cfg(test)]
211mod path_decl_tests {
212 use super::*;
213
214 #[test]
215 fn path_decl_shared_round_trip() {
216 let p = PathDecl {
217 n: 1,
218 paths: PathDeclPaths::Shared(OriginPath {
219 components: vec![
220 PathComponent {
221 hardened: true,
222 value: 84,
223 },
224 PathComponent {
225 hardened: true,
226 value: 0,
227 },
228 PathComponent {
229 hardened: true,
230 value: 0,
231 },
232 ],
233 }),
234 };
235 let mut w = BitWriter::new();
236 p.write(&mut w).unwrap();
237 let bytes = w.into_bytes();
238 let mut r = BitReader::new(&bytes);
239 assert_eq!(PathDecl::read(&mut r, false).unwrap(), p);
240 }
241
242 #[test]
243 fn path_decl_shared_bit_cost_bip84() {
244 let p = PathDecl {
246 n: 1,
247 paths: PathDeclPaths::Shared(OriginPath {
248 components: vec![
249 PathComponent {
250 hardened: true,
251 value: 84,
252 },
253 PathComponent {
254 hardened: true,
255 value: 0,
256 },
257 PathComponent {
258 hardened: true,
259 value: 0,
260 },
261 ],
262 }),
263 };
264 let mut w = BitWriter::new();
265 p.write(&mut w).unwrap();
266 assert_eq!(w.bit_len(), 31);
267 }
268
269 #[test]
270 fn path_decl_divergent_round_trip() {
271 let p = PathDecl {
272 n: 2,
273 paths: PathDeclPaths::Divergent(vec![
274 OriginPath {
275 components: vec![PathComponent {
276 hardened: true,
277 value: 84,
278 }],
279 },
280 OriginPath {
281 components: vec![PathComponent {
282 hardened: true,
283 value: 86,
284 }],
285 },
286 ]),
287 };
288 let mut w = BitWriter::new();
289 p.write(&mut w).unwrap();
290 let bytes = w.into_bytes();
291 let mut r = BitReader::new(&bytes);
292 assert_eq!(PathDecl::read(&mut r, true).unwrap(), p);
293 }
294
295 #[test]
296 fn path_decl_n_zero_rejected() {
297 let p = PathDecl {
298 n: 0,
299 paths: PathDeclPaths::Shared(OriginPath { components: vec![] }),
300 };
301 let mut w = BitWriter::new();
302 assert!(matches!(
303 p.write(&mut w),
304 Err(Error::KeyCountOutOfRange { n: 0 })
305 ));
306 }
307}