ark/
address.rs

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
14/// The human-readable part for mainnet addresses
15const HRP_MAINNET: bech32::Hrp = bech32::Hrp::parse_unchecked("ark");
16
17/// The human-readable part for test addresses
18const HRP_TESTNET: bech32::Hrp = bech32::Hrp::parse_unchecked("tark");
19
20/// Address version 0 used for addressing in Arkade.
21const VERSION_ARKADE: bech32::Fe32 = bech32::Fe32::Q;
22
23/// Address version 1 used for policy addressing in bark.
24const VERSION_POLICY: bech32::Fe32 = bech32::Fe32::P;
25
26
27/// Identifier for an Ark server as used in addresses
28#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
29pub struct ArkId([u8; 4]);
30impl_byte_newtype!(ArkId, 4);
31
32impl ArkId {
33	/// Create a new [ArkId] from a server pubkey
34	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	/// Check whether the given server pubkey matches this [ArkId].
42	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/// Mechanism to deliver a VTXO to a user
54#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
55#[non_exhaustive]
56pub enum VtxoDelivery {
57	/// Use the built-in single-VTXO mailbox of the Ark server
58	ServerBuiltin,
59	/// Use the unified mailbox of the Ark server
60	ServerMailbox {
61		blinded_id: BlindedMailboxIdentifier,
62	},
63	Unknown {
64		delivery_type: u8,
65		data: Vec<u8>,
66	},
67}
68
69/// The type byte for the "server built-in" delivery mechanism
70const DELIVERY_BUILTIN: u8 = 0x00;
71/// The type byte for the "server mailbox" delivery mechanism
72const DELIVERY_MAILBOX: u8 = 0x01;
73
74impl VtxoDelivery {
75	/// Returns whether the VTXO delivery type is unknown
76	pub fn is_unknown(&self) -> bool {
77		match self {
78			Self::Unknown { .. } => true,
79			_ => false,
80		}
81	}
82
83	/// The number of bytes required to encode this delivery
84	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	/// Encode the address payload
93	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	/// Decode the address payload
111	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/// An Ark address
132///
133/// Used to address VTXO payments in an Ark.
134///
135/// Example usage:
136/// ```
137/// let srv_pubkey = "03d2e3205d9fd8fb2d441e9c3aa5e28ac895f7aae68c209ae918e2750861e8ffc1".parse().unwrap();
138/// let vtxo_pubkey = "035c4def84a9883afe60ef72b37aaf8038dd74ed3d0ab1a1f30610acccd68d1cdd".parse().unwrap();
139///
140/// let addr = ark::Address::builder()
141/// 	.server_pubkey(srv_pubkey)
142/// 	.pubkey_policy(vtxo_pubkey)
143/// 	.into_address().unwrap();
144///
145/// assert_eq!(addr.to_string(),
146/// 	"ark1pndckx4ezqqp4cn00sj5cswh7vrhh9vm647qr3ht5a57s4vdp7vrpptxv66x3ehgpqqnevf3z",
147/// );
148/// ```
149#[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	/// Start building an [Address]
159	pub fn builder() -> Builder {
160		Builder::new()
161	}
162
163	/// Create a new [Address]
164	///
165	/// Note that it might be more convenient to use [Address::builder] instead.
166	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	/// Whether or not this [Address] is intended to be used in a test network
181	pub fn is_testnet(&self) -> bool {
182		self.testnet
183	}
184
185	/// The [ArkId] of the Ark in which the user wants to be paid
186	pub fn ark_id(&self) -> ArkId {
187		self.ark_id
188	}
189
190	/// Check whether this [Address] matches the given server pubkey
191	pub fn is_for_server(&self, server_pubkey: PublicKey) -> bool {
192		self.ark_id().is_for_server(server_pubkey)
193	}
194
195	/// The VTXO policy the user wants to be paid in
196	pub fn policy(&self) -> &VtxoPolicy {
197		&self.policy
198	}
199
200	/// The different VTXO delivery options provided by the user
201	pub fn delivery(&self) -> &[VtxoDelivery] {
202		&self.delivery
203	}
204
205	/// Write the address payload to the writer
206	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		// NB our ProtocolEncoding system is not designed to encode unknown types.
210		// Therefore we have to do something a little unusual to know the sizes of
211		// our subfields here.
212
213		let mut buf = Vec::with_capacity(128); // enough to hold any policy currently
214		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	/// Read the address payload from the byte iterator
227	///
228	/// Returns an address straight away given the testnet indicator.
229	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		// this write code is borrowed from bech32 crate
282		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/// Error parsing an [Address]
309#[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			// these should never happen but in order to be safe, we catch them
347			_ => {
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		// NB this unused generic is fixed in next version of bech32 crate
372		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/// Error while building an [Address] using [Builder]
386#[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/// Builder used to create [Address] instances
399///
400/// By default, when no VTXO delivery mechanism is provided by the user,
401/// the builder will add the built-in [VtxoDelivery::ServerBuiltin].
402/// To prevent this, use [Builder::no_delivery].
403#[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	/// Create a new [Builder]
418	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	/// Set the address to be used for test networks
430	///
431	/// Default is false.
432	pub fn testnet(mut self, testnet: bool) -> Self {
433		self.testnet = testnet;
434		self
435	}
436
437	/// Set the Ark server pubkey
438	pub fn server_pubkey(mut self, server_pubkey: PublicKey) -> Self {
439		self.server_pubkey = Some(server_pubkey);
440		self
441	}
442
443	/// Set the VTXO policy
444	pub fn policy(mut self, policy: VtxoPolicy) -> Self {
445		self.policy = Some(policy);
446		self
447	}
448
449	/// Set the VTXO policy to the given [PublicKey].
450	pub fn pubkey_policy(self, user_pubkey: PublicKey) -> Self {
451		self.policy(VtxoPolicy::new_pubkey(user_pubkey))
452	}
453
454	/// Add the given delivery method
455	pub fn delivery(mut self, delivery: VtxoDelivery) -> Self {
456		self.delivery.push(delivery);
457		self
458	}
459
460	/// Prevent the builder from adding the built-in delivery when no delivery is set.
461	pub fn no_delivery(mut self) -> Self {
462		self.delivery.clear();
463		self.add_builtin_delivery = false;
464		self
465	}
466
467	/// Set the mailbox identifier of the server mailbox to use
468	///
469	/// This will also disable adding the built-in delivery mechanism.
470	///
471	/// Errors if no server pubkey was provided yet or if the vtxo key
472	/// is incorrect.
473	pub fn mailbox(
474		mut self,
475		mailbox: MailboxIdentifier,
476		vtxo_key: &Keypair,
477	) -> Result<Self, AddressBuilderError> {
478		// check the vtxo key
479		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	/// Finish by building an [Address]
491	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
513/// Simple wrapper to implement [io::Read] for a byte iterator.
514struct 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		//! because [Fe32] doesn't expose a const from u8 constructor,
540		//! we use the character in the definition, but it's annoying that
541		//! it requires knowledge of the alphabet to know which numerical value
542		//! it has. that's why we enforce it here
543		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		// no delivery
562		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		// built-in delivery
574		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}