Skip to content

Commit 1e2cb9a

Browse files
authored
Merge branch 'master' into jf/remove-vector-start
2 parents 86a2e3c + e65591d commit 1e2cb9a

67 files changed

Lines changed: 1743 additions & 892 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{"suite":"keccak256","name":"keccak256::oracle_tests::test_keccak256_1"}
22
{"suite":"keccak256","name":"keccak256::oracle_tests::test_keccak256_100"}
33
{"suite":"keccak256","name":"keccak256::oracle_tests::test_keccak256_135"}
4+
{"suite":"keccak256","name":"keccak256::oracle_tests::test_keccak256_136"}
45
{"suite":"keccak256","name":"keccak256::oracle_tests::test_keccak256_256"}

acvm-repo/acvm/src/compiler/validator.rs

Lines changed: 93 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -367,10 +367,14 @@ pub fn validate_witness<F: AcirField>(
367367
solver.check_memory_op(op, &witness_map, opcode_index)?;
368368
}
369369
Opcode::MemoryInit { block_id, init, .. } => {
370-
MemoryOpSolver::new(init, &witness_map).map(|solver| {
371-
let existing_block_id = block_solvers.insert(*block_id, solver);
372-
assert!(existing_block_id.is_none(), "Memory block already initialized");
373-
})?;
370+
let solver = MemoryOpSolver::new(init, &witness_map)?;
371+
let existing_block_id = block_solvers.insert(*block_id, solver);
372+
if existing_block_id.is_some() {
373+
return Err(unsatisfied_constraint(
374+
opcode_index,
375+
format!("Attempted reinitialization of memory block {:?}", block_id.0,),
376+
));
377+
}
374378
}
375379
// BrilligCall is unconstrained
376380
Opcode::BrilligCall { .. } => (),
@@ -443,14 +447,32 @@ mod tests {
443447
use acir::{
444448
AcirField, FieldElement,
445449
circuit::{
446-
Circuit, Opcode, PublicInputs,
447-
opcodes::{BlackBoxFuncCall, FunctionInput},
450+
Circuit, Opcode, OpcodeLocation, PublicInputs,
451+
brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs},
452+
opcodes::{AcirFunctionId, BlackBoxFuncCall, BlockId, FunctionInput, MemOp},
448453
},
449454
native_types::{Expression, Witness, WitnessMap},
450455
};
451456
use bn254_blackbox_solver::Bn254BlackBoxSolver;
452457

453458
use super::validate_witness;
459+
use crate::pwg::{
460+
ErrorLocation, OpcodeNotSolvable, OpcodeResolutionError, ResolvedAssertionPayload,
461+
};
462+
463+
fn assert_unsatisfied_constraint(
464+
result: Result<(), OpcodeResolutionError<FieldElement>>,
465+
opcode_index: usize,
466+
message: &str,
467+
) {
468+
assert_eq!(
469+
result.unwrap_err(),
470+
OpcodeResolutionError::UnsatisfiedConstrain {
471+
opcode_location: ErrorLocation::Resolved(OpcodeLocation::Acir(opcode_index)),
472+
payload: Some(ResolvedAssertionPayload::String(message.to_string())),
473+
},
474+
);
475+
}
454476

455477
/// Helper to create a simple circuit with the given opcodes
456478
fn make_circuit(opcodes: Vec<Opcode<FieldElement>>) -> Circuit<FieldElement> {
@@ -511,7 +533,11 @@ mod tests {
511533
]));
512534

513535
let backend = Bn254BlackBoxSolver;
514-
assert!(validate_witness(&backend, witness_map, &circuit).is_err());
536+
assert_unsatisfied_constraint(
537+
validate_witness(&backend, witness_map, &circuit),
538+
0,
539+
"Invalid witness assignment: w1 + w2 - w3",
540+
);
515541
}
516542

517543
#[test]
@@ -564,7 +590,11 @@ mod tests {
564590
]));
565591

566592
let backend = Bn254BlackBoxSolver;
567-
assert!(validate_witness(&backend, witness_map, &circuit).is_err());
593+
assert_unsatisfied_constraint(
594+
validate_witness(&backend, witness_map, &circuit),
595+
0,
596+
"RANGE opcode violation: value 256 does not fit in 8 bits",
597+
);
568598
}
569599

570600
#[test]
@@ -604,7 +634,11 @@ mod tests {
604634
]));
605635

