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

Solidity Ultra Verifier with tests #363

Merged
merged 17 commits into from
Apr 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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