1
2use std::{fmt, io};
3use std::str::FromStr;
4
5use bitcoin::bech32::{self, ByteIterExt, Fe32IterExt};
6use bitcoin::hashes::{sha256, Hash};
7use bitcoin::secp256k1::{Keypair, PublicKey};
8
9use crate::{ProtocolDecodingError, ProtocolEncoding, VtxoPolicy};
10use crate::encode::{ReadExt, WriteExt};
11use crate::mailbox::{BlindedMailboxIdentifier, MailboxIdentifier};
12
13
14const HRP_MAINNET: bech32::Hrp = bech32::Hrp::parse_unchecked("ark");
16
17const HRP_TESTNET: bech32::Hrp = bech32::Hrp::parse_unchecked("tark");
19
20const VERSION_ARKADE: bech32::Fe32 = bech32::Fe32::Q;
22
23const VERSION_POLICY: bech32::Fe32 = bech32::Fe32::P;
25
26
27#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
29pub struct ArkId([u8; 4]);
30impl_byte_newtype!(ArkId, 4);
31
32impl ArkId {
33 pub fn from_server_pubkey(server_pubkey: PublicKey) -> ArkId {
35 let mut buf = [0u8; 4];
36 let hash = sha256::Hash::hash(&server_pubkey.serialize());
37 buf[0..4].copy_from_slice(&hash[0..4]);
38 ArkId(buf)
39 }
40
41 pub fn is_for_server(&self, server_pubkey: PublicKey) -> bool {
43 *self == ArkId::from_server_pubkey(server_pubkey)
44 }
45}
46
47impl From<PublicKey> for ArkId {
48 fn from(pk: PublicKey) -> Self {
49 ArkId::from_server_pubkey(pk)
50 }
51}
52
53#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
55#[non_exhaustive]
56pub enum VtxoDelivery {
57 ServerBuiltin,
59 ServerMailbox {
61 blinded_id: BlindedMailboxIdentifier,
62 },
63 Unknown {
64 delivery_type: u8,
65 data: Vec<u8>,
66 },
67}
68
69const DELIVERY_BUILTIN: u8 = 0x00;
71const DELIVERY_MAILBOX: u8 = 0x01;
73
74impl VtxoDelivery {
75 pub fn is_unknown(&self) -> bool {
77 match self {
78 Self::Unknown { .. } => true,
79 _ => false,
80 }
81 }
82
83 fn encoded_length(&self) -> usize {
85 match self {
86 Self::ServerBuiltin => 1,
87 Self::ServerMailbox { .. } => 1 + 33,
88 Self::Unknown { data, .. } => 1 + data.len(),
89 }
90 }
91
92 fn encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<(), io::Error> {
94 match self {
95 Self::ServerBuiltin => {
96 w.emit_u8(DELIVERY_BUILTIN)?;
97 },
98 Self::ServerMailbox { blinded_id } => {
99 w.emit_u8(DELIVERY_MAILBOX)?;
100 w.emit_slice(blinded_id.as_ref())?;
101 },
102 Self::Unknown { delivery_type, data } => {
103 w.emit_u8(*delivery_type)?;
104 w.emit_slice(data)?;
105 },
106 }
107 Ok(())
108 }
109
110 fn decode(payload: &[u8]) -> Result<Self, ParseAddressError> {
112 if payload.is_empty() {
113 return Err(ParseAddressError::Eof);
114 }
115
116 match payload[0] {
117 DELIVERY_BUILTIN => Ok(Self::ServerBuiltin),
118 DELIVERY_MAILBOX => Ok(Self::ServerMailbox {
119 blinded_id: BlindedMailboxIdentifier::from_slice(&payload[1..]).map_err(
120 |_| ParseAddressError::Invalid("invalid blinded mailbox identifier"),
121 )?,
122 }),
123 delivery_type => Ok(Self::Unknown {
124 delivery_type: delivery_type,
125 data: payload[1..].to_vec(),
126 }),
127 }
128 }
129}
130
131#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
150pub struct Address {
151 testnet: bool,
152 ark_id: ArkId,
153 policy: VtxoPolicy,
154 delivery: Vec<VtxoDelivery>,
155}
156
157impl Address {
158 pub fn builder() -> Builder {
160 Builder::new()
161 }
162
163 pub fn new(
167 testnet: bool,
168 ark_id: impl Into<ArkId>,
169 policy: VtxoPolicy,
170 delivery: Vec<VtxoDelivery>,
171 ) -> Address {
172 Address {
173 testnet: testnet,
174 ark_id: ark_id.into(),
175 policy: policy,
176 delivery: delivery,
177 }
178 }
179
180 pub fn is_testnet(&self) -> bool {
182 self.testnet
183 }
184
185 pub fn ark_id(&self) -> ArkId {
187 self.ark_id
188 }
189
190 pub fn is_for_server(&self, server_pubkey: PublicKey) -> bool {
192 self.ark_id().is_for_server(server_pubkey)
193 }
194
195 pub fn policy(&self) -> &VtxoPolicy {
197 &self.policy
198 }
199
200 pub fn delivery(&self) -> &[VtxoDelivery] {
202 &self.delivery
203 }
204
205 pub fn encode_payload<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<(), io::Error> {
207 writer.emit_slice(&self.ark_id.to_byte_array())?;
208
209 let mut buf = Vec::with_capacity(128); self.policy.encode(&mut buf)?;
215 writer.emit_compact_size(buf.len() as u64)?;
216 writer.emit_slice(&buf[..])?;
217
218 for delivery in &self.delivery {
219 writer.emit_compact_size(delivery.encoded_length() as u64)?;
220 delivery.encode(writer)?;
221 }
222
223 Ok(())
224 }
225
226 pub fn decode_payload(
230 testnet: bool,
231 bytes: impl Iterator<Item = u8>,
232 ) -> Result<Address, ParseAddressError> {
233 let mut peekable = bytes.peekable();
234 let mut reader = ByteIter(&mut peekable);
235
236 let ark_id = {
237 let mut buf = [0u8; 4];
238 reader.read_slice(&mut buf).map_err(|_| ParseAddressError::Eof)?;
239 ArkId(buf)
240 };
241
242 let mut buf = Vec::new();
243 let policy = {
244 let len = reader.read_compact_size()? as usize;
245 buf.resize(len, 0);
246 reader.read_slice(&mut buf[..])?;
247 VtxoPolicy::deserialize(&buf[..]).map_err(ParseAddressError::VtxoPolicy)?
248 };
249
250 let mut delivery = Vec::new();
251 while reader.0.peek().is_some() {
252 let len = reader.read_compact_size()? as usize;
253 buf.resize(len, 0);
254 reader.read_slice(&mut buf[..])?;
255 delivery.push(VtxoDelivery::decode(&buf[..])?);
256 }
257
258 Ok(Address::new(testnet, ark_id, policy, delivery))
259 }
260}
261
262impl fmt::Display for Address {
263 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
264 let hrp = if self.testnet {
265 HRP_TESTNET
266 } else {
267 HRP_MAINNET
268 };
269
270 let ver = VERSION_POLICY;
271 let payload = {
272 let mut buf = Vec::with_capacity(128);
273 self.encode_payload(&mut buf).expect("buffers don't error");
274 buf
275 };
276
277 let chars = [ver].into_iter().chain(payload.into_iter().bytes_to_fes())
278 .with_checksum::<bech32::Bech32m>(&hrp)
279 .chars();
280
281 const BUF_LENGTH: usize = 128;
283 let mut buf = [0u8; BUF_LENGTH];
284 let mut pos = 0;
285 for c in chars {
286 buf[pos] = c as u8;
287 pos += 1;
288
289 if pos == BUF_LENGTH {
290 let s = core::str::from_utf8(&buf).expect("we only write ASCII");
291 f.write_str(s)?;
292 pos = 0;
293 }
294 }
295
296 let s = core::str::from_utf8(&buf[..pos]).expect("we only write ASCII");
297 f.write_str(s)?;
298 Ok(())
299 }
300}
301
302impl fmt::Debug for Address {
303 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
304 fmt::Display::fmt(self, f)
305 }
306}
307
308#[derive(Debug, thiserror::Error)]
310pub enum ParseAddressError {
311 #[error("bech32m decoding error: {0}")]
312 Bech32(bech32::DecodeError),
313 #[error("invalid HRP: '{0}'")]
314 Hrp(bech32::Hrp),
315 #[error("address ins an Arkade address and cannot be used here")]
316 Arkade,
317 #[error("unknown version: '{version}'")]
318 UnknownVersion {
319 version: bech32::Fe32,
320 },
321 #[error("invalid encoding: unexpected end of bytes")]
322 Eof,
323 #[error("invalid or unknown VTXO policy")]
324 VtxoPolicy(ProtocolDecodingError),
325 #[error("invalid address")]
326 Invalid(&'static str),
327}
328
329impl From<bech32::primitives::decode::UncheckedHrpstringError> for ParseAddressError {
330 fn from(e: bech32::primitives::decode::UncheckedHrpstringError) -> Self {
331 Self::Bech32(e.into())
332 }
333}
334
335impl From<bech32::primitives::decode::ChecksumError> for ParseAddressError {
336 fn from(e: bech32::primitives::decode::ChecksumError) -> Self {
337 Self::Bech32(bech32::DecodeError::Checksum(e))
338 }
339}
340
341impl From<io::Error> for ParseAddressError {
342 fn from(e: io::Error) -> Self {
343 match e.kind() {
344 io::ErrorKind::UnexpectedEof => ParseAddressError::Eof,
345 io::ErrorKind::InvalidData => ParseAddressError::Invalid("invalid encoding"),
346 _ => {
348 if cfg!(debug_assertions) {
349 panic!("unexpected I/O error while parsing address: {}", e);
350 }
351 ParseAddressError::Invalid("unexpected I/O error")
352 },
353 }
354 }
355}
356
357impl FromStr for Address {
358 type Err = ParseAddressError;
359 fn from_str(s: &str) -> Result<Self, Self::Err> {
360 let raw = bech32::primitives::decode::UncheckedHrpstring::new(s)?;
361
362 let testnet = if raw.hrp() == HRP_MAINNET {
363 false
364 } else if raw.hrp() == HRP_TESTNET {
365 true
366 } else {
367 return Err(ParseAddressError::Hrp(raw.hrp()));
368 };
369
370 let checked = raw.validate_and_remove_checksum::<bech32::Bech32m>()?;
371 let mut iter = checked.fe32_iter::<std::iter::Empty<u8>>();
373 let ver = iter.next().ok_or(ParseAddressError::Invalid("empty address"))?;
374
375 match ver {
376 VERSION_POLICY => {},
377 VERSION_ARKADE => return Err(ParseAddressError::Arkade),
378 _ => return Err(ParseAddressError::UnknownVersion { version: ver }),
379 }
380
381 Address::decode_payload(testnet, iter.fes_to_bytes())
382 }
383}
384
385#[derive(Clone, Debug, thiserror::Error)]
387#[error("error building address: {msg}")]
388pub struct AddressBuilderError {
389 msg: &'static str,
390}
391
392impl From<&'static str> for AddressBuilderError {
393 fn from(msg: &'static str) -> Self {
394 AddressBuilderError { msg }
395 }
396}
397
398#[derive(Debug)]
404pub struct Builder {
405 testnet: bool,
406
407 server_pubkey: Option<PublicKey>,
408
409 policy: Option<VtxoPolicy>,
410
411 delivery: Vec<VtxoDelivery>,
412 mailbox_id: Option<BlindedMailboxIdentifier>,
413 add_builtin_delivery: bool,
414}
415
416impl Builder {
417 pub fn new() -> Self {
419 Self {
420 testnet: false,
421 server_pubkey: None,
422 policy: None,
423 delivery: Vec::new(),
424 mailbox_id: None,
425 add_builtin_delivery: true,
426 }
427 }
428
429 pub fn testnet(mut self, testnet: bool) -> Self {
433 self.testnet = testnet;
434 self
435 }
436
437 pub fn server_pubkey(mut self, server_pubkey: PublicKey) -> Self {
439 self.server_pubkey = Some(server_pubkey);
440 self
441 }
442
443 pub fn policy(mut self, policy: VtxoPolicy) -> Self {
445 self.policy = Some(policy);
446 self
447 }
448
449 pub fn pubkey_policy(self, user_pubkey: PublicKey) -> Self {
451 self.policy(VtxoPolicy::new_pubkey(user_pubkey))
452 }
453
454 pub fn delivery(mut self, delivery: VtxoDelivery) -> Self {
456 self.delivery.push(delivery);
457 self
458 }
459
460 pub fn no_delivery(mut self) -> Self {
462 self.delivery.clear();
463 self.add_builtin_delivery = false;
464 self
465 }
466
467 pub fn mailbox(
474 mut self,
475 mailbox: MailboxIdentifier,
476 vtxo_key: &Keypair,
477 ) -> Result<Self, AddressBuilderError> {
478 let pol = self.policy.as_ref().ok_or("set policy first")?;
480 if vtxo_key.public_key() != pol.user_pubkey() {
481 return Err("VTXO key does not match policy".into());
482 }
483
484 let server = self.server_pubkey.ok_or("set server pubkey first")?;
485 self.mailbox_id = Some(mailbox.to_blinded(server, vtxo_key));
486 self.add_builtin_delivery = false;
487 Ok(self)
488 }
489
490 pub fn into_address(self) -> Result<Address, AddressBuilderError> {
492 Ok(Address {
493 testnet: self.testnet,
494 ark_id: self.server_pubkey.ok_or("missing server pubkey")?.into(),
495 policy: self.policy.ok_or("missing policy")?,
496 delivery: {
497 let mut ret = Vec::new();
498 if self.delivery.is_empty() && self.add_builtin_delivery {
499 ret.push(VtxoDelivery::ServerBuiltin);
500 }
501
502 if let Some(blinded_id) = self.mailbox_id {
503 ret.push(VtxoDelivery::ServerMailbox { blinded_id });
504 }
505
506 ret.extend(self.delivery);
507 ret
508 }
509 })
510 }
511}
512
513struct ByteIter<T>(T);
515
516impl<T: Iterator<Item = u8>> io::Read for ByteIter<T> {
517 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
518 let mut written = 0;
519 for e in buf.iter_mut() {
520 if let Some(n) = self.0.next() {
521 *e = n;
522 written += 1;
523 } else {
524 break;
525 }
526 }
527 Ok(written)
528 }
529}
530
531#[cfg(test)]
532mod test {
533 use bitcoin::secp256k1::rand;
534 use crate::SECP;
535 use super::*;
536
537 #[test]
538 fn test_versions() {
539 assert_eq!(VERSION_POLICY, bech32::Fe32::try_from(1u8).unwrap());
544 }
545
546 fn test_roundtrip(addr: &Address) -> Address {
547 let parsed = Address::from_str(&addr.to_string()).unwrap();
548 assert_eq!(parsed, *addr);
549 parsed
550 }
551
552 #[test]
553 fn address_roundtrip() {
554 let ark = PublicKey::from_str("02037188bdd7579a0cd0b22a51110986df1ea08e30192658fe0e219590e4a723d3").unwrap();
555 let ark_id = ArkId::from_server_pubkey(ark);
556 let usr = PublicKey::from_str("032217b6ccba4fa98cc433abe4be1ceaf41ea61fd83fcefd27384ca4612ce19512").unwrap();
557 println!("ark pk: {} (id {})", ark, ark_id);
558 println!("usr pk: {}", usr);
559 let policy = VtxoPolicy::new_pubkey(usr);
560
561 let addr = Address::builder()
563 .server_pubkey(ark)
564 .pubkey_policy(usr)
565 .into_address().unwrap();
566 assert_eq!(addr.to_string(), "ark1pwh9vsmezqqpjy9akejayl2vvcse6he97rn40g84xrlvrlnhayuuyefrp9nse2yspqqjl5wpy");
567
568 let parsed = test_roundtrip(&addr);
569 assert_eq!(parsed.ark_id, ark_id);
570 assert_eq!(parsed.policy, policy);
571 assert_eq!(parsed.delivery.len(), 1);
572
573 let addr = Address::builder()
575 .testnet(true)
576 .server_pubkey(ark)
577 .pubkey_policy(usr)
578 .no_delivery()
579 .into_address().unwrap();
580 assert_eq!(addr.to_string(), "tark1pwh9vsmezqqpjy9akejayl2vvcse6he97rn40g84xrlvrlnhayuuyefrp9nse2ysm2x4mn");
581
582 let parsed = test_roundtrip(&addr);
583 assert_eq!(parsed.ark_id, ArkId::from_server_pubkey(ark));
584 assert_eq!(parsed.policy, policy);
585 assert_eq!(parsed.delivery.len(), 0);
586 }
587
588 #[test]
589 fn test_mailbox() {
590 let mailbox_key = Keypair::new(&SECP, &mut rand::thread_rng());
591 let server_key = Keypair::new(&SECP, &mut rand::thread_rng());
592 let vtxo_key = Keypair::new(&SECP, &mut rand::thread_rng());
593
594 let mailbox = MailboxIdentifier::from_pubkey(mailbox_key.public_key());
595
596 let addr = Address::builder()
597 .server_pubkey(server_key.public_key())
598 .pubkey_policy(vtxo_key.public_key())
599 .mailbox(mailbox, &vtxo_key).expect("error mailbox call")
600 .into_address().unwrap();
601
602 let blinded = match addr.delivery[0] {
603 VtxoDelivery::ServerMailbox { blinded_id } => blinded_id,
604 _ => panic!("unexpected delivery"),
605 };
606
607 let unblinded = MailboxIdentifier::from_blinded(
608 blinded, addr.policy().user_pubkey(), &server_key);
609
610 assert_eq!(mailbox, unblinded);
611 }
612}