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(sol-honk): integrate solidity honk verifier with bb and acir tests #7573

Merged
merged 42 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
fa6a841
feat(honk): verifier start
Maddiaa0 Feb 26, 2024
fabd169
temp
Maddiaa0 Mar 4, 2024
43cd61a
Merge branch 'master' into md/02-26-feat_honk_verifier_start
Maddiaa0 Mar 19, 2024
323e7b0
temp - get it compiling
Maddiaa0 Mar 20, 2024
ff22c01
Merge branch 'master' into md/02-26-feat_honk_verifier_start
Maddiaa0 Mar 20, 2024
1ed41cb
temp
Maddiaa0 Mar 20, 2024
78d45b0
temp 2
Maddiaa0 Mar 20, 2024
b7881f1
feat: fiat shamir complete
Maddiaa0 Mar 20, 2024
a724d30
feat: experiment with a custom Fr type
Maddiaa0 Mar 21, 2024
dd85f2d
feat: oink verifier stages
Maddiaa0 Mar 21, 2024
538f432
feat: arith, perm, lookup, gen perm relation
Maddiaa0 Mar 23, 2024
654ab55
feat: field inversions
Maddiaa0 Mar 23, 2024
b9fe7ba
feat: elliptic relation
Maddiaa0 Mar 23, 2024
7f94eb2
feat: sumcheck complete
Maddiaa0 Mar 25, 2024
b510ca8
feat: start zm
Maddiaa0 Mar 26, 2024
1da70e0
feat: more zm
Maddiaa0 Mar 26, 2024
6c5da89
feat: honk verification
Maddiaa0 Mar 27, 2024
d131397
chore: remove logs for gas accounting
Maddiaa0 Mar 27, 2024
b914b69
🧹
Maddiaa0 Mar 30, 2024
1443210
🧹
Maddiaa0 Mar 30, 2024
ecd7af6
🧹
Maddiaa0 Mar 30, 2024
2f9a5d0
🧹
Maddiaa0 Apr 1, 2024
60e2a7a
chore: add verifier for 3 circuit types
Maddiaa0 Apr 1, 2024
56c28fb
fix: fix fuzz tests
Maddiaa0 Apr 1, 2024
3f5a478
fix: add extra check and invert checkSum result
Maddiaa0 Apr 16, 2024
f08a0ec
Merge branch 'master' into md/02-26-feat_honk_verifier_start
Maddiaa0 Jul 16, 2024
9d7a651
temp
Maddiaa0 Jul 17, 2024
10a8eea
fix: Update honk implementation: add logderiv, scaled etas
Maddiaa0 Jul 21, 2024
b2e9eee
fix
Maddiaa0 Jul 21, 2024
eaa1c0e
Merge branch 'master' into md/02-26-feat_honk_verifier_start
Maddiaa0 Jul 21, 2024
4d26743
fix: clean up comments
Maddiaa0 Jul 21, 2024
d2906cf
fix: remove box commit
Maddiaa0 Jul 22, 2024
ebf730a
temp
Maddiaa0 Jul 22, 2024
f48e25b
feat: run acir tests through honk solidity verifier
Maddiaa0 Jul 22, 2024
2547a1e
feat: add to earthly
Maddiaa0 Jul 22, 2024
9205a31
fix: cleanup codegen + earthly
Maddiaa0 Jul 22, 2024
5363e3a
fix: update keygen
Maddiaa0 Jul 23, 2024
2ab22da
Merge branch 'master' into md/sol-honk-codegen
Maddiaa0 Jul 23, 2024
cdfe6d6
chore: regenerate keys after merge
Maddiaa0 Jul 23, 2024
af20b64
fix: rm file
Maddiaa0 Jul 23, 2024
61d2278
Merge branch 'master' into md/sol-honk-codegen
Maddiaa0 Jul 23, 2024
241508e
Update ci.yml to run tests
codygunton Jul 25, 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
17 changes: 17 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,21 @@ jobs:
timeout-minutes: 40
run: earthly-ci --no-output ./+barretenberg-acir-tests-sol

bb-acir-tests-sol-honk:
needs: [noir-build-acir-tests, changes]
runs-on: ${{ github.event.pull_request.user.login || github.actor }}-x86
if: ${{ needs.changes.outputs.barretenberg == 'true' || needs.changes.outputs.noir == 'true' }}
steps:
- uses: actions/checkout@v4
with: { ref: "${{ env.GIT_COMMIT }}" }
- uses: ./.github/ci-setup-action
with:
concurrency_key: barretenberg-acir-tests-sol-honk-x86
- name: "BB Solidity Acir Tests"
working-directory: ./barretenberg/
timeout-minutes: 40
run: earthly-ci --no-output ./+barretenberg-acir-tests-sol-honk

