1
2use std::{fmt, io};
3use std::borrow::Cow;
4use std::str::FromStr;
5
6use bitcoin::bech32::{self, ByteIterExt, Fe32IterExt};
7use bitcoin::hashes::{sha256, Hash};
8use bitcoin::secp256k1::{Keypair, PublicKey};
9
10use crate::{ProtocolDecodingError, ProtocolEncoding, VtxoPolicy};
11use crate::encode::{MAX_VEC_SIZE, ReadExt, WriteExt};
12use crate::mailbox::{BlindedMailboxIdentifier, MailboxIdentifier};
13
14
15const HRP_MAINNET: bech32::Hrp = bech32::Hrp::parse_unchecked("ark");
17
18const HRP_TESTNET: bech32::Hrp = bech32::Hrp::parse_unchecked("tark");
20
21const VERSION_ARKADE: bech32::Fe32 = bech32::Fe32::Q;
23
24const VERSION_POLICY: bech32::Fe32 = bech32::Fe32::P;
26
27
28#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
30pub struct ArkId([u8; 4]);
31impl_byte_newtype!(ArkId, 4);
32
33impl ArkId {
34 pub fn from_server_pubkey(server_pubkey: PublicKey) -> ArkId {
36 let mut buf = [0u8; 4];
37 let hash = sha256::Hash::hash(&server_pubkey.serialize());
38 buf[0..4].copy_from_slice(&hash[0..4]);
39 ArkId(buf)
40 }
41
42 pub fn is_for_server(&self, server_pubkey: PublicKey) -> bool {
44 *self == ArkId::from_server_pubkey(server_pubkey)
45 }
46}
47
48impl From<PublicKey> for ArkId {
49 fn from(pk: PublicKey) -> Self {
50 ArkId::from_server_pubkey(pk)
51 }
52}
53
54#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
56#[non_exhaustive]
57pub enum VtxoDelivery {
58 ServerMailbox {
60 blinded_id: BlindedMailboxIdentifier,
61 },
62 Unknown {
63 delivery_type: u8,
64 data: Vec<u8>,
65 },
66}
67
68#[allow(unused)]
74const DELIVERY_BUILTIN: u8 = 0x00;
75
76const DELIVERY_MAILBOX: u8 = 0x01;
78
79impl VtxoDelivery {
80
81 pub fn is_unknown(&self) -> bool {
83 match self {
84 Self::Unknown { .. } => true,
85 _ => false,
86 }
87 }
88
89 fn encoded_length(&self) -> usize {
91 match self {
92 Self::ServerMailbox { .. } => 1 + 33,
93 Self::Unknown { data, .. } => 1usize.saturating_add(data.len()),
94 }
95 }
96
97 fn encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<(), io::Error> {
99 match self {
100 Self::ServerMailbox { blinded_id } => {
101 w.emit_u8(DELIVERY_MAILBOX)?;
102 w.emit_slice(blinded_id.as_ref())?;
103 },
104 Self::Unknown { delivery_type, data } => {
105 w.emit_u8(*delivery_type)?;
106 w.emit_slice(data)?;
107 },
108 }
109 Ok(())
110 }
111
112 fn decode(payload: &[u8]) -> Result<Self, ParseAddressError> {
114 if payload.is_empty() {
115 return Err(ParseAddressError::Eof);
116 }
117
118 match payload[0] {
119 DELIVERY_MAILBOX => Ok(Self::ServerMailbox {
120 blinded_id: BlindedMailboxIdentifier::from_slice(&payload[1..]).map_err(
121 |_| ParseAddressError::Invalid("invalid blinded mailbox identifier"),
122 )?,
123 }),
124 delivery_type => Ok(Self::Unknown {
125 delivery_type: delivery_type,
126 data: payload[1..].to_vec(),
127 }),
128 }
129 }
130}
131
132#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
156pub struct Address {
157 testnet: bool,
158 ark_id: ArkId,
159 policy: VtxoPolicy,
160 delivery: Vec<VtxoDelivery>,
161}
162
163impl Address {
164 pub fn builder() -> Builder {
166 Builder::new()
167 }
168
169 pub fn new(
173 testnet: bool,
174 ark_id: impl Into<ArkId>,
175 policy: VtxoPolicy,
176 delivery: Vec<VtxoDelivery>,
177 ) -> Address {
178 Address {
179 testnet: testnet,
180 ark_id: ark_id.into(),
181 policy: policy,
182 delivery: delivery,
183 }
184 }
185
186 pub fn is_testnet(&self) -> bool {
188 self.testnet
189 }
190
191 pub fn ark_id(&self) -> ArkId {
193 self.ark_id
194 }
195
196 pub fn is_for_server(&self, server_pubkey: PublicKey) -> bool {
198 self.ark_id().is_for_server(server_pubkey)
199 }
200
201 pub fn policy(&self) -> &VtxoPolicy {
203 &self.policy
204 }
205
206 pub fn delivery(&self) -> &[VtxoDelivery] {
208 &self.delivery
209 }
210
211 pub fn encode_payload<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<(), io::Error> {
213 writer.emit_slice(&self.ark_id.to_byte_array())?;
214
215 let mut buf = Vec::with_capacity(128); self.policy.encode(&mut buf)?;
221 writer.emit_compact_size(buf.len() as u64)?;
222 writer.emit_slice(&buf[..])?;
223
224 for delivery in &self.delivery {
225 writer.emit_compact_size(delivery.encoded_length() as u64)?;
226 delivery.encode(writer)?;
227 }
228
229 Ok(())
230 }
231
232 pub fn decode_payload(
236 testnet: bool,
237 bytes: impl Iterator<Item = u8>,
238 ) -> Result<Address, ParseAddressError> {
239 let mut peekable = bytes.peekable();
240 let mut reader = ByteIter(&mut peekable);
241
242 let ark_id = {
243 let mut buf = [0u8; 4];
244 reader.read_slice(&mut buf).map_err(|_| ParseAddressError::Eof)?;
245 ArkId(buf)
246 };
247
248 let mut buf = Vec::new();
249 let policy = {
250 let len = reader.read_compact_size()?;
251 if len > MAX_VEC_SIZE as u64 {
252 return Err(ParseAddressError::Invalid("policy field exceeds maximum length"));
253 }
254 buf.resize(len as usize, 0);
255 reader.read_slice(&mut buf[..])?;
256 VtxoPolicy::deserialize(&buf[..]).map_err(ParseAddressError::VtxoPolicy)?
257 };
258
259 let mut delivery = Vec::new();
260 while reader.0.peek().is_some() {
261 let len = reader.read_compact_size()?;
262 if len > MAX_VEC_SIZE as u64 {
263 return Err(ParseAddressError::Invalid("delivery field exceeds maximum length"));
264 }
265 buf.resize(len as usize, 0);
266 reader.read_slice(&mut buf[..])?;
267 delivery.push(VtxoDelivery::decode(&buf[..])?);
268 }
269
270 Ok(Address::new(testnet, ark_id, policy, delivery))
271 }
272}
273
274impl fmt::Display for Address {
275 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
276 let hrp = if self.testnet {
277 HRP_TESTNET
278 } else {
279 HRP_MAINNET
280 };
281
282 let ver = VERSION_POLICY;
283 let payload = {
284 let mut buf = Vec::with_capacity(128);
285 self.encode_payload(&mut buf).expect("buffers don't error");
286 buf
287 };
288
289 let chars = [ver].into_iter().chain(payload.into_iter().bytes_to_fes())
290 .with_checksum::<bech32::Bech32m>(&hrp)
291 .chars();
292
293 const BUF_LENGTH: usize = 128;
295 let mut buf = [0u8; BUF_LENGTH];
296 let mut pos = 0;
297 for c in chars {
298 buf[pos] = c as u8;
299 pos = pos.saturating_add(1);
300
301 if pos == BUF_LENGTH {
302 let s = core::str::from_utf8(&buf).expect("we only write ASCII");
303 f.write_str(s)?;
304 pos = 0;
305 }
306 }
307
308 let s = core::str::from_utf8(&buf[..pos]).expect("we only write ASCII");
309 f.write_str(s)?;
310 Ok(())
311 }
312}
313
314impl fmt::Debug for Address {
315 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
316 fmt::Display::fmt(self, f)
317 }
318}
319
320#[derive(Debug, thiserror::Error)]
322pub enum ParseAddressError {
323 #[error("bech32m decoding error: {0}")]
324 Bech32(bech32::DecodeError),
325 #[error("invalid HRP: '{0}'")]
326 Hrp(bech32::Hrp),
327 #[error("address is an Arkade address and cannot be used here")]
328 Arkade,
329 #[error("unknown version: '{version}'")]
330 UnknownVersion {
331 version: bech32::Fe32,
332 },
333 #[error("invalid encoding: unexpected end of bytes")]
334 Eof,
335 #[error("invalid or unknown VTXO policy")]
336 VtxoPolicy(ProtocolDecodingError),
337 #[error("invalid address")]
338 Invalid(&'static str),
339}
340
341impl From<bech32::primitives::decode::UncheckedHrpstringError> for ParseAddressError {
342 fn from(e: bech32::primitives::decode::UncheckedHrpstringError) -> Self {
343 Self::Bech32(e.into())
344 }
345}
346
347impl From<bech32::primitives::decode::ChecksumError> for ParseAddressError {
348 fn from(e: bech32::primitives::decode::ChecksumError) -> Self {
349 Self::Bech32(bech32::DecodeError::Checksum(e))
350 }
351}
352
353impl From<io::Error> for ParseAddressError {
354 fn from(e: io::Error) -> Self {
355 match e.kind() {
356 io::ErrorKind::UnexpectedEof => ParseAddressError::Eof,
357 io::ErrorKind::InvalidData => ParseAddressError::Invalid("invalid encoding"),
358 _ => {
360 if cfg!(debug_assertions) {
361 panic!("unexpected I/O error while parsing address: {}", e);
362 }
363 ParseAddressError::Invalid("unexpected I/O error")
364 },
365 }
366 }
367}
368
369impl FromStr for Address {
370 type Err = ParseAddressError;
371 fn from_str(s: &str) -> Result<Self, Self::Err> {
372 let raw = bech32::primitives::decode::UncheckedHrpstring::new(s)?;
373
374 let testnet = if raw.hrp() == HRP_MAINNET {
375 false
376 } else if raw.hrp() == HRP_TESTNET {
377 true
378 } else {
379 return Err(ParseAddressError::Hrp(raw.hrp()));
380 };
381
382 let checked = raw.validate_and_remove_checksum::<bech32::Bech32m>()?;
383 let mut iter = checked.fe32_iter::<std::iter::Empty<u8>>();
385 let ver = iter.next().ok_or(ParseAddressError::Invalid("empty address"))?;
386
387 match ver {
388 VERSION_POLICY => {},
389 VERSION_ARKADE => return Err(ParseAddressError::Arkade),
390 _ => return Err(ParseAddressError::UnknownVersion { version: ver }),
391 }
392
393 Address::decode_payload(testnet, iter.fes_to_bytes())
394 }
395}
396
397impl serde::Serialize for Address {
398 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
399 where
400 S: serde::Serializer,
401 {
402 serializer.collect_str(&self)
403 }
404}
405
406impl<'de> serde::Deserialize<'de> for Address {
407 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
408 where
409 D: serde::Deserializer<'de>,
410 {
411 let s: Cow<'de, str> = serde::Deserialize::deserialize(deserializer)?;
412 s.parse().map_err(serde::de::Error::custom)
413 }
414}
415
416#[derive(Clone, Debug, thiserror::Error)]
418#[error("error building address: {msg}")]
419pub struct AddressBuilderError {
420 msg: &'static str,
421}
422
423impl From<&'static str> for AddressBuilderError {
424 fn from(msg: &'static str) -> Self {
425 AddressBuilderError { msg }
426 }
427}
428
429#[derive(Debug)]
434pub struct Builder {
435 testnet: bool,
436
437 server_pubkey: Option<PublicKey>,
438
439 policy: Option<VtxoPolicy>,
440
441 delivery: Vec<VtxoDelivery>,
442 mailbox_id: Option<BlindedMailboxIdentifier>,
443}
444
445impl Builder {
446 pub fn new() -> Self {
448 Self {
449 testnet: false,
450 server_pubkey: None,
451 policy: None,
452 delivery: Vec::new(),
453 mailbox_id: None,
454 }
455 }
456
457 pub fn testnet(mut self, testnet: bool) -> Self {
461 self.testnet = testnet;
462 self
463 }
464
465 pub fn server_pubkey(mut self, server_pubkey: PublicKey) -> Self {
467 self.server_pubkey = Some(server_pubkey);
468 self
469 }
470
471 pub fn policy(mut self, policy: VtxoPolicy) -> Self {
473 self.policy = Some(policy);
474 self
475 }
476
477 pub fn pubkey_policy(self, user_pubkey: PublicKey) -> Self {
479 self.policy(VtxoPolicy::new_pubkey(user_pubkey))
480 }
481
482 pub fn delivery(mut self, delivery: VtxoDelivery) -> Self {
484 self.delivery.push(delivery);
485 self
486 }
487
488 pub fn mailbox(
493 mut self,
494 server_mailbox_pubkey: PublicKey,
495 mailbox: MailboxIdentifier,
496 vtxo_key: &Keypair,
497 ) -> Result<Self, AddressBuilderError> {
498 let pol = self.policy.as_ref().ok_or("set policy first")?;
500 if vtxo_key.public_key() != pol.user_pubkey() {
501 return Err("VTXO key does not match policy".into());
502 }
503
504 self.mailbox_id = Some(mailbox.to_blinded(server_mailbox_pubkey, vtxo_key));
505 Ok(self)
506 }
507
508 pub fn into_address(self) -> Result<Address, AddressBuilderError> {
510 Ok(Address {
511 testnet: self.testnet,
512 ark_id: self.server_pubkey.ok_or("missing server pubkey")?.into(),
513 policy: self.policy.ok_or("missing policy")?,
514 delivery: {
515 let mut ret = Vec::new();
516
517 if let Some(blinded_id) = self.mailbox_id {
518 ret.push(VtxoDelivery::ServerMailbox { blinded_id });
519 }
520
521 ret.extend(self.delivery);
522 if ret.is_empty() {
523 return Err("missing delivery mechanism".into());
524 }
525
526 ret
527 }
528 })
529 }
530}
531
532struct ByteIter<T>(T);
534
535impl<T: Iterator<Item = u8>> io::Read for ByteIter<T> {
536 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
537 let mut written = 0;
538 for e in buf.iter_mut() {
539 if let Some(n) = self.0.next() {
540 *e = n;
541 written = (written as usize).saturating_add(1);
542 } else {
543 break;
544 }
545 }
546 Ok(written)
547 }
548}
549
550#[cfg(test)]
551mod test {
552 use bitcoin::secp256k1::rand;
553 use crate::SECP;
554 use super::*;
555
556 #[test]
557 fn test_versions() {
558 assert_eq!(VERSION_POLICY, bech32::Fe32::try_from(1u8).unwrap());
563 }
564
565 fn test_roundtrip(addr: &Address) -> Address {
566 let parsed = Address::from_str(&addr.to_string()).unwrap();
567 assert_eq!(parsed, *addr);
568 parsed
569 }
570
571 #[test]
572 fn address_roundtrip() {
573 let ark = PublicKey::from_str("02037188bdd7579a0cd0b22a51110986df1ea08e30192658fe0e219590e4a723d3").unwrap();
574 let ark_id = ArkId::from_server_pubkey(ark);
575 let ark_mailbox_pk = PublicKey::from_str("02165c883d8c2e3fe0887800191503beb27c9896d7ff5dfdfc5e9b9dcb25da04c1").unwrap();
576 let usr_sk = Keypair::from_str("6b0f024af54172a9aed9a0f044689175787676c469ff2aa75024cae5445c7a02").unwrap();
577 let usr = usr_sk.public_key();
578 let usr_mailbox_id = MailboxIdentifier::from_str("025d1404cf97bcbc81d0d387cd3416238aeb5362b3877fc54c0ae9b6c1f925ced1").unwrap();
579 println!("ark pk: {} (id {})", ark, ark_id);
580 println!("usr pk: {}", usr);
581 let policy = VtxoPolicy::new_pubkey(usr);
582
583 let addr = Address::builder()
585 .server_pubkey(ark)
586 .pubkey_policy(usr)
587 .mailbox(ark_mailbox_pk, usr_mailbox_id, &usr_sk).unwrap()
588 .into_address().unwrap();
589 assert_eq!(addr.to_string(), "ark1pwh9vsmezqqpharv69q4z8m6x364d5m5prnmcalcalq9pdmzw0y7mpveck4pcfhezqypczkrrj3lkx5ue4qrf4jc7ztpt9htdttmh2judhqnu7aue8p0y9mqkr4cf5");
590
591 let parsed = test_roundtrip(&addr);
592 assert_eq!(parsed.ark_id, ark_id);
593 assert_eq!(parsed.policy, policy);
594 assert!(matches!(parsed.delivery[0], VtxoDelivery::ServerMailbox { .. }));
595
596 let addr = Address::builder()
598 .testnet(true)
599 .server_pubkey(ark)
600 .pubkey_policy(usr)
601 .mailbox(ark_mailbox_pk, usr_mailbox_id, &usr_sk).unwrap()
602 .into_address().unwrap();
603 assert_eq!(addr.to_string(), "tark1pwh9vsmezqqpharv69q4z8m6x364d5m5prnmcalcalq9pdmzw0y7mpveck4pcfhezqypczkrrj3lkx5ue4qrf4jc7ztpt9htdttmh2judhqnu7aue8p0y9mq47jn9z");
604
605 let parsed = test_roundtrip(&addr);
606 assert_eq!(parsed.ark_id, ArkId::from_server_pubkey(ark));
607 assert_eq!(parsed.policy, policy);
608 assert!(matches!(parsed.delivery[0], VtxoDelivery::ServerMailbox { .. }));
609 }
610
611 #[test]
612 fn test_mailbox() {
613 let server_key = Keypair::new(&SECP, &mut rand::thread_rng());
614 let server_mailbox_key = Keypair::new(&SECP, &mut rand::thread_rng());
615 let bark_mailbox_key = Keypair::new(&SECP, &mut rand::thread_rng());
616 let vtxo_key = Keypair::new(&SECP, &mut rand::thread_rng());
617
618 let mailbox = MailboxIdentifier::from_pubkey(bark_mailbox_key.public_key());
619
620 let addr = Address::builder()
621 .server_pubkey(server_key.public_key())
622 .pubkey_policy(vtxo_key.public_key())
623 .mailbox(server_mailbox_key.public_key(), mailbox, &vtxo_key).expect("error mailbox call")
624 .into_address().unwrap();
625
626 let blinded = match addr.delivery[0] {
627 VtxoDelivery::ServerMailbox { blinded_id } => blinded_id,
628 _ => panic!("unexpected delivery"),
629 };
630
631 let unblinded = MailboxIdentifier::from_blinded(
632 blinded, addr.policy().user_pubkey(), &server_mailbox_key);
633
634 assert_eq!(mailbox, unblinded);
635 }
636
637 #[test]
638 fn rejects_oversized_field_length() {
639 let mut bytes = vec![0u8; 4]; bytes.push(0xFF); bytes.extend_from_slice(&u64::MAX.to_le_bytes());
642
643 match Address::decode_payload(false, bytes.into_iter()) {
644 Err(ParseAddressError::Invalid(_)) => {},
645 Err(e) => panic!("expected Invalid error, got {:?}", e),
646 Ok(_) => panic!("expected error, got a parsed address"),
647 }
648 }
649}