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

feat: simplify constant calls to poseidon2_permutation, schnorr_verify and embedded_curve_add #5140

Merged
merged 25 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d6fae8a
feat: perform any constant pedersen blackbox functions at compile-time
TomAFrench May 29, 2024
243d158
chore: fix test relying on pedersen commitments not being calculated …
TomAFrench May 29, 2024
9b7c4bc
chore: gracefully fail when unable to simplify pedersen functions
TomAFrench Jun 10, 2024
726b2ac
chore: add simplification for more blackbox functions
TomAFrench Jun 10, 2024
0ba04e5
Merge branch 'master' into tf/more-comptime-optimization
TomAFrench Jun 12, 2024
65d57d5
Merge branch 'master' into tf/more-comptime-optimization
TomAFrench Jun 19, 2024
cb155e5
chore: fix build
TomAFrench Jun 19, 2024
4aa0cef
Merge branch 'master' into tf/more-comptime-optimization
TomAFrench Jul 10, 2024
8c7aa7b
Merge branch 'master' into tf/more-comptime-optimization
TomAFrench Jul 11, 2024
1ff2194
.
TomAFrench Jul 11, 2024
cd7fb0d
Merge branch 'master' into tf/more-comptime-optimization
TomAFrench Jul 15, 2024
21a756d
Merge branch 'master' into tf/more-comptime-optimization
TomAFrench Jul 19, 2024
7425c1b
.
TomAFrench Jul 19, 2024
dda132c
.
TomAFrench Jul 19, 2024
dcc5982
.
TomAFrench Jul 19, 2024
7d3e2e8
Merge branch 'master' into tf/more-comptime-optimization
TomAFrench Aug 10, 2024
603088a
.
TomAFrench Aug 21, 2024
25deec0
Merge branch 'master' into tf/more-comptime-optimization
TomAFrench Aug 21, 2024
386a785
Merge branch 'master' into tf/more-comptime-optimization
TomAFrench Aug 28, 2024
7ea10b9
.
TomAFrench Aug 28, 2024
4505a9c
.
TomAFrench Aug 28, 2024
d97effe
.
TomAFrench Aug 28, 2024
9ed1919
.
TomAFrench Aug 28, 2024
de51683
.
TomAFrench Aug 28, 2024
40871e1
Update compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variabl…
TomAFrench Aug 28, 2024
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
1 change: 1 addition & 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ criterion = "0.5.0"
# https://github.com/tikv/pprof-rs/pull/172
pprof = { version = "0.13", features = ["flamegraph", "criterion"] }


cfg-if = "1.0.0"
dirs = "4"
serde = { version = "1.0.136", features = ["derive"] }
serde_json = "1.0"
Expand Down
2 changes: 1 addition & 1 deletion acvm-repo/acir_field/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ ark-bn254.workspace = true
ark-bls12-381 = { workspace = true, optional = true }
ark-ff.workspace = true

cfg-if = "1.0.0"
cfg-if.workspace = true

[dev-dependencies]
proptest.workspace = true
Expand Down
4 changes: 4 additions & 0 deletions compiler/noirc_driver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,7 @@ rust-embed.workspace = true
tracing.workspace = true

aztec_macros = { path = "../../aztec_macros" }

[features]
bn254 = ["noirc_frontend/bn254", "noirc_evaluator/bn254"]
bls12_381 = ["noirc_frontend/bls12_381", "noirc_evaluator/bls12_381"]
7 changes: 6 additions & 1 deletion compiler/noirc_evaluator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ serde_json.workspace = true
serde_with = "3.2.0"
tracing.workspace = true
chrono = "0.4.37"
cfg-if.workspace = true

[dev-dependencies]
proptest.workspace = true
proptest.workspace = true

