1use crate::Error;
2use bech32::Bech32m;
3use bech32::Hrp;
4use bitcoin::key::TweakedPublicKey;
5use bitcoin::Network;
6use bitcoin::ScriptBuf;
7use bitcoin::XOnlyPublicKey;
8
9#[derive(Debug, Clone, Copy)]
10pub struct ArkAddress {
11 hrp: Hrp,
12 server: XOnlyPublicKey,
13 vtxo_tap_key: TweakedPublicKey,
14}
15
16impl ArkAddress {
17 pub fn to_p2tr_script_pubkey(&self) -> ScriptBuf {
18 ScriptBuf::new_p2tr_tweaked(self.vtxo_tap_key)
19 }
20}
21
22impl ArkAddress {
23 pub fn new(network: Network, server: XOnlyPublicKey, vtxo_tap_key: TweakedPublicKey) -> Self {
24 let hrp = match network {
25 Network::Bitcoin => "ark",
26 _ => "tark",
27 };
28
29 let hrp = Hrp::parse_unchecked(hrp);
30
31 Self {
32 hrp,
33 server,
34 vtxo_tap_key,
35 }
36 }
37
38 pub fn encode(&self) -> String {
39 let mut bytes = [0u8; 64];
40
41 bytes[..32].copy_from_slice(&self.server.serialize());
42 bytes[32..].copy_from_slice(&self.vtxo_tap_key.serialize());
43
44 bech32::encode::<Bech32m>(self.hrp, bytes.as_slice()).expect("data can be encoded")
45 }
46
47 pub fn decode(value: &str) -> Result<Self, Error> {
48 let (hrp, bytes) = bech32::decode(value).map_err(Error::address_format)?;
49
50 let server = XOnlyPublicKey::from_slice(&bytes[..32]).map_err(Error::address_format)?;
51 let vtxo_tap_key =
52 XOnlyPublicKey::from_slice(&bytes[32..]).map_err(Error::address_format)?;
53
54 let vtxo_tap_key = TweakedPublicKey::dangerous_assume_tweaked(vtxo_tap_key);
57
58 Ok(Self {
59 hrp,
60 server,
61 vtxo_tap_key,
62 })
63 }
64}
65
66impl std::fmt::Display for ArkAddress {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 f.write_str(&self.encode())
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75 use bitcoin::hex::DisplayHex;
76
77 #[test]
79 fn roundtrip() {
80 let address = "tark1x0lm8hhr2wc6n6lyemtyh9rz8rg2ftpkfun46aca56kjg3ws0tsztfpuanaquxc6faedvjk3tax0575y6perapg3e95654pk8r4fjecs5fyd2";
81
82 let decoded = ArkAddress::decode(address).unwrap();
83
84 let hrp = decoded.hrp.to_string();
85 assert_eq!(hrp, "tark");
86
87 let server = decoded.server.serialize().as_hex().to_string();
88 assert_eq!(
89 server,
90 "33ffb3dee353b1a9ebe4ced64b946238d0a4ac364f275d771da6ad2445d07ae0"
91 );
92
93 let vtxo_tap_key = decoded.vtxo_tap_key.serialize().as_hex().to_string();
94 assert_eq!(
95 vtxo_tap_key,
96 "25a43cecfa0e1b1a4f72d64ad15f4cfa7a84d0723e8511c969aa543638ea9967"
97 );
98
99 let encoded = decoded.encode();
100
101 assert_eq!(encoded, address);
102 }
103}