celestia_types/share/
proof.rs

1use celestia_proto::celestia::core::v1::proof::ShareProof as RawShareProof;
2use serde::{Deserialize, Serialize};
3use tendermint_proto::Protobuf;
4
5use crate::consts::appconsts::SHARE_SIZE;
6use crate::hash::Hash;
7use crate::nmt::NamespaceProof;
8use crate::{bail_verification, validation_error, RowProof};
9use crate::{nmt::Namespace, Error, Result};
10
11/// A proof of inclusion of a continouous range of shares of some namespace
12/// in a [`DataAvailabilityHeader`].
13///
14/// The proof will proof the inclusion of shares in row roots they span
15/// and the inclusion of those row roots in the dah.
16///
17/// [`DataAvailabilityHeader`]: crate::DataAvailabilityHeader
18#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
19#[serde(try_from = "RawShareProof", into = "RawShareProof")]
20pub struct ShareProof {
21    /// The shares, as a Vec of byte arrays
22    pub data: Vec<[u8; SHARE_SIZE]>,
23    /// The namespace of the shares
24    pub namespace_id: Namespace,
25    /// Vec of the NMT multiproofs for the rows spanned by the range
26    pub share_proofs: Vec<NamespaceProof>,
27    /// Proofs for row roots into data root
28    pub row_proof: RowProof,
29}
30
31impl ShareProof {
32    /// Get the shares proven by this proof.
33    pub fn shares(&self) -> &[[u8; SHARE_SIZE]] {
34        &self.data
35    }
36
37    /// Verify the proof against the hash of [`DataAvailabilityHeader`], proving
38    /// the inclusion of shares.
39    ///
40    /// # Errors
41    ///
42    /// This function will return an error if:
43    ///  - the proof is malformed. Number of shares, nmt proofs and row proofs needs to match.
44    ///  - the verification of any inner row proof fails
45    ///
46    /// [`DataAvailabilityHeader`]: crate::DataAvailabilityHeader
47    pub fn verify(&self, root: Hash) -> Result<()> {
48        let row_roots = self.row_proof.row_roots();
49
50        if self.share_proofs.len() != row_roots.len() {
51            bail_verification!(
52                "share proofs length ({}) != row roots length ({})",
53                self.share_proofs.len(),
54                row_roots.len()
55            );
56        }
57
58        let mut shares_needed = 0;
59        for proof in &self.share_proofs {
60            if proof.is_of_absence() {
61                bail_verification!("only presence proofs allowed");
62            }
63            if proof.start_idx() >= proof.end_idx() {
64                bail_verification!("proof without data");
65            }
66
67            shares_needed += proof.end_idx() - proof.start_idx();
68        }
69
70        if shares_needed as usize != self.data.len() {
71            bail_verification!(
72                "shares needed ({}) != proof's data length ({})",
73                shares_needed,
74                self.data.len()
75            );
76        }
77
78        self.row_proof.verify(root)?;
79
80        let mut data = self.data.as_slice();
81
82        for (proof, row) in self.share_proofs.iter().zip(row_roots) {
83            let amount = proof.end_idx() - proof.start_idx();
84            let leaves = &data[..amount as usize];
85            proof
86                .verify_range(row, leaves, *self.namespace_id)
87                .map_err(Error::RangeProofError)?;
88            data = &data[amount as usize..];
89        }
90
91        Ok(())
92    }
93}
94
95impl Protobuf<RawShareProof> for ShareProof {}
96
97impl TryFrom<RawShareProof> for ShareProof {
98    type Error = Error;
99
100    fn try_from(value: RawShareProof) -> Result<Self> {
101        Ok(Self {
102            data: value
103                .data
104                .into_iter()
105                .map(TryInto::try_into)
106                .collect::<Result<_, _>>()
107                .map_err(|_| validation_error!("invalid share size"))?,
108            namespace_id: Namespace::new(
109                value
110                    .namespace_version
111                    .try_into()
112                    .map_err(|_| validation_error!("namespace version must be single byte"))?,
113                &value.namespace_id,
114            )?,
115            share_proofs: value
116                .share_proofs
117                .into_iter()
118                .map(TryInto::try_into)
119                .collect::<Result<_>>()?,
120            row_proof: value
121                .row_proof
122                .ok_or_else(|| validation_error!("row proof missing"))?
123                .try_into()?,
124        })
125    }
126}
127
128impl From<ShareProof> for RawShareProof {
129    fn from(value: ShareProof) -> Self {
130        Self {
131            data: value.data.into_iter().map(Into::into).collect(),
132            namespace_id: value.namespace_id.id().to_vec(),
133            namespace_version: value.namespace_id.version() as u32,
134            share_proofs: value.share_proofs.into_iter().map(Into::into).collect(),
135            row_proof: Some(value.row_proof.into()),
136        }
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use crate::DataAvailabilityHeader;
143
144    use super::ShareProof;
145
146    #[test]
147    fn share_proof_serde() {
148        let raw_share_proof = r#"{
149          "data": [
150            "AAAAAAAAAAAAAAAAAAAAAAAAANjLtTOiQmHEwKMBAAAEAAK7jTduoBTIVHIsXZYBTeXT+ROAP0ErS1wBn3qRFHoNClY8r4gEOLhvoPDfYX5dN+qGDHdIFPG4F1aF+niSmbfQRSkw2QdjqKwDKhYUKvu10oUo5r/k0SyYJx5KImSJ0d2sBH/ajcpk+DWBD0tXJTmsfiATmM8BqaxRRa5biE1T9yV1WKndAyJUC00P8e2/MG+7P1t7a9tMjG+Oxgxx1EzxJ47FDiRwnYNz2/JBqzIC33fKoiWZSeL+NFLn0Dfx+Ev1GYaKpstd1x1tgJnkEceTFVC6r7qhqRbTFJjAjgYJAB4fbBd/+QUQkdbW0uCHLtmWhkeK9YBuY05L1v1c6wcXI9IhSlBLnFFdxSTonAaZYhOusiG6eNFn7FpTU0i0oHcksQL+MW3HhbOnIyyUE1Wyjsm6pFuHKBi4TwHTQOibOhvxehuxyrHkqk7QcEPK6/ioN08n2eqd1mlfXiG2wk8nDaZfdmIq3hCm2usrpmqxJHYoH/wbbMeB7AzhreueWRk38984H2h1xX93ZpmUWJEJJGJ0St70Afb6RPjH9pX9vtbVXCvj65D+HPpxinReMBUj0rvGZ6IzNzoBhJYGszp5R4sdztGH8NLNWujwAFDThNRhWUX/r+APM1hdW9s=",
151            "AAAAAAAAAAAAAAAAAAAAAAAAANjLtTOiQmHEwKMAaRdGrcBVugXTHUqmX0/UvgFUVjY/T1lnVQuz0gKhCE1aN0WvNPJautwNmv/68eAucdCm6vPqzg4KEgL777G5navyLj/1TMhLJfgTf1YNayDJLN7R13d1QQ3Peagcg+N4Itv0ZmZ6p7/QMQfwaXh30yronynPhxV//932ODigrZVrbW+XBhPtgh+/DlbCrU8d65IGn3VGTDQNfZaajmogy4xNf9x089OgcOv8H1XEjj0X8iZQwW+K05wIE6STWGxXJSMywMM7A+FE5YDrnEHeIT9bsIeXvEAy2cwfrorAnQrbfPyZqSSHHQzGumhOYam1Cyz1oVUMAhJMOXRdrsckPXQdy38cOw/2VNFCZnLDvJIdT9kL3fk+BX/tXUMdvmR0ATY1JiqjW1YPO8fpVaG+IrbUobxDUnrq5kSmK6Gi9WIF+NzCWpPD/bjV+6nwJXEUxzjb18wVitZJZsQpMVsB9t0KXzlvr9AsKob4AZZGAAqbI4cHKjW3PNMbpH6U1WTUPldy7NQvpWDcYsVbYQOEr9YiKGyUPIUdb+nPSyAd4aIpbce8fQhN9D9wE0SIDTm2hMorjJsemwdZSHifZbL4Yya7QR/Oa3x5K+82IZiuhm/y5HGEdlHB2Jg54wqJJrKvO2E=",
152            "AAAAAAAAAAAAAAAAAAAAAAAAANjLtTOiQmHEwKMAIt3G6MztjqXhUJ6Mbw/14mlqVbzIwJNU38ITmjBXACSyjgvQCMhVZYrvWQxCuEtPHboM1HrI1rgVjMC3B7vregAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
153          ],
154          "namespace_id": "AAAAAAAAAAAAAAAAAAAAAAAA2Mu1M6JCYcTAow==",
155          "namespace_version": 0,
156          "share_proofs": [
157            {
158              "end": 2,
159              "nodes": [
160                "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCU0aUrR/wpx09HFWeoyuV1vuw5Ew3rhtCaf/Zd4chb9",
161                "/////////////////////////////////////////////////////////////////////////////ypPU4ZqDz1t8YcunXI8ETuBth1gXLvPWIMd0JPoeJF3"
162              ],
163              "start": 1
164            },
165            {
166              "end": 2,
167              "nodes": [
168                "/////////////////////////////////////////////////////////////////////////////wdXw/2tc8hhuGLcsfU9pWo5BDIKSsNJFCytj++xtFgq"
169              ]
170            }
171          ],
172          "row_proof": {
173             "end_row": 1,
174             "proofs": [
175               {
176                 "aunts": [
177                   "Ch+9PsBdsN5YUt8nvAmjdOAIcVdfmPAEUNmCA8KBe5A=",
178                   "ojjC9H5JG/7OOrt5BzBXs/3w+n1LUI/0YR0d+RSfleU=",
179                   "d6bMQbLTBfZGvqXOW9MPqRM+fTB2/wLJx6CkLc8glCI="
180                 ],
181                 "index": 0,
182                 "leaf_hash": "nOpM3A3d0JYOmNaaI5BFAeKPGwQ90TqmM/kx+sHr79s=",
183                 "total": 8
184               },
185               {
186                 "aunts": [
187                   "nOpM3A3d0JYOmNaaI5BFAeKPGwQ90TqmM/kx+sHr79s=",
188                   "ojjC9H5JG/7OOrt5BzBXs/3w+n1LUI/0YR0d+RSfleU=",
189                   "d6bMQbLTBfZGvqXOW9MPqRM+fTB2/wLJx6CkLc8glCI="
190                 ],
191                 "index": 1,
192                 "leaf_hash": "Ch+9PsBdsN5YUt8nvAmjdOAIcVdfmPAEUNmCA8KBe5A=",
193                 "total": 8
194               }
195             ],
196             "row_roots": [
197               "000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000D8CBB533A24261C4C0A3D37F1CBFB6F4C5EA031472EBA390D482637933874AA0A2B9735E67629993852D",
198               "00000000000000000000000000000000000000D8CBB533A24261C4C0A300000000000000000000000000000000000000D8CBB533A24261C4C0A37E409334CCB1125C793EC040741137634C148F089ACB06BFFF4C1C4CA2CBBA8E"
199             ],
200             "start_row": 0
201          }
202        }"#;
203        let raw_dah = r#"
204          {
205            "row_roots": [
206              "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAA2Mu1M6JCYcTAo9N/HL+29MXqAxRy66OQ1IJjeTOHSqCiuXNeZ2KZk4Ut",
207              "AAAAAAAAAAAAAAAAAAAAAAAAANjLtTOiQmHEwKMAAAAAAAAAAAAAAAAAAAAAAAAA2Mu1M6JCYcTAo35AkzTMsRJceT7AQHQRN2NMFI8ImssGv/9MHEyiy7qO",
208              "/////////////////////////////////////////////////////////////////////////////7mTwL+NxdxcYBd89/wRzW2k9vRkQehZiXsuqZXHy89X",
209              "/////////////////////////////////////////////////////////////////////////////2X/FT2ugeYdWmvnEisSgW+9Ih8paNvrji2NYPb8ujaK"
210            ],
211            "column_roots": [
212              "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAA2Mu1M6JCYcTAo/xEv//wkWzNtkcAZiZmSGU1Te6ERwUxTtTfHzoS4bv+",
213              "AAAAAAAAAAAAAAAAAAAAAAAAANjLtTOiQmHEwKMAAAAAAAAAAAAAAAAAAAAAAAAA2Mu1M6JCYcTAo9FOCNvCjA42xYCwHrlo48iPEXLaKt+d+JdErCIrQIi6",
214              "/////////////////////////////////////////////////////////////////////////////y2UErq/83uv433HekCWokxqcY4g+nMQn3tZn2Tr6v74",
215              "/////////////////////////////////////////////////////////////////////////////z6fKmbJTvfLYFlNuDWHn87vJb6V7n44MlCkxv1dyfT2"
216            ]
217          }
218        "#;
219
220        let proof: ShareProof = serde_json::from_str(raw_share_proof).unwrap();
221        let dah: DataAvailabilityHeader = serde_json::from_str(raw_dah).unwrap();
222
223        proof.verify(dah.hash()).unwrap()
224    }
225}