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 }