Skip to content

Commit 39cc87a

Browse files
committed
feat: optimize solidity honk verifier
Complete, without zk Cuts off a million next steps: proof size has a large consequence with this one, due to memory expansion, so having a different proof type for the keccak one which does tighter serialisation will save a good chunk of gas - working on this next - pr here #16018
1 parent 49bd6da commit 39cc87a

File tree

23 files changed

+8995
-78
lines changed

23 files changed

+8995
-78
lines changed

barretenberg/acir_tests/bootstrap.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ function test_cmds {
152152
for t in assert_statement a_1_mul slices verify_honk_proof; do
153153
echo "$sol_prefix $scripts/bb_prove_sol_verify.sh $t --disable_zk"
154154
echo "$sol_prefix $scripts/bb_prove_sol_verify.sh $t"
155+
echo "$sol_prefix USE_OPTIMIZED_CONTRACT=true $scripts/bb_prove_sol_verify.sh $t"
155156
done
156157
# prove with bb cli and verify with bb.js classes
157158
echo "$sol_prefix $scripts/bb_prove_bbjs_verify.sh a_1_mul"

barretenberg/acir_tests/scripts/bb_prove_sol_verify.sh

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ for arg in "$@"; do
1616
flags+=" $arg"
1717
done
1818

19+
USE_OPTIMIZED_CONTRACT=${USE_OPTIMIZED_CONTRACT:-false}
20+
21+
write_contract_flags=$flags
22+
if [[ -z "$USE_OPTIMIZED_CONTRACT" ]]; then
23+
write_contract_flags+=" --optimized"
24+
fi
25+
1926
# Check if --disable_zk is in the flags to determine HAS_ZK
2027
if [[ "$flags" == *"--disable_zk"* ]]; then
2128
has_zk="false"
@@ -29,7 +36,7 @@ trap "rm -rf output-$$" EXIT
2936
# Create a proof, write the solidity contract, write the proof as fields in order to extract the public inputs
3037
$bb prove $flags -b target/program.json --oracle_hash keccak --output_format bytes_and_fields --write_vk -o output-$$
3138
$bb verify $flags --oracle_hash keccak -i output-$$/public_inputs -k output-$$/vk -p output-$$/proof
32-
$bb write_solidity_verifier $flags -k output-$$/vk -o output-$$/Verifier.sol
39+
$bb write_solidity_verifier $write_contract_flags -k output-$$/vk -o output-$$/Verifier.sol
3340

3441
# Use solcjs to compile the generated key contract with the template verifier and test contract
3542
# index.js will start an anvil, on a random port

barretenberg/acir_tests/sol-test/HonkTest.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ contract Test {
1212
verifier = new HonkVerifier();
1313
}
1414

15-
function test(bytes calldata proof, bytes32[] calldata publicInputs) public view returns (bool) {
15+
function test(bytes calldata proof, bytes32[] calldata publicInputs) public returns (bool) {
1616
return verifier.verify(proof, publicInputs);
1717
}
1818
}

barretenberg/acir_tests/sol-test/src/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,9 @@ try {
274274
// Deploy the library
275275
console.log("Deploying ZKTranscriptLib library...");
276276
const libraryAddress = await deploy(signer, libraryAbi, libraryBytecode);
277+
278+
// Wait for the library deployment - for some reason we have an issue with nonces here
279+
await new Promise((resolve) => setTimeout(resolve, 500));
277280
console.log("ZKTranscriptLib deployed at:", libraryAddress);
278281

279282
// Link the library to the verifier bytecode

barretenberg/cpp/src/barretenberg/api/api.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ class API {
2525
bool slow_low_memory{ false }; // use file backed memory for polynomials
2626
bool update_inputs{ false }; // update inputs when check fails
2727

28+
bool optimized_solidity_verifier{ false }; // should we use the optimized sol verifier? (temp)
29+
2830
friend std::ostream& operator<<(std::ostream& os, const Flags& flags)
2931
{
3032
os << "flags: [\n"

barretenberg/cpp/src/barretenberg/api/api_ultra_honk.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp"
99
#include "barretenberg/dsl/acir_format/proof_surgeon.hpp"
1010
#include "barretenberg/dsl/acir_proofs/honk_contract.hpp"
11+
#include "barretenberg/dsl/acir_proofs/honk_optimized_contract.hpp"
1112
#include "barretenberg/dsl/acir_proofs/honk_zk_contract.hpp"
1213
#include "barretenberg/honk/proof_system/types/proof.hpp"
1314
#include "barretenberg/numeric/uint256/uint256.hpp"
@@ -236,7 +237,8 @@ void UltraHonkAPI::write_solidity_verifier(const Flags& flags,
236237
// Convert flags to ProofSystemSettings
237238
bbapi::ProofSystemSettings settings{ .ipa_accumulation = flags.ipa_accumulation,
238239
.oracle_hash_type = flags.oracle_hash_type,
239-
.disable_zk = flags.disable_zk };
240+
.disable_zk = flags.disable_zk,
241+
.optimized_solidity_verifier = flags.optimized_solidity_verifier };
240242

241243
// Execute solidity verifier command
242244
auto response = bbapi::CircuitWriteSolidityVerifier{ .verification_key = vk_bytes, .settings = settings }.execute();

barretenberg/cpp/src/barretenberg/bb/cli.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,11 @@ int parse_and_run_cli_command(int argc, char* argv[])
282282
return subcommand->add_flag("--update_inputs", flags.update_inputs, "Update inputs if vk check fails.");
283283
};
284284

285+
const auto add_optimized_solidity_verifier_flag = [&](CLI::App* subcommand) {
286+
return subcommand->add_flag(
287+
"--optimized", flags.optimized_solidity_verifier, "Use the optimized Solidity verifier.");
288+
};
289+
285290
bool print_op_counts = false;
286291
const auto add_print_op_counts_flag = [&](CLI::App* subcommand) {
287292
return subcommand->add_flag("--print_op_counts", print_op_counts, "Print op counts to json on one line.");
@@ -412,6 +417,7 @@ int parse_and_run_cli_command(int argc, char* argv[])
412417
add_verbose_flag(write_solidity_verifier);
413418
remove_zk_option(write_solidity_verifier);
414419
add_crs_path_option(write_solidity_verifier);
420+
add_optimized_solidity_verifier_flag(write_solidity_verifier);
415421

416422
/***************************************************************************************************************
417423
* Subcommand: OLD_API

barretenberg/cpp/src/barretenberg/bbapi/bbapi_shared.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,10 @@ struct ProofSystemSettings {
9292
*/
9393
bool disable_zk = false;
9494

95-
MSGPACK_FIELDS(ipa_accumulation, oracle_hash_type, disable_zk);
95+
// TODO(md): remove this once considered stable
96+
bool optimized_solidity_verifier = false;
97+
98+
MSGPACK_FIELDS(ipa_accumulation, oracle_hash_type, disable_zk, optimized_solidity_verifier);
9699
bool operator==(const ProofSystemSettings& other) const = default;
97100
};
98101

barretenberg/cpp/src/barretenberg/bbapi/bbapi_ultra_honk.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp"
1010
#include "barretenberg/dsl/acir_format/serde/witness_stack.hpp"
1111
#include "barretenberg/dsl/acir_proofs/honk_contract.hpp"
12+
#include "barretenberg/dsl/acir_proofs/honk_optimized_contract.hpp"
1213
#include "barretenberg/dsl/acir_proofs/honk_zk_contract.hpp"
1314
#include "barretenberg/flavor/mega_flavor.hpp"
1415
#include "barretenberg/flavor/ultra_flavor.hpp"
@@ -340,8 +341,17 @@ CircuitWriteSolidityVerifier::Response CircuitWriteSolidityVerifier::execute(BB_
340341
{
341342
using VK = UltraKeccakFlavor::VerificationKey;
342343
auto vk = std::make_shared<VK>(from_buffer<VK>(verification_key));
344+
343345
std::string contract = settings.disable_zk ? get_honk_solidity_verifier(vk) : get_honk_zk_solidity_verifier(vk);
344346

347+
// If in wasm, we dont include the optimized solidity verifier - due to its large bundle size
348+
// This will run generate twice, but this should only be run before deployment and not frequently
349+
#ifndef __wasm__
350+
if (settings.disable_zk && settings.optimized_solidity_verifier) {
351+
contract = get_optimized_honk_solidity_verifier(vk);
352+
}
353+
#endif
354+
345355
return { std::move(contract) };
346356
}
347357

barretenberg/cpp/src/barretenberg/dsl/acir_proofs/honk_contract.hpp

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ static const char HONK_CONTRACT_SOURCE[] = R"(
1515
pragma solidity ^0.8.27;
1616
1717
interface IVerifier {
18-
function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool);
18+
function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool);
1919
}
2020
2121
type Fr is uint256;
@@ -68,7 +68,7 @@ library FrLib {
6868
mstore(add(free, 0x20), 0x20)
6969
mstore(add(free, 0x40), 0x20)
7070
mstore(add(free, 0x60), v)
71-
mstore(add(free, 0x80), sub(MODULUS, 2))
71+
mstore(add(free, 0x80), sub(MODULUS, 2))
7272
mstore(add(free, 0xa0), MODULUS)
7373
let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20)
7474
if iszero(success) {
@@ -92,7 +92,7 @@ library FrLib {
9292
mstore(add(free, 0x20), 0x20)
9393
mstore(add(free, 0x40), 0x20)
9494
mstore(add(free, 0x60), b)
95-
mstore(add(free, 0x80), v)
95+
mstore(add(free, 0x80), v)
9696
mstore(add(free, 0xa0), MODULUS)
9797
let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20)
9898
if iszero(success) {
@@ -674,6 +674,7 @@ library RelationsLib {
674674
accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval);
675675
accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval);
676676
accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval);
677+
677678
// batch the subrelations with the alpha challenges to obtain the full honk relation
678679
accumulator = scaleAndBatchSubrelations(evaluations, alphas);
679680
}
@@ -1051,7 +1052,7 @@ library RelationsLib {
10511052
ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L);
10521053
ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4);
10531054
1054-
ap.index_is_monotonically_increasing = ap.index_delta * ap.index_delta - ap.index_delta; // deg 2
1055+
ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2
10551056
10561057
ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2
10571058
@@ -1082,7 +1083,7 @@ library RelationsLib {
10821083
* with a WRITE operation.
10831084
*/
10841085
Fr access_type = (wire(p, WIRE.W_4) - ap.partial_record_check); // will be 0 or 1 for honest Prover; deg 1 or 4
1085-
ap.access_check = access_type * access_type - access_type; // check value is 0 or 1; deg 2 or 8
1086+
ap.access_check = access_type * (access_type - Fr.wrap(1)); // check value is 0 or 1; deg 2 or 8
10861087
10871088
// reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta
10881089
// deg 1 or 4
@@ -1256,7 +1257,7 @@ library RelationsLib {
12561257
function accumulatePoseidonExternalRelation(
12571258
Fr[NUMBER_OF_ENTITIES] memory p,
12581259
Fr[NUMBER_OF_SUBRELATIONS] memory evals,
1259-
Fr domainSep // i guess this is the scaling factor?
1260+
Fr domainSep
12601261
) internal pure {
12611262
PoseidonExternalParams memory ep;
12621263
@@ -1354,7 +1355,7 @@ library RelationsLib {
13541355
Fr[NUMBER_OF_SUBRELATIONS] memory evaluations,
13551356
Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges
13561357
) internal pure returns (Fr accumulator) {
1357-
accumulator = accumulator + evaluations[0];
1358+
accumulator = evaluations[0];
13581359
13591360
for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) {
13601361
accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1];
@@ -1421,10 +1422,9 @@ library CommitmentSchemeLib {
14211422
);
14221423
// Divide by the denominator
14231424
batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert();
1424-
if (i <= logSize) {
1425-
batchedEvalAccumulator = batchedEvalRoundAcc;
1426-
foldPosEvaluations[i - 1] = batchedEvalRoundAcc;
1427-
}
1425+
1426+
batchedEvalAccumulator = batchedEvalRoundAcc;
1427+
foldPosEvaluations[i - 1] = batchedEvalRoundAcc;
14281428
}
14291429
return foldPosEvaluations;
14301430
}
@@ -2073,27 +2073,24 @@ abstract contract BaseHonkVerifier is IVerifier {
20732073
// Compute Shplonk constant term contributions from Aₗ(± r^{2ˡ}) for l = 1, ..., m-1;
20742074
// Compute scalar multipliers for each fold commitment
20752075
for (uint256 i = 0; i < $LOG_N - 1; ++i) {
2076-
bool dummy_round = i >= ($LOG_N - 1);
2077-
2078-
if (!dummy_round) {
2079-
// Update inverted denominators
2080-
mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert();
2081-
mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert();
2082-
2083-
// Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ]
2084-
mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator;
2085-
mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator;
2086-
// [Aₗ] is multiplied by -v^{2l}/(z-r^{2^l}) - v^{2l+1} /(z+ r^{2^l})
2087-
scalars[NUMBER_UNSHIFTED + 1 + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg();
2088-
2089-
// Accumulate the const term contribution given by
2090-
// v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l})
2091-
Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1];
2092-
accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1];
2093-
mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution;
2094-
// Update the running power of v
2095-
mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu;
2096-
}
2076+
// Update inverted denominators
2077+
mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert();
2078+
mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert();
2079+
2080+
// Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ]
2081+
mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator;
2082+
mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator;
2083+
// [Aₗ] is multiplied by -v^{2l}/(z-r^{2^l}) - v^{2l+1} /(z+ r^{2^l})
2084+
scalars[NUMBER_UNSHIFTED + 1 + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg();
2085+
2086+
// Accumulate the const term contribution given by
2087+
// v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l})
2088+
Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1];
2089+
2090+
accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1];
2091+
mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution;
2092+
// Update the running power of v
2093+
mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu;
20972094
20982095
commitments[NUMBER_UNSHIFTED + 1 + i] = proof.geminiFoldComms[i];
20992096
}

0 commit comments

Comments
 (0)