This repository has been archived by the owner on Apr 9, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(stdlib): Add fallback implementation of
SHA256
black box funct…
…ion (#407) Co-authored-by: kevaundray <[email protected]>
- Loading branch information
1 parent
967ec81
commit 040369a
Showing
12 changed files
with
1,360 additions
and
73 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
#![cfg(feature = "testing")] | ||
mod solver; | ||
use crate::solver::StubbedBackend; | ||
use acir::{ | ||
circuit::{ | ||
opcodes::{BlackBoxFuncCall, FunctionInput}, | ||
Circuit, Opcode, PublicInputs, | ||
}, | ||
native_types::Witness, | ||
FieldElement, | ||
}; | ||
use acvm::{ | ||
compiler::{compile, CircuitSimplifier}, | ||
pwg::{ACVMStatus, ACVM}, | ||
Language, | ||
}; | ||
use proptest::prelude::*; | ||
use sha2::{Digest, Sha256}; | ||
use std::collections::{BTreeMap, BTreeSet}; | ||
use stdlib::blackbox_fallbacks::UInt32; | ||
|
||
proptest! { | ||
#[test] | ||
fn test_uint32_ror(x in 0..u32::MAX, y in 0..32_u32) { | ||
let fe = FieldElement::from(x as u128); | ||
let w = Witness(1); | ||
let result = x.rotate_right(y); | ||
let sha256_u32 = UInt32::new(w); | ||
let (w, extra_gates, _) = sha256_u32.ror(y, 2); | ||
let witness_assignments = BTreeMap::from([(Witness(1), fe)]).into(); | ||
let mut acvm = ACVM::new(StubbedBackend, extra_gates, witness_assignments); | ||
let solver_status = acvm.solve(); | ||
|
||
prop_assert_eq!(acvm.witness_map().get(&w.get_inner()).unwrap(), &FieldElement::from(result as u128)); | ||
prop_assert_eq!(solver_status, ACVMStatus::Solved, "should be fully solved"); | ||
} | ||
|
||
#[test] | ||
fn test_uint32_euclidean_division(x in 0..u32::MAX, y in 0..u32::MAX) { | ||
let lhs = FieldElement::from(x as u128); | ||
let rhs = FieldElement::from(y as u128); | ||
let w1 = Witness(1); | ||
let w2 = Witness(2); | ||
let q = x.div_euclid(y); | ||
let r = x.rem_euclid(y); | ||
let u32_1 = UInt32::new(w1); | ||
let u32_2 = UInt32::new(w2); | ||
let (q_w, r_w, extra_gates, _) = UInt32::euclidean_division(&u32_1, &u32_2, 3); | ||
let witness_assignments = BTreeMap::from([(Witness(1), lhs),(Witness(2), rhs)]).into(); | ||
let mut acvm = ACVM::new(StubbedBackend, extra_gates, witness_assignments); | ||
let solver_status = acvm.solve(); | ||
|
||
prop_assert_eq!(acvm.witness_map().get(&q_w.get_inner()).unwrap(), &FieldElement::from(q as u128)); | ||
prop_assert_eq!(acvm.witness_map().get(&r_w.get_inner()).unwrap(), &FieldElement::from(r as u128)); | ||
prop_assert_eq!(solver_status, ACVMStatus::Solved, "should be fully solved"); | ||
} | ||
|
||
#[test] | ||
fn test_uint32_add(x in 0..u32::MAX, y in 0..u32::MAX, z in 0..u32::MAX) { | ||
let lhs = FieldElement::from(x as u128); | ||
let rhs = FieldElement::from(y as u128); | ||
let rhs_z = FieldElement::from(z as u128); | ||
let result = FieldElement::from(((x as u128).wrapping_add(y as u128) % (1_u128 << 32)).wrapping_add(z as u128) % (1_u128 << 32)); | ||
let w1 = Witness(1); | ||
let w2 = Witness(2); | ||
let w3 = Witness(3); | ||
let u32_1 = UInt32::new(w1); | ||
let u32_2 = UInt32::new(w2); | ||
let u32_3 = UInt32::new(w3); | ||
let mut gates = Vec::new(); | ||
let (w, extra_gates, num_witness) = u32_1.add(&u32_2, 4); | ||
gates.extend(extra_gates); | ||
let (w2, extra_gates, _) = w.add(&u32_3, num_witness); | ||
gates.extend(extra_gates); | ||
let witness_assignments = BTreeMap::from([(Witness(1), lhs), (Witness(2), rhs), (Witness(3), rhs_z)]).into(); | ||
let mut acvm = ACVM::new(StubbedBackend, gates, witness_assignments); | ||
let solver_status = acvm.solve(); | ||
|
||
prop_assert_eq!(acvm.witness_map().get(&w2.get_inner()).unwrap(), &result); | ||
prop_assert_eq!(solver_status, ACVMStatus::Solved, "should be fully solved"); | ||
} | ||
|
||
#[test] | ||
fn test_uint32_sub(x in 0..u32::MAX, y in 0..u32::MAX, z in 0..u32::MAX) { | ||
let lhs = FieldElement::from(x as u128); | ||
let rhs = FieldElement::from(y as u128); | ||
let rhs_z = FieldElement::from(z as u128); | ||
let result = FieldElement::from(((x as u128).wrapping_sub(y as u128) % (1_u128 << 32)).wrapping_sub(z as u128) % (1_u128 << 32)); | ||
let w1 = Witness(1); | ||
let w2 = Witness(2); | ||
let w3 = Witness(3); | ||
let u32_1 = UInt32::new(w1); | ||
let u32_2 = UInt32::new(w2); | ||
let u32_3 = UInt32::new(w3); | ||
let mut gates = Vec::new(); | ||
let (w, extra_gates, num_witness) = u32_1.sub(&u32_2, 4); | ||
gates.extend(extra_gates); | ||
let (w2, extra_gates, _) = w.sub(&u32_3, num_witness); | ||
gates.extend(extra_gates); | ||
let witness_assignments = BTreeMap::from([(Witness(1), lhs), (Witness(2), rhs), (Witness(3), rhs_z)]).into(); | ||
let mut acvm = ACVM::new(StubbedBackend, gates, witness_assignments); | ||
let solver_status = acvm.solve(); | ||
|
||
prop_assert_eq!(acvm.witness_map().get(&w2.get_inner()).unwrap(), &result); | ||
prop_assert_eq!(solver_status, ACVMStatus::Solved, "should be fully solved"); | ||
} | ||
|
||
#[test] | ||
fn test_uint32_left_shift(x in 0..u32::MAX, y in 0..32_u32) { | ||
let lhs = FieldElement::from(x as u128); | ||
let w1 = Witness(1); | ||
let result = x.overflowing_shl(y).0; | ||
let u32_1 = UInt32::new(w1); | ||
let (w, extra_gates, _) = u32_1.leftshift(y, 2); | ||
let witness_assignments = BTreeMap::from([(Witness(1), lhs)]).into(); | ||
let mut acvm = ACVM::new(StubbedBackend, extra_gates, witness_assignments); | ||
let solver_status = acvm.solve(); | ||
|
||
prop_assert_eq!(acvm.witness_map().get(&w.get_inner()).unwrap(), &FieldElement::from(result as u128)); | ||
prop_assert_eq!(solver_status, ACVMStatus::Solved, "should be fully solved"); | ||
} | ||
|
||
#[test] | ||
fn test_uint32_right_shift(x in 0..u32::MAX, y in 0..32_u32) { | ||
let lhs = FieldElement::from(x as u128); | ||
let w1 = Witness(1); | ||
let result = x.overflowing_shr(y).0; | ||
let u32_1 = UInt32::new(w1); | ||
let (w, extra_gates, _) = u32_1.rightshift(y, 2); | ||
let witness_assignments = BTreeMap::from([(Witness(1), lhs)]).into(); | ||
let mut acvm = ACVM::new(StubbedBackend, extra_gates, witness_assignments); | ||
let solver_status = acvm.solve(); | ||
|
||
prop_assert_eq!(acvm.witness_map().get(&w.get_inner()).unwrap(), &FieldElement::from(result as u128)); | ||
prop_assert_eq!(solver_status, ACVMStatus::Solved, "should be fully solved"); | ||
} | ||
} | ||
|
||
proptest! { | ||
#![proptest_config(ProptestConfig::with_cases(3))] | ||
#[test] | ||
fn test_sha256(input_values in proptest::collection::vec(0..u8::MAX, 1..50)) { | ||
let mut opcodes = Vec::new(); | ||
let mut witness_assignments = BTreeMap::new(); | ||
let mut sha256_input_witnesses: Vec<FunctionInput> = Vec::new(); | ||
let mut correct_result_witnesses: Vec<Witness> = Vec::new(); | ||
let mut output_witnesses: Vec<Witness> = Vec::new(); | ||
|
||
// prepare test data | ||
hash_witnesses!(input_values, witness_assignments, sha256_input_witnesses, correct_result_witnesses, output_witnesses, Sha256); | ||
let sha256_blackbox = Opcode::BlackBoxFuncCall(BlackBoxFuncCall::SHA256 { inputs: sha256_input_witnesses, outputs: output_witnesses }); | ||
opcodes.push(sha256_blackbox); | ||
|
||
// compile circuit | ||
let circuit_simplifier = CircuitSimplifier::new(witness_assignments.len() as u32 + 32); | ||
let circuit = Circuit {current_witness_index: witness_assignments.len() as u32 + 32, | ||
opcodes, public_parameters: PublicInputs(BTreeSet::new()), return_values: PublicInputs(BTreeSet::new()) }; | ||
let circuit = compile(circuit, Language::PLONKCSat{ width: 3 }, does_not_support_sha256, &circuit_simplifier).unwrap().0; | ||
|
||
// solve witnesses | ||
let mut acvm = ACVM::new(StubbedBackend, circuit.opcodes, witness_assignments.into()); | ||
let solver_status = acvm.solve(); | ||
|
||
prop_assert_eq!(solver_status, ACVMStatus::Solved, "should be fully solved"); | ||
} | ||
} | ||
|
||
fn does_not_support_sha256(opcode: &Opcode) -> bool { | ||
!matches!(opcode, Opcode::BlackBoxFuncCall(BlackBoxFuncCall::SHA256 { .. })) | ||
} | ||
|
||
#[macro_export] | ||
macro_rules! hash_witnesses { | ||
( | ||
$input_values:ident, | ||
$witness_assignments:ident, | ||
$input_witnesses: ident, | ||
$correct_result_witnesses:ident, | ||
$output_witnesses:ident, | ||
$hasher:ident | ||
) => { | ||
let mut counter = 0; | ||
let output = $hasher::digest($input_values.clone()); | ||
for inp_v in $input_values { | ||
counter += 1; | ||
let function_input = FunctionInput { witness: Witness(counter), num_bits: 8 }; | ||
$input_witnesses.push(function_input); | ||
$witness_assignments.insert(Witness(counter), FieldElement::from(inp_v as u128)); | ||
} | ||
|
||
for o_v in output { | ||
counter += 1; | ||
$correct_result_witnesses.push(Witness(counter)); | ||
$witness_assignments.insert(Witness(counter), FieldElement::from(o_v as u128)); | ||
} | ||
|
||
for _ in 0..32 { | ||
counter += 1; | ||
$output_witnesses.push(Witness(counter)); | ||
} | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 2 additions & 68 deletions
70
stdlib/src/fallback.rs → ...src/blackbox_fallbacks/logic_fallbacks.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
mod logic_fallbacks; | ||
mod sha256; | ||
mod uint32; | ||
mod utils; | ||
pub use logic_fallbacks::{and, range, xor}; | ||
pub use sha256::sha256; | ||
pub use uint32::UInt32; |
Oops, something went wrong.