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: Implement recursive verification in the parity circuits #6006

Merged
merged 52 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
f57cf23
Test refactor
PhilWindle Apr 15, 2024
fbe88f8
Tests
PhilWindle Apr 17, 2024
d4b4b34
More tests
PhilWindle Apr 17, 2024
8f65274
Review changes
PhilWindle Apr 17, 2024
082dd2f
Minor test change
PhilWindle Apr 17, 2024
bbd5852
Formatting
PhilWindle Apr 17, 2024
250986b
Debug CI
PhilWindle Apr 17, 2024
8462762
Test refactoring
PhilWindle Apr 17, 2024
ffc2fee
More test refactoring
PhilWindle Apr 17, 2024
a6bdcb4
More cleanup
PhilWindle Apr 17, 2024
3b1e7ed
More cleanup
PhilWindle Apr 17, 2024
1cf82f4
Debugging
PhilWindle Apr 17, 2024
dd83b2e
More debugging
PhilWindle Apr 17, 2024
85148c6
Generate vks
PhilWindle Apr 17, 2024
0ebd7a1
More fixes
PhilWindle Apr 17, 2024
9c72103
Merge branch 'master' into pw/public-krnel-proving
PhilWindle Apr 17, 2024
ab3a8d3
Formatting
PhilWindle Apr 17, 2024
e38f7dc
Merge branch 'pw/public-krnel-proving' of github.com:AztecProtocol/az…
PhilWindle Apr 17, 2024
5d1fcb3
Test fixes
PhilWindle Apr 18, 2024
77c0b5f
Merge branch 'master' into pw/public-krnel-proving
PhilWindle Apr 18, 2024
8d28bc3
Formatting
PhilWindle Apr 18, 2024
ab751ef
Attempt to get parity recursion working
PhilWindle Apr 23, 2024
012abde
Cleanup
PhilWindle Apr 24, 2024
7185130
Cleanup
PhilWindle Apr 24, 2024
6382cce
Merge branch 'master' into pw/parity-recursion
PhilWindle Apr 24, 2024
08d1b25
Cleanup
PhilWindle Apr 24, 2024
5dc5779
Merge fixes
PhilWindle Apr 24, 2024
8ca90c7
More fixes
PhilWindle Apr 24, 2024
0690092
More cleanup
PhilWindle Apr 24, 2024
1ae8447
Fixes
PhilWindle Apr 24, 2024
6734ffa
Test fixes
PhilWindle Apr 24, 2024
3629a22
Cleanup
PhilWindle Apr 24, 2024
8620d4e
Cleanup
PhilWindle Apr 24, 2024
5cd3cc7
Review changes
PhilWindle Apr 25, 2024
e08f32e
Merge branch 'master' into pw/parity-recursion
PhilWindle Apr 25, 2024
e9ceff4
Fixed merge conflicts
PhilWindle Apr 25, 2024
51ebdd4
More merge fixes
PhilWindle Apr 25, 2024
70a2b48
Merge fixes
PhilWindle Apr 25, 2024
9816196
Added negative proving tests
PhilWindle Apr 25, 2024
fc307b8
Increase test timeout
PhilWindle Apr 25, 2024
789aba3
Formatting
PhilWindle Apr 25, 2024
e402c25
Revert some verification key changes. Assert vk equivalence in circuits
PhilWindle Apr 25, 2024
033d424
Reverted some previous changes
PhilWindle Apr 25, 2024
f784f93
Fixes
PhilWindle Apr 25, 2024
f25680f
Fix
PhilWindle Apr 25, 2024
c5a310f
Test fixes
PhilWindle Apr 25, 2024
f200538
Merge branch 'master' into pw/parity-recursion
PhilWindle Apr 25, 2024
c6dc44e
Updated comment
PhilWindle Apr 26, 2024
4b7fded
Review changes
PhilWindle Apr 26, 2024
292e78c
Merge branch 'master' into pw/parity-recursion
PhilWindle Apr 26, 2024
8d7ab6f
Merge branch 'master' into pw/parity-recursion
PhilWindle Apr 26, 2024
c574400
Test fix
PhilWindle Apr 26, 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
75 changes: 70 additions & 5 deletions barretenberg/cpp/src/barretenberg/bb/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,19 @@ std::vector<acir_format::AcirFormat> get_constraint_systems(std::string const& b
return acir_format::program_buf_to_acir_format(bytecode);
}

