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