Skip to content

Commit

Permalink
Merge pull request #2535 from ProvableHQ/fix/deployment-verification
Browse files Browse the repository at this point in the history
[Fix] Make deployment verification consistent by deterministically seeding the RNG.
  • Loading branch information
zkxuerb authored Aug 27, 2024
2 parents be171ce + 7e8005c commit 0dffb36
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 4 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions ledger/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,9 @@ path = "./test-helpers"
[dev-dependencies.serde_json]
version = "1.0"
features = [ "preserve_order" ]

[dev-dependencies.snarkvm-circuit]
path = "../circuit"

[dev-dependencies.snarkvm-utilities]
path = "../utilities"
2 changes: 2 additions & 0 deletions ledger/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,9 +391,11 @@ pub(crate) mod test_helpers {
};
use ledger_block::Block;
use ledger_store::ConsensusStore;
use snarkvm_circuit::network::AleoV0;
use synthesizer::vm::VM;

pub(crate) type CurrentNetwork = MainnetV0;
pub(crate) type CurrentAleo = AleoV0;

#[cfg(not(feature = "rocks"))]
pub(crate) type CurrentLedger =
Expand Down
96 changes: 95 additions & 1 deletion ledger/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

use crate::{
advance::split_candidate_solutions,
test_helpers::{CurrentLedger, CurrentNetwork},
test_helpers::{CurrentAleo, CurrentLedger, CurrentNetwork},
Ledger,
RecordsFilter,
};
Expand All @@ -28,6 +28,7 @@ use console::{
use ledger_block::{ConfirmedTransaction, Execution, Ratify, Rejected, Transaction};
use ledger_committee::{Committee, MIN_VALIDATOR_STAKE};
use ledger_store::{helpers::memory::ConsensusMemory, ConsensusStore};
use snarkvm_utilities::try_vm_runtime;
use synthesizer::{program::Program, vm::VM, Stack};

use indexmap::IndexMap;
Expand Down Expand Up @@ -2346,6 +2347,99 @@ finalize is_id:
ledger.advance_to_next_block(&block_3).unwrap();
}

#[test]
fn test_deployment_with_cast_from_field_to_scalar() {
// Initialize an RNG.
let rng = &mut TestRng::default();

const ITERATIONS: usize = 10;

// Construct a program that casts a field to a scalar.
let program = Program::<CurrentNetwork>::from_str(
r"
program test_cast_field_to_scalar.aleo;
function foo:
input r0 as field.public;
cast r0 into r1 as scalar;",
)
.unwrap();

// Constructs a program that has a struct with a field that is cast to a scalar.
let program_2 = Program::<CurrentNetwork>::from_str(
r"
program test_cast_f_to_s_struct.aleo;
struct message:
first as scalar;
function foo:
input r0 as field.public;
cast r0 into r1 as scalar;
cast r1 into r2 as message;",
)
.unwrap();

// Constructs a program that has an array of scalars cast from fields.
let program_3 = Program::<CurrentNetwork>::from_str(
r"
program test_cast_f_to_s_array.aleo;
function foo:
input r0 as field.public;
cast r0 into r1 as scalar;
cast r1 r1 r1 r1 into r2 as [scalar; 4u32];",
)
.unwrap();

// Initialize the test environment.
let crate::test_helpers::TestEnv { ledger, private_key, .. } = crate::test_helpers::sample_test_env(rng);

// Create a helper method to deploy the programs.
let deploy_program = |program: &Program<CurrentNetwork>, rng: &mut TestRng| {
let mut attempts = 0;
loop {
if attempts >= ITERATIONS {
panic!("Failed to craft deployment after {ITERATIONS} attempts");
}
match try_vm_runtime!(|| ledger.vm().deploy(&private_key, program, None, 0, None, rng)) {
Ok(result) => break result.unwrap(),
Err(_) => attempts += 1,
}
}
};

// Deploy the programs. Keep attempting to create a deployment until it is successful.
let deployment_tx = deploy_program(&program, rng);
let deployment_tx_2 = deploy_program(&program_2, rng);
let deployment_tx_3 = deploy_program(&program_3, rng);

// Verify the deployment under different RNGs to ensure the deployment is valid.
for _ in 0..ITERATIONS {
let process = ledger.vm().process().clone();
// Create a helper method to verify the deployments.
let verify_deployment = |deployment_tx: &Transaction<CurrentNetwork>, rng: &mut TestRng| {
let expected_result = match try_vm_runtime!(|| ledger.vm().check_transaction(deployment_tx, None, rng)) {
Ok(result) => result.is_ok(),
Err(_) => false,
};
let deployment = deployment_tx.deployment().unwrap().clone();
for _ in 0..ITERATIONS {
let result =
match try_vm_runtime!(|| process.read().verify_deployment::<CurrentAleo, _>(&deployment, rng)) {
Ok(result) => result.is_ok(),
Err(_) => false,
};
assert_eq!(result, expected_result);
}
};

// Verify the deployments.
verify_deployment(&deployment_tx, rng);
verify_deployment(&deployment_tx_2, rng);
verify_deployment(&deployment_tx_3, rng);
}
}

// These tests require the proof targets to be low enough to be able to generate **valid** solutions.
// This requires the 'test' feature to be enabled for the `console` dependency.
#[cfg(feature = "test")]
Expand Down
3 changes: 3 additions & 0 deletions synthesizer/process/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ version = "0.12"
[dependencies.rand]
version = "0.8"

[dependencies.rand_chacha]
version = "0.3"

[dependencies.rayon]
version = "1"
optional = true
Expand Down
12 changes: 9 additions & 3 deletions synthesizer/process/src/stack/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ impl<N: Network> Stack<N> {
"The number of functions in the program does not match the number of verifying keys"
);

// Create a seeded rng to use for input value and sub-stack generation.
// This is needed to ensure that the verification results of deployments are consistent across all parties,
// because currently there is a possible flakiness due to overflows in Field to Scalar casting.
let seed = u64::from_bytes_le(&deployment.to_deployment_id()?.to_bytes_le()?[0..8])?;
let mut seeded_rng = rand_chacha::ChaChaRng::seed_from_u64(seed);

// Iterate through the program functions and construct the callstacks and corresponding assignments.
for (function, (_, (verifying_key, _))) in
deployment.program().functions().values().zip_eq(deployment.verifying_keys())
Expand All @@ -111,9 +117,9 @@ impl<N: Network> Stack<N> {
// Retrieve the external stack.
let stack = self.get_external_stack(locator.program_id())?;
// Sample the input.
stack.sample_value(&burner_address, &ValueType::Record(*locator.resource()), rng)
stack.sample_value(&burner_address, &ValueType::Record(*locator.resource()), &mut seeded_rng)
}
_ => self.sample_value(&burner_address, input_type, rng),
_ => self.sample_value(&burner_address, input_type, &mut seeded_rng),
})
.collect::<Result<Vec<_>>>()?;
lap!(timer, "Sample the inputs");
Expand Down Expand Up @@ -154,7 +160,7 @@ impl<N: Network> Stack<N> {
}

// Verify the certificates.
let rngs = (0..call_stacks.len()).map(|_| StdRng::from_seed(rng.gen())).collect::<Vec<_>>();
let rngs = (0..call_stacks.len()).map(|_| StdRng::from_seed(seeded_rng.gen())).collect::<Vec<_>>();
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.
Expand Down

0 comments on commit 0dffb36

Please sign in to comment.