Skip to content

Commit

Permalink
Merge remote-tracking branch 'aleonet/mainnet-staging' into feat/rest…
Browse files Browse the repository at this point in the history
…ricted-list
  • Loading branch information
vicsn committed Jun 11, 2024
2 parents d0495d7 + 43e681f commit 90f384a
Show file tree
Hide file tree
Showing 32 changed files with 853 additions and 159 deletions.
42 changes: 39 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
version: 2.1

parameters:
large:
type: string
default: aleonet/large
xlarge:
type: string
default: anf/xlarge
default: aleonet/xlarge
twoxlarge:
type: string
default: anf/2xlarge
default: aleonet/2xlarge

orbs:
windows: circleci/windows@5.0
Expand Down Expand Up @@ -709,7 +712,7 @@ jobs:
resource_class: << pipeline.parameters.twoxlarge >>
steps:
- run_serial:
flags: --test '*' -- --skip keccak --skip psd --skip sha
flags: --test '*' -- --skip keccak --skip psd --skip sha --skip instruction::is --skip instruction::equal --skip instruction::commit
workspace_member: synthesizer/program
cache_key: snarkvm-synthesizer-program-cache

Expand Down Expand Up @@ -743,6 +746,36 @@ jobs:
workspace_member: synthesizer/program
cache_key: snarkvm-synthesizer-program-cache

synthesizer-program-integration-instruction-is:
docker:
- image: cimg/rust:1.76.0 # Attention - Change the MSRV in Cargo.toml and rust-toolchain as well
resource_class: << pipeline.parameters.twoxlarge >>
steps:
- run_serial:
flags: instruction::is --test '*'
workspace_member: synthesizer/program
cache_key: snarkvm-synthesizer-program-cache

synthesizer-program-integration-instruction-equal:
docker:
- image: cimg/rust:1.76.0 # Attention - Change the MSRV in Cargo.toml and rust-toolchain as well
resource_class: << pipeline.parameters.twoxlarge >>
steps:
- run_serial:
flags: instruction::equal --test '*'
workspace_member: synthesizer/program
cache_key: snarkvm-synthesizer-program-cache

synthesizer-program-integration-instruction-commit:
docker:
- image: cimg/rust:1.76.0 # Attention - Change the MSRV in Cargo.toml and rust-toolchain as well
resource_class: << pipeline.parameters.twoxlarge >>
steps:
- run_serial:
flags: instruction::commit --test '*'
workspace_member: synthesizer/program
cache_key: snarkvm-synthesizer-program-cache

