use acir::brillig::{BlackBoxOp, HeapArray, HeapVector};
use acir::{BlackBoxFunc, FieldElement};
use acvm_blackbox_solver::BigIntSolver;
use acvm_blackbox_solver::{
aes128_encrypt, blake2s, blake3, ecdsa_secp256k1_verify, ecdsa_secp256r1_verify, keccak256,
keccakf1600, sha256, sha256compression, BlackBoxFunctionSolver, BlackBoxResolutionError,
};
use crate::memory::MemoryValue;
use crate::Memory;
fn read_heap_vector<'a>(memory: &'a Memory, vector: &HeapVector) -> &'a [MemoryValue] {
let size = memory.read(vector.size);
memory.read_slice(memory.read_ref(vector.pointer), size.to_usize())
}
fn read_heap_array<'a>(memory: &'a Memory, array: &HeapArray) -> &'a [MemoryValue] {
memory.read_slice(memory.read_ref(array.pointer), array.size)
}
fn to_u8_vec(inputs: &[MemoryValue]) -> Vec<u8> {
let mut result = Vec::with_capacity(inputs.len());
for input in inputs {
result.push(input.try_into().unwrap());
}
result
}
fn to_value_vec(input: &[u8]) -> Vec<MemoryValue> {
input.iter().map(|&x| x.into()).collect()
}
pub(crate) fn evaluate_black_box<Solver: BlackBoxFunctionSolver>(
op: &BlackBoxOp,
solver: &Solver,
memory: &mut Memory,
bigint_solver: &mut BigIntSolver,
) -> Result<(), BlackBoxResolutionError> {
match op {
BlackBoxOp::AES128Encrypt { inputs, iv, key, outputs } => {
let bb_func = black_box_function_from_op(op);
let inputs = to_u8_vec(read_heap_vector(memory, inputs));
let iv: [u8; 16] = to_u8_vec(read_heap_array(memory, iv)).try_into().map_err(|_| {
BlackBoxResolutionError::Failed(bb_func, "Invalid iv length".to_string())
})?;
let key: [u8; 16] =
to_u8_vec(read_heap_array(memory, key)).try_into().map_err(|_| {
BlackBoxResolutionError::Failed(bb_func, "Invalid ley length".to_string())
})?;
let ciphertext = aes128_encrypt(&inputs, iv, key)?;
memory.write(outputs.size, ciphertext.len().into());
memory.write_slice(memory.read_ref(outputs.pointer), &to_value_vec(&ciphertext));
Ok(())
}
BlackBoxOp::Sha256 { message, output } => {
let message = to_u8_vec(read_heap_vector(memory, message));
let bytes = sha256(message.as_slice())?;
memory.write_slice(memory.read_ref(output.pointer), &to_value_vec(&bytes));
Ok(())
}
BlackBoxOp::Blake2s { message, output } => {
let message = to_u8_vec(read_heap_vector(memory, message));
let bytes = blake2s(message.as_slice())?;
memory.write_slice(memory.read_ref(output.pointer), &to_value_vec(&bytes));
Ok(())
}
BlackBoxOp::Blake3 { message, output } => {
let message = to_u8_vec(read_heap_vector(memory, message));
let bytes = blake3(message.as_slice())?;
memory.write_slice(memory.read_ref(output.pointer), &to_value_vec(&bytes));
Ok(())
}
BlackBoxOp::Keccak256 { message, output } => {
let message = to_u8_vec(read_heap_vector(memory, message));
let bytes = keccak256(message.as_slice())?;
memory.write_slice(memory.read_ref(output.pointer), &to_value_vec(&bytes));
Ok(())
}
BlackBoxOp::Keccakf1600 { message, output } => {
let state_vec: Vec<u64> = read_heap_vector(memory, message)
.iter()
.map(|memory_value| memory_value.try_into().unwrap())
.collect();
let state: [u64; 25] = state_vec.try_into().unwrap();
let new_state = keccakf1600(state)?;
let new_state: Vec<MemoryValue> = new_state.into_iter().map(|x| x.into()).collect();
memory.write_slice(memory.read_ref(output.pointer), &new_state);
Ok(())
}
BlackBoxOp::EcdsaSecp256k1 {
hashed_msg,
public_key_x,
public_key_y,
signature,
result: result_address,
}
| BlackBoxOp::EcdsaSecp256r1 {
hashed_msg,
public_key_x,
public_key_y,
signature,
result: result_address,
} => {
let bb_func = black_box_function_from_op(op);
let public_key_x: [u8; 32] =
to_u8_vec(read_heap_array(memory, public_key_x)).try_into().map_err(|_| {
BlackBoxResolutionError::Failed(
bb_func,
"Invalid public key x length".to_string(),
)
})?;
let public_key_y: [u8; 32] =
to_u8_vec(read_heap_array(memory, public_key_y)).try_into().map_err(|_| {
BlackBoxResolutionError::Failed(
bb_func,
"Invalid public key y length".to_string(),
)
})?;
let signature: [u8; 64] =
to_u8_vec(read_heap_array(memory, signature)).try_into().map_err(|_| {
BlackBoxResolutionError::Failed(bb_func, "Invalid signature length".to_string())
})?;
let hashed_msg = to_u8_vec(read_heap_vector(memory, hashed_msg));
let result = match op {
BlackBoxOp::EcdsaSecp256k1 { .. } => {
ecdsa_secp256k1_verify(&hashed_msg, &public_key_x, &public_key_y, &signature)?
}
BlackBoxOp::EcdsaSecp256r1 { .. } => {
ecdsa_secp256r1_verify(&hashed_msg, &public_key_x, &public_key_y, &signature)?
}
_ => unreachable!("`BlackBoxOp` is guarded against being a non-ecdsa operation"),
};
memory.write(*result_address, result.into());
Ok(())
}
BlackBoxOp::SchnorrVerify { public_key_x, public_key_y, message, signature, result } => {
let public_key_x = memory.read(*public_key_x).try_into().unwrap();
let public_key_y = memory.read(*public_key_y).try_into().unwrap();
let message: Vec<u8> = to_u8_vec(read_heap_vector(memory, message));
let signature: [u8; 64] =
to_u8_vec(read_heap_vector(memory, signature)).try_into().unwrap();
let verified =
solver.schnorr_verify(&public_key_x, &public_key_y, &signature, &message)?;
memory.write(*result, verified.into());
Ok(())
}
BlackBoxOp::MultiScalarMul { points, scalars, outputs: result } => {
let points: Vec<FieldElement> =
read_heap_vector(memory, points).iter().map(|x| x.try_into().unwrap()).collect();
let scalars: Vec<FieldElement> =
read_heap_vector(memory, scalars).iter().map(|x| x.try_into().unwrap()).collect();
let (x, y) = solver.multi_scalar_mul(&points, &scalars)?;
memory.write_slice(memory.read_ref(result.pointer), &[x.into(), y.into()]);
Ok(())
}
BlackBoxOp::EmbeddedCurveAdd { input1_x, input1_y, input2_x, input2_y, result } => {
let input1_x = memory.read(*input1_x).try_into().unwrap();
let input1_y = memory.read(*input1_y).try_into().unwrap();
let input2_x = memory.read(*input2_x).try_into().unwrap();
let input2_y = memory.read(*input2_y).try_into().unwrap();
let (x, y) = solver.ec_add(&input1_x, &input1_y, &input2_x, &input2_y)?;
memory.write_slice(memory.read_ref(result.pointer), &[x.into(), y.into()]);
Ok(())
}
BlackBoxOp::PedersenCommitment { inputs, domain_separator, output } => {
let inputs: Vec<FieldElement> =
read_heap_vector(memory, inputs).iter().map(|x| x.try_into().unwrap()).collect();
let domain_separator: u32 =
memory.read(*domain_separator).try_into().map_err(|_| {
BlackBoxResolutionError::Failed(
BlackBoxFunc::PedersenCommitment,
"Invalid signature length".to_string(),
)
})?;
let (x, y) = solver.pedersen_commitment(&inputs, domain_separator)?;
memory.write_slice(memory.read_ref(output.pointer), &[x.into(), y.into()]);
Ok(())
}
BlackBoxOp::PedersenHash { inputs, domain_separator, output } => {
let inputs: Vec<FieldElement> =
read_heap_vector(memory, inputs).iter().map(|x| x.try_into().unwrap()).collect();
let domain_separator: u32 =
memory.read(*domain_separator).try_into().map_err(|_| {
BlackBoxResolutionError::Failed(
BlackBoxFunc::PedersenCommitment,
"Invalid signature length".to_string(),
)
})?;
let hash = solver.pedersen_hash(&inputs, domain_separator)?;
memory.write(*output, hash.into());
Ok(())
}
BlackBoxOp::BigIntAdd { lhs, rhs, output } => {
let lhs = memory.read(*lhs).try_into().unwrap();
let rhs = memory.read(*rhs).try_into().unwrap();
let output = memory.read(*output).try_into().unwrap();
bigint_solver.bigint_op(lhs, rhs, output, BlackBoxFunc::BigIntAdd)?;
Ok(())
}
BlackBoxOp::BigIntSub { lhs, rhs, output } => {
let lhs = memory.read(*lhs).try_into().unwrap();
let rhs = memory.read(*rhs).try_into().unwrap();
let output = memory.read(*output).try_into().unwrap();
bigint_solver.bigint_op(lhs, rhs, output, BlackBoxFunc::BigIntSub)?;
Ok(())
}
BlackBoxOp::BigIntMul { lhs, rhs, output } => {
let lhs = memory.read(*lhs).try_into().unwrap();
let rhs = memory.read(*rhs).try_into().unwrap();
let output = memory.read(*output).try_into().unwrap();
bigint_solver.bigint_op(lhs, rhs, output, BlackBoxFunc::BigIntMul)?;
Ok(())
}
BlackBoxOp::BigIntDiv { lhs, rhs, output } => {
let lhs = memory.read(*lhs).try_into().unwrap();
let rhs = memory.read(*rhs).try_into().unwrap();
let output = memory.read(*output).try_into().unwrap();
bigint_solver.bigint_op(lhs, rhs, output, BlackBoxFunc::BigIntDiv)?;
Ok(())
}
BlackBoxOp::BigIntFromLeBytes { inputs, modulus, output } => {
let input = read_heap_vector(memory, inputs);
let input: Vec<u8> = input.iter().map(|x| x.try_into().unwrap()).collect();
let modulus = read_heap_vector(memory, modulus);
let modulus: Vec<u8> = modulus.iter().map(|x| x.try_into().unwrap()).collect();
let output = memory.read(*output).try_into().unwrap();
bigint_solver.bigint_from_bytes(&input, &modulus, output)?;
Ok(())
}
BlackBoxOp::BigIntToLeBytes { input, output } => {
let input: u32 = memory.read(*input).try_into().unwrap();
let bytes = bigint_solver.bigint_to_bytes(input)?;
let mut values = Vec::new();
for i in 0..32 {
if i < bytes.len() {
values.push(bytes[i].into());
} else {
values.push(0_u8.into());
}
}
memory.write_slice(memory.read_ref(output.pointer), &values);
Ok(())
}
BlackBoxOp::Poseidon2Permutation { message, output, len } => {
let input = read_heap_vector(memory, message);
let input: Vec<FieldElement> = input.iter().map(|x| x.try_into().unwrap()).collect();
let len = memory.read(*len).try_into().unwrap();
let result = solver.poseidon2_permutation(&input, len)?;
let mut values = Vec::new();
for i in result {
values.push(i.into());
}
memory.write_slice(memory.read_ref(output.pointer), &values);
Ok(())
}
BlackBoxOp::Sha256Compression { input, hash_values, output } => {
let mut message = [0; 16];
let inputs = read_heap_vector(memory, input);
if inputs.len() != 16 {
return Err(BlackBoxResolutionError::Failed(
BlackBoxFunc::Sha256Compression,
format!("Expected 16 inputs but encountered {}", &inputs.len()),
));
}
for (i, input) in inputs.iter().enumerate() {
message[i] = input.try_into().unwrap();
}
let mut state = [0; 8];
let values = read_heap_vector(memory, hash_values);
if values.len() != 8 {
return Err(BlackBoxResolutionError::Failed(
BlackBoxFunc::Sha256Compression,
format!("Expected 8 values but encountered {}", &values.len()),
));
}
for (i, value) in values.iter().enumerate() {
state[i] = value.try_into().unwrap();
}
sha256compression(&mut state, &message);
let state = state.map(|x| x.into());
memory.write_slice(memory.read_ref(output.pointer), &state);
Ok(())
}
}
}
fn black_box_function_from_op(op: &BlackBoxOp) -> BlackBoxFunc {
match op {
BlackBoxOp::AES128Encrypt { .. } => BlackBoxFunc::AES128Encrypt,
BlackBoxOp::Sha256 { .. } => BlackBoxFunc::SHA256,
BlackBoxOp::Blake2s { .. } => BlackBoxFunc::Blake2s,
BlackBoxOp::Blake3 { .. } => BlackBoxFunc::Blake3,
BlackBoxOp::Keccak256 { .. } => BlackBoxFunc::Keccak256,
BlackBoxOp::Keccakf1600 { .. } => BlackBoxFunc::Keccakf1600,
BlackBoxOp::EcdsaSecp256k1 { .. } => BlackBoxFunc::EcdsaSecp256k1,
BlackBoxOp::EcdsaSecp256r1 { .. } => BlackBoxFunc::EcdsaSecp256r1,
BlackBoxOp::SchnorrVerify { .. } => BlackBoxFunc::SchnorrVerify,
BlackBoxOp::PedersenCommitment { .. } => BlackBoxFunc::PedersenCommitment,
BlackBoxOp::PedersenHash { .. } => BlackBoxFunc::PedersenHash,
BlackBoxOp::MultiScalarMul { .. } => BlackBoxFunc::MultiScalarMul,
BlackBoxOp::EmbeddedCurveAdd { .. } => BlackBoxFunc::EmbeddedCurveAdd,
BlackBoxOp::BigIntAdd { .. } => BlackBoxFunc::BigIntAdd,
BlackBoxOp::BigIntSub { .. } => BlackBoxFunc::BigIntSub,
BlackBoxOp::BigIntMul { .. } => BlackBoxFunc::BigIntMul,
BlackBoxOp::BigIntDiv { .. } => BlackBoxFunc::BigIntDiv,
BlackBoxOp::BigIntFromLeBytes { .. } => BlackBoxFunc::BigIntFromLeBytes,
BlackBoxOp::BigIntToLeBytes { .. } => BlackBoxFunc::BigIntToLeBytes,
BlackBoxOp::Poseidon2Permutation { .. } => BlackBoxFunc::Poseidon2Permutation,
BlackBoxOp::Sha256Compression { .. } => BlackBoxFunc::Sha256Compression,
}
}
#[cfg(test)]
mod test {
use acir::brillig::{BlackBoxOp, MemoryAddress};
use acvm_blackbox_solver::{BigIntSolver, StubbedBlackBoxSolver};
use crate::{
black_box::{evaluate_black_box, to_u8_vec, to_value_vec},
HeapArray, HeapVector, Memory,
};
#[test]
fn sha256() {
let message: Vec<u8> = b"hello world".to_vec();
let message_length = message.len();
let mut memory = Memory::default();
let message_pointer = 3;
let result_pointer = message_pointer + message_length;
memory.write(MemoryAddress(0), message_pointer.into());
memory.write(MemoryAddress(1), message_length.into());
memory.write(MemoryAddress(2), result_pointer.into());
memory.write_slice(MemoryAddress(message_pointer), to_value_vec(&message).as_slice());
let op = BlackBoxOp::Sha256 {
message: HeapVector { pointer: 0.into(), size: 1.into() },
output: HeapArray { pointer: 2.into(), size: 32 },
};
evaluate_black_box(&op, &StubbedBlackBoxSolver, &mut memory, &mut BigIntSolver::default())
.unwrap();
let result = memory.read_slice(MemoryAddress(result_pointer), 32);
assert_eq!(
to_u8_vec(result),
vec![
185, 77, 39, 185, 147, 77, 62, 8, 165, 46, 82, 215, 218, 125, 171, 250, 196, 132,
239, 227, 122, 83, 128, 238, 144, 136, 247, 172, 226, 239, 205, 233
]
);
}
}