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