606636
let backend = Bn254BlackBoxSolver;
607-
assert!(validate_witness(&backend, witness_map, &circuit).is_err());
637+
assert_unsatisfied_constraint(
638+
validate_witness(&backend, witness_map, &circuit),
639+
0,
640+
"AND opcode violation: 10 AND 12 != 15 for 8 bits",
641+
);
608642
}
609643

610644
#[test]
@@ -644,7 +678,11 @@ mod tests {
644678
]));
645679

646680
let backend = Bn254BlackBoxSolver;
647-
assert!(validate_witness(&backend, witness_map, &circuit).is_err());
681+
assert_unsatisfied_constraint(
682+
validate_witness(&backend, witness_map, &circuit),
683+
0,
684+
"XOR opcode violation: 10 XOR 12 != 15 for 8 bits",
685+
);
648686
}
649687

650688
#[test]
@@ -661,15 +699,15 @@ mod tests {
661699
let witness_map = WitnessMap::default();
662700

663701
let backend = Bn254BlackBoxSolver;
664-
// The expression evaluates with missing witness, but won't be zero
665-
// so this should fail
666-
assert!(validate_witness(&backend, witness_map, &circuit).is_err());
702+
assert_unsatisfied_constraint(
703+
validate_witness(&backend, witness_map, &circuit),
704+
0,
705+
"Invalid witness assignment: w1",
706+
);
667707
}
668708