std::string proof_to_json(std::vector<bb::fr>& proof)
{
return format("[", join(map(proof, [](auto fr) { return format("\"", fr, "\""); })), "]");
}

std::string vk_to_json(std::vector<bb::fr>& data)
{
// We need to move vk_hash to the front...
std::rotate(data.begin(), data.end() - 1, data.end());

return format("[", join(map(data, [](auto fr) { return format("\"", fr, "\""); })), "]");
}

/**
* @brief Proves and Verifies an ACIR circuit
*
Expand Down Expand Up @@ -433,7 +446,7 @@ void proof_as_fields(const std::string& proof_path, std::string const& vk_path,
auto acir_composer = verifier_init();
auto vk_data = from_buffer<plonk::verification_key_data>(read_file(vk_path));
auto data = acir_composer.serialize_proof_into_fields(read_file(proof_path), vk_data.num_public_inputs);
auto json = format("[", join(map(data, [](auto fr) { return format("\"", fr, "\""); })), "]");
auto json = proof_to_json(data);

if (output_path == "-") {
writeStringToStdout(json);
Expand Down Expand Up @@ -464,10 +477,7 @@ void vk_as_fields(const std::string& vk_path, const std::string& output_path)
acir_composer.load_verification_key(std::move(vk_data));
auto data = acir_composer.serialize_verification_key_into_fields();

// We need to move vk_hash to the front...
std::rotate(data.begin(), data.end() - 1, data.end());

auto json = format("[", join(map(data, [](auto fr) { return format("\"", fr, "\""); })), "]");
auto json = vk_to_json(data);
if (output_path == "-") {
writeStringToStdout(json);
vinfo("vk as fields written to stdout");
Expand Down Expand Up @@ -572,6 +582,58 @@ bool avm_verify(const std::filesystem::path& proof_path)
return true;
}

/**
* @brief Creates a proof for an ACIR circuit, outputs the proof and verification key in binary and 'field' format
*
* Communication:
* - Filesystem: The proof is written to the path specified by outputPath
*
* @param bytecodePath Path to the file containing the serialized circuit
* @param witnessPath Path to the file containing the serialized witness
* @param outputPath Directory into which we write the proof and verification key data
*/
void prove_output_all(const std::string& bytecodePath, const std::string& witnessPath, const std::string& outputPath)
{
auto constraint_system = get_constraint_system(bytecodePath);
auto witness = get_witness(witnessPath);

acir_proofs::AcirComposer acir_composer{ 0, verbose };
acir_composer.create_circuit(constraint_system, witness);
init_bn254_crs(acir_composer.get_dyadic_circuit_size());
acir_composer.init_proving_key();
auto proof = acir_composer.create_proof();

// We have been given a directory, we will write the proof and verification key
// into the directory in both 'binary' and 'fields' formats
std::string vkOutputPath = outputPath + "/vk";
std::string proofPath = outputPath + "/proof";
std::string vkFieldsOutputPath = outputPath + "/vk_fields.json";
std::string proofFieldsPath = outputPath + "/proof_fields.json";

std::shared_ptr<bb::plonk::verification_key> vk = acir_composer.init_verification_key();

// Write the 'binary' proof
write_file(proofPath, proof);
vinfo("proof written to: ", proofPath);

// Write the proof as fields
auto proofAsFields = acir_composer.serialize_proof_into_fields(proof, vk->as_data().num_public_inputs);
std::string proofJson = proof_to_json(proofAsFields);
write_file(proofFieldsPath, { proofJson.begin(), proofJson.end() });
vinfo("proof as fields written to: ", proofFieldsPath);

// Write the vk as binary
auto serialized_vk = to_buffer(*vk);
write_file(vkOutputPath, serialized_vk);
vinfo("vk written to: ", vkOutputPath);

// Write the vk as fields
auto data = acir_composer.serialize_verification_key_into_fields();
std::string vk_json = vk_to_json(data);
write_file(vkFieldsOutputPath, { vk_json.begin(), vk_json.end() });
vinfo("vk as fields written to: ", vkFieldsOutputPath);
}

