/ parameters / examples / inclusion.rs
inclusion.rs
  1  // Copyright (c) 2019-2025 Alpha-Delta Network Inc.
  2  // This file is part of the deltavm library.
  3  
  4  // Licensed under the Apache License, Version 2.0 (the "License");
  5  // you may not use this file except in compliance with the License.
  6  // You may obtain a copy of the License at:
  7  
  8  // http://www.apache.org/licenses/LICENSE-2.0
  9  
 10  // Unless required by applicable law or agreed to in writing, software
 11  // distributed under the License is distributed on an "AS IS" BASIS,
 12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  // See the License for the specific language governing permissions and
 14  // limitations under the License.
 15  
 16  use alphastd::StorageMode;
 17  use deltavm_algorithms::{crypto_hash::sha256::sha256, snark::varuna::VarunaVersion};
 18  use deltavm_circuit::{Alpha, Assignment};
 19  use deltavm_console::{
 20      account::PrivateKey,
 21      network::{CanaryV0, FromBits, MainnetV0, Network, TestnetV0},
 22      prelude::{One, ToBytes, Zero},
 23      program::{Plaintext, Record, StatePath},
 24      types::Field,
 25  };
 26  use deltavm_ledger_store::ConsensusStore;
 27  
 28  #[cfg(not(feature = "rocks"))]
 29  type LedgerType<N> = deltavm_ledger_store::helpers::memory::ConsensusMemory<N>;
 30  #[cfg(feature = "rocks")]
 31  type LedgerType<N> = deltavm_ledger_store::helpers::rocksdb::ConsensusDB<N>;
 32  
 33  use deltavm_synthesizer::{
 34      VM,
 35      process::{InclusionAssignment, InclusionV0Assignment},
 36      snark::UniversalSRS,
 37  };
 38  
 39  use anyhow::{Result, anyhow};
 40  use rand::thread_rng;
 41  use serde_json::{Value, json};
 42  use std::{
 43      fs::File,
 44      io::{BufWriter, Write},
 45      path::PathBuf,
 46  };
 47  
 48  fn checksum(bytes: &[u8]) -> String {
 49      hex::encode(sha256(bytes))
 50  }
 51  
 52  fn versioned_filename(filename: &str, checksum: &str) -> String {
 53      match checksum.get(0..7) {
 54          Some(sum) => format!("{filename}.{sum}"),
 55          _ => filename.to_string(),
 56      }
 57  }
 58  
 59  /// Writes the given bytes to the given versioned filename.
 60  fn write_remote(filename: &str, version: &str, bytes: &[u8]) -> Result<()> {
 61      let mut file = BufWriter::new(File::create(PathBuf::from(&versioned_filename(filename, version)))?);
 62      file.write_all(bytes)?;
 63      Ok(())
 64  }
 65  
 66  /// Writes the given bytes to the given filename.
 67  fn write_local(filename: &str, bytes: &[u8]) -> Result<()> {
 68      let mut file = BufWriter::new(File::create(PathBuf::from(filename))?);
 69      file.write_all(bytes)?;
 70      Ok(())
 71  }
 72  
 73  /// Writes the given metadata as JSON to the given filename.
 74  fn write_metadata(filename: &str, metadata: &Value) -> Result<()> {
 75      let mut file = BufWriter::new(File::create(PathBuf::from(filename))?);
 76      file.write_all(&serde_json::to_vec_pretty(metadata)?)?;
 77      Ok(())
 78  }
 79  
 80  /// Returns the assignment for verifying the state path.
 81  #[allow(clippy::type_complexity)]
 82  pub fn sample_assignment_v0<N: Network, A: Alpha<Network = N>>() -> Result<(Assignment<N::Field>, StatePath<N>, Field<N>)> {
 83      // Initialize the consensus store.
 84      let store = ConsensusStore::<N, LedgerType<N>>::open(StorageMode::new_test(None))?;
 85      // Initialize a new VM.
 86      let vm = VM::from(store)?;
 87  
 88      // Initialize an RNG.
 89      let rng = &mut thread_rng();
 90      // Initialize a new caller.
 91      let caller_private_key = PrivateKey::<N>::new(rng).unwrap();
 92      // Return the block.
 93      let genesis_block = vm.genesis_beacon(&caller_private_key, rng)?;
 94  
 95      // Update the VM.
 96      vm.add_next_block(&genesis_block)?;
 97  
 98      // Fetch the first commitment.
 99      let commitment = genesis_block.commitments().next().ok_or_else(|| anyhow!("No commitments found"))?;
100      // Compute the state path for the commitment.
101      let state_path = vm.block_store().get_state_path_for_commitment(commitment)?;
102  
103      // Compute the generator `H` as `HashToGroup(commitment)`.
104      let h = N::hash_to_group_psd2(&[N::serial_number_domain(), *commitment])?;
105      // Compute `gamma` as `sk_sig * H`.
106      let gamma = h * caller_private_key.sk_sig();
107      // Compute the serial number.
108      let serial_number = Record::<N, Plaintext<N>>::serial_number_from_gamma(&gamma, *commitment)?;
109  
110      // Construct the assignment for the inclusion circuit.
111      let assignment = InclusionV0Assignment::new(state_path.clone(), *commitment, gamma, serial_number, Default::default(), true)
112          .to_circuit_assignment::<A>()?;
113  
114      Ok((assignment, state_path, serial_number))
115  }
116  
117  /// Returns the assignment for verifying the state path.
118  #[allow(clippy::type_complexity)]
119  pub fn sample_assignment<N: Network, A: Alpha<Network = N>>() -> Result<(Assignment<N::Field>, StatePath<N>, Field<N>, bool, u32)>
120  {
121      // Initialize the consensus store.
122      let store = ConsensusStore::<N, LedgerType<N>>::open(StorageMode::new_test(None))?;
123      // Initialize a new VM.
124      let vm = VM::from(store)?;
125  
126      // Initialize an RNG.
127      let rng = &mut thread_rng();
128      // Initialize a new caller.
129      let caller_private_key = PrivateKey::<N>::new(rng).unwrap();
130      // Return the block.
131      let genesis_block = vm.genesis_beacon(&caller_private_key, rng)?;
132  
133      // Update the VM.
134      vm.add_next_block(&genesis_block)?;
135  
136      // Fetch the first commitment.
137      let commitment = genesis_block.commitments().next().ok_or_else(|| anyhow!("No commitments found"))?;
138      // Compute the state path for the commitment.
139      let state_path = vm.block_store().get_state_path_for_commitment(commitment)?;
140  
141      // Compute the generator `H` as `HashToGroup(commitment)`.
142      let h = N::hash_to_group_psd2(&[N::serial_number_domain(), *commitment])?;
143      // Compute `gamma` as `sk_sig * H`.
144      let gamma = h * caller_private_key.sk_sig();
145      // Compute the serial number.
146      let serial_number = Record::<N, Plaintext<N>>::serial_number_from_gamma(&gamma, *commitment)?;
147  
148      // Set the `is_record_block_height_reached` flag.
149      let is_record_block_height_reached = true;
150      // Initialize the `upgrade_block_height`.
151      let upgrade_block_height = 0;
152  
153      // Construct the assignment for the inclusion circuit.
154      let assignment = InclusionAssignment::new(
155          state_path.clone(),
156          *commitment,
157          gamma,
158          serial_number,
159          is_record_block_height_reached,
160          upgrade_block_height,
161          Default::default(),
162          true,
163      )
164      .to_circuit_assignment::<A>()?;
165  
166      Ok((assignment, state_path, serial_number, is_record_block_height_reached, upgrade_block_height))
167  }
168  
169  /// Synthesizes the circuit keys for the inclusion circuit. (cargo run --release --example inclusion [network])
170  pub fn inclusion<N: Network, A: Alpha<Network = N>>() -> Result<()> {
171      // Load the universal SRS.
172      let universal_srs = UniversalSRS::<N>::load()?;
173  
174      // Sample the assignment for the inclusion circuit.
175      let (assignment, state_path, serial_number, is_record_block_height_reached, upgrade_block_height) =
176          sample_assignment::<N, A>()?;
177  
178      // Synthesize the proving and verifying key.
179      let inclusion_function_name = N::INCLUSION_FUNCTION_NAME;
180      let (proving_key, verifying_key) = universal_srs.to_circuit_key(inclusion_function_name, &assignment)?;
181  
182      for varuna_version in [VarunaVersion::V1, VarunaVersion::V2] {
183          // Ensure the proving key and verifying keys are valid.
184          let proof = proving_key.prove(inclusion_function_name, varuna_version, &assignment, &mut thread_rng())?;
185          assert!(verifying_key.verify(
186              inclusion_function_name,
187              varuna_version,
188              &[
189                  N::Field::one(),
190                  **state_path.global_state_root(),
191                  *Field::<N>::zero(),
192                  *serial_number,
193                  *Field::<N>::from_bits_le(&[is_record_block_height_reached])?,
194                  *Field::<N>::from_u32(upgrade_block_height)
195              ],
196              &proof
197          ));
198          // Ensure using the wrong varuna version is not valid.
199          let wrong_varuna_version = if varuna_version == VarunaVersion::V1 { VarunaVersion::V2 } else { VarunaVersion::V1 };
200          assert!(!verifying_key.verify(
201              inclusion_function_name,
202              wrong_varuna_version,
203              &[
204                  N::Field::one(),
205                  **state_path.global_state_root(),
206                  *Field::<N>::zero(),
207                  *serial_number,
208                  *Field::<N>::from_bits_le(&[is_record_block_height_reached])?,
209                  *Field::<N>::from_u32(upgrade_block_height)
210              ],
211              &proof
212          ));
213      }
214  
215      // Initialize a vector for the commands.
216      let mut commands = vec![];
217  
218      let proving_key_bytes = proving_key.to_bytes_le()?;
219      let proving_key_checksum = checksum(&proving_key_bytes);
220  
221      let verifying_key_bytes = verifying_key.to_bytes_le()?;
222      let verifying_key_checksum = checksum(&verifying_key_bytes);
223  
224      let metadata = json!({
225          "prover_checksum": proving_key_checksum,
226          "prover_size": proving_key_bytes.len(),
227          "verifier_checksum": verifying_key_checksum,
228          "verifier_size": verifying_key_bytes.len(),
229      });
230  
231      println!("{}", serde_json::to_string_pretty(&metadata)?);
232      write_metadata(&format!("{inclusion_function_name}.metadata"), &metadata)?;
233      write_remote(&format!("{inclusion_function_name}.prover"), &proving_key_checksum, &proving_key_bytes)?;
234      write_local(&format!("{inclusion_function_name}.verifier"), &verifying_key_bytes)?;
235  
236      commands
237          .push(format!("upload \"{}\"", versioned_filename(&format!("{inclusion_function_name}.prover"), &proving_key_checksum)));
238  
239      // Print the commands.
240      println!("\nNow, perform the following operations:\n");
241      for command in commands {
242          println!("{command}");
243      }
244      println!();
245  
246      Ok(())
247  }
248  
249  /// Run the following command to generate the inclusion circuit keys.
250  /// `cargo run --example inclusion [network]`
251  pub fn main() -> Result<()> {
252      let args: Vec<String> = std::env::args().collect();
253  
254      if args.len() < 2 {
255          println!("Invalid number of arguments. Given: {} - Required: 1", args.len() - 1);
256          return Ok(());
257      }
258  
259      match args[1].as_str() {
260          "mainnet" => {
261              inclusion::<MainnetV0, deltavm_circuit::AlphaV0>()?;
262          }
263          "testnet" => {
264              inclusion::<TestnetV0, deltavm_circuit::AlphaTestnetV0>()?;
265          }
266          "canary" => {
267              inclusion::<CanaryV0, deltavm_circuit::AlphaCanaryV0>()?;
268          }
269          _ => panic!("Invalid network"),
270      };
271  
272      Ok(())
273  }