Skip to content

Commit

Permalink
Solidity Ultra Verifier with tests (#363)
Browse files Browse the repository at this point in the history
* initial setup

* feat: add test setup

* fix: revert unneeded cmakelist changes + typos

* fix: solidity helper binaries in docker

* chore: Use `-assert` variant instead of plain

* chore: config.yml missing whitespace

* fix: alpine base dockerfile

* chore: add sol to build_manifest

* Dockerfile: add string utils

* fix: use different base, add bash

* chore: remove stale misc

* circle-ci fiddling

* more circle fiddling

* fiddle

* Circle-ci fiddling for verifiers (#365)

* build_manifest update

* fiddling

* fiddling

* skip ensure_repo

* skipping more stuff

* fiddling

* get docker version

* force change in key_gen

* fiddle

* docker version in cond_spot

* fiddle

* update path

* fiddle

* build in init

* fiddle

* fiddle

* fiddle

* fiddle

* alpine

* package naming

* add apt-repository

* add apt rep key

* lld-15

* fiddle with docker

* chore: cleanups

* chore: remove log

---------

Co-authored-by: cheethas <[email protected]>
  • Loading branch information
LHerskind and cheethas authored Apr 27, 2023
1 parent b85ab8d commit 2396c3f
Show file tree
Hide file tree
Showing 40 changed files with 3,919 additions and 1 deletion.
8 changes: 8 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
sol/broadcast
sol/cache
sol/out
sol/Dockerfile
sol/lib
cpp/build
cpp/srs_db/ignition
.gitmodules
23 changes: 23 additions & 0 deletions .github/workflows/solidity_verifier_test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: solidity-verifier-test

on:
- workflow_dispatch
- push

env:
FOUNDRY_PROFILE: ci

jobs:
check:
strategy:
fail-fast: true

name: Solidity Verifier Tests
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
with:
submodules: recursive

- name: Build docker image
run: docker build . -f sol/Dockerfile
9 changes: 9 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
[submodule "foundation"]
path = foundation
url = [email protected]:AztecProtocol/foundation.git
[submodule "sol/lib/forge-std"]
path = sol/lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "sol/lib/solidity-stringutils"]
path = sol/lib/solidity-stringutils
url = https://github.com/Arachnid/solidity-stringutils
[submodule "sol/lib/openzeppelin-contracts"]
path = sol/lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
2 changes: 1 addition & 1 deletion cpp/dockerfiles/Dockerfile.x86_64-linux-clang
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ RUN cmake --preset default && cmake --build --preset default

FROM alpine:3.17
RUN apk update && apk add openmp
COPY --from=builder /usr/src/barretenberg/cpp/srs_db /usr/src/barretenberg/cpp/srs_db
COPY --from=builder /usr/src/barretenberg/cpp/srs_db /usr/src/barretenberg/cpp/srs_db
1 change: 1 addition & 0 deletions cpp/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ add_subdirectory(barretenberg/plonk)
add_subdirectory(barretenberg/stdlib)
add_subdirectory(barretenberg/join_split_example)
add_subdirectory(barretenberg/dsl)
add_subdirectory(barretenberg/solidity_helpers)

if(BENCHMARKS)
add_subdirectory(barretenberg/benchmark)
Expand Down
17 changes: 17 additions & 0 deletions cpp/src/barretenberg/solidity_helpers/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
barretenberg_module(stdlib_solidity_helpers plonk proof_system transcript crypto_pedersen_commitment polynomials crypto_sha256 ecc crypto_blake3s stdlib_primitives stdlib_pedersen_commitment stdlib_blake3s stdlib_blake2s)

add_executable(solidity_key_gen key_gen.cpp)

add_executable(solidity_proof_gen proof_gen.cpp)

target_link_libraries(
solidity_key_gen
stdlib_solidity_helpers
env
)

target_link_libraries(
solidity_proof_gen
stdlib_solidity_helpers
env
)
25 changes: 25 additions & 0 deletions cpp/src/barretenberg/solidity_helpers/circuits/add_2_circuit.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include "barretenberg/stdlib/primitives/field/field.hpp"
#include "barretenberg/stdlib/primitives/witness/witness.hpp"
#include "barretenberg/stdlib/primitives/uint/uint.hpp"
#include "barretenberg/stdlib/primitives/bool/bool.hpp"

template <typename Composer> class Add2Circuit {
public:
typedef stdlib::field_t<Composer> field_ct;
typedef stdlib::witness_t<Composer> witness_ct;
typedef stdlib::public_witness_t<Composer> public_witness_ct;

// Three public inputs
static Composer generate(std::string srs_path, uint256_t inputs[])
{

Composer composer(srs_path);

field_ct a(public_witness_ct(&composer, inputs[0]));
field_ct b(public_witness_ct(&composer, inputs[1]));
field_ct c(public_witness_ct(&composer, inputs[2]));
c.assert_equal(a + b);

return composer;
}
};
31 changes: 31 additions & 0 deletions cpp/src/barretenberg/solidity_helpers/circuits/blake_circuit.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#include "barretenberg/stdlib/primitives/field/field.hpp"
#include "barretenberg/stdlib/primitives/witness/witness.hpp"
#include "barretenberg/stdlib/hash/blake2s/blake2s.hpp"

using namespace proof_system::plonk;
using namespace proof_system::plonk::stdlib;

using numeric::uint256_t;

template <typename Composer> class BlakeCircuit {
public:
typedef stdlib::field_t<Composer> field_ct;
typedef stdlib::public_witness_t<Composer> public_witness_ct;
typedef stdlib::byte_array<Composer> byte_array_ct;

static constexpr size_t NUM_PUBLIC_INPUTS = 4;

static Composer generate(std::string srs_path, uint256_t public_inputs[])
{
Composer composer(srs_path);

byte_array_ct input_buffer(&composer);
for (size_t i = 0; i < NUM_PUBLIC_INPUTS; ++i) {
input_buffer.write(byte_array_ct(field_ct(public_witness_ct(&composer, public_inputs[i]))));
}

stdlib::blake2s<Composer>(input_buffer);

return composer;
}
};
136 changes: 136 additions & 0 deletions cpp/src/barretenberg/solidity_helpers/circuits/recursive_circuit.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#include "barretenberg/transcript/transcript.hpp"
#include "barretenberg/plonk/proof_system/proving_key/serialize.hpp"
#include "barretenberg/stdlib/primitives/curves/bn254.hpp"
#include "barretenberg/stdlib/recursion/verifier/verifier.hpp"
#include "barretenberg/stdlib/recursion/verifier/program_settings.hpp"
#include "barretenberg/ecc/curves/bn254/fq12.hpp"
#include "barretenberg/ecc/curves/bn254/pairing.hpp"

using namespace proof_system::plonk;

using numeric::uint256_t;

template <typename OuterComposer> class RecursiveCircuit {
using InnerComposer = UltraComposer;

typedef stdlib::field_t<InnerComposer> field_ct;
typedef stdlib::bn254<InnerComposer> inner_curve;
typedef stdlib::bn254<OuterComposer> outer_curve;
typedef stdlib::recursion::verification_key<outer_curve> verification_key_pt;
typedef stdlib::recursion::recursive_ultra_verifier_settings<outer_curve> recursive_settings;
typedef stdlib::recursion::recursive_ultra_to_standard_verifier_settings<outer_curve>
ultra_to_standard_recursive_settings;
typedef inner_curve::fr_ct fr_ct;
typedef inner_curve::public_witness_ct public_witness_ct;
typedef inner_curve::witness_ct witness_ct;

struct circuit_outputs {
stdlib::recursion::aggregation_state<outer_curve> aggregation_state;
std::shared_ptr<verification_key_pt> verification_key;
};

static void create_inner_circuit_no_tables(InnerComposer& composer, uint256_t inputs[])
{
field_ct a(public_witness_ct(&composer, inputs[0]));
field_ct b(public_witness_ct(&composer, inputs[1]));
field_ct c(public_witness_ct(&composer, inputs[2]));

// @note For some reason, if we don't have this line, the circuit fails to verify.
auto c_sq = c * c;

c.assert_equal(a + b);
c_sq.assert_equal(c * c);
};

static circuit_outputs create_outer_circuit(InnerComposer& inner_composer, OuterComposer& outer_composer)
{
// These constexpr definitions are to allow for the following:
// An Ultra Pedersen hash evaluates to a different value from the Turbo/Standard versions of the Pedersen hash.
// Therefore, the fiat-shamir challenges generated by the prover and verifier _could_ accidentally be different
// if an ultra proof is generated using ultra-pedersen challenges, but is being verified within a non-ultra
// circuit which uses non-ultra-pedersen challenges. We need the prover and verifier hashes to be the same. The
// solution is to select the relevant prover and verifier types (whose settings use the same hash for
// fiat-shamir), depending on the Inner-Outer combo. It's a bit clunky, but the alternative is to have a
// template argument for the hashtype, and that would pervade the entire UltraComposer, which would be
// horrendous.
constexpr bool is_ultra_to_ultra = std::is_same<OuterComposer, UltraComposer>::value;
typedef
typename std::conditional<is_ultra_to_ultra, UltraProver, UltraToStandardProver>::type ProverOfInnerCircuit;
typedef typename std::conditional<is_ultra_to_ultra, UltraVerifier, UltraToStandardVerifier>::type
VerifierOfInnerProof;
typedef
typename std::conditional<is_ultra_to_ultra, recursive_settings, ultra_to_standard_recursive_settings>::type
RecursiveSettings;

ProverOfInnerCircuit prover;
if constexpr (is_ultra_to_ultra) {
prover = inner_composer.create_prover();
} else {
prover = inner_composer.create_ultra_to_standard_prover();
}

const auto verification_key_native = inner_composer.compute_verification_key();

// Convert the verification key's elements into _circuit_ types, using the OUTER composer.
std::shared_ptr<verification_key_pt> verification_key =
verification_key_pt::from_witness(&outer_composer, verification_key_native);

proof recursive_proof = prover.construct_proof();

{
// Native check is mainly for comparison vs circuit version of the verifier.
VerifierOfInnerProof native_verifier;

if constexpr (is_ultra_to_ultra) {
native_verifier = inner_composer.create_verifier();
} else {
native_verifier = inner_composer.create_ultra_to_standard_verifier();
}
auto native_result = native_verifier.verify_proof(recursive_proof);
if (native_result == false) {
throw std::runtime_error("Native verification failed");
}
}

transcript::Manifest recursive_manifest = InnerComposer::create_manifest(prover.key->num_public_inputs);

// Verifying the ultra (inner) proof with CIRCUIT TYPES (i.e. within a standard or ultra plonk arithmetic
// circuit)
stdlib::recursion::aggregation_state<outer_curve> output =
stdlib::recursion::verify_proof<outer_curve, RecursiveSettings>(
&outer_composer, verification_key, recursive_manifest, recursive_proof);

return { output, verification_key };
};

public:
static OuterComposer generate(std::string srs_path, uint256_t inputs[])
{
InnerComposer inner_composer = InnerComposer(srs_path);
OuterComposer outer_composer = OuterComposer(srs_path);

create_inner_circuit_no_tables(inner_composer, inputs);
auto circuit_output = create_outer_circuit(inner_composer, outer_composer);

g1::affine_element P[2];
P[0].x = barretenberg::fq(circuit_output.aggregation_state.P0.x.get_value().lo);
P[0].y = barretenberg::fq(circuit_output.aggregation_state.P0.y.get_value().lo);
P[1].x = barretenberg::fq(circuit_output.aggregation_state.P1.x.get_value().lo);
P[1].y = barretenberg::fq(circuit_output.aggregation_state.P1.y.get_value().lo);

barretenberg::fq12 inner_proof_result = barretenberg::pairing::reduced_ate_pairing_batch_precomputed(
P, circuit_output.verification_key->reference_string->get_precomputed_g2_lines(), 2);

if (inner_proof_result != barretenberg::fq12::one()) {
throw std::runtime_error("inner proof result != 1");
}

circuit_output.aggregation_state.add_proof_outputs_as_public_inputs();

if (outer_composer.failed()) {
throw std::runtime_error("outer composer failed");
}

return outer_composer;
}
};
87 changes: 87 additions & 0 deletions cpp/src/barretenberg/solidity_helpers/key_gen.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#include <iostream>

#include "barretenberg/plonk/composer/standard_composer.hpp"
#include "barretenberg/plonk/composer/ultra_composer.hpp"
#include "barretenberg/plonk/proof_system/verification_key/sol_gen.hpp"

#include "circuits/blake_circuit.hpp"
#include "circuits/add_2_circuit.hpp"
#include "circuits/recursive_circuit.hpp"

#include "utils/utils.hpp"
#include "utils/instance_sol_gen.hpp"

template <typename Composer, typename Circuit>
void generate_keys(std::string output_path, std::string srs_path, std::string flavour_prefix, std::string circuit_name)
{
uint256_t public_inputs[4] = { 0, 0, 0, 0 };
Composer composer = Circuit::generate(srs_path, public_inputs);

std::shared_ptr<plonk::verification_key> vkey = composer.compute_verification_key();

// Make verification key file upper case
circuit_name.at(0) = static_cast<char>(std::toupper(static_cast<unsigned char>(circuit_name.at(0))));
flavour_prefix.at(0) = static_cast<char>(std::toupper(static_cast<unsigned char>(flavour_prefix.at(0))));

std::string vk_class_name = circuit_name + flavour_prefix + "VerificationKey";
std::string base_class_name = "Base" + flavour_prefix + "Verifier";
std::string instance_class_name = circuit_name + flavour_prefix + "Verifier";

{
auto vk_filename = output_path + "/keys/" + vk_class_name + ".sol";
std::ofstream os(vk_filename);
proof_system::output_vk_sol(os, vkey, vk_class_name);
info("VK contract written to: ", vk_filename);
}

{
auto instance_filename = output_path + "/instance/" + instance_class_name + ".sol";
std::ofstream os(instance_filename);
output_instance(os, vk_class_name, base_class_name, instance_class_name);
info("Verifier instance written to: ", instance_filename);
}
}

/*
* @brief Main entry point for the verification key generator
*
* 1. project_root_path: path to the solidity project root
* 2. srs_path: path to the srs db
*/
int main(int argc, char** argv)
{
std::vector<std::string> args(argv, argv + argc);

if (args.size() < 5) {
info("usage: ", args[0], "[plonk flavour] [circuit flavour] [output path] [srs path]");
return 1;
}

const std::string plonk_flavour = args[1];
const std::string circuit_flavour = args[2];
const std::string output_path = args[3];
const std::string srs_path = args[4];

// @todo - Add support for unrolled standard verifier. Needs a new solidity verifier contract.

if (plonk_flavour != "ultra") {
info("Only ultra plonk flavour is supported at the moment");
return 1;
} else {
info("Generating ultra plonk keys for ", circuit_flavour, " circuit");

if (circuit_flavour == "blake") {
generate_keys<UltraComposer, BlakeCircuit<UltraComposer>>(
output_path, srs_path, plonk_flavour, circuit_flavour);
} else if (circuit_flavour == "add2") {
generate_keys<UltraComposer, Add2Circuit<UltraComposer>>(
output_path, srs_path, plonk_flavour, circuit_flavour);
} else if (circuit_flavour == "recursive") {
generate_keys<UltraComposer, RecursiveCircuit<UltraComposer>>(
output_path, srs_path, plonk_flavour, circuit_flavour);
} else {
info("Only blake, add2 and recursive circuits are supported at the moment");
return 1;
}
}
}
Loading

0 comments on commit 2396c3f

Please sign in to comment.