eq_common/
lib.rs

1use celestia_types::{
2    nmt::{Namespace, NamespaceProof},
3    RowProof,
4};
5use serde::{Deserialize, Serialize};
6
7#[cfg(feature = "host")]
8mod error;
9#[cfg(feature = "host")]
10pub use error::{ErrorLabels, InclusionServiceError};
11
12#[cfg(feature = "grpc")]
13/// gRPC generated bindings
14pub mod eqs {
15    include!("generated/eqs.rs");
16}
17
18/*
19    For now, we only support ZKStackEqProofs
20    These are used for Celestia integrations with Matter Labs' ZKStack
21    TODO: Add support for Payy Celestia integration
22*/
23#[derive(Serialize, Deserialize, Clone, Debug)]
24pub struct ZKStackEqProofInput {
25    #[serde(rename = "blob_data")]
26    pub data: Vec<u8>,
27
28    #[serde(rename = "blob_namespace")]
29    pub namespace_id: Namespace,
30
31    #[serde(rename = "nmt_multiproofs")]
32    pub share_proofs: Vec<NamespaceProof>,
33
34    #[serde(rename = "row_root_multiproof")]
35    pub row_proof: RowProof,
36
37    pub data_root: [u8; 32],   // already matches
38    pub keccak_hash: [u8; 32], // already matches
39    // batch_number and chain_id are passed through to prevent proofs from being replayed
40    pub batch_number: u32,
41    pub chain_id: u64,
42}
43
44/// Expecting bytes:
45/// (keccak_hash: [u8; 32], pub data_root: [u8; 32])
46pub struct ZKStackEqProofOutput {
47    pub keccak_hash: [u8; 32],
48    pub data_root: [u8; 32],
49    pub batch_number: u32,
50    pub chain_id: u64,
51}
52impl ZKStackEqProofOutput {
53    // Simple encoding, rather than use any Ethereum libraries
54    pub fn to_vec(&self) -> Vec<u8> {
55        let mut encoded = Vec::new();
56        encoded.extend_from_slice(&self.keccak_hash);
57        encoded.extend_from_slice(&self.data_root);
58        encoded.extend_from_slice(&self.batch_number.to_le_bytes());
59        encoded.extend_from_slice(&self.chain_id.to_le_bytes());
60        encoded
61    }
62
63    #[cfg(feature = "host")]
64    pub fn from_bytes(data: &[u8]) -> Result<Self, InclusionServiceError> {
65        if data.len() != 76 {
66            return Err(InclusionServiceError::OutputDeserializationError);
67        }
68        let decoded = ZKStackEqProofOutput {
69            keccak_hash: data[0..32]
70                .try_into()
71                .map_err(|_| InclusionServiceError::OutputDeserializationError)?,
72            data_root: data[32..64]
73                .try_into()
74                .map_err(|_| InclusionServiceError::OutputDeserializationError)?,
75            batch_number: u32::from_le_bytes(
76                data[64..68]
77                    .try_into()
78                    .map_err(|_| InclusionServiceError::OutputDeserializationError)?,
79            ),
80            chain_id: u64::from_le_bytes(
81                data[68..76]
82                    .try_into()
83                    .map_err(|_| InclusionServiceError::OutputDeserializationError)?,
84            ),
85        };
86        Ok(decoded)
87    }
88}
89
90#[cfg(test)]
91mod test {
92    use super::*;
93
94    #[test]
95    #[cfg(feature = "host")]
96    fn test_serialization() {
97        let output = ZKStackEqProofOutput {
98            keccak_hash: [0; 32],
99            data_root: [0; 32],
100            batch_number: 0u32,
101            chain_id: 0u64,
102        };
103        let encoded = output.to_vec();
104        let decoded = ZKStackEqProofOutput::from_bytes(&encoded).unwrap();
105        assert_eq!(output.keccak_hash, decoded.keccak_hash);
106        assert_eq!(output.data_root, decoded.data_root);
107    }
108}