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::{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 built-in single-VTXO mailbox of the Ark server
59	ServerBuiltin,
60	/// Use the unified mailbox of the Ark server
61	ServerMailbox {
62		blinded_id: BlindedMailboxIdentifier,
63	},
64	Unknown {
65		delivery_type: u8,
66		data: Vec<u8>,
67	},
68}
69
70/// The type byte for the "server built-in" delivery mechanism
71const DELIVERY_BUILTIN: u8 = 0x00;
72/// The type byte for the "server mailbox" delivery mechanism
73const DELIVERY_MAILBOX: u8 = 0x01;
74
75impl VtxoDelivery {
76	/// Returns whether the VTXO delivery type is unknown
77	pub fn is_unknown(&self) -> bool {
78		match self {
79			Self::Unknown { .. } => true,
80			_ => false,
81		}
82	}
83
84	/// The number of bytes required to encode this delivery
85	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	/// Encode the address payload
94	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	/// Decode the address payload
112	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/// An Ark address
133///
134/// Used to address VTXO payments in an Ark.
135///
136/// Example usage:
137/// ```
138/// let srv_pubkey = "03d2e3205d9fd8fb2d441e9c3aa5e28ac895f7aae68c209ae918e2750861e8ffc1".parse().unwrap();
139/// let vtxo_pubkey = "035c4def84a9883afe60ef72b37aaf8038dd74ed3d0ab1a1f30610acccd68d1cdd".parse().unwrap();
140///
141/// let addr = ark::Address::builder()
142/// 	.server_pubkey(srv_pubkey)
143/// 	.pubkey_policy(vtxo_pubkey)
144/// 	.no_delivery()
145/// 	.into_address().unwrap();
146///
147/// assert_eq!(addr.to_string(),
148/// 	"ark1pndckx4ezqqp4cn00sj5cswh7vrhh9vm647qr3ht5a57s4vdp7vrpptxv66x3ehgxrng5y",
149/// );
150/// ```
151#[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	/// Start building an [Address]
161	pub fn builder() -> Builder {
162		Builder::new()
163	}
164
165	/// Create a new [Address]
166	///
167	/// Note that it might be more convenient to use [Address::builder] instead.
168	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	/// Whether or not this [Address] is intended to be used in a test network
183	pub fn is_testnet(&self) -> bool {
184		self.testnet
185	}
186
187	/// The [ArkId] of the Ark in which the user wants to be paid
188	pub fn ark_id(&self) -> ArkId {
189		self.ark_id
190	}
191
192	/// Check whether this [Address] matches the given server pubkey
193	pub fn is_for_server(&self, server_pubkey: PublicKey) -> bool {
194		self.ark_id().is_for_server(server_pubkey)
195	}
196
197	/// The VTXO policy the user wants to be paid in
198	pub fn policy(&self) -> &VtxoPolicy {
199		&self.policy
200	}
201
202	/// The different VTXO delivery options provided by the user
203	pub fn delivery(&self) -> &[VtxoDelivery] {
204		&self.delivery
205	}
206
207	/// Write the address payload to the writer
208	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		// NB our ProtocolEncoding system is not designed to encode unknown types.
212		// Therefore we have to do something a little unusual to know the sizes of
213		// our subfields here.
214
215		let mut buf = Vec::with_capacity(128); // enough to hold any policy currently
216		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	/// Read the address payload from the byte iterator
229	///
230	/// Returns an address straight away given the testnet indicator.
231	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		// this write code is borrowed from bech32 crate
284		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/// Error parsing an [Address]
311#[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			// these should never happen but in order to be safe, we catch them
349			_ => {
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		// NB this unused generic is fixed in next version of bech32 crate
374		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/// Error while building an [Address] using [Builder]
407#[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/// Builder used to create [Address] instances
420///
421/// By default, when no VTXO delivery mechanism is provided by the user,
422/// the builder will add the built-in [VtxoDelivery::ServerBuiltin].
423/// To prevent this, use [Builder::no_delivery].
424#[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	/// Create a new [Builder]
439	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	/// Set the address to be used for test networks
451	///
452	/// Default is false.
453	pub fn testnet(mut self, testnet: bool) -> Self {
454		self.testnet = testnet;
455		self
456	}
457
458	/// Set the Ark server pubkey
459	pub fn server_pubkey(mut self, server_pubkey: PublicKey) -> Self {
460		self.server_pubkey = Some(server_pubkey);
461		self
462	}
463
464	/// Set the VTXO policy
465	pub fn policy(mut self, policy: VtxoPolicy) -> Self {
466		self.policy = Some(policy);
467		self
468	}
469
470	/// Set the VTXO policy to the given [PublicKey].
471	pub fn pubkey_policy(self, user_pubkey: PublicKey) -> Self {
472		self.policy(VtxoPolicy::new_pubkey(user_pubkey))
473	}
474
475	/// Add the given delivery method
476	pub fn delivery(mut self, delivery: VtxoDelivery) -> Self {
477		self.delivery.push(delivery);
478		self
479	}
480
481	/// Prevent the builder from adding the built-in delivery when no delivery is set.
482	pub fn no_delivery(mut self) -> Self {
483		self.delivery.clear();
484		self.add_builtin_delivery = false;
485		self
486	}
487
488	/// Set the mailbox identifier of the server mailbox to use
489	///
490	/// This will also disable adding the built-in delivery mechanism.
491	///
492	/// Errors if no server pubkey was provided yet or if the vtxo key
493	/// is incorrect.
494	pub fn mailbox(
495		mut self,
496		server_mailbox_pubkey: PublicKey,
497		mailbox: MailboxIdentifier,
498		vtxo_key: &Keypair,
499	) -> Result<Self, AddressBuilderError> {
500		// check the vtxo key
501		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	/// Finish by building an [Address]
512	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
534/// Simple wrapper to implement [io::Read] for a byte iterator.
535struct 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		//! because [Fe32] doesn't expose a const from u8 constructor,
561		//! we use the character in the definition, but it's annoying that
562		//! it requires knowledge of the alphabet to know which numerical value
563		//! it has. that's why we enforce it here
564		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		// no delivery
583		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		// no delivery testnet
596		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}