celestia_types/share/
proof.rs1use 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#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
19#[serde(try_from = "RawShareProof", into = "RawShareProof")]
20pub struct ShareProof {
21 pub data: Vec<[u8; SHARE_SIZE]>,
23 pub namespace_id: Namespace,
25 pub share_proofs: Vec<NamespaceProof>,
27 pub row_proof: RowProof,
29}
30
31impl ShareProof {
32 pub fn shares(&self) -> &[[u8; SHARE_SIZE]] {
34 &self.data
35 }
36
37 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}