iota_types/block/address/
mod.rs1mod 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::block::{
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 as_ed25519(&self) -> &Ed25519Address {
62 if let Self::Ed25519(address) = self {
63 address
64 } else {
65 panic!("as_ed25519 called on a non-ed25519 address");
66 }
67 }
68
69 pub fn is_alias(&self) -> bool {
71 matches!(self, Self::Alias(_))
72 }
73
74 pub fn as_alias(&self) -> &AliasAddress {
77 if let Self::Alias(address) = self {
78 address
79 } else {
80 panic!("as_alias called on a non-alias address");
81 }
82 }
83
84 pub fn is_nft(&self) -> bool {
86 matches!(self, Self::Nft(_))
87 }
88
89 pub fn as_nft(&self) -> &NftAddress {
92 if let Self::Nft(address) = self {
93 address
94 } else {
95 panic!("as_nft called on a non-nft address");
96 }
97 }
98
99 pub fn try_from_bech32<T: AsRef<str>>(address: T) -> Result<(String, Self), Error> {
101 match bech32::decode(address.as_ref()) {
102 Ok((hrp, data, _)) => {
103 let bytes = Vec::<u8>::from_base32(&data).map_err(|_| Error::InvalidAddress)?;
104 Self::unpack_verified(bytes.as_slice(), &())
105 .map_err(|_| Error::InvalidAddress)
106 .map(|address| (hrp, address))
107 }
108 Err(_) => Err(Error::InvalidAddress),
109 }
110 }
111
112 pub fn to_bech32<T: AsRef<str>>(&self, hrp: T) -> String {
114 bech32::encode(hrp.as_ref(), self.pack_to_vec().to_base32(), Variant::Bech32).unwrap()
116 }
117
118 pub fn unlock(
120 &self,
121 unlock: &Unlock,
122 inputs: &[(OutputId, &Output)],
123 context: &mut ValidationContext<'_>,
124 ) -> Result<(), ConflictReason> {
125 match (self, unlock) {
126 (Self::Ed25519(ed25519_address), Unlock::Signature(unlock)) => {
127 if context.unlocked_addresses.contains(self) {
128 return Err(ConflictReason::InvalidUnlock);
129 }
130
131 let Signature::Ed25519(signature) = unlock.signature();
132
133 if signature.is_valid(&context.essence_hash, ed25519_address).is_err() {
134 return Err(ConflictReason::InvalidSignature);
135 }
136
137 context.unlocked_addresses.insert(*self);
138 }
139 (Self::Ed25519(_ed25519_address), Unlock::Reference(_unlock)) => {
140 if !context.unlocked_addresses.contains(self) {
142 return Err(ConflictReason::InvalidUnlock);
143 }
144 }
145 (Self::Alias(alias_address), Unlock::Alias(unlock)) => {
146 if let (output_id, Output::Alias(alias_output)) = inputs[unlock.index() as usize] {
148 if &alias_output.alias_id_non_null(&output_id) != alias_address.alias_id() {
149 return Err(ConflictReason::InvalidUnlock);
150 }
151 if !context.unlocked_addresses.contains(self) {
152 return Err(ConflictReason::InvalidUnlock);
153 }
154 } else {
155 return Err(ConflictReason::InvalidUnlock);
156 }
157 }
158 (Self::Nft(nft_address), Unlock::Nft(unlock)) => {
159 if let (output_id, Output::Nft(nft_output)) = inputs[unlock.index() as usize] {
161 if &nft_output.nft_id_non_null(&output_id) != nft_address.nft_id() {
162 return Err(ConflictReason::InvalidUnlock);
163 }
164 if !context.unlocked_addresses.contains(self) {
165 return Err(ConflictReason::InvalidUnlock);
166 }
167 } else {
168 return Err(ConflictReason::InvalidUnlock);
169 }
170 }
171 _ => return Err(ConflictReason::InvalidUnlock),
172 }
173
174 Ok(())
175 }
176}
177
178#[cfg(feature = "dto")]
179#[allow(missing_docs)]
180pub mod dto {
181 use serde::{Deserialize, Serialize, Serializer};
182 use serde_json::Value;
183
184 use super::*;
185 pub use super::{alias::dto::AliasAddressDto, ed25519::dto::Ed25519AddressDto, nft::dto::NftAddressDto};
186 use crate::block::error::dto::DtoError;
187
188 #[derive(Clone, Debug, Eq, PartialEq, From)]
190 pub enum AddressDto {
191 Ed25519(Ed25519AddressDto),
193 Alias(AliasAddressDto),
195 Nft(NftAddressDto),
197 }
198
199 impl From<&Address> for AddressDto {
200 fn from(value: &Address) -> Self {
201 match value {
202 Address::Ed25519(a) => Self::Ed25519(a.into()),
203 Address::Alias(a) => Self::Alias(a.into()),
204 Address::Nft(a) => Self::Nft(a.into()),
205 }
206 }
207 }
208
209 impl TryFrom<&AddressDto> for Address {
210 type Error = DtoError;
211
212 fn try_from(value: &AddressDto) -> Result<Self, Self::Error> {
213 match value {
214 AddressDto::Ed25519(a) => Ok(Self::Ed25519(a.try_into()?)),
215 AddressDto::Alias(a) => Ok(Self::Alias(a.try_into()?)),
216 AddressDto::Nft(a) => Ok(Self::Nft(a.try_into()?)),
217 }
218 }
219 }
220
221 impl<'de> Deserialize<'de> for AddressDto {
222 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
223 let value = Value::deserialize(d)?;
224 Ok(
225 match value
226 .get("type")
227 .and_then(Value::as_u64)
228 .ok_or_else(|| serde::de::Error::custom("invalid address type"))? as u8
229 {
230 Ed25519Address::KIND => {
231 Self::Ed25519(Ed25519AddressDto::deserialize(value).map_err(|e| {
232 serde::de::Error::custom(format!("cannot deserialize ed25519 address: {e}"))
233 })?)
234 }
235 AliasAddress::KIND => Self::Alias(
236 AliasAddressDto::deserialize(value)
237 .map_err(|e| serde::de::Error::custom(format!("cannot deserialize alias address: {e}")))?,
238 ),
239 NftAddress::KIND => Self::Nft(
240 NftAddressDto::deserialize(value)
241 .map_err(|e| serde::de::Error::custom(format!("cannot deserialize NFT address: {e}")))?,
242 ),
243 _ => return Err(serde::de::Error::custom("invalid address type")),
244 },
245 )
246 }
247 }
248
249 impl Serialize for AddressDto {
250 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
251 where
252 S: Serializer,
253 {
254 #[derive(Serialize)]
255 #[serde(untagged)]
256 enum AddressDto_<'a> {
257 T1(&'a Ed25519AddressDto),
258 T2(&'a AliasAddressDto),
259 T3(&'a NftAddressDto),
260 }
261 #[derive(Serialize)]
262 struct TypedAddress<'a> {
263 #[serde(flatten)]
264 address: AddressDto_<'a>,
265 }
266 let address = match self {
267 Self::Ed25519(o) => TypedAddress {
268 address: AddressDto_::T1(o),
269 },
270 Self::Alias(o) => TypedAddress {
271 address: AddressDto_::T2(o),
272 },
273 Self::Nft(o) => TypedAddress {
274 address: AddressDto_::T3(o),
275 },
276 };
277 address.serialize(serializer)
278 }
279 }
280}