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 ServerBuiltin,
60 ServerMailbox {
62 blinded_id: BlindedMailboxIdentifier,
63 },
64 Unknown {
65 delivery_type: u8,
66 data: Vec<u8>,
67 },
68}
69
70const DELIVERY_BUILTIN: u8 = 0x00;
72const DELIVERY_MAILBOX: u8 = 0x01;
74
75impl VtxoDelivery {
76 pub fn is_unknown(&self) -> bool {
78 match self {
79 Self::Unknown { .. } => true,
80 _ => false,
81 }
82 }
83
84 fn encoded_length(&self) -> usize {
86 match self {
87 Self::ServerBuiltin => 1,
88 Self::ServerMailbox { .. } => 1 + 33,
89 Self::Unknown { data, .. } => 1 + data.len(),
90 }
91 }
92
93 fn encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<(), io::Error> {
95 match self {
96 Self::ServerBuiltin => {
97 w.emit_u8(DELIVERY_BUILTIN)?;
98 },
99 Self::ServerMailbox { blinded_id } => {
100 w.emit_u8(DELIVERY_MAILBOX)?;
101 w.emit_slice(blinded_id.as_ref())?;
102 },
103 Self::Unknown { delivery_type, data } => {
104 w.emit_u8(*delivery_type)?;
105 w.emit_slice(data)?;
106 },
107 }
108 Ok(())
109 }
110
111 fn decode(payload: &[u8]) -> Result<Self, ParseAddressError> {
113 if payload.is_empty() {
114 return Err(ParseAddressError::Eof);
115 }
116
117 match payload[0] {
118 DELIVERY_BUILTIN => Ok(Self::ServerBuiltin),
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)]
152pub struct Address {
153 testnet: bool,
154 ark_id: ArkId,
155 policy: VtxoPolicy,
156 delivery: Vec<VtxoDelivery>,
157}
158
159impl Address {
160 pub fn builder() -> Builder {
162 Builder::new()
163 }
164
165 pub fn new(
169 testnet: bool,
170 ark_id: impl Into<ArkId>,
171 policy: VtxoPolicy,
172 delivery: Vec<VtxoDelivery>,
173 ) -> Address {
174 Address {
175 testnet: testnet,
176 ark_id: ark_id.into(),
177 policy: policy,
178 delivery: delivery,
179 }
180 }
181
182 pub fn is_testnet(&self) -> bool {
184 self.testnet
185 }
186
187 pub fn ark_id(&self) -> ArkId {
189 self.ark_id
190 }
191
192 pub fn is_for_server(&self, server_pubkey: PublicKey) -> bool {
194 self.ark_id().is_for_server(server_pubkey)
195 }
196
197 pub fn policy(&self) -> &VtxoPolicy {
199 &self.policy
200 }
201
202 pub fn delivery(&self) -> &[VtxoDelivery] {
204 &self.delivery
205 }
206
207 pub fn encode_payload<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<(), io::Error> {
209 writer.emit_slice(&self.ark_id.to_byte_array())?;
210
211 let mut buf = Vec::with_capacity(128); self.policy.encode(&mut buf)?;
217 writer.emit_compact_size(buf.len() as u64)?;
218 writer.emit_slice(&buf[..])?;
219
220 for delivery in &self.delivery {
221 writer.emit_compact_size(delivery.encoded_length() as u64)?;
222 delivery.encode(writer)?;
223 }
224
225 Ok(())
226 }
227
228 pub fn decode_payload(
232 testnet: bool,
233 bytes: impl Iterator<Item = u8>,
234 ) -> Result<Address, ParseAddressError> {
235 let mut peekable = bytes.peekable();
236 let mut reader = ByteIter(&mut peekable);
237
238 let ark_id = {
239 let mut buf = [0u8; 4];
240 reader.read_slice(&mut buf).map_err(|_| ParseAddressError::Eof)?;
241 ArkId(buf)
242 };
243
244 let mut buf = Vec::new();
245 let policy = {
246 let len = reader.read_compact_size()? as usize;
247 buf.resize(len, 0);
248 reader.read_slice(&mut buf[..])?;
249 VtxoPolicy::deserialize(&buf[..]).map_err(ParseAddressError::VtxoPolicy)?
250 };
251
252 let mut delivery = Vec::new();
253 while reader.0.peek().is_some() {
254 let len = reader.read_compact_size()? as usize;
255 buf.resize(len, 0);
256 reader.read_slice(&mut buf[..])?;
257 delivery.push(VtxoDelivery::decode(&buf[..])?);
258 }
259
260 Ok(Address::new(testnet, ark_id, policy, delivery))
261 }
262}
263
264impl fmt::Display for Address {
265 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
266 let hrp = if self.testnet {
267 HRP_TESTNET
268 } else {
269 HRP_MAINNET
270 };
271
272 let ver = VERSION_POLICY;
273 let payload = {
274 let mut buf = Vec::with_capacity(128);
275 self.encode_payload(&mut buf).expect("buffers don't error");
276 buf
277 };
278
279 let chars = [ver].into_iter().chain(payload.into_iter().bytes_to_fes())
280 .with_checksum::<bech32::Bech32m>(&hrp)
281 .chars();
282
283 const BUF_LENGTH: usize = 128;
285 let mut buf = [0u8; BUF_LENGTH];
286 let mut pos = 0;
287 for c in chars {
288 buf[pos] = c as u8;
289 pos += 1;
290
291 if pos == BUF_LENGTH {
292 let s = core::str::from_utf8(&buf).expect("we only write ASCII");
293 f.write_str(s)?;
294 pos = 0;
295 }
296 }
297
298 let s = core::str::from_utf8(&buf[..pos]).expect("we only write ASCII");
299 f.write_str(s)?;
300 Ok(())
301 }
302}
303
304impl fmt::Debug for Address {
305 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
306 fmt::Display::fmt(self, f)
307 }
308}
309
310#[derive(Debug, thiserror::Error)]
312pub enum ParseAddressError {
313 #[error("bech32m decoding error: {0}")]
314 Bech32(bech32::DecodeError),
315 #[error("invalid HRP: '{0}'")]
316 Hrp(bech32::Hrp),
317 #[error("address ins an Arkade address and cannot be used here")]
318 Arkade,
319 #[error("unknown version: '{version}'")]
320 UnknownVersion {
321 version: bech32::Fe32,
322 },
323 #[error("invalid encoding: unexpected end of bytes")]
324 Eof,
325 #[error("invalid or unknown VTXO policy")]
326 VtxoPolicy(ProtocolDecodingError),
327 #[error("invalid address")]
328 Invalid(&'static str),
329}
330
331impl From<bech32::primitives::decode::UncheckedHrpstringError> for ParseAddressError {
332 fn from(e: bech32::primitives::decode::UncheckedHrpstringError) -> Self {
333 Self::Bech32(e.into())
334 }
335}
336
337impl From<bech32::primitives::decode::ChecksumError> for ParseAddressError {
338 fn from(e: bech32::primitives::decode::ChecksumError) -> Self {
339 Self::Bech32(bech32::DecodeError::Checksum(e))
340 }
341}
342
343impl From<io::Error> for ParseAddressError {
344 fn from(e: io::Error) -> Self {
345 match e.kind() {
346 io::ErrorKind::UnexpectedEof => ParseAddressError::Eof,
347 io::ErrorKind::InvalidData => ParseAddressError::Invalid("invalid encoding"),
348 _ => {
350 if cfg!(debug_assertions) {
351 panic!("unexpected I/O error while parsing address: {}", e);
352 }
353 ParseAddressError::Invalid("unexpected I/O error")
354 },
355 }
356 }
357}
358
359impl FromStr for Address {
360 type Err = ParseAddressError;
361 fn from_str(s: &str) -> Result<Self, Self::Err> {
362 let raw = bech32::primitives::decode::UncheckedHrpstring::new(s)?;
363
364 let testnet = if raw.hrp() == HRP_MAINNET {
365 false
366 } else if raw.hrp() == HRP_TESTNET {
367 true
368 } else {
369 return Err(ParseAddressError::Hrp(raw.hrp()));
370 };
371
372 let checked = raw.validate_and_remove_checksum::<bech32::Bech32m>()?;
373 let mut iter = checked.fe32_iter::<std::iter::Empty<u8>>();
375 let ver = iter.next().ok_or(ParseAddressError::Invalid("empty address"))?;
376
377 match ver {
378 VERSION_POLICY => {},
379 VERSION_ARKADE => return Err(ParseAddressError::Arkade),
380 _ => return Err(ParseAddressError::UnknownVersion { version: ver }),
381 }
382
383 Address::decode_payload(testnet, iter.fes_to_bytes())
384 }
385}
386
387impl serde::Serialize for Address {
388 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
389 where
390 S: serde::Serializer,
391 {
392 serializer.collect_str(&self)
393 }
394}
395
396impl<'de> serde::Deserialize<'de> for Address {
397 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
398 where
399 D: serde::Deserializer<'de>,
400 {
401 let s: Cow<'de, str> = serde::Deserialize::deserialize(deserializer)?;
402 s.parse().map_err(serde::de::Error::custom)
403 }
404}
405
406#[derive(Clone, Debug, thiserror::Error)]
408#[error("error building address: {msg}")]
409pub struct AddressBuilderError {
410 msg: &'static str,
411}
412
413impl From<&'static str> for AddressBuilderError {
414 fn from(msg: &'static str) -> Self {
415 AddressBuilderError { msg }
416 }
417}
418
419#[derive(Debug)]
425pub struct Builder {
426 testnet: bool,
427
428 server_pubkey: Option<PublicKey>,
429
430 policy: Option<VtxoPolicy>,
431
432 delivery: Vec<VtxoDelivery>,
433 mailbox_id: Option<BlindedMailboxIdentifier>,
434 add_builtin_delivery: bool,
435}
436
437impl Builder {
438 pub fn new() -> Self {
440 Self {
441 testnet: false,
442 server_pubkey: None,
443 policy: None,
444 delivery: Vec::new(),
445 mailbox_id: None,
446 add_builtin_delivery: true,
447 }
448 }
449
450 pub fn testnet(mut self, testnet: bool) -> Self {
454 self.testnet = testnet;
455 self
456 }
457
458 pub fn server_pubkey(mut self, server_pubkey: PublicKey) -> Self {
460 self.server_pubkey = Some(server_pubkey);
461 self
462 }
463
464 pub fn policy(mut self, policy: VtxoPolicy) -> Self {
466 self.policy = Some(policy);
467 self
468 }
469
470 pub fn pubkey_policy(self, user_pubkey: PublicKey) -> Self {
472 self.policy(VtxoPolicy::new_pubkey(user_pubkey))
473 }
474
475 pub fn delivery(mut self, delivery: VtxoDelivery) -> Self {
477 self.delivery.push(delivery);
478 self
479 }
480
481 pub fn no_delivery(mut self) -> Self {
483 self.delivery.clear();
484 self.add_builtin_delivery = false;
485 self
486 }
487
488 pub fn mailbox(
495 mut self,
496 server_mailbox_pubkey: PublicKey,
497 mailbox: MailboxIdentifier,
498 vtxo_key: &Keypair,
499 ) -> Result<Self, AddressBuilderError> {
500 let pol = self.policy.as_ref().ok_or("set policy first")?;
502 if vtxo_key.public_key() != pol.user_pubkey() {
503 return Err("VTXO key does not match policy".into());
504 }
505
506 self.mailbox_id = Some(mailbox.to_blinded(server_mailbox_pubkey, vtxo_key));
507 self.add_builtin_delivery = false;
508 Ok(self)
509 }
510
511 pub fn into_address(self) -> Result<Address, AddressBuilderError> {
513 Ok(Address {
514 testnet: self.testnet,
515 ark_id: self.server_pubkey.ok_or("missing server pubkey")?.into(),
516 policy: self.policy.ok_or("missing policy")?,
517 delivery: {
518 let mut ret = Vec::new();
519 if self.delivery.is_empty() && self.add_builtin_delivery {
520 ret.push(VtxoDelivery::ServerBuiltin);
521 }
522
523 if let Some(blinded_id) = self.mailbox_id {
524 ret.push(VtxoDelivery::ServerMailbox { blinded_id });
525 }
526
527 ret.extend(self.delivery);
528 ret
529 }
530 })
531 }
532}
533
534struct ByteIter<T>(T);
536
537impl<T: Iterator<Item = u8>> io::Read for ByteIter<T> {
538 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
539 let mut written = 0;
540 for e in buf.iter_mut() {
541 if let Some(n) = self.0.next() {
542 *e = n;
543 written += 1;
544 } else {
545 break;
546 }
547 }
548 Ok(written)
549 }
550}
551
552#[cfg(test)]
553mod test {
554 use bitcoin::secp256k1::rand;
555 use crate::SECP;
556 use super::*;
557
558 #[test]
559 fn test_versions() {
560 assert_eq!(VERSION_POLICY, bech32::Fe32::try_from(1u8).unwrap());
565 }
566
567 fn test_roundtrip(addr: &Address) -> Address {
568 let parsed = Address::from_str(&addr.to_string()).unwrap();
569 assert_eq!(parsed, *addr);
570 parsed
571 }
572
573 #[test]
574 fn address_roundtrip() {
575 let ark = PublicKey::from_str("02037188bdd7579a0cd0b22a51110986df1ea08e30192658fe0e219590e4a723d3").unwrap();
576 let ark_id = ArkId::from_server_pubkey(ark);
577 let usr = PublicKey::from_str("032217b6ccba4fa98cc433abe4be1ceaf41ea61fd83fcefd27384ca4612ce19512").unwrap();
578 println!("ark pk: {} (id {})", ark, ark_id);
579 println!("usr pk: {}", usr);
580 let policy = VtxoPolicy::new_pubkey(usr);
581
582 let addr = Address::builder()
584 .server_pubkey(ark)
585 .pubkey_policy(usr)
586 .no_delivery()
587 .into_address().unwrap();
588 assert_eq!(addr.to_string(), "ark1pwh9vsmezqqpjy9akejayl2vvcse6he97rn40g84xrlvrlnhayuuyefrp9nse2yszc7ehh");
589
590 let parsed = test_roundtrip(&addr);
591 assert_eq!(parsed.ark_id, ark_id);
592 assert_eq!(parsed.policy, policy);
593 assert_eq!(parsed.delivery.len(), 0);
594
595 let addr = Address::builder()
597 .testnet(true)
598 .server_pubkey(ark)
599 .pubkey_policy(usr)
600 .no_delivery()
601 .into_address().unwrap();
602 assert_eq!(addr.to_string(), "tark1pwh9vsmezqqpjy9akejayl2vvcse6he97rn40g84xrlvrlnhayuuyefrp9nse2ysm2x4mn");
603
604 let parsed = test_roundtrip(&addr);
605 assert_eq!(parsed.ark_id, ArkId::from_server_pubkey(ark));
606 assert_eq!(parsed.policy, policy);
607 assert_eq!(parsed.delivery.len(), 0);
608 }
609
610 #[test]
611 fn test_mailbox() {
612 let server_key = Keypair::new(&SECP, &mut rand::thread_rng());
613 let server_mailbox_key = Keypair::new(&SECP, &mut rand::thread_rng());
614 let bark_mailbox_key = Keypair::new(&SECP, &mut rand::thread_rng());
615 let vtxo_key = Keypair::new(&SECP, &mut rand::thread_rng());
616
617 let mailbox = MailboxIdentifier::from_pubkey(bark_mailbox_key.public_key());
618
619 let addr = Address::builder()
620 .server_pubkey(server_key.public_key())
621 .pubkey_policy(vtxo_key.public_key())
622 .mailbox(server_mailbox_key.public_key(), mailbox, &vtxo_key).expect("error mailbox call")
623 .into_address().unwrap();
624
625 let blinded = match addr.delivery[0] {
626 VtxoDelivery::ServerMailbox { blinded_id } => blinded_id,
627 _ => panic!("unexpected delivery"),
628 };
629
630 let unblinded = MailboxIdentifier::from_blinded(
631 blinded, addr.policy().user_pubkey(), &server_mailbox_key);
632
633 assert_eq!(mailbox, unblinded);
634 }
635}