bool flag_present(std::vector<std::string>& args, const std::string& flag)
{
return std::find(args.begin(), args.end(), flag) != args.end();
Expand Down Expand Up @@ -632,6 +694,9 @@ int main(int argc, char* argv[])
if (command == "prove") {
std::string output_path = get_option(args, "-o", "./proofs/proof");
prove(bytecode_path, witness_path, output_path);
} else if (command == "prove_output_all") {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is probably temporary until we get a long lasting process version running.

std::string output_path = get_option(args, "-o", "./proofs");
prove_output_all(bytecode_path, witness_path, output_path);
} else if (command == "gates") {
gateCount(bytecode_path);
} else if (command == "verify") {
Expand Down
3 changes: 3 additions & 0 deletions l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,7 @@ library Constants {
uint256 internal constant LOGS_HASHES_NUM_BYTES_PER_BASE_ROLLUP = 64;
uint256 internal constant NUM_MSGS_PER_BASE_PARITY = 4;
uint256 internal constant NUM_BASE_PARITY_PER_ROOT_PARITY = 4;
uint256 internal constant RECURSIVE_PROOF_LENGTH = 93;
uint256 internal constant NESTED_RECURSIVE_PROOF_LENGTH = 109;
uint256 internal constant VERIFICATION_KEY_LENGTH_IN_FIELDS = 114;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use dep::parity_lib::{BaseParityInputs, ParityPublicInputs};

fn main(inputs: BaseParityInputs) -> pub ParityPublicInputs {
#[recursive]
fn main(inputs: BaseParityInputs) -> distinct pub ParityPublicInputs {
inputs.base_parity_circuit()
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
use crate::{parity_public_inputs::ParityPublicInputs, utils::sha256_merkle_tree::Sha256MerkleTree};
use dep::types::{
constants::NUM_MSGS_PER_BASE_PARITY, merkle_tree::MerkleTree, mocked::AggregationObject,
utils::uint256::U256
};
use dep::types::{constants::NUM_MSGS_PER_BASE_PARITY, merkle_tree::MerkleTree, utils::uint256::U256};

struct BaseParityInputs {
msgs: [Field; NUM_MSGS_PER_BASE_PARITY],
Expand All @@ -13,11 +10,7 @@ impl BaseParityInputs {
let sha_tree = Sha256MerkleTree::new(self.msgs);
let pedersen_tree = MerkleTree::new(self.msgs);

ParityPublicInputs {
aggregation_object: AggregationObject {},
sha_root: sha_tree.get_root(),
converted_root: pedersen_tree.get_root()
}
ParityPublicInputs { sha_root: sha_tree.get_root(), converted_root: pedersen_tree.get_root() }
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ mod utils;
use crate::base::base_parity_inputs::BaseParityInputs;
use crate::root::root_parity_input::RootParityInput;
use crate::root::root_parity_inputs::RootParityInputs;
use crate::root::root_rollup_parity_input::RootRollupParityInput;
use crate::parity_public_inputs::ParityPublicInputs;
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
use dep::types::{mocked::AggregationObject, traits::Empty};
use dep::types::{traits::{Empty, Serialize, Deserialize}};

struct ParityPublicInputs {
aggregation_object: AggregationObject,
sha_root: Field,
converted_root: Field,
sha_root: Field,
converted_root: Field,
}

impl Empty for ParityPublicInputs {
fn empty() -> Self {
ParityPublicInputs {
aggregation_object: AggregationObject::empty(),
sha_root: 0,
ParityPublicInputs {
sha_root: 0,
converted_root: 0,
}
}
}
}

impl Serialize<2> for ParityPublicInputs {
fn serialize(self) -> [Field; 2] {
let mut fields = [0; 2];
fields[0] = self.sha_root;
fields[1] = self.converted_root;
fields
}
}

impl Deserialize<2> for ParityPublicInputs {
fn deserialize(fields: [Field; 2]) -> Self {
ParityPublicInputs {
sha_root: fields[0],
converted_root: fields[1],
}
}
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
mod root_parity_input;
mod root_parity_inputs;
mod root_rollup_parity_input;
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
use dep::types::{
mocked::Proof,
traits::Empty
};
use dep::types::{traits::Empty, recursion::{verification_key::VerificationKey, proof::RecursiveProof}};
use crate::parity_public_inputs::ParityPublicInputs;

struct RootParityInput {
proof: Proof,
proof: RecursiveProof,
verification_key: VerificationKey,
public_inputs: ParityPublicInputs,
}

impl Empty for RootParityInput {
fn empty() -> Self {
RootParityInput {
proof: Proof::empty(),
proof: RecursiveProof::empty(),
verification_key: VerificationKey::empty(),
public_inputs: ParityPublicInputs::empty(),
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
use dep::types::{merkle_tree::MerkleTree, mocked::AggregationObject};
use dep::types::{
merkle_tree::MerkleTree, recursion::{verification_key::VerificationKey, proof::RecursiveProof},
traits::Serialize
};
use crate::{
parity_public_inputs::ParityPublicInputs, root::root_parity_input::RootParityInput,
utils::sha256_merkle_tree::Sha256MerkleTree
};
use dep::std;

global NUM_BASE_PARITY_PER_ROOT_PARITY: u64 = 4;

Expand All @@ -12,7 +16,7 @@ struct RootParityInputs {

impl RootParityInputs {
pub fn root_parity_circuit(self) -> ParityPublicInputs {
// TODO: verify proofs of inputs.children
self.verify_child_proofs();

let mut sha_roots = [0; NUM_BASE_PARITY_PER_ROOT_PARITY];
let mut converted_roots = [0; NUM_BASE_PARITY_PER_ROOT_PARITY];
Expand All @@ -24,10 +28,24 @@ impl RootParityInputs {
let sha_tree = Sha256MerkleTree::new(sha_roots);
let pedersen_tree = MerkleTree::new(converted_roots);

ParityPublicInputs {
aggregation_object: AggregationObject {},
sha_root: sha_tree.get_root(),
converted_root: pedersen_tree.get_root()
ParityPublicInputs { sha_root: sha_tree.get_root(), converted_root: pedersen_tree.get_root() }
}

fn verify_child_proofs(self) {
//TODO(@PhilWindle): Validate all keys against the actual BASE_PARITY_CIRCUIT_VK_HASH
//assert(self.children[0].verification_key.hash == BASE_PARITY_CIRCUIT_VK_HASH);
for i in 0..NUM_BASE_PARITY_PER_ROOT_PARITY {
assert(
self.children[i].verification_key.hash == self.children[0].verification_key.hash, "Inconsistent vk hashes across base parity circuits"
);
//TODO: Do we need to validate this following hash
//assert(hash(self.children[i].verification_key) == self.children[i].verification_key.hash);
std::verify_proof(
self.children[i].verification_key.key.as_slice(),
self.children[i].proof.fields.as_slice(),
ParityPublicInputs::serialize(self.children[i].public_inputs).as_slice(),
self.children[i].verification_key.hash
);
}
}
}
Expand All @@ -37,10 +55,9 @@ mod tests {
parity_public_inputs::ParityPublicInputs,
root::{root_parity_input::RootParityInput, root_parity_inputs::RootParityInputs}
};
use dep::types::mocked::{AggregationObject, Proof};
use dep::types::recursion::{verification_key::VerificationKey, proof::RecursiveProof};

#[test]
fn test_sha_root_matches_frontier_tree() {
fn test_setup() -> [RootParityInput; 4] {
// 31 byte test SHA roots
let children_sha_roots = [
0xb3a3fc1968999f2c2d798b900bdf0de41311be2a4d20496a7e792a521fc8ab,
Expand All @@ -49,25 +66,85 @@ mod tests {
0x53042d820859d80c474d4694e03778f8dc0ac88fc1c3a97b4369c1096e904a
];

let mut vk1 = VerificationKey::empty();
vk1.hash = 0x43f78e0ebc9633ce336a8c086064d898c32fb5d7d6011f5427459c0b8d14e9;

let children = [
RootParityInput {
proof: Proof {},
public_inputs: ParityPublicInputs { aggregation_object: AggregationObject {}, sha_root: children_sha_roots[0], converted_root: 0 }
proof: RecursiveProof::empty(),
verification_key: vk1,
public_inputs: ParityPublicInputs { sha_root: children_sha_roots[0], converted_root: 0 }
},
RootParityInput {
proof: Proof {},
public_inputs: ParityPublicInputs { aggregation_object: AggregationObject {}, sha_root: children_sha_roots[1], converted_root: 0 }
proof: RecursiveProof::empty(),
verification_key: vk1,
public_inputs: ParityPublicInputs { sha_root: children_sha_roots[1], converted_root: 0 }
},
RootParityInput {
proof: Proof {},
public_inputs: ParityPublicInputs { aggregation_object: AggregationObject {}, sha_root: children_sha_roots[2], converted_root: 0 }
proof: RecursiveProof::empty(),
verification_key: vk1,
public_inputs: ParityPublicInputs { sha_root: children_sha_roots[2], converted_root: 0 }
},
RootParityInput {
proof: Proof {},
public_inputs: ParityPublicInputs { aggregation_object: AggregationObject {}, sha_root: children_sha_roots[3], converted_root: 0 }
proof: RecursiveProof::empty(),
verification_key: vk1,
public_inputs: ParityPublicInputs { sha_root: children_sha_roots[3], converted_root: 0 }
}
];
children
}

#[test]
fn test_sha_root_matches_frontier_tree() {
let children = test_setup();
let root_parity_inputs = RootParityInputs { children };

let public_inputs = root_parity_inputs.root_parity_circuit();

// 31 byte truncated root hash
let expected_sha_root = 0xa0c56543aa73140e5ca27231eee3107bd4e11d62164feb411d77c9d9b2da47;

assert(public_inputs.sha_root == expected_sha_root, "sha root does not match");
}

#[test(should_fail_with = "Inconsistent vk hashes across base parity circuits")]
fn test_asserts_incorrect_vk_hash_1() {
let mut vk2 = VerificationKey::empty();
vk2.hash = 0x53042d820859d80c474d4694e03778f8dc0ac88fc1c3a97b4369c1096e904a;
let mut children = test_setup();
children[1].verification_key = vk2;
let root_parity_inputs = RootParityInputs { children };

let public_inputs = root_parity_inputs.root_parity_circuit();

// 31 byte truncated root hash
let expected_sha_root = 0xa0c56543aa73140e5ca27231eee3107bd4e11d62164feb411d77c9d9b2da47;

assert(public_inputs.sha_root == expected_sha_root, "sha root does not match");
}

#[test(should_fail_with = "Inconsistent vk hashes across base parity circuits")]
fn test_asserts_incorrect_vk_hash_2() {
let mut vk2 = VerificationKey::empty();
vk2.hash = 0x53042d820859d80c474d4694e03778f8dc0ac88fc1c3a97b4369c1096e904a;
let mut children = test_setup();
children[2].verification_key = vk2;
let root_parity_inputs = RootParityInputs { children };

let public_inputs = root_parity_inputs.root_parity_circuit();

// 31 byte truncated root hash
let expected_sha_root = 0xa0c56543aa73140e5ca27231eee3107bd4e11d62164feb411d77c9d9b2da47;

assert(public_inputs.sha_root == expected_sha_root, "sha root does not match");
}

#[test(should_fail_with = "Inconsistent vk hashes across base parity circuits")]
fn test_asserts_incorrect_vk_hash_3() {
let mut vk2 = VerificationKey::empty();
vk2.hash = 0x53042d820859d80c474d4694e03778f8dc0ac88fc1c3a97b4369c1096e904a;
let mut children = test_setup();
children[3].verification_key = vk2;
let root_parity_inputs = RootParityInputs { children };

let public_inputs = root_parity_inputs.root_parity_circuit();
Expand Down
Loading
Loading