synthesizer-snark:
docker:
- image: cimg/rust:1.76.0 # Attention - Change the MSRV in Cargo.toml and rust-toolchain as well
Expand Down Expand Up @@ -920,6 +953,9 @@ workflows:
- synthesizer-program-integration-keccak
- synthesizer-program-integration-psd
- synthesizer-program-integration-sha
- synthesizer-program-integration-instruction-is
- synthesizer-program-integration-instruction-equal
- synthesizer-program-integration-instruction-commit
- synthesizer-snark
- utilities
- utilities-derives
Expand Down
22 changes: 22 additions & 0 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion console/network/src/mainnet_v0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ impl Network for MainnetV0 {
/// The function name for the inclusion circuit.
const INCLUSION_FUNCTION_NAME: &'static str = snarkvm_parameters::mainnet::NETWORK_INCLUSION_FUNCTION_NAME;
/// The maximum number of certificates in a batch.
const MAX_CERTIFICATES: u16 = 10;
const MAX_CERTIFICATES: u16 = 15;
/// The network name.
const NAME: &'static str = "Aleo Mainnet (v0)";

Expand Down
3 changes: 3 additions & 0 deletions console/program/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ version = "0.2"
[dependencies.enum_index_derive]
version = "0.2"

[dependencies.enum-iterator]
version = "2.1"

[dependencies.indexmap]
version = "2.0"

Expand Down
2 changes: 1 addition & 1 deletion console/program/src/data/identifier/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ impl<N: Network> FromStr for Identifier<N> {

// Ensure that the identifier is not a literal.
ensure!(
crate::LiteralType::from_str(identifier).is_err(),
!enum_iterator::all::<crate::LiteralType>().any(|lt| lt.type_name() == identifier),
"Identifier '{identifier}' is a reserved literal type"
);

Expand Down
3 changes: 2 additions & 1 deletion console/program/src/data_types/literal_type/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ use snarkvm_console_network::prelude::*;
use snarkvm_console_types::{prelude::*, Boolean};

use core::fmt::{self, Debug, Display};
use enum_iterator::Sequence;
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;

#[derive(Copy, Clone, PartialEq, Eq, Hash, FromPrimitive)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, FromPrimitive, Sequence)]
pub enum LiteralType {
/// The Aleo address type.
Address,
Expand Down
4 changes: 4 additions & 0 deletions ledger/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ package = "snarkvm-ledger-block"
path = "./block"
features = [ "test" ]

[dev-dependencies.ledger-test-helpers]
package = "snarkvm-ledger-test-helpers"
path = "./test-helpers"

[dev-dependencies.serde_json]
version = "1.0"
features = [ "preserve_order" ]
5 changes: 3 additions & 2 deletions ledger/block/src/solutions/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ impl<'de, N: Network> Deserialize<'de> for Solutions<N> {
pub(super) mod tests {
use super::*;
use console::account::{Address, PrivateKey};
use ledger_puzzle::Solution;
use ledger_puzzle::{PartialSolution, Solution};

type CurrentNetwork = console::network::MainnetV0;

Expand All @@ -73,7 +73,8 @@ pub(super) mod tests {
for _ in 0..rng.gen_range(1..=CurrentNetwork::MAX_SOLUTIONS) {
let private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap();
let address = Address::try_from(private_key).unwrap();
solutions.push(Solution::new(rng.gen(), address, u64::rand(rng)).unwrap());
let partial_solution = PartialSolution::new(rng.gen(), address, u64::rand(rng)).unwrap();
solutions.push(Solution::new(partial_solution, u64::rand(rng)));
}
Solutions::new(PuzzleSolutions::new(solutions).unwrap()).unwrap()
}
Expand Down
123 changes: 111 additions & 12 deletions ledger/puzzle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
#![allow(clippy::too_many_arguments)]
#![warn(clippy::cast_possible_truncation)]

mod partial_solution;
pub use partial_solution::*;

mod solution;
pub use solution::*;

Expand Down Expand Up @@ -97,7 +100,7 @@ impl<N: Network> Puzzle<N> {
}

/// Returns the Merkle leaves for the puzzle, given the solution.
pub fn get_leaves(&self, solution: &Solution<N>) -> Result<Vec<Vec<bool>>> {
pub fn get_leaves(&self, solution: &PartialSolution<N>) -> Result<Vec<Vec<bool>>> {
// Initialize a seeded random number generator.
let mut rng = ChaChaRng::seed_from_u64(*solution.id());
// Output the leaves.
Expand All @@ -119,18 +122,36 @@ impl<N: Network> Puzzle<N> {

/// Returns the proof target given the solution.
pub fn get_proof_target(&self, solution: &Solution<N>) -> Result<u64> {
// Calculate the proof target.
let proof_target = self.get_proof_target_unchecked(solution)?;
// Ensure the proof target matches the expected proof target.
ensure!(solution.target() == proof_target, "The proof target does not match the expected proof target");
// Return the proof target.
Ok(proof_target)
}

/// Returns the proof target given the solution.
///
/// Note: This method does **not** check the proof target against the expected proof target.
pub fn get_proof_target_unchecked(&self, solution: &Solution<N>) -> Result<u64> {
// Calculate the proof target.
self.get_proof_target_from_partial_solution(solution.partial_solution())
}

/// Returns the proof target given the partial solution.
pub fn get_proof_target_from_partial_solution(&self, partial_solution: &PartialSolution<N>) -> Result<u64> {
// If the proof target is in the cache, then return it.
if let Some(proof_target) = self.proof_target_cache.write().get(&solution.id()) {
if let Some(proof_target) = self.proof_target_cache.write().get(&partial_solution.id()) {
return Ok(*proof_target);
}

// Construct the leaves of the Merkle tree.
let leaves = self.get_leaves(solution)?;
let leaves = self.get_leaves(partial_solution)?;
// Get the proof target.
let proof_target = Self::leaves_to_proof_target(&leaves)?;

// Insert the proof target into the cache.
self.proof_target_cache.write().put(solution.id(), proof_target);
self.proof_target_cache.write().put(partial_solution.id(), proof_target);
// Return the proof target.
Ok(proof_target)
}
Expand All @@ -147,7 +168,14 @@ impl<N: Network> Puzzle<N> {
// Check if the proof target is in the cache.
match self.proof_target_cache.write().get(id) {
// If the proof target is in the cache, then store it.
Some(proof_target) => targets[i] = *proof_target,
Some(proof_target) => {
// Ensure that the proof target matches the expected proof target.
ensure!(
solution.target() == *proof_target,
"The proof target does not match the expected proof target"
);
targets[i] = *proof_target
}
// Otherwise, add it to the list of solutions that need to be computed.
None => to_compute.push((i, id, *solution)),
}
Expand All @@ -160,10 +188,15 @@ impl<N: Network> Puzzle<N> {
let leaves = self.get_all_leaves(&solutions_subset)?;
// Construct the Merkle roots and truncate them to a u64.
let targets_subset = cfg_iter!(leaves)
.zip(cfg_keys!(solutions_subset))
.map(|(leaves, solution_id)| {
.zip(cfg_iter!(solutions_subset))
.map(|(leaves, (solution_id, solution))| {
// Get the proof target.
let proof_target = Self::leaves_to_proof_target(leaves)?;
// Ensure that the proof target matches the expected proof target.
ensure!(
solution.target() == proof_target,
"The proof target does not match the expected proof target"
);
// Insert the proof target into the cache.
self.proof_target_cache.write().put(*solution_id, proof_target);
// Return the proof target.
Expand Down Expand Up @@ -196,18 +229,19 @@ impl<N: Network> Puzzle<N> {
counter: u64,
minimum_proof_target: Option<u64>,
) -> Result<Solution<N>> {
// Construct the solution.
let solution = Solution::new(epoch_hash, address, counter)?;
// Construct the partial solution.
let partial_solution = PartialSolution::new(epoch_hash, address, counter)?;
// Compute the proof target.
let proof_target = self.get_proof_target(&solution)?;
let proof_target = self.get_proof_target_from_partial_solution(&partial_solution)?;
// Check that the minimum proof target is met.
if let Some(minimum_proof_target) = minimum_proof_target {
if proof_target < minimum_proof_target {
bail!("Solution was below the minimum proof target ({proof_target} < {minimum_proof_target})")
}
}
// Return the solution.
Ok(solution)

// Construct the solution.
Ok(Solution::new(partial_solution, proof_target))
}

/// Returns `Ok(())` if the solution is valid.
Expand Down Expand Up @@ -445,6 +479,71 @@ mod tests {
assert!(puzzle.check_solutions(&solutions, epoch_hash, 0u64).is_ok());
}

#[test]
fn test_check_solution_with_incorrect_target_fails() {
let mut rng = rand::thread_rng();

// Initialize a new puzzle.
let puzzle = sample_puzzle();

// Initialize an epoch hash.
let epoch_hash = rng.gen();

// Generate inputs.
let private_key = PrivateKey::<CurrentNetwork>::new(&mut rng).unwrap();
let address = Address::try_from(private_key).unwrap();

// Generate a solution.
let solution = puzzle.prove(epoch_hash, address, rng.gen(), None).unwrap();

// Generate a solution with an incorrect target.
let incorrect_solution = Solution::new(*solution.partial_solution(), solution.target().saturating_add(1));

// Ensure the incorrect solution is invalid.
assert!(puzzle.check_solution(&incorrect_solution, epoch_hash, 0u64).is_err());

// Ensure the invalid solution is invalid on a fresh puzzle instance.
let new_puzzle = sample_puzzle();
assert!(new_puzzle.check_solution(&incorrect_solution, epoch_hash, 0u64).is_err());

// Ensure the incorrect solutions are invalid.
let incorrect_solutions = PuzzleSolutions::new(vec![incorrect_solution]).unwrap();
assert!(puzzle.check_solutions(&incorrect_solutions, epoch_hash, 0u64).is_err());

// Ensure the incorrect solutions are invalid on a fresh puzzle instance.
let new_puzzle = sample_puzzle();
assert!(new_puzzle.check_solutions(&incorrect_solutions, epoch_hash, 0u64).is_err());
}

#[test]
fn test_check_solutions_with_incorrect_target_fails() {
let mut rng = TestRng::default();

// Initialize a new puzzle.
let puzzle = sample_puzzle();

// Initialize an epoch hash.
let epoch_hash = rng.gen();

for batch_size in 1..=CurrentNetwork::MAX_SOLUTIONS {
// Initialize the incorrect solutions.
let incorrect_solutions = (0..batch_size)
.map(|_| {
let solution = puzzle.prove(epoch_hash, rng.gen(), rng.gen(), None).unwrap();
Solution::new(*solution.partial_solution(), solution.target().saturating_add(1))
})
.collect::<Vec<_>>();
let incorrect_solutions = PuzzleSolutions::new(incorrect_solutions).unwrap();

// Ensure the incorrect solutions are invalid.
assert!(puzzle.check_solutions(&incorrect_solutions, epoch_hash, 0u64).is_err());

// Ensure the incorrect solutions are invalid on a fresh puzzle instance.
let new_puzzle = sample_puzzle();
assert!(new_puzzle.check_solutions(&incorrect_solutions, epoch_hash, 0u64).is_err());
}
}

#[test]
fn test_check_solutions_with_duplicate_nonces() {
let mut rng = TestRng::default();
Expand Down
Loading

0 comments on commit 90f384a

Please sign in to comment.