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::{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, .. } => 1 + 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()? as usize;
251 buf.resize(len, 0);
252 reader.read_slice(&mut buf[..])?;
253 VtxoPolicy::deserialize(&buf[..]).map_err(ParseAddressError::VtxoPolicy)?
254 };
255
256 let mut delivery = Vec::new();
257 while reader.0.peek().is_some() {
258 let len = reader.read_compact_size()? as usize;
259 buf.resize(len, 0);
260 reader.read_slice(&mut buf[..])?;
261 delivery.push(VtxoDelivery::decode(&buf[..])?);
262 }
263
264 Ok(Address::new(testnet, ark_id, policy, delivery))
265 }
266}
267
268impl fmt::Display for Address {
269 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
270 let hrp = if self.testnet {
271 HRP_TESTNET
272 } else {
273 HRP_MAINNET
274 };
275
276 let ver = VERSION_POLICY;
277 let payload = {
278 let mut buf = Vec::with_capacity(128);
279 self.encode_payload(&mut buf).expect("buffers don't error");
280 buf
281 };
282
283 let chars = [ver].into_iter().chain(payload.into_iter().bytes_to_fes())
284 .with_checksum::<bech32::Bech32m>(&hrp)
285 .chars();
286
287 const BUF_LENGTH: usize = 128;
289 let mut buf = [0u8; BUF_LENGTH];
290 let mut pos = 0;
291 for c in chars {
292 buf[pos] = c as u8;
293 pos += 1;
294
295 if pos == BUF_LENGTH {
296 let s = core::str::from_utf8(&buf).expect("we only write ASCII");
297 f.write_str(s)?;
298 pos = 0;
299 }
300 }
301
302 let s = core::str::from_utf8(&buf[..pos]).expect("we only write ASCII");
303 f.write_str(s)?;
304 Ok(())
305 }
306}
307
308impl fmt::Debug for Address {
309 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
310 fmt::Display::fmt(self, f)
311 }
312}
313
314#[derive(Debug, thiserror::Error)]
316pub enum ParseAddressError {
317 #[error("bech32m decoding error: {0}")]
318 Bech32(bech32::DecodeError),
319 #[error("invalid HRP: '{0}'")]
320 Hrp(bech32::Hrp),
321 #[error("address is an Arkade address and cannot be used here")]
322 Arkade,
323 #[error("unknown version: '{version}'")]
324 UnknownVersion {
325 version: bech32::Fe32,
326 },
327 #[error("invalid encoding: unexpected end of bytes")]
328 Eof,
329 #[error("invalid or unknown VTXO policy")]
330 VtxoPolicy(ProtocolDecodingError),
331 #[error("invalid address")]
332 Invalid(&'static str),
333}
334
335impl From<bech32::primitives::decode::UncheckedHrpstringError> for ParseAddressError {
336 fn from(e: bech32::primitives::decode::UncheckedHrpstringError) -> Self {
337 Self::Bech32(e.into())
338 }
339}
340
341impl From<bech32::primitives::decode::ChecksumError> for ParseAddressError {
342 fn from(e: bech32::primitives::decode::ChecksumError) -> Self {
343 Self::Bech32(bech32::DecodeError::Checksum(e))
344 }
345}
346
347impl From<io::Error> for ParseAddressError {
348 fn from(e: io::Error) -> Self {
349 match e.kind() {
350 io::ErrorKind::UnexpectedEof => ParseAddressError::Eof,
351 io::ErrorKind::InvalidData => ParseAddressError::Invalid("invalid encoding"),
352 _ => {
354 if cfg!(debug_assertions) {
355 panic!("unexpected I/O error while parsing address: {}", e);
356 }
357 ParseAddressError::Invalid("unexpected I/O error")
358 },
359 }
360 }
361}
362
363impl FromStr for Address {
364 type Err = ParseAddressError;
365 fn from_str(s: &str) -> Result<Self, Self::Err> {
366 let raw = bech32::primitives::decode::UncheckedHrpstring::new(s)?;
367
368 let testnet = if raw.hrp() == HRP_MAINNET {
369 false
370 } else if raw.hrp() == HRP_TESTNET {
371 true
372 } else {
373 return Err(ParseAddressError::Hrp(raw.hrp()));
374 };
375
376 let checked = raw.validate_and_remove_checksum::<bech32::Bech32m>()?;
377 let mut iter = checked.fe32_iter::<std::iter::Empty<u8>>();
379 let ver = iter.next().ok_or(ParseAddressError::Invalid("empty address"))?;
380
381 match ver {
382 VERSION_POLICY => {},
383 VERSION_ARKADE => return Err(ParseAddressError::Arkade),
384 _ => return Err(ParseAddressError::UnknownVersion { version: ver }),
385 }
386
387 Address::decode_payload(testnet, iter.fes_to_bytes())
388 }
389}
390
391impl serde::Serialize for Address {
392 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
393 where
394 S: serde::Serializer,
395 {
396 serializer.collect_str(&self)
397 }
398}
399
400impl<'de> serde::Deserialize<'de> for Address {
401 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
402 where
403 D: serde::Deserializer<'de>,
404 {
405 let s: Cow<'de, str> = serde::Deserialize::deserialize(deserializer)?;
406 s.parse().map_err(serde::de::Error::custom)
407 }
408}
409
410#[derive(Clone, Debug, thiserror::Error)]
412#[error("error building address: {msg}")]
413pub struct AddressBuilderError {
414 msg: &'static str,
415}
416
417impl From<&'static str> for AddressBuilderError {
418 fn from(msg: &'static str) -> Self {
419 AddressBuilderError { msg }
420 }
421}
422
423#[derive(Debug)]
428pub struct Builder {
429 testnet: bool,
430
431 server_pubkey: Option<PublicKey>,
432
433 policy: Option<VtxoPolicy>,
434
435 delivery: Vec<VtxoDelivery>,
436 mailbox_id: Option<BlindedMailboxIdentifier>,
437}
438
439impl Builder {
440 pub fn new() -> Self {
442 Self {
443 testnet: false,
444 server_pubkey: None,
445 policy: None,
446 delivery: Vec::new(),
447 mailbox_id: None,
448 }
449 }
450
451 pub fn testnet(mut self, testnet: bool) -> Self {
455 self.testnet = testnet;
456 self
457 }
458
459 pub fn server_pubkey(mut self, server_pubkey: PublicKey) -> Self {
461 self.server_pubkey = Some(server_pubkey);
462 self
463 }
464
465 pub fn policy(mut self, policy: VtxoPolicy) -> Self {
467 self.policy = Some(policy);
468 self
469 }
470
471 pub fn pubkey_policy(self, user_pubkey: PublicKey) -> Self {
473 self.policy(VtxoPolicy::new_pubkey(user_pubkey))
474 }
475
476 pub fn delivery(mut self, delivery: VtxoDelivery) -> Self {
478 self.delivery.push(delivery);
479 self
480 }
481
482 pub fn mailbox(
487 mut self,
488 server_mailbox_pubkey: PublicKey,
489 mailbox: MailboxIdentifier,
490 vtxo_key: &Keypair,
491 ) -> Result<Self, AddressBuilderError> {
492 let pol = self.policy.as_ref().ok_or("set policy first")?;
494 if vtxo_key.public_key() != pol.user_pubkey() {
495 return Err("VTXO key does not match policy".into());
496 }
497
498 self.mailbox_id = Some(mailbox.to_blinded(server_mailbox_pubkey, vtxo_key));
499 Ok(self)
500 }
501
502 pub fn into_address(self) -> Result<Address, AddressBuilderError> {
504 Ok(Address {
505 testnet: self.testnet,
506 ark_id: self.server_pubkey.ok_or("missing server pubkey")?.into(),
507 policy: self.policy.ok_or("missing policy")?,
508 delivery: {
509 let mut ret = Vec::new();
510
511 if let Some(blinded_id) = self.mailbox_id {
512 ret.push(VtxoDelivery::ServerMailbox { blinded_id });
513 }
514
515 ret.extend(self.delivery);
516 if ret.is_empty() {
517 return Err("missing delivery mechanism".into());
518 }
519
520 ret
521 }
522 })
523 }
524}
525
526struct ByteIter<T>(T);
528
529impl<T: Iterator<Item = u8>> io::Read for ByteIter<T> {
530 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
531 let mut written = 0;
532 for e in buf.iter_mut() {
533 if let Some(n) = self.0.next() {
534 *e = n;
535 written += 1;
536 } else {
537 break;
538 }
539 }
540 Ok(written)
541 }
542}
543
544#[cfg(test)]
545mod test {
546 use bitcoin::secp256k1::rand;
547 use crate::SECP;
548 use super::*;
549
550 #[test]
551 fn test_versions() {
552 assert_eq!(VERSION_POLICY, bech32::Fe32::try_from(1u8).unwrap());
557 }
558
559 fn test_roundtrip(addr: &Address) -> Address {
560 let parsed = Address::from_str(&addr.to_string()).unwrap();
561 assert_eq!(parsed, *addr);
562 parsed
563 }
564
565 #[test]
566 fn address_roundtrip() {
567 let ark = PublicKey::from_str("02037188bdd7579a0cd0b22a51110986df1ea08e30192658fe0e219590e4a723d3").unwrap();
568 let ark_id = ArkId::from_server_pubkey(ark);
569 let ark_mailbox_pk = PublicKey::from_str("02165c883d8c2e3fe0887800191503beb27c9896d7ff5dfdfc5e9b9dcb25da04c1").unwrap();
570 let usr_sk = Keypair::from_str("6b0f024af54172a9aed9a0f044689175787676c469ff2aa75024cae5445c7a02").unwrap();
571 let usr = usr_sk.public_key();
572 let usr_mailbox_id = MailboxIdentifier::from_str("025d1404cf97bcbc81d0d387cd3416238aeb5362b3877fc54c0ae9b6c1f925ced1").unwrap();
573 println!("ark pk: {} (id {})", ark, ark_id);
574 println!("usr pk: {}", usr);
575 let policy = VtxoPolicy::new_pubkey(usr);
576
577 let addr = Address::builder()
579 .server_pubkey(ark)
580 .pubkey_policy(usr)
581 .mailbox(ark_mailbox_pk, usr_mailbox_id, &usr_sk).unwrap()
582 .into_address().unwrap();
583 assert_eq!(addr.to_string(), "ark1pwh9vsmezqqpharv69q4z8m6x364d5m5prnmcalcalq9pdmzw0y7mpveck4pcfhezqypczkrrj3lkx5ue4qrf4jc7ztpt9htdttmh2judhqnu7aue8p0y9mqkr4cf5");
584
585 let parsed = test_roundtrip(&addr);
586 assert_eq!(parsed.ark_id, ark_id);
587 assert_eq!(parsed.policy, policy);
588 assert!(matches!(parsed.delivery[0], VtxoDelivery::ServerMailbox { .. }));
589
590 let addr = Address::builder()
592 .testnet(true)
593 .server_pubkey(ark)
594 .pubkey_policy(usr)
595 .mailbox(ark_mailbox_pk, usr_mailbox_id, &usr_sk).unwrap()
596 .into_address().unwrap();
597 assert_eq!(addr.to_string(), "tark1pwh9vsmezqqpharv69q4z8m6x364d5m5prnmcalcalq9pdmzw0y7mpveck4pcfhezqypczkrrj3lkx5ue4qrf4jc7ztpt9htdttmh2judhqnu7aue8p0y9mq47jn9z");
598
599 let parsed = test_roundtrip(&addr);
600 assert_eq!(parsed.ark_id, ArkId::from_server_pubkey(ark));
601 assert_eq!(parsed.policy, policy);
602 assert!(matches!(parsed.delivery[0], VtxoDelivery::ServerMailbox { .. }));
603 }
604
605 #[test]
606 fn test_mailbox() {
607 let server_key = Keypair::new(&SECP, &mut rand::thread_rng());
608 let server_mailbox_key = Keypair::new(&SECP, &mut rand::thread_rng());
609 let bark_mailbox_key = Keypair::new(&SECP, &mut rand::thread_rng());
610 let vtxo_key = Keypair::new(&SECP, &mut rand::thread_rng());
611
612 let mailbox = MailboxIdentifier::from_pubkey(bark_mailbox_key.public_key());
613
614 let addr = Address::builder()
615 .server_pubkey(server_key.public_key())
616 .pubkey_policy(vtxo_key.public_key())
617 .mailbox(server_mailbox_key.public_key(), mailbox, &vtxo_key).expect("error mailbox call")
618 .into_address().unwrap();
619
620 let blinded = match addr.delivery[0] {
621 VtxoDelivery::ServerMailbox { blinded_id } => blinded_id,
622 _ => panic!("unexpected delivery"),
623 };
624
625 let unblinded = MailboxIdentifier::from_blinded(
626 blinded, addr.policy().user_pubkey(), &server_mailbox_key);
627
628 assert_eq!(mailbox, unblinded);
629 }
630}