1mod alias;
5mod ed25519;
6mod nft;
7
8use alloc::{string::String, vec::Vec};
9
10use bech32::{self, FromBase32, ToBase32, Variant};
11use derive_more::From;
12use packable::PackableExt;
13
14pub use self::{alias::AliasAddress, ed25519::Ed25519Address, nft::NftAddress};
15use crate::{
16 output::{Output, OutputId},
17 semantic::{ConflictReason, ValidationContext},
18 signature::Signature,
19 unlock::Unlock,
20 Error,
21};
22
23#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, From, packable::Packable)]
25#[cfg_attr(
26 feature = "serde",
27 derive(serde::Serialize, serde::Deserialize),
28 serde(tag = "type", content = "data")
29)]
30#[packable(tag_type = u8, with_error = Error::InvalidAddressKind)]
31#[packable(unpack_error = Error)]
32pub enum Address {
33 #[packable(tag = Ed25519Address::KIND)]
35 Ed25519(Ed25519Address),
36 #[packable(tag = AliasAddress::KIND)]
38 Alias(AliasAddress),
39 #[packable(tag = NftAddress::KIND)]
41 Nft(NftAddress),
42}
43
44impl Address {
45 pub fn kind(&self) -> u8 {
47 match self {
48 Self::Ed25519(_) => Ed25519Address::KIND,
49 Self::Alias(_) => AliasAddress::KIND,
50 Self::Nft(_) => NftAddress::KIND,
51 }
52 }
53
54 pub fn is_ed25519(&self) -> bool {
56 matches!(self, Self::Ed25519(_))
57 }
58
59 pub fn is_alias(&self) -> bool {
61 matches!(self, Self::Alias(_))
62 }
63
64 pub fn is_nft(&self) -> bool {
66 matches!(self, Self::Nft(_))
67 }
68
69 pub fn try_from_bech32<T: AsRef<str>>(address: T) -> Result<(String, Self), Error> {
71 match bech32::decode(address.as_ref()) {
72 Ok((hrp, data, _)) => {
73 let bytes = Vec::<u8>::from_base32(&data).map_err(|_| Error::InvalidAddress)?;
74 Self::unpack_verified(bytes.as_slice(), &())
75 .map_err(|_| Error::InvalidAddress)
76 .map(|address| (hrp, address))
77 }
78 Err(_) => Err(Error::InvalidAddress),
79 }
80 }
81
82 #[allow(clippy::wrong_self_convention)]
84 pub fn to_bech32<T: AsRef<str>>(&self, hrp: T) -> String {
85 bech32::encode(hrp.as_ref(), self.pack_to_vec().to_base32(), Variant::Bech32).unwrap()
87 }
88
89 pub fn unlock(
91 &self,
92 unlock: &Unlock,
93 inputs: &[(OutputId, &Output)],
94 context: &mut ValidationContext,
95 ) -> Result<(), ConflictReason> {
96 match (self, unlock) {
97 (Address::Ed25519(ed25519_address), Unlock::Signature(unlock)) => {
98 if context.unlocked_addresses.contains(self) {
99 return Err(ConflictReason::InvalidUnlock);
100 }
101
102 let Signature::Ed25519(signature) = unlock.signature();
103
104 if signature.is_valid(&context.essence_hash, ed25519_address).is_err() {
105 return Err(ConflictReason::InvalidSignature);
106 }
107
108 context.unlocked_addresses.insert(*self);
109 }
110 (Address::Ed25519(_ed25519_address), Unlock::Reference(_unlock)) => {
111 if !context.unlocked_addresses.contains(self) {
113 return Err(ConflictReason::InvalidUnlock);
114 }
115 }
116 (Address::Alias(alias_address), Unlock::Alias(unlock)) => {
117 if let (output_id, Output::Alias(alias_output)) = inputs[unlock.index() as usize] {
119 if &alias_output.alias_id().or_from_output_id(output_id) != alias_address.alias_id() {
120 return Err(ConflictReason::InvalidUnlock);
121 }
122 if !context.unlocked_addresses.contains(self) {
123 return Err(ConflictReason::InvalidUnlock);
124 }
125 } else {
126 return Err(ConflictReason::InvalidUnlock);
127 }
128 }
129 (Address::Nft(nft_address), Unlock::Nft(unlock)) => {
130 if let (output_id, Output::Nft(nft_output)) = inputs[unlock.index() as usize] {
132 if &nft_output.nft_id().or_from_output_id(output_id) != nft_address.nft_id() {
133 return Err(ConflictReason::InvalidUnlock);
134 }
135 if !context.unlocked_addresses.contains(self) {
136 return Err(ConflictReason::InvalidUnlock);
137 }
138 } else {
139 return Err(ConflictReason::InvalidUnlock);
140 }
141 }
142 _ => return Err(ConflictReason::InvalidUnlock),
143 }
144
145 Ok(())
146 }
147}
148
149#[cfg(feature = "dto")]
150#[allow(missing_docs)]
151pub mod dto {
152 use serde::{Deserialize, Serialize, Serializer};
153 use serde_json::Value;
154
155 use super::*;
156 pub use super::{alias::dto::AliasAddressDto, ed25519::dto::Ed25519AddressDto, nft::dto::NftAddressDto};
157 use crate::error::dto::DtoError;
158
159 #[derive(Clone, Debug, Eq, PartialEq, From)]
161 pub enum AddressDto {
162 Ed25519(Ed25519AddressDto),
164 Alias(AliasAddressDto),
166 Nft(NftAddressDto),
168 }
169
170 impl From<&Address> for AddressDto {
171 fn from(value: &Address) -> Self {
172 match value {
173 Address::Ed25519(a) => AddressDto::Ed25519(a.into()),
174 Address::Alias(a) => AddressDto::Alias(a.into()),
175 Address::Nft(a) => AddressDto::Nft(a.into()),
176 }
177 }
178 }
179
180 impl TryFrom<&AddressDto> for Address {
181 type Error = DtoError;
182
183 fn try_from(value: &AddressDto) -> Result<Self, Self::Error> {
184 match value {
185 AddressDto::Ed25519(a) => Ok(Address::Ed25519(a.try_into()?)),
186 AddressDto::Alias(a) => Ok(Address::Alias(a.try_into()?)),
187 AddressDto::Nft(a) => Ok(Address::Nft(a.try_into()?)),
188 }
189 }
190 }
191
192 impl<'de> Deserialize<'de> for AddressDto {
193 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
194 let value = Value::deserialize(d)?;
195 Ok(
196 match value
197 .get("type")
198 .and_then(Value::as_u64)
199 .ok_or_else(|| serde::de::Error::custom("invalid address type"))? as u8
200 {
201 Ed25519Address::KIND => {
202 AddressDto::Ed25519(Ed25519AddressDto::deserialize(value).map_err(|e| {
203 serde::de::Error::custom(format!("cannot deserialize ed25519 address: {}", e))
204 })?)
205 }
206 AliasAddress::KIND => {
207 AddressDto::Alias(AliasAddressDto::deserialize(value).map_err(|e| {
208 serde::de::Error::custom(format!("cannot deserialize alias address: {}", e))
209 })?)
210 }
211 NftAddress::KIND => AddressDto::Nft(
212 NftAddressDto::deserialize(value)
213 .map_err(|e| serde::de::Error::custom(format!("cannot deserialize NFT address: {}", e)))?,
214 ),
215 _ => return Err(serde::de::Error::custom("invalid address type")),
216 },
217 )
218 }
219 }
220
221 impl Serialize for AddressDto {
222 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
223 where
224 S: Serializer,
225 {
226 #[derive(Serialize)]
227 #[serde(untagged)]
228 enum AddressDto_<'a> {
229 T1(&'a Ed25519AddressDto),
230 T2(&'a AliasAddressDto),
231 T3(&'a NftAddressDto),
232 }
233 #[derive(Serialize)]
234 struct TypedAddress<'a> {
235 #[serde(flatten)]
236 address: AddressDto_<'a>,
237 }
238 let address = match self {
239 AddressDto::Ed25519(o) => TypedAddress {
240 address: AddressDto_::T1(o),
241 },
242 AddressDto::Alias(o) => TypedAddress {
243 address: AddressDto_::T2(o),
244 },
245 AddressDto::Nft(o) => TypedAddress {
246 address: AddressDto_::T3(o),
247 },
248 };
249 address.serialize(serializer)
250 }
251 }
252}