Skip to content

Commit

Permalink
feat(acvm_js): export black box solver functions (#2812)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomAFrench authored Sep 29, 2023
1 parent d1ffa07 commit da8a98e
Show file tree
Hide file tree
Showing 5 changed files with 535 additions and 0 deletions.
100 changes: 100 additions & 0 deletions acvm-repo/acvm_js/src/black_box_solvers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use js_sys::JsString;
use wasm_bindgen::prelude::*;

use crate::js_witness_map::{field_element_to_js_string, js_value_to_field_element};
use acvm::FieldElement;

/// Performs a bitwise AND operation between `lhs` and `rhs`
#[wasm_bindgen]
pub fn and(lhs: JsString, rhs: JsString) -> JsString {
let lhs = js_value_to_field_element(lhs.into()).unwrap();
let rhs = js_value_to_field_element(rhs.into()).unwrap();
let result = lhs.and(&rhs, FieldElement::max_num_bits());
field_element_to_js_string(&result)
}

/// Performs a bitwise XOR operation between `lhs` and `rhs`
#[wasm_bindgen]
pub fn xor(lhs: JsString, rhs: JsString) -> JsString {
let lhs = js_value_to_field_element(lhs.into()).unwrap();
let rhs = js_value_to_field_element(rhs.into()).unwrap();
let result = lhs.xor(&rhs, FieldElement::max_num_bits());
field_element_to_js_string(&result)
}

/// Calculates the SHA256 hash of the input bytes
#[wasm_bindgen]
pub fn sha256(inputs: &[u8]) -> Vec<u8> {
acvm::blackbox_solver::sha256(inputs).unwrap().into()
}

/// Calculates the Blake2s256 hash of the input bytes
#[wasm_bindgen]
pub fn blake2s256(inputs: &[u8]) -> Vec<u8> {
acvm::blackbox_solver::blake2s(inputs).unwrap().into()
}

/// Calculates the Keccak256 hash of the input bytes
#[wasm_bindgen]
pub fn keccak256(inputs: &[u8]) -> Vec<u8> {
acvm::blackbox_solver::keccak256(inputs).unwrap().into()
}

/// Calculates the Blake2s256 hash of the input bytes and represents these as a single field element.
// #[wasm_bindgen]
// pub fn hash_to_field_128_security(inputs: Vec<JsString>) -> JsString {
// let input_bytes: Vec<u8> = inputs
// .into_iter()
// .flat_map(|field_string| {
// let field_element = js_value_to_field_element(field_string.into()).unwrap();
// witness_assignment.fetch_nearest_bytes(FieldElement::max_num_bits());
// })
// .collect();
// field_element_to_js_string(
// &acvm::blackbox_solver::hash_to_field_128_security(&input_bytes).unwrap(),
// )
// }

/// Verifies a ECDSA signature over the secp256k1 curve.
#[wasm_bindgen]
pub fn ecdsa_secp256k1_verify(
hashed_msg: &[u8],
public_key_x_bytes: &[u8],
public_key_y_bytes: &[u8],
signature: &[u8],
) -> bool {
let public_key_x_bytes: &[u8; 32] = public_key_x_bytes.try_into().unwrap();
let public_key_y_bytes: &[u8; 32] = public_key_y_bytes.try_into().unwrap();
let signature: &[u8; 64] = signature.try_into().unwrap();

acvm::blackbox_solver::ecdsa_secp256k1_verify(
hashed_msg,
public_key_x_bytes,
public_key_y_bytes,
signature,
)
.unwrap()
.into()
}

/// Verifies a ECDSA signature over the secp256r1 curve.
#[wasm_bindgen]
pub fn ecdsa_secp256r1_verify(
hashed_msg: &[u8],
public_key_x_bytes: &[u8],
public_key_y_bytes: &[u8],
signature: &[u8],
) -> bool {
let public_key_x_bytes: &[u8; 32] = public_key_x_bytes.try_into().unwrap();
let public_key_y_bytes: &[u8; 32] = public_key_y_bytes.try_into().unwrap();
let signature: &[u8; 64] = signature.try_into().unwrap();

acvm::blackbox_solver::ecdsa_secp256r1_verify(
hashed_msg,
public_key_x_bytes,
public_key_y_bytes,
signature,
)
.unwrap()
.into()
}
2 changes: 2 additions & 0 deletions acvm-repo/acvm_js/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ cfg_if::cfg_if! {
mod logging;
mod public_witness;
mod js_execution_error;
mod black_box_solvers;

pub use black_box_solvers::{and, xor, sha256, blake2s256, keccak256, ecdsa_secp256k1_verify, ecdsa_secp256r1_verify};
pub use build_info::build_info;
pub use compression::{compress_witness, decompress_witness};
pub use execute::{execute_circuit, execute_circuit_with_black_box_solver, create_black_box_solver};
Expand Down
131 changes: 131 additions & 0 deletions acvm-repo/acvm_js/test/browser/black_box_solvers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { expect } from "@esm-bundle/chai";
import initACVM, {
and,
blake2s256,
ecdsa_secp256k1_verify,
ecdsa_secp256r1_verify,
initLogLevel,
keccak256,
sha256,
xor,
} from "@noir-lang/acvm_js";

beforeEach(async () => {
await initACVM();

initLogLevel("INFO");
});

it("successfully calculates the bitwise AND of two fields", async () => {
const { and_test_cases } = await import("../shared/black_box_solvers");

for (const testCase of and_test_cases) {
const [[lhs, rhs], expectedResult] = testCase;
expect(and(lhs, rhs)).to.be.eq(expectedResult);
}
});

it("successfully calculates the bitwise XOR of two fields", async () => {
const { xor_test_cases } = await import("../shared/black_box_solvers");

for (const testCase of xor_test_cases) {
const [[lhs, rhs], expectedResult] = testCase;
expect(xor(lhs, rhs)).to.be.eq(expectedResult);
}
});

it("successfully calculates the sha256 hash", async () => {
const { sha256_test_cases } = await import("../shared/black_box_solvers");

for (const testCase of sha256_test_cases) {
const [preimage, expectedResult] = testCase;
const hash = sha256(preimage);
hash.forEach((value, index) =>
expect(value).to.be.eq(expectedResult.at(index)),
);
}
});

it("successfully calculates the blake2s256 hash", async () => {
const { blake2s256_test_cases } = await import("../shared/black_box_solvers");

for (const testCase of blake2s256_test_cases) {
const [preimage, expectedResult] = testCase;
const hash = blake2s256(preimage);
hash.forEach((value, index) =>
expect(value).to.be.eq(expectedResult.at(index)),
);
}
});

it("successfully calculates the keccak256 hash", async () => {
const { keccak256_test_cases } = await import("../shared/black_box_solvers");

for (const testCase of keccak256_test_cases) {
const [preimage, expectedResult] = testCase;
const hash = keccak256(preimage);
hash.forEach((value, index) =>
expect(value).to.be.eq(expectedResult.at(index)),
);
}
});

// it("successfully calculates the hash_to_field_128_security field", async () => {
// const { hash_to_field_128_security_test_cases } = await import(
// "../shared/black_box_solvers"
// );

// for (const testCase of hash_to_field_128_security_test_cases) {
// const [preimage, expectedResult] = testCase;
// const hashField = hash_to_field_128_security(preimage);
// expect(hashField).to.be.eq(expectedResult);
// }
// });

it("successfully verifies secp256k1 ECDSA signatures", async () => {
const { ecdsa_secp256k1_test_cases } = await import(
"../shared/black_box_solvers"
);

for (const testCase of ecdsa_secp256k1_test_cases) {
const [[hashed_msg, pubkey_x, pubkey_y, signature], expectedResult] =
testCase;

expect(hashed_msg.length).to.be.eq(32);
expect(pubkey_x.length).to.be.eq(32);
expect(pubkey_y.length).to.be.eq(32);
expect(signature.length).to.be.eq(64);

const result = ecdsa_secp256k1_verify(
hashed_msg,
pubkey_x,
pubkey_y,
signature,
);
expect(result).to.be.eq(expectedResult);
}
});

it("successfully verifies secp256r1 ECDSA signatures", async () => {
const { ecdsa_secp256r1_test_cases } = await import(
"../shared/black_box_solvers"
);

for (const testCase of ecdsa_secp256r1_test_cases) {
const [[hashed_msg, pubkey_x, pubkey_y, signature], expectedResult] =
testCase;

expect(hashed_msg.length).to.be.eq(32);
expect(pubkey_x.length).to.be.eq(32);
expect(pubkey_y.length).to.be.eq(32);
expect(signature.length).to.be.eq(64);

const result = ecdsa_secp256r1_verify(
hashed_msg,
pubkey_x,
pubkey_y,
signature,
);
expect(result).to.be.eq(expectedResult);
}
});
124 changes: 124 additions & 0 deletions acvm-repo/acvm_js/test/node/black_box_solvers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { expect } from "chai";
import {
and,
blake2s256,
ecdsa_secp256k1_verify,
ecdsa_secp256r1_verify,
keccak256,
sha256,
xor,
} from "@noir-lang/acvm_js";

it("successfully calculates the bitwise AND of two fields", async () => {
const { and_test_cases } = await import("../shared/black_box_solvers");

for (const testCase of and_test_cases) {
const [[lhs, rhs], expectedResult] = testCase;
expect(and(lhs, rhs)).to.be.eq(expectedResult);
}
});

it("successfully calculates the bitwise XOR of two fields", async () => {
const { xor_test_cases } = await import("../shared/black_box_solvers");

for (const testCase of xor_test_cases) {
const [[lhs, rhs], expectedResult] = testCase;
expect(xor(lhs, rhs)).to.be.eq(expectedResult);
}
});

it("successfully calculates the sha256 hash", async () => {
const { sha256_test_cases } = await import("../shared/black_box_solvers");

for (const testCase of sha256_test_cases) {
const [preimage, expectedResult] = testCase;
const hash = sha256(preimage);
hash.forEach((value, index) =>
expect(value).to.be.eq(expectedResult.at(index)),
);
}
});

it("successfully calculates the blake2s256 hash", async () => {
const { blake2s256_test_cases } = await import("../shared/black_box_solvers");

for (const testCase of blake2s256_test_cases) {
const [preimage, expectedResult] = testCase;
const hash = blake2s256(preimage);
hash.forEach((value, index) =>
expect(value).to.be.eq(expectedResult.at(index)),
);
}
});

it("successfully calculates the keccak256 hash", async () => {
const { keccak256_test_cases } = await import("../shared/black_box_solvers");

for (const testCase of keccak256_test_cases) {
const [preimage, expectedResult] = testCase;
const hash = keccak256(preimage);
hash.forEach((value, index) =>
expect(value).to.be.eq(expectedResult.at(index)),
);
}
});

// it("successfully calculates the hash_to_field_128_security field", async () => {
// const { hash_to_field_128_security_test_cases } = await import(
// "../shared/black_box_solvers"
// );

// for (const testCase of hash_to_field_128_security_test_cases) {
// const [preimage, expectedResult] = testCase;
// const hashField = hash_to_field_128_security(preimage);
// expect(hashField).to.be.eq(expectedResult);
// }
// });

it("successfully verifies secp256k1 ECDSA signatures", async () => {
const { ecdsa_secp256k1_test_cases } = await import(
"../shared/black_box_solvers"
);

for (const testCase of ecdsa_secp256k1_test_cases) {
const [[hashed_msg, pubkey_x, pubkey_y, signature], expectedResult] =
testCase;

expect(hashed_msg.length).to.be.eq(32);
expect(pubkey_x.length).to.be.eq(32);
expect(pubkey_y.length).to.be.eq(32);
expect(signature.length).to.be.eq(64);

const result = ecdsa_secp256k1_verify(
hashed_msg,
pubkey_x,
pubkey_y,
signature,
);
expect(result).to.be.eq(expectedResult);
}
});

it("successfully verifies secp256r1 ECDSA signatures", async () => {
const { ecdsa_secp256r1_test_cases } = await import(
"../shared/black_box_solvers"
);

for (const testCase of ecdsa_secp256r1_test_cases) {
const [[hashed_msg, pubkey_x, pubkey_y, signature], expectedResult] =
testCase;

expect(hashed_msg.length).to.be.eq(32);
expect(pubkey_x.length).to.be.eq(32);
expect(pubkey_y.length).to.be.eq(32);
expect(signature.length).to.be.eq(64);

const result = ecdsa_secp256r1_verify(
hashed_msg,
pubkey_x,
pubkey_y,
signature,
);
expect(result).to.be.eq(expectedResult);
}
});
Loading

0 comments on commit da8a98e

Please sign in to comment.