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::PublicKey;
8
9use crate::{ProtocolDecodingError, ProtocolEncoding, VtxoPolicy};
10use crate::encode::{ReadExt, WriteExt};
11
12
13/// The human-readable part for mainnet addresses
14const HRP_MAINNET: bech32::Hrp = bech32::Hrp::parse_unchecked("ark");
15
16/// The human-readable part for test addresses
17const HRP_TESTNET: bech32::Hrp = bech32::Hrp::parse_unchecked("tark");
18
19/// Address version 0 used for addressing in Arkade.
20const VERSION_ARKADE: bech32::Fe32 = bech32::Fe32::Q;
21
22/// Address version 1 used for policy addressing in bark.
23const VERSION_POLICY: bech32::Fe32 = bech32::Fe32::P;
24
25
26/// Identifier for an Ark server as used in addresses
27#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
28pub struct ArkId([u8; 4]);
29impl_byte_newtype!(ArkId, 4);
30
31impl ArkId {
32	/// Create a new [ArkId] from a server pubkey
33	pub fn from_server_pubkey(server_pubkey: PublicKey) -> ArkId {
34		let mut buf = [0u8; 4];
35		let hash = sha256::Hash::hash(&server_pubkey.serialize());
36		buf[0..4].copy_from_slice(&hash[0..4]);
37		ArkId(buf)
38	}
39
40	/// Check whether the given server pubkey matches this [ArkId].
41	pub fn is_for_server(&self, server_pubkey: PublicKey) -> bool {
42		*self == ArkId::from_server_pubkey(server_pubkey)
43	}
44}
45
46impl From<PublicKey> for ArkId {
47	fn from(pk: PublicKey) -> Self {
48	    ArkId::from_server_pubkey(pk)
49	}
50}
51
52/// Mechanism to deliver a VTXO to a user
53#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
54#[non_exhaustive]
55pub enum VtxoDelivery {
56	/// Use the built-in VTXO mailbox of the Ark server
57	ServerBuiltin,
58	Unknown {
59		delivery_type: u8,
60		data: Vec<u8>,
61	},
62}
63
64/// The type byte for the "server built-in" delivery mechanism
65const DELIVERY_BUILTIN: u8 = 0x00;
66
67impl VtxoDelivery {
68	/// Returns whether the VTXO delivery type is unknown
69	pub fn is_unknown(&self) -> bool {
70		match self {
71			Self::Unknown { .. } => true,
72			_ => false,
73		}
74	}
75
76	/// The number of bytes required to encode this delivery
77	fn encoded_length(&self) -> usize {
78		match self {
79			Self::ServerBuiltin => 1,
80			Self::Unknown { data, .. } => 1 + data.len(),
81		}
82	}
83
84	fn encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<(), io::Error> {
85		match self {
86			Self::ServerBuiltin => {
87				w.emit_u8(DELIVERY_BUILTIN)?;
88			},
89			Self::Unknown { delivery_type, data } => {
90				w.emit_u8(*delivery_type)?;
91				w.emit_slice(data)?;
92			},
93		}
94		Ok(())
95	}
96
97	fn decode(payload: &[u8]) -> Result<Self, ParseAddressError> {
98		if payload.is_empty() {
99			return Err(ParseAddressError::Eof);
100		}
101
102		match payload[0] {
103			DELIVERY_BUILTIN => Ok(Self::ServerBuiltin),
104			delivery_type => Ok(Self::Unknown {
105				delivery_type: delivery_type,
106				data: payload[1..].to_vec(),
107			}),
108		}
109	}
110}
111
112/// An Ark address
113///
114/// Used to address VTXO payments in an Ark.
115///
116/// Example usage:
117/// ```
118/// let srv_pubkey = "03d2e3205d9fd8fb2d441e9c3aa5e28ac895f7aae68c209ae918e2750861e8ffc1".parse().unwrap();
119/// let vtxo_pubkey = "035c4def84a9883afe60ef72b37aaf8038dd74ed3d0ab1a1f30610acccd68d1cdd".parse().unwrap();
120///
121/// let addr = ark::Address::builder()
122/// 	.server_pubkey(srv_pubkey)
123/// 	.pubkey_policy(vtxo_pubkey)
124/// 	.into_address().unwrap();
125///
126/// assert_eq!(addr.to_string(),
127/// 	"ark1pndckx4ezqqp4cn00sj5cswh7vrhh9vm647qr3ht5a57s4vdp7vrpptxv66x3ehgpqqnevf3z",
128/// );
129/// ```
130#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
131pub struct Address {
132	testnet: bool,
133	ark_id: ArkId,
134	policy: VtxoPolicy,
135	delivery: Vec<VtxoDelivery>,
136}
137
138impl Address {
139	/// Start building an [Address]
140	pub fn builder() -> Builder {
141		Builder::new()
142	}
143
144	/// Create a new [Address]
145	///
146	/// Note that it might be more convenient to use [Address::builder] instead.
147	pub fn new(
148		testnet: bool,
149		ark_id: impl Into<ArkId>,
150		policy: VtxoPolicy,
151		delivery: Vec<VtxoDelivery>,
152	) -> Address {
153		Address {
154			testnet: testnet,
155			ark_id: ark_id.into(),
156			policy: policy,
157			delivery: delivery,
158		}
159	}
160
161	/// Whether or not this [Address] is intended to be used in a test network
162	pub fn is_testnet(&self) -> bool {
163		self.testnet
164	}
165
166	/// The [ArkId] of the Ark in which the user wants to be paid
167	pub fn ark_id(&self) -> ArkId {
168		self.ark_id
169	}
170
171	/// Check whether this [Address] matches the given server pubkey
172	pub fn is_for_server(&self, server_pubkey: PublicKey) -> bool {
173		self.ark_id().is_for_server(server_pubkey)
174	}
175
176	/// The VTXO policy the user wants to be paid in
177	pub fn policy(&self) -> &VtxoPolicy {
178		&self.policy
179	}
180
181	/// The different VTXO delivery options provided by the user
182	pub fn delivery(&self) -> &[VtxoDelivery] {
183		&self.delivery
184	}
185
186	/// Write the address payload to the writer
187	pub fn encode_payload<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<(), io::Error> {
188		writer.emit_slice(&self.ark_id.to_byte_array())?;
189
190		// NB our ProtocolEncoding system is not designed to encode unknown types.
191		// Therefore we have to do something a little unusual to know the sizes of
192		// our subfields here.
193
194		let mut buf = Vec::with_capacity(128); // enough to hold any policy currently
195		self.policy.encode(&mut buf)?;
196		writer.emit_compact_size(buf.len() as u64)?;
197		writer.emit_slice(&buf[..])?;
198
199		for delivery in &self.delivery {
200			writer.emit_compact_size(delivery.encoded_length() as u64)?;
201			delivery.encode(writer)?;
202		}
203
204		Ok(())
205	}
206
207	/// Read the address payload from the byte iterator
208	///
209	/// Returns an address straight away given the testnet indicator.
210	pub fn decode_payload(
211		testnet: bool,
212		bytes: impl Iterator<Item = u8>,
213	) -> Result<Address, ParseAddressError> {
214		let mut peekable = bytes.peekable();
215		let mut reader = ByteIter(&mut peekable);
216
217		let ark_id = {
218			let mut buf = [0u8; 4];
219			reader.read_slice(&mut buf).map_err(|_| ParseAddressError::Eof)?;
220			ArkId(buf)
221		};
222
223		let mut buf = Vec::new();
224		let policy = {
225			let len = reader.read_compact_size()? as usize;
226			buf.resize(len, 0);
227			reader.read_slice(&mut buf[..])?;
228			VtxoPolicy::deserialize(&buf[..]).map_err(ParseAddressError::VtxoPolicy)?
229		};
230
231		let mut delivery = Vec::new();
232		while reader.0.peek().is_some() {
233			let len = reader.read_compact_size()? as usize;
234			buf.resize(len, 0);
235			reader.read_slice(&mut buf[..])?;
236			delivery.push(VtxoDelivery::decode(&buf[..])?);
237		}
238
239		Ok(Address::new(testnet, ark_id, policy, delivery))
240	}
241}
242
243impl fmt::Display for Address {
244	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
245		let hrp = if self.testnet {
246			HRP_TESTNET
247		} else {
248			HRP_MAINNET
249		};
250
251		let ver = VERSION_POLICY;
252		let payload = {
253			let mut buf = Vec::with_capacity(128);
254			self.encode_payload(&mut buf).expect("buffers don't error");
255			buf
256		};
257
258		let chars = [ver].into_iter().chain(payload.into_iter().bytes_to_fes())
259			.with_checksum::<bech32::Bech32m>(&hrp)
260			.chars();
261
262		// this write code is borrowed from bech32 crate
263		const BUF_LENGTH: usize = 128;
264		let mut buf = [0u8; BUF_LENGTH];
265		let mut pos = 0;
266		for c in chars {
267			buf[pos] = c as u8;
268			pos += 1;
269
270			if pos == BUF_LENGTH {
271				let s = core::str::from_utf8(&buf).expect("we only write ASCII");
272				f.write_str(s)?;
273				pos = 0;
274			}
275		}
276
277		let s = core::str::from_utf8(&buf[..pos]).expect("we only write ASCII");
278		f.write_str(s)?;
279		Ok(())
280	}
281}
282
283impl fmt::Debug for Address {
284	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
285	    fmt::Display::fmt(self, f)
286	}
287}
288
289/// Error parsing an [Address]
290#[derive(Debug, thiserror::Error)]
291pub enum ParseAddressError {
292	#[error("bech32m decoding error: {0}")]
293	Bech32(bech32::DecodeError),
294	#[error("invalid HRP: '{0}'")]
295	Hrp(bech32::Hrp),
296	#[error("address ins an Arkade address and cannot be used here")]
297	Arkade,
298	#[error("unknown version: '{version}'")]
299	UnknownVersion {
300		version: bech32::Fe32,
301	},
302	#[error("invalid encoding: unexpected end of bytes")]
303	Eof,
304	#[error("invalid or unknown VTXO policy")]
305	VtxoPolicy(ProtocolDecodingError),
306	#[error("invalid address")]
307	Invalid(&'static str),
308}
309
310impl From<bech32::primitives::decode::UncheckedHrpstringError> for ParseAddressError {
311	fn from(e: bech32::primitives::decode::UncheckedHrpstringError) -> Self {
312	    Self::Bech32(e.into())
313	}
314}
315
316impl From<bech32::primitives::decode::ChecksumError> for ParseAddressError {
317	fn from(e: bech32::primitives::decode::ChecksumError) -> Self {
318	    Self::Bech32(bech32::DecodeError::Checksum(e))
319	}
320}
321
322impl From<io::Error> for ParseAddressError {
323	fn from(e: io::Error) -> Self {
324		match e.kind() {
325			io::ErrorKind::UnexpectedEof => ParseAddressError::Eof,
326			io::ErrorKind::InvalidData => ParseAddressError::Invalid("invalid encoding"),
327			// these should never happen but in order to be safe, we catch them
328			_ => {
329				if cfg!(debug_assertions) {
330					panic!("unexpected I/O error while parsing address: {}", e);
331				}
332				ParseAddressError::Invalid("unexpected I/O error")
333			},
334		}
335	}
336}
337
338impl FromStr for Address {
339	type Err = ParseAddressError;
340	fn from_str(s: &str) -> Result<Self, Self::Err> {
341		let raw = bech32::primitives::decode::UncheckedHrpstring::new(s)?;
342
343		let testnet = if raw.hrp() == HRP_MAINNET {
344			false
345		} else if raw.hrp() == HRP_TESTNET {
346			true
347		} else {
348			return Err(ParseAddressError::Hrp(raw.hrp()));
349		};
350
351		let checked = raw.validate_and_remove_checksum::<bech32::Bech32m>()?;
352		// NB this unused generic is fixed in next version of bech32 crate
353		let mut iter = checked.fe32_iter::<std::iter::Empty<u8>>();
354		let ver = iter.next().ok_or(ParseAddressError::Invalid("empty address"))?;
355
356		match ver {
357			VERSION_POLICY => {},
358			VERSION_ARKADE => return Err(ParseAddressError::Arkade),
359			_ => return Err(ParseAddressError::UnknownVersion { version: ver }),
360		}
361
362		Address::decode_payload(testnet, iter.fes_to_bytes())
363	}
364}
365
366/// Error while building an [Address] using [Builder]
367#[derive(Clone, Debug, thiserror::Error)]
368#[error("error building address: {msg}")]
369pub struct AddressBuilderError {
370	msg: &'static str,
371}
372
373impl From<&'static str> for AddressBuilderError {
374	fn from(msg: &'static str) -> Self {
375	    AddressBuilderError { msg }
376	}
377}
378
379/// Builder used to create [Address] instances
380///
381/// By default, when no VTXO delivery mechanism is provided by the user,
382/// the builder will add the built-in [VtxoDelivery::ServerBuiltin].
383/// To prevent this, use [Builder::no_delivery].
384#[derive(Debug)]
385pub struct Builder {
386	testnet: bool,
387	ark_id: Option<ArkId>,
388	policy: Option<VtxoPolicy>,
389	delivery: Vec<VtxoDelivery>,
390	/// Do not add default built-in delivery when no delivery is sets.
391	no_delivery: bool,
392}
393
394impl Builder {
395	/// Create a new [Builder]
396	pub fn new() -> Self {
397		Self {
398			testnet: false,
399			ark_id: None,
400			policy: None,
401			delivery: Vec::new(),
402			no_delivery: false,
403		}
404	}
405
406	/// Set the address to be used for test networks
407	///
408	/// Default is false.
409	pub fn testnet(mut self, testnet: bool) -> Self {
410		self.testnet = testnet;
411		self
412	}
413
414	/// Set the Ark server pubkey
415	pub fn server_pubkey(mut self, server_pubkey: PublicKey) -> Self {
416		self.ark_id = Some(ArkId::from_server_pubkey(server_pubkey));
417		self
418	}
419
420	/// Set the VTXO policy
421	pub fn policy(mut self, policy: VtxoPolicy) -> Self {
422		self.policy = Some(policy);
423		self
424	}
425
426	/// Set the VTXO policy to [VtxoPolicy::PublicKey]
427	pub fn pubkey_policy(self, user_pubkey: PublicKey) -> Self {
428		self.policy(VtxoPolicy::new_pubkey(user_pubkey))
429	}
430
431	/// Add the given delivery method
432	pub fn delivery(mut self, delivery: VtxoDelivery) -> Self {
433		self.delivery.push(delivery);
434		self
435	}
436
437	/// Prevent the builder from adding the built-in delivery when no delivery is set.
438	pub fn no_delivery(mut self) -> Self {
439		self.no_delivery = true;
440		self
441	}
442
443	/// Finish by building an [Address]
444	pub fn into_address(self) -> Result<Address, AddressBuilderError> {
445		Ok(Address {
446			testnet: self.testnet,
447			ark_id: self.ark_id.ok_or("missing ark_id")?,
448			policy: self.policy.ok_or("missing policy")?,
449			delivery: if self.delivery.is_empty() && !self.no_delivery {
450				vec![VtxoDelivery::ServerBuiltin]
451			} else {
452				self.delivery
453			},
454		})
455	}
456}
457
458/// Simple wrapper to implement [io::Read] for a byte iterator.
459struct ByteIter<T>(T);
460
461impl<T: Iterator<Item = u8>> io::Read for ByteIter<T> {
462	fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
463		let mut written = 0;
464		for e in buf.iter_mut() {
465			if let Some(n) = self.0.next() {
466				*e = n;
467				written += 1;
468			} else {
469				break;
470			}
471		}
472		Ok(written)
473	}
474}
475
476#[cfg(test)]
477mod test {
478	use super::*;
479
480	#[test]
481	fn test_versions() {
482		//! because [Fe32] doesn't expose a const from u8 constructor,
483		//! we use the character in the definition, but it's annoying that
484		//! it requires knowledge of the alphabet to know which numerical value
485		//! it has. that's why we enforce it here
486		assert_eq!(VERSION_POLICY, bech32::Fe32::try_from(1u8).unwrap());
487	}
488
489	fn test_roundtrip(addr: &Address) -> Address {
490		let parsed = Address::from_str(&addr.to_string()).unwrap();
491		assert_eq!(parsed, *addr);
492		parsed
493	}
494
495	#[test]
496	fn address_roundtrip() {
497		let ark = PublicKey::from_str("02037188bdd7579a0cd0b22a51110986df1ea08e30192658fe0e219590e4a723d3").unwrap();
498		let ark_id = ArkId::from_server_pubkey(ark);
499		let usr = PublicKey::from_str("032217b6ccba4fa98cc433abe4be1ceaf41ea61fd83fcefd27384ca4612ce19512").unwrap();
500		println!("ark pk: {} (id {})", ark, ark_id);
501		println!("usr pk: {}", usr);
502		let policy = VtxoPolicy::new_pubkey(usr);
503
504		// no delivery
505		let addr = Address::builder()
506			.server_pubkey(ark)
507			.pubkey_policy(usr)
508			.into_address().unwrap();
509		assert_eq!(addr.to_string(), "ark1pwh9vsmezqqpjy9akejayl2vvcse6he97rn40g84xrlvrlnhayuuyefrp9nse2yspqqjl5wpy");
510
511		let parsed = test_roundtrip(&addr);
512		assert_eq!(parsed.ark_id, ark_id);
513		assert_eq!(parsed.policy, policy);
514		assert_eq!(parsed.delivery.len(), 1);
515
516		// built-in delivery
517		let addr = Address::builder()
518			.testnet(true)
519			.server_pubkey(ark)
520			.pubkey_policy(usr)
521			.no_delivery()
522			.into_address().unwrap();
523		assert_eq!(addr.to_string(), "tark1pwh9vsmezqqpjy9akejayl2vvcse6he97rn40g84xrlvrlnhayuuyefrp9nse2ysm2x4mn");
524
525		let parsed = test_roundtrip(&addr);
526		assert_eq!(parsed.ark_id, ArkId::from_server_pubkey(ark));
527		assert_eq!(parsed.policy, policy);
528		assert_eq!(parsed.delivery.len(), 0);
529	}
530}