diff --git a/Cargo.lock b/Cargo.lock index 9e94495b20..2798799191 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3147,6 +3147,7 @@ dependencies = [ name = "snarkvm-ledger-coinbase" version = "0.14.6" dependencies = [ + "aleo-std", "anyhow", "bincode", "blake2", diff --git a/ledger/coinbase/Cargo.toml b/ledger/coinbase/Cargo.toml index 55038c015f..270a9d3d98 100644 --- a/ledger/coinbase/Cargo.toml +++ b/ledger/coinbase/Cargo.toml @@ -40,6 +40,7 @@ serial = [ "snarkvm-utilities/serial" ] setup = [ ] +timer = [ "aleo-std/timer" ] wasm = [ "console/wasm", "snarkvm-algorithms/wasm", @@ -72,6 +73,10 @@ path = "../../utilities" version = "=0.14.6" default-features = false +[dependencies.aleo-std] +version = "0.1.18" +default-features = false + [dependencies.anyhow] version = "1.0.72" diff --git a/ledger/coinbase/src/lib.rs b/ledger/coinbase/src/lib.rs index a6ea82fd6d..5c2c3bad43 100644 --- a/ledger/coinbase/src/lib.rs +++ b/ledger/coinbase/src/lib.rs @@ -41,6 +41,8 @@ use snarkvm_fields::{PrimeField, Zero}; use snarkvm_synthesizer_snark::UniversalSRS; use snarkvm_utilities::cfg_zip_fold; +use aleo_std::prelude::*; + use std::sync::Arc; #[cfg(feature = "serial")] @@ -263,6 +265,8 @@ impl CoinbasePuzzle { epoch_challenge: &EpochChallenge, proof_target: u64, ) -> Result { + let timer = timer!("CoinbasePuzzle::verify"); + // Ensure the solutions are not empty. if coinbase_solution.is_empty() { bail!("There are no solutions"); @@ -286,6 +290,7 @@ impl CoinbasePuzzle { if has_duplicates(coinbase_solution.puzzle_commitments()) { bail!("The solutions contain duplicate puzzle commitments"); } + lap!(timer, "Perform initial checks"); // Compute the prover polynomials. let prover_polynomials = cfg_iter!(coinbase_solution.partial_solutions()) @@ -296,6 +301,7 @@ impl CoinbasePuzzle { false => bail!("Prover puzzle does not meet the proof target requirements."), }) .collect::>>()?; + lap!(timer, "Compute the prover polynomials"); // Compute the challenge points. let mut challenge_points = @@ -310,6 +316,7 @@ impl CoinbasePuzzle { Some(point) => point, None => bail!("Missing the accumulator challenge point"), }; + lap!(timer, "Construct the accumulator point"); // Compute the accumulator evaluation. let mut accumulator_evaluation = cfg_zip_fold!( @@ -322,6 +329,7 @@ impl CoinbasePuzzle { _ ); accumulator_evaluation *= &epoch_challenge.epoch_polynomial().evaluate(accumulator_point); + lap!(timer, "Compute the accumulator evaluation"); // Compute the accumulator commitment. let commitments: Vec<_> = @@ -329,6 +337,7 @@ impl CoinbasePuzzle { let fs_challenges = challenge_points.into_iter().map(|f| f.to_bigint()).collect::>(); let accumulator_commitment = KZGCommitment::(VariableBase::msm(&commitments, &fs_challenges).into()); + lap!(timer, "Compute the accumulator commitment"); // Retrieve the coinbase verifying key. let coinbase_verifying_key = match self { @@ -336,14 +345,20 @@ impl CoinbasePuzzle { Self::Verifier(coinbase_verifying_key) => coinbase_verifying_key, }; - // Return the verification result. - Ok(KZG10::check( + // Verify the KZG10 proof. + let check = KZG10::check( coinbase_verifying_key, &accumulator_commitment, accumulator_point, accumulator_evaluation, coinbase_solution.proof(), - )?) + )?; + lap!(timer, "Verify the KZG10 proof"); + + finish!(timer); + + // Return the verification result. + Ok(check) } /// Returns the coinbase proving key. diff --git a/ledger/coinbase/src/tests.rs b/ledger/coinbase/src/tests.rs index 04549a4834..3f8c74458c 100644 --- a/ledger/coinbase/src/tests.rs +++ b/ledger/coinbase/src/tests.rs @@ -93,7 +93,7 @@ fn test_edge_case_for_degree() { let srs = CoinbasePuzzle::::setup(max_config).unwrap(); // Generate PK and VK. - let degree = (1 << 13) - 1; // IF YOU ADD `- 1` THIS WILL PASS + let degree = (1 << 13) - 1; let puzzle = CoinbasePuzzle::::trim(&srs, PuzzleConfig { degree }).unwrap(); // Generate proof inputs @@ -106,3 +106,47 @@ fn test_edge_case_for_degree() { let (coinbase_solution, _) = puzzle.accumulate_unchecked(&epoch_challenge, &[prover_solution]).unwrap(); assert!(puzzle.verify(&coinbase_solution, &epoch_challenge, 0u64).unwrap()); } + +/// Use `cargo test profiler --features timer` to run this test. +#[ignore] +#[test] +fn test_profiler() -> Result<()> { + fn sample_address_and_nonce(rng: &mut (impl CryptoRng + RngCore)) -> (Address, u64) { + let private_key = PrivateKey::new(rng).unwrap(); + let address = Address::try_from(private_key).unwrap(); + let nonce = rng.next_u64(); + (address, nonce) + } + + let mut rng = rand::thread_rng(); + + // Generate srs. + let max_degree = 1 << 15; + let max_config = PuzzleConfig { degree: max_degree }; + let universal_srs = CoinbasePuzzle::::setup(max_config).unwrap(); + + // Generate PK and VK. + let degree = (1 << 13) - 1; + let config = PuzzleConfig { degree }; + let puzzle = CoinbasePuzzle::trim(&universal_srs, config).unwrap(); + + // Generate proof inputs + let epoch_challenge = EpochChallenge::new(rng.next_u32(), Default::default(), degree).unwrap(); + + for batch_size in [10, 100, ::MAX_PROVER_SOLUTIONS] { + // Generate the solutions. + let solutions = (0..batch_size) + .map(|_| { + let (address, nonce) = sample_address_and_nonce(&mut rng); + puzzle.prove(&epoch_challenge, address, nonce, None).unwrap() + }) + .collect::>(); + // Accumulate the solutions. + let (solution, _) = puzzle.accumulate_unchecked(&epoch_challenge, &solutions).unwrap(); + + // Verify the solution. + puzzle.verify(&solution, &epoch_challenge, 0u64).unwrap(); + } + + bail!("\n\nRemember to #[ignore] this test!\n\n") +}