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