1
2use std::{fmt, io};
3use std::str::FromStr;
4
5use bitcoin::bech32::{self, ByteIterExt, Fe32IterExt};
6use bitcoin::hashes::{sha256, Hash};
7use bitcoin::secp256k1::PublicKey;
8
9use crate::{ProtocolDecodingError, ProtocolEncoding, VtxoPolicy};
10use crate::encode::{ReadExt, WriteExt};
11
12
13const HRP_MAINNET: bech32::Hrp = bech32::Hrp::parse_unchecked("ark");
15
16const HRP_TESTNET: bech32::Hrp = bech32::Hrp::parse_unchecked("tark");
18
19const VERSION_ARKADE: bech32::Fe32 = bech32::Fe32::Q;
21
22const VERSION_POLICY: bech32::Fe32 = bech32::Fe32::P;
24
25
26#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
28pub struct ArkId([u8; 4]);
29impl_byte_newtype!(ArkId, 4);
30
31impl ArkId {
32 pub fn from_server_pubkey(server_pubkey: PublicKey) -> ArkId {
34 let mut buf = [0u8; 4];
35 let hash = sha256::Hash::hash(&server_pubkey.serialize());
36 buf[0..4].copy_from_slice(&hash[0..4]);
37 ArkId(buf)
38 }
39
40 pub fn is_for_server(&self, server_pubkey: PublicKey) -> bool {
42 *self == ArkId::from_server_pubkey(server_pubkey)
43 }
44}
45
46impl From<PublicKey> for ArkId {
47 fn from(pk: PublicKey) -> Self {
48 ArkId::from_server_pubkey(pk)
49 }
50}
51
52#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
54#[non_exhaustive]
55pub enum VtxoDelivery {
56 ServerBuiltin,
58 Unknown {
59 delivery_type: u8,
60 data: Vec<u8>,
61 },
62}
63
64const DELIVERY_BUILTIN: u8 = 0x00;
66
67impl VtxoDelivery {
68 pub fn is_unknown(&self) -> bool {
70 match self {
71 Self::Unknown { .. } => true,
72 _ => false,
73 }
74 }
75
76 fn encoded_length(&self) -> usize {
78 match self {
79 Self::ServerBuiltin => 1,
80 Self::Unknown { data, .. } => 1 + data.len(),
81 }
82 }
83
84 fn encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<(), io::Error> {
85 match self {
86 Self::ServerBuiltin => {
87 w.emit_u8(DELIVERY_BUILTIN)?;
88 },
89 Self::Unknown { delivery_type, data } => {
90 w.emit_u8(*delivery_type)?;
91 w.emit_slice(data)?;
92 },
93 }
94 Ok(())
95 }
96
97 fn decode(payload: &[u8]) -> Result<Self, ParseAddressError> {
98 if payload.is_empty() {
99 return Err(ParseAddressError::Eof);
100 }
101
102 match payload[0] {
103 DELIVERY_BUILTIN => Ok(Self::ServerBuiltin),
104 delivery_type => Ok(Self::Unknown {
105 delivery_type: delivery_type,
106 data: payload[1..].to_vec(),
107 }),
108 }
109 }
110}
111
112#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
131pub struct Address {
132 testnet: bool,
133 ark_id: ArkId,
134 policy: VtxoPolicy,
135 delivery: Vec<VtxoDelivery>,
136}
137
138impl Address {
139 pub fn builder() -> Builder {
141 Builder::new()
142 }
143
144 pub fn new(
148 testnet: bool,
149 ark_id: impl Into<ArkId>,
150 policy: VtxoPolicy,
151 delivery: Vec<VtxoDelivery>,
152 ) -> Address {
153 Address {
154 testnet: testnet,
155 ark_id: ark_id.into(),
156 policy: policy,
157 delivery: delivery,
158 }
159 }
160
161 pub fn is_testnet(&self) -> bool {
163 self.testnet
164 }
165
166 pub fn ark_id(&self) -> ArkId {
168 self.ark_id
169 }
170
171 pub fn is_for_server(&self, server_pubkey: PublicKey) -> bool {
173 self.ark_id().is_for_server(server_pubkey)
174 }
175
176 pub fn policy(&self) -> &VtxoPolicy {
178 &self.policy
179 }
180
181 pub fn delivery(&self) -> &[VtxoDelivery] {
183 &self.delivery
184 }
185
186 pub fn encode_payload<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<(), io::Error> {
188 writer.emit_slice(&self.ark_id.to_byte_array())?;
189
190 let mut buf = Vec::with_capacity(128); self.policy.encode(&mut buf)?;
196 writer.emit_compact_size(buf.len() as u64)?;
197 writer.emit_slice(&buf[..])?;
198
199 for delivery in &self.delivery {
200 writer.emit_compact_size(delivery.encoded_length() as u64)?;
201 delivery.encode(writer)?;
202 }
203
204 Ok(())
205 }
206
207 pub fn decode_payload(
211 testnet: bool,
212 bytes: impl Iterator<Item = u8>,
213 ) -> Result<Address, ParseAddressError> {
214 let mut peekable = bytes.peekable();
215 let mut reader = ByteIter(&mut peekable);
216
217 let ark_id = {
218 let mut buf = [0u8; 4];
219 reader.read_slice(&mut buf).map_err(|_| ParseAddressError::Eof)?;
220 ArkId(buf)
221 };
222
223 let mut buf = Vec::new();
224 let policy = {
225 let len = reader.read_compact_size()? as usize;
226 buf.resize(len, 0);
227 reader.read_slice(&mut buf[..])?;
228 VtxoPolicy::deserialize(&buf[..]).map_err(ParseAddressError::VtxoPolicy)?
229 };
230
231 let mut delivery = Vec::new();
232 while reader.0.peek().is_some() {
233 let len = reader.read_compact_size()? as usize;
234 buf.resize(len, 0);
235 reader.read_slice(&mut buf[..])?;
236 delivery.push(VtxoDelivery::decode(&buf[..])?);
237 }
238
239 Ok(Address::new(testnet, ark_id, policy, delivery))
240 }
241}
242
243impl fmt::Display for Address {
244 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
245 let hrp = if self.testnet {
246 HRP_TESTNET
247 } else {
248 HRP_MAINNET
249 };
250
251 let ver = VERSION_POLICY;
252 let payload = {
253 let mut buf = Vec::with_capacity(128);
254 self.encode_payload(&mut buf).expect("buffers don't error");
255 buf
256 };
257
258 let chars = [ver].into_iter().chain(payload.into_iter().bytes_to_fes())
259 .with_checksum::<bech32::Bech32m>(&hrp)
260 .chars();
261
262 const BUF_LENGTH: usize = 128;
264 let mut buf = [0u8; BUF_LENGTH];
265 let mut pos = 0;
266 for c in chars {
267 buf[pos] = c as u8;
268 pos += 1;
269
270 if pos == BUF_LENGTH {
271 let s = core::str::from_utf8(&buf).expect("we only write ASCII");
272 f.write_str(s)?;
273 pos = 0;
274 }
275 }
276
277 let s = core::str::from_utf8(&buf[..pos]).expect("we only write ASCII");
278 f.write_str(s)?;
279 Ok(())
280 }
281}
282
283impl fmt::Debug for Address {
284 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
285 fmt::Display::fmt(self, f)
286 }
287}
288
289#[derive(Debug, thiserror::Error)]
291pub enum ParseAddressError {
292 #[error("bech32m decoding error: {0}")]
293 Bech32(bech32::DecodeError),
294 #[error("invalid HRP: '{0}'")]
295 Hrp(bech32::Hrp),
296 #[error("address ins an Arkade address and cannot be used here")]
297 Arkade,
298 #[error("unknown version: '{version}'")]
299 UnknownVersion {
300 version: bech32::Fe32,
301 },
302 #[error("invalid encoding: unexpected end of bytes")]
303 Eof,
304 #[error("invalid or unknown VTXO policy")]
305 VtxoPolicy(ProtocolDecodingError),
306 #[error("invalid address")]
307 Invalid(&'static str),
308}
309
310impl From<bech32::primitives::decode::UncheckedHrpstringError> for ParseAddressError {
311 fn from(e: bech32::primitives::decode::UncheckedHrpstringError) -> Self {
312 Self::Bech32(e.into())
313 }
314}
315
316impl From<bech32::primitives::decode::ChecksumError> for ParseAddressError {
317 fn from(e: bech32::primitives::decode::ChecksumError) -> Self {
318 Self::Bech32(bech32::DecodeError::Checksum(e))
319 }
320}
321
322impl From<io::Error> for ParseAddressError {
323 fn from(e: io::Error) -> Self {
324 match e.kind() {
325 io::ErrorKind::UnexpectedEof => ParseAddressError::Eof,
326 io::ErrorKind::InvalidData => ParseAddressError::Invalid("invalid encoding"),
327 _ => {
329 if cfg!(debug_assertions) {
330 panic!("unexpected I/O error while parsing address: {}", e);
331 }
332 ParseAddressError::Invalid("unexpected I/O error")
333 },
334 }
335 }
336}
337
338impl FromStr for Address {
339 type Err = ParseAddressError;
340 fn from_str(s: &str) -> Result<Self, Self::Err> {
341 let raw = bech32::primitives::decode::UncheckedHrpstring::new(s)?;
342
343 let testnet = if raw.hrp() == HRP_MAINNET {
344 false
345 } else if raw.hrp() == HRP_TESTNET {
346 true
347 } else {
348 return Err(ParseAddressError::Hrp(raw.hrp()));
349 };
350
351 let checked = raw.validate_and_remove_checksum::<bech32::Bech32m>()?;
352 let mut iter = checked.fe32_iter::<std::iter::Empty<u8>>();
354 let ver = iter.next().ok_or(ParseAddressError::Invalid("empty address"))?;
355
356 match ver {
357 VERSION_POLICY => {},
358 VERSION_ARKADE => return Err(ParseAddressError::Arkade),
359 _ => return Err(ParseAddressError::UnknownVersion { version: ver }),
360 }
361
362 Address::decode_payload(testnet, iter.fes_to_bytes())
363 }
364}
365
366#[derive(Clone, Debug, thiserror::Error)]
368#[error("error building address: {msg}")]
369pub struct AddressBuilderError {
370 msg: &'static str,
371}
372
373impl From<&'static str> for AddressBuilderError {
374 fn from(msg: &'static str) -> Self {
375 AddressBuilderError { msg }
376 }
377}
378
379#[derive(Debug)]
385pub struct Builder {
386 testnet: bool,
387 ark_id: Option<ArkId>,
388 policy: Option<VtxoPolicy>,
389 delivery: Vec<VtxoDelivery>,
390 no_delivery: bool,
392}
393
394impl Builder {
395 pub fn new() -> Self {
397 Self {
398 testnet: false,
399 ark_id: None,
400 policy: None,
401 delivery: Vec::new(),
402 no_delivery: false,
403 }
404 }
405
406 pub fn testnet(mut self, testnet: bool) -> Self {
410 self.testnet = testnet;
411 self
412 }
413
414 pub fn server_pubkey(mut self, server_pubkey: PublicKey) -> Self {
416 self.ark_id = Some(ArkId::from_server_pubkey(server_pubkey));
417 self
418 }
419
420 pub fn policy(mut self, policy: VtxoPolicy) -> Self {
422 self.policy = Some(policy);
423 self
424 }
425
426 pub fn pubkey_policy(self, user_pubkey: PublicKey) -> Self {
428 self.policy(VtxoPolicy::new_pubkey(user_pubkey))
429 }
430
431 pub fn delivery(mut self, delivery: VtxoDelivery) -> Self {
433 self.delivery.push(delivery);
434 self
435 }
436
437 pub fn no_delivery(mut self) -> Self {
439 self.no_delivery = true;
440 self
441 }
442
443 pub fn into_address(self) -> Result<Address, AddressBuilderError> {
445 Ok(Address {
446 testnet: self.testnet,
447 ark_id: self.ark_id.ok_or("missing ark_id")?,
448 policy: self.policy.ok_or("missing policy")?,
449 delivery: if self.delivery.is_empty() && !self.no_delivery {
450 vec![VtxoDelivery::ServerBuiltin]
451 } else {
452 self.delivery
453 },
454 })
455 }
456}
457
458struct ByteIter<T>(T);
460
461impl<T: Iterator<Item = u8>> io::Read for ByteIter<T> {
462 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
463 let mut written = 0;
464 for e in buf.iter_mut() {
465 if let Some(n) = self.0.next() {
466 *e = n;
467 written += 1;
468 } else {
469 break;
470 }
471 }
472 Ok(written)
473 }
474}
475
476#[cfg(test)]
477mod test {
478 use super::*;
479
480 #[test]
481 fn test_versions() {
482 assert_eq!(VERSION_POLICY, bech32::Fe32::try_from(1u8).unwrap());
487 }
488
489 fn test_roundtrip(addr: &Address) -> Address {
490 let parsed = Address::from_str(&addr.to_string()).unwrap();
491 assert_eq!(parsed, *addr);
492 parsed
493 }
494
495 #[test]
496 fn address_roundtrip() {
497 let ark = PublicKey::from_str("02037188bdd7579a0cd0b22a51110986df1ea08e30192658fe0e219590e4a723d3").unwrap();
498 let ark_id = ArkId::from_server_pubkey(ark);
499 let usr = PublicKey::from_str("032217b6ccba4fa98cc433abe4be1ceaf41ea61fd83fcefd27384ca4612ce19512").unwrap();
500 println!("ark pk: {} (id {})", ark, ark_id);
501 println!("usr pk: {}", usr);
502 let policy = VtxoPolicy::new_pubkey(usr);
503
504 let addr = Address::builder()
506 .server_pubkey(ark)
507 .pubkey_policy(usr)
508 .into_address().unwrap();
509 assert_eq!(addr.to_string(), "ark1pwh9vsmezqqpjy9akejayl2vvcse6he97rn40g84xrlvrlnhayuuyefrp9nse2yspqqjl5wpy");
510
511 let parsed = test_roundtrip(&addr);
512 assert_eq!(parsed.ark_id, ark_id);
513 assert_eq!(parsed.policy, policy);
514 assert_eq!(parsed.delivery.len(), 1);
515
516 let addr = Address::builder()
518 .testnet(true)
519 .server_pubkey(ark)
520 .pubkey_policy(usr)
521 .no_delivery()
522 .into_address().unwrap();
523 assert_eq!(addr.to_string(), "tark1pwh9vsmezqqpjy9akejayl2vvcse6he97rn40g84xrlvrlnhayuuyefrp9nse2ysm2x4mn");
524
525 let parsed = test_roundtrip(&addr);
526 assert_eq!(parsed.ark_id, ArkId::from_server_pubkey(ark));
527 assert_eq!(parsed.policy, policy);
528 assert_eq!(parsed.delivery.len(), 0);
529 }
530}