lib.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 #![forbid(unsafe_code)] 17 #![allow(clippy::too_many_arguments)] 18 // #![warn(clippy::cast_possible_truncation)] 19 // TODO (howardwu): Update the return type on `execute` after stabilizing the interface. 20 #![allow(clippy::type_complexity)] 21 22 extern crate deltavm_circuit as circuit; 23 extern crate deltavm_console as console; 24 25 mod cost; 26 pub use cost::*; 27 28 mod stack; 29 pub use stack::*; 30 31 mod trace; 32 pub use trace::*; 33 34 mod authorize; 35 mod deploy; 36 mod evaluate; 37 mod execute; 38 mod finalize; 39 mod verify_deployment; 40 mod verify_execution; 41 mod verify_fee; 42 43 #[cfg(test)] 44 mod tests; 45 46 use deltavm_algorithms::snark::varuna::VarunaVersion; 47 use deltavm_ledger_block::{Deployment, Execution, Fee, Input, Output, Transaction, Transition}; 48 use deltavm_ledger_store::{FinalizeStorage, FinalizeStore, atomic_batch_scope}; 49 use deltavm_synthesizer_program::{ 50 Branch, 51 Command, 52 FinalizeGlobalState, 53 FinalizeOperation, 54 Instruction, 55 Program, 56 StackTrait, 57 }; 58 use deltavm_synthesizer_snark::{ProvingKey, UniversalSRS, VerifyingKey}; 59 use deltavm_utilities::{defer, dev_println}; 60 use console::{ 61 account::PrivateKey, 62 network::prelude::*, 63 program::{ 64 Identifier, 65 Literal, 66 Locator, 67 Plaintext, 68 ProgramID, 69 Record, 70 Request, 71 Response, 72 Value, 73 compute_function_id, 74 }, 75 types::{Field, U16, U64}, 76 }; 77 78 use alphastd::prelude::{finish, lap, timer}; 79 use indexmap::IndexMap; 80 #[cfg(feature = "locktick")] 81 use locktick::parking_lot::RwLock; 82 #[cfg(not(feature = "locktick"))] 83 use parking_lot::RwLock; 84 use std::{collections::HashMap, sync::Arc}; 85 86 #[derive(Clone)] 87 pub struct Process<N: Network> { 88 /// The universal SRS. 89 universal_srs: UniversalSRS<N>, 90 /// The mapping of program IDs to stacks. 91 stacks: Arc<RwLock<IndexMap<ProgramID<N>, Arc<Stack<N>>>>>, 92 /// The mapping of program IDs to old stacks. 93 old_stacks: Arc<RwLock<IndexMap<ProgramID<N>, Option<Arc<Stack<N>>>>>>, 94 } 95 96 impl<N: Network> Process<N> { 97 /// Initializes a new process. 98 #[inline] 99 pub fn setup<A: circuit::Alpha<Network = N>, R: Rng + CryptoRng>(rng: &mut R) -> Result<Self> { 100 let timer = timer!("Process:setup"); 101 102 // Initialize the process. 103 let mut process = 104 Self { universal_srs: UniversalSRS::load()?, stacks: Default::default(), old_stacks: Default::default() }; 105 lap!(timer, "Initialize process"); 106 107 // Initialize the 'credits.delta' program. 108 let program = Program::credits()?; 109 lap!(timer, "Load credits program"); 110 111 // Compute the 'credits.delta' program stack. 112 let stack = Stack::new(&process, &program)?; 113 lap!(timer, "Initialize stack"); 114 115 // Synthesize the 'credits.delta' circuit keys. 116 for function_name in program.functions().keys() { 117 stack.synthesize_key::<A, _>(function_name, rng)?; 118 lap!(timer, "Synthesize circuit keys for {function_name}"); 119 } 120 lap!(timer, "Synthesize credits program keys"); 121 122 // Add the 'credits.delta' stack to the process. 123 process.add_stack(stack); 124 125 finish!(timer); 126 // Return the process. 127 Ok(process) 128 } 129 130 /// Adds a new stack to the process. 131 /// If the program already exists, then the existing stack is replaced and the original stack is returned. 132 /// Note. This method assumes that the provided stack is valid. 133 #[inline] 134 pub fn add_stack(&mut self, stack: Stack<N>) -> Option<Arc<Stack<N>>> { 135 // Get the program ID. 136 let program_id = *stack.program_id(); 137 // Arc the stack first to limit the scope of the write lock. 138 let stack = Arc::new(stack); 139 // Insert the stack into the process, replacing the existing stack if it exists. 140 self.stacks.write().insert(program_id, stack) 141 } 142 143 /// Stages a stack to be added to the process. 144 /// The new stack is active, while the old stack is retained in `old_stacks`. 145 /// The `commit_stacks` method must be called to finalize the addition of the new stack. 146 /// The `revert_stacks` method can be called to revert the staged stacks. 147 #[inline] 148 pub fn stage_stack(&self, stack: Stack<N>) { 149 // Get the program ID. 150 let program_id = *stack.program_id(); 151 // Arc the stack first to limit the scope of the write lock. 152 let stack = Arc::new(stack); 153 // If no entry in `old_stacks` exists for `program_id`, store the old stack. 154 // Note: If `old_stack` is `None`, it means that we are adding a new program to the process. 155 let old_stack = self.stacks.write().insert(program_id, stack); 156 let mut old_stacks = self.old_stacks.write(); 157 if !old_stacks.contains_key(&program_id) { 158 old_stacks.insert(program_id, old_stack); 159 } 160 } 161 162 /// Commits the staged stacks to the process. 163 /// This finalizes the addition of the new stacks and clears the old stacks. 164 #[inline] 165 pub fn commit_stacks(&self) { 166 // Clear the old stacks. 167 self.old_stacks.write().clear(); 168 } 169 170 /// Reverts the staged stacks, restoring the previous state of the process. 171 /// This will remove the new stacks and restore the old stacks. 172 #[inline] 173 pub fn revert_stacks(&self) { 174 // Restore the old stacks. 175 for (program_id, stack) in self.old_stacks.write().drain(..) { 176 // If the stack is `None`, remove the program from the process. 177 // Otherwise, insert the old stack back into the process. 178 if let Some(stack) = stack { 179 self.stacks.write().insert(program_id, stack); 180 } else { 181 self.stacks.write().shift_remove(&program_id); 182 } 183 } 184 } 185 } 186 187 impl<N: Network> Process<N> { 188 /// Initializes a new process. 189 #[inline] 190 pub fn load() -> Result<Self> { 191 let timer = timer!("Process::load"); 192 193 // Initialize the process. 194 let mut process = 195 Self { universal_srs: UniversalSRS::load()?, stacks: Default::default(), old_stacks: Default::default() }; 196 lap!(timer, "Initialize process"); 197 198 // Initialize the 'credits.delta' program. 199 let program = Program::credits()?; 200 lap!(timer, "Load credits program"); 201 202 // Compute the 'credits.delta' program stack. 203 let stack = Stack::new(&process, &program)?; 204 lap!(timer, "Initialize stack"); 205 206 // Synthesize the 'credits.delta' verifying keys. 207 for function_name in program.functions().keys() { 208 // Load the verifying key. 209 let verifying_key = N::get_credits_verifying_key(function_name.to_string())?; 210 // Retrieve the number of public and private variables. 211 // Note: This number does *NOT* include the number of constants. This is safe because 212 // this program is never deployed, as it is a first-class citizen of the protocol. 213 let num_variables = verifying_key.circuit_info.num_public_and_private_variables as u64; 214 // Insert the verifying key. 215 stack.insert_verifying_key(function_name, VerifyingKey::new(verifying_key.clone(), num_variables))?; 216 lap!(timer, "Load verifying key for {function_name}"); 217 } 218 lap!(timer, "Load circuit keys"); 219 220 // Add the stack to the process. 221 process.add_stack(stack); 222 223 finish!(timer, "Process::load"); 224 // Return the process. 225 Ok(process) 226 } 227 228 /// Initializes a new process with the V0 credits.delta verifiying keys. 229 #[inline] 230 pub fn load_v0() -> Result<Self> { 231 let timer = timer!("Process::load_v0"); 232 233 // Initialize the process. 234 let mut process = 235 Self { universal_srs: UniversalSRS::load()?, stacks: Default::default(), old_stacks: Default::default() }; 236 lap!(timer, "Initialize process"); 237 238 // Initialize the 'credits.delta' program. 239 let program = Program::credits()?; 240 lap!(timer, "Load credits program"); 241 242 // Compute the 'credits.delta' program stack. 243 let stack = Stack::new(&process, &program)?; 244 lap!(timer, "Initialize stack"); 245 246 // Synthesize the 'credits.delta' verifying keys. 247 for function_name in program.functions().keys() { 248 // Load the verifying key. 249 let verifying_key = N::get_credits_v0_verifying_key(function_name.to_string())?; 250 // Retrieve the number of public and private variables. 251 // Note: This number does *NOT* include the number of constants. This is safe because 252 // this program is never deployed, as it is a first-class citizen of the protocol. 253 let num_variables = verifying_key.circuit_info.num_public_and_private_variables as u64; 254 // Insert the verifying key. 255 stack.insert_verifying_key(function_name, VerifyingKey::new(verifying_key.clone(), num_variables))?; 256 lap!(timer, "Load verifying key for {function_name}"); 257 } 258 lap!(timer, "Load circuit keys"); 259 260 // Add the stack to the process. 261 process.add_stack(stack); 262 263 finish!(timer, "Process::load_v0"); 264 // Return the process. 265 Ok(process) 266 } 267 268 /// Initializes a new process without downloading the 'credits.delta' circuit keys (for web contexts). 269 #[inline] 270 #[cfg(feature = "wasm")] 271 pub fn load_web() -> Result<Self> { 272 // Initialize the process. 273 let mut process = 274 Self { universal_srs: UniversalSRS::load()?, stacks: Default::default(), old_stacks: Default::default() }; 275 276 // Initialize the 'credits.delta' program. 277 let program = Program::credits()?; 278 279 // Compute the 'credits.delta' program stack. 280 let stack = Stack::new(&process, &program)?; 281 282 // Add the stack to the process. 283 process.add_stack(stack); 284 285 // Return the process. 286 Ok(process) 287 } 288 289 /// Adds a new program to the process, verifying that it is a valid addition. 290 /// If the program exists, then the existing stack is replaced and discarded. 291 /// Note. This method should **NOT** be used by the on-chain VM to add new program, use `finalize_deployment` or `load_deployment` instead instead. 292 #[inline] 293 pub fn add_program(&mut self, program: &Program<N>) -> Result<()> { 294 // Initialize the 'credits.delta' program ID. 295 let credits_program_id = ProgramID::<N>::from_str("credits.delta")?; 296 // If the program is not 'credits.delta', compute the program stack, and add it to the process. 297 if program.id() != &credits_program_id { 298 self.add_stack(Stack::new(self, program)?); 299 } 300 Ok(()) 301 } 302 303 /// Adds a new program with the given edition to the process, verifying that it is a valid addition. 304 /// If the program exists, then the existing stack is replaced and discarded. 305 /// Note. This method should **NOT** be used by the on-chain VM to add new program, use `finalize_deployment` or `load_deployment` instead instead. 306 #[inline] 307 pub fn add_program_with_edition(&mut self, program: &Program<N>, edition: u16) -> Result<()> { 308 // Initialize the 'credits.delta' program ID. 309 let credits_program_id = ProgramID::<N>::from_str("credits.delta")?; 310 // If the program is not 'credits.delta', compute the program stack, and add it to the process. 311 if program.id() != &credits_program_id { 312 let stack = Stack::new_raw(self, program, edition)?; 313 stack.initialize_and_check(self)?; 314 self.add_stack(stack); 315 } 316 Ok(()) 317 } 318 319 /// Adds a set of programs and editions, in topological order, to the process, deferring validation of the programs until all programs are added. 320 /// If a program exists, then the existing stack is replaced and discarded. 321 /// Either all programs are added or none are. 322 /// Note. This method should **NOT** be used by the on-chain VM to add new program, use `finalize_deployment` or `load_deployment` instead instead. 323 #[inline] 324 pub fn add_programs_with_editions(&mut self, programs: &[(Program<N>, u16)]) -> Result<()> { 325 // Initialize the 'credits.delta' program ID. 326 let credits_program_id = ProgramID::<N>::from_str("credits.delta")?; 327 // Defer cleanup of the uncommitted stacks. 328 defer! { 329 self.revert_stacks() 330 } 331 // Initialize raw stacks for each of the programs, skipping `credits.delta`. 332 for (program, edition) in programs { 333 if program.id() != &credits_program_id { 334 self.stage_stack(Stack::new_raw(self, program, *edition)?) 335 } 336 } 337 // For each stack, check and initialize it before adding it to the process. 338 for (program, _) in programs { 339 // Retrieve the stack. 340 let stack = self.get_stack(program.id())?; 341 // Initialize and check the stack for well-formedness. 342 stack.initialize_and_check(self)?; 343 } 344 // Commit the staged stacks. 345 self.commit_stacks(); 346 Ok(()) 347 } 348 349 /// Returns the universal SRS. 350 #[inline] 351 pub const fn universal_srs(&self) -> &UniversalSRS<N> { 352 &self.universal_srs 353 } 354 355 /// Returns `true` if the process contains the program with the given ID. 356 #[inline] 357 pub fn contains_program(&self, program_id: &ProgramID<N>) -> bool { 358 self.stacks.read().contains_key(program_id) 359 } 360 361 /// Returns the program IDs of all programs in the process. 362 #[inline] 363 pub fn program_ids(&self) -> Vec<ProgramID<N>> { 364 self.stacks.read().keys().copied().collect() 365 } 366 367 /// Returns the stack for the given program ID. 368 #[inline] 369 pub fn get_stack(&self, program_id: impl TryInto<ProgramID<N>>) -> Result<Arc<Stack<N>>> { 370 // Prepare the program ID. 371 let program_id = program_id.try_into().map_err(|_| anyhow!("Invalid program ID"))?; 372 // Retrieve the stack. 373 let stack = self 374 .stacks 375 .read() 376 .get(&program_id) 377 .ok_or_else(|| anyhow!("Program '{program_id}' does not exist"))? 378 .clone(); 379 // Ensure the program ID matches. 380 ensure!(stack.program_id() == &program_id, "Expected program '{}', found '{program_id}'", stack.program_id()); 381 // Return the stack. 382 Ok(stack) 383 } 384 385 /// Returns the proving key for the given program ID and function name. 386 #[inline] 387 pub fn get_proving_key( 388 &self, 389 program_id: impl TryInto<ProgramID<N>>, 390 function_name: impl TryInto<Identifier<N>>, 391 ) -> Result<ProvingKey<N>> { 392 // Prepare the function name. 393 let function_name = function_name.try_into().map_err(|_| anyhow!("Invalid function name"))?; 394 // Return the proving key. 395 self.get_stack(program_id)?.get_proving_key(&function_name) 396 } 397 398 /// Returns the verifying key for the given program ID and function name. 399 #[inline] 400 pub fn get_verifying_key( 401 &self, 402 program_id: impl TryInto<ProgramID<N>>, 403 function_name: impl TryInto<Identifier<N>>, 404 ) -> Result<VerifyingKey<N>> { 405 // Prepare the function name. 406 let function_name = function_name.try_into().map_err(|_| anyhow!("Invalid function name"))?; 407 // Return the verifying key. 408 self.get_stack(program_id)?.get_verifying_key(&function_name) 409 } 410 411 /// Inserts the given proving key, for the given program ID and function name. 412 #[inline] 413 pub fn insert_proving_key( 414 &self, 415 program_id: &ProgramID<N>, 416 function_name: &Identifier<N>, 417 proving_key: ProvingKey<N>, 418 ) -> Result<()> { 419 self.get_stack(program_id)?.insert_proving_key(function_name, proving_key) 420 } 421 422 /// Removes the given proving key, for the given program ID and function name. 423 #[inline] 424 pub fn remove_proving_key(&self, program_id: &ProgramID<N>, function_name: &Identifier<N>) -> Result<()> { 425 self.get_stack(program_id)?.remove_proving_key(function_name); 426 Ok(()) 427 } 428 429 /// Inserts the given verifying key, for the given program ID and function name. 430 #[inline] 431 pub fn insert_verifying_key( 432 &self, 433 program_id: &ProgramID<N>, 434 function_name: &Identifier<N>, 435 verifying_key: VerifyingKey<N>, 436 ) -> Result<()> { 437 self.get_stack(program_id)?.insert_verifying_key(function_name, verifying_key) 438 } 439 440 /// Removes the given verifying key, for the given program ID and function name. 441 #[inline] 442 pub fn remove_verifying_key(&self, program_id: &ProgramID<N>, function_name: &Identifier<N>) -> Result<()> { 443 self.get_stack(program_id)?.remove_verifying_key(function_name); 444 Ok(()) 445 } 446 447 /// Synthesizes the proving and verifying key for the given program ID and function name. 448 #[inline] 449 pub fn synthesize_key<A: circuit::Alpha<Network = N>, R: Rng + CryptoRng>( 450 &self, 451 program_id: &ProgramID<N>, 452 function_name: &Identifier<N>, 453 rng: &mut R, 454 ) -> Result<()> { 455 // Synthesize the proving and verifying key. 456 self.get_stack(program_id)?.synthesize_key::<A, R>(function_name, rng) 457 } 458 } 459 460 #[cfg(test)] 461 pub mod test_helpers { 462 use super::*; 463 use deltavm_ledger_block::Transition; 464 use deltavm_ledger_query::Query; 465 use deltavm_ledger_store::{BlockStore, helpers::memory::BlockMemory}; 466 use deltavm_synthesizer_program::Program; 467 use console::{account::PrivateKey, network::MainnetV0, program::Identifier}; 468 469 use alphastd::StorageMode; 470 use std::sync::OnceLock; 471 472 type CurrentNetwork = MainnetV0; 473 type CurrentAlpha = circuit::network::AlphaV0; 474 475 /// Returns an execution for the given program and function name. 476 pub fn get_execution( 477 process: &mut Process<CurrentNetwork>, 478 program: &Program<CurrentNetwork>, 479 function_name: &Identifier<CurrentNetwork>, 480 inputs: impl ExactSizeIterator<Item = impl TryInto<Value<CurrentNetwork>>>, 481 ) -> Execution<CurrentNetwork> { 482 // Initialize a new rng. 483 let rng = &mut TestRng::default(); 484 485 // Initialize a private key. 486 let private_key = PrivateKey::new(rng).unwrap(); 487 488 // Add the program to the process if doesn't yet exist. 489 if !process.contains_program(program.id()) { 490 process.add_program(program).unwrap(); 491 } 492 493 // Compute the authorization. 494 let authorization = 495 process.authorize::<CurrentAlpha, _>(&private_key, program.id(), function_name, inputs, rng).unwrap(); 496 497 // Execute the program. 498 let (_, mut trace) = process.execute::<CurrentAlpha, _>(authorization, rng).unwrap(); 499 500 // Initialize a new block store. 501 let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap(); 502 503 // Prepare the assignments from the block store. 504 trace.prepare(&deltavm_ledger_query::Query::from(block_store)).unwrap(); 505 506 // Get the locator. 507 let locator = format!("{:?}:{function_name:?}", program.id()); 508 509 // Return the execution object. 510 trace.prove_execution::<CurrentAlpha, _>(&locator, VarunaVersion::V1, rng).unwrap() 511 } 512 513 pub fn sample_key() -> (Identifier<CurrentNetwork>, ProvingKey<CurrentNetwork>, VerifyingKey<CurrentNetwork>) { 514 static INSTANCE: OnceLock<( 515 Identifier<CurrentNetwork>, 516 ProvingKey<CurrentNetwork>, 517 VerifyingKey<CurrentNetwork>, 518 )> = OnceLock::new(); 519 INSTANCE 520 .get_or_init(|| { 521 // Initialize a new program. 522 let (string, program) = Program::<CurrentNetwork>::parse( 523 r" 524 program testing.delta; 525 526 function compute: 527 input r0 as u32.private; 528 input r1 as u32.public; 529 add r0 r1 into r2; 530 output r2 as u32.public;", 531 ) 532 .unwrap(); 533 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'"); 534 535 // Declare the function name. 536 let function_name = Identifier::from_str("compute").unwrap(); 537 538 // Initialize the RNG. 539 let rng = &mut TestRng::default(); 540 541 // Construct the process. 542 let process = sample_process(&program); 543 544 // Synthesize a proving and verifying key. 545 process.synthesize_key::<CurrentAlpha, _>(program.id(), &function_name, rng).unwrap(); 546 547 // Get the proving and verifying key. 548 let proving_key = process.get_proving_key(program.id(), function_name).unwrap(); 549 let verifying_key = process.get_verifying_key(program.id(), function_name).unwrap(); 550 551 (function_name, proving_key, verifying_key) 552 }) 553 .clone() 554 } 555 556 pub(crate) fn sample_execution() -> Execution<CurrentNetwork> { 557 static INSTANCE: OnceLock<Execution<CurrentNetwork>> = OnceLock::new(); 558 INSTANCE 559 .get_or_init(|| { 560 // Initialize a new program. 561 let (string, program) = Program::<CurrentNetwork>::parse( 562 r" 563 program testing.delta; 564 565 function compute: 566 input r0 as u32.private; 567 input r1 as u32.public; 568 add r0 r1 into r2; 569 output r2 as u32.public;", 570 ) 571 .unwrap(); 572 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'"); 573 574 // Declare the function name. 575 let function_name = Identifier::from_str("compute").unwrap(); 576 577 // Initialize the RNG. 578 let rng = &mut TestRng::default(); 579 // Initialize a new caller account. 580 let caller_private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap(); 581 582 // Initialize a new block store. 583 let block_store = 584 BlockStore::<CurrentNetwork, BlockMemory<_>>::open(StorageMode::new_test(None)).unwrap(); 585 586 // Construct the process. 587 let process = sample_process(&program); 588 // Authorize the function call. 589 let authorization = process 590 .authorize::<CurrentAlpha, _>( 591 &caller_private_key, 592 program.id(), 593 function_name, 594 ["5u32", "10u32"].into_iter(), 595 rng, 596 ) 597 .unwrap(); 598 assert_eq!(authorization.len(), 1); 599 // Execute the request. 600 let (_response, mut trace) = process.execute::<CurrentAlpha, _>(authorization, rng).unwrap(); 601 assert_eq!(trace.transitions().len(), 1); 602 603 // Prepare the trace. 604 trace.prepare(&Query::from(block_store)).unwrap(); 605 // Compute the execution. 606 trace.prove_execution::<CurrentAlpha, _>("testing", VarunaVersion::V1, rng).unwrap() 607 }) 608 .clone() 609 } 610 611 pub fn sample_transition() -> Transition<CurrentNetwork> { 612 // Retrieve the execution. 613 let mut execution = sample_execution(); 614 // Ensure the execution is not empty. 615 assert!(!execution.is_empty()); 616 // Return the transition. 617 execution.pop().unwrap() 618 } 619 620 /// Initializes a new process with the given program. 621 pub(crate) fn sample_process(program: &Program<CurrentNetwork>) -> Process<CurrentNetwork> { 622 // Construct a new process. 623 let mut process = Process::load().unwrap(); 624 // Add the program to the process. 625 process.add_program(program).unwrap(); 626 // Return the process. 627 process 628 } 629 }