bb-acir-tests-bb-js:
needs: [noir-build-acir-tests, changes]
runs-on: ${{ github.event.pull_request.user.login || github.actor }}-x86
Expand Down Expand Up @@ -667,6 +682,7 @@ jobs:
- noir-build-acir-tests
- bb-acir-tests-bb
- bb-acir-tests-sol
- bb-acir-tests-sol-honk
- bb-acir-tests-bb-js
- noir-format
- noir-test
Expand Down Expand Up @@ -717,6 +733,7 @@ jobs:
- noir-build-acir-tests
- bb-acir-tests-bb
- bb-acir-tests-sol
- bb-acir-tests-sol-honk
- bb-acir-tests-bb-js
- noir-format
- noir-test
Expand Down
17 changes: 17 additions & 0 deletions barretenberg/Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,23 @@ barretenberg-acir-tests-sol:
RUN (cd sol-test && yarn)
RUN PARALLEL=1 FLOW=sol ./run_acir_tests.sh assert_statement double_verify_proof double_verify_nested_proof

barretenberg-acir-tests-sol-honk:
FROM ../build-images/+from-registry

COPY ./cpp/+preset-sol/ /usr/src/barretenberg/cpp/build
COPY ./cpp/+preset-clang-assert/bin/bb /usr/src/barretenberg/cpp/build/bin/bb
COPY ./+acir-tests/ /usr/src/barretenberg/acir_tests
COPY ./+sol/ /usr/src/barretenberg/sol
COPY ../noir/+build-acir-tests/ /usr/src/acir_artifacts

WORKDIR /usr/src/barretenberg/acir_tests

ENV TEST_SRC /usr/src/acir_artifacts
ENV VERBOSE=1

RUN (cd sol-test && yarn)
RUN PARALLEL=1 FLOW=honk_sol ./run_acir_tests.sh assert_statement 1_mul slices

barretenberg-acir-tests-bb.js:
# Playwright not supported on base image ubuntu:noble, results in unmet dependencies
FROM ../build-images/+base-slim-node
Expand Down
22 changes: 22 additions & 0 deletions barretenberg/acir_tests/flows/honk_sol.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/sh
set -eu

export PROOF="$(pwd)/proof"
export PROOF_AS_FIELDS="$(pwd)/proof_fields.json"

# Create a proof, write the solidity contract, write the proof as fields in order to extract the public inputs
$BIN prove_keccak_ultra_honk -o proof
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have included a second bb command that uses the UltraKeccak flavor for proving, in its current form this is not very user friendly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🫡

$BIN write_vk_ultra_honk -o vk
$BIN proof_as_fields_honk -k vk -c $CRS_PATH -p $PROOF
$BIN contract_ultra_honk -k vk -c $CRS_PATH -b ./target/program.json -o Verifier.sol

# Export the paths to the environment variables for the js test runner
export VERIFIER_PATH="$(pwd)/Verifier.sol"
export TEST_PATH=$(realpath "../../sol-test/HonkTest.sol")
export TESTING_HONK="true"

# Use solcjs to compile the generated key contract with the template verifier and test contract
# index.js will start an anvil, on a random port
# Deploy the verifier then send a test transaction
export TEST_NAME=$(basename $(pwd))
node ../../sol-test/src/index.js
18 changes: 18 additions & 0 deletions barretenberg/acir_tests/sol-test/HonkTest.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// THIS FILE WILL NOT COMPILE BY ITSELF
// Compilation is handled in `src/index.js` where solcjs gathers the dependencies

pragma solidity >=0.8.4;

import {HonkVerifier} from "./Verifier.sol";

contract Test {
HonkVerifier verifier;

constructor() {
verifier = new HonkVerifier();
}

function test(bytes calldata proof, bytes32[] calldata publicInputs) view public returns(bool) {
return verifier.verify(proof, publicInputs);
}
}
77 changes: 59 additions & 18 deletions barretenberg/acir_tests/sol-test/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { spawn } from "child_process";
import { ethers } from "ethers";
import solc from "solc";

const NUMBER_OF_FIELDS_IN_PROOF = 93;
const NUMBER_OF_FIELDS_IN_PLONK_PROOF = 93;
const NUMBER_OF_FIELDS_IN_HONK_PROOF = 393;

