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