Skip to main content

hotshot_types/vid/
advz.rs

1// Copyright (c) 2021-2024 Espresso Systems (espressosys.com)
2// This file is part of the HotShot repository.
3
4// You should have received a copy of the MIT License
5// along with the HotShot repository. If not, see <https://mit-license.org/>.
6
7//! Provides the implementation for ADVZ VID scheme
8
9use std::{fmt::Debug, ops::Range};
10
11use ark_bn254::Bn254;
12use ark_serialize::CanonicalDeserialize;
13use jf_advz::{
14    VidDisperse, VidResult, VidScheme,
15    advz::{
16        self,
17        payload_prover::{LargeRangeProof, SmallRangeProof},
18    },
19    payload_prover::{PayloadProver, Statement},
20};
21use jf_pcs::{
22    PolynomialCommitmentScheme,
23    prelude::{UnivariateKzgPCS, UnivariateUniversalParams},
24};
25use lazy_static::lazy_static;
26use serde::{Deserialize, Serialize};
27use sha2::Sha256;
28
29/// VID scheme constructor.
30///
31/// Returns an opaque type that impls jellyfish traits:
32/// [`VidScheme`], [`PayloadProver`], [`Precomputable`].
33///
34/// # Rust forbids naming impl Trait in return types
35///
36/// Due to Rust limitations the return type of [`vid_scheme`] is a newtype
37/// wrapper [`VidSchemeType`] that impls the above traits.
38///
39/// We prefer that the return type of [`vid_scheme`] be `impl Trait` for the
40/// above traits. But the ability to name an impl Trait return type is
41/// currently missing from Rust:
42/// - [Naming impl trait in return types - Impl trait initiative](https://rust-lang.github.io/impl-trait-initiative/explainer/rpit_names.html)
43/// - [RFC: Type alias impl trait (TAIT)](https://github.com/rust-lang/rfcs/blob/master/text/2515-type_alias_impl_trait.md)
44///
45/// # Panics
46/// When the construction fails for the underlying VID scheme.
47#[must_use]
48#[memoize::memoize(SharedCache, Capacity: 10)]
49pub fn advz_scheme(num_storage_nodes: usize) -> ADVZScheme {
50    // recovery_threshold is currently num_storage_nodes rounded down to a power of two
51    // TODO recovery_threshold should be a function of the desired erasure code rate
52    // https://github.com/EspressoSystems/HotShot/issues/2152
53    let recovery_threshold = 1 << num_storage_nodes.ilog2();
54
55    #[allow(clippy::panic)]
56    let num_storage_nodes = u32::try_from(num_storage_nodes).unwrap_or_else(|err| {
57        panic!("num_storage_nodes {num_storage_nodes} should fit into u32; error: {err}")
58    });
59
60    // TODO panic, return `Result`, or make `new` infallible upstream (eg. by panicking)?
61    #[allow(clippy::panic)]
62    ADVZScheme(
63        Advz::new(num_storage_nodes, recovery_threshold, &*KZG_SRS).unwrap_or_else(|err| {
64            panic!(
65                "advz construction failure: (num_storage \
66                 nodes,recovery_threshold)=({num_storage_nodes},{recovery_threshold}); error: \
67                 {err}"
68            )
69        }),
70    )
71}
72
73/// VID commitment type
74pub type ADVZCommitment = <ADVZScheme as VidScheme>::Commit;
75/// VID common type
76pub type ADVZCommon = <ADVZScheme as VidScheme>::Common;
77/// VID share type
78pub type ADVZShare = <ADVZScheme as VidScheme>::Share;
79
80#[cfg(not(feature = "gpu-vid"))]
81/// Internal Jellyfish VID scheme
82type Advz = advz::Advz<E, H>;
83#[cfg(feature = "gpu-vid")]
84/// Internal Jellyfish VID scheme
85type Advz = advz::AdvzGPU<'static, E, H>;
86
87/// Newtype wrapper for a VID scheme type that impls
88/// [`VidScheme`], [`PayloadProver`], [`Precomputable`].
89#[derive(Clone)]
90pub struct ADVZScheme(Advz);
91
92/// Newtype wrapper for a large payload range proof.
93///
94/// Useful for namespace proofs.
95#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
96pub struct LargeRangeProofType(
97    // # Type complexity
98    //
99    // Jellyfish's `LargeRangeProof` type has a prime field generic parameter `F`.
100    // This `F` is determined by the type parameter `E` for `Advz`.
101    // Jellyfish needs a more ergonomic way for downstream users to refer to this type.
102    //
103    // There is a `KzgEval` type alias in jellyfish that helps a little, but it's currently private:
104    // <https://github.com/EspressoSystems/jellyfish/issues/423>
105    // If it were public then we could instead use
106    // `LargeRangeProof<KzgEval<E>>`
107    // but that's still pretty crufty.
108    LargeRangeProof<<UnivariateKzgPCS<E> as PolynomialCommitmentScheme>::Evaluation>,
109);
110
111/// Newtype wrapper for a small payload range proof.
112///
113/// Useful for transaction proofs.
114#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
115pub struct SmallRangeProofType(
116    // # Type complexity
117    //
118    // Similar to the comments in `LargeRangeProofType`.
119    SmallRangeProof<<UnivariateKzgPCS<E> as PolynomialCommitmentScheme>::Proof>,
120);
121
122// By default, use SRS from Aztec's ceremony
123lazy_static! {
124    /// SRS comment
125    static ref KZG_SRS: UnivariateUniversalParams<E> = {
126        // `build.rs` fetches the Aztec SRS at build time and stores it in `$OUT_DIR/kzg_srs.bin`.
127        // Here we embed that binary file directly into the compiled library, and deserialize it
128        // into the `UnivariateUniversalParams` struct at runtime.
129        let bytes = include_bytes!(concat!(env!("OUT_DIR"), "/kzg_srs.bin"));
130        UnivariateUniversalParams::deserialize_compressed(bytes.as_slice())
131            .expect("failed to deserialize generated SRS")
132    };
133}
134
135/// Private type alias for the EC pairing type parameter for [`Advz`].
136type E = Bn254;
137/// Private type alias for the hash type parameter for [`Advz`].
138type H = Sha256;
139
140// THE REST OF THIS FILE IS BOILERPLATE
141//
142// All this boilerplate can be deleted when we finally get
143// type alias impl trait (TAIT):
144// [rfcs/text/2515-type_alias_impl_trait.md at master ยท rust-lang/rfcs](https://github.com/rust-lang/rfcs/blob/master/text/2515-type_alias_impl_trait.md)
145impl VidScheme for ADVZScheme {
146    type Commit = <Advz as VidScheme>::Commit;
147    type Share = <Advz as VidScheme>::Share;
148    type Common = <Advz as VidScheme>::Common;
149
150    fn commit_only<B>(&mut self, payload: B) -> VidResult<Self::Commit>
151    where
152        B: AsRef<[u8]>,
153    {
154        self.0.commit_only(payload)
155    }
156
157    fn disperse<B>(&mut self, payload: B) -> VidResult<VidDisperse<Self>>
158    where
159        B: AsRef<[u8]>,
160    {
161        self.0.disperse(payload).map(vid_disperse_conversion)
162    }
163
164    fn verify_share(
165        &self,
166        share: &Self::Share,
167        common: &Self::Common,
168        commit: &Self::Commit,
169    ) -> VidResult<Result<(), ()>> {
170        self.0.verify_share(share, common, commit)
171    }
172
173    fn recover_payload(&self, shares: &[Self::Share], common: &Self::Common) -> VidResult<Vec<u8>> {
174        self.0.recover_payload(shares, common)
175    }
176
177    fn is_consistent(commit: &Self::Commit, common: &Self::Common) -> VidResult<()> {
178        <Advz as VidScheme>::is_consistent(commit, common)
179    }
180
181    fn get_payload_byte_len(common: &Self::Common) -> u32 {
182        <Advz as VidScheme>::get_payload_byte_len(common)
183    }
184
185    fn get_num_storage_nodes(common: &Self::Common) -> u32 {
186        <Advz as VidScheme>::get_num_storage_nodes(common)
187    }
188
189    fn get_multiplicity(common: &Self::Common) -> u32 {
190        <Advz as VidScheme>::get_multiplicity(common)
191    }
192}
193
194impl PayloadProver<LargeRangeProofType> for ADVZScheme {
195    fn payload_proof<B>(&self, payload: B, range: Range<usize>) -> VidResult<LargeRangeProofType>
196    where
197        B: AsRef<[u8]>,
198    {
199        self.0
200            .payload_proof(payload, range)
201            .map(LargeRangeProofType)
202    }
203
204    fn payload_verify(
205        &self,
206        stmt: Statement<'_, Self>,
207        proof: &LargeRangeProofType,
208    ) -> VidResult<Result<(), ()>> {
209        self.0.payload_verify(stmt_conversion(stmt), &proof.0)
210    }
211}
212
213impl PayloadProver<SmallRangeProofType> for ADVZScheme {
214    fn payload_proof<B>(&self, payload: B, range: Range<usize>) -> VidResult<SmallRangeProofType>
215    where
216        B: AsRef<[u8]>,
217    {
218        self.0
219            .payload_proof(payload, range)
220            .map(SmallRangeProofType)
221    }
222
223    fn payload_verify(
224        &self,
225        stmt: Statement<'_, Self>,
226        proof: &SmallRangeProofType,
227    ) -> VidResult<Result<(), ()>> {
228        self.0.payload_verify(stmt_conversion(stmt), &proof.0)
229    }
230}
231
232/// Convert a [`VidDisperse<Advz>`] to a [`VidDisperse<VidSchemeType>`].
233///
234/// Foreign type rules prevent us from doing:
235/// - `impl From<VidDisperse<VidSchemeType>> for VidDisperse<Advz>`
236/// - `impl VidDisperse<VidSchemeType> {...}`
237///
238/// and similarly for `Statement`.
239/// Thus, we accomplish type conversion via functions.
240fn vid_disperse_conversion(vid_disperse: VidDisperse<Advz>) -> VidDisperse<ADVZScheme> {
241    VidDisperse {
242        shares: vid_disperse.shares,
243        common: vid_disperse.common,
244        commit: vid_disperse.commit,
245    }
246}
247
248/// Convert a [`Statement<'_, VidSchemeType>`] to a [`Statement<'_, Advz>`].
249fn stmt_conversion(stmt: Statement<'_, ADVZScheme>) -> Statement<'_, Advz> {
250    Statement {
251        payload_subslice: stmt.payload_subslice,
252        range: stmt.range,
253        commit: stmt.commit,
254        common: stmt.common,
255    }
256}