foundation_urtypes/registry/
address.rs1use minicbor::{
9 data::Tag, data::Type, decode::Error, encode::Write, Decode, Decoder, Encode, Encoder,
10};
11
12use crate::registry::CoinInfo;
13
14#[derive(Debug, Clone, PartialEq, Eq, Hash)]
16pub struct Address<'a> {
17 pub info: Option<CoinInfo>,
19 pub kind: Option<AddressKind>,
21 pub data: &'a [u8],
23}
24
25impl<'a> Address<'a> {
26 pub const TAG: Tag = Tag::new(307);
29}
30
31#[cfg(feature = "bitcoin")]
32fn data_from_payload(payload: &bitcoin::address::Payload) -> Result<&[u8], InterpretAddressError> {
33 use bitcoin::address::Payload;
34
35 match payload {
36 Payload::PubkeyHash(ref pkh) => Ok(pkh.as_ref()),
37 Payload::ScriptHash(ref sh) => Ok(sh.as_ref()),
38 Payload::WitnessProgram(ref wp) => Ok(wp.program().as_bytes()),
39 _ => Err(InterpretAddressError::UnsupportedPayload),
40 }
41}
42
43#[cfg(feature = "bitcoin")]
44impl<'a> TryFrom<&'a bitcoin::Address<bitcoin::address::NetworkUnchecked>> for Address<'a> {
45 type Error = InterpretAddressError;
46
47 fn try_from(
48 address: &'a bitcoin::Address<bitcoin::address::NetworkUnchecked>,
49 ) -> Result<Self, Self::Error> {
50 let kind = AddressKind::try_from(address.payload()).ok();
51 let data = data_from_payload(address.payload())?;
52
53 Ok(Self {
54 info: None,
55 kind,
56 data,
57 })
58 }
59}
60
61#[cfg(feature = "bitcoin")]
62impl<'a> TryFrom<&'a bitcoin::Address<bitcoin::address::NetworkChecked>> for Address<'a> {
63 type Error = InterpretAddressError;
64
65 fn try_from(address: &'a bitcoin::Address) -> Result<Self, Self::Error> {
66 use crate::registry::CoinType;
67 use bitcoin::Network;
68
69 let network = match address.network() {
70 Network::Bitcoin => CoinInfo::NETWORK_MAINNET,
71 Network::Testnet => CoinInfo::NETWORK_BTC_TESTNET,
72 _ => return Err(InterpretAddressError::UnsupportedNetwork),
73 };
74 let info = CoinInfo::new(CoinType::BTC, network);
75 let kind = AddressKind::try_from(address.payload()).ok();
76 let data = data_from_payload(address.payload())?;
77
78 Ok(Self {
79 info: Some(info),
80 kind,
81 data,
82 })
83 }
84}
85
86#[cfg(feature = "bitcoin")]
87#[derive(Debug, Clone, PartialEq, Eq)]
88pub enum InterpretAddressError {
89 UnsupportedNetwork,
90 UnsupportedPayload,
91}
92
93impl<'b, C> Decode<'b, C> for Address<'b> {
94 fn decode(d: &mut Decoder<'b>, ctx: &mut C) -> Result<Self, Error> {
95 let mut info = None;
96 let mut address_type = None;
97 let mut data = None;
98
99 let mut len = d.map()?;
100 loop {
101 match len {
102 Some(0) => break,
103 Some(n) => len = Some(n - 1),
104 None => {
105 if d.datatype()? == Type::Break {
106 break;
107 }
108 }
109 }
110
111 match d.u32()? {
112 1 => {
113 if CoinInfo::TAG != d.tag()? {
114 return Err(Error::message("crypto-coin-info tag is invalid"));
115 }
116
117 info = Some(CoinInfo::decode(d, ctx)?);
118 }
119 2 => address_type = Some(AddressKind::decode(d, ctx)?),
120 3 => data = Some(d.bytes()?),
121 _ => return Err(Error::message("unknown map entry")),
122 }
123 }
124
125 Ok(Self {
126 info,
127 kind: address_type,
128 data: data.ok_or_else(|| Error::message("data is missing"))?,
129 })
130 }
131}
132
133impl<'a, C> Encode<C> for Address<'a> {
134 fn encode<W: Write>(
135 &self,
136 e: &mut Encoder<W>,
137 ctx: &mut C,
138 ) -> Result<(), minicbor::encode::Error<W::Error>> {
139 let include_info = match self.info {
140 Some(ref i) => !i.is_default(),
141 None => false,
142 };
143
144 let len = include_info as u64 + self.kind.is_some() as u64 + 1;
145 e.map(len)?;
146
147 if include_info {
148 let info = self.info.as_ref().unwrap();
149 e.u8(1)?.tag(CoinInfo::TAG)?;
150 info.encode(e, ctx)?;
151 }
152
153 if let Some(ref address_type) = self.kind {
154 e.u8(2)?;
155 address_type.encode(e, ctx)?;
156 }
157
158 e.u8(3)?.bytes(self.data)?;
159
160 Ok(())
161 }
162}
163
164#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
166pub enum AddressKind {
167 P2PKH,
169 P2SH,
171 P2WPKH,
173}
174
175impl TryFrom<u8> for AddressKind {
176 type Error = InvalidAddressType;
177
178 fn try_from(value: u8) -> Result<Self, Self::Error> {
179 Ok(match value {
180 0 => AddressKind::P2PKH,
181 1 => AddressKind::P2SH,
182 2 => AddressKind::P2WPKH,
183 _ => {
184 return Err(InvalidAddressType {
185 invalid_type: value,
186 })
187 }
188 })
189 }
190}
191
192#[derive(Debug)]
195pub struct InvalidAddressType {
196 pub invalid_type: u8,
198}
199
200impl From<AddressKind> for u8 {
201 fn from(value: AddressKind) -> Self {
202 match value {
203 AddressKind::P2PKH => 0,
204 AddressKind::P2SH => 1,
205 AddressKind::P2WPKH => 2,
206 }
207 }
208}
209
210#[cfg(feature = "bitcoin")]
211impl TryFrom<&bitcoin::address::Payload> for AddressKind {
212 type Error = UnknownAddressType;
213
214 fn try_from(value: &bitcoin::address::Payload) -> Result<Self, Self::Error> {
215 use bitcoin::{address::Payload, blockdata::script::witness_version::WitnessVersion};
216
217 let kind = match value {
218 Payload::PubkeyHash(_) => AddressKind::P2PKH,
219 Payload::ScriptHash(_) => AddressKind::P2SH,
220 Payload::WitnessProgram(wp) => match wp.version() {
221 WitnessVersion::V0 if wp.program().as_bytes().len() == 20 => AddressKind::P2WPKH,
222 _ => return Err(UnknownAddressType),
223 },
224 _ => return Err(UnknownAddressType),
225 };
226
227 Ok(kind)
228 }
229}
230
231#[cfg(feature = "bitcoin")]
232#[derive(Debug, Clone, PartialEq, Eq)]
233pub struct UnknownAddressType;
234
235impl<'b, C> Decode<'b, C> for AddressKind {
236 fn decode(d: &mut Decoder<'b>, _ctx: &mut C) -> Result<Self, Error> {
237 AddressKind::try_from(d.u8()?).map_err(|_| Error::message("invalid address type"))
238 }
239}
240
241impl<C> Encode<C> for AddressKind {
242 fn encode<W: Write>(
243 &self,
244 e: &mut Encoder<W>,
245 _ctx: &mut C,
246 ) -> Result<(), minicbor::encode::Error<W::Error>> {
247 e.u8((*self).into())?;
248 Ok(())
249 }
250}