// We use the solcjs compiler version in this test, although it is slower than foundry, to run the test end to end
// it simplifies of parallelising the test suite
Expand All @@ -18,6 +19,14 @@ const NUMBER_OF_FIELDS_IN_PROOF = 93;
// 5. Run the test against the deployed contract
// 6. Kill the anvil instance

const getEnvVarCanBeUndefined = (envvar) => {
const varVal = process.env[envvar];
if (!varVal) {
return false;
}
return varVal;
};

const getEnvVar = (envvar) => {
const varVal = process.env[envvar];
if (!varVal) {
Expand All @@ -30,33 +39,23 @@ const getEnvVar = (envvar) => {
const testName = getEnvVar("TEST_NAME");

// Get solidity files, passed into environment from `flows/sol.sh`
const keyPath = getEnvVar("KEY_PATH");
const verifierPath = getEnvVar("VERIFIER_PATH");
const testPath = getEnvVar("TEST_PATH");
const basePath = getEnvVar("BASE_PATH");
const verifierPath = getEnvVar("VERIFIER_PATH");
const encoding = { encoding: "utf8" };
const [key, test, verifier, base] = await Promise.all([
fsPromises.readFile(keyPath, encoding),
const [test, verifier] = await Promise.all([
fsPromises.readFile(testPath, encoding),
fsPromises.readFile(verifierPath, encoding),
fsPromises.readFile(basePath, encoding),
]);

var input = {
export const compilationInput = {
language: "Solidity",
sources: {
"Key.sol": {
content: key,
},
"Test.sol": {
content: test,
},
"Verifier.sol": {
content: verifier,
},
"BaseUltraVerifier.sol": {
content: base,
},
},
settings: {
// we require the optimizer
Expand All @@ -72,7 +71,30 @@ var input = {
},
};

var output = JSON.parse(solc.compile(JSON.stringify(input)));
// If testing honk is set, then we compile the honk test suite
const testingHonk = getEnvVarCanBeUndefined("TESTING_HONK");
const NUMBER_OF_FIELDS_IN_PROOF = testingHonk ? NUMBER_OF_FIELDS_IN_HONK_PROOF : NUMBER_OF_FIELDS_IN_PLONK_PROOF;
if (!testingHonk) {

const keyPath = getEnvVar("KEY_PATH");
const basePath = getEnvVar("BASE_PATH");
const [key, base] = await Promise.all(
[
fsPromises.readFile(keyPath, encoding),
fsPromises.readFile(basePath, encoding),
]
);

compilationInput.sources["BaseUltraVerifier.sol"] = {
content: base,
};
compilationInput.sources["Key.sol"] = {
content: key,
};
}

var output = JSON.parse(solc.compile(JSON.stringify(compilationInput)));

const contract = output.contracts["Test.sol"]["Test"];
const bytecode = contract.evm.bytecode.object;
const abi = contract.abi;
Expand Down Expand Up @@ -127,8 +149,15 @@ const readPublicInputs = (proofAsFields) => {
const publicInputs = [];
// A proof with no public inputs is 93 fields long
const numPublicInputs = proofAsFields.length - NUMBER_OF_FIELDS_IN_PROOF;
let publicInputsOffset = 0;

// Honk proofs contain 3 pieces of metadata before the public inputs, while plonk does not
if (testingHonk) {
publicInputsOffset = 3;
}

for (let i = 0; i < numPublicInputs; i++) {
publicInputs.push(proofAsFields[i]);
publicInputs.push(proofAsFields[publicInputsOffset + i]);
}
return [numPublicInputs, publicInputs];
};
Expand Down Expand Up @@ -179,8 +208,20 @@ try {
const proofPath = getEnvVar("PROOF");
const proof = readFileSync(proofPath);

// Cut the number of public inputs off of the proof string
const proofStr = `0x${proof.toString("hex").substring(64 * numPublicInputs)}`;
// Cut the number of public inputs out of the proof string
let proofStr = proof.toString("hex");
if (testingHonk) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The verifier's interface is verify(proof, publicInputs[]). However, in it's current form honk proofs have 3 elements before the public inputs, so I (and the user) will need to slice these out in order to have proofs which can be processed by the verifier.

I did not want to change the serialization of the proofs in this pr, but it is something to think about ahead of the noir switch over

// Cut off the serialised buffer size at start
proofStr = proofStr.substring(8);
// Get the part before and after the public inputs
const proofStart = proofStr.slice(0, 64 * 3);
const proofEnd = proofStr.substring((64 * 3) + (64 * numPublicInputs));
proofStr = proofStart + proofEnd;
} else {
proofStr = proofStr.substring(64 * numPublicInputs);
}

proofStr = "0x" + proofStr;

const key =
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
Expand Down
46 changes: 46 additions & 0 deletions barretenberg/cpp/src/barretenberg/bb/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
#include "barretenberg/common/map.hpp"
#include "barretenberg/common/serialize.hpp"
#include "barretenberg/dsl/acir_format/acir_format.hpp"
#include "barretenberg/dsl/acir_proofs/honk_contract.hpp"
#include "barretenberg/honk/proof_system/types/proof.hpp"
#include "barretenberg/plonk/proof_system/proving_key/serialize.hpp"
#include "barretenberg/serialize/cbind.hpp"
#include "barretenberg/stdlib/honk_recursion/verifier/client_ivc_recursive_verifier.hpp"
#include "barretenberg/stdlib_circuit_builders/ultra_flavor.hpp"
#include "barretenberg/stdlib_circuit_builders/ultra_keccak.hpp"

#include <cstddef>
#ifndef DISABLE_AZTEC_VM
#include "barretenberg/vm/avm_trace/avm_common.hpp"
Expand Down Expand Up @@ -791,6 +795,40 @@ void contract(const std::string& output_path, const std::string& vk_path)
}
}

/**
* @brief Writes a Honk Solidity verifier contract for an ACIR circuit to a file
*
* Communication:
* - stdout: The Solidity verifier contract is written to stdout as a string
* - Filesystem: The Solidity verifier contract is written to the path specified by outputPath
*
* Note: The fact that the contract was computed is for an ACIR circuit is not of importance
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: note unclear

* because this method uses the verification key to compute the Solidity verifier contract
*
* @param output_path Path to write the contract to
* @param vk_path Path to the file containing the serialized verification key
*/
void contract_honk(const std::string& output_path, const std::string& vk_path)
{
using VerificationKey = UltraFlavor::VerificationKey;
using VerifierCommitmentKey = bb::VerifierCommitmentKey<curve::BN254>;

auto g2_data = get_bn254_g2_data(CRS_PATH);
srs::init_crs_factory({}, g2_data);
auto vk = std::make_shared<VerificationKey>(from_buffer<VerificationKey>(read_file(vk_path)));
vk->pcs_verification_key = std::make_shared<VerifierCommitmentKey>();

std::string contract = get_honk_solidity_verifier(std::move(vk));

if (output_path == "-") {
writeStringToStdout(contract);
vinfo("contract written to stdout");
} else {
write_file(output_path, { contract.begin(), contract.end() });
vinfo("contract written to: ", output_path);
}
}

/**
* @brief Converts a proof from a byte array into a list of field elements
*
Expand Down Expand Up @@ -1360,6 +1398,9 @@ int main(int argc, char* argv[])
} else if (command == "contract") {
std::string output_path = get_option(args, "-o", "./target/contract.sol");
contract(output_path, vk_path);
} else if (command == "contract_ultra_honk") {
std::string output_path = get_option(args, "-o", "./target/contract.sol");
contract_honk(output_path, vk_path);
} else if (command == "write_vk") {
std::string output_path = get_option(args, "-o", "./target/vk");
write_vk(bytecode_path, output_path);
Expand Down Expand Up @@ -1390,8 +1431,13 @@ int main(int argc, char* argv[])
} else if (command == "prove_ultra_honk") {
std::string output_path = get_option(args, "-o", "./proofs/proof");
prove_honk<UltraFlavor>(bytecode_path, witness_path, output_path);
} else if (command == "prove_keccak_ultra_honk") {
std::string output_path = get_option(args, "-o", "./proofs/proof");
prove_honk<UltraKeccakFlavor>(bytecode_path, witness_path, output_path);
} else if (command == "verify_ultra_honk") {
return verify_honk<UltraFlavor>(proof_path, vk_path) ? 0 : 1;
} else if (command == "verify_keccak_ultra_honk") {
return verify_honk<UltraKeccakFlavor>(proof_path, vk_path) ? 0 : 1;
} else if (command == "write_vk_ultra_honk") {
std::string output_path = get_option(args, "-o", "./target/vk");
write_vk_honk<UltraFlavor>(bytecode_path, output_path);
Expand Down
Loading
Loading