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