1use coins_core::hashes::{Digest, Hash256};
2use k256::ecdsa;
3use std::marker::PhantomData;
4
5use crate::{
6 primitives::{ChainCode, Hint, KeyFingerprint, XKeyInfo},
7 xkeys::{XPriv, XPub},
8 Bip32Error,
9};
10
11pub fn decode_b58_check(s: &str) -> Result<Vec<u8>, Bip32Error> {
13 let data: Vec<u8> = bs58::decode(s).into_vec()?;
14 let idx = data.len() - 4;
15 let payload = &data[..idx];
16 let checksum = &data[idx..];
17
18 let digest = &Hash256::digest(payload);
19
20 let mut expected = [0u8; 4];
21 expected.copy_from_slice(&digest.as_slice()[..4]);
22 if expected != checksum {
23 Err(Bip32Error::BadB58Checksum)
24 } else {
25 Ok(payload.to_vec())
26 }
27}
28
29pub fn encode_b58_check(v: &[u8]) -> String {
31 let digest = &Hash256::digest(v);
32
33 let mut checksum = [0u8; 4];
34 checksum.copy_from_slice(&digest.as_slice()[..4]);
35
36 let mut data = v.to_vec();
37 data.extend(checksum);
38
39 bs58::encode(data).into_string()
40}
41
42pub trait NetworkParams {
44 const PRIV_VERSION: u32;
46 const BIP49_PRIV_VERSION: u32;
48 const BIP84_PRIV_VERSION: u32;
50 const PUB_VERSION: u32;
52 const BIP49_PUB_VERSION: u32;
54 const BIP84_PUB_VERSION: u32;
56}
57
58params!(
59 Main {
61 bip32: 0x0488_ADE4,
62 bip49: 0x049d_7878,
63 bip84: 0x04b2_430c,
64 bip32_pub: 0x0488_B21E,
65 bip49_pub: 0x049d_7cb2,
66 bip84_pub: 0x04b2_4746
67 }
68);
69
70params!(
71 Test {
73 bip32: 0x0435_8394,
74 bip49: 0x044a_4e28,
75 bip84: 0x045f_18bc,
76 bip32_pub: 0x0435_87CF,
77 bip49_pub: 0x044a_5262,
78 bip84_pub: 0x045f_1cf6
79 }
80);
81
82#[derive(Debug, Clone)]
84pub struct BitcoinEncoder<P: NetworkParams>(PhantomData<fn(P) -> P>);
85
86pub type MainnetEncoder = BitcoinEncoder<Main>;
88pub type TestnetEncoder = BitcoinEncoder<Test>;
90
91pub trait XKeyEncoder {
93 #[doc(hidden)]
94 fn write_key_details<K, W>(writer: &mut W, key: &K) -> Result<usize, Bip32Error>
95 where
96 K: AsRef<XKeyInfo>,
97 W: std::io::Write,
98 {
99 let key = key.as_ref();
100 let mut written = writer.write(&[key.depth])?;
101 written += writer.write(&key.parent.0)?;
102 written += writer.write(&key.index.to_be_bytes())?;
103 written += writer.write(&key.chain_code.0)?;
104 Ok(written)
105 }
106
107 fn write_xpub<W, K>(writer: &mut W, key: &K) -> Result<usize, Bip32Error>
109 where
110 W: std::io::Write,
111 K: AsRef<XPub>;
112
113 fn write_xpriv<W, K>(writer: &mut W, key: &K) -> Result<usize, Bip32Error>
115 where
116 W: std::io::Write,
117 K: AsRef<XPriv>;
118
119 #[doc(hidden)]
120 fn read_depth<R>(reader: &mut R) -> Result<u8, Bip32Error>
121 where
122 R: std::io::Read,
123 {
124 let mut buf = [0u8; 1];
125 reader.read_exact(&mut buf)?;
126 Ok(buf[0])
127 }
128
129 #[doc(hidden)]
130 fn read_parent<R>(reader: &mut R) -> Result<KeyFingerprint, Bip32Error>
131 where
132 R: std::io::Read,
133 {
134 let mut buf = [0u8; 4];
135 reader.read_exact(&mut buf)?;
136 Ok(buf.into())
137 }
138
139 #[doc(hidden)]
140 fn read_index<R>(reader: &mut R) -> Result<u32, Bip32Error>
141 where
142 R: std::io::Read,
143 {
144 let mut buf = [0u8; 4];
145 reader.read_exact(&mut buf)?;
146 Ok(u32::from_be_bytes(buf))
147 }
148
149 #[doc(hidden)]
150 fn read_chain_code<R>(reader: &mut R) -> Result<ChainCode, Bip32Error>
151 where
152 R: std::io::Read,
153 {
154 let mut buf = [0u8; 32];
155 reader.read_exact(&mut buf)?;
156 Ok(buf.into())
157 }
158
159 #[doc(hidden)]
160 fn read_xpriv_body<R>(reader: &mut R, hint: Hint) -> Result<XPriv, Bip32Error>
161 where
162 R: std::io::Read,
163 {
164 let depth = Self::read_depth(reader)?;
165 let parent = Self::read_parent(reader)?;
166 let index = Self::read_index(reader)?;
167 let chain_code = Self::read_chain_code(reader)?;
168
169 let mut buf = [0u8];
170 reader.read_exact(&mut buf)?;
171 if buf != [0] {
172 return Err(Bip32Error::BadPadding(buf[0]));
173 }
174
175 let mut buf = [0u8; 32];
176 reader.read_exact(&mut buf)?;
177 let key = ecdsa::SigningKey::from_bytes(&buf.into())?;
178
179 Ok(XPriv {
180 key,
181 xkey_info: XKeyInfo {
182 depth,
183 parent,
184 index,
185 chain_code,
186 hint,
187 },
188 })
189 }
190
191 #[doc(hidden)]
192 fn read_xpriv_without_network<R>(reader: &mut R) -> Result<XPriv, Bip32Error>
195 where
196 R: std::io::Read,
197 {
198 let mut buf = [0u8; 4];
199 reader.read_exact(&mut buf)?;
200
201 Self::read_xpriv_body(reader, Hint::Legacy)
202 }
203
204 fn read_xpriv<R>(reader: &mut R) -> Result<XPriv, Bip32Error>
216 where
217 R: std::io::Read;
218
219 #[doc(hidden)]
220 fn read_xpub_body<R>(reader: &mut R, hint: Hint) -> Result<XPub, Bip32Error>
221 where
222 R: std::io::Read,
223 {
224 let depth = Self::read_depth(reader)?;
225 let parent = Self::read_parent(reader)?;
226 let index = Self::read_index(reader)?;
227 let chain_code = Self::read_chain_code(reader)?;
228
229 let mut buf = [0u8; 33];
230 reader.read_exact(&mut buf)?;
231 let key = ecdsa::VerifyingKey::from_sec1_bytes(&buf)?;
232
233 Ok(XPub {
234 key,
235 xkey_info: XKeyInfo {
236 depth,
237 parent,
238 index,
239 chain_code,
240 hint,
241 },
242 })
243 }
244
245 #[doc(hidden)]
246 fn read_xpub_without_network<R>(reader: &mut R) -> Result<XPub, Bip32Error>
249 where
250 R: std::io::Read,
251 {
252 let mut buf = [0u8; 4];
253 reader.read_exact(&mut buf)?;
254
255 Self::read_xpub_body(reader, Hint::Legacy)
256 }
257
258 fn read_xpub<R>(reader: &mut R) -> Result<XPub, Bip32Error>
270 where
271 R: std::io::Read;
272
273 fn xpriv_to_base58<K>(k: &K) -> Result<String, Bip32Error>
275 where
276 K: AsRef<XPriv>,
277 {
278 let mut v: Vec<u8> = vec![];
279 Self::write_xpriv(&mut v, k)?;
280 Ok(encode_b58_check(&v))
281 }
282
283 fn xpub_to_base58<K>(k: &K) -> Result<String, Bip32Error>
285 where
286 K: AsRef<XPub>,
287 {
288 let mut v: Vec<u8> = vec![];
289 Self::write_xpub(&mut v, k)?;
290 Ok(encode_b58_check(&v))
291 }
292
293 fn xpriv_from_base58(s: &str) -> Result<XPriv, Bip32Error>
305where {
306 let data = decode_b58_check(s)?;
307 Self::read_xpriv(&mut &data[..])
308 }
309
310 fn xpub_from_base58(s: &str) -> Result<XPub, Bip32Error>
322where {
323 let data = decode_b58_check(s)?;
324 Self::read_xpub(&mut &data[..])
325 }
326}
327
328impl<P: NetworkParams> XKeyEncoder for BitcoinEncoder<P> {
329 fn write_xpub<W, K>(writer: &mut W, key: &K) -> Result<usize, Bip32Error>
331 where
332 W: std::io::Write,
333 K: AsRef<XPub>,
334 {
335 let version = match key.as_ref().xkey_info.hint {
336 Hint::Legacy => P::PUB_VERSION,
337 Hint::Compatibility => P::BIP49_PUB_VERSION,
338 Hint::SegWit => P::BIP84_PUB_VERSION,
339 };
340 let mut written = writer.write(&version.to_be_bytes())?;
341 written += Self::write_key_details(writer, key.as_ref())?;
342 written += writer.write(key.as_ref().key.to_sec1_bytes().as_ref())?;
343 Ok(written)
344 }
345
346 fn write_xpriv<W, K>(writer: &mut W, key: &K) -> Result<usize, Bip32Error>
348 where
349 W: std::io::Write,
350 K: AsRef<XPriv>,
351 {
352 let version = match key.as_ref().xkey_info.hint {
353 Hint::Legacy => P::PRIV_VERSION,
354 Hint::Compatibility => P::BIP49_PRIV_VERSION,
355 Hint::SegWit => P::BIP84_PRIV_VERSION,
356 };
357 let mut written = writer.write(&version.to_be_bytes())?;
358 written += Self::write_key_details(writer, key.as_ref())?;
359 written += writer.write(&[0])?;
360 written += writer.write(key.as_ref().key.to_bytes().as_ref())?;
361 Ok(written)
362 }
363
364 fn read_xpriv<R>(reader: &mut R) -> Result<XPriv, Bip32Error>
365 where
366 R: std::io::Read,
367 {
368 let mut buf = [0u8; 4];
369 reader.read_exact(&mut buf)?;
370 let version_bytes = u32::from_be_bytes(buf);
371
372 let hint = if version_bytes == P::PRIV_VERSION {
374 Hint::Legacy
375 } else if version_bytes == P::BIP49_PRIV_VERSION {
376 Hint::Compatibility
377 } else if version_bytes == P::BIP84_PRIV_VERSION {
378 Hint::SegWit
379 } else {
380 return Err(Bip32Error::BadXPrivVersionBytes(buf));
381 };
382 Self::read_xpriv_body(reader, hint)
383 }
384
385 fn read_xpub<R>(reader: &mut R) -> Result<XPub, Bip32Error>
386 where
387 R: std::io::Read,
388 {
389 let mut buf = [0u8; 4];
390 reader.read_exact(&mut buf)?;
391 let version_bytes = u32::from_be_bytes(buf);
392
393 let hint = if version_bytes == P::PUB_VERSION {
395 Hint::Legacy
396 } else if version_bytes == P::BIP49_PUB_VERSION {
397 Hint::Compatibility
398 } else if version_bytes == P::BIP84_PUB_VERSION {
399 Hint::SegWit
400 } else {
401 return Err(Bip32Error::BadXPrivVersionBytes(buf));
402 };
403 Self::read_xpub_body(reader, hint)
404 }
405}