Skip to main content

ark/
address.rs

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
15/// The human-readable part for mainnet addresses
16const HRP_MAINNET: bech32::Hrp = bech32::Hrp::parse_unchecked("ark");
17
18/// The human-readable part for test addresses
19const HRP_TESTNET: bech32::Hrp = bech32::Hrp::parse_unchecked("tark");
20
21/// Address version 0 used for addressing in Arkade.
22const VERSION_ARKADE: bech32::Fe32 = bech32::Fe32::Q;
23
24/// Address version 1 used for policy addressing in bark.
25const VERSION_POLICY: bech32::Fe32 = bech32::Fe32::P;
26
27
28/// Identifier for an Ark server as used in addresses
29#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
30pub struct ArkId([u8; 4]);
31impl_byte_newtype!(ArkId, 4);
32
33impl ArkId {
34	/// Create a new [ArkId] from a server pubkey
35	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	/// Check whether the given server pubkey matches this [ArkId].
43	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/// Mechanism to deliver a VTXO to a user
55#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
56#[non_exhaustive]
57pub enum VtxoDelivery {
58	/// Use the unified mailbox of the Ark server
59	ServerMailbox {
60		blinded_id: BlindedMailboxIdentifier,
61	},
62	Unknown {
63		delivery_type: u8,
64		data: Vec<u8>,
65	},
66}
67
68/// The type byte for the legacy "server built-in" delivery
69/// mechanism
70///
71/// This is currently unused but we reserve the byte for a
72/// better implementation of per-pubkey delivery
73#[allow(unused)]
74const DELIVERY_BUILTIN: u8 = 0x00;
75
76/// The type byte for the "server mailbox" delivery mechanism
77const DELIVERY_MAILBOX: u8 = 0x01;
78
79impl VtxoDelivery {
80
81	/// Returns whether the VTXO delivery type is unknown
82	pub fn is_unknown(&self) -> bool {
83		match self {
84			Self::Unknown { .. } => true,
85			_ => false,
86		}
87	}
88
89	/// The number of bytes required to encode this delivery
90	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	/// Encode the address payload
98	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	/// Decode the address payload
113	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/// An Ark address
133///
134/// Used to address VTXO payments in an Ark.
135///
136/// Example usage:
137/// ```
138/// # use ark::mailbox::BlindedMailboxIdentifier;
139/// # use ark::address::VtxoDelivery;
140///
141/// let srv_pubkey = "03d2e3205d9fd8fb2d441e9c3aa5e28ac895f7aae68c209ae918e2750861e8ffc1".parse().unwrap();
142/// let vtxo_pubkey = "035c4def84a9883afe60ef72b37aaf8038dd74ed3d0ab1a1f30610acccd68d1cdd".parse().unwrap();
143/// let blinded_id = BlindedMailboxIdentifier::from_pubkey(vtxo_pubkey);
144///
145/// let addr = ark::Address::builder()
146/// 	.server_pubkey(srv_pubkey)
147/// 	.pubkey_policy(vtxo_pubkey)
148/// 	.delivery(VtxoDelivery::ServerMailbox { blinded_id })
149/// 	.into_address().unwrap();
150///
151/// assert_eq!(addr.to_string(),
152/// 	"ark1pndckx4ezqqp4cn00sj5cswh7vrhh9vm647qr3ht5a57s4vdp7vrpptxv66x3ehfzqyp4cn00sj5cswh7vrhh9vm647qr3ht5a57s4vdp7vrpptxv66x3ehgjdr0q7",
153/// );
154/// ```
155#[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	/// Start building an [Address]
165	pub fn builder() -> Builder {
166		Builder::new()
167	}
168
169	/// Create a new [Address]
170	///
171	/// Note that it might be more convenient to use [Address::builder] instead.
172	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	/// Whether or not this [Address] is intended to be used in a test network
187	pub fn is_testnet(&self) -> bool {
188		self.testnet
189	}
190
191	/// The [ArkId] of the Ark in which the user wants to be paid
192	pub fn ark_id(&self) -> ArkId {
193		self.ark_id
194	}
195
196	/// Check whether this [Address] matches the given server pubkey
197	pub fn is_for_server(&self, server_pubkey: PublicKey) -> bool {
198		self.ark_id().is_for_server(server_pubkey)
199	}
200
201	/// The VTXO policy the user wants to be paid in
202	pub fn policy(&self) -> &VtxoPolicy {
203		&self.policy
204	}
205
206	/// The different VTXO delivery options provided by the user
207	pub fn delivery(&self) -> &[VtxoDelivery] {
208		&self.delivery
209	}
210
211	/// Write the address payload to the writer
212	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		// NB our ProtocolEncoding system is not designed to encode unknown types.
216		// Therefore we have to do something a little unusual to know the sizes of
217		// our subfields here.
218
219		let mut buf = Vec::with_capacity(128); // enough to hold any policy currently
220		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	/// Read the address payload from the byte iterator
233	///
234	/// Returns an address straight away given the testnet indicator.
235	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		// this write code is borrowed from bech32 crate
294		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/// Error parsing an [Address]
321#[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			// these should never happen but in order to be safe, we catch them
359			_ => {
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		// NB this unused generic is fixed in next version of bech32 crate
384		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/// Error while building an [Address] using [Builder]
417#[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/// Builder used to create [Address] instances
430///
431/// Currently, only mailbox delivery is supported.
432
433#[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	/// Create a new [Builder]
447	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	/// Set the address to be used for test networks
458	///
459	/// Default is false.
460	pub fn testnet(mut self, testnet: bool) -> Self {
461		self.testnet = testnet;
462		self
463	}
464
465	/// Set the Ark server pubkey
466	pub fn server_pubkey(mut self, server_pubkey: PublicKey) -> Self {
467		self.server_pubkey = Some(server_pubkey);
468		self
469	}
470
471	/// Set the VTXO policy
472	pub fn policy(mut self, policy: VtxoPolicy) -> Self {
473		self.policy = Some(policy);
474		self
475	}
476
477	/// Set the VTXO policy to the given [PublicKey].
478	pub fn pubkey_policy(self, user_pubkey: PublicKey) -> Self {
479		self.policy(VtxoPolicy::new_pubkey(user_pubkey))
480	}
481
482	/// Add the given delivery method
483	pub fn delivery(mut self, delivery: VtxoDelivery) -> Self {
484		self.delivery.push(delivery);
485		self
486	}
487
488	/// Set the mailbox identifier of the server mailbox to use
489	///
490	/// Errors if no server pubkey was provided yet or if the vtxo key
491	/// is incorrect.
492	pub fn mailbox(
493		mut self,
494		server_mailbox_pubkey: PublicKey,
495		mailbox: MailboxIdentifier,
496		vtxo_key: &Keypair,
497	) -> Result<Self, AddressBuilderError> {
498		// check the vtxo key
499		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	/// Finish by building an [Address]
509	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
532/// Simple wrapper to implement [io::Read] for a byte iterator.
533struct 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		//! because [Fe32] doesn't expose a const from u8 constructor,
559		//! we use the character in the definition, but it's annoying that
560		//! it requires knowledge of the alphabet to know which numerical value
561		//! it has. that's why we enforce it here
562		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		// mailbox delivery
584		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		// mailbox delivery testnet
597		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]; // ark_id
640		bytes.push(0xFF); // compact-size marker: a u64 length follows
641		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}