669709
#[test]
670710
fn test_call_opcode_valid() {
671-
use acir::circuit::opcodes::AcirFunctionId;
672-
673711
let circuit = make_circuit(vec![Opcode::Call {
674712
id: AcirFunctionId(1),
675713
inputs: vec![Witness(1), Witness(2)],
@@ -689,8 +727,6 @@ mod tests {
689727

690728
#[test]
691729
fn test_call_opcode_missing_input() {
692-
use acir::circuit::opcodes::AcirFunctionId;
693-
694730
let circuit = make_circuit(vec![Opcode::Call {
695731
id: AcirFunctionId(1),
696732
inputs: vec![Witness(1), Witness(2)],
@@ -705,13 +741,14 @@ mod tests {
705741
]));
706742

707743
let backend = Bn254BlackBoxSolver;
708-
assert!(validate_witness(&backend, witness_map, &circuit).is_err());
744+
assert_eq!(
745+
validate_witness(&backend, witness_map, &circuit).unwrap_err(),
746+
OpcodeResolutionError::OpcodeNotSolvable(OpcodeNotSolvable::MissingAssignment(2)),
747+
);
709748
}
710749

711750
#[test]
712751
fn test_call_opcode_missing_output() {
713-
use acir::circuit::opcodes::AcirFunctionId;
714-
715752
let circuit = make_circuit(vec![Opcode::Call {
716753
id: AcirFunctionId(1),
717754
inputs: vec![Witness(1), Witness(2)],
@@ -726,13 +763,14 @@ mod tests {
726763
]));
727764

728765
let backend = Bn254BlackBoxSolver;
729-
assert!(validate_witness(&backend, witness_map, &circuit).is_err());
766+
assert_eq!(
767+
validate_witness(&backend, witness_map, &circuit).unwrap_err(),
768+
OpcodeResolutionError::OpcodeNotSolvable(OpcodeNotSolvable::MissingAssignment(3)),
769+
);
730770
}
731771

732772
#[test]
733773
fn test_call_opcode_skipped_with_zero_predicate() {
734-
use acir::circuit::opcodes::AcirFunctionId;
735-
736774
// Predicate is zero, so call should be skipped even with missing witnesses
737775
let circuit = make_circuit(vec![Opcode::Call {
738776
id: AcirFunctionId(1),
@@ -756,8 +794,6 @@ mod tests {
756794

757795
#[test]
758796
fn test_memory_init_and_read() {
759-
use acir::circuit::opcodes::{BlockId, MemOp};
760-
761797
let block_id = BlockId(0);
762798

763799
let circuit = make_circuit(vec![
@@ -784,8 +820,6 @@ mod tests {
784820

785821
#[test]
786822
fn test_memory_read_wrong_value() {
787-
use acir::circuit::opcodes::{BlockId, MemOp};
788-
789823
let block_id = BlockId(0);
790824

791825
let circuit = make_circuit(vec![
@@ -805,13 +839,15 @@ mod tests {
805839
]));
806840

807841
let backend = Bn254BlackBoxSolver;
808-
assert!(validate_witness(&backend, witness_map, &circuit).is_err());
842+
assert_unsatisfied_constraint(
843+
validate_witness(&backend, witness_map, &circuit),
844+
1,
845+
"Memory read opcode violation at index 0: expected 42 but found 99",
846+
);
809847
}
810848

811849
#[test]
812850
fn test_memory_write_then_read() {
813-
use acir::circuit::opcodes::{BlockId, MemOp};
814-
815851
let block_id = BlockId(0);
816852

817853
let circuit = make_circuit(vec![
@@ -841,8 +877,6 @@ mod tests {
841877

842878
#[test]
843879
fn test_brillig_call_with_empty_witness_map() {
844-
use acir::circuit::brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs};
845-
846880
// Create a BrilligCall opcode with input and output witnesses
847881
// Brillig calls are unconstrained and should be skipped during validation,
848882
// so this should pass even with an empty witness map
@@ -862,4 +896,31 @@ mod tests {
862896
let backend = Bn254BlackBoxSolver;
863897
assert!(validate_witness(&backend, witness_map, &circuit).is_ok());
864898
}
899+
900+
#[test]
901+
fn error_on_memory_init_duplicate_block_id() {
902+
let block_id = BlockId(0);
903+
904+
let circuit = make_circuit(vec![
905+
Opcode::MemoryInit {
906+
block_id,
907+
init: vec![],
908+
block_type: acir::circuit::opcodes::BlockType::Memory,
909+
},
910+
Opcode::MemoryInit {
911+
block_id,
912+
init: vec![],
913+
block_type: acir::circuit::opcodes::BlockType::Memory,
914+
},
915+
]);
916+
917+
let witness_map = WitnessMap::default();
918+
let backend = Bn254BlackBoxSolver;
919+
920+
assert_unsatisfied_constraint(
921+
validate_witness(&backend, witness_map, &circuit),
922+
1,
923+
format!("Attempted reinitialization of memory block {}", block_id.0).as_str(),
924+
);
925+
}
865926
}

compiler/noirc_evaluator/src/ssa/interpreter/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1379,7 +1379,7 @@ impl<'ssa, W: Write> Interpreter<'ssa, W> {
13791379
elements.iter().zip(element_types.iter().cycle()).enumerate()
13801380
{
13811381
let actual_type = element.get_type();
1382-
if &actual_type != expected_type {
1382+
if !actual_type.canonical_eq(expected_type) {
13831383
return Err(internal(InternalError::MakeArrayElementTypeMismatch {
13841384
result,
13851385
index,

compiler/noirc_evaluator/src/ssa/interpreter/tests/instructions.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,6 +1161,31 @@ fn make_array() {
11611161
assert_eq!(values[3], Value::vector(hello, Arc::new(vec![Type::char()])));
11621162
}
11631163

1164+
#[test]
1165+
fn make_array_allows_reference_mutability_mismatch() {
1166+
// Reference mutability is a frontend concern with no meaning at the SSA
1167+
// level: the validator accepts a `&mut T` value in a `&T` MakeArray slot,
1168+
// and the interpreter must agree so that running the post-validation SSA
1169+
// doesn't fail with `MakeArrayElementTypeMismatch`. The unconstrained
1170+
// SSA-gen pattern this guards is a tuple `[&mut T, &T]` constructed from
1171+
// a mutable allocate alongside an immutable one — exactly what the
1172+
// `pass_vs_prev` fuzzer surfaces when it interprets intermediate SSA
1173+
// between passes.
1174+
executes_with_no_errors(
1175+
"
1176+
brillig(inline) fn main f0 {
1177+
b0():
1178+
v0 = allocate -> &mut Field
1179+
store Field 1 at v0
1180+
v1 = allocate -> &Field
1181+
store Field 2 at v1
1182+
v2 = make_array [v0, v1] : [&Field; 2]
1183+
return
1184+
}
1185+
",
1186+
);
1187+
}
1188+
11641189
#[test]
11651190
fn nop() {
11661191
executes_with_no_errors(

compiler/noirc_evaluator/src/ssa/ir/function.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -372,12 +372,11 @@ pub(crate) struct Signature {
372372
impl Signature {
373373
/// Construct a [Signature] whose parameter and return types have all
374374
/// reference mutability canonicalized away. This makes `&T` and `&mut T`
375-
/// compare equal when a [Signature] is used as a map key, matching the
376-
/// validator's `types_equal_ignoring_reference_mutability` leniency and
377-
/// the frontend's `&mut T → &T` coercion.
375+
/// compare equal when a [Signature] is used as a map key, matching
376+
/// [Type::canonical_eq] leniency and the frontend's `&mut T → &T` coercion.
378377
pub(crate) fn new(mut params: Vec<Type>, mut returns: Vec<Type>) -> Self {
379378
for typ in params.iter_mut().chain(returns.iter_mut()) {
380-
typ.canonicalize_reference_mutability();
379+
typ.canonicalize();
381380
}
382381
Self { params, returns }
383382
}

compiler/noirc_evaluator/src/ssa/ir/instruction.rs

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -635,10 +635,21 @@ impl Instruction {
635635
}
636636

637637
/// Replaces values present in this instruction with other values according to the given mapping.
638-
pub(crate) fn replace_values(&mut self, mapping: &ValueMapping) {
639-
if !mapping.is_empty() {
640-
self.map_values_mut(|value_id| mapping.get(value_id));
638+
///
639+
/// Returns `true` if any value was actually replaced.
640+
pub(crate) fn replace_values(&mut self, mapping: &ValueMapping) -> bool {
641+
if mapping.is_empty() {
642+
return false;
641643
}
644+
let mut changed = false;
645+
self.map_values_mut(|value_id| {
646+
let new_value = mapping.get(value_id);
647+
if new_value != value_id {
648+
changed = true;
649+
}
650+
new_value
651+
});
652+
changed
642653
}
643654

644655
/// Maps each ValueId inside this instruction to a new ValueId, returning the new instruction.
@@ -1178,3 +1189,56 @@ where
11781189
focus.set(i, y);
11791190
}
11801191
}
1192+
1193+
#[cfg(test)]
1194+
mod tests {
1195+
use super::*;
1196+
use acvm::acir::brillig::lengths::SemanticLength;
1197+
1198+
#[test]
1199+
fn replace_values_returns_true_only_when_a_value_changes() {
1200+
let v0 = ValueId::test_new(0);
1201+
let v1 = ValueId::test_new(1);
1202+
let v2 = ValueId::test_new(2);
1203+
1204+
let mut mapping = ValueMapping::default();
1205+
mapping.insert(v1, v2);
1206+
1207+
let mut instruction_using_v1 = Instruction::Cast(v1, NumericType::NativeField);
1208+
assert!(instruction_using_v1.replace_values(&mapping));
1209+
assert!(matches!(instruction_using_v1, Instruction::Cast(v, _) if v == v2));
1210+
1211+
let mut instruction_using_v0 = Instruction::Cast(v0, NumericType::NativeField);
1212+
assert!(!instruction_using_v0.replace_values(&mapping));
1213+
assert!(matches!(instruction_using_v0, Instruction::Cast(v, _) if v == v0));
1214+
1215+
let empty_mapping = ValueMapping::default();
1216+
let mut instruction = Instruction::Cast(v1, NumericType::NativeField);
1217+
assert!(!instruction.replace_values(&empty_mapping));
1218+
}
1219+
1220+
#[test]
1221+
fn replace_values_returns_true_when_a_make_array_element_changes() {
1222+
let v0 = ValueId::test_new(0);
1223+
let v1 = ValueId::test_new(1);
1224+
let v2 = ValueId::test_new(2);
1225+
1226+
let mut mapping = ValueMapping::default();
1227+
mapping.insert(v1, v2);
1228+
1229+
let typ = Type::Array(std::sync::Arc::new(vec![Type::field()]), SemanticLength(2));
1230+
let mut instruction =
1231+
Instruction::MakeArray { elements: im::Vector::from(vec![v0, v1]), typ: typ.clone() };
1232+
assert!(instruction.replace_values(&mapping));
1233+
let Instruction::MakeArray { elements, .. } = instruction else { unreachable!() };
1234+
assert_eq!(elements[0], v0);
1235+
assert_eq!(elements[1], v2);
1236+
1237+
let mut unrelated =
1238+
Instruction::MakeArray { elements: im::Vector::from(vec![v0, v0]), typ };
1239+
assert!(!unrelated.replace_values(&mapping));
1240+
let Instruction::MakeArray { elements, .. } = unrelated else { unreachable!() };
1241+
assert_eq!(elements[0], v0);
1242+
assert_eq!(elements[1], v0);
1243+
}
1244+
}

0 commit comments

Comments
 (0)