1use core::cmp::Ordering;
25use core::str::FromStr;
26use std::vec;
27
28use aluvm::alu::LibSite;
29use aluvm::fe256;
30use amplify::confinement::SmallBlob;
31use amplify::hex::FromHex;
32use amplify::num::u256;
33use amplify::{hex, Bytes};
34use commit_verify::{CommitEncode, CommitEngine, MerkleHash, StrictHash};
35
36use crate::LIB_NAME_ULTRASONIC;
37
38#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, From)]
39#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
40#[strict_type(lib = LIB_NAME_ULTRASONIC)]
41#[cfg_attr(
42 all(feature = "serde", not(feature = "baid64")),
43 derive(Serialize, Deserialize),
44 serde(transparent)
45)]
46pub struct AuthToken(#[from] fe256);
47
48impl PartialOrd for AuthToken {
53 fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
54}
55impl Ord for AuthToken {
56 fn cmp(&self, other: &Self) -> Ordering { self.0.to_u256().cmp(&other.0.to_u256()) }
57}
58
59impl From<[u8; 30]> for AuthToken {
60 fn from(value: [u8; 30]) -> Self { Self::from_byte_array(value) }
61}
62impl From<Bytes<30>> for AuthToken {
63 fn from(value: Bytes<30>) -> Self { Self::from_byte_array(value.to_byte_array()) }
64}
65
66impl AuthToken {
67 pub const fn to_fe256(&self) -> fe256 { self.0 }
68
69 pub fn from_byte_array(bytes: [u8; 30]) -> Self {
70 let mut buf = [0u8; 32];
71 buf[..30].copy_from_slice(&bytes);
72 let val = fe256::from(buf);
73 Self(val)
74 }
75
76 pub fn to_byte_array(&self) -> [u8; 30] {
77 let bytes = self.0.to_u256().to_le_bytes();
78 debug_assert_eq!(&bytes[30..], &[0, 0]);
79
80 let mut buf = [0u8; 30];
81 buf.copy_from_slice(&bytes[..30]);
82 buf
83 }
84
85 pub fn to_bytes30(&self) -> Bytes<30> {
86 let bytes = self.to_byte_array();
87 Bytes::from(bytes)
88 }
89}
90
91#[cfg(feature = "baid64")]
92mod _baid64 {
93 use core::fmt::{self, Display, Formatter};
94 use core::str::FromStr;
95
96 use baid64::{Baid64ParseError, DisplayBaid64, FromBaid64Str};
97
98 use super::*;
99
100 impl DisplayBaid64<30> for AuthToken {
101 const HRI: &'static str = "at";
102 const CHUNKING: bool = true;
103 const CHUNK_FIRST: usize = 8;
104 const CHUNK_LEN: usize = 8;
105 const PREFIX: bool = true;
106 const EMBED_CHECKSUM: bool = true;
107 const MNEMONIC: bool = false;
108 fn to_baid64_payload(&self) -> [u8; 30] { self.to_byte_array() }
109 }
110 impl FromBaid64Str<30> for AuthToken {}
111 impl FromStr for AuthToken {
112 type Err = Baid64ParseError;
113 fn from_str(s: &str) -> Result<Self, Self::Err> { Self::from_baid64_str(s) }
114 }
115 impl Display for AuthToken {
116 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { self.fmt_baid64(f) }
117 }
118}
119
120#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
121#[derive(StrictType, StrictEncode, StrictDecode)]
122#[strict_type(lib = LIB_NAME_ULTRASONIC, tags = custom)]
123#[cfg_attr(
124 feature = "serde",
125 derive(Serialize, Deserialize),
126 serde(tag = "type", rename_all = "camelCase")
127)]
128pub enum StateValue {
129 #[default]
130 #[strict_type(tag = 0x00)]
131 None,
132 #[strict_type(tag = 0x01)]
133 Single { first: fe256 },
134 #[strict_type(tag = 0x02)]
135 Double { first: fe256, second: fe256 },
136 #[strict_type(tag = 0x03)]
137 Triple {
138 first: fe256,
139 second: fe256,
140 third: fe256,
141 },
142 #[strict_type(tag = 0x04)]
143 Quadripple {
144 first: fe256,
145 second: fe256,
146 third: fe256,
147 fourth: fe256,
148 },
149}
150
151impl FromIterator<u256> for StateValue {
152 fn from_iter<T: IntoIterator<Item = u256>>(iter: T) -> Self {
153 Self::from_iter(iter.into_iter().map(fe256::from))
154 }
155}
156
157impl FromIterator<fe256> for StateValue {
158 fn from_iter<T: IntoIterator<Item = fe256>>(iter: T) -> Self {
159 let mut iter = iter.into_iter();
160 let first = iter.next();
161 let second = iter.next();
162 let third = iter.next();
163 let fourth = iter.next();
164 assert!(
165 iter.next().is_none(),
166 "the provided iterator for StateValue construction must not contain more than 4 \
167 elements"
168 );
169 let len = if fourth.is_some() {
170 4
171 } else if third.is_some() {
172 3
173 } else if second.is_some() {
174 2
175 } else if first.is_some() {
176 1
177 } else {
178 0
179 };
180 match len {
181 0 => StateValue::None,
182 1 => StateValue::Single { first: first.unwrap() },
183 2 => StateValue::Double { first: first.unwrap(), second: second.unwrap() },
184 3 => StateValue::Triple {
185 first: first.unwrap(),
186 second: second.unwrap(),
187 third: third.unwrap(),
188 },
189 4 => StateValue::Quadripple {
190 first: first.unwrap(),
191 second: second.unwrap(),
192 third: third.unwrap(),
193 fourth: fourth.unwrap(),
194 },
195 _ => panic!("state value can't use more than 4 elements"),
196 }
197 }
198}
199
200impl StateValue {
201 pub fn new(ty: impl Into<fe256>, val: impl Into<fe256>) -> Self {
202 StateValue::Double { first: ty.into(), second: val.into() }
203 }
204
205 pub const fn get(&self, pos: u8) -> Option<fe256> {
206 match (*self, pos) {
207 (Self::Single { first }, 0)
208 | (Self::Double { first, .. }, 0)
209 | (Self::Triple { first, .. }, 0)
210 | (Self::Quadripple { first, .. }, 0) => Some(first),
211
212 (Self::Double { second, .. }, 1)
213 | (Self::Triple { second, .. }, 1)
214 | (Self::Quadripple { second, .. }, 1) => Some(second),
215
216 (Self::Triple { third, .. }, 2) | (Self::Quadripple { third, .. }, 2) => Some(third),
217
218 (Self::Quadripple { fourth, .. }, 3) => Some(fourth),
219
220 _ => None,
221 }
222 }
223}
224
225impl IntoIterator for StateValue {
226 type Item = fe256;
227 type IntoIter = vec::IntoIter<fe256>;
228
229 fn into_iter(self) -> Self::IntoIter {
230 let vec = match self {
231 Self::None => vec![],
232 Self::Single { first } => vec![first],
233 Self::Double { first, second } => vec![first, second],
234 Self::Triple { first, second, third } => vec![first, second, third],
235 Self::Quadripple { first, second, third, fourth } => vec![first, second, third, fourth],
236 };
237 vec.into_iter()
238 }
239}
240
241#[derive(Copy, Clone, PartialEq, Eq, Debug)]
243#[derive(CommitEncode)]
244#[commit_encode(strategy = strict, id = MerkleHash)]
245#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
246#[strict_type(lib = LIB_NAME_ULTRASONIC)]
247#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
248pub struct StateCell {
249 pub data: StateValue,
250 pub auth: AuthToken,
252 pub lock: Option<LibSite>,
253}
254
255#[derive(Wrapper, WrapperMut, Clone, PartialEq, Eq, Debug, Display, From)]
256#[wrapper(AsSlice, BorrowSlice, Hex, RangeOps)]
257#[wrapper_mut(BorrowSliceMut, RangeMut)]
258#[display("0x{0:X}")]
259#[derive(CommitEncode)]
260#[commit_encode(strategy = strict, id = StrictHash)]
261#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
262#[strict_type(lib = LIB_NAME_ULTRASONIC)]
263pub struct RawData(#[from] SmallBlob);
264
265impl FromStr for RawData {
266 type Err = hex::Error;
267 fn from_str(mut s: &str) -> Result<Self, Self::Err> {
268 s = s.strip_prefix("0x").unwrap_or(s);
269 Self::from_hex(s)
270 }
271}
272
273#[derive(Clone, PartialEq, Eq, Debug)]
274#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
275#[strict_type(lib = LIB_NAME_ULTRASONIC)]
276#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
277pub struct StateData {
278 pub value: StateValue,
279 pub raw: Option<RawData>,
280}
281
282impl CommitEncode for StateData {
283 type CommitmentId = MerkleHash;
284
285 fn commit_encode(&self, e: &mut CommitEngine) {
286 e.commit_to_serialized(&self.value);
287 match &self.raw {
288 None => e.commit_to_option(&Option::<RawData>::None),
289 Some(raw) => e.commit_to_hash(raw),
290 }
291 }
292}
293
294impl StateData {
295 pub fn new(ty: impl Into<fe256>, val: impl Into<fe256>) -> Self {
296 Self { value: StateValue::new(ty, val), raw: None }
297 }
298
299 pub fn with_raw(ty: impl Into<fe256>, val: impl Into<fe256>, raw: impl Into<RawData>) -> Self {
300 Self { value: StateValue::new(ty, val), raw: Some(raw.into()) }
301 }
302}
303
304#[cfg(all(feature = "serde", feature = "baid64"))]
305mod _serde {
306 use serde::de::Error;
307 use serde::{Deserialize, Deserializer, Serialize, Serializer};
308
309 use super::*;
310
311 impl Serialize for AuthToken {
312 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
313 where S: Serializer {
314 if serializer.is_human_readable() {
315 serializer.serialize_str(&self.to_string())
316 } else {
317 self.0.serialize(serializer)
318 }
319 }
320 }
321
322 impl<'de> Deserialize<'de> for AuthToken {
323 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
324 where D: Deserializer<'de> {
325 if deserializer.is_human_readable() {
326 let s = String::deserialize(deserializer)?;
327 s.parse().map_err(D::Error::custom)
328 } else {
329 fe256::deserialize(deserializer).map(Self)
330 }
331 }
332 }
333}
334
335#[cfg(feature = "serde")]
336mod _serde2 {
337 use serde::de::Error;
338 use serde::{Deserialize, Deserializer, Serialize, Serializer};
339
340 use super::*;
341 impl Serialize for RawData {
342 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
343 where S: Serializer {
344 if serializer.is_human_readable() {
345 serializer.serialize_str(&self.to_string())
346 } else {
347 self.0.serialize(serializer)
348 }
349 }
350 }
351
352 impl<'de> Deserialize<'de> for RawData {
353 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
354 where D: Deserializer<'de> {
355 if deserializer.is_human_readable() {
356 let s = String::deserialize(deserializer)?;
357 s.parse().map_err(D::Error::custom)
358 } else {
359 SmallBlob::deserialize(deserializer).map(Self)
360 }
361 }
362 }
363}
364
365#[cfg(test)]
366mod test {
367 #[cfg(feature = "baid64")]
368 use super::*;
369
370 #[test]
371 #[cfg(feature = "baid64")]
372 fn auth_baid64() {
373 use baid64::DisplayBaid64;
374 let auth = AuthToken::from_byte_array([0xAD; 30]);
375
376 let baid64 = "at:ra2tra2t-ra2tra2t-ra2tra2t-ra2tra2t-ra2tra2t-HURE_w";
377 assert_eq!(baid64, auth.to_string());
378 assert_eq!(auth.to_string(), auth.to_baid64_string());
379
380 let auth2: AuthToken = baid64.parse().unwrap();
381 assert_eq!(auth, auth2);
382
383 let reconstructed = AuthToken::from_str(&baid64.replace('-', "")).unwrap();
384 assert_eq!(reconstructed, auth);
385 }
386}