[features]
bn254 = ["noirc_frontend/bn254"]
bls12_381= []
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use crate::ssa::ir::{instruction::Endian, types::NumericType};
use acvm::acir::circuit::brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs};
use acvm::acir::circuit::opcodes::{AcirFunctionId, BlockId, BlockType, MemOp};
use acvm::acir::circuit::{AssertionPayload, ExpressionOrMemory, ExpressionWidth, Opcode};
use acvm::blackbox_solver;
use acvm::brillig_vm::{MemoryValue, VMStatus, VM};
use acvm::{
acir::AcirField,
Expand Down Expand Up @@ -2128,7 +2127,11 @@ fn execute_brillig<F: AcirField>(
}

// Instantiate a Brillig VM given the solved input registers and memory, along with the Brillig bytecode.
let mut vm = VM::new(calldata, code, Vec::new(), &blackbox_solver::StubbedBlackBoxSolver);
//
// We pass a stubbed solver here as a concrete solver implies a field choice which conflicts with this function
// being generic.
let solver = acvm::blackbox_solver::StubbedBlackBoxSolver;
vezenovm marked this conversation as resolved.
Show resolved Hide resolved
let mut vm = VM::new(calldata, code, Vec::new(), &solver);

// Run the Brillig VM on these inputs, bytecode, etc!
let vm_status = vm.process_opcodes();
Expand Down
45 changes: 31 additions & 14 deletions compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use fxhash::FxHashMap as HashMap;
use std::{collections::VecDeque, rc::Rc};

use acvm::{acir::AcirField, acir::BlackBoxFunc, BlackBoxResolutionError, FieldElement};
use acvm::{
acir::{AcirField, BlackBoxFunc},
BlackBoxResolutionError, FieldElement,
};
use bn254_blackbox_solver::derive_generators;
use iter_extended::vecmap;
use num_bigint::BigUint;
Expand All @@ -20,6 +23,8 @@ use crate::ssa::{

use super::{Binary, BinaryOp, Endian, Instruction, SimplifyResult};

mod blackbox;

/// Try to simplify this call instruction. If the instruction can be simplified to a known value,
/// that value is returned. Otherwise None is returned.
///
Expand Down Expand Up @@ -468,11 +473,17 @@ fn simplify_black_box_func(
arguments: &[ValueId],
dfg: &mut DataFlowGraph,
) -> SimplifyResult {
cfg_if::cfg_if! {
if #[cfg(feature = "bn254")] {
let solver = bn254_blackbox_solver::Bn254BlackBoxSolver;
} else {
let solver = acvm::blackbox_solver::StubbedBlackBoxSolver;
}
};
match bb_func {
BlackBoxFunc::SHA256 => simplify_hash(dfg, arguments, acvm::blackbox_solver::sha256),
BlackBoxFunc::Blake2s => simplify_hash(dfg, arguments, acvm::blackbox_solver::blake2s),
BlackBoxFunc::Blake3 => simplify_hash(dfg, arguments, acvm::blackbox_solver::blake3),
BlackBoxFunc::PedersenCommitment | BlackBoxFunc::PedersenHash => SimplifyResult::None,
BlackBoxFunc::Keccakf1600 => {
if let Some((array_input, _)) = dfg.get_array_constant(arguments[0]) {
if array_is_constant(dfg, &array_input) {
Expand Down Expand Up @@ -503,20 +514,26 @@ fn simplify_black_box_func(
BlackBoxFunc::Keccak256 => {
unreachable!("Keccak256 should have been replaced by calls to Keccakf1600")
}
BlackBoxFunc::Poseidon2Permutation => SimplifyResult::None, //TODO(Guillaume)
BlackBoxFunc::EcdsaSecp256k1 => {
simplify_signature(dfg, arguments, acvm::blackbox_solver::ecdsa_secp256k1_verify)
}
BlackBoxFunc::EcdsaSecp256r1 => {
simplify_signature(dfg, arguments, acvm::blackbox_solver::ecdsa_secp256r1_verify)
BlackBoxFunc::Poseidon2Permutation => {
blackbox::simplify_poseidon2_permutation(dfg, solver, arguments)
}
BlackBoxFunc::EcdsaSecp256k1 => blackbox::simplify_signature(
dfg,
arguments,
acvm::blackbox_solver::ecdsa_secp256k1_verify,
),
BlackBoxFunc::EcdsaSecp256r1 => blackbox::simplify_signature(
dfg,
arguments,
acvm::blackbox_solver::ecdsa_secp256r1_verify,
),

BlackBoxFunc::PedersenCommitment
| BlackBoxFunc::PedersenHash
| BlackBoxFunc::MultiScalarMul => SimplifyResult::None,
BlackBoxFunc::EmbeddedCurveAdd => blackbox::simplify_ec_add(dfg, solver, arguments),
BlackBoxFunc::SchnorrVerify => blackbox::simplify_schnorr_verify(dfg, solver, arguments),

BlackBoxFunc::MultiScalarMul
| BlackBoxFunc::SchnorrVerify
| BlackBoxFunc::EmbeddedCurveAdd => {
// Currently unsolvable here as we rely on an implementation in the backend.
SimplifyResult::None
}
BlackBoxFunc::BigIntAdd
| BlackBoxFunc::BigIntSub
| BlackBoxFunc::BigIntMul
Expand Down
190 changes: 190 additions & 0 deletions compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
use std::rc::Rc;

use acvm::{acir::AcirField, BlackBoxFunctionSolver, BlackBoxResolutionError, FieldElement};
use iter_extended::vecmap;

use crate::ssa::ir::{
dfg::DataFlowGraph, instruction::SimplifyResult, types::Type, value::ValueId,
};

use super::{array_is_constant, make_constant_array, to_u8_vec};

pub(super) fn simplify_ec_add(
dfg: &mut DataFlowGraph,
solver: impl BlackBoxFunctionSolver<FieldElement>,
arguments: &[ValueId],
) -> SimplifyResult {
match (
TomAFrench marked this conversation as resolved.
Show resolved Hide resolved
dfg.get_numeric_constant(arguments[0]),
dfg.get_numeric_constant(arguments[1]),
dfg.get_numeric_constant(arguments[2]),
dfg.get_numeric_constant(arguments[3]),
dfg.get_numeric_constant(arguments[4]),
dfg.get_numeric_constant(arguments[5]),
) {
(
Some(point1_x),
Some(point1_y),
Some(point1_is_infinity),
Some(point2_x),
Some(point2_y),
Some(point2_is_infinity),
) => {
let Ok((result_x, result_y, result_is_infinity)) = solver.ec_add(
&point1_x,
&point1_y,
&point1_is_infinity,
&point2_x,
&point2_y,
&point2_is_infinity,
) else {
return SimplifyResult::None;
};

let result_x = dfg.make_constant(result_x, Type::field());
let result_y = dfg.make_constant(result_y, Type::field());
let result_is_infinity = dfg.make_constant(result_is_infinity, Type::bool());

let typ = Type::Array(Rc::new(vec![Type::field()]), 3);
let result_array =
dfg.make_array(im::vector![result_x, result_y, result_is_infinity], typ);

SimplifyResult::SimplifiedTo(result_array)
}
_ => SimplifyResult::None,
}
}

pub(super) fn simplify_poseidon2_permutation(
dfg: &mut DataFlowGraph,
solver: impl BlackBoxFunctionSolver<FieldElement>,
arguments: &[ValueId],
) -> SimplifyResult {
match (dfg.get_array_constant(arguments[0]), dfg.get_numeric_constant(arguments[1])) {
(Some((state, _)), Some(state_length)) if array_is_constant(dfg, &state) => {
let state: Vec<FieldElement> = state
.iter()
.map(|id| {
dfg.get_numeric_constant(*id)
.expect("value id from array should point at constant")
})
.collect();

let Some(state_length) = state_length.try_to_u32() else {
return SimplifyResult::None;
};

let Ok(new_state) = solver.poseidon2_permutation(&state, state_length) else {
return SimplifyResult::None;
};

let result_array = make_constant_array(dfg, new_state, Type::field());
TomAFrench marked this conversation as resolved.
Show resolved Hide resolved

SimplifyResult::SimplifiedTo(result_array)
}
_ => SimplifyResult::None,
}
}

pub(super) fn simplify_schnorr_verify(
dfg: &mut DataFlowGraph,
solver: impl BlackBoxFunctionSolver<FieldElement>,
arguments: &[ValueId],
) -> SimplifyResult {
match (
dfg.get_numeric_constant(arguments[0]),
dfg.get_numeric_constant(arguments[1]),
dfg.get_array_constant(arguments[2]),
dfg.get_array_constant(arguments[3]),
) {
(Some(public_key_x), Some(public_key_y), Some((signature, _)), Some((message, _)))
if array_is_constant(dfg, &signature) && array_is_constant(dfg, &message) =>
{
let signature = to_u8_vec(dfg, signature);
let signature: [u8; 64] =
signature.try_into().expect("Compiler should produce correctly sized signature");

let message = to_u8_vec(dfg, message);

let Ok(valid_signature) =
solver.schnorr_verify(&public_key_x, &public_key_y, &signature, &message)
else {
return SimplifyResult::None;
};

let valid_signature = dfg.make_constant(valid_signature.into(), Type::bool());
SimplifyResult::SimplifiedTo(valid_signature)
}
_ => SimplifyResult::None,
}
}

pub(super) fn simplify_hash(
dfg: &mut DataFlowGraph,
arguments: &[ValueId],
hash_function: fn(&[u8]) -> Result<[u8; 32], BlackBoxResolutionError>,
) -> SimplifyResult {
match dfg.get_array_constant(arguments[0]) {
Some((input, _)) if array_is_constant(dfg, &input) => {
let input_bytes: Vec<u8> = to_u8_vec(dfg, input);

let hash = hash_function(&input_bytes)
.expect("Rust solvable black box function should not fail");

let hash_values = vecmap(hash, |byte| FieldElement::from_be_bytes_reduce(&[byte]));

let result_array = make_constant_array(dfg, hash_values, Type::unsigned(8));
SimplifyResult::SimplifiedTo(result_array)
}
_ => SimplifyResult::None,
}
}

type ECDSASignatureVerifier = fn(
hashed_msg: &[u8],
public_key_x: &[u8; 32],
public_key_y: &[u8; 32],
signature: &[u8; 64],
) -> Result<bool, BlackBoxResolutionError>;

pub(super) fn simplify_signature(
dfg: &mut DataFlowGraph,
arguments: &[ValueId],
signature_verifier: ECDSASignatureVerifier,
) -> SimplifyResult {
match (
TomAFrench marked this conversation as resolved.
Show resolved Hide resolved
dfg.get_array_constant(arguments[0]),
dfg.get_array_constant(arguments[1]),
dfg.get_array_constant(arguments[2]),
dfg.get_array_constant(arguments[3]),
) {
(
Some((public_key_x, _)),
Some((public_key_y, _)),
Some((signature, _)),
Some((hashed_message, _)),
) if array_is_constant(dfg, &public_key_x)
&& array_is_constant(dfg, &public_key_y)
&& array_is_constant(dfg, &signature)
&& array_is_constant(dfg, &hashed_message) =>
{
let public_key_x: [u8; 32] = to_u8_vec(dfg, public_key_x)
.try_into()
.expect("ECDSA public key fields are 32 bytes");
let public_key_y: [u8; 32] = to_u8_vec(dfg, public_key_y)
.try_into()
.expect("ECDSA public key fields are 32 bytes");
let signature: [u8; 64] =
to_u8_vec(dfg, signature).try_into().expect("ECDSA signatures are 64 bytes");
let hashed_message: Vec<u8> = to_u8_vec(dfg, hashed_message);

let valid_signature =
signature_verifier(&hashed_message, &public_key_x, &public_key_y, &signature)
.expect("Rust solvable black box function should not fail");

let valid_signature = dfg.make_constant(valid_signature.into(), Type::bool());
SimplifyResult::SimplifiedTo(valid_signature)
}
_ => SimplifyResult::None,
}
}
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ num-traits.workspace = true
rustc-hash = "1.1.0"
small-ord-set = "0.1.3"
regex = "1.9.1"
cfg-if = "1.0.0"
cfg-if.workspace = true
tracing.workspace = true
petgraph = "0.6"
rangemap = "1.4.0"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "embedded_curve_add_simplification"
type = "bin"
authors = [""]
compiler_version = ">=0.23.0"

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use std::embedded_curve_ops::{EmbeddedCurvePoint, EmbeddedCurveScalar, multi_scalar_mul};

fn main() {
let zero = EmbeddedCurvePoint::point_at_infinity();
let g1 = EmbeddedCurvePoint { x: 1, y: 17631683881184975370165255887551781615748388533673675138860, is_infinite: false };

assert(g1 + zero == g1);
assert(g1 - g1 == zero);
assert(g1 - zero == g1);
assert(zero + zero == zero);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "poseidon2_simplification"
type = "bin"
authors = [""]
compiler_version = ">=0.33.0"

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use std::hash::poseidon2;

fn main() {
let digest = poseidon2::Poseidon2::hash([0], 1);
let expected_digest = 0x2710144414c3a5f2354f4c08d52ed655b9fe253b4bf12cb9ad3de693d9b1db11;
assert_eq(digest, expected_digest);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "schnorr_simplification"
type = "bin"
authors = [""]

[dependencies]
Loading
Loading