Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix] Make deployment verification consistent by deterministically seeding the RNG. #2535

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.

8 changes: 4 additions & 4 deletions fields/src/fp_256.rs
Original file line number Diff line number Diff line change
Expand Up @@ -767,7 +767,7 @@ impl<'a, P: Fp256Parameters> MulAssign<&'a Self> for Fp256<P> {

r[3] = fa::mac_with_carry(r[3], (self.0).0[3], (other.0).0[0], &mut carry1);
r[2] = fa::mac_with_carry(r[3], k, P::MODULUS.0[3], &mut carry2);
r[3] = carry1 + carry2;
r[3] = carry1.wrapping_add(carry2);

// Iteration 1.
r[0] = fa::mac(r[0], (self.0).0[0], (other.0).0[1], &mut carry1);
Expand All @@ -781,7 +781,7 @@ impl<'a, P: Fp256Parameters> MulAssign<&'a Self> for Fp256<P> {

r[3] = fa::mac_with_carry(r[3], (self.0).0[3], (other.0).0[1], &mut carry1);
r[2] = fa::mac_with_carry(r[3], k, P::MODULUS.0[3], &mut carry2);
r[3] = carry1 + carry2;
r[3] = carry1.wrapping_add(carry2);

// Iteration 2.
r[0] = fa::mac(r[0], (self.0).0[0], (other.0).0[2], &mut carry1);
Expand All @@ -795,7 +795,7 @@ impl<'a, P: Fp256Parameters> MulAssign<&'a Self> for Fp256<P> {

r[3] = fa::mac_with_carry(r[3], (self.0).0[3], (other.0).0[2], &mut carry1);
r[2] = fa::mac_with_carry(r[3], k, P::MODULUS.0[3], &mut carry2);
r[3] = carry1 + carry2;
r[3] = carry1.wrapping_add(carry2);

// Iteration 3.
r[0] = fa::mac(r[0], (self.0).0[0], (other.0).0[3], &mut carry1);
Expand All @@ -809,7 +809,7 @@ impl<'a, P: Fp256Parameters> MulAssign<&'a Self> for Fp256<P> {

r[3] = fa::mac_with_carry(r[3], (self.0).0[3], (other.0).0[3], &mut carry1);
r[2] = fa::mac_with_carry(r[3], k, P::MODULUS.0[3], &mut carry2);
r[3] = carry1 + carry2;
r[3] = carry1.wrapping_add(carry2);

(self.0).0 = r;
self.reduce();
Expand Down
12 changes: 6 additions & 6 deletions fields/src/fp_384.rs
Original file line number Diff line number Diff line change
Expand Up @@ -790,7 +790,7 @@ impl<'a, P: Fp384Parameters> MulAssign<&'a Self> for Fp384<P> {

r[5] = fa::mac_with_carry(r[5], (self.0).0[5], (other.0).0[0], &mut carry1);
r[4] = fa::mac_with_carry(r[5], k, P::MODULUS.0[5], &mut carry2);
r[5] = carry1 + carry2;
r[5] = carry1.wrapping_add(carry2);

// Iteration 1.
r[0] = fa::mac(r[0], (self.0).0[0], (other.0).0[1], &mut carry1);
Expand All @@ -810,7 +810,7 @@ impl<'a, P: Fp384Parameters> MulAssign<&'a Self> for Fp384<P> {

r[5] = fa::mac_with_carry(r[5], (self.0).0[5], (other.0).0[1], &mut carry1);
r[4] = fa::mac_with_carry(r[5], k, P::MODULUS.0[5], &mut carry2);
r[5] = carry1 + carry2;
r[5] = carry1.wrapping_add(carry2);

// Iteration 2.
r[0] = fa::mac(r[0], (self.0).0[0], (other.0).0[2], &mut carry1);
Expand All @@ -830,7 +830,7 @@ impl<'a, P: Fp384Parameters> MulAssign<&'a Self> for Fp384<P> {

r[5] = fa::mac_with_carry(r[5], (self.0).0[5], (other.0).0[2], &mut carry1);
r[4] = fa::mac_with_carry(r[5], k, P::MODULUS.0[5], &mut carry2);
r[5] = carry1 + carry2;
r[5] = carry1.wrapping_add(carry2);

// Iteration 3.
r[0] = fa::mac(r[0], (self.0).0[0], (other.0).0[3], &mut carry1);
Expand All @@ -850,7 +850,7 @@ impl<'a, P: Fp384Parameters> MulAssign<&'a Self> for Fp384<P> {

r[5] = fa::mac_with_carry(r[5], (self.0).0[5], (other.0).0[3], &mut carry1);
r[4] = fa::mac_with_carry(r[5], k, P::MODULUS.0[5], &mut carry2);
r[5] = carry1 + carry2;
r[5] = carry1.wrapping_add(carry2);

// Iteration 4.
r[0] = fa::mac(r[0], (self.0).0[0], (other.0).0[4], &mut carry1);
Expand All @@ -870,7 +870,7 @@ impl<'a, P: Fp384Parameters> MulAssign<&'a Self> for Fp384<P> {

r[5] = fa::mac_with_carry(r[5], (self.0).0[5], (other.0).0[4], &mut carry1);
r[4] = fa::mac_with_carry(r[5], k, P::MODULUS.0[5], &mut carry2);
r[5] = carry1 + carry2;
r[5] = carry1.wrapping_add(carry2);

// Iteration 5.
r[0] = fa::mac(r[0], (self.0).0[0], (other.0).0[5], &mut carry1);
Expand All @@ -890,7 +890,7 @@ impl<'a, P: Fp384Parameters> MulAssign<&'a Self> for Fp384<P> {

r[5] = fa::mac_with_carry(r[5], (self.0).0[5], (other.0).0[5], &mut carry1);
r[4] = fa::mac_with_carry(r[5], k, P::MODULUS.0[5], &mut carry2);
r[5] = carry1 + carry2;
r[5] = carry1.wrapping_add(carry2);

(self.0).0 = r;
self.reduce();
Expand Down
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
115 changes: 114 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,118 @@ 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);

// Deploy the programs. Keep attempting to create a deployment until it is successful.
let deployment_tx = {
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,
}
}
};
let deployment_tx_2 = {
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_2, None, 0, None, rng)) {
Ok(result) => break result.unwrap(),
Err(_) => attempts += 1,
}
}
};
let deployment_tx_3 = {
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_3, None, 0, None, rng)) {
Ok(result) => break result.unwrap(),
Err(_) => attempts += 1,
}
}
};

// Verify the deployment under different RNGs to ensure the deployment is valid.
for _ in 0..ITERATIONS {
let process = ledger.vm().process().clone();
// Check that the verification of the deployments are consistent.
let expected_result = ledger.vm().check_transaction(&deployment_tx, None, rng).is_ok();
let deployment = deployment_tx.deployment().unwrap().clone();
for _ in 0..ITERATIONS {
let result = process.read().verify_deployment::<CurrentAleo, _>(&deployment, rng).is_ok();
assert_eq!(result, expected_result);
}

let expected_result = ledger.vm().check_transaction(&deployment_tx_2, None, rng).is_ok();
let deployment_2 = deployment_tx_2.deployment().unwrap().clone();
for _ in 0..ITERATIONS {
let result = process.read().verify_deployment::<CurrentAleo, _>(&deployment_2, rng).is_ok();
assert_eq!(result, expected_result);
}

let expected_result = ledger.vm().check_transaction(&deployment_tx_3, None, rng).is_ok();
let deployment_3 = deployment_tx_3.deployment().unwrap().clone();
for _ in 0..ITERATIONS {
let result = process.read().verify_deployment::<CurrentAleo, _>(&deployment_3, rng).is_ok();
assert_eq!(result, expected_result);
}
}
}

// 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