1use alloc::{
2 format,
3 string::{String, ToString},
4 vec::Vec,
5};
6use core::{
7 fmt::Display,
8 ops::{Add, Deref, DerefMut, Sub},
9};
10
11use borsh::{BorshDeserialize, BorshSerialize};
12use serde::{Deserialize, Serialize};
13
14use crate::{utils::TimestampMs, LaneId};
15
16#[derive(
17 Serialize,
18 Deserialize,
19 Clone,
20 BorshSerialize,
21 BorshDeserialize,
22 PartialEq,
23 Eq,
24 Default,
25 Ord,
26 PartialOrd,
27)]
28#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
29pub struct ConsensusProposalHash(#[serde(with = "crate::utils::hex_bytes")] pub Vec<u8>);
30pub type BlockHash = ConsensusProposalHash;
31
32impl core::hash::Hash for ConsensusProposalHash {
33 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
34 state.write(&self.0);
35 }
36}
37
38impl core::fmt::Debug for ConsensusProposalHash {
39 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
40 write!(f, "ConsensusProposalHash({})", hex::encode(&self.0))
41 }
42}
43
44pub trait Hashed<T> {
45 fn hashed(&self) -> T;
46}
47
48pub trait DataSized {
49 fn estimate_size(&self) -> usize;
50}
51
52#[derive(
54 Default,
55 Serialize,
56 Deserialize,
57 Debug,
58 Clone,
59 PartialEq,
60 Eq,
61 Hash,
62 BorshSerialize,
63 BorshDeserialize,
64)]
65#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
66pub struct IndexedBlobs(pub Vec<(BlobIndex, Blob)>);
67
68impl Deref for IndexedBlobs {
69 type Target = Vec<(BlobIndex, Blob)>;
70
71 fn deref(&self) -> &Self::Target {
72 &self.0
73 }
74}
75
76impl DerefMut for IndexedBlobs {
77 fn deref_mut(&mut self) -> &mut Self::Target {
78 &mut self.0
79 }
80}
81
82impl IndexedBlobs {
83 pub fn get(&self, index: &BlobIndex) -> Option<&Blob> {
84 self.0.iter().find(|(i, _)| i == index).map(|(_, b)| b)
85 }
86}
87
88impl<T> From<T> for IndexedBlobs
89where
90 T: IntoIterator<Item = Blob>,
91{
92 fn from(vec: T) -> Self {
93 let mut blobs = IndexedBlobs::default();
94 for (i, blob) in vec.into_iter().enumerate() {
95 blobs.push((BlobIndex(i), blob));
96 }
97 blobs
98 }
99}
100
101impl<'a> IntoIterator for &'a IndexedBlobs {
102 type Item = &'a (BlobIndex, Blob);
103 type IntoIter = core::slice::Iter<'a, (BlobIndex, Blob)>;
104
105 fn into_iter(self) -> Self::IntoIter {
106 self.0.iter()
107 }
108}
109
110#[derive(Default, Serialize, Deserialize, BorshSerialize, BorshDeserialize, Debug, Clone)]
113pub struct Calldata {
114 pub tx_hash: TxHash,
116 pub identity: Identity,
118 pub blobs: IndexedBlobs,
120 pub tx_blob_count: usize,
122 pub index: BlobIndex,
125 pub tx_ctx: Option<TxContext>,
127 pub private_input: Vec<u8>,
129}
130
131impl Calldata {
132 pub fn get_blob(&self) -> Result<&Blob, String> {
133 self.blobs
134 .get(&self.index)
135 .ok_or_else(|| format!("Blob with index {} not found in calldata", self.index.0))
136 }
137}
138
139#[derive(
141 Default, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, BorshSerialize, BorshDeserialize,
142)]
143#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
144pub struct StateCommitment(pub Vec<u8>);
145
146impl core::fmt::Debug for StateCommitment {
147 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
148 write!(f, "StateCommitment({})", hex::encode(&self.0))
149 }
150}
151
152#[derive(
153 Default,
154 Serialize,
155 Deserialize,
156 Debug,
157 Clone,
158 PartialEq,
159 Eq,
160 Hash,
161 BorshSerialize,
162 BorshDeserialize,
163 Ord,
164 PartialOrd,
165)]
166#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
167pub struct Identity(pub String);
170
171#[derive(
172 Default,
173 Serialize,
174 Deserialize,
175 Clone,
176 PartialEq,
177 Eq,
178 Hash,
179 BorshSerialize,
180 BorshDeserialize,
181 Ord,
182 PartialOrd,
183)]
184#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
185pub struct TxHash(#[serde(with = "crate::utils::hex_bytes")] pub Vec<u8>);
186
187#[derive(
188 Default,
189 Serialize,
190 Deserialize,
191 Debug,
192 Clone,
193 PartialEq,
194 Eq,
195 Hash,
196 BorshDeserialize,
197 BorshSerialize,
198 Copy,
199 PartialOrd,
200 Ord,
201)]
202#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
203pub struct BlobIndex(pub usize);
204
205impl Add<usize> for BlobIndex {
206 type Output = BlobIndex;
207 fn add(self, other: usize) -> BlobIndex {
208 BlobIndex(self.0 + other)
209 }
210}
211
212#[derive(
213 Default, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, BorshSerialize, BorshDeserialize,
214)]
215#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
216pub struct BlobData(pub Vec<u8>);
217
218impl core::fmt::Debug for BlobData {
219 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
220 if self.0.len() > 20 {
221 write!(f, "BlobData({}...)", hex::encode(&self.0[..20]))
222 } else {
223 write!(f, "BlobData({})", hex::encode(&self.0))
224 }
225 }
226}
227
228#[derive(Debug, BorshSerialize)]
302pub struct StructuredBlobData<Action> {
303 pub caller: Option<BlobIndex>,
304 pub callees: Option<Vec<BlobIndex>>,
305 pub parameters: Action,
306}
307
308pub struct DropEndOfReader;
315
316impl<Action: BorshDeserialize> BorshDeserialize for StructuredBlobData<Action> {
317 fn deserialize_reader<R: borsh::io::Read>(reader: &mut R) -> borsh::io::Result<Self> {
318 let caller = Option::<BlobIndex>::deserialize_reader(reader)?;
319 let callees = Option::<Vec<BlobIndex>>::deserialize_reader(reader)?;
320 let parameters = Action::deserialize_reader(reader)?;
321 Ok(StructuredBlobData {
322 caller,
323 callees,
324 parameters,
325 })
326 }
327}
328
329impl BorshDeserialize for StructuredBlobData<DropEndOfReader> {
330 fn deserialize_reader<R: borsh::io::Read>(reader: &mut R) -> borsh::io::Result<Self> {
331 let caller = Option::<BlobIndex>::deserialize_reader(reader)?;
332 let callees = Option::<Vec<BlobIndex>>::deserialize_reader(reader)?;
333 let mut buf = [0u8; 64];
335 loop {
336 if reader.read(&mut buf)? == 0 {
337 break;
338 }
339 }
340 let parameters = DropEndOfReader;
341 Ok(StructuredBlobData {
342 caller,
343 callees,
344 parameters,
345 })
346 }
347}
348
349impl<Action: BorshSerialize> From<StructuredBlobData<Action>> for BlobData {
350 fn from(val: StructuredBlobData<Action>) -> Self {
351 BlobData(borsh::to_vec(&val).expect("failed to encode BlobData"))
352 }
353}
354impl<Action: BorshDeserialize> TryFrom<BlobData> for StructuredBlobData<Action> {
355 type Error = borsh::io::Error;
356
357 fn try_from(val: BlobData) -> Result<StructuredBlobData<Action>, Self::Error> {
358 borsh::from_slice(&val.0)
359 }
360}
361impl TryFrom<BlobData> for StructuredBlobData<DropEndOfReader> {
362 type Error = borsh::io::Error;
363
364 fn try_from(val: BlobData) -> Result<StructuredBlobData<DropEndOfReader>, Self::Error> {
365 borsh::from_slice(&val.0)
366 }
367}
368
369#[derive(
370 Debug,
371 Serialize,
372 Deserialize,
373 Default,
374 Clone,
375 PartialEq,
376 Eq,
377 BorshSerialize,
378 BorshDeserialize,
379 Hash,
380)]
381#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
382pub struct Blob {
386 pub contract_name: ContractName,
387 pub data: BlobData,
388}
389
390#[derive(
391 Default, Clone, Serialize, Deserialize, Eq, PartialEq, Hash, BorshSerialize, BorshDeserialize,
392)]
393#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
394pub struct BlobHash(#[serde(with = "crate::utils::hex_bytes")] pub Vec<u8>);
395
396#[cfg(feature = "full")]
397impl Hashed<BlobHash> for Blob {
398 fn hashed(&self) -> BlobHash {
399 use sha3::{Digest, Sha3_256};
400
401 let mut hasher = Sha3_256::new();
402 hasher.update(self.contract_name.0.clone());
403 hasher.update(self.data.0.clone());
404 let hash_bytes = hasher.finalize();
405 BlobHash(hash_bytes.to_vec())
406 }
407}
408
409impl core::fmt::Debug for BlobHash {
410 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
411 write!(f, "BlobHash({})", hex::encode(&self.0))
412 }
413}
414
415impl core::fmt::Display for BlobHash {
416 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
417 write!(f, "{}", hex::encode(&self.0))
418 }
419}
420
421#[derive(Debug, BorshSerialize, BorshDeserialize)]
422pub struct StructuredBlob<Action> {
423 pub contract_name: ContractName,
424 pub data: StructuredBlobData<Action>,
425}
426
427impl<Action: BorshSerialize> From<StructuredBlob<Action>> for Blob {
428 fn from(val: StructuredBlob<Action>) -> Self {
429 Blob {
430 contract_name: val.contract_name,
431 data: BlobData::from(val.data),
432 }
433 }
434}
435
436impl<Action: BorshDeserialize> TryFrom<Blob> for StructuredBlob<Action> {
437 type Error = borsh::io::Error;
438
439 fn try_from(val: Blob) -> Result<StructuredBlob<Action>, Self::Error> {
440 let data = borsh::from_slice(&val.data.0)?;
441 Ok(StructuredBlob {
442 contract_name: val.contract_name,
443 data,
444 })
445 }
446}
447
448pub trait ContractAction: Send {
449 fn as_blob(
450 &self,
451 contract_name: ContractName,
452 caller: Option<BlobIndex>,
453 callees: Option<Vec<BlobIndex>>,
454 ) -> Blob;
455}
456
457#[derive(
458 Default,
459 Debug,
460 Clone,
461 Serialize,
462 Deserialize,
463 Eq,
464 PartialEq,
465 Hash,
466 BorshSerialize,
467 BorshDeserialize,
468 Ord,
469 PartialOrd,
470)]
471#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
472pub struct ContractName(pub String);
473
474impl ContractName {
475 pub fn validate(&self) -> Result<(), String> {
476 if self.0.is_empty()
477 || !self.0.chars().all(|c| {
478 c.is_ascii_lowercase()
479 || c.is_ascii_digit()
480 || c == '-'
481 || c == '_'
482 || c == '/'
483 || c == '.'
484 })
485 {
486 return Err("ContractName must be a non-empty string containing only lowercase letters, digits, hyphens, underscores, slashes or dots.".to_string());
487 }
488 Ok(())
489 }
490}
491
492#[derive(
493 Default,
494 Debug,
495 Clone,
496 Serialize,
497 Deserialize,
498 Eq,
499 PartialEq,
500 Hash,
501 BorshSerialize,
502 BorshDeserialize,
503)]
504#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
505pub struct Verifier(pub String);
506
507#[derive(
508 Default,
509 Debug,
510 Clone,
511 Serialize,
512 Deserialize,
513 Eq,
514 PartialEq,
515 Hash,
516 BorshSerialize,
517 BorshDeserialize,
518)]
519#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
520pub struct ProgramId(pub Vec<u8>);
521
522impl core::fmt::Display for ProgramId {
523 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
524 write!(f, "{}", hex::encode(&self.0))
525 }
526}
527
528#[derive(Debug, Default, PartialEq, Eq, Clone, BorshSerialize, BorshDeserialize)]
529#[cfg_attr(feature = "full", derive(Serialize, utoipa::ToSchema))]
530pub struct ProofData(#[cfg_attr(feature = "full", serde(with = "base64_field"))] pub Vec<u8>);
531
532#[derive(Debug, Default, PartialEq, Eq, Clone, BorshSerialize, BorshDeserialize)]
533#[cfg_attr(feature = "full", derive(Serialize, utoipa::ToSchema))]
534pub struct ProofMetadata {
535 pub cycles: Option<u64>,
536 pub prover: Option<String>,
537 pub id: Option<String>,
539}
540
541#[derive(Debug, Default, PartialEq, Eq, Clone, BorshSerialize, BorshDeserialize)]
542#[cfg_attr(feature = "full", derive(Serialize, utoipa::ToSchema))]
543pub struct Proof {
544 pub data: ProofData,
545 pub metadata: ProofMetadata,
546}
547
548#[cfg(feature = "full")]
549impl<'de> Deserialize<'de> for ProofData {
550 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
551 where
552 D: serde::Deserializer<'de>,
553 {
554 struct ProofDataVisitor;
555
556 impl<'de> serde::de::Visitor<'de> for ProofDataVisitor {
557 type Value = ProofData;
558
559 fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
560 formatter.write_str("a Base64 string or a Vec<u8>")
561 }
562
563 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
564 where
565 E: serde::de::Error,
566 {
567 use base64::prelude::*;
568 let decoded = BASE64_STANDARD
569 .decode(value)
570 .map_err(serde::de::Error::custom)?;
571 Ok(ProofData(decoded))
572 }
573
574 fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
575 where
576 A: serde::de::SeqAccess<'de>,
577 {
578 let vec_u8: Vec<u8> = serde::de::Deserialize::deserialize(
579 serde::de::value::SeqAccessDeserializer::new(seq),
580 )?;
581 Ok(ProofData(vec_u8))
582 }
583 }
584
585 deserializer.deserialize_any(ProofDataVisitor)
586 }
587}
588
589#[derive(
590 Default, Serialize, Deserialize, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize,
591)]
592pub struct ProofDataHash(#[serde(with = "crate::utils::hex_bytes")] pub Vec<u8>);
593
594impl From<Vec<u8>> for ProofDataHash {
595 fn from(v: Vec<u8>) -> Self {
596 ProofDataHash(v)
597 }
598}
599impl From<&[u8]> for ProofDataHash {
600 fn from(v: &[u8]) -> Self {
601 ProofDataHash(v.to_vec())
602 }
603}
604impl<const N: usize> From<&[u8; N]> for ProofDataHash {
605 fn from(v: &[u8; N]) -> Self {
606 ProofDataHash(v.to_vec())
607 }
608}
609impl ProofDataHash {
610 pub fn from_hex(s: &str) -> Result<Self, hex::FromHexError> {
611 crate::utils::decode_hex_string_checked(s).map(ProofDataHash)
612 }
613}
614
615impl core::fmt::Debug for ProofDataHash {
616 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
617 write!(f, "ProofDataHash({})", hex::encode(&self.0))
618 }
619}
620
621impl Display for ProofDataHash {
622 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
623 write!(f, "{}", hex::encode(&self.0))
624 }
625}
626
627#[cfg(feature = "full")]
628impl Hashed<ProofDataHash> for ProofData {
629 fn hashed(&self) -> ProofDataHash {
630 use sha3::Digest;
631 let mut hasher = sha3::Sha3_256::new();
632 hasher.update(self.0.as_slice());
633 let hash_bytes = hasher.finalize();
634 ProofDataHash(hash_bytes.to_vec())
635 }
636}
637
638#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
639#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
640pub enum OnchainEffect {
643 RegisterContractWithConstructor(RegisterContractEffect),
645 RegisterContract(RegisterContractEffect),
647 DeleteContract(ContractName),
648 UpdateContractProgramId(ContractName, ProgramId),
649 UpdateTimeoutWindow(ContractName, TimeoutWindow),
650}
651
652pub struct OnchainEffectHash(pub Vec<u8>);
653
654#[cfg(feature = "full")]
655impl Hashed<OnchainEffectHash> for OnchainEffect {
656 fn hashed(&self) -> OnchainEffectHash {
657 use sha3::{Digest, Sha3_256};
658 let mut hasher = Sha3_256::new();
659 match self {
660 OnchainEffect::RegisterContractWithConstructor(c) => hasher.update(&c.hashed().0),
661 OnchainEffect::RegisterContract(c) => hasher.update(&c.hashed().0),
662 OnchainEffect::DeleteContract(cn) => hasher.update(cn.0.as_bytes()),
663 OnchainEffect::UpdateContractProgramId(cn, pid) => {
664 hasher.update(cn.0.as_bytes());
665 hasher.update(pid.0.clone());
666 }
667 OnchainEffect::UpdateTimeoutWindow(cn, timeout_window) => {
668 hasher.update(cn.0.as_bytes());
669 match timeout_window {
670 TimeoutWindow::NoTimeout => hasher.update(0u8.to_le_bytes()),
671 TimeoutWindow::Timeout {
672 hard_timeout,
673 soft_timeout,
674 } => {
675 hasher.update(hard_timeout.0.to_le_bytes());
676 hasher.update(soft_timeout.0.to_le_bytes());
677 }
678 }
679 }
680 };
681 OnchainEffectHash(hasher.finalize().to_vec())
682 }
683}
684
685#[derive(
688 Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize,
689)]
690#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
691pub struct HyliOutput {
692 pub version: u32,
694 pub initial_state: StateCommitment,
696 pub next_state: StateCommitment,
698 pub identity: Identity,
701
702 pub index: BlobIndex,
704 pub blobs: IndexedBlobs,
709 pub tx_blob_count: usize,
712
713 pub tx_hash: TxHash, pub success: bool,
719
720 pub state_reads: Vec<(ContractName, StateCommitment)>,
723
724 pub tx_ctx: Option<TxContext>,
726
727 pub onchain_effects: Vec<OnchainEffect>,
728
729 pub program_outputs: Vec<u8>,
733}
734
735#[derive(
736 Default,
737 Serialize,
738 Deserialize,
739 Debug,
740 Clone,
741 PartialEq,
742 Eq,
743 Hash,
744 BorshSerialize,
745 BorshDeserialize,
746)]
747#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
748pub struct TxContext {
749 pub lane_id: LaneId,
750 pub block_hash: BlockHash,
751 pub block_height: BlockHeight,
752 pub timestamp: TimestampMs,
753 #[serde(
754 serialize_with = "serialize_chain_id",
755 deserialize_with = "deserialize_chain_id"
756 )]
757 pub chain_id: u128,
758}
759
760fn serialize_chain_id<S>(chain_id: &u128, serializer: S) -> Result<S::Ok, S::Error>
761where
762 S: serde::Serializer,
763{
764 serializer.serialize_str(&chain_id.to_string())
765}
766
767fn deserialize_chain_id<'de, D>(deserializer: D) -> Result<u128, D::Error>
768where
769 D: serde::Deserializer<'de>,
770{
771 let s: String = serde::Deserialize::deserialize(deserializer)?;
772 s.parse::<u128>().map_err(serde::de::Error::custom)
773}
774
775impl Identity {
776 pub fn new<S: Into<Self>>(s: S) -> Self {
777 s.into()
778 }
779}
780impl<S: Into<String>> From<S> for Identity {
781 fn from(s: S) -> Self {
782 Identity(s.into())
783 }
784}
785
786impl ConsensusProposalHash {
787 pub fn new<S: Into<Self>>(s: S) -> Self {
788 s.into()
789 }
790}
791impl From<Vec<u8>> for ConsensusProposalHash {
792 fn from(v: Vec<u8>) -> Self {
793 ConsensusProposalHash(v)
794 }
795}
796impl From<&[u8]> for ConsensusProposalHash {
797 fn from(v: &[u8]) -> Self {
798 ConsensusProposalHash(v.to_vec())
799 }
800}
801impl<const N: usize> From<&[u8; N]> for ConsensusProposalHash {
802 fn from(v: &[u8; N]) -> Self {
803 ConsensusProposalHash(v.to_vec())
804 }
805}
806impl ConsensusProposalHash {
807 pub fn from_hex(s: &str) -> Result<Self, hex::FromHexError> {
808 crate::utils::decode_hex_string_checked(s).map(ConsensusProposalHash)
809 }
810}
811
812impl TxHash {
813 pub fn new<S: Into<Self>>(s: S) -> Self {
814 s.into()
815 }
816}
817impl From<Vec<u8>> for TxHash {
818 fn from(v: Vec<u8>) -> Self {
819 TxHash(v)
820 }
821}
822impl From<&[u8]> for TxHash {
823 fn from(v: &[u8]) -> Self {
824 TxHash(v.to_vec())
825 }
826}
827impl<const N: usize> From<&[u8; N]> for TxHash {
828 fn from(v: &[u8; N]) -> Self {
829 TxHash(v.to_vec())
830 }
831}
832impl TxHash {
833 pub fn from_hex(s: &str) -> Result<Self, hex::FromHexError> {
834 crate::utils::decode_hex_string_checked(s).map(TxHash)
835 }
836}
837
838impl ContractName {
839 pub fn new<S: Into<Self>>(s: S) -> Self {
840 s.into()
841 }
842}
843impl<S: Into<String>> From<S> for ContractName {
844 fn from(s: S) -> Self {
845 ContractName(s.into())
846 }
847}
848
849impl Verifier {
850 pub fn new<S: Into<Self>>(s: S) -> Self {
851 s.into()
852 }
853}
854impl<S: Into<String>> From<S> for Verifier {
855 fn from(s: S) -> Self {
856 Verifier(s.into())
857 }
858}
859impl From<Vec<u8>> for ProgramId {
860 fn from(v: Vec<u8>) -> Self {
861 ProgramId(v.clone())
862 }
863}
864impl From<&Vec<u8>> for ProgramId {
865 fn from(v: &Vec<u8>) -> Self {
866 ProgramId(v.clone())
867 }
868}
869impl<const N: usize> From<&[u8; N]> for ProgramId {
870 fn from(v: &[u8; N]) -> Self {
871 ProgramId(v.to_vec())
872 }
873}
874impl From<&[u8]> for ProgramId {
875 fn from(v: &[u8]) -> Self {
876 ProgramId(v.to_vec().clone())
877 }
878}
879
880impl Display for TxHash {
881 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
882 write!(f, "{}", hex::encode(&self.0))
883 }
884}
885impl core::fmt::Debug for TxHash {
886 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
887 write!(f, "TxHash({})", hex::encode(&self.0))
888 }
889}
890impl Display for BlobIndex {
891 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
892 write!(f, "{}", &self.0)
893 }
894}
895impl Display for ContractName {
896 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
897 write!(f, "{}", &self.0)
898 }
899}
900impl Display for Identity {
901 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
902 write!(f, "{}", &self.0)
903 }
904}
905impl Display for Verifier {
906 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
907 write!(f, "{}", &self.0)
908 }
909}
910impl From<usize> for BlobIndex {
911 fn from(i: usize) -> Self {
912 BlobIndex(i)
913 }
914}
915
916#[cfg_attr(feature = "full", derive(derive_more::derive::Display))]
917#[derive(
918 Default,
919 Debug,
920 Clone,
921 Serialize,
922 Deserialize,
923 Eq,
924 PartialEq,
925 Hash,
926 Copy,
927 BorshSerialize,
928 BorshDeserialize,
929 PartialOrd,
930 Ord,
931)]
932#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
933pub struct BlockHeight(pub u64);
934
935impl Add<BlockHeight> for u64 {
936 type Output = BlockHeight;
937 fn add(self, other: BlockHeight) -> BlockHeight {
938 BlockHeight(self + other.0)
939 }
940}
941
942impl Add<u64> for BlockHeight {
943 type Output = BlockHeight;
944 fn add(self, other: u64) -> BlockHeight {
945 BlockHeight(self.0 + other)
946 }
947}
948
949impl Sub<u64> for BlockHeight {
950 type Output = BlockHeight;
951 fn sub(self, other: u64) -> BlockHeight {
952 BlockHeight(self.0 - other)
953 }
954}
955
956impl Add<BlockHeight> for BlockHeight {
957 type Output = BlockHeight;
958 fn add(self, other: BlockHeight) -> BlockHeight {
959 BlockHeight(self.0 + other.0)
960 }
961}
962
963#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
964#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
965pub enum TimeoutWindow {
966 NoTimeout,
967 Timeout {
968 hard_timeout: BlockHeight,
971 soft_timeout: BlockHeight,
974 },
975}
976impl Display for TimeoutWindow {
977 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
978 match &self {
979 TimeoutWindow::NoTimeout => write!(f, "NoTimeout"),
980 TimeoutWindow::Timeout {
981 hard_timeout,
982 soft_timeout,
983 } => write!(f, "Timeout({},{})", hard_timeout.0, soft_timeout.0),
984 }
985 }
986}
987
988impl Default for TimeoutWindow {
989 fn default() -> Self {
990 TimeoutWindow::timeout(BlockHeight(100), BlockHeight(100))
991 }
992}
993
994impl TimeoutWindow {
1011 pub fn timeout(hard_timeout: BlockHeight, soft_timeout: BlockHeight) -> Self {
1012 let (hard_timeout, soft_timeout) = if hard_timeout <= soft_timeout {
1013 (hard_timeout, soft_timeout)
1014 } else {
1015 (soft_timeout, hard_timeout)
1016 };
1017 TimeoutWindow::Timeout {
1018 hard_timeout,
1019 soft_timeout,
1020 }
1021 }
1022}
1023
1024#[derive(
1025 Debug, Serialize, Deserialize, Default, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize,
1026)]
1027#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
1028pub struct RegisterContractAction {
1030 pub verifier: Verifier,
1032 pub program_id: ProgramId,
1035 pub state_commitment: StateCommitment,
1037 pub contract_name: ContractName,
1039 pub timeout_window: Option<TimeoutWindow>,
1042 pub constructor_metadata: Option<Vec<u8>>,
1045}
1046
1047#[cfg(feature = "full")]
1048impl Hashed<TxHash> for RegisterContractAction {
1049 fn hashed(&self) -> TxHash {
1050 use sha3::{Digest, Sha3_256};
1051
1052 let mut hasher = Sha3_256::new();
1053 hasher.update(self.verifier.0.clone());
1054 hasher.update(self.program_id.0.clone());
1055 hasher.update(self.state_commitment.0.clone());
1056 hasher.update(self.contract_name.0.clone());
1057 if let Some(timeout_window) = &self.timeout_window {
1058 match timeout_window {
1059 TimeoutWindow::NoTimeout => hasher.update(0u8.to_le_bytes()),
1060 TimeoutWindow::Timeout {
1061 hard_timeout,
1062 soft_timeout,
1063 } => {
1064 hasher.update(hard_timeout.0.to_le_bytes());
1065 hasher.update(soft_timeout.0.to_le_bytes());
1066 }
1067 }
1068 }
1069 let hash_bytes = hasher.finalize();
1071 TxHash(hash_bytes.to_vec())
1072 }
1073}
1074
1075impl RegisterContractAction {
1076 pub fn as_blob(&self, contract_name: ContractName) -> Blob {
1077 <Self as ContractAction>::as_blob(self, contract_name, None, None)
1078 }
1079}
1080
1081impl ContractAction for RegisterContractAction {
1082 fn as_blob(
1083 &self,
1084 contract_name: ContractName,
1085 _caller: Option<BlobIndex>,
1086 _callees: Option<Vec<BlobIndex>>,
1087 ) -> Blob {
1088 Blob {
1089 contract_name,
1090 data: BlobData(borsh::to_vec(self).expect("failed to encode RegisterContractAction")),
1091 }
1092 }
1093}
1094
1095#[derive(
1096 Debug, Serialize, Deserialize, Default, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize,
1097)]
1098pub struct UpdateContractProgramIdAction {
1099 pub contract_name: ContractName,
1100 pub program_id: ProgramId,
1101}
1102
1103impl UpdateContractProgramIdAction {
1104 pub fn as_blob(&self, contract_name: ContractName) -> Blob {
1105 <Self as ContractAction>::as_blob(self, contract_name, None, None)
1106 }
1107}
1108
1109impl ContractAction for UpdateContractProgramIdAction {
1110 fn as_blob(
1111 &self,
1112 contract_name: ContractName,
1113 _caller: Option<BlobIndex>,
1114 _callees: Option<Vec<BlobIndex>>,
1115 ) -> Blob {
1116 Blob {
1117 contract_name,
1118 data: BlobData(
1119 borsh::to_vec(self).expect("failed to encode UpdateContractProgramIdAction"),
1120 ),
1121 }
1122 }
1123}
1124
1125#[derive(
1126 Debug, Serialize, Deserialize, Default, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize,
1127)]
1128pub struct UpdateContractTimeoutWindowAction {
1129 pub contract_name: ContractName,
1130 pub timeout_window: TimeoutWindow,
1131}
1132
1133impl UpdateContractTimeoutWindowAction {
1134 pub fn as_blob(&self, contract_name: ContractName) -> Blob {
1135 <Self as ContractAction>::as_blob(self, contract_name, None, None)
1136 }
1137}
1138
1139impl ContractAction for UpdateContractTimeoutWindowAction {
1140 fn as_blob(
1141 &self,
1142 contract_name: ContractName,
1143 _caller: Option<BlobIndex>,
1144 _callees: Option<Vec<BlobIndex>>,
1145 ) -> Blob {
1146 Blob {
1147 contract_name,
1148 data: BlobData(
1149 borsh::to_vec(self).expect("failed to encode UpdateContractTimeoutWindowAction"),
1150 ),
1151 }
1152 }
1153}
1154
1155#[derive(
1156 Debug, Serialize, Deserialize, Default, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize,
1157)]
1158pub struct DeleteContractAction {
1160 pub contract_name: ContractName,
1161}
1162
1163impl DeleteContractAction {
1164 pub fn as_blob(&self, contract_name: ContractName) -> Blob {
1165 <Self as ContractAction>::as_blob(self, contract_name, None, None)
1166 }
1167}
1168
1169impl ContractAction for DeleteContractAction {
1170 fn as_blob(
1171 &self,
1172 contract_name: ContractName,
1173 _caller: Option<BlobIndex>,
1174 _callees: Option<Vec<BlobIndex>>,
1175 ) -> Blob {
1176 Blob {
1177 contract_name,
1178 data: BlobData(borsh::to_vec(self).expect("failed to encode DeleteContractAction")),
1179 }
1180 }
1181}
1182
1183#[derive(
1187 Debug, Serialize, Deserialize, Default, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize,
1188)]
1189#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
1190pub struct RegisterContractEffect {
1191 pub verifier: Verifier,
1193 pub program_id: ProgramId,
1196 pub state_commitment: StateCommitment,
1198 pub contract_name: ContractName,
1200 pub timeout_window: Option<TimeoutWindow>,
1203}
1204
1205impl From<RegisterContractAction> for RegisterContractEffect {
1206 fn from(action: RegisterContractAction) -> Self {
1207 RegisterContractEffect {
1208 verifier: action.verifier,
1209 program_id: action.program_id,
1210 state_commitment: action.state_commitment,
1211 contract_name: action.contract_name,
1212 timeout_window: action.timeout_window,
1213 }
1214 }
1215}
1216
1217#[cfg(feature = "full")]
1218impl Hashed<TxHash> for RegisterContractEffect {
1219 fn hashed(&self) -> TxHash {
1220 use sha3::{Digest, Sha3_256};
1221
1222 let mut hasher = Sha3_256::new();
1223 hasher.update(self.verifier.0.clone());
1224 hasher.update(self.program_id.0.clone());
1225 hasher.update(self.state_commitment.0.clone());
1226 hasher.update(self.contract_name.0.clone());
1227 if let Some(timeout_window) = &self.timeout_window {
1228 match timeout_window {
1229 TimeoutWindow::NoTimeout => hasher.update(0u8.to_le_bytes()),
1230 TimeoutWindow::Timeout {
1231 hard_timeout,
1232 soft_timeout,
1233 } => {
1234 hasher.update(hard_timeout.0.to_le_bytes());
1235 hasher.update(soft_timeout.0.to_le_bytes());
1236 }
1237 }
1238 }
1239 let hash_bytes = hasher.finalize();
1240 TxHash(hash_bytes.to_vec())
1241 }
1242}
1243
1244#[cfg(feature = "full")]
1245pub mod base64_field {
1246 use base64::prelude::*;
1247 use serde::{Deserialize, Deserializer, Serializer};
1248
1249 pub fn serialize<S>(bytes: &Vec<u8>, serializer: S) -> Result<S::Ok, S::Error>
1250 where
1251 S: Serializer,
1252 {
1253 let encoded = BASE64_STANDARD.encode(bytes);
1254 serializer.serialize_str(&encoded)
1255 }
1256
1257 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
1258 where
1259 D: Deserializer<'de>,
1260 {
1261 let s = String::deserialize(deserializer)?;
1262 BASE64_STANDARD.decode(&s).map_err(serde::de::Error::custom)
1263 }
1264}
1265
1266#[cfg(test)]
1267mod tests {
1268 use super::*;
1269
1270 #[test]
1271 fn consensus_proposal_hash_from_hex_str_roundtrip() {
1272 let hex_str = "74657374";
1273 let hash = ConsensusProposalHash::from_hex(hex_str).expect("consensus hash hex");
1274 assert_eq!(hash.0, b"test".to_vec());
1275 assert_eq!(format!("{hash}"), hex_str);
1276 let json = serde_json::to_string(&hash).expect("serialize consensus hash");
1277 assert_eq!(json, "\"74657374\"");
1278 let decoded: ConsensusProposalHash =
1279 serde_json::from_str(&json).expect("deserialize consensus hash");
1280 assert_eq!(decoded, hash);
1281 }
1282
1283 #[test]
1284 fn txhash_from_hex_str_roundtrip() {
1285 let hex_str = "746573745f7478";
1286 let hash = TxHash::from_hex(hex_str).expect("txhash hex");
1287 assert_eq!(hash.0, b"test_tx".to_vec());
1288 assert_eq!(format!("{hash}"), hex_str);
1289 let json = serde_json::to_string(&hash).expect("serialize tx hash");
1290 assert_eq!(json, "\"746573745f7478\"");
1291 let decoded: TxHash = serde_json::from_str(&json).expect("deserialize tx hash");
1292 assert_eq!(decoded, hash);
1293 }
1294
1295 #[test]
1296 fn proof_data_hash_from_hex_str_roundtrip() {
1297 let hex_str = "0x74657374";
1298 let hash = ProofDataHash::from_hex(hex_str).expect("proof hash hex");
1299 assert_eq!(hash.0, b"test".to_vec());
1300 assert_eq!(hex::encode(&hash.0), "74657374");
1301 let json = serde_json::to_string(&hash).expect("serialize proof hash");
1302 assert_eq!(json, "\"74657374\"");
1303 let decoded: ProofDataHash = serde_json::from_str(&json).expect("deserialize proof hash");
1304 assert_eq!(decoded, hash);
1305 }
1306}