light_client/consensus/
payload.rs

1use anyhow::{Context, Result, anyhow, ensure};
2use espresso_types::{Header, Payload};
3use hotshot_types::{
4    data::{VidCommitment, VidCommon, ns_table::parse_ns_table},
5    traits::block_contents::EncodeBytes,
6    vid::{
7        advz::{ADVZScheme, advz_scheme},
8        avidm::{AvidMScheme, init_avidm_param},
9        avidm_gf2::{AvidmGf2Scheme, init_avidm_gf2_param},
10    },
11};
12use jf_advz::VidScheme;
13use serde::{Deserialize, Serialize};
14
15/// Information required to verify a payload.
16#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
17pub struct PayloadProof {
18    /// The payload to be verified.
19    payload: Payload,
20
21    /// VID common data.
22    ///
23    /// This is data necessary to recompute the VID commitment of the payload, which can then be
24    /// verified against a commitment in a previously-verified header.
25    vid_common: VidCommon,
26}
27
28impl PayloadProof {
29    /// Construct a [`PayloadProof`].
30    ///
31    /// Takes the payload to be verified, plus corresponding [`VidCommon`] data to allow a client to
32    /// recompute and verify the commitment of the data.
33    pub fn new(payload: Payload, vid_common: VidCommon) -> Self {
34        Self {
35            payload,
36            vid_common,
37        }
38    }
39
40    /// Verify a [`PayloadProof`].
41    ///
42    /// If the data in this proof matches the expected `header`, the full payload data is returned.
43    pub fn verify(self, header: &Header) -> Result<Payload> {
44        Ok(self.verify_with_vid_common(header)?.0)
45    }
46
47    /// Verify a [`PayloadProof`] and get the corresponding [`VidCommon`].
48    ///
49    /// If the data in this proof matches the expected `header`, the full payload data is returned.
50    pub fn verify_with_vid_common(self, header: &Header) -> Result<(Payload, VidCommon)> {
51        let commit = match &self.vid_common {
52            VidCommon::V0(common) => {
53                advz_scheme(ADVZScheme::get_num_storage_nodes(common) as usize)
54                    .commit_only(self.payload.encode())
55                    .map(VidCommitment::V0)
56                    .context("computing ADVZ commitment")?
57            },
58            VidCommon::V1(avidm) => {
59                let param = init_avidm_param(avidm.total_weights)?;
60                let bytes = self.payload.encode();
61                AvidMScheme::commit(
62                    &param,
63                    &bytes,
64                    parse_ns_table(bytes.len(), &header.ns_table().encode()),
65                )
66                .map(VidCommitment::V1)
67                .map_err(|err| anyhow!("computing AvidM commitment: {err:#}"))?
68            },
69            VidCommon::V2(avidm_gf2) => {
70                let param = init_avidm_gf2_param(avidm_gf2.param.total_weights)?;
71                let bytes = self.payload.encode();
72                AvidmGf2Scheme::commit(
73                    &param,
74                    &bytes,
75                    parse_ns_table(bytes.len(), &header.ns_table().encode()),
76                )
77                .map(|(comm, _)| VidCommitment::V2(comm))
78                .map_err(|err| anyhow!("computing AvidM commitment: {err:#}"))?
79            },
80        };
81        ensure!(
82            commit == header.payload_commitment(),
83            "commitment of payload does not match commitment in header"
84        );
85        Ok((self.payload, self.vid_common))
86    }
87}