use std::{
fmt::Display,
ops::{Add, Deref, DerefMut, Sub},
};
use borsh::{BorshDeserialize, BorshSerialize};
use serde::{Deserialize, Serialize};
use crate::{utils::TimestampMs, LaneId};
#[derive(
Serialize,
Deserialize,
Clone,
BorshSerialize,
BorshDeserialize,
PartialEq,
Eq,
Default,
Ord,
PartialOrd,
)]
#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
pub struct ConsensusProposalHash(#[serde(with = "crate::utils::hex_bytes")] pub Vec<u8>);
pub type BlockHash = ConsensusProposalHash;
impl std::hash::Hash for ConsensusProposalHash {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
state.write(&self.0);
}
}
impl std::fmt::Debug for ConsensusProposalHash {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ConsensusProposalHash({})", hex::encode(&self.0))
}
}
pub trait Hashed<T> {
fn hashed(&self) -> T;
}
pub trait DataSized {
fn estimate_size(&self) -> usize;
}
#[derive(
Default,
Serialize,
Deserialize,
Debug,
Clone,
PartialEq,
Eq,
Hash,
BorshSerialize,
BorshDeserialize,
)]
#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
pub struct IndexedBlobs(pub Vec<(BlobIndex, Blob)>);
impl Deref for IndexedBlobs {
type Target = Vec<(BlobIndex, Blob)>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for IndexedBlobs {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl IndexedBlobs {
pub fn get(&self, index: &BlobIndex) -> Option<&Blob> {
self.0.iter().find(|(i, _)| i == index).map(|(_, b)| b)
}
}
impl<T> From<T> for IndexedBlobs
where
T: IntoIterator<Item = Blob>,
{
fn from(vec: T) -> Self {
let mut blobs = IndexedBlobs::default();
for (i, blob) in vec.into_iter().enumerate() {
blobs.push((BlobIndex(i), blob));
}
blobs
}
}
impl<'a> IntoIterator for &'a IndexedBlobs {
type Item = &'a (BlobIndex, Blob);
type IntoIter = std::slice::Iter<'a, (BlobIndex, Blob)>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
#[derive(Default, Serialize, Deserialize, BorshSerialize, BorshDeserialize, Debug, Clone)]
pub struct Calldata {
pub tx_hash: TxHash,
pub identity: Identity,
pub blobs: IndexedBlobs,
pub tx_blob_count: usize,
pub index: BlobIndex,
pub tx_ctx: Option<TxContext>,
pub private_input: Vec<u8>,
}
#[derive(
Default, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, BorshSerialize, BorshDeserialize,
)]
#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
pub struct StateCommitment(pub Vec<u8>);
impl std::fmt::Debug for StateCommitment {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "StateCommitment({})", hex::encode(&self.0))
}
}
#[derive(
Default,
Serialize,
Deserialize,
Debug,
Clone,
PartialEq,
Eq,
Hash,
BorshSerialize,
BorshDeserialize,
Ord,
PartialOrd,
)]
#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
pub struct Identity(pub String);
#[derive(
Default,
Serialize,
Deserialize,
Clone,
PartialEq,
Eq,
Hash,
BorshSerialize,
BorshDeserialize,
Ord,
PartialOrd,
)]
#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
pub struct TxHash(#[serde(with = "crate::utils::hex_bytes")] pub Vec<u8>);
#[derive(
Default,
Serialize,
Deserialize,
Debug,
Clone,
PartialEq,
Eq,
Hash,
BorshDeserialize,
BorshSerialize,
Copy,
PartialOrd,
Ord,
)]
#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
pub struct BlobIndex(pub usize);
impl Add<usize> for BlobIndex {
type Output = BlobIndex;
fn add(self, other: usize) -> BlobIndex {
BlobIndex(self.0 + other)
}
}
#[derive(
Default, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, BorshSerialize, BorshDeserialize,
)]
#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
pub struct BlobData(pub Vec<u8>);
impl std::fmt::Debug for BlobData {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if self.0.len() > 20 {
write!(f, "BlobData({}...)", hex::encode(&self.0[..20]))
} else {
write!(f, "BlobData({})", hex::encode(&self.0))
}
}
}
#[derive(Debug, BorshSerialize)]
pub struct StructuredBlobData<Action> {
pub caller: Option<BlobIndex>,
pub callees: Option<Vec<BlobIndex>>,
pub parameters: Action,
}
pub struct DropEndOfReader;
impl<Action: BorshDeserialize> BorshDeserialize for StructuredBlobData<Action> {
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
let caller = Option::<BlobIndex>::deserialize_reader(reader)?;
let callees = Option::<Vec<BlobIndex>>::deserialize_reader(reader)?;
let parameters = Action::deserialize_reader(reader)?;
Ok(StructuredBlobData {
caller,
callees,
parameters,
})
}
}
impl BorshDeserialize for StructuredBlobData<DropEndOfReader> {
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
let caller = Option::<BlobIndex>::deserialize_reader(reader)?;
let callees = Option::<Vec<BlobIndex>>::deserialize_reader(reader)?;
reader.read_to_end(&mut vec![])?;
let parameters = DropEndOfReader;
Ok(StructuredBlobData {
caller,
callees,
parameters,
})
}
}
impl<Action: BorshSerialize> From<StructuredBlobData<Action>> for BlobData {
fn from(val: StructuredBlobData<Action>) -> Self {
BlobData(borsh::to_vec(&val).expect("failed to encode BlobData"))
}
}
impl<Action: BorshDeserialize> TryFrom<BlobData> for StructuredBlobData<Action> {
type Error = std::io::Error;
fn try_from(val: BlobData) -> Result<StructuredBlobData<Action>, Self::Error> {
borsh::from_slice(&val.0)
}
}
impl TryFrom<BlobData> for StructuredBlobData<DropEndOfReader> {
type Error = std::io::Error;
fn try_from(val: BlobData) -> Result<StructuredBlobData<DropEndOfReader>, Self::Error> {
borsh::from_slice(&val.0)
}
}
#[derive(
Debug,
Serialize,
Deserialize,
Default,
Clone,
PartialEq,
Eq,
BorshSerialize,
BorshDeserialize,
Hash,
)]
#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
pub struct Blob {
pub contract_name: ContractName,
pub data: BlobData,
}
#[derive(
Default, Clone, Serialize, Deserialize, Eq, PartialEq, Hash, BorshSerialize, BorshDeserialize,
)]
#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
pub struct BlobHash(#[serde(with = "crate::utils::hex_bytes")] pub Vec<u8>);
#[cfg(feature = "full")]
impl Hashed<BlobHash> for Blob {
fn hashed(&self) -> BlobHash {
use sha3::{Digest, Sha3_256};
let mut hasher = Sha3_256::new();
hasher.update(self.contract_name.0.clone());
hasher.update(self.data.0.clone());
let hash_bytes = hasher.finalize();
BlobHash(hash_bytes.to_vec())
}
}
impl std::fmt::Debug for BlobHash {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "BlobHash({})", hex::encode(&self.0))
}
}
impl std::fmt::Display for BlobHash {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", hex::encode(&self.0))
}
}
#[derive(Debug, BorshSerialize, BorshDeserialize)]
pub struct StructuredBlob<Action> {
pub contract_name: ContractName,
pub data: StructuredBlobData<Action>,
}
impl<Action: BorshSerialize> From<StructuredBlob<Action>> for Blob {
fn from(val: StructuredBlob<Action>) -> Self {
Blob {
contract_name: val.contract_name,
data: BlobData::from(val.data),
}
}
}
impl<Action: BorshDeserialize> TryFrom<Blob> for StructuredBlob<Action> {
type Error = std::io::Error;
fn try_from(val: Blob) -> Result<StructuredBlob<Action>, Self::Error> {
let data = borsh::from_slice(&val.data.0)?;
Ok(StructuredBlob {
contract_name: val.contract_name,
data,
})
}
}
pub trait ContractAction: Send {
fn as_blob(
&self,
contract_name: ContractName,
caller: Option<BlobIndex>,
callees: Option<Vec<BlobIndex>>,
) -> Blob;
}
#[derive(
Default,
Debug,
Clone,
Serialize,
Deserialize,
Eq,
PartialEq,
Hash,
BorshSerialize,
BorshDeserialize,
Ord,
PartialOrd,
)]
#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
pub struct ContractName(pub String);
#[derive(
Default,
Debug,
Clone,
Serialize,
Deserialize,
Eq,
PartialEq,
Hash,
BorshSerialize,
BorshDeserialize,
)]
#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
pub struct Verifier(pub String);
#[derive(
Default,
Debug,
Clone,
Serialize,
Deserialize,
Eq,
PartialEq,
Hash,
BorshSerialize,
BorshDeserialize,
)]
#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
pub struct ProgramId(pub Vec<u8>);
impl std::fmt::Display for ProgramId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", hex::encode(&self.0))
}
}
#[derive(Debug, Default, PartialEq, Eq, Clone, BorshSerialize, BorshDeserialize)]
#[cfg_attr(feature = "full", derive(Serialize, utoipa::ToSchema))]
pub struct ProofData(#[cfg_attr(feature = "full", serde(with = "base64_field"))] pub Vec<u8>);
#[derive(Debug, Default, PartialEq, Eq, Clone, BorshSerialize, BorshDeserialize)]
#[cfg_attr(feature = "full", derive(Serialize, utoipa::ToSchema))]
pub struct ProofMetadata {
pub cycles: Option<u64>,
pub prover: Option<String>,
pub id: Option<String>,
}
#[derive(Debug, Default, PartialEq, Eq, Clone, BorshSerialize, BorshDeserialize)]
#[cfg_attr(feature = "full", derive(Serialize, utoipa::ToSchema))]
pub struct Proof {
pub data: ProofData,
pub metadata: ProofMetadata,
}
#[cfg(feature = "full")]
impl<'de> Deserialize<'de> for ProofData {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct ProofDataVisitor;
impl<'de> serde::de::Visitor<'de> for ProofDataVisitor {
type Value = ProofData;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a Base64 string or a Vec<u8>")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
use base64::prelude::*;
let decoded = BASE64_STANDARD
.decode(value)
.map_err(serde::de::Error::custom)?;
Ok(ProofData(decoded))
}
fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let vec_u8: Vec<u8> = serde::de::Deserialize::deserialize(
serde::de::value::SeqAccessDeserializer::new(seq),
)?;
Ok(ProofData(vec_u8))
}
}
deserializer.deserialize_any(ProofDataVisitor)
}
}
#[derive(
Default, Serialize, Deserialize, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize,
)]
pub struct ProofDataHash(#[serde(with = "crate::utils::hex_bytes")] pub Vec<u8>);
impl From<Vec<u8>> for ProofDataHash {
fn from(v: Vec<u8>) -> Self {
ProofDataHash(v)
}
}
impl From<&[u8]> for ProofDataHash {
fn from(v: &[u8]) -> Self {
ProofDataHash(v.to_vec())
}
}
impl<const N: usize> From<&[u8; N]> for ProofDataHash {
fn from(v: &[u8; N]) -> Self {
ProofDataHash(v.to_vec())
}
}
impl ProofDataHash {
pub fn from_hex(s: &str) -> Result<Self, hex::FromHexError> {
crate::utils::decode_hex_string_checked(s).map(ProofDataHash)
}
}
impl std::fmt::Debug for ProofDataHash {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ProofDataHash({})", hex::encode(&self.0))
}
}
impl Display for ProofDataHash {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", hex::encode(&self.0))
}
}
#[cfg(feature = "full")]
impl Hashed<ProofDataHash> for ProofData {
fn hashed(&self) -> ProofDataHash {
use sha3::Digest;
let mut hasher = sha3::Sha3_256::new();
hasher.update(self.0.as_slice());
let hash_bytes = hasher.finalize();
ProofDataHash(hash_bytes.to_vec())
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
pub enum OnchainEffect {
RegisterContractWithConstructor(RegisterContractEffect),
RegisterContract(RegisterContractEffect),
DeleteContract(ContractName),
UpdateContractProgramId(ContractName, ProgramId),
UpdateTimeoutWindow(ContractName, TimeoutWindow),
}
pub struct OnchainEffectHash(pub Vec<u8>);
#[cfg(feature = "full")]
impl Hashed<OnchainEffectHash> for OnchainEffect {
fn hashed(&self) -> OnchainEffectHash {
use sha3::{Digest, Sha3_256};
let mut hasher = Sha3_256::new();
match self {
OnchainEffect::RegisterContractWithConstructor(c) => hasher.update(&c.hashed().0),
OnchainEffect::RegisterContract(c) => hasher.update(&c.hashed().0),
OnchainEffect::DeleteContract(cn) => hasher.update(cn.0.as_bytes()),
OnchainEffect::UpdateContractProgramId(cn, pid) => {
hasher.update(cn.0.as_bytes());
hasher.update(pid.0.clone());
}
OnchainEffect::UpdateTimeoutWindow(cn, timeout_window) => {
hasher.update(cn.0.as_bytes());
match timeout_window {
TimeoutWindow::NoTimeout => hasher.update(0u8.to_le_bytes()),
TimeoutWindow::Timeout {
hard_timeout,
soft_timeout,
} => {
hasher.update(hard_timeout.0.to_le_bytes());
hasher.update(soft_timeout.0.to_le_bytes());
}
}
}
};
OnchainEffectHash(hasher.finalize().to_vec())
}
}
#[derive(
Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize,
)]
#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
pub struct HyliOutput {
pub version: u32,
pub initial_state: StateCommitment,
pub next_state: StateCommitment,
pub identity: Identity,
pub index: BlobIndex,
pub blobs: IndexedBlobs,
pub tx_blob_count: usize,
pub tx_hash: TxHash,
pub success: bool,
pub state_reads: Vec<(ContractName, StateCommitment)>,
pub tx_ctx: Option<TxContext>,
pub onchain_effects: Vec<OnchainEffect>,
pub program_outputs: Vec<u8>,
}
#[derive(
Default,
Serialize,
Deserialize,
Debug,
Clone,
PartialEq,
Eq,
Hash,
BorshSerialize,
BorshDeserialize,
)]
#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
pub struct TxContext {
pub lane_id: LaneId,
pub block_hash: BlockHash,
pub block_height: BlockHeight,
pub timestamp: TimestampMs,
#[serde(
serialize_with = "serialize_chain_id",
deserialize_with = "deserialize_chain_id"
)]
pub chain_id: u128,
}
fn serialize_chain_id<S>(chain_id: &u128, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&chain_id.to_string())
}
fn deserialize_chain_id<'de, D>(deserializer: D) -> Result<u128, D::Error>
where
D: serde::Deserializer<'de>,
{
let s: String = serde::Deserialize::deserialize(deserializer)?;
s.parse::<u128>().map_err(serde::de::Error::custom)
}
impl Identity {
pub fn new<S: Into<Self>>(s: S) -> Self {
s.into()
}
}
impl<S: Into<String>> From<S> for Identity {
fn from(s: S) -> Self {
Identity(s.into())
}
}
impl ConsensusProposalHash {
pub fn new<S: Into<Self>>(s: S) -> Self {
s.into()
}
}
impl From<Vec<u8>> for ConsensusProposalHash {
fn from(v: Vec<u8>) -> Self {
ConsensusProposalHash(v)
}
}
impl From<&[u8]> for ConsensusProposalHash {
fn from(v: &[u8]) -> Self {
ConsensusProposalHash(v.to_vec())
}
}
impl<const N: usize> From<&[u8; N]> for ConsensusProposalHash {
fn from(v: &[u8; N]) -> Self {
ConsensusProposalHash(v.to_vec())
}
}
impl ConsensusProposalHash {
pub fn from_hex(s: &str) -> Result<Self, hex::FromHexError> {
crate::utils::decode_hex_string_checked(s).map(ConsensusProposalHash)
}
}
impl TxHash {
pub fn new<S: Into<Self>>(s: S) -> Self {
s.into()
}
}
impl From<Vec<u8>> for TxHash {
fn from(v: Vec<u8>) -> Self {
TxHash(v)
}
}
impl From<&[u8]> for TxHash {
fn from(v: &[u8]) -> Self {
TxHash(v.to_vec())
}
}
impl<const N: usize> From<&[u8; N]> for TxHash {
fn from(v: &[u8; N]) -> Self {
TxHash(v.to_vec())
}
}
impl TxHash {
pub fn from_hex(s: &str) -> Result<Self, hex::FromHexError> {
crate::utils::decode_hex_string_checked(s).map(TxHash)
}
}
impl ContractName {
pub fn new<S: Into<Self>>(s: S) -> Self {
s.into()
}
}
impl<S: Into<String>> From<S> for ContractName {
fn from(s: S) -> Self {
ContractName(s.into())
}
}
impl Verifier {
pub fn new<S: Into<Self>>(s: S) -> Self {
s.into()
}
}
impl<S: Into<String>> From<S> for Verifier {
fn from(s: S) -> Self {
Verifier(s.into())
}
}
impl From<Vec<u8>> for ProgramId {
fn from(v: Vec<u8>) -> Self {
ProgramId(v.clone())
}
}
impl From<&Vec<u8>> for ProgramId {
fn from(v: &Vec<u8>) -> Self {
ProgramId(v.clone())
}
}
impl<const N: usize> From<&[u8; N]> for ProgramId {
fn from(v: &[u8; N]) -> Self {
ProgramId(v.to_vec())
}
}
impl From<&[u8]> for ProgramId {
fn from(v: &[u8]) -> Self {
ProgramId(v.to_vec().clone())
}
}
impl Display for TxHash {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", hex::encode(&self.0))
}
}
impl std::fmt::Debug for TxHash {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "TxHash({})", hex::encode(&self.0))
}
}
impl Display for BlobIndex {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &self.0)
}
}
impl Display for ContractName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &self.0)
}
}
impl Display for Identity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &self.0)
}
}
impl Display for Verifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &self.0)
}
}
impl From<usize> for BlobIndex {
fn from(i: usize) -> Self {
BlobIndex(i)
}
}
#[cfg_attr(feature = "full", derive(derive_more::derive::Display))]
#[derive(
Default,
Debug,
Clone,
Serialize,
Deserialize,
Eq,
PartialEq,
Hash,
Copy,
BorshSerialize,
BorshDeserialize,
PartialOrd,
Ord,
)]
#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
pub struct BlockHeight(pub u64);
impl Add<BlockHeight> for u64 {
type Output = BlockHeight;
fn add(self, other: BlockHeight) -> BlockHeight {
BlockHeight(self + other.0)
}
}
impl Add<u64> for BlockHeight {
type Output = BlockHeight;
fn add(self, other: u64) -> BlockHeight {
BlockHeight(self.0 + other)
}
}
impl Sub<u64> for BlockHeight {
type Output = BlockHeight;
fn sub(self, other: u64) -> BlockHeight {
BlockHeight(self.0 - other)
}
}
impl Add<BlockHeight> for BlockHeight {
type Output = BlockHeight;
fn add(self, other: BlockHeight) -> BlockHeight {
BlockHeight(self.0 + other.0)
}
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
pub enum TimeoutWindow {
NoTimeout,
Timeout {
hard_timeout: BlockHeight,
soft_timeout: BlockHeight,
},
}
impl Display for TimeoutWindow {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self {
TimeoutWindow::NoTimeout => write!(f, "NoTimeout"),
TimeoutWindow::Timeout {
hard_timeout,
soft_timeout,
} => write!(f, "Timeout({},{})", hard_timeout.0, soft_timeout.0),
}
}
}
impl Default for TimeoutWindow {
fn default() -> Self {
TimeoutWindow::timeout(BlockHeight(100), BlockHeight(100))
}
}
impl TimeoutWindow {
pub fn timeout(hard_timeout: BlockHeight, soft_timeout: BlockHeight) -> Self {
let (hard_timeout, soft_timeout) = if hard_timeout <= soft_timeout {
(hard_timeout, soft_timeout)
} else {
(soft_timeout, hard_timeout)
};
TimeoutWindow::Timeout {
hard_timeout,
soft_timeout,
}
}
}
#[derive(
Debug, Serialize, Deserialize, Default, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize,
)]
#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
pub struct RegisterContractAction {
pub verifier: Verifier,
pub program_id: ProgramId,
pub state_commitment: StateCommitment,
pub contract_name: ContractName,
pub timeout_window: Option<TimeoutWindow>,
pub constructor_metadata: Option<Vec<u8>>,
}
#[cfg(feature = "full")]
impl Hashed<TxHash> for RegisterContractAction {
fn hashed(&self) -> TxHash {
use sha3::{Digest, Sha3_256};
let mut hasher = Sha3_256::new();
hasher.update(self.verifier.0.clone());
hasher.update(self.program_id.0.clone());
hasher.update(self.state_commitment.0.clone());
hasher.update(self.contract_name.0.clone());
if let Some(timeout_window) = &self.timeout_window {
match timeout_window {
TimeoutWindow::NoTimeout => hasher.update(0u8.to_le_bytes()),
TimeoutWindow::Timeout {
hard_timeout,
soft_timeout,
} => {
hasher.update(hard_timeout.0.to_le_bytes());
hasher.update(soft_timeout.0.to_le_bytes());
}
}
}
let hash_bytes = hasher.finalize();
TxHash(hash_bytes.to_vec())
}
}
impl RegisterContractAction {
pub fn as_blob(&self, contract_name: ContractName) -> Blob {
<Self as ContractAction>::as_blob(self, contract_name, None, None)
}
}
impl ContractAction for RegisterContractAction {
fn as_blob(
&self,
contract_name: ContractName,
_caller: Option<BlobIndex>,
_callees: Option<Vec<BlobIndex>>,
) -> Blob {
Blob {
contract_name,
data: BlobData(borsh::to_vec(self).expect("failed to encode RegisterContractAction")),
}
}
}
#[derive(
Debug, Serialize, Deserialize, Default, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize,
)]
pub struct UpdateContractProgramIdAction {
pub contract_name: ContractName,
pub program_id: ProgramId,
}
impl UpdateContractProgramIdAction {
pub fn as_blob(&self, contract_name: ContractName) -> Blob {
<Self as ContractAction>::as_blob(self, contract_name, None, None)
}
}
impl ContractAction for UpdateContractProgramIdAction {
fn as_blob(
&self,
contract_name: ContractName,
_caller: Option<BlobIndex>,
_callees: Option<Vec<BlobIndex>>,
) -> Blob {
Blob {
contract_name,
data: BlobData(
borsh::to_vec(self).expect("failed to encode UpdateContractProgramIdAction"),
),
}
}
}
#[derive(
Debug, Serialize, Deserialize, Default, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize,
)]
pub struct UpdateContractTimeoutWindowAction {
pub contract_name: ContractName,
pub timeout_window: TimeoutWindow,
}
impl UpdateContractTimeoutWindowAction {
pub fn as_blob(&self, contract_name: ContractName) -> Blob {
<Self as ContractAction>::as_blob(self, contract_name, None, None)
}
}
impl ContractAction for UpdateContractTimeoutWindowAction {
fn as_blob(
&self,
contract_name: ContractName,
_caller: Option<BlobIndex>,
_callees: Option<Vec<BlobIndex>>,
) -> Blob {
Blob {
contract_name,
data: BlobData(
borsh::to_vec(self).expect("failed to encode UpdateContractTimeoutWindowAction"),
),
}
}
}
#[derive(
Debug, Serialize, Deserialize, Default, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize,
)]
pub struct DeleteContractAction {
pub contract_name: ContractName,
}
impl DeleteContractAction {
pub fn as_blob(&self, contract_name: ContractName) -> Blob {
<Self as ContractAction>::as_blob(self, contract_name, None, None)
}
}
impl ContractAction for DeleteContractAction {
fn as_blob(
&self,
contract_name: ContractName,
_caller: Option<BlobIndex>,
_callees: Option<Vec<BlobIndex>>,
) -> Blob {
Blob {
contract_name,
data: BlobData(borsh::to_vec(self).expect("failed to encode DeleteContractAction")),
}
}
}
#[derive(
Debug, Serialize, Deserialize, Default, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize,
)]
#[cfg_attr(feature = "full", derive(utoipa::ToSchema))]
pub struct RegisterContractEffect {
pub verifier: Verifier,
pub program_id: ProgramId,
pub state_commitment: StateCommitment,
pub contract_name: ContractName,
pub timeout_window: Option<TimeoutWindow>,
}
impl From<RegisterContractAction> for RegisterContractEffect {
fn from(action: RegisterContractAction) -> Self {
RegisterContractEffect {
verifier: action.verifier,
program_id: action.program_id,
state_commitment: action.state_commitment,
contract_name: action.contract_name,
timeout_window: action.timeout_window,
}
}
}
#[cfg(feature = "full")]
impl Hashed<TxHash> for RegisterContractEffect {
fn hashed(&self) -> TxHash {
use sha3::{Digest, Sha3_256};
let mut hasher = Sha3_256::new();
hasher.update(self.verifier.0.clone());
hasher.update(self.program_id.0.clone());
hasher.update(self.state_commitment.0.clone());
hasher.update(self.contract_name.0.clone());
if let Some(timeout_window) = &self.timeout_window {
match timeout_window {
TimeoutWindow::NoTimeout => hasher.update(0u8.to_le_bytes()),
TimeoutWindow::Timeout {
hard_timeout,
soft_timeout,
} => {
hasher.update(hard_timeout.0.to_le_bytes());
hasher.update(soft_timeout.0.to_le_bytes());
}
}
}
let hash_bytes = hasher.finalize();
TxHash(hash_bytes.to_vec())
}
}
#[cfg(feature = "full")]
pub mod base64_field {
use base64::prelude::*;
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S>(bytes: &Vec<u8>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let encoded = BASE64_STANDARD.encode(bytes);
serializer.serialize_str(&encoded)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
BASE64_STANDARD.decode(&s).map_err(serde::de::Error::custom)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn consensus_proposal_hash_from_hex_str_roundtrip() {
let hex_str = "74657374";
let hash = ConsensusProposalHash::from_hex(hex_str).expect("consensus hash hex");
assert_eq!(hash.0, b"test".to_vec());
assert_eq!(format!("{hash}"), hex_str);
let json = serde_json::to_string(&hash).expect("serialize consensus hash");
assert_eq!(json, "\"74657374\"");
let decoded: ConsensusProposalHash =
serde_json::from_str(&json).expect("deserialize consensus hash");
assert_eq!(decoded, hash);
}
#[test]
fn txhash_from_hex_str_roundtrip() {
let hex_str = "746573745f7478";
let hash = TxHash::from_hex(hex_str).expect("txhash hex");
assert_eq!(hash.0, b"test_tx".to_vec());
assert_eq!(format!("{hash}"), hex_str);
let json = serde_json::to_string(&hash).expect("serialize tx hash");
assert_eq!(json, "\"746573745f7478\"");
let decoded: TxHash = serde_json::from_str(&json).expect("deserialize tx hash");
assert_eq!(decoded, hash);
}
#[test]
fn proof_data_hash_from_hex_str_roundtrip() {
let hex_str = "0x74657374";
let hash = ProofDataHash::from_hex(hex_str).expect("proof hash hex");
assert_eq!(hash.0, b"test".to_vec());
assert_eq!(hex::encode(&hash.0), "74657374");
let json = serde_json::to_string(&hash).expect("serialize proof hash");
assert_eq!(json, "\"74657374\"");
let decoded: ProofDataHash = serde_json::from_str(&json).expect("deserialize proof hash");
assert_eq!(decoded, hash);
}
}