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