diff --git a/.github/workflows/gates_report_brillig_execution.yml b/.github/workflows/gates_report_brillig_execution.yml new file mode 100644 index 00000000000..0ef98f5045b --- /dev/null +++ b/.github/workflows/gates_report_brillig_execution.yml @@ -0,0 +1,92 @@ +name: Report Brillig opcodes executed diff + +on: + push: + branches: + - master + pull_request: + +jobs: + build-nargo: + runs-on: ubuntu-latest + strategy: + matrix: + target: [x86_64-unknown-linux-gnu] + + steps: + - name: Checkout Noir repo + uses: actions/checkout@v4 + + - name: Setup toolchain + uses: dtolnay/rust-toolchain@1.74.1 + + - uses: Swatinem/rust-cache@v2 + with: + key: ${{ matrix.target }} + cache-on-failure: true + save-if: ${{ github.event_name != 'merge_group' }} + + - name: Build Nargo + run: cargo build --package nargo_cli --release + + - name: Package artifacts + run: | + mkdir dist + cp ./target/release/nargo ./dist/nargo + 7z a -ttar -so -an ./dist/* | 7z a -si ./nargo-x86_64-unknown-linux-gnu.tar.gz + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: nargo + path: ./dist/* + retention-days: 3 + + compare_brillig_execution_reports: + needs: [build-nargo] + runs-on: ubuntu-latest + permissions: + pull-requests: write + + steps: + - uses: actions/checkout@v4 + + - name: Download nargo binary + uses: actions/download-artifact@v4 + with: + name: nargo + path: ./nargo + + - name: Set nargo on PATH + run: | + nargo_binary="${{ github.workspace }}/nargo/nargo" + chmod +x $nargo_binary + echo "$(dirname $nargo_binary)" >> $GITHUB_PATH + export PATH="$PATH:$(dirname $nargo_binary)" + nargo -V + + - name: Generate Brillig execution report + working-directory: ./test_programs + run: | + chmod +x gates_report_brillig_execution.sh + ./gates_report_brillig_execution.sh + mv gates_report_brillig_execution.json ../gates_report_brillig_execution.json + + - name: Compare Brillig execution reports + id: brillig_execution_diff + uses: noir-lang/noir-gates-diff@d88f7523b013b9edd3f31c5cfddaef87a3fe1b48 + with: + report: gates_report_brillig_execution.json + header: | + # Changes to number of Brillig opcodes executed + brillig_report: true + summaryQuantile: 0.9 # only display the 10% most significant bytecode size diffs in the summary (defaults to 20%) + + - name: Add bytecode size diff to sticky comment + if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target' + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: brillig_execution + # delete the comment in case changes no longer impact brillig bytecode sizes + delete: ${{ !steps.brillig_execution_diff.outputs.markdown }} + message: ${{ steps.brillig_execution_diff.outputs.markdown }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index aeb7d8757c4..f1f0ea47bcf 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ tooling/noir_js/lib gates_report.json gates_report_brillig.json +gates_report_brillig_execution.json # Github Actions scratch space # This gives a location to download artifacts into the repository in CI without making git dirty. diff --git a/noir_stdlib/src/hash/sha256.nr b/noir_stdlib/src/hash/sha256.nr index d55044907ac..32d746c5bcf 100644 --- a/noir_stdlib/src/hash/sha256.nr +++ b/noir_stdlib/src/hash/sha256.nr @@ -187,8 +187,9 @@ unconstrained fn build_msg_block( for i in 0..int_input { let mut msg_item: u32 = 0; // Always construct the integer as 4 bytes, even if it means going beyond the input. + let i_times_k = i * INT_SIZE; for j in 0..INT_SIZE { - let k = i * INT_SIZE + j; + let k = i_times_k + j; let msg_byte = if k < block_input { msg[msg_start + k] } else { diff --git a/test_programs/execution_success/sha256_regression/Prover.toml b/test_programs/execution_success/sha256_regression/Prover.toml index ea0a0f2e8a7..555be7f6723 100644 --- a/test_programs/execution_success/sha256_regression/Prover.toml +++ b/test_programs/execution_success/sha256_regression/Prover.toml @@ -3,7 +3,6 @@ msg_multiple_of_block = [102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 1 msg_just_under_block = [102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, 108, 97, 105, 110, 59] msg_big_not_block_multiple = [102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 115, 45, 97, 115, 99, 105, 105, 13, 10, 109, 105, 109, 101, 45, 118, 101, 114, 115, 105, 111, 110, 58, 49, 46, 48, 32, 40, 77, 97, 99, 32, 79, 83, 32, 88, 32, 77, 97, 105, 108, 32, 49, 54, 46, 48, 32, 92, 40, 51, 55, 51, 49, 46, 53, 48, 48, 46, 50, 51, 49, 92, 41, 41, 13, 10, 115, 117, 98, 106, 101, 99, 116, 58, 72, 101, 108, 108, 111, 13, 10, 109, 101, 115, 115, 97, 103, 101, 45, 105, 100, 58, 60, 56, 70, 56, 49, 57, 68, 51, 50, 45, 66, 54, 65, 67, 45, 52, 56, 57, 68, 45, 57, 55, 55, 70, 45, 52, 51, 56, 66, 66, 67, 52, 67, 65, 66, 50, 55, 64, 109, 101, 46, 99, 111, 109, 62, 13, 10, 100, 97, 116, 101, 58, 83, 97, 116, 44, 32, 50, 54, 32, 65, 117, 103, 32, 50, 48, 50, 51, 32, 49, 50, 58, 50, 53, 58, 50, 50, 32, 43, 48, 52, 48, 48, 13, 10, 116, 111, 58, 122, 107, 101, 119, 116, 101, 115, 116, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 13, 10, 100, 107, 105, 109, 45, 115, 105, 103, 110, 97, 116, 117, 114, 101, 58, 118, 61, 49, 59, 32, 97, 61, 114, 115, 97, 45, 115, 104, 97, 50, 53, 54, 59, 32, 99, 61, 114, 101, 108, 97, 120, 101, 100, 47, 114, 101, 108, 97, 120, 101, 100, 59, 32, 100, 61, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 59, 32, 115, 61, 49, 97, 49, 104, 97, 105, 59, 32, 116, 61, 49, 54, 57, 51, 48, 51, 56, 51, 51, 55, 59, 32, 98, 104, 61, 55, 120, 81, 77, 68, 117, 111, 86, 86, 85, 52, 109, 48, 87, 48, 87, 82, 86, 83, 114, 86, 88, 77, 101, 71, 83, 73, 65, 83, 115, 110, 117, 99, 75, 57, 100, 74, 115, 114, 99, 43, 118, 85, 61, 59, 32, 104, 61, 102, 114, 111, 109, 58, 67, 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 77, 105, 109, 101, 45, 86, 101, 114, 115, 105, 111, 110, 58, 83, 117, 98, 106, 101, 99, 116, 58, 77, 101, 115, 115, 97, 103, 101, 45, 73, 100, 58, 68, 97, 116, 101, 58, 116, 111, 59, 32, 98, 61] msg_big_with_padding = [48, 130, 1, 37, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 48, 130, 1, 17, 48, 37, 2, 1, 1, 4, 32, 176, 223, 31, 133, 108, 84, 158, 102, 70, 11, 165, 175, 196, 12, 201, 130, 25, 131, 46, 125, 156, 194, 28, 23, 55, 133, 157, 164, 135, 136, 220, 78, 48, 37, 2, 1, 2, 4, 32, 190, 82, 180, 235, 222, 33, 79, 50, 152, 136, 142, 35, 116, 224, 6, 242, 156, 141, 128, 248, 10, 61, 98, 86, 248, 45, 207, 210, 90, 232, 175, 38, 48, 37, 2, 1, 3, 4, 32, 0, 194, 104, 108, 237, 246, 97, 230, 116, 198, 69, 110, 26, 87, 17, 89, 110, 199, 108, 250, 36, 21, 39, 87, 110, 102, 250, 213, 174, 131, 171, 174, 48, 37, 2, 1, 11, 4, 32, 136, 155, 87, 144, 111, 15, 152, 127, 85, 25, 154, 81, 20, 58, 51, 75, 193, 116, 234, 0, 60, 30, 29, 30, 183, 141, 72, 247, 255, 203, 100, 124, 48, 37, 2, 1, 12, 4, 32, 41, 234, 106, 78, 31, 11, 114, 137, 237, 17, 92, 71, 134, 47, 62, 78, 189, 233, 201, 214, 53, 4, 47, 189, 201, 133, 6, 121, 34, 131, 64, 142, 48, 37, 2, 1, 13, 4, 32, 91, 222, 210, 193, 62, 222, 104, 82, 36, 41, 138, 253, 70, 15, 148, 208, 156, 45, 105, 171, 241, 195, 185, 43, 217, 162, 146, 201, 222, 89, 238, 38, 48, 37, 2, 1, 14, 4, 32, 76, 123, 216, 13, 51, 227, 72, 245, 59, 193, 238, 166, 103, 49, 23, 164, 171, 188, 194, 197, 156, 187, 249, 28, 198, 95, 69, 15, 182, 56, 54, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] -msg_big_no_padding = [48, 130, 1, 37, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 48, 130, 1, 17, 48, 37, 2, 1, 1, 4, 32, 176, 223, 31, 133, 108, 84, 158, 102, 70, 11, 165, 175, 196, 12, 201, 130, 25, 131, 46, 125, 156, 194, 28, 23, 55, 133, 157, 164, 135, 136, 220, 78, 48, 37, 2, 1, 2, 4, 32, 190, 82, 180, 235, 222, 33, 79, 50, 152, 136, 142, 35, 116, 224, 6, 242, 156, 141, 128, 248, 10, 61, 98, 86, 248, 45, 207, 210, 90, 232, 175, 38, 48, 37, 2, 1, 3, 4, 32, 0, 194, 104, 108, 237, 246, 97, 230, 116, 198, 69, 110, 26, 87, 17, 89, 110, 199, 108, 250, 36, 21, 39, 87, 110, 102, 250, 213, 174, 131, 171, 174, 48, 37, 2, 1, 11, 4, 32, 136, 155, 87, 144, 111, 15, 152, 127, 85, 25, 154, 81, 20, 58, 51, 75, 193, 116, 234, 0, 60, 30, 29, 30, 183, 141, 72, 247, 255, 203, 100, 124, 48, 37, 2, 1, 12, 4, 32, 41, 234, 106, 78, 31, 11, 114, 137, 237, 17, 92, 71, 134, 47, 62, 78, 189, 233, 201, 214, 53, 4, 47, 189, 201, 133, 6, 121, 34, 131, 64, 142, 48, 37, 2, 1, 13, 4, 32, 91, 222, 210, 193, 62, 222, 104, 82, 36, 41, 138, 253, 70, 15, 148, 208, 156, 45, 105, 171, 241, 195, 185, 43, 217, 162, 146, 201, 222, 89, 238, 38, 48, 37, 2, 1, 14, 4, 32, 76, 123, 216, 13, 51, 227, 72, 245, 59, 193, 238, 166, 103, 49, 23, 164, 171, 188, 194, 197, 156, 187, 249, 28, 198, 95, 69, 15, 182, 56, 54, 38] message_size = 297 # Results matched against ethers library diff --git a/test_programs/execution_success/sha256_regression/src/main.nr b/test_programs/execution_success/sha256_regression/src/main.nr index dbbcc07e501..2f74651e55e 100644 --- a/test_programs/execution_success/sha256_regression/src/main.nr +++ b/test_programs/execution_success/sha256_regression/src/main.nr @@ -11,8 +11,6 @@ fn main( result_big: pub [u8; 32], // This message is only 297 elements and we want to hash only a variable amount msg_big_with_padding: [u8; 700], - // This is the same as `msg_big_with_padding` but with no padding - msg_big_no_padding: [u8; 297], message_size: u64, result_big_with_padding: pub [u8; 32], ) { @@ -33,7 +31,4 @@ fn main( let hash_padding = std::hash::sha256_var(msg_big_with_padding, message_size); assert_eq(hash_padding, result_big_with_padding); - - let hash_no_padding = std::hash::sha256_var(msg_big_no_padding, message_size); - assert_eq(hash_no_padding, result_big_with_padding); } diff --git a/test_programs/gates_report_brillig_execution.sh b/test_programs/gates_report_brillig_execution.sh new file mode 100644 index 00000000000..024c7612541 --- /dev/null +++ b/test_programs/gates_report_brillig_execution.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +set -e + +# These tests are incompatible with gas reporting +excluded_dirs=( + "workspace" + "workspace_default_member" + "double_verify_nested_proof" + "overlapping_dep_and_mod" + "comptime_println" + # Takes a very long time to execute as large loops do not get simplified. + "regression_4709" + # bit sizes for bigint operation doesn't match up. + "bigint" + # Expected to fail as test asserts on which runtime it is in. + "is_unconstrained" +) + +current_dir=$(pwd) +base_path="$current_dir/execution_success" +test_dirs=$(ls $base_path) + +# We generate a Noir workspace which contains all of the test cases +# This allows us to generate a gates report using `nargo info` for all of them at once. + +echo "[workspace]" > Nargo.toml +echo "members = [" >> Nargo.toml + +for dir in $test_dirs; do + if [[ " ${excluded_dirs[@]} " =~ " ${dir} " ]]; then + continue + fi + + if [[ ${CI-false} = "true" ]] && [[ " ${ci_excluded_dirs[@]} " =~ " ${dir} " ]]; then + continue + fi + + echo " \"execution_success/$dir\"," >> Nargo.toml +done + +echo "]" >> Nargo.toml + +nargo info --profile-execution --json > gates_report_brillig_execution.json + +rm Nargo.toml \ No newline at end of file diff --git a/tooling/nargo_cli/src/cli/info_cmd.rs b/tooling/nargo_cli/src/cli/info_cmd.rs index d9468bb7e6a..cf416b1fa5f 100644 --- a/tooling/nargo_cli/src/cli/info_cmd.rs +++ b/tooling/nargo_cli/src/cli/info_cmd.rs @@ -1,15 +1,21 @@ use acvm::acir::circuit::ExpressionWidth; +use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; use iter_extended::vecmap; -use nargo::package::{CrateName, Package}; +use nargo::{ + constants::PROVER_INPUT_FILE, + ops::DefaultForeignCallExecutor, + package::{CrateName, Package}, +}; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; +use noirc_abi::input_parser::Format; use noirc_artifacts::program::ProgramArtifact; use noirc_driver::{CompileOptions, NOIR_ARTIFACT_VERSION_STRING}; use prettytable::{row, table, Row}; use rayon::prelude::*; use serde::Serialize; -use crate::errors::CliError; +use crate::{cli::fs::inputs::read_inputs_from_file, errors::CliError}; use super::{ compile_cmd::{compile_workspace_full, get_target_width}, @@ -37,11 +43,18 @@ pub(crate) struct InfoCommand { #[clap(long, hide = true)] json: bool, + #[clap(long)] + profile_execution: bool, + + /// The name of the toml file which contains the inputs for the prover + #[clap(long, short, default_value = PROVER_INPUT_FILE)] + prover_name: String, + #[clap(flatten)] compile_options: CompileOptions, } -pub(crate) fn run(args: InfoCommand, config: NargoConfig) -> Result<(), CliError> { +pub(crate) fn run(mut args: InfoCommand, config: NargoConfig) -> Result<(), CliError> { let toml_path = get_package_manifest(&config.program_dir)?; let default_selection = if args.workspace { PackageSelection::All } else { PackageSelection::DefaultOrAll }; @@ -52,6 +65,11 @@ pub(crate) fn run(args: InfoCommand, config: NargoConfig) -> Result<(), CliError Some(NOIR_ARTIFACT_VERSION_STRING.to_string()), )?; + if args.profile_execution { + // Execution profiling is only relevant with the Brillig VM + // as a constrained circuit should have totally flattened control flow (e.g. loops and if statements). + args.compile_options.force_brillig = true; + } // Compile the full workspace in order to generate any build artifacts. compile_workspace_full(&workspace, &args.compile_options)?; @@ -65,15 +83,29 @@ pub(crate) fn run(args: InfoCommand, config: NargoConfig) -> Result<(), CliError }) .collect::>()?; - let program_info = binary_packages - .into_iter() - .par_bridge() - .map(|(package, program)| { - let target_width = - get_target_width(package.expression_width, args.compile_options.expression_width); - count_opcodes_and_gates_in_program(program, &package, target_width) - }) - .collect(); + let program_info = if args.profile_execution { + assert!( + args.compile_options.force_brillig, + "Internal CLI Error: --force-brillig must be active when --profile-execution is active" + ); + profile_brillig_execution( + binary_packages, + &args.prover_name, + args.compile_options.expression_width, + )? + } else { + binary_packages + .into_iter() + .par_bridge() + .map(|(package, program)| { + let target_width = get_target_width( + package.expression_width, + args.compile_options.expression_width, + ); + count_opcodes_and_gates_in_program(program, &package, target_width) + }) + .collect() + }; let info_report = InfoReport { programs: program_info }; @@ -210,3 +242,42 @@ fn count_opcodes_and_gates_in_program( unconstrained_functions: unconstrained_info, } } + +fn profile_brillig_execution( + binary_packages: Vec<(Package, ProgramArtifact)>, + prover_name: &str, + expression_width: Option, +) -> Result, CliError> { + let mut program_info = Vec::new(); + for (package, program_artifact) in binary_packages.iter() { + // Parse the initial witness values from Prover.toml + let (inputs_map, _) = read_inputs_from_file( + &package.root_dir, + prover_name, + Format::Toml, + &program_artifact.abi, + )?; + let initial_witness = program_artifact.abi.encode(&inputs_map, None)?; + + let (_, profiling_samples) = nargo::ops::execute_program_with_profiling( + &program_artifact.bytecode, + initial_witness, + &Bn254BlackBoxSolver, + &mut DefaultForeignCallExecutor::new(false, None, None, None), + )?; + + let expression_width = get_target_width(package.expression_width, expression_width); + + program_info.push(ProgramInfo { + package_name: package.name.to_string(), + expression_width, + functions: vec![FunctionInfo { name: "main".to_string(), opcodes: 0 }], + unconstrained_functions_opcodes: profiling_samples.len(), + unconstrained_functions: vec![FunctionInfo { + name: "main".to_string(), + opcodes: profiling_samples.len(), + }], + }); + } + Ok(program_info) +}