diff --git a/circuit/environment/src/circuit.rs b/circuit/environment/src/circuit.rs index be60fe946b..1fde647271 100644 --- a/circuit/environment/src/circuit.rs +++ b/circuit/environment/src/circuit.rs @@ -22,6 +22,7 @@ use core::{ type Field = ::Field; thread_local! { + pub(super) static CONSTRAINT_LIMIT: Cell> = Cell::new(None); pub(super) static CIRCUIT: RefCell> = RefCell::new(R1CS::new()); pub(super) static IN_WITNESS: Cell = Cell::new(false); pub(super) static ZERO: LinearCombination = LinearCombination::zero(); @@ -146,6 +147,15 @@ impl Environment for Circuit { // Ensure we are not in witness mode. if !in_witness.get() { CIRCUIT.with(|circuit| { + // Ensure that we do not surpass the constraint limit for the circuit. + CONSTRAINT_LIMIT.with(|constraint_limit| { + if let Some(limit) = constraint_limit.get() { + if circuit.borrow().num_constraints() > limit { + Self::halt(format!("Surpassed the constraint limit ({limit})")) + } + } + }); + let (a, b, c) = constraint(); let (a, b, c) = (a.into(), b.into(), c.into()); @@ -248,8 +258,16 @@ impl Environment for Circuit { panic!("{}", &error) } - /// TODO (howardwu): Abstraction - Refactor this into an appropriate design. - /// Circuits should not have easy access to this during synthesis. + /// Returns the constraint limit for the circuit, if one exists. + fn get_constraint_limit() -> Option { + CONSTRAINT_LIMIT.with(|current_limit| current_limit.get()) + } + + /// Sets the constraint limit for the circuit. + fn set_constraint_limit(limit: Option) { + CONSTRAINT_LIMIT.with(|current_limit| current_limit.replace(limit)); + } + /// Returns the R1CS circuit, resetting the circuit. fn inject_r1cs(r1cs: R1CS) { CIRCUIT.with(|circuit| { @@ -268,13 +286,13 @@ impl Environment for Circuit { }) } - /// TODO (howardwu): Abstraction - Refactor this into an appropriate design. - /// Circuits should not have easy access to this during synthesis. /// Returns the R1CS circuit, resetting the circuit. fn eject_r1cs_and_reset() -> R1CS { CIRCUIT.with(|circuit| { // Reset the witness mode. IN_WITNESS.with(|in_witness| in_witness.replace(false)); + // Reset the constraint limit. + Self::set_constraint_limit(None); // Eject the R1CS instance. let r1cs = circuit.replace(R1CS::<::BaseField>::new()); // Ensure the circuit is now empty. @@ -287,13 +305,13 @@ impl Environment for Circuit { }) } - /// TODO (howardwu): Abstraction - Refactor this into an appropriate design. - /// Circuits should not have easy access to this during synthesis. /// Returns the R1CS assignment of the circuit, resetting the circuit. fn eject_assignment_and_reset() -> Assignment<::Field> { CIRCUIT.with(|circuit| { // Reset the witness mode. IN_WITNESS.with(|in_witness| in_witness.replace(false)); + // Reset the constraint limit. + Self::set_constraint_limit(None); // Eject the R1CS instance. let r1cs = circuit.replace(R1CS::<::BaseField>::new()); assert_eq!(0, circuit.borrow().num_constants()); @@ -310,6 +328,9 @@ impl Environment for Circuit { CIRCUIT.with(|circuit| { // Reset the witness mode. IN_WITNESS.with(|in_witness| in_witness.replace(false)); + // Reset the constraint limit. + Self::set_constraint_limit(None); + // Reset the circuit. *circuit.borrow_mut() = R1CS::<::BaseField>::new(); assert_eq!(0, circuit.borrow().num_constants()); assert_eq!(1, circuit.borrow().num_public()); diff --git a/circuit/environment/src/environment.rs b/circuit/environment/src/environment.rs index 34ef4370ea..d8fdc5a181 100644 --- a/circuit/environment/src/environment.rs +++ b/circuit/environment/src/environment.rs @@ -160,6 +160,12 @@ pub trait Environment: 'static + Copy + Clone + fmt::Debug + fmt::Display + Eq + ::halt(message) } + /// Returns the constraint limit for the circuit, if one exists. + fn get_constraint_limit() -> Option; + + /// Sets the constraint limit for the circuit. + fn set_constraint_limit(limit: Option); + /// Returns the R1CS circuit, resetting the circuit. fn inject_r1cs(r1cs: R1CS); diff --git a/circuit/network/src/v0.rs b/circuit/network/src/v0.rs index 9dc7a9d8da..936e1e581c 100644 --- a/circuit/network/src/v0.rs +++ b/circuit/network/src/v0.rs @@ -466,6 +466,16 @@ impl Environment for AleoV0 { E::halt(message) } + /// Returns the constraint limit for the circuit, if one exists. + fn get_constraint_limit() -> Option { + E::get_constraint_limit() + } + + /// Sets the constraint limit for the circuit. + fn set_constraint_limit(limit: Option) { + E::set_constraint_limit(limit) + } + /// Returns the R1CS circuit, resetting the circuit. fn inject_r1cs(r1cs: R1CS) { E::inject_r1cs(r1cs) diff --git a/console/network/src/lib.rs b/console/network/src/lib.rs index 717946cba2..4995a5f577 100644 --- a/console/network/src/lib.rs +++ b/console/network/src/lib.rs @@ -98,6 +98,10 @@ pub trait Network: const STARTING_SUPPLY: u64 = 1_500_000_000_000_000; // 1.5B credits /// The cost in microcredits per byte for the deployment transaction. const DEPLOYMENT_FEE_MULTIPLIER: u64 = 1_000; // 1 millicredit per byte + /// The cost in microcredits per constraint for the deployment transaction. + const SYNTHESIS_FEE_MULTIPLIER: u64 = 25; // 25 microcredits per constraint + /// The maximum number of constraints in a deployment. + const MAX_DEPLOYMENT_LIMIT: u64 = 1 << 20; // 1,048,576 constraints /// The maximum number of microcredits that can be spent as a fee. const MAX_FEE: u64 = 1_000_000_000_000_000; diff --git a/ledger/block/src/transaction/deployment/mod.rs b/ledger/block/src/transaction/deployment/mod.rs index 01d4cac081..fbea825444 100644 --- a/ledger/block/src/transaction/deployment/mod.rs +++ b/ledger/block/src/transaction/deployment/mod.rs @@ -124,6 +124,23 @@ impl Deployment { &self.verifying_keys } + /// Returns the sum of the constraint counts for all functions in this deployment. + pub fn num_combined_constraints(&self) -> Result { + // Initialize the accumulator. + let mut num_combined_constraints = 0u64; + // Iterate over the functions. + for (_, (vk, _)) in &self.verifying_keys { + // Add the number of constraints. + // Note: This method must be *checked* because the claimed constraint count + // is from the user, not the synthesizer. + num_combined_constraints = num_combined_constraints + .checked_add(vk.circuit_info.num_constraints as u64) + .ok_or_else(|| anyhow!("Overflow when counting constraints for '{}'", self.program_id()))?; + } + // Return the number of combined constraints. + Ok(num_combined_constraints) + } + /// Returns the deployment ID. pub fn to_deployment_id(&self) -> Result> { Ok(*Transaction::deployment_tree(self, None)?.root()) diff --git a/synthesizer/process/src/stack/call/mod.rs b/synthesizer/process/src/stack/call/mod.rs index a2c804e33d..69e7349685 100644 --- a/synthesizer/process/src/stack/call/mod.rs +++ b/synthesizer/process/src/stack/call/mod.rs @@ -382,6 +382,11 @@ impl CallTrait for Call { // Inject the existing circuit. A::inject_r1cs(r1cs); + // If in 'CheckDeployment' mode, set the expected constraint limit. + if let CallStack::CheckDeployment(_, _, _, constraint_limit) = ®isters.call_stack() { + A::set_constraint_limit(*constraint_limit); + } + use circuit::Inject; // Inject the network ID as `Mode::Constant`. diff --git a/synthesizer/process/src/stack/deploy.rs b/synthesizer/process/src/stack/deploy.rs index 79803d91b9..be662d134f 100644 --- a/synthesizer/process/src/stack/deploy.rs +++ b/synthesizer/process/src/stack/deploy.rs @@ -73,6 +73,9 @@ impl Stack { let program_id = self.program.id(); + // Check that the number of combined constraints does not exceed the deployment limit. + ensure!(deployment.num_combined_constraints()? <= N::MAX_DEPLOYMENT_LIMIT); + // Construct the call stacks and assignments used to verify the certificates. let mut call_stacks = Vec::with_capacity(deployment.verifying_keys().len()); @@ -82,8 +85,16 @@ impl Stack { // The `caller` is `None` when verifying the deployment of an individual circuit. let caller = None; + // Check that the number of functions matches the number of verifying keys. + ensure!( + deployment.program().functions().len() == deployment.verifying_keys().len(), + "The number of functions in the program does not match the number of verifying keys" + ); + // Iterate through the program functions and construct the callstacks and corresponding assignments. - for function in deployment.program().functions().values() { + for (function, (_, (verifying_key, _))) in + deployment.program().functions().values().zip_eq(deployment.verifying_keys()) + { // Initialize a burner private key. let burner_private_key = PrivateKey::new(rng)?; // Compute the burner address. @@ -121,18 +132,28 @@ impl Stack { lap!(timer, "Compute the request for {}", function.name()); // Initialize the assignments. let assignments = Assignments::::default(); + // Initialize the constraint limit. Account for the constraint added after synthesis that makes the Varuna zerocheck hiding. + let Some(constraint_limit) = verifying_key.circuit_info.num_constraints.checked_sub(1) else { + // Since a deployment must always pay non-zero fee, it must always have at least one constraint. + bail!("The constraint limit of 0 for function '{}' is invalid", function.name()); + }; // Initialize the call stack. - let call_stack = CallStack::CheckDeployment(vec![request], burner_private_key, assignments.clone()); + let call_stack = CallStack::CheckDeployment( + vec![request], + burner_private_key, + assignments.clone(), + Some(constraint_limit as u64), + ); // Append the function name, callstack, and assignments. call_stacks.push((function.name(), call_stack, assignments)); } // Verify the certificates. let rngs = (0..call_stacks.len()).map(|_| StdRng::from_seed(rng.gen())).collect::>(); - cfg_iter!(call_stacks).zip_eq(deployment.verifying_keys()).zip_eq(rngs).try_for_each( + cfg_into_iter!(call_stacks).zip_eq(deployment.verifying_keys()).zip_eq(rngs).try_for_each( |(((function_name, call_stack, assignments), (_, (verifying_key, certificate))), mut rng)| { // Synthesize the circuit. - if let Err(err) = self.execute_function::(call_stack.clone(), caller, root_tvk, &mut rng) { + if let Err(err) = self.execute_function::(call_stack, caller, root_tvk, &mut rng) { bail!("Failed to synthesize the circuit for '{function_name}': {err}") } // Check the certificate. diff --git a/synthesizer/process/src/stack/execute.rs b/synthesizer/process/src/stack/execute.rs index 7e165a9fb6..015dd57fb0 100644 --- a/synthesizer/process/src/stack/execute.rs +++ b/synthesizer/process/src/stack/execute.rs @@ -143,6 +143,11 @@ impl StackExecute for Stack { // Ensure the circuit environment is clean. A::reset(); + // If in 'CheckDeployment' mode, set the constraint limit. + if let CallStack::CheckDeployment(_, _, _, constraint_limit) = &call_stack { + A::set_constraint_limit(*constraint_limit); + } + // Retrieve the next request. let console_request = call_stack.pop()?; @@ -431,7 +436,7 @@ impl StackExecute for Stack { lap!(timer, "Save the transition"); } // If the circuit is in `CheckDeployment` mode, then save the assignment. - else if let CallStack::CheckDeployment(_, _, ref assignments) = registers.call_stack() { + else if let CallStack::CheckDeployment(_, _, ref assignments, _) = registers.call_stack() { // Construct the call metrics. let metrics = CallMetrics { program_id: *self.program_id(), diff --git a/synthesizer/process/src/stack/mod.rs b/synthesizer/process/src/stack/mod.rs index 3c8ec34874..a6f9ff58f5 100644 --- a/synthesizer/process/src/stack/mod.rs +++ b/synthesizer/process/src/stack/mod.rs @@ -81,7 +81,7 @@ pub type Assignments = Arc pub enum CallStack { Authorize(Vec>, PrivateKey, Authorization), Synthesize(Vec>, PrivateKey, Authorization), - CheckDeployment(Vec>, PrivateKey, Assignments), + CheckDeployment(Vec>, PrivateKey, Assignments, Option), Evaluate(Authorization), Execute(Authorization, Arc>>), PackageRun(Vec>, PrivateKey, Assignments), @@ -109,11 +109,14 @@ impl CallStack { CallStack::Synthesize(requests, private_key, authorization) => { CallStack::Synthesize(requests.clone(), *private_key, authorization.replicate()) } - CallStack::CheckDeployment(requests, private_key, assignments) => CallStack::CheckDeployment( - requests.clone(), - *private_key, - Arc::new(RwLock::new(assignments.read().clone())), - ), + CallStack::CheckDeployment(requests, private_key, assignments, constraint_limit) => { + CallStack::CheckDeployment( + requests.clone(), + *private_key, + Arc::new(RwLock::new(assignments.read().clone())), + *constraint_limit, + ) + } CallStack::Evaluate(authorization) => CallStack::Evaluate(authorization.replicate()), CallStack::Execute(authorization, trace) => { CallStack::Execute(authorization.replicate(), Arc::new(RwLock::new(trace.read().clone()))) diff --git a/synthesizer/process/src/tests/test_credits.rs b/synthesizer/process/src/tests/test_credits.rs index cfebf638d5..d18259e6cb 100644 --- a/synthesizer/process/src/tests/test_credits.rs +++ b/synthesizer/process/src/tests/test_credits.rs @@ -1531,7 +1531,7 @@ mod sanity_checks { // Initialize the assignments. let assignments = Assignments::::default(); // Initialize the call stack. - let call_stack = CallStack::CheckDeployment(vec![request], *private_key, assignments.clone()); + let call_stack = CallStack::CheckDeployment(vec![request], *private_key, assignments.clone(), None); // Synthesize the circuit. let _response = stack.execute_function::(call_stack, None, None, rng).unwrap(); // Retrieve the assignment. diff --git a/synthesizer/src/vm/deploy.rs b/synthesizer/src/vm/deploy.rs index e080ba54d9..f9e260793c 100644 --- a/synthesizer/src/vm/deploy.rs +++ b/synthesizer/src/vm/deploy.rs @@ -40,7 +40,7 @@ impl> VM { let owner = ProgramOwner::new(private_key, deployment_id, rng)?; // Compute the minimum deployment cost. - let (minimum_deployment_cost, (_, _)) = deployment_cost(&deployment)?; + let (minimum_deployment_cost, _) = deployment_cost(&deployment)?; // Authorize the fee. let fee_authorization = match fee_record { Some(record) => self.authorize_fee_private( diff --git a/synthesizer/src/vm/helpers/cost.rs b/synthesizer/src/vm/helpers/cost.rs index e3a962b03a..bcee10e70d 100644 --- a/synthesizer/src/vm/helpers/cost.rs +++ b/synthesizer/src/vm/helpers/cost.rs @@ -24,20 +24,25 @@ use ledger_block::{Deployment, Execution}; use ledger_store::ConsensusStorage; use synthesizer_program::{CastType, Command, Finalize, Instruction, Operand, StackProgram}; -/// Returns the *minimum* cost in microcredits to publish the given deployment (total cost, (storage cost, namespace cost)). -pub fn deployment_cost(deployment: &Deployment) -> Result<(u64, (u64, u64))> { +/// Returns the *minimum* cost in microcredits to publish the given deployment (total cost, (storage cost, synthesis cost, namespace cost)). +pub fn deployment_cost(deployment: &Deployment) -> Result<(u64, (u64, u64, u64))> { // Determine the number of bytes in the deployment. let size_in_bytes = deployment.size_in_bytes()?; // Retrieve the program ID. let program_id = deployment.program_id(); // Determine the number of characters in the program ID. let num_characters = u32::try_from(program_id.name().to_string().len())?; + // Compute the number of combined constraints in the program. + let num_combined_constraints = deployment.num_combined_constraints()?; // Compute the storage cost in microcredits. let storage_cost = size_in_bytes .checked_mul(N::DEPLOYMENT_FEE_MULTIPLIER) .ok_or(anyhow!("The storage cost computation overflowed for a deployment"))?; + // Compute the synthesis cost in microcredits. + let synthesis_cost = num_combined_constraints * N::SYNTHESIS_FEE_MULTIPLIER; + // Compute the namespace cost in credits: 10^(10 - num_characters). let namespace_cost = 10u64 .checked_pow(10u32.saturating_sub(num_characters)) @@ -46,13 +51,14 @@ pub fn deployment_cost(deployment: &Deployment) -> Result<(u64, ( // Compute the total cost in microcredits. let total_cost = storage_cost - .checked_add(namespace_cost) + .checked_add(synthesis_cost) + .and_then(|x| x.checked_add(namespace_cost)) .ok_or(anyhow!("The total cost computation overflowed for a deployment"))?; - Ok((total_cost, (storage_cost, namespace_cost))) + Ok((total_cost, (storage_cost, synthesis_cost, namespace_cost))) } -/// Returns the *minimum* cost in microcredits to publish the given execution (total cost, (storage cost, namespace cost)). +/// Returns the *minimum* cost in microcredits to publish the given execution (total cost, (storage cost, finalize cost)). pub fn execution_cost>( vm: &VM, execution: &Execution, diff --git a/synthesizer/src/vm/mod.rs b/synthesizer/src/vm/mod.rs index 13a2add918..a24d2c89c0 100644 --- a/synthesizer/src/vm/mod.rs +++ b/synthesizer/src/vm/mod.rs @@ -374,6 +374,7 @@ pub(crate) mod test_helpers { use indexmap::IndexMap; use once_cell::sync::OnceCell; use std::borrow::Borrow; + use synthesizer_snark::VerifyingKey; pub(crate) type CurrentNetwork = Testnet3; @@ -950,7 +951,7 @@ function a: // Note: `deployment_transaction_ids` is sorted lexicographically by transaction ID, so the order may change if we update internal methods. assert_eq!( deployment_transaction_ids, - vec![deployment_4.id(), deployment_1.id(), deployment_2.id(), deployment_3.id()], + vec![deployment_2.id(), deployment_1.id(), deployment_4.id(), deployment_3.id()], "Update me if serialization has changed" ); } @@ -1122,6 +1123,157 @@ function transfer: assert!(vm.contains_program(&ProgramID::from_str("test_program.aleo").unwrap())); } + #[test] + fn test_deployment_synthesis_overload() { + let rng = &mut TestRng::default(); + + // Initialize a private key. + let private_key = sample_genesis_private_key(rng); + + // Initialize the genesis block. + let genesis = sample_genesis_block(rng); + + // Initialize the VM. + let vm = sample_vm(); + // Update the VM. + vm.add_next_block(&genesis).unwrap(); + + // Deploy the base program. + let program = Program::from_str( + r" +program synthesis_overload.aleo; + +function do: + input r0 as [[u128; 32u32]; 2u32].private; + hash.sha3_256 r0 into r1 as field; + output r1 as field.public;", + ) + .unwrap(); + + // Create the deployment transaction. + let deployment = vm.deploy(&private_key, &program, None, 0, None, rng).unwrap(); + + // Verify the deployment transaction. It should fail because there are too many constraints. + assert!(vm.check_transaction(&deployment, None, rng).is_err()); + } + + #[test] + fn test_deployment_synthesis_overreport() { + let rng = &mut TestRng::default(); + + // Initialize a private key. + let private_key = sample_genesis_private_key(rng); + + // Initialize the genesis block. + let genesis = sample_genesis_block(rng); + + // Initialize the VM. + let vm = sample_vm(); + // Update the VM. + vm.add_next_block(&genesis).unwrap(); + + // Deploy the base program. + let program = Program::from_str( + r" +program synthesis_overreport.aleo; + +function do: + input r0 as u32.private; + add r0 r0 into r1; + output r1 as u32.public;", + ) + .unwrap(); + + // Create the deployment transaction. + let transaction = vm.deploy(&private_key, &program, None, 0, None, rng).unwrap(); + + // Destructure the deployment transaction. + let Transaction::Deploy(_, program_owner, deployment, fee) = transaction else { + panic!("Expected a deployment transaction"); + }; + + // Increase the number of constraints in the verifying keys. + let mut vks_with_overreport = Vec::with_capacity(deployment.verifying_keys().len()); + for (id, (vk, cert)) in deployment.verifying_keys() { + let mut vk = vk.deref().clone(); + vk.circuit_info.num_constraints += 1; + let vk = VerifyingKey::new(Arc::new(vk)); + vks_with_overreport.push((*id, (vk, cert.clone()))); + } + + // Each additional constraint costs 25 microcredits, so we need to increase the fee by 25 microcredits. + let required_fee = *fee.base_amount().unwrap() + 25; + // Authorize a new fee. + let fee_authorization = vm + .authorize_fee_public(&private_key, required_fee, 0, deployment.as_ref().to_deployment_id().unwrap(), rng) + .unwrap(); + // Compute the fee. + let fee = vm.execute_fee_authorization(fee_authorization, None, rng).unwrap(); + + // Create a new deployment transaction with the overreported verifying keys. + let adjusted_deployment = + Deployment::new(deployment.edition(), deployment.program().clone(), vks_with_overreport).unwrap(); + let adjusted_transaction = Transaction::from_deployment(program_owner, adjusted_deployment, fee).unwrap(); + + // Verify the deployment transaction. It should error when certificate checking for constraint count mismatch. + let res = vm.check_transaction(&adjusted_transaction, None, rng); + assert!(res.is_err()); + } + + #[test] + #[should_panic] + fn test_deployment_synthesis_underreport() { + let rng = &mut TestRng::default(); + + // Initialize a private key. + let private_key = sample_genesis_private_key(rng); + + // Initialize the genesis block. + let genesis = sample_genesis_block(rng); + + // Initialize the VM. + let vm = sample_vm(); + // Update the VM. + vm.add_next_block(&genesis).unwrap(); + + // Deploy the base program. + let program = Program::from_str( + r" +program synthesis_underreport.aleo; + +function do: + input r0 as u32.private; + add r0 r0 into r1; + output r1 as u32.public;", + ) + .unwrap(); + + // Create the deployment transaction. + let transaction = vm.deploy(&private_key, &program, None, 0, None, rng).unwrap(); + + // Destructure the deployment transaction. + let Transaction::Deploy(txid, program_owner, deployment, fee) = transaction else { + panic!("Expected a deployment transaction"); + }; + + // Decrease the number of constraints in the verifying keys. + let mut vks_with_underreport = Vec::with_capacity(deployment.verifying_keys().len()); + for (id, (vk, cert)) in deployment.verifying_keys() { + let mut vk = vk.deref().clone(); + vk.circuit_info.num_constraints -= 2; + let vk = VerifyingKey::new(Arc::new(vk)); + vks_with_underreport.push((*id, (vk, cert.clone()))); + } + + // Create a new deployment transaction with the underreported verifying keys. + let adjusted_deployment = + Deployment::new(deployment.edition(), deployment.program().clone(), vks_with_underreport).unwrap(); + let adjusted_transaction = Transaction::Deploy(txid, program_owner, Box::new(adjusted_deployment), fee); + + // Verify the deployment transaction. It should panic when enforcing the first constraint over the vk limit. + let _ = vm.check_transaction(&adjusted_transaction, None, rng); + } + #[test] #[ignore] fn test_deployment_memory_overload() { diff --git a/synthesizer/src/vm/verify.rs b/synthesizer/src/vm/verify.rs index e1a7c05330..6fc47523a5 100644 --- a/synthesizer/src/vm/verify.rs +++ b/synthesizer/src/vm/verify.rs @@ -145,7 +145,7 @@ impl> VM { let Ok(deployment_id) = deployment.to_deployment_id() else { bail!("Failed to compute the Merkle root for deployment transaction '{id}'") }; - // Compute the deployment cost. + // Compute the minimum deployment cost. let (cost, _) = deployment_cost(deployment)?; // Ensure the fee is sufficient to cover the cost. if *fee.base_amount()? < cost { diff --git a/synthesizer/tests/expectations/vm/execute_and_finalize/test_rand.out b/synthesizer/tests/expectations/vm/execute_and_finalize/test_rand.out index 57dd18584c..511c0af4b1 100644 --- a/synthesizer/tests/expectations/vm/execute_and_finalize/test_rand.out +++ b/synthesizer/tests/expectations/vm/execute_and_finalize/test_rand.out @@ -19,7 +19,7 @@ outputs: test_rand.aleo/rand_chacha_check: outputs: - '{"type":"future","id":"488590592441422127997725386233571306457829570948543232762424611162021835080field","value":"{\n program_id: test_rand.aleo,\n function_name: rand_chacha_check,\n arguments: [\n 0field,\n false\n ]\n}"}' - speculate: the execution was rejected + speculate: the execution was accepted add_next_block: succeeded. - verified: true execute: