1use anyhow::{anyhow, ensure, Result};
2use ark_std::{
3 cmp::Ordering,
4 collections::BTreeMap,
5 format,
6 string::{String, ToString},
7 vec::Vec,
8};
9use ciborium::Value;
10use core::{convert::TryInto, fmt};
11use serde::{
12 de,
13 de::{DeserializeOwned, SeqAccess, Visitor},
14 ser::SerializeTuple,
15 Deserialize, Deserializer, Serialize, Serializer,
16};
17pub mod util;
18
19#[macro_export]
35macro_rules! check {
36 ($condition:expr) => {
37 if !$condition {
38 eprintln!("condition does not hold: {}", stringify!($condition));
39 return false;
40 }
41 };
42}
43
44#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
49pub struct Transaction {
50 pub ins: BTreeMap<UtxoId, Charms>,
52 pub refs: BTreeMap<UtxoId, Charms>,
54 pub outs: Vec<Charms>,
56}
57
58pub type Charms = BTreeMap<App, Data>;
62
63#[cfg_attr(test, derive(test_strategy::Arbitrary))]
66#[derive(Clone, Default, Eq, Ord, PartialEq, PartialOrd)]
67pub struct UtxoId(pub TxId, pub u32);
68
69impl UtxoId {
70 pub fn to_bytes(&self) -> [u8; 36] {
72 let mut bytes = [0u8; 36];
73 bytes[..32].copy_from_slice(&self.0 .0); bytes[32..].copy_from_slice(&self.1.to_le_bytes()); bytes
76 }
77
78 pub fn from_bytes(bytes: [u8; 36]) -> Self {
80 let mut txid_bytes = [0u8; 32];
81 txid_bytes.copy_from_slice(&bytes[..32]);
82 let index = u32::from_le_bytes(bytes[32..].try_into().unwrap());
83 UtxoId(TxId(txid_bytes), index)
84 }
85
86 pub fn from_str(s: &str) -> Result<Self> {
93 let parts: Vec<&str> = s.split(':').collect();
94 if parts.len() != 2 {
95 return Err(anyhow!("expected format: txid_hex:index"));
96 }
97
98 let txid = TxId::from_str(parts[0])?;
99
100 let index = parts[1]
101 .parse::<u32>()
102 .map_err(|e| anyhow!("invalid index: {}", e))?;
103
104 Ok(UtxoId(txid, index))
105 }
106
107 fn to_string_internal(&self) -> String {
108 format!("{}:{}", self.0.to_string(), self.1)
109 }
110}
111
112impl fmt::Display for UtxoId {
113 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
114 self.to_string_internal().fmt(f)
115 }
116}
117
118impl fmt::Debug for UtxoId {
119 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120 write!(f, "UtxoId({})", self.to_string_internal())
121 }
122}
123
124impl Serialize for UtxoId {
125 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
126 where
127 S: Serializer,
128 {
129 if serializer.is_human_readable() {
130 serializer.serialize_str(&self.to_string())
131 } else {
132 serializer.serialize_bytes(self.to_bytes().as_ref())
133 }
134 }
135}
136
137impl<'de> Deserialize<'de> for UtxoId {
138 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
139 where
140 D: Deserializer<'de>,
141 {
142 struct UtxoIdVisitor;
143
144 impl<'de> Visitor<'de> for UtxoIdVisitor {
145 type Value = UtxoId;
146
147 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
148 formatter.write_str("a string in format 'txid_hex:index' or a tuple (TxId, u32)")
149 }
150
151 fn visit_str<E>(self, value: &str) -> Result<UtxoId, E>
153 where
154 E: de::Error,
155 {
156 UtxoId::from_str(value).map_err(E::custom)
157 }
158
159 fn visit_bytes<E>(self, v: &[u8]) -> core::result::Result<Self::Value, E>
161 where
162 E: de::Error,
163 {
164 Ok(UtxoId::from_bytes(v.try_into().map_err(|e| {
165 E::custom(format!("invalid utxo_id bytes: {}", e))
166 })?))
167 }
168 }
169
170 if deserializer.is_human_readable() {
171 deserializer.deserialize_str(UtxoIdVisitor)
172 } else {
173 deserializer.deserialize_bytes(UtxoIdVisitor)
174 }
175 }
176}
177
178#[cfg_attr(test, derive(proptest_derive::Arbitrary))]
196#[derive(Clone, Default, Eq, Ord, PartialEq, PartialOrd)]
197pub struct App {
198 pub tag: char,
199 pub identity: B32,
200 pub vk: B32,
201}
202
203impl fmt::Display for App {
204 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
205 write!(f, "{}/{}/{}", self.tag, self.identity, self.vk)
206 }
207}
208
209impl fmt::Debug for App {
210 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
211 write!(f, "App({}/{}/{})", self.tag, self.identity, self.vk)
212 }
213}
214
215impl Serialize for App {
216 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
217 where
218 S: Serializer,
219 {
220 if serializer.is_human_readable() {
221 serializer.serialize_str(&self.to_string())
222 } else {
223 let mut s = serializer.serialize_tuple(3)?;
224 s.serialize_element(&self.tag)?;
225 s.serialize_element(&self.identity)?;
226 s.serialize_element(&self.vk)?;
227 s.end()
228 }
229 }
230}
231
232impl<'de> Deserialize<'de> for App {
233 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
234 where
235 D: Deserializer<'de>,
236 {
237 struct AppVisitor;
238
239 impl<'de> Visitor<'de> for AppVisitor {
240 type Value = App;
241
242 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
243 formatter.write_str("a string in format 'tag_char/identity_hex/vk_hex' or a struct with tag, identity and vk fields")
244 }
245
246 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
248 where
249 E: de::Error,
250 {
251 let mut parts: Vec<&str> = value.split('/').collect();
253 if parts[0].is_empty() && parts[1].is_empty() {
254 parts.remove(0);
255 parts[0] = "/";
256 }
257 let parts = parts;
258 if parts.len() != 3 {
259 return Err(E::custom("expected format: tag_char/identity_hex/vk_hex"));
260 }
261
262 let tag: char = {
264 let mut chars = parts[0].chars();
265 let Some(tag) = chars.next() else {
266 return Err(E::custom("expected tag"));
267 };
268 let None = chars.next() else {
269 return Err(E::custom("tag must be a single character"));
270 };
271 tag
272 };
273
274 let identity = B32::from_str(parts[1]).map_err(E::custom)?;
275
276 let vk = B32::from_str(parts[2]).map_err(E::custom)?;
277
278 Ok(App { tag, identity, vk })
279 }
280
281 fn visit_seq<A>(self, mut seq: A) -> core::result::Result<Self::Value, A::Error>
282 where
283 A: SeqAccess<'de>,
284 {
285 let tag = seq
286 .next_element()?
287 .ok_or_else(|| de::Error::missing_field("tag"))?;
288 let identity = seq
289 .next_element()?
290 .ok_or_else(|| de::Error::missing_field("identity"))?;
291 let vk = seq
292 .next_element()?
293 .ok_or_else(|| de::Error::missing_field("vk"))?;
294
295 Ok(App { tag, identity, vk })
296 }
297 }
298
299 if deserializer.is_human_readable() {
300 deserializer.deserialize_str(AppVisitor)
301 } else {
302 deserializer.deserialize_tuple(3, AppVisitor)
303 }
304 }
305}
306
307#[cfg_attr(test, derive(proptest_derive::Arbitrary))]
309#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
310pub struct TxId(pub [u8; 32]);
311
312impl TxId {
313 pub fn from_str(s: &str) -> Result<Self> {
323 ensure!(s.len() == 64, "expected 64 hex characters");
324 let bytes = hex::decode(s).map_err(|e| anyhow!("invalid txid hex: {}", e))?;
325 let mut txid: [u8; 32] = bytes.try_into().unwrap();
326 txid.reverse();
327 Ok(TxId(txid))
328 }
329
330 fn to_string_internal(&self) -> String {
331 let mut txid = self.0;
332 txid.reverse();
333 hex::encode(&txid)
334 }
335}
336
337impl fmt::Display for TxId {
338 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
339 self.to_string_internal().fmt(f)
340 }
341}
342
343impl fmt::Debug for TxId {
344 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
345 write!(f, "TxId({})", self.to_string_internal())
346 }
347}
348
349impl Serialize for TxId {
350 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
351 where
352 S: Serializer,
353 {
354 if serializer.is_human_readable() {
355 serializer.serialize_str(&self.to_string())
356 } else {
357 serializer.serialize_bytes(&self.0)
358 }
359 }
360}
361
362impl<'de> Deserialize<'de> for TxId {
363 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
364 where
365 D: Deserializer<'de>,
366 {
367 struct TxIdVisitor;
368
369 impl<'de> Visitor<'de> for TxIdVisitor {
370 type Value = TxId;
371
372 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
373 formatter.write_str("a string of 64 hex characters or a byte array of 32 bytes")
374 }
375
376 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
378 where
379 E: de::Error,
380 {
381 TxId::from_str(value).map_err(E::custom)
382 }
383
384 fn visit_bytes<E>(self, v: &[u8]) -> core::result::Result<Self::Value, E>
386 where
387 E: de::Error,
388 {
389 Ok(TxId(v.try_into().map_err(|e| {
390 E::custom(format!("invalid txid bytes: {}", e))
391 })?))
392 }
393 }
394
395 if deserializer.is_human_readable() {
396 deserializer.deserialize_str(TxIdVisitor)
397 } else {
398 deserializer.deserialize_bytes(TxIdVisitor)
399 }
400 }
401}
402
403#[cfg_attr(test, derive(proptest_derive::Arbitrary))]
405#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
406pub struct B32(pub [u8; 32]);
407
408impl B32 {
409 pub fn from_str(s: &str) -> Result<Self> {
411 ensure!(s.len() == 64, "expected 64 hex characters");
412 let bytes = hex::decode(s).map_err(|e| anyhow!("invalid hex: {}", e))?;
413 let hash: [u8; 32] = bytes.try_into().unwrap();
414 Ok(B32(hash))
415 }
416}
417
418impl AsRef<[u8]> for B32 {
419 fn as_ref(&self) -> &[u8] {
420 &self.0
421 }
422}
423
424impl fmt::Display for B32 {
425 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
426 hex::encode(&self.0).fmt(f)
427 }
428}
429
430impl fmt::Debug for B32 {
431 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
432 write!(f, "Bytes32({})", hex::encode(&self.0))
433 }
434}
435
436impl Serialize for B32 {
437 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
438 where
439 S: Serializer,
440 {
441 if serializer.is_human_readable() {
442 serializer.serialize_str(&self.to_string())
443 } else {
444 let mut seq = serializer.serialize_tuple(32)?;
445 for &byte in &self.0 {
446 seq.serialize_element(&byte)?;
447 }
448 seq.end()
449 }
450 }
451}
452
453impl<'de> Deserialize<'de> for B32 {
454 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
455 where
456 D: Deserializer<'de>,
457 {
458 struct B32Visitor;
459
460 impl<'de> Visitor<'de> for B32Visitor {
461 type Value = B32;
462
463 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
464 formatter.write_str("a string of 64 hex characters or a sequence of 32 bytes")
465 }
466
467 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
469 where
470 E: de::Error,
471 {
472 B32::from_str(value).map_err(E::custom)
473 }
474
475 fn visit_seq<A>(self, mut seq: A) -> std::result::Result<Self::Value, A::Error>
477 where
478 A: SeqAccess<'de>,
479 {
480 let mut bytes = [0u8; 32];
481 for i in 0..32 {
482 bytes[i] = seq
483 .next_element()?
484 .ok_or_else(|| serde::de::Error::invalid_length(i, &"32 elements"))?;
485 }
486
487 if seq.next_element::<u8>()?.is_some() {
489 return Err(serde::de::Error::invalid_length(33, &"exactly 32 elements"));
490 }
491
492 Ok(B32(bytes))
493 }
494 }
495
496 if deserializer.is_human_readable() {
497 deserializer.deserialize_str(B32Visitor)
498 } else {
499 deserializer.deserialize_tuple(32, B32Visitor)
500 }
501 }
502}
503
504#[derive(Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
506pub struct Data(Value);
507
508impl Eq for Data {}
509
510impl Ord for Data {
511 fn cmp(&self, other: &Self) -> Ordering {
512 self.0
513 .partial_cmp(&other.0)
514 .expect("Value comparison should have succeeded")
515 }
516}
517
518impl Data {
519 pub fn empty() -> Self {
521 Self(Value::Null)
522 }
523
524 pub fn is_empty(&self) -> bool {
526 self.0.is_null()
527 }
528
529 pub fn value<T: DeserializeOwned>(&self) -> Result<T> {
532 self.0
533 .deserialized()
534 .map_err(|e| anyhow!("deserialization error: {}", e))
535 }
536
537 pub fn bytes(&self) -> Vec<u8> {
539 util::write(&self).expect("serialization should have succeeded")
540 }
541}
542
543impl<T> From<&T> for Data
544where
545 T: Serialize,
546{
547 fn from(value: &T) -> Self {
548 Self(Value::serialized(value).expect("casting to a CBOR Value should have succeeded"))
549 }
550}
551
552impl Default for Data {
553 fn default() -> Self {
554 Self::empty()
555 }
556}
557
558impl fmt::Debug for Data {
559 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
560 write!(f, "Data({})", format!("{:?}", &self.0))
561 }
562}
563
564pub const TOKEN: char = 't';
566pub const NFT: char = 'n';
568
569pub fn is_simple_transfer(app: &App, tx: &Transaction) -> bool {
571 match app.tag {
572 TOKEN => token_amounts_balanced(app, tx),
573 NFT => nft_state_preserved(app, tx),
574 _ => false,
575 }
576}
577
578pub fn token_amounts_balanced(app: &App, tx: &Transaction) -> bool {
582 match (
583 sum_token_amount(app, tx.ins.values()),
584 sum_token_amount(app, tx.outs.iter()),
585 ) {
586 (Ok(amount_in), Ok(amount_out)) => amount_in == amount_out,
587 (..) => false,
588 }
589}
590
591pub fn nft_state_preserved(app: &App, tx: &Transaction) -> bool {
594 let nft_states_in = app_state_multiset(app, tx.ins.values());
595 let nft_states_out = app_state_multiset(app, tx.outs.iter());
596
597 nft_states_in == nft_states_out
598}
599
600pub fn app_datas<'a>(
601 app: &'a App,
602 strings_of_charms: impl Iterator<Item = &'a Charms>,
603) -> impl Iterator<Item = &'a Data> {
604 strings_of_charms.filter_map(|charms| charms.get(app))
605}
606
607fn app_state_multiset<'a>(
608 app: &App,
609 strings_of_charms: impl Iterator<Item = &'a Charms>,
610) -> BTreeMap<&'a Data, usize> {
611 strings_of_charms
612 .filter_map(|charms| charms.get(app))
613 .fold(BTreeMap::new(), |mut r, s| {
614 match r.get_mut(&s) {
615 Some(count) => *count += 1,
616 None => {
617 r.insert(s, 1);
618 }
619 }
620 r
621 })
622}
623
624pub fn sum_token_amount<'a>(
626 app: &App,
627 strings_of_charms: impl Iterator<Item = &'a Charms>,
628) -> Result<u64> {
629 ensure!(app.tag == TOKEN);
630 strings_of_charms.fold(Ok(0u64), |amount, charms| match charms.get(app) {
631 Some(state) => Ok(amount? + state.value::<u64>()?),
632 None => amount,
633 })
634}
635
636#[cfg(test)]
637mod tests {
638 use super::*;
639 use ciborium::Value;
640 use proptest::prelude::*;
641 use test_strategy::proptest;
642
643 #[proptest]
644 fn doesnt_crash(s: String) {
645 let _ = TxId::from_str(&s);
646 }
647
648 #[proptest]
649 fn txid_roundtrip(txid: TxId) {
650 let s = txid.to_string();
651 let txid2 = TxId::from_str(&s).unwrap();
652 prop_assert_eq!(txid, txid2);
653 }
654
655 #[proptest]
656 fn vk_serde_roundtrip(vk: B32) {
657 let bytes = util::write(&vk).unwrap();
658 let vk2 = util::read(bytes.as_slice()).unwrap();
659 prop_assert_eq!(vk, vk2);
660 }
661
662 #[proptest]
663 fn vk_serde_json_roundtrip(vk: App) {
664 let json_str = serde_json::to_string(&vk).unwrap();
665 let vk2 = serde_json::from_str(&json_str).unwrap();
667 prop_assert_eq!(vk, vk2);
668 }
669
670 #[proptest]
671 fn vk_serde_yaml_roundtrip(vk: App) {
672 let yaml_str = serde_yaml::to_string(&vk).unwrap();
673 let vk2 = serde_yaml::from_str(&yaml_str).unwrap();
675 prop_assert_eq!(vk, vk2);
676 }
677
678 #[proptest]
679 fn app_serde_roundtrip(app: App) {
680 let bytes = util::write(&app).unwrap();
681 let app2 = util::read(bytes.as_slice()).unwrap();
682 prop_assert_eq!(app, app2);
683 }
684
685 #[proptest]
686 fn utxo_id_serde_roundtrip(utxo_id: UtxoId) {
687 let bytes = util::write(&utxo_id).unwrap();
688 let utxo_id2 = util::read(bytes.as_slice()).unwrap();
689 prop_assert_eq!(utxo_id, utxo_id2);
690 }
691
692 #[proptest]
693 fn tx_id_serde_roundtrip(tx_id: TxId) {
694 let bytes = util::write(&tx_id).unwrap();
695 let tx_id2 = util::read(bytes.as_slice()).unwrap();
696 prop_assert_eq!(tx_id, tx_id2);
697 }
698
699 #[test]
700 fn minimal_txid() {
701 let tx_id_bytes: [u8; 32] = [&[1u8], [0u8; 31].as_ref()].concat().try_into().unwrap();
702 let tx_id = TxId(tx_id_bytes);
703 let tx_id_str = tx_id.to_string();
704 let tx_id_str_expected = "0000000000000000000000000000000000000000000000000000000000000001";
705 assert_eq!(tx_id_str, tx_id_str_expected);
706 }
707
708 #[test]
709 fn data_dbg() {
710 let v = 42u64;
711 let data: Data = Data::from(&v);
712 assert_eq!(format!("{:?}", data), format!("Data({:?})", Value::from(v)));
713
714 let data = Data::empty();
715 assert_eq!(format!("{:?}", data), "Data(Null)");
716
717 let vec1: Vec<u64> = vec![];
718 let data: Data = Data::from(&vec1);
719 assert_eq!(format!("{:?}", data), "Data(Array([]))");
720 }
721
722 #[test]
723 fn data_bytes() {
724 let v = ("42u64", 42u64);
725 let data = Data::from(&v);
726 let value = Value::serialized(&v).expect("serialization should have succeeded");
727
728 let buf = util::write(&value).expect("serialization should have succeeded");
729
730 assert_eq!(data.bytes(), buf);
731 }
732
733 #[test]
734 fn dummy() {}
735}