-
Notifications
You must be signed in to change notification settings - Fork 101
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Solidity Ultra Verifier with tests (#363)
* 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
Showing
40 changed files
with
3,919 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
25
cpp/src/barretenberg/solidity_helpers/circuits/add_2_circuit.hpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
31
cpp/src/barretenberg/solidity_helpers/circuits/blake_circuit.hpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
136
cpp/src/barretenberg/solidity_helpers/circuits/recursive_circuit.hpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |
Oops, something went wrong.