From 26fc59965b4bd95ba8b06ec3139e3fc44e5c3495 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 25 Jun 2024 13:38:00 +0100 Subject: [PATCH 1/9] chore(ci): add new e2e base target (#7179) I've noticed that we have to do a fair amount of installing from apt relatively often which only happens once the `+build` target is done. Ideally we'd do this in parallel so I've split off a different target in order to try and get earthly to be more proactive. --- yarn-project/Earthfile | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/yarn-project/Earthfile b/yarn-project/Earthfile index 82f52d6e6eb..1f4e2fdc790 100644 --- a/yarn-project/Earthfile +++ b/yarn-project/Earthfile @@ -202,7 +202,7 @@ anvil: FROM ../build-images+build SAVE ARTIFACT /opt/foundry/bin/anvil -end-to-end: +end-to-end-base: FROM ubuntu:noble # add repository for chromium RUN apt-get update && apt-get install -y software-properties-common \ @@ -221,10 +221,15 @@ end-to-end: ENV ACVM_BINARY_PATH=/usr/src/noir/noir-repo/target/release/acvm ENV PROVER_AGENT_CONCURRENCY=8 RUN mkdir -p $BB_WORKING_DIRECTORY $ACVM_WORKING_DIRECTORY + + RUN ln -s /usr/src/yarn-project/.yarn/releases/yarn-3.6.3.cjs /usr/local/bin/yarn + +end-to-end: + FROM +end-to-end-base + COPY +anvil/anvil /opt/foundry/bin/anvil COPY +end-to-end-prod/usr/src /usr/src WORKDIR /usr/src/yarn-project/end-to-end - RUN ln -s /usr/src/yarn-project/.yarn/releases/yarn-3.6.3.cjs /usr/local/bin/yarn ENTRYPOINT ["yarn", "test"] scripts-prod: From bb38246d09ee0e5430d31ade32bc28da688b4a84 Mon Sep 17 00:00:00 2001 From: Gregorio Juliana Date: Tue, 25 Jun 2024 14:51:20 +0200 Subject: [PATCH 2/9] feat: TXE 2: Electric boogaloo (#7154) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds new capabilities to TXE, and a proper `TokenContract` suite to showcase them: - Private -> Public calls - Public -> Public calls - Actual account contract deployment for complex interactions - Auhtwits, both public and private - Note cache storage - Speed improvements ![Screenshot 2024-06-21 at 18 16 23](https://github.com/AztecProtocol/aztec-packages/assets/5404052/16e583bb-e05e-497a-b278-1a39ee177b23) The full test suite is comparable to the e2e one (with some caveats, some things just cannot be tested yet using TXE), but runs significantly faster. Interestingly, runs faster on an M3 macbook than on mainframe (120s vs. 240s). e2e tests take more than 600s (without stored snapshots), but the gap increases as we reduce the amount of tests launched simultaneously. An unconstrained private transfer runs in less than 10s using TXE! If launching many tests in parallel, it is recommended to increase the nargo timeout for foreign calls to avoid timeout errors: `NARGO_FOREIGN_CALL_TIMEOUT=300000` Closes https://github.com/AztecProtocol/aztec-packages/issues/7086 --------- Co-authored-by: Nicolás Venturo --- noir-projects/Dockerfile.test | 4 +- noir-projects/Earthfile | 5 +- .../aztec-nr/authwit/src/cheatcodes.nr | 44 ++ noir-projects/aztec-nr/authwit/src/lib.nr | 1 + .../aztec/src/context/call_interfaces.nr | 59 ++- .../aztec-nr/aztec/src/note/lifecycle.nr | 6 +- .../aztec-nr/aztec/src/oracle/notes.nr | 3 +- .../aztec-nr/aztec/src/test/helpers.nr | 2 +- .../aztec/src/test/helpers/cheatcodes.nr | 94 +++- .../src/test/helpers/test_environment.nr | 87 ++-- .../src/test/helpers/{types.nr => utils.nr} | 51 +- .../contracts/counter_contract/src/main.nr | 24 +- .../contracts/parent_contract/src/main.nr | 5 +- .../contracts/token_contract/src/main.nr | 71 +-- .../contracts/token_contract/src/test.nr | 9 + .../token_contract/src/test/access_control.nr | 52 ++ .../contracts/token_contract/src/test/burn.nr | 179 +++++++ .../token_contract/src/test/minting.nr | 239 ++++++++++ .../src/test/reading_constants.nr | 29 ++ .../token_contract/src/test/shielding.nr | 156 ++++++ .../src/test/transfer_private.nr | 131 ++++++ .../src/test/transfer_public.nr | 122 +++++ .../token_contract/src/test/unshielding.nr | 89 ++++ .../token_contract/src/test/utils.nr | 89 ++++ .../crates/types/src/utils.nr | 3 +- .../src/transforms/contract_interface.rs | 24 +- .../src/hir/resolution/import.rs | 43 +- yarn-project/pxe/src/index.ts | 1 + yarn-project/txe/package.json | 3 +- yarn-project/txe/src/bin/index.ts | 15 +- yarn-project/txe/src/oracle/txe_oracle.ts | 444 ++++++++++++++---- .../txe/src/txe_service/txe_service.ts | 179 +++++-- .../txe/src/util/expected_failure_error.ts | 5 + .../util/txe_public_contract_data_source.ts | 63 +++ .../txe/src/util/txe_public_state_db.ts | 57 +++ 35 files changed, 2047 insertions(+), 341 deletions(-) create mode 100644 noir-projects/aztec-nr/authwit/src/cheatcodes.nr rename noir-projects/aztec-nr/aztec/src/test/helpers/{types.nr => utils.nr} (67%) create mode 100644 noir-projects/noir-contracts/contracts/token_contract/src/test.nr create mode 100644 noir-projects/noir-contracts/contracts/token_contract/src/test/access_control.nr create mode 100644 noir-projects/noir-contracts/contracts/token_contract/src/test/burn.nr create mode 100644 noir-projects/noir-contracts/contracts/token_contract/src/test/minting.nr create mode 100644 noir-projects/noir-contracts/contracts/token_contract/src/test/reading_constants.nr create mode 100644 noir-projects/noir-contracts/contracts/token_contract/src/test/shielding.nr create mode 100644 noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_private.nr create mode 100644 noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_public.nr create mode 100644 noir-projects/noir-contracts/contracts/token_contract/src/test/unshielding.nr create mode 100644 noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr create mode 100644 yarn-project/txe/src/util/expected_failure_error.ts create mode 100644 yarn-project/txe/src/util/txe_public_contract_data_source.ts create mode 100644 yarn-project/txe/src/util/txe_public_state_db.ts diff --git a/noir-projects/Dockerfile.test b/noir-projects/Dockerfile.test index 40edcbaaf35..91adc723c6a 100644 --- a/noir-projects/Dockerfile.test +++ b/noir-projects/Dockerfile.test @@ -28,7 +28,9 @@ RUN cd /usr/src/yarn-project/txe && yarn start & echo $! > /tmp/txe.pid && \ # Wait for TXE to initialize sleep 5 && \ cd ./noir-contracts && \ - ./bootstrap.sh && nargo test --silence-warnings --oracle-resolver http://localhost:8080 ; \ + # We need to increase the timeout since all tests running in parallel hammer TXE at the same time, and processing slows down leading to timeouts + # The only way we currently have to batch tests is via RAYON_NUM_THREADS, which is not ideal + ./bootstrap.sh && NARGO_FOREIGN_CALL_TIMEOUT=300000 nargo test --silence-warnings --oracle-resolver http://localhost:8080 ; \ kill $(cat /tmp/txe.pid) RUN cd /usr/src/yarn-project/txe && yarn start & echo $! > /tmp/txe.pid && \ diff --git a/noir-projects/Earthfile b/noir-projects/Earthfile index df8db2aa046..a828544fea2 100644 --- a/noir-projects/Earthfile +++ b/noir-projects/Earthfile @@ -58,7 +58,10 @@ test: RUN cd /usr/src/yarn-project/txe && yarn start & echo $! > /tmp/txe.pid && \ # Wait for TXE to initialize sleep 5 && \ - cd /usr/src/noir-projects/noir-contracts && nargo test --silence-warnings --oracle-resolver http://localhost:8080 ; \ + cd /usr/src/noir-projects/noir-contracts && \ + # We need to increase the timeout since all tests running in parallel hammer TXE at the same time and processing slows down, leading to timeouts + # The only way we currently have to batch tests is via RAYON_NUM_THREADS, which is not ideal + NARGO_FOREIGN_CALL_TIMEOUT=300000 nargo test --silence-warnings --oracle-resolver http://localhost:8080 ; \ kill $(cat /tmp/txe.pid) format: diff --git a/noir-projects/aztec-nr/authwit/src/cheatcodes.nr b/noir-projects/aztec-nr/authwit/src/cheatcodes.nr new file mode 100644 index 00000000000..f673a203277 --- /dev/null +++ b/noir-projects/aztec-nr/authwit/src/cheatcodes.nr @@ -0,0 +1,44 @@ +use dep::aztec::{ + protocol_types::address::AztecAddress, + context::{public_context::PublicContext, call_interfaces::CallInterface}, test::helpers::cheatcodes, + hash::hash_args +}; + +use crate::auth::{compute_inner_authwit_hash, compute_outer_authwit_hash, set_authorized}; + +pub fn add_private_authwit_from_call_interface( + on_behalf_of: AztecAddress, + caller: AztecAddress, + call_interface: C +) where C: CallInterface { + let target = call_interface.get_contract_address(); + let inputs = cheatcodes::get_private_context_inputs(cheatcodes::get_block_number()); + let chain_id = inputs.tx_context.chain_id; + let version = inputs.tx_context.version; + let args_hash = hash_args(call_interface.get_args()); + let selector = call_interface.get_selector(); + let inner_hash = compute_inner_authwit_hash([caller.to_field(), selector.to_field(), args_hash]); + let message_hash = compute_outer_authwit_hash(target, chain_id, version, inner_hash); + cheatcodes::add_authwit(on_behalf_of, message_hash); +} + +pub fn add_public_authwit_from_call_interface( + on_behalf_of: AztecAddress, + caller: AztecAddress, + call_interface: C +) where C: CallInterface { + let current_contract = cheatcodes::get_contract_address(); + cheatcodes::set_contract_address(on_behalf_of); + let target = call_interface.get_contract_address(); + let inputs = cheatcodes::get_private_context_inputs(cheatcodes::get_block_number()); + let chain_id = inputs.tx_context.chain_id; + let version = inputs.tx_context.version; + let args_hash = hash_args(call_interface.get_args()); + let selector = call_interface.get_selector(); + let inner_hash = compute_inner_authwit_hash([caller.to_field(), selector.to_field(), args_hash]); + let message_hash = compute_outer_authwit_hash(target, chain_id, version, inner_hash); + let mut inputs = cheatcodes::get_public_context_inputs(); + let mut context = PublicContext::new(inputs); + set_authorized(&mut context, message_hash, true); + cheatcodes::set_contract_address(current_contract); +} diff --git a/noir-projects/aztec-nr/authwit/src/lib.nr b/noir-projects/aztec-nr/authwit/src/lib.nr index e56460fd701..c4d792a4a26 100644 --- a/noir-projects/aztec-nr/authwit/src/lib.nr +++ b/noir-projects/aztec-nr/authwit/src/lib.nr @@ -2,3 +2,4 @@ mod account; mod auth_witness; mod auth; mod entrypoint; +mod cheatcodes; diff --git a/noir-projects/aztec-nr/aztec/src/context/call_interfaces.nr b/noir-projects/aztec-nr/aztec/src/context/call_interfaces.nr index dd1374f9eb0..35151d1427d 100644 --- a/noir-projects/aztec-nr/aztec/src/context/call_interfaces.nr +++ b/noir-projects/aztec-nr/aztec/src/context/call_interfaces.nr @@ -16,6 +16,7 @@ trait CallInterface { fn get_selector(self) -> FunctionSelector; fn get_name(self) -> str; fn get_contract_address(self) -> AztecAddress; + fn get_is_static(self) -> bool; } impl CallInterface for PrivateCallInterface { @@ -38,6 +39,10 @@ impl CallInterface AztecAddress { self.target_contract } + + fn get_is_static(self) -> bool { + self.is_static + } } struct PrivateCallInterface { @@ -46,7 +51,8 @@ struct PrivateCallInterface { name: str, args_hash: Field, args: [Field], - original: fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs + original: fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs, + is_static: bool } impl PrivateCallInterface { @@ -93,6 +99,10 @@ impl CallInterface AztecAddress { self.target_contract } + + fn get_is_static(self) -> bool { + self.is_static + } } struct PrivateVoidCallInterface { @@ -101,7 +111,8 @@ struct PrivateVoidCallInterface { name: str, args_hash: Field, args: [Field], - original: fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs + original: fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs, + is_static: bool } impl PrivateVoidCallInterface { @@ -144,6 +155,10 @@ impl CallInterface AztecAddress { self.target_contract } + + fn get_is_static(self) -> bool { + self.is_static + } } struct PrivateStaticCallInterface { @@ -152,7 +167,8 @@ struct PrivateStaticCallInterface { name: str, args_hash: Field, args: [Field], - original: fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs + original: fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs, + is_static: bool } impl PrivateStaticCallInterface { @@ -182,6 +198,10 @@ impl CallInterface AztecAddress { self.target_contract } + + fn get_is_static(self) -> bool { + self.is_static + } } struct PrivateStaticVoidCallInterface { @@ -190,7 +210,8 @@ struct PrivateStaticVoidCallInterface { name: str, args_hash: Field, args: [Field], - original: fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs + original: fn[Env](PrivateContextInputs) -> PrivateCircuitPublicInputs, + is_static: bool } impl PrivateStaticVoidCallInterface { @@ -219,6 +240,10 @@ impl CallInterface for PublicCallI fn get_contract_address(self) -> AztecAddress { self.target_contract } + + fn get_is_static(self) -> bool { + self.is_static + } } struct PublicCallInterface { @@ -227,7 +252,8 @@ struct PublicCallInterface { name: str, args: [Field], gas_opts: GasOpts, - original: fn[Env](PublicContextInputs) -> T + original: fn[Env](PublicContextInputs) -> T, + is_static: bool } impl PublicCallInterface { @@ -308,6 +334,10 @@ impl CallInterface for PublicVoid fn get_contract_address(self) -> AztecAddress { self.target_contract } + + fn get_is_static(self) -> bool { + self.is_static + } } struct PublicVoidCallInterface { @@ -316,7 +346,8 @@ struct PublicVoidCallInterface { name: str, args: [Field], gas_opts: GasOpts, - original: fn[Env](PublicContextInputs) -> () + original: fn[Env](PublicContextInputs) -> (), + is_static: bool } impl PublicVoidCallInterface { @@ -378,7 +409,7 @@ impl PublicVoidCallInterface { } impl CallInterface for PublicStaticCallInterface { - fn get_args(self) -> [Field] { + fn get_args(self) -> [Field] { self.args } @@ -397,6 +428,10 @@ impl CallInterface for PublicStati fn get_contract_address(self) -> AztecAddress { self.target_contract } + + fn get_is_static(self) -> bool { + self.is_static + } } struct PublicStaticCallInterface { @@ -405,7 +440,8 @@ struct PublicStaticCallInterface { name: str, args: [Field], gas_opts: GasOpts, - original: fn[Env](PublicContextInputs) -> T + original: fn[Env](PublicContextInputs) -> T, + is_static: bool } impl PublicStaticCallInterface { @@ -453,6 +489,10 @@ impl CallInterface for PublicStat fn get_contract_address(self) -> AztecAddress { self.target_contract } + + fn get_is_static(self) -> bool { + self.is_static + } } struct PublicStaticVoidCallInterface { @@ -461,7 +501,8 @@ struct PublicStaticVoidCallInterface { name: str, args: [Field], gas_opts: GasOpts, - original: fn[Env](PublicContextInputs) -> () + original: fn[Env](PublicContextInputs) -> (), + is_static: bool } impl PublicStaticVoidCallInterface { diff --git a/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr b/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr index 4a7a3a95e94..7fe6021326a 100644 --- a/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr +++ b/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr @@ -15,12 +15,10 @@ pub fn create_note( let note_hash_counter = context.side_effect_counter; let header = NoteHeader { contract_address, storage_slot, nonce: 0, note_hash_counter }; - // TODO: change this to note.set_header(header) once https://github.com/noir-lang/noir/issues/4095 is fixed - Note::set_header(note, header); + note.set_header(header); let inner_note_hash = compute_inner_note_hash(*note); - // TODO: Strong typing required because of https://github.com/noir-lang/noir/issues/4088 - let serialized_note: [Field; N] = Note::serialize_content(*note); + let serialized_note = Note::serialize_content(*note); assert( notify_created_note( storage_slot, diff --git a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr index 42c6bcdb7ee..4d7aad6f6e2 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr @@ -145,8 +145,7 @@ unconstrained pub fn get_notes( let header = NoteHeader { contract_address, nonce, storage_slot, note_hash_counter }; let serialized_note = arr_copy_slice(fields, [0; N], read_offset + 2); let mut note = Note::deserialize_content(serialized_note); - // TODO: change this to note.set_header(header) once https://github.com/noir-lang/noir/issues/4095 is fixed - Note::set_header(&mut note, header); + note.set_header(header); placeholder_opt_notes[i] = Option::some(note); }; } diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers.nr b/noir-projects/aztec-nr/aztec/src/test/helpers.nr index b28a85add1c..b7164a82359 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers.nr @@ -1,4 +1,4 @@ mod test_environment; mod cheatcodes; -mod types; +mod utils; mod keys; diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr index 014757cf9b0..db5e13ed424 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr @@ -1,6 +1,9 @@ -use dep::protocol_types::{abis::function_selector::FunctionSelector, address::{AztecAddress, PartialAddress}}; +use dep::protocol_types::{ + abis::function_selector::FunctionSelector, address::{AztecAddress, PartialAddress}, + constants::CONTRACT_INSTANCE_LENGTH, contract_instance::ContractInstance +}; use crate::context::inputs::{PublicContextInputs, PrivateContextInputs}; -use crate::test::helpers::types::{Deployer, TestAccount}; +use crate::test::helpers::utils::{Deployer, TestAccount}; use crate::keys::public_keys::PublicKeys; unconstrained pub fn reset() { @@ -19,8 +22,8 @@ unconstrained pub fn get_block_number() -> u32 { oracle_get_block_number() } -unconstrained pub fn advance_blocks(blocks: u32) { - oracle_time_travel(blocks); +unconstrained pub fn advance_blocks_by(blocks: u32) { + oracle_advance_blocks_by(blocks); } unconstrained pub fn get_private_context_inputs(historical_block_number: u32) -> PrivateContextInputs { @@ -31,20 +34,12 @@ unconstrained pub fn get_public_context_inputs() -> PublicContextInputs { oracle_get_public_context_inputs() } -unconstrained pub fn deploy( - path: str, - initializer: str, - args: [Field], - public_keys_hash: Field -) -> AztecAddress { - oracle_deploy(path, initializer, args, public_keys_hash) +unconstrained pub fn deploy(path: str, initializer: str, args: [Field], public_keys_hash: Field) -> ContractInstance { + let instance_fields = oracle_deploy(path, initializer, args, public_keys_hash); + ContractInstance::deserialize(instance_fields) } -unconstrained pub fn direct_storage_write( - contract_address: AztecAddress, - storage_slot: Field, - fields: [Field; N] -) { +unconstrained pub fn direct_storage_write(contract_address: AztecAddress, storage_slot: Field, fields: [Field; N]) { let _hash = direct_storage_write_oracle(contract_address, storage_slot, fields); } @@ -72,6 +67,40 @@ unconstrained pub fn get_side_effects_counter() -> u32 { oracle_get_side_effects_counter() } +unconstrained pub fn add_authwit(address: AztecAddress, message_hash: Field) { + orable_add_authwit(address, message_hash) +} + +unconstrained pub fn assert_public_call_fails(target_address: AztecAddress, function_selector: FunctionSelector, args: [Field]) { + oracle_assert_public_call_fails(target_address, function_selector, args) +} + +unconstrained pub fn assert_private_call_fails( + target_address: AztecAddress, + function_selector: FunctionSelector, + argsHash: Field, + sideEffectsCounter: Field, + isStaticCall: bool, + isDelegateCall: bool +) { + oracle_assert_private_call_fails( + target_address, + function_selector, + argsHash, + sideEffectsCounter, + isStaticCall, + isDelegateCall + ) +} + +unconstrained pub fn add_nullifiers(contractAddress: AztecAddress, nullifiers: [Field]) { + oracle_add_nullifiers(contractAddress, nullifiers) +} + +unconstrained pub fn add_note_hashes(contractAddress: AztecAddress, inner_note_hashes: [Field]) { + oracle_add_note_hashes(contractAddress, inner_note_hashes) +} + #[oracle(reset)] fn oracle_reset() {} @@ -84,8 +113,8 @@ fn oracle_set_contract_address(address: AztecAddress) {} #[oracle(getBlockNumber)] fn oracle_get_block_number() -> u32 {} -#[oracle(timeTravel)] -fn oracle_time_travel(blocks: u32) {} +#[oracle(advanceBlocksBy)] +fn oracle_advance_blocks_by(blocks: u32) {} #[oracle(getPrivateContextInputs)] fn oracle_get_private_context_inputs(historical_block_number: u32) -> PrivateContextInputs {} @@ -99,7 +128,7 @@ fn oracle_deploy( initializer: str, args: [Field], public_keys_hash: Field -) -> AztecAddress {} +) -> [Field; CONTRACT_INSTANCE_LENGTH] {} #[oracle(directStorageWrite)] fn direct_storage_write_oracle( @@ -125,3 +154,30 @@ fn oracle_set_msg_sender(msg_sender: AztecAddress) {} #[oracle(getSideEffectsCounter)] fn oracle_get_side_effects_counter() -> u32 {} + +#[oracle(addAuthWitness)] +fn orable_add_authwit(address: AztecAddress, message_hash: Field) {} + +#[oracle(assertPublicCallFails)] +fn oracle_assert_public_call_fails( + target_address: AztecAddress, + function_selector: FunctionSelector, + args: [Field] +) {} + +#[oracle(assertPrivateCallFails)] +fn oracle_assert_private_call_fails( + target_address: AztecAddress, + function_selector: FunctionSelector, + argsHash: Field, + sideEffectsCounter: Field, + isStaticCall: bool, + isDelegateCall: bool +) {} + +#[oracle(addNullifiers)] +fn oracle_add_nullifiers(contractAddress: AztecAddress, nullifiers: [Field]) {} + +#[oracle(addNoteHashes)] +fn oracle_add_note_hashes(contractAddress: AztecAddress, inner_note_hashes: [Field]) {} + diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr index 4f2800b19fc..9b66e64264b 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr @@ -8,9 +8,9 @@ use crate::context::inputs::{PublicContextInputs, PrivateContextInputs}; use crate::context::{packed_returns::PackedReturns, call_interfaces::CallInterface}; use crate::context::{PrivateContext, PublicContext, PrivateVoidCallInterface}; -use crate::test::helpers::{cheatcodes, types::{Deployer, TestAccount}, keys}; +use crate::test::helpers::{cheatcodes, utils::{apply_side_effects_private, Deployer, TestAccount}, keys}; use crate::keys::constants::{NULLIFIER_INDEX, INCOMING_INDEX, OUTGOING_INDEX, TAGGING_INDEX}; -use crate::hash::hash_args; +use crate::hash::{hash_args, hash_args_array}; use crate::note::{ note_header::NoteHeader, note_interface::NoteInterface, @@ -18,16 +18,12 @@ use crate::note::{ }; use crate::oracle::notes::notify_created_note; -struct TestEnvironment { - contract_address: Option, - args_hash: Option, - function_selector: Option -} +struct TestEnvironment {} impl TestEnvironment { fn new() -> Self { cheatcodes::reset(); - Self { contract_address: Option::none(), args_hash: Option::none(), function_selector: Option::none() } + Self {} } fn block_number(self) -> u32 { @@ -40,7 +36,7 @@ impl TestEnvironment { } fn advance_block_by(&mut self, blocks: u32) { - cheatcodes::advance_blocks(blocks); + cheatcodes::advance_blocks_by(blocks); } fn public(self) -> PublicContext { @@ -74,26 +70,41 @@ impl TestEnvironment { test_account.address } - fn create_account_contract(self, secret: Field) -> AztecAddress { + fn create_account_contract(&mut self, secret: Field) -> AztecAddress { let public_keys = cheatcodes::derive_keys(secret); - let args = &[public_keys.ivpk_m.x, public_keys.ivpk_m.y]; - let address = cheatcodes::deploy( + let args = [public_keys.ivpk_m.x, public_keys.ivpk_m.y]; + let instance = cheatcodes::deploy( "@aztec/noir-contracts.js/SchnorrAccount", "constructor", - args, + args.as_slice(), public_keys.hash().to_field() ); - cheatcodes::advance_blocks(1); - let test_account = cheatcodes::add_account(secret, PartialAddress::from_field(address.to_field())); - let address = test_account.address; + cheatcodes::advance_blocks_by(1); + let test_account = cheatcodes::add_account( + secret, + PartialAddress::compute( + instance.contract_class_id, + instance.salt, + instance.initialization_hash, + instance.deployer + ) + ); let keys = test_account.keys; + let address = instance.to_address(); + keys::store_master_key(NULLIFIER_INDEX, address, keys.npk_m); keys::store_master_key(INCOMING_INDEX, address, keys.ivpk_m); keys::store_master_key(OUTGOING_INDEX, address, keys.ovpk_m); keys::store_master_key(TAGGING_INDEX, address, keys.tpk_m); - test_account.address + let selector = FunctionSelector::from_signature("constructor(Field,Field)"); + + let mut context = self.private_at(cheatcodes::get_block_number()); + + let _ = context.call_private_function(address, selector, args); + + address } fn deploy(self, path: str) -> Deployer { @@ -113,7 +124,9 @@ impl TestEnvironment { cheatcodes::set_msg_sender(original_contract_address); let mut inputs = cheatcodes::get_private_context_inputs(cheatcodes::get_block_number() - 1); inputs.call_context.function_selector = call_interface.get_selector(); + inputs.call_context.is_static_call = call_interface.get_is_static(); let public_inputs = original_fn(inputs); + apply_side_effects_private(target_address, public_inputs); cheatcodes::set_contract_address(original_contract_address); cheatcodes::set_msg_sender(original_msg_sender); @@ -133,7 +146,9 @@ impl TestEnvironment { cheatcodes::set_msg_sender(original_contract_address); let mut inputs = cheatcodes::get_private_context_inputs(cheatcodes::get_block_number() - 1); inputs.call_context.function_selector = call_interface.get_selector(); + inputs.call_context.is_static_call = call_interface.get_is_static(); let public_inputs = original_fn(inputs); + apply_side_effects_private(target_address, public_inputs); cheatcodes::set_contract_address(original_contract_address); cheatcodes::set_msg_sender(original_msg_sender); @@ -151,6 +166,7 @@ impl TestEnvironment { let mut inputs = cheatcodes::get_public_context_inputs(); inputs.selector = call_interface.get_selector().to_field(); inputs.args_hash = hash_args(call_interface.get_args()); + inputs.is_static_call = call_interface.get_is_static(); let result = original_fn(inputs); cheatcodes::set_contract_address(original_contract_address); @@ -158,21 +174,23 @@ impl TestEnvironment { result } - fn call_public_void(self, call_interface: C) where C: CallInterface { - let original_fn = call_interface.get_original(); - let original_msg_sender = cheatcodes::get_msg_sender(); - let original_contract_address = cheatcodes::get_contract_address(); - let target_address = call_interface.get_contract_address(); - - cheatcodes::set_contract_address(target_address); - cheatcodes::set_msg_sender(original_contract_address); - let mut inputs = cheatcodes::get_public_context_inputs(); - inputs.selector = call_interface.get_selector().to_field(); - inputs.args_hash = hash_args(call_interface.get_args()); - original_fn(inputs); + fn assert_public_call_fails(self, call_interface: C) where C: CallInterface { + cheatcodes::assert_public_call_fails( + call_interface.get_contract_address(), + call_interface.get_selector(), + call_interface.get_args() + ); + } - cheatcodes::set_contract_address(original_contract_address); - cheatcodes::set_msg_sender(original_msg_sender); + fn assert_private_call_fails(self, call_interface: C) where C: CallInterface { + cheatcodes::assert_private_call_fails( + call_interface.get_contract_address(), + call_interface.get_selector(), + hash_args(call_interface.get_args()), + cheatcodes::get_side_effects_counter() as Field, + call_interface.get_is_static(), + false + ); } pub fn store_note_in_cache( @@ -186,12 +204,9 @@ impl TestEnvironment { let note_hash_counter = cheatcodes::get_side_effects_counter(); let header = NoteHeader { contract_address, storage_slot, nonce: 0, note_hash_counter }; - // TODO: change this to note.set_header(header) once https://github.com/noir-lang/noir/issues/4095 is fixed - Note::set_header(note, header); + note.set_header(header); let inner_note_hash = compute_inner_note_hash(*note); - - // TODO: Strong typing required because of https://github.com/noir-lang/noir/issues/4088 - let serialized_note: [Field; N] = Note::serialize_content(*note); + let serialized_note = Note::serialize_content(*note); assert( notify_created_note( storage_slot, diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/utils.nr similarity index 67% rename from noir-projects/aztec-nr/aztec/src/test/helpers/types.nr rename to noir-projects/aztec-nr/aztec/src/test/helpers/utils.nr index 7baec3523d8..808b5ad37f5 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/types.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/utils.nr @@ -1,6 +1,7 @@ use dep::protocol_types::{ traits::{Deserialize, Serialize}, address::AztecAddress, - abis::{function_selector::FunctionSelector, private_circuit_public_inputs::PrivateCircuitPublicInputs} + abis::{function_selector::FunctionSelector, private_circuit_public_inputs::PrivateCircuitPublicInputs}, + contract_instance::ContractInstance }; use crate::context::inputs::{PublicContextInputs, PrivateContextInputs}; @@ -9,6 +10,25 @@ use crate::test::helpers::cheatcodes; use crate::keys::public_keys::{PUBLIC_KEYS_LENGTH, PublicKeys}; use crate::hash::hash_args; +use crate::oracle::notes::notify_nullified_note; + +pub fn apply_side_effects_private(contract_address: AztecAddress, public_inputs: PrivateCircuitPublicInputs) { + let mut nullifiers = &[]; + for nullifier in public_inputs.new_nullifiers { + if nullifier.value != 0 { + nullifiers = nullifiers.push_back(nullifier.value); + } + } + cheatcodes::add_nullifiers(contract_address, nullifiers); + let mut note_hashes = &[]; + for note_hash in public_inputs.new_note_hashes { + if note_hash.value != 0 { + note_hashes = note_hashes.push_back(note_hash.value); + } + } + cheatcodes::add_note_hashes(contract_address, note_hashes); +} + struct Deployer { path: str, public_keys_hash: Field @@ -18,14 +38,15 @@ impl Deployer { pub fn with_private_initializer( self, call_interface: C - ) -> AztecAddress where C: CallInterface { - let address = cheatcodes::deploy( + ) -> ContractInstance where C: CallInterface { + let instance = cheatcodes::deploy( self.path, call_interface.get_name(), call_interface.get_args(), self.public_keys_hash ); - cheatcodes::advance_blocks(1); + let address = instance.to_address(); + cheatcodes::advance_blocks_by(1); let block_number = cheatcodes::get_block_number(); let original_fn = call_interface.get_original(); let original_msg_sender = cheatcodes::get_msg_sender(); @@ -35,29 +56,30 @@ impl Deployer { cheatcodes::set_msg_sender(original_contract_address); let mut inputs = cheatcodes::get_private_context_inputs(block_number - 1); inputs.call_context.function_selector = call_interface.get_selector(); - let _result = original_fn(inputs); - + let public_inputs = original_fn(inputs); + apply_side_effects_private(address, public_inputs); + cheatcodes::advance_blocks_by(1); cheatcodes::set_contract_address(original_contract_address); cheatcodes::set_msg_sender(original_msg_sender); - address + instance } pub fn with_public_initializer( self, call_interface: C - ) -> AztecAddress where C: CallInterface { - let address = cheatcodes::deploy( + ) -> ContractInstance where C: CallInterface { + let instance = cheatcodes::deploy( self.path, call_interface.get_name(), call_interface.get_args(), self.public_keys_hash ); - cheatcodes::advance_blocks(1); + cheatcodes::advance_blocks_by(1); let original_fn = call_interface.get_original(); let original_msg_sender = cheatcodes::get_msg_sender(); let original_contract_address = cheatcodes::get_contract_address(); - cheatcodes::set_contract_address(address); + cheatcodes::set_contract_address(instance.to_address()); cheatcodes::set_msg_sender(original_contract_address); let mut inputs = cheatcodes::get_public_context_inputs(); inputs.selector = call_interface.get_selector().to_field(); @@ -66,12 +88,11 @@ impl Deployer { cheatcodes::set_contract_address(original_contract_address); cheatcodes::set_msg_sender(original_msg_sender); - address + instance } - pub fn without_initializer(self) -> AztecAddress { - let address = cheatcodes::deploy(self.path, "", &[], self.public_keys_hash); - address + pub fn without_initializer(self) -> ContractInstance { + cheatcodes::deploy(self.path, "", &[], self.public_keys_hash) } } diff --git a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr index b843313be4b..27631ddbe72 100644 --- a/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/counter_contract/src/main.nr @@ -44,15 +44,19 @@ contract Counter { use dep::aztec::note::note_viewer_options::NoteViewerOptions; #[test] - fn test_initialize() { + fn test_increment() { // Setup env, generate keys let mut env = TestEnvironment::new(); let owner = env.create_account(); let outgoing_viewer = env.create_account(); + let initial_value: Field = 5; + cheatcodes::set_contract_address(owner); // Deploy contract and initialize - let initializer = Counter::interface().initialize(5, owner, outgoing_viewer); - let contract_address = env.deploy("@aztec/noir-contracts.js/Counter").with_private_initializer(initializer); + let initializer = Counter::interface().initialize(initial_value as u64, owner, outgoing_viewer); + let counter_contract = env.deploy("@aztec/noir-contracts.js/Counter").with_private_initializer(initializer); + let contract_address = counter_contract.to_address(); + // Read the stored value in the note cheatcodes::set_contract_address(contract_address); @@ -60,6 +64,18 @@ contract Counter { let owner_slot = derive_storage_slot_in_map(counter_slot, owner); let mut options = NoteViewerOptions::new(); let notes: BoundedVec = view_notes(owner_slot, options); - assert(notes.get(0).value == 5); + let initial_note_value = notes.get(0).value; + assert( + initial_note_value == initial_value, f"Expected {initial_value} but got {initial_note_value}" + ); + + // Increment the counter + let increment_call_interface = Counter::at(contract_address).increment(owner, outgoing_viewer); + env.call_private_void(increment_call_interface); + let current_value_for_owner = get_counter(owner); + let expected_current_value = initial_value + 1; + assert( + expected_current_value == current_value_for_owner, f"Expected {expected_current_value} but got {current_value_for_owner}" + ); } } diff --git a/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr b/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr index b8789b55e6f..efeae7bcda8 100644 --- a/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/parent_contract/src/main.nr @@ -257,8 +257,9 @@ contract Parent { let owner = env.create_account(); // Deploy child contract - let child_contract_address = env.deploy("@aztec/noir-contracts.js/Child").without_initializer(); - cheatcodes::advance_blocks(1); + let child_contract = env.deploy("@aztec/noir-contracts.js/Child").without_initializer(); + let child_contract_address = child_contract.to_address(); + cheatcodes::advance_blocks_by(1); // Set value in child through parent let value_to_set = 7; diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr index 36390dda0be..2cea5e637f2 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr @@ -1,6 +1,7 @@ // docs:start:token_all // docs:start:imports mod types; +mod test; // Minimal token implementation that supports `AuthWit` accounts. // The auth message follows a similar pattern to the cross-chain message and includes a designated caller. @@ -406,75 +407,5 @@ contract Token { storage.balances.balance_of(owner).to_field() } // docs:end:balance_of_private - - use dep::aztec::test::{helpers::{cheatcodes, test_environment::TestEnvironment}}; - use dep::aztec::protocol_types::storage::map::derive_storage_slot_in_map; - use dep::aztec::note::note_getter::{MAX_NOTES_PER_PAGE, view_notes}; - use dep::aztec::note::note_viewer_options::NoteViewerOptions; - - #[test] - fn test_private_transfer() { - // Setup env, generate keys - let mut env = TestEnvironment::new(); - let owner = env.create_account(); - let recipient = env.create_account(); - let mint_amount = 10000; - - // Start the test in the account contract address - cheatcodes::set_contract_address(owner); - - // Deploy token contract - let initializer_call_interface = Token::interface().constructor( - owner, - "TestToken0000000000000000000000", - "TT00000000000000000000000000000", - 18 - ); - let token_contract_address = env.deploy("@aztec/noir-contracts.js/Token").with_public_initializer(initializer_call_interface); - env.advance_block_by(1); - - // Mint some tokens - let secret = 1; - let secret_hash = compute_secret_hash(secret); - let mint_private_call_interface = Token::at(token_contract_address).mint_private(mint_amount, secret_hash); - env.call_public(mint_private_call_interface); - - // Time travel so we can read keys from the registry - env.advance_block_by(6); - - // Store a note in the cache so we can redeem it - env.store_note_in_cache( - &mut TransparentNote::new(mint_amount, secret_hash), - Token::storage().pending_shields.slot, - token_contract_address - ); - - // Redeem our shielded tokens - let redeem_shield_call_interface = Token::at(token_contract_address).redeem_shield(owner, mint_amount, secret); - env.call_private_void(redeem_shield_call_interface); - - // Not really sure why this is needed? Nullifier inclusion in contract initializer fails otherwise. - // If it were to fail, it should do it at line 443, investigation required - env.advance_block_by(1); - - // Transfer tokens - let transfer_amount = 1000; - let private_token_transfer_call_interface = Token::at(token_contract_address).transfer(recipient, transfer_amount); - env.call_private_void(private_token_transfer_call_interface); - - // Check balances - cheatcodes::set_contract_address(token_contract_address); - - let balances_slot = Token::storage().balances.slot; - let recipient_slot = derive_storage_slot_in_map(balances_slot, recipient); - let mut options = NoteViewerOptions::new(); - let notes: BoundedVec = view_notes(recipient_slot, options); - assert(notes.get(0).amount.to_field() == transfer_amount); - - let owner_slot = derive_storage_slot_in_map(balances_slot, owner); - let mut options = NoteViewerOptions::new(); - let notes: BoundedVec = view_notes(owner_slot, options); - assert(notes.get(0).amount.to_field() == mint_amount - transfer_amount); - } } // docs:end:token_all \ No newline at end of file diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test.nr new file mode 100644 index 00000000000..cf797ce3bcc --- /dev/null +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test.nr @@ -0,0 +1,9 @@ +mod access_control; +mod burn; +mod utils; +mod transfer_public; +mod transfer_private; +mod unshielding; +mod minting; +mod reading_constants; +mod shielding; diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/access_control.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/access_control.nr new file mode 100644 index 00000000000..37a84e09a7b --- /dev/null +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/access_control.nr @@ -0,0 +1,52 @@ +use crate::test::utils; +use dep::aztec::test::helpers::cheatcodes; +use crate::Token; + +#[test] +unconstrained fn access_control() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, recipient) = utils::setup(/* with_account_contracts */ false); + + // Set a new admin + let set_admin_call_interface = Token::at(token_contract_address).set_admin(recipient); + env.call_public(set_admin_call_interface); + + // Check it worked + let get_admin_call_interface = Token::at(token_contract_address).admin(); + let admin = env.call_public(get_admin_call_interface); + assert(admin == recipient.to_field()); + + // Impersonate new admin + cheatcodes::set_contract_address(recipient); + + // Check new admin is not a minter + let is_minter_call_interface = Token::at(token_contract_address).is_minter(recipient); + let is_minter = env.call_public(is_minter_call_interface); + assert(is_minter == false); + // Set admin as minter + let set_minter_call_interface = Token::at(token_contract_address).set_minter(recipient, true); + env.call_public(set_minter_call_interface); + + // Check it worked + let is_minter = env.call_public(is_minter_call_interface); + assert(is_minter == true); + + // Revoke minter as admin + let set_minter_call_interface = Token::at(token_contract_address).set_minter(recipient, false); + env.call_public(set_minter_call_interface); + + // Check it worked + let is_minter = env.call_public(is_minter_call_interface); + assert(is_minter == false); + + // Impersonate original admin + cheatcodes::set_contract_address(owner); + + // Try to set ourselves as admin, fail miserably + let set_admin_call_interface = Token::at(token_contract_address).set_admin(recipient); + env.assert_public_call_fails(set_admin_call_interface); + + // Try to revoke minter status to recipient, fail miserably + let set_minter_call_interface = Token::at(token_contract_address).set_minter(recipient, false); + env.assert_public_call_fails(set_minter_call_interface); +} diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/burn.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/burn.nr new file mode 100644 index 00000000000..af0e6cb3c31 --- /dev/null +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/burn.nr @@ -0,0 +1,179 @@ +use crate::test::utils; +use dep::aztec::{test::helpers::cheatcodes, oracle::unsafe_rand::unsafe_rand}; +use dep::authwit::cheatcodes as authwit_cheatcodes; +use crate::Token; + +#[test] +unconstrained fn burn_public_success() { + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ false); + let burn_amount = mint_amount / 10; + + // Burn less than balance + let burn_call_interface = Token::at(token_contract_address).burn_public(owner, burn_amount, 0); + env.call_public(burn_call_interface); + utils::check_public_balance(token_contract_address, owner, mint_amount - burn_amount); +} + +#[test] +unconstrained fn burn_public_on_behalf_of_other() { + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + let burn_amount = mint_amount / 10; + + // Burn on behalf of other + let burn_call_interface = Token::at(token_contract_address).burn_public(owner, burn_amount, unsafe_rand()); + authwit_cheatcodes::add_public_authwit_from_call_interface(owner, recipient, burn_call_interface); + // Impersonate recipient to perform the call + cheatcodes::set_contract_address(recipient); + // Burn tokens + env.call_public(burn_call_interface); + utils::check_public_balance(token_contract_address, owner, mint_amount - burn_amount); +} + +#[test] +unconstrained fn burn_public_failure_more_than_balance() { + let (env, token_contract_address, owner, _, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ false); + + // Burn more than balance + let burn_amount = mint_amount * 10; + let burn_call_interface = Token::at(token_contract_address).burn_public(owner, burn_amount, 0); + env.assert_public_call_fails(burn_call_interface); + utils::check_public_balance(token_contract_address, owner, mint_amount); +} + +#[test] +unconstrained fn burn_public_failure_on_behalf_of_self_non_zero_nonce() { + let (env, token_contract_address, owner, _, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ false); + + // Burn on behalf of self with non-zero nonce + let burn_amount = mint_amount / 10; + let burn_call_interface = Token::at(token_contract_address).burn_public(owner, burn_amount, unsafe_rand()); + env.assert_public_call_fails(burn_call_interface); + utils::check_public_balance(token_contract_address, owner, mint_amount); +} + +#[test] +unconstrained fn burn_public_failure_on_behalf_of_other_without_approval() { + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + + // Burn on behalf of other without approval + let burn_amount = mint_amount / 10; + let burn_call_interface = Token::at(token_contract_address).burn_public(owner, burn_amount, unsafe_rand()); + // Impersonate recipient to perform the call + cheatcodes::set_contract_address(recipient); + env.assert_public_call_fails(burn_call_interface); + utils::check_public_balance(token_contract_address, owner, mint_amount); + + // Burn on behalf of other, wrong designated caller + let burn_call_interface = Token::at(token_contract_address).burn_public(owner, burn_amount, unsafe_rand()); + authwit_cheatcodes::add_public_authwit_from_call_interface(owner, owner, burn_call_interface); + // Impersonate recipient to perform the call + cheatcodes::set_contract_address(recipient); + env.assert_public_call_fails(burn_call_interface); + utils::check_public_balance(token_contract_address, owner, mint_amount); +} + +#[test] +unconstrained fn burn_public_failure_on_behalf_of_other_wrong_caller() { + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + + // Burn on behalf of other, wrong designated caller + let burn_amount = mint_amount / 10; + let burn_call_interface = Token::at(token_contract_address).burn_public(owner, burn_amount, unsafe_rand()); + authwit_cheatcodes::add_public_authwit_from_call_interface(owner, owner, burn_call_interface); + // Impersonate recipient to perform the call + cheatcodes::set_contract_address(recipient); + env.assert_public_call_fails(burn_call_interface); + utils::check_public_balance(token_contract_address, owner, mint_amount); +} + +#[test] +unconstrained fn burn_private_on_behalf_of_self() { + let (env, token_contract_address, owner, _, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ false); + let burn_amount = mint_amount / 10; + + // Burn less than balance + let burn_call_interface = Token::at(token_contract_address).burn(owner, burn_amount, 0); + env.call_private_void(burn_call_interface); + utils::check_private_balance(token_contract_address, owner, mint_amount - burn_amount); +} + +#[test] +unconstrained fn burn_private_on_behalf_of_other() { + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + let burn_amount = mint_amount / 10; + + // Burn on behalf of other + let burn_call_interface = Token::at(token_contract_address).burn(owner, burn_amount, unsafe_rand()); + authwit_cheatcodes::add_private_authwit_from_call_interface(owner, recipient, burn_call_interface); + // Impersonate recipient to perform the call + cheatcodes::set_contract_address(recipient); + // Burn tokens + env.call_private_void(burn_call_interface); + utils::check_private_balance(token_contract_address, owner, mint_amount - burn_amount); +} + +#[test(should_fail_with="Balance too low")] +unconstrained fn burn_private_failure_more_than_balance() { + let (env, token_contract_address, owner, _, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ false); + + // Burn more than balance + let burn_amount = mint_amount * 10; + let burn_call_interface = Token::at(token_contract_address).burn(owner, burn_amount, 0); + env.call_private_void(burn_call_interface); + // Private doesnt revert, so we cannot check balances here since notes have already been nullified. Test is done. +} + +#[test(should_fail_with="invalid nonce")] +unconstrained fn burn_private_failure_on_behalf_of_self_non_zero_nonce() { + let (env, token_contract_address, owner, _, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ false); + + // Burn more than balance + let burn_amount = mint_amount / 10; + let burn_call_interface = Token::at(token_contract_address).burn(owner, burn_amount, unsafe_rand()); + env.call_private_void(burn_call_interface); + // Private doesnt revert, so we cannot check balances here since notes have already been nullified. Test is done. +} + +#[test(should_fail)] +unconstrained fn burn_private_failure_on_behalf_of_other_more_than_balance() { + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + + // Burn more than balance + let burn_amount = mint_amount * 10; + // Burn on behalf of other + let burn_call_interface = Token::at(token_contract_address).burn(owner, burn_amount, unsafe_rand()); + authwit_cheatcodes::add_private_authwit_from_call_interface(owner, recipient, burn_call_interface); + // Impersonate recipient to perform the call + cheatcodes::set_contract_address(recipient); + env.call_private_void(burn_call_interface); + // Private doesnt revert, so we cannot check balances here since notes have already been nullified. Test is done. +} + +#[test(should_fail)] +unconstrained fn burn_private_failure_on_behalf_of_other_without_approval() { + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + + // Burn more than balance + let burn_amount = mint_amount / 10; + // Burn on behalf of other + let burn_call_interface = Token::at(token_contract_address).burn(owner, burn_amount, unsafe_rand()); + // Impersonate recipient to perform the call + cheatcodes::set_contract_address(recipient); + env.call_private_void(burn_call_interface); + // Private doesnt revert, so we cannot check balances here since notes have already been nullified. Test is done. +} + +#[test(should_fail)] +unconstrained fn burn_private_failure_on_behalf_of_other_wrong_designated_caller() { + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + + // Burn more than balance + let burn_amount = mint_amount / 10; + // Burn on behalf of other + let burn_call_interface = Token::at(token_contract_address).burn(owner, burn_amount, unsafe_rand()); + authwit_cheatcodes::add_private_authwit_from_call_interface(owner, owner, burn_call_interface); + // Impersonate recipient to perform the call + cheatcodes::set_contract_address(recipient); + env.call_private_void(burn_call_interface); + // Private doesnt revert, so we cannot check balances here since notes have already been nullified. Test is done. +} diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/minting.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/minting.nr new file mode 100644 index 00000000000..4e92489a59a --- /dev/null +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/minting.nr @@ -0,0 +1,239 @@ +use crate::test::utils; +use dep::aztec::{test::helpers::cheatcodes, oracle::unsafe_rand::unsafe_rand, hash::compute_secret_hash}; +use crate::{types::transparent_note::TransparentNote, Token}; + +#[test] +unconstrained fn mint_public_success() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, _) = utils::setup(/* with_account_contracts */ false); + + let mint_amount = 10000; + let mint_public_call_interface = Token::at(token_contract_address).mint_public(owner, mint_amount); + env.call_public(mint_public_call_interface); + + utils::check_public_balance(token_contract_address, owner, mint_amount); + + let total_supply_call_interface = Token::at(token_contract_address).total_supply(); + let total_supply = env.call_public(total_supply_call_interface); + + assert(total_supply == mint_amount); +} + +#[test] +unconstrained fn mint_public_failures() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, recipient) = utils::setup(/* with_account_contracts */ false); + + // As non-minter + let mint_amount = 10000; + cheatcodes::set_contract_address(recipient); + let mint_public_call_interface = Token::at(token_contract_address).mint_public(owner, mint_amount); + env.assert_public_call_fails(mint_public_call_interface); + + utils::check_public_balance(token_contract_address, owner, 0); + + cheatcodes::set_contract_address(owner); + + // Overflow recipient + + let mint_amount = 2.pow_32(128); + let mint_public_call_interface = Token::at(token_contract_address).mint_public(owner, mint_amount); + env.assert_public_call_fails(mint_public_call_interface); + + utils::check_public_balance(token_contract_address, owner, 0); + + // Overflow total supply + + let mint_for_recipient_amount = 1000; + + let mint_public_call_interface = Token::at(token_contract_address).mint_public(recipient, mint_for_recipient_amount); + env.call_public(mint_public_call_interface); + + let mint_amount = 2.pow_32(128) - mint_for_recipient_amount; + let mint_public_call_interface = Token::at(token_contract_address).mint_public(owner, mint_amount); + env.assert_public_call_fails(mint_public_call_interface); + + utils::check_public_balance(token_contract_address, recipient, mint_for_recipient_amount); + utils::check_public_balance(token_contract_address, owner, 0); +} + +#[test] +unconstrained fn mint_private_success() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, _) = utils::setup(/* with_account_contracts */ false); + let mint_amount = 10000; + // Mint some tokens + let secret = unsafe_rand(); + let secret_hash = compute_secret_hash(secret); + let mint_private_call_interface = Token::at(token_contract_address).mint_private(mint_amount, secret_hash); + env.call_public(mint_private_call_interface); + + let mint_public_call_interface = Token::at(token_contract_address).mint_public(owner, mint_amount); + env.call_public(mint_public_call_interface); + + // Time travel so we can read keys from the registry + env.advance_block_by(6); + + // Store a note in the cache so we can redeem it + env.store_note_in_cache( + &mut TransparentNote::new(mint_amount, secret_hash), + Token::storage().pending_shields.slot, + token_contract_address + ); + + // Redeem our shielded tokens + let redeem_shield_call_interface = Token::at(token_contract_address).redeem_shield(owner, mint_amount, secret); + env.call_private_void(redeem_shield_call_interface); + + utils::check_private_balance(token_contract_address, owner, mint_amount); +} + +#[test(should_fail_with="Cannot return zero notes")] +unconstrained fn mint_private_failure_double_spend() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, recipient) = utils::setup(/* with_account_contracts */ false); + let mint_amount = 10000; + // Mint some tokens + let secret = unsafe_rand(); + let secret_hash = compute_secret_hash(secret); + let mint_private_call_interface = Token::at(token_contract_address).mint_private(mint_amount, secret_hash); + env.call_public(mint_private_call_interface); + + let mint_public_call_interface = Token::at(token_contract_address).mint_public(owner, mint_amount); + env.call_public(mint_public_call_interface); + + // Time travel so we can read keys from the registry + env.advance_block_by(6); + + // Store a note in the cache so we can redeem it + env.store_note_in_cache( + &mut TransparentNote::new(mint_amount, secret_hash), + Token::storage().pending_shields.slot, + token_contract_address + ); + + // Redeem our shielded tokens + let redeem_shield_call_interface = Token::at(token_contract_address).redeem_shield(owner, mint_amount, secret); + env.call_private_void(redeem_shield_call_interface); + + utils::check_private_balance(token_contract_address, owner, mint_amount); + + // Attempt to double spend + let redeem_shield_call_interface = Token::at(token_contract_address).redeem_shield(recipient, mint_amount, secret); + env.call_private_void(redeem_shield_call_interface); +} + +#[test(should_fail_with="caller is not minter")] +unconstrained fn mint_private_failure_non_minter() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, _, recipient) = utils::setup(/* with_account_contracts */ false); + let mint_amount = 10000; + // Try to mint some tokens impersonating recipient + cheatcodes::set_contract_address(recipient); + + let secret = unsafe_rand(); + let secret_hash = compute_secret_hash(secret); + let mint_private_call_interface = Token::at(token_contract_address).mint_private(mint_amount, secret_hash); + env.call_public(mint_private_call_interface); +} + +#[test(should_fail_with="call to assert_max_bit_size")] +unconstrained fn mint_private_failure_overflow() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, _, _) = utils::setup(/* with_account_contracts */ false); + + // Overflow recipient + let mint_amount = 2.pow_32(128); + let secret = unsafe_rand(); + let secret_hash = compute_secret_hash(secret); + let mint_private_call_interface = Token::at(token_contract_address).mint_private(mint_amount, secret_hash); + env.call_public(mint_private_call_interface); +} + +#[test(should_fail_with="attempt to add with overflow")] +unconstrained fn mint_private_failure_overflow_recipient() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, _) = utils::setup(/* with_account_contracts */ false); + let mint_amount = 10000; + // Mint some tokens + let secret = unsafe_rand(); + let secret_hash = compute_secret_hash(secret); + let mint_private_call_interface = Token::at(token_contract_address).mint_private(mint_amount, secret_hash); + env.call_public(mint_private_call_interface); + + // Time travel so we can read keys from the registry + env.advance_block_by(6); + + // Store a note in the cache so we can redeem it + env.store_note_in_cache( + &mut TransparentNote::new(mint_amount, secret_hash), + Token::storage().pending_shields.slot, + token_contract_address + ); + + // Redeem our shielded tokens + let redeem_shield_call_interface = Token::at(token_contract_address).redeem_shield(owner, mint_amount, secret); + env.call_private_void(redeem_shield_call_interface); + + utils::check_private_balance(token_contract_address, owner, mint_amount); + + let mint_amount = 2.pow_32(128) - mint_amount; + // Mint some tokens + let secret = unsafe_rand(); + let secret_hash = compute_secret_hash(secret); + let mint_private_call_interface = Token::at(token_contract_address).mint_private(mint_amount, secret_hash); + env.call_public(mint_private_call_interface); +} + +#[test(should_fail_with="attempt to add with overflow")] +unconstrained fn mint_private_failure_overflow_total_supply() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, recipient) = utils::setup(/* with_account_contracts */ false); + let mint_amount = 10000; + // Mint some tokens + let secret_owner = unsafe_rand(); + let secret_recipient = unsafe_rand(); + let secret_hash_owner = compute_secret_hash(secret_owner); + let secret_hash_recipient = compute_secret_hash(secret_recipient); + + let mint_private_call_interface = Token::at(token_contract_address).mint_private(mint_amount, secret_hash_owner); + env.call_public(mint_private_call_interface); + let mint_private_call_interface = Token::at(token_contract_address).mint_private(mint_amount, secret_hash_recipient); + env.call_public(mint_private_call_interface); + + // Time travel so we can read keys from the registry + env.advance_block_by(6); + + // Store 2 notes in the cache so we can redeem it for owner and recipient + env.store_note_in_cache( + &mut TransparentNote::new(mint_amount, secret_hash_owner), + Token::storage().pending_shields.slot, + token_contract_address + ); + env.store_note_in_cache( + &mut TransparentNote::new(mint_amount, secret_hash_recipient), + Token::storage().pending_shields.slot, + token_contract_address + ); + + // Redeem owner's shielded tokens + cheatcodes::set_contract_address(owner); + let redeem_shield_call_interface = Token::at(token_contract_address).redeem_shield(owner, mint_amount, secret_owner); + env.call_private_void(redeem_shield_call_interface); + + // Redeem recipient's shielded tokens + cheatcodes::set_contract_address(recipient); + let redeem_shield_call_interface = Token::at(token_contract_address).redeem_shield(recipient, mint_amount, secret_recipient); + env.call_private_void(redeem_shield_call_interface); + + utils::check_private_balance(token_contract_address, owner, mint_amount); + utils::check_private_balance(token_contract_address, recipient, mint_amount); + + cheatcodes::set_contract_address(owner); + let mint_amount = 2.pow_32(128) - 2 * mint_amount; + // Try to mint some tokens + let secret = unsafe_rand(); + let secret_hash = compute_secret_hash(secret); + let mint_private_call_interface = Token::at(token_contract_address).mint_private(mint_amount, secret_hash); + env.call_public(mint_private_call_interface); +} diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/reading_constants.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/reading_constants.nr new file mode 100644 index 00000000000..469ff747590 --- /dev/null +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/reading_constants.nr @@ -0,0 +1,29 @@ +use crate::test::utils; +use dep::aztec::test::helpers::cheatcodes; +use crate::Token; + +// It is not possible to deserialize strings in Noir ATM, so name and symbol cannot be checked yet. + +#[test] +unconstrained fn check_decimals_private() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, _, _) = utils::setup(/* with_account_contracts */ false); + + // Check decimals + let private_get_decimals_call_interface = Token::at(token_contract_address).private_get_decimals(); + let result = env.call_private(private_get_decimals_call_interface); + + assert(result == 18); +} + +#[test] +unconstrained fn check_decimals_public() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, _, _) = utils::setup(/* with_account_contracts */ false); + + // Check decimals + let public_get_decimals_call_interface = Token::at(token_contract_address).public_get_decimals(); + let result = env.call_public(public_get_decimals_call_interface); + + assert(result == 18 as u8); +} diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/shielding.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/shielding.nr new file mode 100644 index 00000000000..66280304481 --- /dev/null +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/shielding.nr @@ -0,0 +1,156 @@ +use crate::test::utils; +use dep::aztec::{test::helpers::cheatcodes, oracle::unsafe_rand::unsafe_rand, hash::compute_secret_hash}; +use dep::authwit::cheatcodes as authwit_cheatcodes; +use crate::{types::transparent_note::TransparentNote, Token}; + +#[test] +unconstrained fn shielding_on_behalf_of_self() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, _, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ false); + let secret = unsafe_rand(); + let secret_hash = compute_secret_hash(secret); + // Shield tokens + let shield_amount = mint_amount / 10; + let shield_call_interface = Token::at(token_contract_address).shield(owner, shield_amount, secret_hash, 0); + env.call_public(shield_call_interface); + + // Store a note in the cache so we can redeem it + env.store_note_in_cache( + &mut TransparentNote::new(shield_amount, secret_hash), + Token::storage().pending_shields.slot, + token_contract_address + ); + + // Redeem our shielded tokens + let redeem_shield_call_interface = Token::at(token_contract_address).redeem_shield(owner, shield_amount, secret); + env.call_private_void(redeem_shield_call_interface); + + // Check balances + utils::check_public_balance(token_contract_address, owner, mint_amount - shield_amount); + utils::check_private_balance(token_contract_address, owner, mint_amount + shield_amount); +} + +#[test] +unconstrained fn shielding_on_behalf_of_other() { + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + let secret = unsafe_rand(); + let secret_hash = compute_secret_hash(secret); + + // Shield tokens on behalf of owner + let shield_amount = 1000; + let shield_call_interface = Token::at(token_contract_address).shield(owner, shield_amount, secret_hash, 0); + authwit_cheatcodes::add_public_authwit_from_call_interface(owner, recipient, shield_call_interface); + // Impersonate recipient to perform the call + cheatcodes::set_contract_address(recipient); + // Shield tokens + env.call_public(shield_call_interface); + + // Become owner again + cheatcodes::set_contract_address(owner); + // Store a note in the cache so we can redeem it + env.store_note_in_cache( + &mut TransparentNote::new(shield_amount, secret_hash), + Token::storage().pending_shields.slot, + token_contract_address + ); + + // Redeem our shielded tokens + let redeem_shield_call_interface = Token::at(token_contract_address).redeem_shield(owner, shield_amount, secret); + env.call_private_void(redeem_shield_call_interface); + + // Check balances + utils::check_public_balance(token_contract_address, owner, mint_amount - shield_amount); + utils::check_private_balance(token_contract_address, owner, mint_amount + shield_amount); +} + +#[test] +unconstrained fn shielding_failure_on_behalf_of_self_more_than_balance() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, _, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + let secret = unsafe_rand(); + let secret_hash = compute_secret_hash(secret); + // Shield tokens + let shield_amount = mint_amount + 1; + let shield_call_interface = Token::at(token_contract_address).shield(owner, shield_amount, secret_hash, 0); + env.assert_public_call_fails(shield_call_interface); + + // Check balances + utils::check_public_balance(token_contract_address, owner, mint_amount); + utils::check_private_balance(token_contract_address, owner, mint_amount); +} + +#[test] +unconstrained fn shielding_failure_on_behalf_of_self_invalid_nonce() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, _, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + let secret = unsafe_rand(); + let secret_hash = compute_secret_hash(secret); + // Shield tokens + let shield_amount = mint_amount / 10; + let shield_call_interface = Token::at(token_contract_address).shield(owner, shield_amount, secret_hash, unsafe_rand()); + env.assert_public_call_fails(shield_call_interface); + + // Check balances + utils::check_public_balance(token_contract_address, owner, mint_amount); + utils::check_private_balance(token_contract_address, owner, mint_amount); +} + +#[test] +unconstrained fn shielding_failure_on_behalf_of_other_more_than_balance() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + let secret = unsafe_rand(); + let secret_hash = compute_secret_hash(secret); + // Shield tokens on behalf of owner + let shield_amount = mint_amount + 1; + let shield_call_interface = Token::at(token_contract_address).shield(owner, shield_amount, secret_hash, 0); + authwit_cheatcodes::add_public_authwit_from_call_interface(owner, recipient, shield_call_interface); + // Impersonate recipient to perform the call + cheatcodes::set_contract_address(recipient); + // Shield tokens + env.assert_public_call_fails(shield_call_interface); + + // Check balances + utils::check_public_balance(token_contract_address, owner, mint_amount); + utils::check_private_balance(token_contract_address, owner, mint_amount); +} + +#[test] +unconstrained fn shielding_failure_on_behalf_of_other_wrong_caller() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + let secret = unsafe_rand(); + let secret_hash = compute_secret_hash(secret); + // Shield tokens on behalf of owner + let shield_amount = mint_amount + 1; + let shield_call_interface = Token::at(token_contract_address).shield(owner, shield_amount, secret_hash, 0); + authwit_cheatcodes::add_public_authwit_from_call_interface(owner, owner, shield_call_interface); + // Impersonate recipient to perform the call + cheatcodes::set_contract_address(recipient); + // Shield tokens + env.assert_public_call_fails(shield_call_interface); + + // Check balances + utils::check_public_balance(token_contract_address, owner, mint_amount); + utils::check_private_balance(token_contract_address, owner, mint_amount); +} + +#[test] +unconstrained fn shielding_failure_on_behalf_of_other_without_approval() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + let secret = unsafe_rand(); + let secret_hash = compute_secret_hash(secret); + // Shield tokens on behalf of owner + let shield_amount = mint_amount + 1; + let shield_call_interface = Token::at(token_contract_address).shield(owner, shield_amount, secret_hash, 0); + // Impersonate recipient to perform the call + cheatcodes::set_contract_address(recipient); + // Shield tokens + env.assert_public_call_fails(shield_call_interface); + + // Check balances + utils::check_public_balance(token_contract_address, owner, mint_amount); + utils::check_private_balance(token_contract_address, owner, mint_amount); +} + diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_private.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_private.nr new file mode 100644 index 00000000000..47e04809114 --- /dev/null +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_private.nr @@ -0,0 +1,131 @@ +use crate::test::utils; +use dep::aztec::{test::helpers::cheatcodes, oracle::unsafe_rand::unsafe_rand, protocol_types::address::AztecAddress}; +use dep::authwit::cheatcodes as authwit_cheatcodes; +use crate::Token; + +#[test] +unconstrained fn transfer_private() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ false); + // Transfer tokens + let transfer_amount = 1000; + let transfer_private_call_interface = Token::at(token_contract_address).transfer(recipient, transfer_amount); + env.call_private_void(transfer_private_call_interface); + + // Check balances + utils::check_private_balance(token_contract_address, owner, mint_amount - transfer_amount); + utils::check_private_balance(token_contract_address, recipient, transfer_amount); +} + +#[test] +unconstrained fn transfer_private_to_self() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, _, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ false); + // Transfer tokens + let transfer_amount = 1000; + let transfer_private_call_interface = Token::at(token_contract_address).transfer(owner, transfer_amount); + env.call_private_void(transfer_private_call_interface); + + // Check balances + utils::check_private_balance(token_contract_address, owner, mint_amount); +} + +#[test] +unconstrained fn transfer_private_to_non_deployed_account() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, _, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ false); + let not_deployed = cheatcodes::create_account(); + // Transfer tokens + let transfer_amount = 1000; + let transfer_private_call_interface = Token::at(token_contract_address).transfer(not_deployed.address, transfer_amount); + env.call_private_void(transfer_private_call_interface); + + // Check balances + utils::check_private_balance(token_contract_address, owner, mint_amount - transfer_amount); + utils::check_private_balance(token_contract_address, not_deployed.address, transfer_amount); +} + +#[test] +unconstrained fn transfer_private_on_behalf_of_other() { + // Setup with account contracts. Slower since we actually deploy them, but needed for authwits. + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + // Add authwit + let transfer_amount = 1000; + let transfer_private_from_call_interface = Token::at(token_contract_address).transfer_from(owner, recipient, transfer_amount, 1); + authwit_cheatcodes::add_private_authwit_from_call_interface(owner, recipient, transfer_private_from_call_interface); + // Impersonate recipient to perform the call + cheatcodes::set_contract_address(recipient); + // Transfer tokens + env.call_private_void(transfer_private_from_call_interface); + // Check balances + utils::check_private_balance(token_contract_address, owner, mint_amount - transfer_amount); + utils::check_private_balance(token_contract_address, recipient, transfer_amount); +} + +#[test(should_fail_with="Balance too low")] +unconstrained fn transfer_private_failure_more_than_balance() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, _, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ false); + // Transfer tokens + let transfer_amount = mint_amount + 1; + let transfer_private_call_interface = Token::at(token_contract_address).transfer(recipient, transfer_amount); + env.call_private_void(transfer_private_call_interface); +} + +#[test(should_fail_with="invalid nonce")] +unconstrained fn transfer_private_failure_on_behalf_of_self_non_zero_nonce() { + // Setup with account contracts. Slower since we actually deploy them, but needed for authwits. + let (env, token_contract_address, owner, recipient, _) = utils::setup_and_mint(/* with_account_contracts */ true); + // Add authwit + let transfer_amount = 1000; + let transfer_private_from_call_interface = Token::at(token_contract_address).transfer_from(owner, recipient, transfer_amount, 1); + // Transfer tokens + env.call_private_void(transfer_private_from_call_interface); +} + +#[test(should_fail_with="Balance too low")] +unconstrained fn transfer_private_failure_on_behalf_of_more_than_balance() { + // Setup with account contracts. Slower since we actually deploy them, but needed for authwits. + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + // Add authwit + let transfer_amount = mint_amount + 1; + let transfer_private_from_call_interface = Token::at(token_contract_address).transfer_from(owner, recipient, transfer_amount, 1); + authwit_cheatcodes::add_private_authwit_from_call_interface(owner, recipient, transfer_private_from_call_interface); + // Impersonate recipient to perform the call + cheatcodes::set_contract_address(recipient); + // Transfer tokens + env.call_private_void(transfer_private_from_call_interface); +} + +#[test(should_fail)] +unconstrained fn transfer_private_failure_on_behalf_of_other_without_approval() { + // Setup with account contracts. Slower since we actually deploy them, but needed for authwits. + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + // Add authwit + let transfer_amount = 1000; + let transfer_private_from_call_interface = Token::at(token_contract_address).transfer_from(owner, recipient, transfer_amount, 1); + // Impersonate recipient to perform the call + cheatcodes::set_contract_address(recipient); + // Transfer tokens + env.call_private_void(transfer_private_from_call_interface); + // Check balances + utils::check_private_balance(token_contract_address, owner, mint_amount - transfer_amount); + utils::check_private_balance(token_contract_address, recipient, transfer_amount); +} + +#[test(should_fail)] +unconstrained fn transfer_private_failure_on_behalf_of_other_wrong_caller() { + // Setup with account contracts. Slower since we actually deploy them, but needed for authwits. + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + // Add authwit + let transfer_amount = 1000; + let transfer_private_from_call_interface = Token::at(token_contract_address).transfer_from(owner, recipient, transfer_amount, 1); + authwit_cheatcodes::add_private_authwit_from_call_interface(owner, owner, transfer_private_from_call_interface); + // Impersonate recipient to perform the call + cheatcodes::set_contract_address(recipient); + // Transfer tokens + env.call_private_void(transfer_private_from_call_interface); + // Check balances + utils::check_private_balance(token_contract_address, owner, mint_amount - transfer_amount); + utils::check_private_balance(token_contract_address, recipient, transfer_amount); +} diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_public.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_public.nr new file mode 100644 index 00000000000..ae0b631ce37 --- /dev/null +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/transfer_public.nr @@ -0,0 +1,122 @@ +use crate::test::utils; +use dep::aztec::{test::helpers::cheatcodes, oracle::unsafe_rand::unsafe_rand}; +use dep::authwit::cheatcodes as authwit_cheatcodes; +use crate::Token; + +#[test] +unconstrained fn public_transfer() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ false); + // Transfer tokens + let transfer_amount = mint_amount / 10; + let public_transfer_call_interface = Token::at(token_contract_address).transfer_public(owner, recipient, transfer_amount, 0); + env.call_public(public_transfer_call_interface); + + // Check balances + utils::check_public_balance(token_contract_address, owner, mint_amount - transfer_amount); + utils::check_public_balance(token_contract_address, recipient, transfer_amount); +} + +#[test] +unconstrained fn public_transfer_to_self() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, _, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ false); + // Transfer tokens + let transfer_amount = mint_amount / 10; + let public_transfer_call_interface = Token::at(token_contract_address).transfer_public(owner, owner, transfer_amount, 0); + env.call_public(public_transfer_call_interface); + + // Check balances + utils::check_public_balance(token_contract_address, owner, mint_amount); +} + +#[test] +unconstrained fn public_transfer_on_behalf_of_other() { + // Setup with account contracts. Slower since we actually deploy them, but needed for authwits. + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + let transfer_amount = mint_amount / 10; + let public_transfer_from_call_interface = Token::at(token_contract_address).transfer_public(owner, recipient, transfer_amount, 1); + authwit_cheatcodes::add_public_authwit_from_call_interface(owner, recipient, public_transfer_from_call_interface); + // Impersonate recipient to perform the call + cheatcodes::set_contract_address(recipient); + // Transfer tokens + env.call_public(public_transfer_from_call_interface); + // Check balances + utils::check_public_balance(token_contract_address, owner, mint_amount - transfer_amount); + utils::check_public_balance(token_contract_address, recipient, transfer_amount); +} + +#[test] +unconstrained fn public_transfer_failure_more_than_balance() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ false); + // Transfer tokens + let transfer_amount = mint_amount + 1; + let public_transfer_call_interface = Token::at(token_contract_address).transfer_public(owner, recipient, transfer_amount, 0); + // Try to transfer tokens + env.assert_public_call_fails(public_transfer_call_interface); + + // Check balances + utils::check_public_balance(token_contract_address, owner, mint_amount); +} + +#[test] +unconstrained fn public_transfer_failure_on_behalf_of_self_non_zero_nonce() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + // Transfer tokens + let transfer_amount = mint_amount / 10; + let public_transfer_call_interface = Token::at(token_contract_address).transfer_public(owner, recipient, transfer_amount, unsafe_rand()); + // Try to transfer tokens + env.assert_public_call_fails(public_transfer_call_interface); + + // Check balances + utils::check_public_balance(token_contract_address, owner, mint_amount); +} + +#[test] +unconstrained fn public_transfer_failure_on_behalf_of_other_without_approval() { + // Setup with account contracts. Slower since we actually deploy them, but needed for authwits. + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + let transfer_amount = mint_amount / 10; + let public_transfer_from_call_interface = Token::at(token_contract_address).transfer_public(owner, recipient, transfer_amount, 1); + // Impersonate recipient to perform the call + cheatcodes::set_contract_address(recipient); + // Try to transfer tokens + env.assert_public_call_fails(public_transfer_from_call_interface); + // Check balances + utils::check_public_balance(token_contract_address, owner, mint_amount); + utils::check_public_balance(token_contract_address, recipient, 0); +} + +#[test] +unconstrained fn public_transfer_failure_on_behalf_of_other_more_than_balance() { + // Setup with account contracts. Slower since we actually deploy them, but needed for authwits. + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + let transfer_amount = mint_amount + 1; + let public_transfer_from_call_interface = Token::at(token_contract_address).transfer_public(owner, recipient, transfer_amount, 1); + authwit_cheatcodes::add_public_authwit_from_call_interface(owner, recipient, public_transfer_from_call_interface); + // Impersonate recipient to perform the call + cheatcodes::set_contract_address(recipient); + // Try to transfer tokens + env.assert_public_call_fails(public_transfer_from_call_interface); + // Check balances + utils::check_public_balance(token_contract_address, owner, mint_amount); + utils::check_public_balance(token_contract_address, recipient, 0); +} + +#[test] +unconstrained fn public_transfer_failure_on_behalf_of_other_wrong_caller() { + // Setup with account contracts. Slower since we actually deploy them, but needed for authwits. + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + let transfer_amount = mint_amount / 10; + let public_transfer_from_call_interface = Token::at(token_contract_address).transfer_public(owner, recipient, transfer_amount, 1); + authwit_cheatcodes::add_public_authwit_from_call_interface(owner, owner, public_transfer_from_call_interface); + // Impersonate recipient to perform the call + cheatcodes::set_contract_address(recipient); + // Try to transfer tokens + env.assert_public_call_fails(public_transfer_from_call_interface); + // Check balances + utils::check_public_balance(token_contract_address, owner, mint_amount); + utils::check_public_balance(token_contract_address, recipient, 0); +} diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/unshielding.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/unshielding.nr new file mode 100644 index 00000000000..52987cb1736 --- /dev/null +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/unshielding.nr @@ -0,0 +1,89 @@ +use crate::test::utils; +use dep::aztec::{oracle::unsafe_rand::unsafe_rand, test::helpers::cheatcodes}; +use dep::authwit::cheatcodes as authwit_cheatcodes; +use crate::Token; + +#[test] +unconstrained fn unshield_on_behalf_of_self() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, _, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ false); + + let unshield_amount = mint_amount / 10; + let unshield_call_interface = Token::at(token_contract_address).unshield(owner, owner, unshield_amount, 0); + env.call_private_void(unshield_call_interface); + utils::check_private_balance(token_contract_address, owner, mint_amount - unshield_amount); + utils::check_public_balance(token_contract_address, owner, mint_amount + unshield_amount); +} + +#[test] +unconstrained fn unshield_on_behalf_of_other() { + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + + let unshield_amount = mint_amount / 10; + let unshield_call_interface = Token::at(token_contract_address).unshield(owner, recipient, unshield_amount, 0); + authwit_cheatcodes::add_private_authwit_from_call_interface(owner, recipient, unshield_call_interface); + // Impersonate recipient + cheatcodes::set_contract_address(recipient); + // Unshield tokens + env.call_private_void(unshield_call_interface); + utils::check_private_balance(token_contract_address, owner, mint_amount - unshield_amount); + utils::check_public_balance(token_contract_address, recipient, unshield_amount); +} + +#[test(should_fail_with="Balance too low")] +unconstrained fn unshield_failure_more_than_balance() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, _, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ false); + + let unshield_amount = mint_amount + 1; + let unshield_call_interface = Token::at(token_contract_address).unshield(owner, owner, unshield_amount, 0); + env.call_private_void(unshield_call_interface); +} + +#[test(should_fail_with="invalid nonce")] +unconstrained fn unshield_failure_on_behalf_of_self_non_zero_nonce() { + // Setup without account contracts. We are not using authwits here, so dummy accounts are enough + let (env, token_contract_address, owner, _, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ false); + + let unshield_amount = mint_amount + 1; + let unshield_call_interface = Token::at(token_contract_address).unshield(owner, owner, unshield_amount, unsafe_rand()); + env.call_private_void(unshield_call_interface); +} + +#[test(should_fail_with="Balance too low")] +unconstrained fn unshield_failure_on_behalf_of_other_more_than_balance() { + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + + let unshield_amount = mint_amount + 1; + let unshield_call_interface = Token::at(token_contract_address).unshield(owner, recipient, unshield_amount, 0); + authwit_cheatcodes::add_private_authwit_from_call_interface(owner, recipient, unshield_call_interface); + // Impersonate recipient + cheatcodes::set_contract_address(recipient); + // Unshield tokens + env.call_private_void(unshield_call_interface); +} + +#[test(should_fail)] +unconstrained fn unshield_failure_on_behalf_of_other_invalid_designated_caller() { + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + + let unshield_amount = mint_amount + 1; + let unshield_call_interface = Token::at(token_contract_address).unshield(owner, recipient, unshield_amount, 0); + authwit_cheatcodes::add_private_authwit_from_call_interface(owner, owner, unshield_call_interface); + // Impersonate recipient + cheatcodes::set_contract_address(recipient); + // Unshield tokens + env.call_private_void(unshield_call_interface); +} + +#[test(should_fail)] +unconstrained fn unshield_failure_on_behalf_of_other_no_approval() { + let (env, token_contract_address, owner, recipient, mint_amount) = utils::setup_and_mint(/* with_account_contracts */ true); + + let unshield_amount = mint_amount + 1; + let unshield_call_interface = Token::at(token_contract_address).unshield(owner, recipient, unshield_amount, 0); + // Impersonate recipient + cheatcodes::set_contract_address(recipient); + // Unshield tokens + env.call_private_void(unshield_call_interface); +} diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr new file mode 100644 index 00000000000..1801ddd7213 --- /dev/null +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr @@ -0,0 +1,89 @@ +use dep::aztec::{ + hash::compute_secret_hash, prelude::AztecAddress, + test::helpers::{cheatcodes, test_environment::TestEnvironment}, + protocol_types::storage::map::derive_storage_slot_in_map, + note::{note_getter::{MAX_NOTES_PER_PAGE, view_notes}, note_viewer_options::NoteViewerOptions}, + oracle::{unsafe_rand::unsafe_rand, storage::storage_read} +}; + +use crate::{types::{token_note::TokenNote, transparent_note::TransparentNote}, Token}; + +pub fn setup(with_account_contracts: bool) -> (&mut TestEnvironment, AztecAddress, AztecAddress, AztecAddress) { + // Setup env, generate keys + let mut env = TestEnvironment::new(); + let (owner, recipient) = if with_account_contracts { + let owner = env.create_account_contract(1); + let recipient = env.create_account_contract(2); + // Deploy canonical auth registry + let _auth_registry = env.deploy("@aztec/noir-contracts.js/AuthRegistry").without_initializer(); + (owner, recipient) + } else { + let owner = env.create_account(); + let recipient = env.create_account(); + (owner, recipient) + }; + + // Start the test in the account contract address + cheatcodes::set_contract_address(owner); + + // Deploy token contract + let initializer_call_interface = Token::interface().constructor( + owner, + "TestToken0000000000000000000000", + "TT00000000000000000000000000000", + 18 + ); + let token_contract = env.deploy("@aztec/noir-contracts.js/Token").with_public_initializer(initializer_call_interface); + let token_contract_address = token_contract.to_address(); + env.advance_block_by(1); + (&mut env, token_contract_address, owner, recipient) +} + +pub fn setup_and_mint(with_account_contracts: bool) -> (&mut TestEnvironment, AztecAddress, AztecAddress, AztecAddress, Field) { + // Setup + let (env, token_contract_address, owner, recipient) = setup(with_account_contracts); + let mint_amount = 10000; + // Mint some tokens + let secret = unsafe_rand(); + let secret_hash = compute_secret_hash(secret); + let mint_private_call_interface = Token::at(token_contract_address).mint_private(mint_amount, secret_hash); + env.call_public(mint_private_call_interface); + + let mint_public_call_interface = Token::at(token_contract_address).mint_public(owner, mint_amount); + env.call_public(mint_public_call_interface); + + // Time travel so we can read keys from the registry + env.advance_block_by(6); + + // Store a note in the cache so we can redeem it + env.store_note_in_cache( + &mut TransparentNote::new(mint_amount, secret_hash), + Token::storage().pending_shields.slot, + token_contract_address + ); + + // Redeem our shielded tokens + let redeem_shield_call_interface = Token::at(token_contract_address).redeem_shield(owner, mint_amount, secret); + env.call_private_void(redeem_shield_call_interface); + + (env, token_contract_address, owner, recipient, mint_amount) +} + +pub fn check_public_balance(token_contract_address: AztecAddress, address: AztecAddress, address_amount: Field) { + let current_contract_address = cheatcodes::get_contract_address(); + cheatcodes::set_contract_address(token_contract_address); + + let balances_slot = Token::storage().public_balances.slot; + let address_slot = derive_storage_slot_in_map(balances_slot, address); + let fields = storage_read(address_slot); + assert(U128::deserialize(fields).to_field() == address_amount, "Public balance is not correct"); + cheatcodes::set_contract_address(current_contract_address); +} + +pub fn check_private_balance(token_contract_address: AztecAddress, address: AztecAddress, address_amount: Field) { + let current_contract_address = cheatcodes::get_contract_address(); + cheatcodes::set_contract_address(token_contract_address); + let balance_of_private = Token::balance_of_private(address); + assert(balance_of_private == address_amount, "Private balance is not correct"); + cheatcodes::set_contract_address(current_contract_address); +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils.nr index 95561df1094..88624e25476 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils.nr @@ -13,7 +13,8 @@ pub fn conditional_assign(predicate: bool, lhs: Field, rhs: Field) -> Field { } pub fn arr_copy_slice(src: [T; N], mut dst: [T; M], offset: u32) -> [T; M] { - for i in 0..dst.len() { + let iterator_len = if N > M { M } else { N }; + for i in 0..iterator_len { dst[i] = src[i + offset]; } dst diff --git a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs index 1875ab0b252..8b763dfcc57 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs @@ -155,9 +155,17 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call name: \"{}\", args_hash, args: args_acc, - original: {} + original: {}, + is_static: {} }}", - args_hash, fn_selector, aztec_visibility, is_static, is_void, fn_name, original + args_hash, + fn_selector, + aztec_visibility, + is_static, + is_void, + fn_name, + original, + is_static_call ) } else { let args = format!( @@ -175,9 +183,17 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call name: \"{}\", args: args_acc, gas_opts: dep::aztec::context::gas::GasOpts::default(), - original: {} + original: {}, + is_static: {} }}", - args, fn_selector, aztec_visibility, is_static, is_void, fn_name, original + args, + fn_selector, + aztec_visibility, + is_static, + is_void, + fn_name, + original, + is_static_call ) }; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/import.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/import.rs index 343113836ed..9a0be775c30 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/import.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/import.rs @@ -88,15 +88,12 @@ pub fn resolve_import( import_directive: &ImportDirective, def_maps: &BTreeMap, ) -> Result { - let allow_contracts = - allow_referencing_contracts(def_maps, crate_id, import_directive.module_id); - let module_scope = import_directive.module_id; let NamespaceResolution { module_id: resolved_module, namespace: resolved_namespace, mut error, - } = resolve_path_to_ns(import_directive, crate_id, crate_id, def_maps, allow_contracts)?; + } = resolve_path_to_ns(import_directive, crate_id, crate_id, def_maps)?; let name = resolve_path_name(import_directive); @@ -129,20 +126,11 @@ pub fn resolve_import( }) } -fn allow_referencing_contracts( - def_maps: &BTreeMap, - krate: CrateId, - local_id: LocalModuleId, -) -> bool { - ModuleId { krate, local_id }.module(def_maps).is_contract -} - fn resolve_path_to_ns( import_directive: &ImportDirective, crate_id: CrateId, importing_crate: CrateId, def_maps: &BTreeMap, - allow_contracts: bool, ) -> NamespaceResolutionResult { let import_path = &import_directive.path.segments; let def_map = &def_maps[&crate_id]; @@ -150,21 +138,11 @@ fn resolve_path_to_ns( match import_directive.path.kind { crate::ast::PathKind::Crate => { // Resolve from the root of the crate - resolve_path_from_crate_root( - crate_id, - importing_crate, - import_path, - def_maps, - allow_contracts, - ) + resolve_path_from_crate_root(crate_id, importing_crate, import_path, def_maps) + } + crate::ast::PathKind::Dep => { + resolve_external_dep(def_map, import_directive, def_maps, importing_crate) } - crate::ast::PathKind::Dep => resolve_external_dep( - def_map, - import_directive, - def_maps, - allow_contracts, - importing_crate, - ), crate::ast::PathKind::Plain => { // Plain paths are only used to import children modules. It's possible to allow import of external deps, but maybe this distinction is better? // In Rust they can also point to external Dependencies, if no children can be found with the specified name @@ -174,7 +152,6 @@ fn resolve_path_to_ns( import_path, import_directive.module_id, def_maps, - allow_contracts, ) } } @@ -186,7 +163,6 @@ fn resolve_path_from_crate_root( import_path: &[Ident], def_maps: &BTreeMap, - allow_contracts: bool, ) -> NamespaceResolutionResult { resolve_name_in_module( crate_id, @@ -194,7 +170,6 @@ fn resolve_path_from_crate_root( import_path, def_maps[&crate_id].root, def_maps, - allow_contracts, ) } @@ -204,7 +179,6 @@ fn resolve_name_in_module( import_path: &[Ident], starting_mod: LocalModuleId, def_maps: &BTreeMap, - allow_contracts: bool, ) -> NamespaceResolutionResult { let def_map = &def_maps[&krate]; let mut current_mod_id = ModuleId { krate, local_id: starting_mod }; @@ -267,10 +241,6 @@ fn resolve_name_in_module( return Err(PathResolutionError::Unresolved(current_segment.clone())); } - // Check if it is a contract and we're calling from a non-contract context - if current_mod.is_contract && !allow_contracts { - return Err(PathResolutionError::ExternalContractUsed(current_segment.clone())); - } current_ns = found_ns; } @@ -288,7 +258,6 @@ fn resolve_external_dep( current_def_map: &CrateDefMap, directive: &ImportDirective, def_maps: &BTreeMap, - allow_contracts: bool, importing_crate: CrateId, ) -> NamespaceResolutionResult { // Use extern_prelude to get the dep @@ -316,7 +285,7 @@ fn resolve_external_dep( is_prelude: false, }; - resolve_path_to_ns(&dep_directive, dep_module.krate, importing_crate, def_maps, allow_contracts) + resolve_path_to_ns(&dep_directive, dep_module.krate, importing_crate, def_maps) } // Issue an error if the given private function is being called from a non-child module, or diff --git a/yarn-project/pxe/src/index.ts b/yarn-project/pxe/src/index.ts index 7c62b24d3ff..86b3f1205e7 100644 --- a/yarn-project/pxe/src/index.ts +++ b/yarn-project/pxe/src/index.ts @@ -11,3 +11,4 @@ export * from '@aztec/foundation/aztec-address'; export * from '@aztec/key-store'; export * from './database/index.js'; export { ContractDataOracle } from './contract_data_oracle/index.js'; +export { PrivateFunctionsTree } from './contract_data_oracle/private_functions_tree.js'; diff --git a/yarn-project/txe/package.json b/yarn-project/txe/package.json index 5658cd3b454..506521672f5 100644 --- a/yarn-project/txe/package.json +++ b/yarn-project/txe/package.json @@ -18,7 +18,8 @@ "formatting": "run -T prettier --check ./src && run -T eslint ./src", "formatting:fix": "run -T eslint --fix ./src && run -T prettier -w ./src", "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests", - "start": "DEBUG='aztec:*' && node ./dest/bin/index.js" + "dev": "DEBUG='aztec:*' && node ./dest/bin/index.js", + "start": "node ./dest/bin/index.js" }, "inherits": [ "../package.common.json" diff --git a/yarn-project/txe/src/bin/index.ts b/yarn-project/txe/src/bin/index.ts index 14934762159..f46cd541c95 100644 --- a/yarn-project/txe/src/bin/index.ts +++ b/yarn-project/txe/src/bin/index.ts @@ -1,4 +1,5 @@ #!/usr/bin/env -S node --no-warnings +import { Fr } from '@aztec/foundation/fields'; import { JsonRpcServer } from '@aztec/foundation/json-rpc/server'; import { type Logger, createDebugLogger } from '@aztec/foundation/log'; @@ -32,25 +33,20 @@ class TXEDispatcher { function: functionName, inputs, }: TXEForeignCallInput): Promise { - this.logger.debug( - `Calling ${functionName} with inputs: ${JSON.stringify(inputs, null, 2)} on session ${sessionId}`, - ); + this.logger.debug(`Calling ${functionName} on session ${sessionId}`); if (!TXESessions.has(sessionId) && functionName != 'reset') { - this.logger.debug(`Creating new session ${sessionId}`); + this.logger.info(`Creating new session ${sessionId}`); TXESessions.set(sessionId, await TXEService.init(logger)); } if (functionName === 'reset') { TXESessions.delete(sessionId) && - this.logger.debug(`Called reset on session ${sessionId}, yeeting it out of existence`); + this.logger.info(`Called reset on session ${sessionId}, yeeting it out of existence`); return toForeignCallResult([]); } else { const txeService = TXESessions.get(sessionId); const response = await (txeService as any)[functionName](...inputs); - this.logger.debug( - `${sessionId}:${functionName}(${JSON.stringify(inputs, null, 2)}) -> ${JSON.stringify(response, null, 2)}`, - ); return response; } } @@ -63,10 +59,11 @@ class TXEDispatcher { * @returns A running http server. */ export function startTXEHttpServer(dispatcher: TXEDispatcher, port: string | number): http.Server { - const txeServer = new JsonRpcServer(dispatcher, {}, {}, ['init']); + const txeServer = new JsonRpcServer(dispatcher, { Fr }, {}, ['init']); const app = txeServer.getApp(); const httpServer = http.createServer(app.callback()); + httpServer.timeout = 1e3 * 60 * 5; // 5 minutes httpServer.listen(port); return httpServer; diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index d3139ee1007..bdf5273f232 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -1,4 +1,5 @@ import { + AuthWitness, L1NotePayload, MerkleTreeId, Note, @@ -11,9 +12,11 @@ import { } from '@aztec/circuit-types'; import { type CircuitWitnessGenerationStats } from '@aztec/circuit-types/stats'; import { - type CompleteAddress, + CallContext, FunctionData, - type Header, + Gas, + GlobalVariables, + Header, type KeyValidationRequest, NULLIFIER_SUBTREE_HEIGHT, type NULLIFIER_TREE_HEIGHT, @@ -23,14 +26,15 @@ import { PrivateCallStackItem, PrivateCircuitPublicInputs, PrivateContextInputs, - type PublicCallRequest, + PublicCallRequest, PublicDataTreeLeaf, type PublicDataTreeLeafPreimage, + TxContext, computeContractClassId, deriveKeys, getContractClassFromArtifact, } from '@aztec/circuits.js'; -import { Aes128 } from '@aztec/circuits.js/barretenberg'; +import { Aes128, Schnorr } from '@aztec/circuits.js/barretenberg'; import { computePublicDataTreeLeafSlot, siloNoteHash, siloNullifier } from '@aztec/circuits.js/hash'; import { type ContractArtifact, type FunctionAbi, FunctionSelector, countArgumentsSize } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; @@ -40,13 +44,16 @@ import { Timer } from '@aztec/foundation/timer'; import { type KeyStore } from '@aztec/key-store'; import { ContractDataOracle } from '@aztec/pxe'; import { + ContractsDataSourcePublicDB, ExecutionError, type ExecutionNoteCache, type MessageLoadOracleInputs, type NoteData, Oracle, type PackedValuesCache, + PublicExecutor, type TypedOracle, + WorldStateDB, acvm, createSimulationError, extractCallStack, @@ -58,12 +65,15 @@ import { type ContractInstance, type ContractInstanceWithAddress } from '@aztec/ import { MerkleTreeSnapshotOperationsFacade, type MerkleTrees } from '@aztec/world-state'; import { type TXEDatabase } from '../util/txe_database.js'; +import { TXEPublicContractDataSource } from '../util/txe_public_contract_data_source.js'; +import { TXEPublicStateDB } from '../util/txe_public_state_db.js'; export class TXE implements TypedOracle { private blockNumber = 0; private sideEffectsCounter = 0; private contractAddress: AztecAddress; private msgSender: AztecAddress; + private functionSelector = FunctionSelector.fromField(new Fr(0)); private contractDataOracle: ContractDataOracle; @@ -85,22 +95,14 @@ export class TXE implements TypedOracle { // Utils - getChainId(): Promise { + getChainId() { return Promise.resolve(this.chainId); } - getVersion(): Promise { + getVersion() { return Promise.resolve(this.version); } - setChainId(chainId: Fr) { - this.chainId = chainId; - } - - setVersion(version: Fr) { - this.version = version; - } - getMsgSender() { return this.msgSender; } @@ -109,6 +111,10 @@ export class TXE implements TypedOracle { this.msgSender = msgSender; } + setFunctionSelector(functionSelector: FunctionSelector) { + this.functionSelector = functionSelector; + } + getSideEffectsCounter() { return this.sideEffectsCounter; } @@ -129,6 +135,10 @@ export class TXE implements TypedOracle { return this.trees; } + getContractDataOracle() { + return this.contractDataOracle; + } + getTXEDatabase() { return this.txeDatabase; } @@ -146,16 +156,24 @@ export class TXE implements TypedOracle { await this.txeDatabase.addContractArtifact(computeContractClassId(contractClass), artifact); } - async getPrivateContextInputs(blockNumber: number, sideEffectsCounter = this.sideEffectsCounter) { + async getPrivateContextInputs( + blockNumber: number, + sideEffectsCounter = this.sideEffectsCounter, + isStaticCall = false, + isDelegateCall = false, + ) { const trees = this.getTrees(); - const stateReference = await trees.getStateReference(true); + const stateReference = await trees.getStateReference(false); const inputs = PrivateContextInputs.empty(); inputs.historicalHeader.globalVariables.blockNumber = new Fr(blockNumber); inputs.historicalHeader.state = stateReference; inputs.callContext.msgSender = this.msgSender; inputs.callContext.storageContractAddress = this.contractAddress; inputs.callContext.sideEffectCounter = sideEffectsCounter; + inputs.callContext.isStaticCall = isStaticCall; + inputs.callContext.isDelegateCall = isDelegateCall; inputs.startSideEffectCounter = sideEffectsCounter; + inputs.callContext.functionSelector = this.functionSelector; return inputs; } @@ -196,13 +214,35 @@ export class TXE implements TypedOracle { return deriveKeys(secret); } + async addAuthWitness(address: AztecAddress, messageHash: Fr) { + const account = this.txeDatabase.getAccount(address); + const privateKey = await this.keyStore.getMasterSecretKey(account.publicKeys.masterIncomingViewingPublicKey); + const schnorr = new Schnorr(); + const signature = schnorr.constructSignature(messageHash.toBuffer(), privateKey).toBuffer(); + const authWitness = new AuthWitness(messageHash, [...signature]); + return this.txeDatabase.addAuthWitness(authWitness.requestHash, authWitness.witness); + } + + async addNullifiers(contractAddress: AztecAddress, nullifiers: Fr[]) { + const db = this.trees.asLatest(); + const siloedNullifiers = nullifiers.map(nullifier => siloNullifier(contractAddress, nullifier).toBuffer()); + + await db.batchInsert(MerkleTreeId.NULLIFIER_TREE, siloedNullifiers, NULLIFIER_SUBTREE_HEIGHT); + } + + async addNoteHashes(contractAddress: AztecAddress, innerNoteHashes: Fr[]) { + const db = this.trees.asLatest(); + const siloedNoteHashes = innerNoteHashes.map(innerNoteHash => siloNoteHash(contractAddress, innerNoteHash)); + await db.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, siloedNoteHashes); + } + // TypedOracle - getBlockNumber(): Promise { + getBlockNumber() { return Promise.resolve(this.blockNumber); } - getContractAddress(): Promise { + getContractAddress() { return Promise.resolve(this.contractAddress); } @@ -210,15 +250,15 @@ export class TXE implements TypedOracle { return Fr.random(); } - packArgumentsArray(args: Fr[]): Promise { + packArgumentsArray(args: Fr[]) { return Promise.resolve(this.packedValuesCache.pack(args)); } - packReturns(returns: Fr[]): Promise { + packReturns(returns: Fr[]) { return Promise.resolve(this.packedValuesCache.pack(returns)); } - unpackReturns(returnsHash: Fr): Promise { + unpackReturns(returnsHash: Fr) { return Promise.resolve(this.packedValuesCache.unpack(returnsHash)); } @@ -227,11 +267,11 @@ export class TXE implements TypedOracle { } async getContractInstance(address: AztecAddress): Promise { - const contractInstance = await this.txeDatabase.getContractInstance(address); + const contractInstance = await this.contractDataOracle.getContractInstance(address); if (!contractInstance) { throw new Error(`Contract instance not found for address ${address}`); } - return Promise.resolve(contractInstance); + return contractInstance; } getMembershipWitness(_blockNumber: number, _treeId: MerkleTreeId, _leafValue: Fr): Promise { @@ -298,12 +338,12 @@ export class TXE implements TypedOracle { throw new Error('Method not implemented.'); } - getCompleteAddress(account: AztecAddress): Promise { + getCompleteAddress(account: AztecAddress) { return Promise.resolve(this.txeDatabase.getAccount(account)); } - getAuthWitness(_messageHash: Fr): Promise { - throw new Error('Method not implemented.'); + getAuthWitness(messageHash: Fr) { + return this.txeDatabase.getAuthWitness(messageHash); } popCapsule(): Promise { @@ -352,7 +392,7 @@ export class TXE implements TypedOracle { return Promise.resolve(notes); } - async notifyCreatedNote(storageSlot: Fr, noteTypeId: Fr, noteItems: Fr[], innerNoteHash: Fr, counter: number) { + notifyCreatedNote(storageSlot: Fr, noteTypeId: Fr, noteItems: Fr[], innerNoteHash: Fr, counter: number) { const note = new Note(noteItems); this.noteCache.addNewNote( { @@ -365,16 +405,11 @@ export class TXE implements TypedOracle { }, counter, ); - const db = this.trees.asLatest(); - const noteHash = siloNoteHash(this.contractAddress, innerNoteHash); - await db.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, [noteHash]); + return Promise.resolve(); } - async notifyNullifiedNote(innerNullifier: Fr, innerNoteHash: Fr, _counter: number) { + notifyNullifiedNote(innerNullifier: Fr, innerNoteHash: Fr, _counter: number) { this.noteCache.nullifyNote(this.contractAddress, innerNullifier, innerNoteHash); - const db = this.trees.asLatest(); - const siloedNullifier = siloNullifier(this.contractAddress, innerNullifier); - await db.batchInsert(MerkleTreeId.NULLIFIER_TREE, [siloedNullifier.toBuffer()], NULLIFIER_SUBTREE_HEIGHT); return Promise.resolve(); } @@ -461,7 +496,7 @@ export class TXE implements TypedOracle { } emitUnencryptedLog(_log: UnencryptedL2Log, _counter: number): void { - throw new Error('Method not implemented.'); + return; } emitContractClassUnencryptedLog(_log: UnencryptedL2Log, _counter: number): Fr { @@ -473,69 +508,97 @@ export class TXE implements TypedOracle { functionSelector: FunctionSelector, argsHash: Fr, sideEffectCounter: number, - _isStaticCall: boolean, - _isDelegateCall: boolean, + isStaticCall: boolean, + isDelegateCall: boolean, ): Promise { - this.logger.debug( - `Calling private function ${targetContractAddress}:${functionSelector} from ${this.contractAddress}`, + this.logger.verbose( + `Executing external function ${targetContractAddress}:${functionSelector}(${await this.getDebugFunctionName( + targetContractAddress, + functionSelector, + )}) isStaticCall=${isStaticCall} isDelegateCall=${isDelegateCall}`, ); + // Store and modify env const currentContractAddress = AztecAddress.fromField(this.contractAddress); const currentMessageSender = AztecAddress.fromField(this.msgSender); + const currentFunctionSelector = FunctionSelector.fromField(this.functionSelector.toField()); this.setMsgSender(this.contractAddress); this.setContractAddress(targetContractAddress); + this.setFunctionSelector(functionSelector); const artifact = await this.contractDataOracle.getFunctionArtifact(targetContractAddress, functionSelector); const acir = artifact.bytecode; - const initialWitness = await this.getInitialWitness(artifact, argsHash, sideEffectCounter); + const initialWitness = await this.getInitialWitness( + artifact, + argsHash, + sideEffectCounter, + isStaticCall, + isDelegateCall, + ); const acvmCallback = new Oracle(this); const timer = new Timer(); - const acirExecutionResult = await acvm(acir, initialWitness, acvmCallback).catch((err: Error) => { - const execError = new ExecutionError( - err.message, - { - contractAddress: targetContractAddress, - functionSelector, - }, - extractCallStack(err, artifact.debug), - { cause: err }, + try { + const acirExecutionResult = await acvm(acir, initialWitness, acvmCallback).catch((err: Error) => { + const execError = new ExecutionError( + err.message, + { + contractAddress: targetContractAddress, + functionSelector, + }, + extractCallStack(err, artifact.debug), + { cause: err }, + ); + this.logger.debug(`Error executing private function ${targetContractAddress}:${functionSelector}`); + throw createSimulationError(execError); + }); + const duration = timer.ms(); + const returnWitness = witnessMapToFields(acirExecutionResult.returnWitness); + const publicInputs = PrivateCircuitPublicInputs.fromFields(returnWitness); + + const initialWitnessSize = witnessMapToFields(initialWitness).length * Fr.SIZE_IN_BYTES; + this.logger.debug(`Ran external function ${targetContractAddress.toString()}:${functionSelector}`, { + circuitName: 'app-circuit', + duration, + eventName: 'circuit-witness-generation', + inputSize: initialWitnessSize, + outputSize: publicInputs.toBuffer().length, + appCircuitName: 'noname', + } satisfies CircuitWitnessGenerationStats); + + const callStackItem = new PrivateCallStackItem( + targetContractAddress, + new FunctionData(functionSelector, true), + publicInputs, ); - this.logger.debug( - `Error executing private function ${targetContractAddress}:${functionSelector}\n${createSimulationError( - execError, - )}`, + // Apply side effects + this.sideEffectsCounter = publicInputs.endSideEffectCounter.toNumber(); + + await this.addNullifiers( + targetContractAddress, + publicInputs.newNullifiers.filter(nullifier => !nullifier.isEmpty()).map(nullifier => nullifier.value), + ); + + await this.addNoteHashes( + targetContractAddress, + publicInputs.newNoteHashes.filter(noteHash => !noteHash.isEmpty()).map(noteHash => noteHash.value), ); - throw execError; - }); - const duration = timer.ms(); - const returnWitness = witnessMapToFields(acirExecutionResult.returnWitness); - const publicInputs = PrivateCircuitPublicInputs.fromFields(returnWitness); - - const initialWitnessSize = witnessMapToFields(initialWitness).length * Fr.SIZE_IN_BYTES; - this.logger.debug(`Ran external function ${targetContractAddress.toString()}:${functionSelector}`, { - circuitName: 'app-circuit', - duration, - eventName: 'circuit-witness-generation', - inputSize: initialWitnessSize, - outputSize: publicInputs.toBuffer().length, - appCircuitName: 'noname', - } satisfies CircuitWitnessGenerationStats); - - const callStackItem = new PrivateCallStackItem( - targetContractAddress, - new FunctionData(functionSelector, true), - publicInputs, - ); - // Apply side effects - this.sideEffectsCounter += publicInputs.endSideEffectCounter.toNumber(); - this.setContractAddress(currentContractAddress); - this.setMsgSender(currentMessageSender); - return callStackItem; + return callStackItem; + } finally { + this.setContractAddress(currentContractAddress); + this.setMsgSender(currentMessageSender); + this.setFunctionSelector(currentFunctionSelector); + } } - async getInitialWitness(abi: FunctionAbi, argsHash: Fr, sideEffectCounter: number) { + async getInitialWitness( + abi: FunctionAbi, + argsHash: Fr, + sideEffectCounter: number, + isStaticCall: boolean, + isDelegateCall: boolean, + ) { const argumentsSize = countArgumentsSize(abi); const args = this.packedValuesCache.unpack(argsHash); @@ -544,33 +607,220 @@ export class TXE implements TypedOracle { throw new Error('Invalid arguments size'); } - const privateContextInputs = await this.getPrivateContextInputs(this.blockNumber - 1, sideEffectCounter); + const privateContextInputs = await this.getPrivateContextInputs( + this.blockNumber - 1, + sideEffectCounter, + isStaticCall, + isDelegateCall, + ); const fields = [...privateContextInputs.toFields(), ...args]; return toACVMWitness(0, fields); } - callPublicFunction( - _targetContractAddress: AztecAddress, - _functionSelector: FunctionSelector, - _argsHash: Fr, - _sideEffectCounter: number, - _isStaticCall: boolean, - _isDelegateCall: boolean, + public async getDebugFunctionName(address: AztecAddress, selector: FunctionSelector): Promise { + const instance = await this.contractDataOracle.getContractInstance(address); + if (!instance) { + return undefined; + } + const artifact = await this.contractDataOracle.getContractArtifact(instance!.contractClassId); + if (!artifact) { + return undefined; + } + + const f = artifact.functions.find(f => + FunctionSelector.fromNameAndParameters(f.name, f.parameters).equals(selector), + ); + if (!f) { + return undefined; + } + + return `${artifact.name}:${f.name}`; + } + + async executePublicFunction( + targetContractAddress: AztecAddress, + functionSelector: FunctionSelector, + args: Fr[], + callContext: CallContext, + ) { + const header = Header.empty(); + header.state = await this.trees.getStateReference(true); + header.globalVariables.blockNumber = new Fr(await this.getBlockNumber()); + header.state.partial.nullifierTree.root = Fr.fromBuffer( + (await this.trees.getTreeInfo(MerkleTreeId.NULLIFIER_TREE, true)).root, + ); + header.state.partial.noteHashTree.root = Fr.fromBuffer( + (await this.trees.getTreeInfo(MerkleTreeId.NOTE_HASH_TREE, true)).root, + ); + header.state.partial.publicDataTree.root = Fr.fromBuffer( + (await this.trees.getTreeInfo(MerkleTreeId.PUBLIC_DATA_TREE, true)).root, + ); + header.state.l1ToL2MessageTree.root = Fr.fromBuffer( + (await this.trees.getTreeInfo(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, true)).root, + ); + const executor = new PublicExecutor( + new TXEPublicStateDB(this), + new ContractsDataSourcePublicDB(new TXEPublicContractDataSource(this)), + new WorldStateDB(this.trees.asLatest()), + header, + ); + const execution = { + contractAddress: targetContractAddress, + functionSelector, + args, + callContext, + }; + + return executor.simulate( + execution, + GlobalVariables.empty(), + Gas.test(), + TxContext.empty(), + /* pendingNullifiers */ [], + /* transactionFee */ Fr.ZERO, + callContext.sideEffectCounter, + ); + } + + async avmOpcodeCall( + targetContractAddress: AztecAddress, + functionSelector: FunctionSelector, + args: Fr[], + isStaticCall: boolean, + isDelegateCall: boolean, + ) { + // Store and modify env + const currentContractAddress = AztecAddress.fromField(this.contractAddress); + const currentMessageSender = AztecAddress.fromField(this.msgSender); + const currentFunctionSelector = FunctionSelector.fromField(this.functionSelector.toField()); + this.setMsgSender(this.contractAddress); + this.setContractAddress(targetContractAddress); + this.setFunctionSelector(functionSelector); + + const callContext = CallContext.empty(); + callContext.msgSender = this.msgSender; + callContext.functionSelector = this.functionSelector; + callContext.sideEffectCounter = this.sideEffectsCounter; + callContext.storageContractAddress = targetContractAddress; + callContext.isStaticCall = isStaticCall; + callContext.isDelegateCall = isDelegateCall; + + const executionResult = await this.executePublicFunction( + targetContractAddress, + functionSelector, + args, + callContext, + ); + + // Apply side effects + if (!executionResult.reverted) { + this.sideEffectsCounter += executionResult.endSideEffectCounter.toNumber(); + } + this.setContractAddress(currentContractAddress); + this.setMsgSender(currentMessageSender); + this.setFunctionSelector(currentFunctionSelector); + + return executionResult; + } + + async callPublicFunction( + targetContractAddress: AztecAddress, + functionSelector: FunctionSelector, + argsHash: Fr, + sideEffectCounter: number, + isStaticCall: boolean, + isDelegateCall: boolean, ): Promise { - throw new Error('Method not implemented.'); + // Store and modify env + const currentContractAddress = AztecAddress.fromField(this.contractAddress); + const currentMessageSender = AztecAddress.fromField(this.msgSender); + const currentFunctionSelector = FunctionSelector.fromField(this.functionSelector.toField()); + this.setMsgSender(this.contractAddress); + this.setContractAddress(targetContractAddress); + this.setFunctionSelector(functionSelector); + + const callContext = CallContext.empty(); + callContext.msgSender = this.msgSender; + callContext.functionSelector = this.functionSelector; + callContext.sideEffectCounter = sideEffectCounter; + callContext.storageContractAddress = targetContractAddress; + callContext.isStaticCall = isStaticCall; + callContext.isDelegateCall = isDelegateCall; + + const args = this.packedValuesCache.unpack(argsHash); + + const executionResult = await this.executePublicFunction( + targetContractAddress, + functionSelector, + args, + callContext, + ); + + // Apply side effects + this.sideEffectsCounter = executionResult.endSideEffectCounter.toNumber(); + this.setContractAddress(currentContractAddress); + this.setMsgSender(currentMessageSender); + this.setFunctionSelector(currentFunctionSelector); + + return executionResult.returnValues; } - enqueuePublicFunctionCall( - _targetContractAddress: AztecAddress, - _functionSelector: FunctionSelector, - _argsHash: Fr, - _sideEffectCounter: number, - _isStaticCall: boolean, - _isDelegateCall: boolean, + async enqueuePublicFunctionCall( + targetContractAddress: AztecAddress, + functionSelector: FunctionSelector, + argsHash: Fr, + sideEffectCounter: number, + isStaticCall: boolean, + isDelegateCall: boolean, ): Promise { - throw new Error('Method not implemented.'); + // Store and modify env + const currentContractAddress = AztecAddress.fromField(this.contractAddress); + const currentMessageSender = AztecAddress.fromField(this.msgSender); + const currentFunctionSelector = FunctionSelector.fromField(this.functionSelector.toField()); + this.setMsgSender(this.contractAddress); + this.setContractAddress(targetContractAddress); + this.setFunctionSelector(functionSelector); + + const callContext = CallContext.empty(); + callContext.msgSender = this.msgSender; + callContext.functionSelector = this.functionSelector; + callContext.sideEffectCounter = sideEffectCounter; + callContext.storageContractAddress = targetContractAddress; + callContext.isStaticCall = isStaticCall; + callContext.isDelegateCall = isDelegateCall; + + const args = this.packedValuesCache.unpack(argsHash); + + const executionResult = await this.executePublicFunction( + targetContractAddress, + functionSelector, + args, + callContext, + ); + + // Apply side effects + this.sideEffectsCounter += executionResult.endSideEffectCounter.toNumber(); + this.setContractAddress(currentContractAddress); + this.setMsgSender(currentMessageSender); + this.setFunctionSelector(currentFunctionSelector); + + const parentCallContext = CallContext.empty(); + parentCallContext.msgSender = currentMessageSender; + parentCallContext.functionSelector = currentFunctionSelector; + parentCallContext.sideEffectCounter = sideEffectCounter; + parentCallContext.storageContractAddress = currentContractAddress; + parentCallContext.isStaticCall = isStaticCall; + parentCallContext.isDelegateCall = isDelegateCall; + + return PublicCallRequest.from({ + parentCallContext, + contractAddress: targetContractAddress, + functionSelector, + callContext, + args, + }); } setPublicTeardownFunctionCall( diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index 6b53c0de1eb..aef0d8e3d49 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -13,7 +13,6 @@ import { computePublicDataTreeLeafSlot } from '@aztec/circuits.js/hash'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { type Logger } from '@aztec/foundation/log'; import { KeyStore } from '@aztec/key-store'; -import { type AztecKVStore } from '@aztec/kv-store'; import { openTmpStore } from '@aztec/kv-store/utils'; import { ExecutionNoteCache, PackedValuesCache, type TypedOracle } from '@aztec/simulator'; import { MerkleTrees } from '@aztec/world-state'; @@ -28,10 +27,11 @@ import { toForeignCallResult, toSingle, } from '../util/encoding.js'; +import { ExpectedFailureError } from '../util/expected_failure_error.js'; import { TXEDatabase } from '../util/txe_database.js'; export class TXEService { - constructor(private logger: Logger, private typedOracle: TypedOracle, private store: AztecKVStore) {} + constructor(private logger: Logger, private typedOracle: TypedOracle) {} static async init(logger: Logger) { const store = openTmpStore(true); @@ -42,8 +42,8 @@ export class TXEService { const txeDatabase = new TXEDatabase(store); logger.info(`TXE service initialized`); const txe = new TXE(logger, trees, packedValuesCache, noteCache, keyStore, txeDatabase); - const service = new TXEService(logger, txe, store); - await service.timeTravel(toSingle(new Fr(1n))); + const service = new TXEService(logger, txe); + await service.advanceBlocksBy(toSingle(new Fr(1n))); return service; } @@ -59,31 +59,20 @@ export class TXEService { return toForeignCallResult(inputs.toFields().map(toSingle)); } - async timeTravel(blocks: ForeignCallSingle) { + async advanceBlocksBy(blocks: ForeignCallSingle) { const nBlocks = fromSingle(blocks).toNumber(); - this.logger.info(`time traveling ${nBlocks} blocks`); + this.logger.debug(`time traveling ${nBlocks} blocks`); const trees = (this.typedOracle as TXE).getTrees(); + const header = Header.empty(); + const l2Block = L2Block.empty(); + header.state = await trees.getStateReference(true); + const blockNumber = await this.typedOracle.getBlockNumber(); + header.globalVariables.blockNumber = new Fr(blockNumber); + l2Block.archive.root = Fr.fromBuffer((await trees.getTreeInfo(MerkleTreeId.ARCHIVE, true)).root); + l2Block.header = header; for (let i = 0; i < nBlocks; i++) { - const header = Header.empty(); - const l2Block = L2Block.empty(); - header.state = await trees.getStateReference(true); const blockNumber = await this.typedOracle.getBlockNumber(); - header.globalVariables.blockNumber = new Fr(blockNumber); - header.state.partial.nullifierTree.root = Fr.fromBuffer( - (await trees.getTreeInfo(MerkleTreeId.NULLIFIER_TREE, true)).root, - ); - header.state.partial.noteHashTree.root = Fr.fromBuffer( - (await trees.getTreeInfo(MerkleTreeId.NOTE_HASH_TREE, true)).root, - ); - header.state.partial.publicDataTree.root = Fr.fromBuffer( - (await trees.getTreeInfo(MerkleTreeId.PUBLIC_DATA_TREE, true)).root, - ); - header.state.l1ToL2MessageTree.root = Fr.fromBuffer( - (await trees.getTreeInfo(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, true)).root, - ); - l2Block.archive.root = Fr.fromBuffer((await trees.getTreeInfo(MerkleTreeId.ARCHIVE, true)).root); - l2Block.header = header; await trees.handleL2BlockAndMessages(l2Block, []); (this.typedOracle as TXE).setBlockNumber(blockNumber + 1); } @@ -115,7 +104,10 @@ export class TXEService { .map(char => String.fromCharCode(char.toNumber())) .join(''); const decodedArgs = fromArray(args); - this.logger.debug(`Deploy ${pathStr} with ${initializerStr} and ${decodedArgs}`); + const publicKeysHashFr = fromSingle(publicKeysHash); + this.logger.debug( + `Deploy ${pathStr} with initializer ${initializerStr}(${decodedArgs}) and public keys hash ${publicKeysHashFr}`, + ); const contractModule = await import(pathStr); // Hacky way of getting the class, the name of the Artifact is always longer const contractClass = contractModule[Object.keys(contractModule).sort((a, b) => a.length - b.length)[0]]; @@ -123,7 +115,7 @@ export class TXEService { constructorArgs: decodedArgs, skipArgsDecoding: true, salt: Fr.ONE, - publicKeysHash: fromSingle(publicKeysHash), + publicKeysHash: publicKeysHashFr, constructorArtifact: initializerStr ? initializerStr : undefined, deployer: AztecAddress.ZERO, }); @@ -131,7 +123,15 @@ export class TXEService { this.logger.debug(`Deployed ${contractClass.artifact.name} at ${instance.address}`); await (this.typedOracle as TXE).addContractInstance(instance); await (this.typedOracle as TXE).addContractArtifact(contractClass.artifact); - return toForeignCallResult([toSingle(instance.address)]); + return toForeignCallResult([ + toArray([ + instance.salt, + instance.deployer, + instance.contractClassId, + instance.initializationHash, + instance.publicKeysHash, + ]), + ]); } async directStorageWrite( @@ -175,6 +175,7 @@ export class TXEService { const completeAddress = await keyStore.addAccount(fromSingle(secret), fromSingle(partialAddress)); const accountStore = (this.typedOracle as TXE).getTXEDatabase(); await accountStore.setAccount(completeAddress.address, completeAddress); + this.logger.debug(`Created account ${completeAddress.address}`); return toForeignCallResult([ toSingle(completeAddress.address), ...completeAddress.publicKeys.toFields().map(toSingle), @@ -196,6 +197,59 @@ export class TXEService { return toForeignCallResult([toSingle(new Fr(counter))]); } + async addAuthWitness(address: ForeignCallSingle, messageHash: ForeignCallSingle) { + await (this.typedOracle as TXE).addAuthWitness(fromSingle(address), fromSingle(messageHash)); + return toForeignCallResult([]); + } + + async assertPublicCallFails( + address: ForeignCallSingle, + functionSelector: ForeignCallSingle, + _length: ForeignCallSingle, + args: ForeignCallArray, + ) { + const parsedAddress = fromSingle(address); + const parsedSelector = FunctionSelector.fromField(fromSingle(functionSelector)); + const result = await (this.typedOracle as TXE).avmOpcodeCall( + parsedAddress, + parsedSelector, + fromArray(args), + false, + false, + ); + if (!result.reverted) { + throw new ExpectedFailureError('Public call did not revert'); + } + + return toForeignCallResult([]); + } + + async assertPrivateCallFails( + targetContractAddress: ForeignCallSingle, + functionSelector: ForeignCallSingle, + argsHash: ForeignCallSingle, + sideEffectCounter: ForeignCallSingle, + isStaticCall: ForeignCallSingle, + isDelegateCall: ForeignCallSingle, + ) { + try { + await this.typedOracle.callPrivateFunction( + fromSingle(targetContractAddress), + FunctionSelector.fromField(fromSingle(functionSelector)), + fromSingle(argsHash), + fromSingle(sideEffectCounter).toNumber(), + fromSingle(isStaticCall).toBool(), + fromSingle(isDelegateCall).toBool(), + ); + throw new ExpectedFailureError('Private call did not fail'); + } catch (e) { + if (e instanceof ExpectedFailureError) { + throw e; + } + } + return toForeignCallResult([]); + } + // PXE oracles getRandomField() { @@ -433,10 +487,27 @@ export class TXEService { return toForeignCallResult([toSingle(new Fr(exists))]); } + async avmOpcodeCall( + _gas: ForeignCallArray, + address: ForeignCallSingle, + _length: ForeignCallSingle, + args: ForeignCallArray, + functionSelector: ForeignCallSingle, + ) { + const result = await (this.typedOracle as TXE).avmOpcodeCall( + fromSingle(address), + FunctionSelector.fromField(fromSingle(functionSelector)), + fromArray(args), + false, + false, + ); + + return toForeignCallResult([toArray(result.returnValues), toSingle(new Fr(1))]); + } + async getPublicKeysAndPartialAddress(address: ForeignCallSingle) { const parsedAddress = AztecAddress.fromField(fromSingle(address)); const { publicKeys, partialAddress } = await this.typedOracle.getCompleteAddress(parsedAddress); - return toForeignCallResult([toArray([...publicKeys.toFields(), partialAddress])]); } @@ -519,4 +590,56 @@ export class TXEService { } return toForeignCallResult([toArray(witness.toFields())]); } + + async getAuthWitness(messageHash: ForeignCallSingle) { + const parsedMessageHash = fromSingle(messageHash); + const authWitness = await this.typedOracle.getAuthWitness(parsedMessageHash); + if (!authWitness) { + throw new Error(`Auth witness not found for message hash ${parsedMessageHash}.`); + } + return toForeignCallResult([toArray(authWitness)]); + } + + async enqueuePublicFunctionCall( + targetContractAddress: ForeignCallSingle, + functionSelector: ForeignCallSingle, + argsHash: ForeignCallSingle, + sideEffectCounter: ForeignCallSingle, + isStaticCall: ForeignCallSingle, + isDelegateCall: ForeignCallSingle, + ) { + const publicCallRequest = await this.typedOracle.enqueuePublicFunctionCall( + fromSingle(targetContractAddress), + FunctionSelector.fromField(fromSingle(functionSelector)), + fromSingle(argsHash), + fromSingle(sideEffectCounter).toNumber(), + fromSingle(isStaticCall).toBool(), + fromSingle(isDelegateCall).toBool(), + ); + const fields = [ + publicCallRequest.contractAddress.toField(), + publicCallRequest.functionSelector.toField(), + ...publicCallRequest.callContext.toFields(), + publicCallRequest.getArgsHash(), + ]; + return toForeignCallResult([toArray(fields)]); + } + + async getChainId() { + return toForeignCallResult([toSingle(await this.typedOracle.getChainId())]); + } + + async getVersion() { + return toForeignCallResult([toSingle(await this.typedOracle.getVersion())]); + } + + async addNullifiers(contractAddress: ForeignCallSingle, _length: ForeignCallSingle, nullifiers: ForeignCallArray) { + await (this.typedOracle as TXE).addNullifiers(fromSingle(contractAddress), fromArray(nullifiers)); + return toForeignCallResult([]); + } + + async addNoteHashes(contractAddress: ForeignCallSingle, _length: ForeignCallSingle, noteHashes: ForeignCallArray) { + await (this.typedOracle as TXE).addNoteHashes(fromSingle(contractAddress), fromArray(noteHashes)); + return toForeignCallResult([]); + } } diff --git a/yarn-project/txe/src/util/expected_failure_error.ts b/yarn-project/txe/src/util/expected_failure_error.ts new file mode 100644 index 00000000000..8f97a3ae2bf --- /dev/null +++ b/yarn-project/txe/src/util/expected_failure_error.ts @@ -0,0 +1,5 @@ +export class ExpectedFailureError extends Error { + constructor(message: string) { + super(message); + } +} diff --git a/yarn-project/txe/src/util/txe_public_contract_data_source.ts b/yarn-project/txe/src/util/txe_public_contract_data_source.ts new file mode 100644 index 00000000000..64f410f9595 --- /dev/null +++ b/yarn-project/txe/src/util/txe_public_contract_data_source.ts @@ -0,0 +1,63 @@ +import { type AztecAddress, Fr, type FunctionSelector, unpackBytecode } from '@aztec/circuits.js'; +import { type ContractArtifact } from '@aztec/foundation/abi'; +import { PrivateFunctionsTree } from '@aztec/pxe'; +import { + type ContractClassPublic, + type ContractDataSource, + type ContractInstanceWithAddress, + type PublicFunction, +} from '@aztec/types/contracts'; + +import { type TXE } from '../oracle/txe_oracle.js'; + +export class TXEPublicContractDataSource implements ContractDataSource { + constructor(private txeOracle: TXE) {} + + async getPublicFunction(address: AztecAddress, selector: FunctionSelector): Promise { + const bytecode = await this.txeOracle.getContractDataOracle().getBytecode(address, selector); + if (!bytecode) { + return undefined; + } + return { bytecode, selector }; + } + + getBlockNumber(): Promise { + return this.txeOracle.getBlockNumber(); + } + + async getContractClass(id: Fr): Promise { + const contractClass = await this.txeOracle.getContractDataOracle().getContractClass(id); + const artifact = await this.txeOracle.getContractDataOracle().getContractArtifact(id); + const tree = new PrivateFunctionsTree(artifact); + const privateFunctionsRoot = tree.getFunctionTreeRoot(); + + return { + id, + artifactHash: contractClass!.artifactHash, + packedBytecode: contractClass!.packedBytecode, + publicFunctions: unpackBytecode(contractClass!.packedBytecode), + privateFunctionsRoot: new Fr(privateFunctionsRoot!.root), + version: contractClass!.version, + privateFunctions: [], + unconstrainedFunctions: [], + }; + } + + async getContract(address: AztecAddress): Promise { + const instance = await this.txeOracle.getContractDataOracle().getContractInstance(address); + return { ...instance, address }; + } + + getContractClassIds(): Promise { + throw new Error('Method not implemented.'); + } + + async getContractArtifact(address: AztecAddress): Promise { + const instance = await this.txeOracle.getContractDataOracle().getContractInstance(address); + return this.txeOracle.getContractDataOracle().getContractArtifact(instance.contractClassId); + } + + addContractArtifact(address: AztecAddress, contract: ContractArtifact): Promise { + return this.txeOracle.addContractArtifact(contract); + } +} diff --git a/yarn-project/txe/src/util/txe_public_state_db.ts b/yarn-project/txe/src/util/txe_public_state_db.ts new file mode 100644 index 00000000000..62bdbaf7e5b --- /dev/null +++ b/yarn-project/txe/src/util/txe_public_state_db.ts @@ -0,0 +1,57 @@ +import { MerkleTreeId } from '@aztec/circuit-types'; +import { + type AztecAddress, + Fr, + PUBLIC_DATA_SUBTREE_HEIGHT, + PublicDataTreeLeaf, + type PublicDataTreeLeafPreimage, +} from '@aztec/circuits.js'; +import { computePublicDataTreeLeafSlot } from '@aztec/circuits.js/hash'; +import { type PublicStateDB } from '@aztec/simulator'; + +import { type TXE } from '../oracle/txe_oracle.js'; + +export class TXEPublicStateDB implements PublicStateDB { + constructor(private txeOracle: TXE) {} + + async storageRead(contract: AztecAddress, slot: Fr): Promise { + const db = this.txeOracle.getTrees().asLatest(); + const leafSlot = computePublicDataTreeLeafSlot(contract, slot).toBigInt(); + + const lowLeafResult = await db.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot); + + let value = Fr.ZERO; + if (lowLeafResult && lowLeafResult.alreadyPresent) { + const preimage = (await db.getLeafPreimage( + MerkleTreeId.PUBLIC_DATA_TREE, + lowLeafResult.index, + )) as PublicDataTreeLeafPreimage; + value = preimage.value; + } + return value; + } + + async storageWrite(contract: AztecAddress, slot: Fr, newValue: Fr): Promise { + const db = this.txeOracle.getTrees().asLatest(); + + await db.batchInsert( + MerkleTreeId.PUBLIC_DATA_TREE, + [new PublicDataTreeLeaf(computePublicDataTreeLeafSlot(contract, slot), newValue).toBuffer()], + PUBLIC_DATA_SUBTREE_HEIGHT, + ); + return newValue.toBigInt(); + } + + checkpoint(): Promise { + return Promise.resolve(); + } + rollbackToCheckpoint(): Promise { + throw new Error('Cannot rollback'); + } + commit(): Promise { + return Promise.resolve(); + } + rollbackToCommit(): Promise { + throw new Error('Cannot rollback'); + } +} From 34eb5a01d34e5ff5d1414fff53ca0623d83bde5d Mon Sep 17 00:00:00 2001 From: maramihali Date: Tue, 25 Jun 2024 15:03:35 +0100 Subject: [PATCH 3/9] feat: Shplonk revival in ECCVM (#7164) Refactor Shplonk to have better encapsulated functionality on proving side and integrate it in ECCVM. Until now, we performed two IPA rounds in ECCVM, the second one necessary to ensure the consistency of transcript polynomials. We introduce Shplonk to batch the two opening claims and perform one round of IPA which reduces the number of constraints by ~5 million constraints in the ClientIVCRecursiveVerifier. The branch also includes small refactorings in ECCVM prover and verifier. ``` Before: 17956264 After: 12495384 ``` (number of gates produced by running the `prove_tube` flow in branch and master) --- .../benchmark/ipa_bench/ipa.bench.cpp | 2 +- .../commitment_schemes/ipa/ipa.fuzzer.cpp | 7 +- .../commitment_schemes/ipa/ipa.hpp | 12 +- .../commitment_schemes/ipa/ipa.test.cpp | 28 ++--- .../commitment_schemes/kzg/kzg.hpp | 14 +-- .../commitment_schemes/kzg/kzg.test.cpp | 18 ++- .../commitment_schemes/shplonk/shplonk.hpp | 104 +++++++++--------- .../shplonk/shplonk.test.cpp | 29 ++--- .../zeromorph/zeromorph.test.cpp | 10 +- .../eccvm/eccvm_composer.test.cpp | 6 +- .../src/barretenberg/eccvm/eccvm_flavor.hpp | 60 ++++------ .../src/barretenberg/eccvm/eccvm_prover.cpp | 88 ++++++++------- .../eccvm/eccvm_transcript.test.cpp | 22 ++-- .../src/barretenberg/eccvm/eccvm_verifier.cpp | 101 ++++++++--------- .../eccvm_recursive_verifier.cpp | 104 +++++++++--------- .../translator_vm/translator_prover.cpp | 3 +- .../ultra_honk/decider_prover.cpp | 3 +- .../barretenberg/vm/generated/avm_prover.cpp | 3 +- 18 files changed, 286 insertions(+), 328 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/benchmark/ipa_bench/ipa.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/ipa_bench/ipa.bench.cpp index 2f4b2cd88f4..fac0f30f3b5 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/ipa_bench/ipa.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/ipa_bench/ipa.bench.cpp @@ -43,7 +43,7 @@ void ipa_open(State& state) noexcept auto prover_transcript = std::make_shared(); state.ResumeTiming(); // Compute proof - IPA::compute_opening_proof(ck, opening_pair, poly, prover_transcript); + IPA::compute_opening_proof(ck, { poly, opening_pair }, prover_transcript); // Store info for verifier prover_transcripts[static_cast(state.range(0)) - MIN_POLYNOMIAL_DEGREE_LOG2] = prover_transcript; opening_claims[static_cast(state.range(0)) - MIN_POLYNOMIAL_DEGREE_LOG2] = opening_claim; diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp index cebb8c59c7a..df6c3ec3115 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp @@ -21,11 +21,10 @@ class ProxyCaller { public: template static void compute_opening_proof_internal(const std::shared_ptr>& ck, - const OpeningPair& opening_pair, - const Polynomial& polynomial, + const ProverOpeningClaim& opening_claim, const std::shared_ptr& transcript) { - IPA::compute_opening_proof_internal(ck, opening_pair, polynomial, transcript); + IPA::compute_opening_proof_internal(ck, opening_claim, transcript); } template static bool verify_internal(const std::shared_ptr>& vk, @@ -145,7 +144,7 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) } auto const opening_pair = OpeningPair{ x, poly.evaluate(x) }; auto const opening_claim = OpeningClaim{ opening_pair, ck->commit(poly) }; - ProxyCaller::compute_opening_proof_internal(ck, opening_pair, poly, transcript); + ProxyCaller::compute_opening_proof_internal(ck, { poly, opening_pair }, transcript); // Reset challenge indices transcript->reset_indices(); diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp index 288ad34b3ab..0fcc7c65841 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp @@ -130,10 +130,12 @@ template class IPA { */ template static void compute_opening_proof_internal(const std::shared_ptr& ck, - const OpeningPair& opening_pair, - const Polynomial& polynomial, + const ProverOpeningClaim& opening_claim, const std::shared_ptr& transcript) { + + Polynomial polynomial = opening_claim.polynomial; + // clang-format on auto poly_length = static_cast(polynomial.size()); @@ -184,6 +186,7 @@ template class IPA { // Step 5. // Compute vector b (vector of the powers of the challenge) + OpeningPair opening_pair = opening_claim.opening_pair; std::vector b_vec(poly_length); run_loop_in_parallel_if_effective( poly_length, @@ -603,11 +606,10 @@ template class IPA { * compute_opening_proof_internal \endlink. */ static void compute_opening_proof(const std::shared_ptr& ck, - const OpeningPair& opening_pair, - const Polynomial& polynomial, + const ProverOpeningClaim& opening_claim, const std::shared_ptr& transcript) { - compute_opening_proof_internal(ck, opening_pair, polynomial, transcript); + compute_opening_proof_internal(ck, opening_claim, transcript); } /** diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp index 4defedb4500..db8a2597a5b 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp @@ -67,7 +67,7 @@ TEST_F(IPATest, OpenZeroPolynomial) // initialize empty prover transcript auto prover_transcript = std::make_shared(); - IPA::compute_opening_proof(this->ck(), opening_pair, poly, prover_transcript); + IPA::compute_opening_proof(this->ck(), { poly, opening_pair }, prover_transcript); // initialize verifier transcript from proof data auto verifier_transcript = std::make_shared(prover_transcript->proof_data); @@ -92,7 +92,7 @@ TEST_F(IPATest, OpenAtZero) // initialize empty prover transcript auto prover_transcript = std::make_shared(); - IPA::compute_opening_proof(this->ck(), opening_pair, poly, prover_transcript); + IPA::compute_opening_proof(this->ck(), { poly, opening_pair }, prover_transcript); // initialize verifier transcript from proof data auto verifier_transcript = std::make_shared(prover_transcript->proof_data); @@ -131,7 +131,7 @@ TEST_F(IPATest, ChallengesAreZero) auto new_random_vector = random_vector; new_random_vector[i] = Fr::zero(); transcript->initialize(new_random_vector); - EXPECT_ANY_THROW(IPA::compute_opening_proof_internal(this->ck(), opening_pair, poly, transcript)); + EXPECT_ANY_THROW(IPA::compute_opening_proof_internal(this->ck(), { poly, opening_pair }, transcript)); } // Fill out a vector of affine elements that the verifier receives from the prover with generators (we don't care // about them right now) @@ -181,7 +181,7 @@ TEST_F(IPATest, AIsZeroAfterOneRound) transcript->initialize(random_vector); // Compute opening proof - IPA::compute_opening_proof_internal(this->ck(), opening_pair, poly, transcript); + IPA::compute_opening_proof_internal(this->ck(), { poly, opening_pair }, transcript); // Reset indices transcript->reset_indices(); @@ -221,7 +221,7 @@ TEST_F(IPATest, Open) // initialize empty prover transcript auto prover_transcript = std::make_shared(); - IPA::compute_opening_proof(this->ck(), opening_pair, poly, prover_transcript); + IPA::compute_opening_proof(this->ck(), { poly, opening_pair }, prover_transcript); // initialize verifier transcript from proof data auto verifier_transcript = std::make_shared(prover_transcript->proof_data); @@ -295,22 +295,18 @@ TEST_F(IPATest, GeminiShplonkIPAWithShift) const auto [gemini_opening_pairs, gemini_witnesses] = GeminiProver::compute_fold_polynomial_evaluations( mle_opening_point, std::move(gemini_polynomials), r_challenge); + std::vector> opening_claims; + for (size_t l = 0; l < log_n; ++l) { std::string label = "Gemini:a_" + std::to_string(l); const auto& evaluation = gemini_opening_pairs[l + 1].evaluation; prover_transcript->send_to_verifier(label, evaluation); + opening_claims.emplace_back(gemini_witnesses[l], gemini_opening_pairs[l]); } + opening_claims.emplace_back(gemini_witnesses[log_n], gemini_opening_pairs[log_n]); - const Fr nu_challenge = prover_transcript->template get_challenge("Shplonk:nu"); - auto batched_quotient_Q = - ShplonkProver::compute_batched_quotient(gemini_opening_pairs, gemini_witnesses, nu_challenge); - prover_transcript->send_to_verifier("Shplonk:Q", this->ck()->commit(batched_quotient_Q)); - - const Fr z_challenge = prover_transcript->template get_challenge("Shplonk:z"); - const auto [shplonk_opening_pair, shplonk_witness] = ShplonkProver::compute_partially_evaluated_batched_quotient( - gemini_opening_pairs, gemini_witnesses, std::move(batched_quotient_Q), nu_challenge, z_challenge); - - IPA::compute_opening_proof(this->ck(), shplonk_opening_pair, shplonk_witness, prover_transcript); + const auto opening_claim = ShplonkProver::prove(this->ck(), opening_claims, prover_transcript); + IPA::compute_opening_proof(this->ck(), opening_claim, prover_transcript); auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript); @@ -321,7 +317,7 @@ TEST_F(IPATest, GeminiShplonkIPAWithShift) verifier_transcript); const auto shplonk_verifier_claim = - ShplonkVerifier::reduce_verification(this->vk(), gemini_verifier_claim, verifier_transcript); + ShplonkVerifier::reduce_verification(this->vk()->get_g1_identity(), gemini_verifier_claim, verifier_transcript); auto result = IPA::reduce_verify(this->vk(), shplonk_verifier_claim, verifier_transcript); EXPECT_EQ(result, true); diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.hpp index c763f3a2ecf..a067b224fc6 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.hpp @@ -26,19 +26,19 @@ template class KZG { * @brief Computes the KZG commitment to an opening proof polynomial at a single evaluation point * * @param ck The commitment key which has a commit function, the srs and pippenger_runtime_state - * @param opening_pair OpeningPair = {r, v = p(r)} - * @param polynomial The witness whose opening proof needs to be computed + * @param opening_claim {p, (r, v = p(r))} where p is the witness polynomial whose opening proof needs to be + * computed * @param prover_transcript Prover transcript */ static void compute_opening_proof(std::shared_ptr ck, - const OpeningPair& opening_pair, - const Polynomial& polynomial, + const ProverOpeningClaim& opening_claim, const std::shared_ptr& prover_trancript) { - Polynomial quotient = polynomial; - quotient[0] -= opening_pair.evaluation; + Polynomial quotient = opening_claim.polynomial; + OpeningPair pair = opening_claim.opening_pair; + quotient[0] -= pair.evaluation; // Computes the coefficients for the quotient polynomial q(X) = (p(X) - v) / (X - r) through an FFT - quotient.factor_roots(opening_pair.challenge); + quotient.factor_roots(pair.challenge); auto quotient_commitment = ck->commit(quotient); // TODO(#479): for now we compute the KZG commitment directly to unify the KZG and IPA interfaces but in the // future we might need to adjust this to use the incoming alternative to work queue (i.e. variation of diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp index 5271e92b890..5dd1fa892c4 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.test.cpp @@ -41,7 +41,7 @@ TYPED_TEST(KZGTest, single) auto prover_transcript = NativeTranscript::prover_init_empty(); - KZG::compute_opening_proof(this->ck(), opening_pair, witness, prover_transcript); + KZG::compute_opening_proof(this->ck(), { witness, opening_pair }, prover_transcript); auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript); auto pairing_points = KZG::reduce_verify(opening_claim, verifier_transcript); @@ -130,27 +130,23 @@ TYPED_TEST(KZGTest, GeminiShplonkKzgWithShift) const auto [gemini_opening_pairs, gemini_witnesses] = GeminiProver::compute_fold_polynomial_evaluations( mle_opening_point, std::move(gemini_polynomials), r_challenge); + std::vector> opening_claims; for (size_t l = 0; l < log_n; ++l) { std::string label = "Gemini:a_" + std::to_string(l); const auto& evaluation = gemini_opening_pairs[l + 1].evaluation; prover_transcript->send_to_verifier(label, evaluation); + opening_claims.emplace_back(gemini_witnesses[l], gemini_opening_pairs[l]); } + opening_claims.emplace_back(gemini_witnesses[log_n], gemini_opening_pairs[log_n]); // Shplonk prover output: // - opening pair: (z_challenge, 0) // - witness: polynomial Q - Q_z - const Fr nu_challenge = prover_transcript->template get_challenge("Shplonk:nu"); - auto batched_quotient_Q = - ShplonkProver::compute_batched_quotient(gemini_opening_pairs, gemini_witnesses, nu_challenge); - prover_transcript->send_to_verifier("Shplonk:Q", this->ck()->commit(batched_quotient_Q)); - - const Fr z_challenge = prover_transcript->template get_challenge("Shplonk:z"); - const auto [shplonk_opening_pair, shplonk_witness] = ShplonkProver::compute_partially_evaluated_batched_quotient( - gemini_opening_pairs, gemini_witnesses, std::move(batched_quotient_Q), nu_challenge, z_challenge); + const auto opening_claim = ShplonkProver::prove(this->ck(), opening_claims, prover_transcript); // KZG prover: // - Adds commitment [W] to transcript - KZG::compute_opening_proof(this->ck(), shplonk_opening_pair, shplonk_witness, prover_transcript); + KZG::compute_opening_proof(this->ck(), opening_claim, prover_transcript); // Run the full verifier PCS protocol with genuine opening claims (genuine commitment, genuine evaluation) @@ -166,7 +162,7 @@ TYPED_TEST(KZGTest, GeminiShplonkKzgWithShift) // Shplonk verifier claim: commitment [Q] - [Q_z], opening point (z_challenge, 0) const auto shplonk_verifier_claim = - ShplonkVerifier::reduce_verification(this->vk(), gemini_verifier_claim, verifier_transcript); + ShplonkVerifier::reduce_verification(this->vk()->get_g1_identity(), gemini_verifier_claim, verifier_transcript); // KZG verifier: // aggregates inputs [Q] - [Q_z] and [W] into an 'accumulator' (can perform pairing check on result) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplonk.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplonk.hpp index 1dbca1cd296..9eac7b4a48c 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplonk.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplonk.hpp @@ -20,24 +20,6 @@ */ namespace bb { -/** - * @brief Polynomial G(X) = Q(X) - ∑ₖ ẑₖ(r)⋅( Bₖ(X) − Tₖ(z) ), where Q(X) = ∑ₖ ( Bₖ(X) − Tₖ(X) ) / zₖ(X) - * - * @tparam Curve EC parameters - */ -template using OutputWitness = bb::Polynomial; - -/** - * @brief Prover output (claim=([G], r, 0), witness = G(X), proof = [Q]) - * that can be passed on to a univariate opening protocol. - * - * @tparam Curve EC parameters - */ -template struct ShplonkProverOutput { - OpeningPair opening_pair; // single opening pair (challenge, evaluation) - OutputWitness witness; // single polynomial G(X) -}; - /** * @brief Shplonk Prover * @@ -51,34 +33,31 @@ template class ShplonkProver_ { /** * @brief Compute batched quotient polynomial Q(X) = ∑ⱼ ρʲ ⋅ ( fⱼ(X) − vⱼ) / ( X − xⱼ ) * - * @param opening_pairs list of opening pairs (xⱼ, vⱼ) for a witness polynomial fⱼ(X), s.t. fⱼ(xⱼ) = vⱼ. - * @param witness_polynomials list of polynomials fⱼ(X). - * @param nu + * @param opening_claims list of prover opening claims {fⱼ(X), (xⱼ, vⱼ)} for a witness polynomial fⱼ(X), s.t. fⱼ(xⱼ) + * = vⱼ. + * @param nu batching challenge * @return Polynomial Q(X) */ - static Polynomial compute_batched_quotient(std::span> opening_pairs, - std::span witness_polynomials, - const Fr& nu) + static Polynomial compute_batched_quotient(std::span> opening_claims, const Fr& nu) { // Find n, the maximum size of all polynomials fⱼ(X) size_t max_poly_size{ 0 }; - for (const auto& poly : witness_polynomials) { - max_poly_size = std::max(max_poly_size, poly.size()); + for (const auto& claim : opening_claims) { + max_poly_size = std::max(max_poly_size, claim.polynomial.size()); } // Q(X) = ∑ⱼ ρʲ ⋅ ( fⱼ(X) − vⱼ) / ( X − xⱼ ) Polynomial Q(max_poly_size); Polynomial tmp(max_poly_size); Fr current_nu = Fr::one(); - for (size_t j = 0; j < opening_pairs.size(); ++j) { - // (Cⱼ, xⱼ, vⱼ) - const auto& [challenge, evaluation] = opening_pairs[j]; + for (const auto& claim : opening_claims) { - // tmp = ρʲ ⋅ ( fⱼ(X) − vⱼ) / ( X − xⱼ ) - tmp = witness_polynomials[j]; - tmp[0] -= evaluation; - tmp.factor_roots(challenge); + // Compute individual claim quotient tmp = ( fⱼ(X) − vⱼ) / ( X − xⱼ ) + tmp = claim.polynomial; + tmp[0] -= claim.opening_pair.evaluation; + tmp.factor_roots(claim.opening_pair.challenge); + // Add the claim quotient to the batched quotient polynomial Q.add_scaled(tmp, current_nu); current_nu *= nu; } @@ -97,20 +76,19 @@ template class ShplonkProver_ { * @param z_challenge * @return Output{OpeningPair, Polynomial} */ - static ShplonkProverOutput compute_partially_evaluated_batched_quotient( - std::span> opening_pairs, - std::span witness_polynomials, - Polynomial&& batched_quotient_Q, + static ProverOpeningClaim compute_partially_evaluated_batched_quotient( + std::span> opening_claims, + Polynomial& batched_quotient_Q, const Fr& nu_challenge, const Fr& z_challenge) { - const size_t num_opening_pairs = opening_pairs.size(); + const size_t num_opening_claims = opening_claims.size(); // {ẑⱼ(r)}ⱼ , where ẑⱼ(r) = 1/zⱼ(r) = 1/(r - xⱼ) std::vector inverse_vanishing_evals; - inverse_vanishing_evals.reserve(num_opening_pairs); - for (const auto& pair : opening_pairs) { - inverse_vanishing_evals.emplace_back(z_challenge - pair.challenge); + inverse_vanishing_evals.reserve(num_opening_claims); + for (const auto& claim : opening_claims) { + inverse_vanishing_evals.emplace_back(z_challenge - claim.opening_pair.challenge); } Fr::batch_invert(inverse_vanishing_evals); @@ -121,24 +99,44 @@ template class ShplonkProver_ { // G₀ = ∑ⱼ ρʲ ⋅ vⱼ / ( r − xⱼ ) Fr current_nu = Fr::one(); Polynomial tmp(G.size()); - for (size_t j = 0; j < num_opening_pairs; ++j) { - // (Cⱼ, xⱼ, vⱼ) - const auto& [challenge, evaluation] = opening_pairs[j]; - + size_t idx = 0; + for (const auto& claim : opening_claims) { // tmp = ρʲ ⋅ ( fⱼ(X) − vⱼ) / ( r − xⱼ ) - tmp = witness_polynomials[j]; - tmp[0] -= evaluation; - Fr scaling_factor = current_nu * inverse_vanishing_evals[j]; // = ρʲ / ( r − xⱼ ) + tmp = claim.polynomial; + tmp[0] -= claim.opening_pair.evaluation; + Fr scaling_factor = current_nu * inverse_vanishing_evals[idx]; // = ρʲ / ( r − xⱼ ) // G -= ρʲ ⋅ ( fⱼ(X) − vⱼ) / ( r − xⱼ ) G.add_scaled(tmp, -scaling_factor); current_nu *= nu_challenge; + idx++; } // Return opening pair (z, 0) and polynomial G(X) = Q(X) - Q_z(X) - return { .opening_pair = { .challenge = z_challenge, .evaluation = Fr::zero() }, .witness = std::move(G) }; + return { .polynomial = G, .opening_pair = { .challenge = z_challenge, .evaluation = Fr::zero() } }; }; + + /** + * @brief Returns a batched opening claim equivalent to a set of opening claims consisting of polynomials, each + * opened at a single point. + * + * @param commitment_key + * @param opening_claims + * @param transcript + * @return ProverOpeningClaim + */ + static ProverOpeningClaim prove(const std::shared_ptr>& commitment_key, + std::span> opening_claims, + auto& transcript) + { + const Fr nu = transcript->template get_challenge("Shplonk:nu"); + auto batched_quotient = compute_batched_quotient(opening_claims, nu); + auto batched_quotient_commitment = commitment_key->commit(batched_quotient); + transcript->send_to_verifier("Shplonk:Q", batched_quotient_commitment); + const Fr z = transcript->template get_challenge("Shplonk:z"); + return compute_partially_evaluated_batched_quotient(opening_claims, batched_quotient, nu, z); + } }; /** @@ -156,12 +154,12 @@ template class ShplonkVerifier_ { * @brief Recomputes the new claim commitment [G] given the proof and * the challenge r. No verification happens so this function always succeeds. * + * @param g1_identity the identity element for the Curve * @param claims list of opening claims (Cⱼ, xⱼ, vⱼ) for a witness polynomial fⱼ(X), s.t. fⱼ(xⱼ) = vⱼ. - * @param proof [Q(X)] = [ ∑ⱼ ρʲ ⋅ ( fⱼ(X) − vⱼ) / ( X − xⱼ ) ] * @param transcript * @return OpeningClaim */ - static OpeningClaim reduce_verification(std::shared_ptr vk, + static OpeningClaim reduce_verification(Commitment g1_identity, std::span> claims, auto& transcript) { @@ -227,7 +225,7 @@ template class ShplonkVerifier_ { scalars.emplace_back(-scaling_factor); } - commitments.emplace_back(GroupElement::one(builder)); + commitments.emplace_back(g1_identity); scalars.emplace_back(G_commitment_constant); // [G] += G₀⋅[1] = [G] + (∑ⱼ ρʲ ⋅ vⱼ / ( r − xⱼ ))⋅[1] @@ -264,7 +262,7 @@ template class ShplonkVerifier_ { } // [G] += G₀⋅[1] = [G] + (∑ⱼ ρʲ ⋅ vⱼ / ( r − xⱼ ))⋅[1] - G_commitment += vk->get_g1_identity() * G_commitment_constant; + G_commitment += g1_identity * G_commitment_constant; } // Return opening pair (z, 0) and commitment [G] diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplonk.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplonk.test.cpp index 35976616526..299ee846a2a 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplonk.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/shplonk/shplonk.test.cpp @@ -22,8 +22,8 @@ TYPED_TEST(ShplonkTest, ShplonkSimple) using ShplonkProver = ShplonkProver_; using ShplonkVerifier = ShplonkVerifier_; using Fr = typename TypeParam::ScalarField; - using Polynomial = typename bb::Polynomial; - using OpeningPair = bb::OpeningPair; + using ProverOpeningClaim = ProverOpeningClaim; + using OpeningClaim = OpeningClaim; const size_t n = 16; @@ -43,32 +43,23 @@ TYPED_TEST(ShplonkTest, ShplonkSimple) const auto commitment2 = this->commit(poly2); // Aggregate polynomials and their opening pairs - std::vector opening_pairs = { { r1, eval1 }, { r2, eval2 } }; - std::vector polynomials = { poly1.share(), poly2.share() }; + std::vector prover_opening_claims = { { poly1, { r1, eval1 } }, { poly2, { r2, eval2 } } }; // Execute the shplonk prover functionality - const Fr nu_challenge = prover_transcript->template get_challenge("Shplonk:nu"); - auto batched_quotient_Q = ShplonkProver::compute_batched_quotient(opening_pairs, polynomials, nu_challenge); - prover_transcript->send_to_verifier("Shplonk:Q", this->ck()->commit(batched_quotient_Q)); - - const Fr z_challenge = prover_transcript->template get_challenge("Shplonk:z"); - const auto [prover_opening_pair, shplonk_prover_witness] = - ShplonkProver::compute_partially_evaluated_batched_quotient( - opening_pairs, polynomials, std::move(batched_quotient_Q), nu_challenge, z_challenge); - + const auto batched_opening_claim = ShplonkProver::prove(this->ck(), prover_opening_claims, prover_transcript); // An intermediate check to confirm the opening of the shplonk prover witness Q - this->verify_opening_pair(prover_opening_pair, shplonk_prover_witness); + this->verify_opening_pair(batched_opening_claim.opening_pair, batched_opening_claim.polynomial); // Aggregate polynomial commitments and their opening pairs - std::vector opening_claims; - opening_claims.emplace_back(OpeningClaim{ opening_pairs[0], commitment1 }); - opening_claims.emplace_back(OpeningClaim{ opening_pairs[1], commitment2 }); + std::vector verifier_opening_claims = { { { r1, eval1 }, commitment1 }, + { { r2, eval2 }, commitment2 } }; auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript); // Execute the shplonk verifier functionality - const auto verifier_claim = ShplonkVerifier::reduce_verification(this->vk(), opening_claims, verifier_transcript); + const auto batched_verifier_claim = ShplonkVerifier::reduce_verification( + this->vk()->get_g1_identity(), verifier_opening_claims, verifier_transcript); - this->verify_opening_claim(verifier_claim, shplonk_prover_witness); + this->verify_opening_claim(batched_verifier_claim, batched_opening_claim.polynomial); } } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.test.cpp index 3ce54297fba..122fcb1187f 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.test.cpp @@ -220,10 +220,7 @@ template class ZeroMorphTest : public CommitmentTestcommitment_key, prover_transcript); - PCS::compute_opening_proof(this->commitment_key, - prover_opening_claim.opening_pair, - prover_opening_claim.polynomial, - prover_transcript); + PCS::compute_opening_proof(this->commitment_key, prover_opening_claim, prover_transcript); auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript); @@ -274,10 +271,7 @@ template class ZeroMorphTest : public CommitmentTestcommitment_key, - prover_opening_claim.opening_pair, - prover_opening_claim.polynomial, - prover_transcript); + PCS::compute_opening_proof(this->commitment_key, prover_opening_claim, prover_transcript); auto verifier_transcript = NativeTranscript::verifier_init_empty(prover_transcript); diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_composer.test.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_composer.test.cpp index cd2db6b7124..c3ae908ff71 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_composer.test.cpp @@ -15,7 +15,7 @@ using namespace bb; -class ECCVMComposerTests : public ::testing::Test { +class ECCVMTests : public ::testing::Test { protected: void SetUp() override { srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); }; }; @@ -60,7 +60,7 @@ ECCVMCircuitBuilder generate_circuit(numeric::RNG* engine = nullptr) return builder; } -TEST_F(ECCVMComposerTests, BaseCase) +TEST_F(ECCVMTests, BaseCase) { ECCVMCircuitBuilder builder = generate_circuit(&engine); ECCVMProver prover(builder); @@ -71,7 +71,7 @@ TEST_F(ECCVMComposerTests, BaseCase) ASSERT_TRUE(verified); } -TEST_F(ECCVMComposerTests, EqFails) +TEST_F(ECCVMTests, EqFails) { auto builder = generate_circuit(&engine); // Tamper with the eq op such that the expected value is incorect diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp index 821de070780..4a87e300d09 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp @@ -911,10 +911,6 @@ class ECCVMFlavor { std::array sumcheck_evaluations; std::vector zm_cq_comms; Commitment zm_cq_comm; - uint32_t ipa_poly_degree; - std::vector ipa_l_comms; - std::vector ipa_r_comms; - FF ipa_a_0_eval; Commitment translation_hack_comm; FF translation_eval_op; FF translation_eval_px; @@ -922,10 +918,11 @@ class ECCVMFlavor { FF translation_eval_z1; FF translation_eval_z2; FF hack_eval; - uint32_t translation_ipa_poly_degree; - std::vector translation_ipa_l_comms; - std::vector translation_ipa_r_comms; - FF translation_ipa_a_0_eval; + Commitment shplonk_q_comm; + uint32_t ipa_poly_degree; + std::vector ipa_l_comms; + std::vector ipa_r_comms; + FF ipa_a_0_eval; Transcript() = default; @@ -1129,17 +1126,6 @@ class ECCVMFlavor { } zm_cq_comm = NativeTranscript::template deserialize_from_buffer(proof_data, num_frs_read); - ipa_poly_degree = NativeTranscript::template deserialize_from_buffer(NativeTranscript::proof_data, - num_frs_read); - auto log_poly_degree = static_cast(numeric::get_msb(ipa_poly_degree)); - for (size_t i = 0; i < log_poly_degree; ++i) { - ipa_l_comms.emplace_back(NativeTranscript::template deserialize_from_buffer( - NativeTranscript::proof_data, num_frs_read)); - ipa_r_comms.emplace_back(NativeTranscript::template deserialize_from_buffer( - NativeTranscript::proof_data, num_frs_read)); - } - ipa_a_0_eval = - NativeTranscript::template deserialize_from_buffer(NativeTranscript::proof_data, num_frs_read); translation_hack_comm = NativeTranscript::template deserialize_from_buffer( NativeTranscript::proof_data, num_frs_read); translation_eval_op = @@ -1155,17 +1141,20 @@ class ECCVMFlavor { hack_eval = NativeTranscript::template deserialize_from_buffer(NativeTranscript::proof_data, num_frs_read); - translation_ipa_poly_degree = NativeTranscript::template deserialize_from_buffer( - NativeTranscript::proof_data, num_frs_read); + shplonk_q_comm = NativeTranscript::template deserialize_from_buffer(proof_data, num_frs_read); + ipa_poly_degree = NativeTranscript::template deserialize_from_buffer(NativeTranscript::proof_data, + num_frs_read); + + auto log_poly_degree = static_cast(numeric::get_msb(ipa_poly_degree)); for (size_t i = 0; i < log_poly_degree; ++i) { - translation_ipa_l_comms.emplace_back(NativeTranscript::template deserialize_from_buffer( + ipa_l_comms.emplace_back(NativeTranscript::template deserialize_from_buffer( NativeTranscript::proof_data, num_frs_read)); - translation_ipa_r_comms.emplace_back(NativeTranscript::template deserialize_from_buffer( + ipa_r_comms.emplace_back(NativeTranscript::template deserialize_from_buffer( NativeTranscript::proof_data, num_frs_read)); } - translation_ipa_a_0_eval = + ipa_a_0_eval = NativeTranscript::template deserialize_from_buffer(NativeTranscript::proof_data, num_frs_read); } @@ -1284,15 +1273,6 @@ class ECCVMFlavor { } NativeTranscript::template serialize_to_buffer(zm_cq_comm, NativeTranscript::proof_data); - NativeTranscript::template serialize_to_buffer(ipa_poly_degree, NativeTranscript::proof_data); - - auto log_poly_degree = static_cast(numeric::get_msb(ipa_poly_degree)); - for (size_t i = 0; i < log_poly_degree; ++i) { - NativeTranscript::template serialize_to_buffer(ipa_l_comms[i], NativeTranscript::proof_data); - NativeTranscript::template serialize_to_buffer(ipa_r_comms[i], NativeTranscript::proof_data); - } - - NativeTranscript::template serialize_to_buffer(ipa_a_0_eval, NativeTranscript::proof_data); NativeTranscript::template serialize_to_buffer(translation_hack_comm, NativeTranscript::proof_data); NativeTranscript::template serialize_to_buffer(translation_eval_op, NativeTranscript::proof_data); NativeTranscript::template serialize_to_buffer(translation_eval_px, NativeTranscript::proof_data); @@ -1301,16 +1281,16 @@ class ECCVMFlavor { NativeTranscript::template serialize_to_buffer(translation_eval_z2, NativeTranscript::proof_data); NativeTranscript::template serialize_to_buffer(hack_eval, NativeTranscript::proof_data); - NativeTranscript::template serialize_to_buffer(translation_ipa_poly_degree, NativeTranscript::proof_data); - log_poly_degree = static_cast(numeric::get_msb(translation_ipa_poly_degree)); + NativeTranscript::template serialize_to_buffer(shplonk_q_comm, NativeTranscript::proof_data); + + NativeTranscript::template serialize_to_buffer(ipa_poly_degree, NativeTranscript::proof_data); + auto log_poly_degree = static_cast(numeric::get_msb(ipa_poly_degree)); for (size_t i = 0; i < log_poly_degree; ++i) { - NativeTranscript::template serialize_to_buffer(translation_ipa_l_comms[i], - NativeTranscript::proof_data); - NativeTranscript::template serialize_to_buffer(translation_ipa_r_comms[i], - NativeTranscript::proof_data); + NativeTranscript::template serialize_to_buffer(ipa_l_comms[i], NativeTranscript::proof_data); + NativeTranscript::template serialize_to_buffer(ipa_r_comms[i], NativeTranscript::proof_data); } - serialize_to_buffer(translation_ipa_a_0_eval, proof_data); + serialize_to_buffer(ipa_a_0_eval, proof_data); ASSERT(NativeTranscript::proof_data.size() == old_proof_length); } diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp index b752a861a90..3215ffac416 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp @@ -1,6 +1,7 @@ #include "eccvm_prover.hpp" #include "barretenberg/commitment_schemes/claim.hpp" #include "barretenberg/commitment_schemes/commitment_key.hpp" +#include "barretenberg/commitment_schemes/shplonk/shplonk.hpp" #include "barretenberg/common/ref_array.hpp" #include "barretenberg/honk/proof_system/logderivative_library.hpp" #include "barretenberg/honk/proof_system/permutation_library.hpp" @@ -104,72 +105,74 @@ void ECCVMProver::execute_relation_check_rounds() } /** - * @brief Execute the ZeroMorph protocol to produce an opening claim for the multilinear evaluations produced by - * Sumcheck and then produce an opening proof with a univariate PCS - * @details See https://hackmd.io/dlf9xEwhTQyE3hiGbq4FsA?view for a complete description of the unrolled protocol. + * @brief Produce a univariate opening claim for the sumcheck multivariate evalutions and a batched univariate claim + * for the transcript polynomials (for the Translator consistency check). Reduce the two opening claims to a single one + * via Shplonk and produce an opening proof with the univariate PCS of choice (IPA when operating on Grumpkin). + * @details See https://hackmd.io/dlf9xEwhTQyE3hiGbq4FsA?view for a complete description of the unrolled ZeroMorph + * protocol. * - * */ + */ void ECCVMProver::execute_pcs_rounds() { using Curve = typename Flavor::Curve; using ZeroMorph = ZeroMorphProver_; - auto prover_opening_claim = ZeroMorph::prove(key->polynomials.get_unshifted(), - key->polynomials.get_to_be_shifted(), - sumcheck_output.claimed_evaluations.get_unshifted(), - sumcheck_output.claimed_evaluations.get_shifted(), - sumcheck_output.challenge, - commitment_key, - transcript); - PCS::compute_opening_proof( - commitment_key, prover_opening_claim.opening_pair, prover_opening_claim.polynomial, transcript); -} - -/** - * @brief Batch open the transcript polynomials as univariates for Translator consistency check - * TODO(#768): Find a better way to do this. See issue for details. - * - * @tparam Flavor - */ -void ECCVMProver::execute_transcript_consistency_univariate_opening_round() -{ - // Since IPA cannot currently handle polynomials for which the latter half of the coefficients are 0, we hackily - // batch the constant polynomial 1 in with the 5 transcript polynomials. See issue #768 for more details. + using Shplonk = ShplonkProver_; + using OpeningClaim = ProverOpeningClaim; + + // Execute the ZeroMorph protocol to produce a univariate opening claim for the multilinear evaluations produced by + // Sumcheck + auto multivariate_to_univariate_opening_claim = + ZeroMorph::prove(key->polynomials.get_unshifted(), + key->polynomials.get_to_be_shifted(), + sumcheck_output.claimed_evaluations.get_unshifted(), + sumcheck_output.claimed_evaluations.get_shifted(), + sumcheck_output.challenge, + commitment_key, + transcript); + + // Batch open the transcript polynomials as univariates for Translator consistency check. Since IPA cannot + // currently handle polynomials for which the latter half of the coefficients are 0, we hackily + // batch the constant polynomial 1 in with the 5 transcript polynomials. + // TODO(https://github.com/AztecProtocol/barretenberg/issues/768): fix IPA to avoid the need for the hack polynomial Polynomial hack(key->circuit_size); for (size_t idx = 0; idx < key->circuit_size; idx++) { hack[idx] = 1; } transcript->send_to_verifier("Translation:hack_commitment", commitment_key->commit(hack)); - // Get the challenge at which we evaluate the polynomials as univariates + // Get the challenge at which we evaluate all transcript polynomials as univariates evaluation_challenge_x = transcript->template get_challenge("Translation:evaluation_challenge_x"); + // Evaluate the transcript polynomials at the challenge translation_evaluations.op = key->polynomials.transcript_op.evaluate(evaluation_challenge_x); translation_evaluations.Px = key->polynomials.transcript_Px.evaluate(evaluation_challenge_x); translation_evaluations.Py = key->polynomials.transcript_Py.evaluate(evaluation_challenge_x); translation_evaluations.z1 = key->polynomials.transcript_z1.evaluate(evaluation_challenge_x); translation_evaluations.z2 = key->polynomials.transcript_z2.evaluate(evaluation_challenge_x); - // Add the univariate evaluations to the transcript + // Add the univariate evaluations to the transcript so the verifier can reconstruct the batched evaluation transcript->send_to_verifier("Translation:op", translation_evaluations.op); transcript->send_to_verifier("Translation:Px", translation_evaluations.Px); transcript->send_to_verifier("Translation:Py", translation_evaluations.Py); transcript->send_to_verifier("Translation:z1", translation_evaluations.z1); transcript->send_to_verifier("Translation:z2", translation_evaluations.z2); - transcript->send_to_verifier("Translation:hack_evaluation", hack.evaluate(evaluation_challenge_x)); - // Get another challenge for batching the univariate claims + FF hack_evaluation = hack.evaluate(evaluation_challenge_x); + transcript->send_to_verifier("Translation:hack_evaluation", hack_evaluation); + + // Get another challenge for batching the univariates and evaluations FF ipa_batching_challenge = transcript->template get_challenge("Translation:ipa_batching_challenge"); // Collect the polynomials and evaluations to be batched RefArray univariate_polynomials{ key->polynomials.transcript_op, key->polynomials.transcript_Px, key->polynomials.transcript_Py, key->polynomials.transcript_z1, key->polynomials.transcript_z2, hack }; - std::array univariate_evaluations; - for (auto [eval, polynomial] : zip_view(univariate_evaluations, univariate_polynomials)) { - eval = polynomial.evaluate(evaluation_challenge_x); - } + std::array univariate_evaluations{ + translation_evaluations.op, translation_evaluations.Px, translation_evaluations.Py, + translation_evaluations.z1, translation_evaluations.z2, hack_evaluation + }; - // Construct the batched polynomial and batched evaluation + // Construct the batched polynomial and batched evaluation to produce the batched opening claim Polynomial batched_univariate{ key->circuit_size }; FF batched_evaluation{ 0 }; auto batching_scalar = FF(1); @@ -179,12 +182,17 @@ void ECCVMProver::execute_transcript_consistency_univariate_opening_round() batching_scalar *= ipa_batching_challenge; } - // TODO(https://github.com/AztecProtocol/barretenberg/issues/922): We are doing another round of IPA here with - // exactly the same labels and no domain separation so if/when labels are going to matter we are clashing. - PCS::compute_opening_proof( - commitment_key, { evaluation_challenge_x, batched_evaluation }, batched_univariate, transcript); + std::array opening_claims = { multivariate_to_univariate_opening_claim, + { .polynomial = batched_univariate, + .opening_pair = { evaluation_challenge_x, batched_evaluation } } }; - // Get another challenge for batching the univariate claims + // Reduce the opening claims to a single opening claim via Shplonk + const OpeningClaim batched_opening_claim = Shplonk::prove(commitment_key, opening_claims, transcript); + + // Compute the opening proof for the batched opening claim with the univariate PCS + PCS::compute_opening_proof(commitment_key, batched_opening_claim, transcript); + + // Produce another challenge passed as input to the translator verifier translation_batching_challenge_v = transcript->template get_challenge("Translation:batching_challenge"); } @@ -210,8 +218,6 @@ HonkProof ECCVMProver::construct_proof() execute_pcs_rounds(); - execute_transcript_consistency_univariate_opening_round(); - return export_proof(); } } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_transcript.test.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_transcript.test.cpp index 0b2e13a7850..9eaedc9df93 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_transcript.test.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_transcript.test.cpp @@ -164,20 +164,6 @@ class ECCVMTranscriptTests : public ::testing::Test { manifest_expected.add_challenge(round, "ZM:x", "ZM:z"); round++; - manifest_expected.add_entry(round, "IPA:poly_degree_plus_1", frs_per_uint32); - manifest_expected.add_challenge(round, "IPA:generator_challenge"); - - for (size_t i = 0; i < log_n; ++i) { - round++; - std::string idx = std::to_string(log_n - i - 1); - manifest_expected.add_entry(round, "IPA:L_" + idx, frs_per_G); - manifest_expected.add_entry(round, "IPA:R_" + idx, frs_per_G); - std::string label = "IPA:round_challenge_" + idx; - manifest_expected.add_challenge(round, label); - } - - round++; - manifest_expected.add_entry(round, "IPA:a_0", frs_per_Fr); manifest_expected.add_entry(round, "Translation:hack_commitment", frs_per_G); manifest_expected.add_challenge(round, "Translation:evaluation_challenge_x"); @@ -190,6 +176,13 @@ class ECCVMTranscriptTests : public ::testing::Test { manifest_expected.add_entry(round, "Translation:hack_evaluation", frs_per_Fr); manifest_expected.add_challenge(round, "Translation:ipa_batching_challenge"); + round++; + manifest_expected.add_challenge(round, "Shplonk:nu"); + + round++; + manifest_expected.add_entry(round, "Shplonk:Q", frs_per_G); + manifest_expected.add_challenge(round, "Shplonk:z"); + round++; manifest_expected.add_entry(round, "IPA:poly_degree_plus_1", frs_per_uint32); manifest_expected.add_challenge(round, "IPA:generator_challenge"); @@ -209,6 +202,7 @@ class ECCVMTranscriptTests : public ::testing::Test { return manifest_expected; } + ECCVMCircuitBuilder generate_trace(numeric::RNG* engine = nullptr) { std::shared_ptr op_queue = std::make_shared(); diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp index b6bf6e75187..2c1e3d6dc57 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp @@ -1,4 +1,5 @@ #include "./eccvm_verifier.hpp" +#include "barretenberg/commitment_schemes/shplonk/shplonk.hpp" #include "barretenberg/commitment_schemes/zeromorph/zeromorph.hpp" #include "barretenberg/sumcheck/sumcheck.hpp" @@ -11,6 +12,7 @@ bool ECCVMVerifier::verify_proof(const HonkProof& proof) { using Curve = typename Flavor::Curve; using ZeroMorph = ZeroMorphVerifier_; + using Shplonk = ShplonkVerifier_; RelationParameters relation_parameters; transcript = std::make_shared(proof); @@ -58,59 +60,58 @@ bool ECCVMVerifier::verify_proof(const HonkProof& proof) return false; } - auto multivariate_opening_claim = ZeroMorph::verify(commitments.get_unshifted(), - commitments.get_to_be_shifted(), - claimed_evaluations.get_unshifted(), - claimed_evaluations.get_shifted(), - multivariate_challenge, - key->pcs_verification_key->get_g1_identity(), - transcript); - bool multivariate_opening_verified = - PCS::reduce_verify(key->pcs_verification_key, multivariate_opening_claim, transcript); + // Reduce the multivariate evaluation claims produced by sumcheck to a single univariate opening claim + auto multivariate_to_univariate_opening_claim = ZeroMorph::verify(commitments.get_unshifted(), + commitments.get_to_be_shifted(), + claimed_evaluations.get_unshifted(), + claimed_evaluations.get_shifted(), + multivariate_challenge, + key->pcs_verification_key->get_g1_identity(), + transcript); // Execute transcript consistency univariate opening round - // TODO(#768): Find a better way to do this. See issue for details. - bool univariate_opening_verified = false; - { - auto hack_commitment = transcript->template receive_from_prover("Translation:hack_commitment"); - - FF evaluation_challenge_x = transcript->template get_challenge("Translation:evaluation_challenge_x"); - - // Construct arrays of commitments and evaluations to be batched - const size_t NUM_UNIVARIATES = 6; - std::array transcript_commitments = { - commitments.transcript_op, commitments.transcript_Px, commitments.transcript_Py, - commitments.transcript_z1, commitments.transcript_z2, hack_commitment - }; - std::array transcript_evaluations = { - transcript->template receive_from_prover("Translation:op"), - transcript->template receive_from_prover("Translation:Px"), - transcript->template receive_from_prover("Translation:Py"), - transcript->template receive_from_prover("Translation:z1"), - transcript->template receive_from_prover("Translation:z2"), - transcript->template receive_from_prover("Translation:hack_evaluation") - }; - - // Get another challenge for batching the univariate claims - FF ipa_batching_challenge = transcript->template get_challenge("Translation:ipa_batching_challenge"); - - // Construct batched commitment and batched evaluation - auto batched_commitment = transcript_commitments[0]; - auto batched_transcript_eval = transcript_evaluations[0]; - auto batching_scalar = ipa_batching_challenge; - for (size_t idx = 1; idx < transcript_commitments.size(); ++idx) { - batched_commitment = batched_commitment + transcript_commitments[idx] * batching_scalar; - batched_transcript_eval += batching_scalar * transcript_evaluations[idx]; - batching_scalar *= ipa_batching_challenge; - } - - // Construct and verify batched opening claim - OpeningClaim batched_univariate_claim = { { evaluation_challenge_x, batched_transcript_eval }, - batched_commitment }; - univariate_opening_verified = - PCS::reduce_verify(key->pcs_verification_key, batched_univariate_claim, transcript); + auto hack_commitment = transcript->template receive_from_prover("Translation:hack_commitment"); + + FF evaluation_challenge_x = transcript->template get_challenge("Translation:evaluation_challenge_x"); + + // Construct arrays of commitments and evaluations to be batched, the evaluations being received from the prover + const size_t NUM_UNIVARIATES = 6; + std::array transcript_commitments = { + commitments.transcript_op, commitments.transcript_Px, commitments.transcript_Py, + commitments.transcript_z1, commitments.transcript_z2, hack_commitment + }; + std::array transcript_evaluations = { + transcript->template receive_from_prover("Translation:op"), + transcript->template receive_from_prover("Translation:Px"), + transcript->template receive_from_prover("Translation:Py"), + transcript->template receive_from_prover("Translation:z1"), + transcript->template receive_from_prover("Translation:z2"), + transcript->template receive_from_prover("Translation:hack_evaluation") + }; + + // Get the batching challenge for commitments and evaluations + FF ipa_batching_challenge = transcript->template get_challenge("Translation:ipa_batching_challenge"); + + // Compute the batched commitment and batched evaluation for the univariate opening claim + auto batched_commitment = transcript_commitments[0]; + auto batched_transcript_eval = transcript_evaluations[0]; + auto batching_scalar = ipa_batching_challenge; + for (size_t idx = 1; idx < transcript_commitments.size(); ++idx) { + batched_commitment = batched_commitment + transcript_commitments[idx] * batching_scalar; + batched_transcript_eval += batching_scalar * transcript_evaluations[idx]; + batching_scalar *= ipa_batching_challenge; } - return sumcheck_verified.value() && multivariate_opening_verified && univariate_opening_verified; + std::array, 2> opening_claims = { multivariate_to_univariate_opening_claim, + { { evaluation_challenge_x, batched_transcript_eval }, + batched_commitment } }; + + // Construct and verify the combined opening claim + auto batched_opening_claim = + Shplonk::reduce_verification(key->pcs_verification_key->get_g1_identity(), opening_claims, transcript); + + bool batched_opening_verified = PCS::reduce_verify(key->pcs_verification_key, batched_opening_claim, transcript); + + return sumcheck_verified.value() && batched_opening_verified; } } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp index e2598508e2b..4ceb6478179 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm_recursion/eccvm_recursive_verifier.cpp @@ -1,4 +1,5 @@ #include "./eccvm_recursive_verifier.hpp" +#include "barretenberg/commitment_schemes/shplonk/shplonk.hpp" #include "barretenberg/commitment_schemes/zeromorph/zeromorph.hpp" #include "barretenberg/sumcheck/sumcheck.hpp" #include "barretenberg/transcript/transcript.hpp" @@ -20,6 +21,8 @@ template void ECCVMRecursiveVerifier_::verify_proof(co { using Curve = typename Flavor::Curve; using ZeroMorph = ZeroMorphVerifier_; + using Shplonk = ShplonkVerifier_; + RelationParameters relation_parameters; StdlibProof stdlib_proof = bb::convert_proof_to_witness(builder, proof); @@ -72,57 +75,58 @@ template void ECCVMRecursiveVerifier_::verify_proof(co auto [multivariate_challenge, claimed_evaluations, sumcheck_verified] = sumcheck.verify(relation_parameters, alpha, gate_challenges); - auto opening_claim = ZeroMorph::verify(commitments.get_unshifted(), - commitments.get_to_be_shifted(), - claimed_evaluations.get_unshifted(), - claimed_evaluations.get_shifted(), - multivariate_challenge, - key->pcs_verification_key->get_g1_identity(), - transcript); - auto multivariate_opening_verified = PCS::reduce_verify(key->pcs_verification_key, opening_claim, transcript); - - // Execute transcript consistency univariate opening round - // TODO(#768): Find a better way to do this. See issue for details. - bool univariate_opening_verified = false; - { - auto hack_commitment = transcript->template receive_from_prover("Translation:hack_commitment"); - - FF evaluation_challenge_x = transcript->template get_challenge("Translation:evaluation_challenge_x"); - - // Construct arrays of commitments and evaluations to be batched - const size_t NUM_UNIVARIATES = 6; - std::array transcript_commitments = { - commitments.transcript_op, commitments.transcript_Px, commitments.transcript_Py, - commitments.transcript_z1, commitments.transcript_z2, hack_commitment - }; - std::array transcript_evaluations = { - transcript->template receive_from_prover("Translation:op"), - transcript->template receive_from_prover("Translation:Px"), - transcript->template receive_from_prover("Translation:Py"), - transcript->template receive_from_prover("Translation:z1"), - transcript->template receive_from_prover("Translation:z2"), - transcript->template receive_from_prover("Translation:hack_evaluation") - }; - - // Get another challenge for batching the univariate claims - FF ipa_batching_challenge = transcript->template get_challenge("Translation:ipa_batching_challenge"); - - // Construct batched commitment and batched evaluation - auto batched_commitment = transcript_commitments[0]; - auto batched_transcript_eval = transcript_evaluations[0]; - auto batching_scalar = ipa_batching_challenge; - for (size_t idx = 1; idx < transcript_commitments.size(); ++idx) { - batched_commitment = batched_commitment + transcript_commitments[idx] * batching_scalar; - batched_transcript_eval += batching_scalar * transcript_evaluations[idx]; - batching_scalar *= ipa_batching_challenge; - } - - // Construct and verify batched opening claim - OpeningClaim batched_opening_claim = { { evaluation_challenge_x, batched_transcript_eval }, - batched_commitment }; - univariate_opening_verified = PCS::reduce_verify(key->pcs_verification_key, batched_opening_claim, transcript); + auto multivariate_to_univariate_opening_claim = ZeroMorph::verify(commitments.get_unshifted(), + commitments.get_to_be_shifted(), + claimed_evaluations.get_unshifted(), + claimed_evaluations.get_shifted(), + multivariate_challenge, + key->pcs_verification_key->get_g1_identity(), + transcript); + auto hack_commitment = transcript->template receive_from_prover("Translation:hack_commitment"); + + FF evaluation_challenge_x = transcript->template get_challenge("Translation:evaluation_challenge_x"); + + // Construct the vector of commitments (needs to be vector for the batch_mul) and array of evaluations to be batched + std::vector transcript_commitments = { commitments.transcript_op, commitments.transcript_Px, + commitments.transcript_Py, commitments.transcript_z1, + commitments.transcript_z2, hack_commitment }; + + std::vector transcript_evaluations = { transcript->template receive_from_prover("Translation:op"), + transcript->template receive_from_prover("Translation:Px"), + transcript->template receive_from_prover("Translation:Py"), + transcript->template receive_from_prover("Translation:z1"), + transcript->template receive_from_prover("Translation:z2"), + transcript->template receive_from_prover( + "Translation:hack_evaluation") }; + + // Get the batching challenge for commitments and evaluations + FF ipa_batching_challenge = transcript->template get_challenge("Translation:ipa_batching_challenge"); + + // Compute the batched commitment and batched evaluation for the univariate opening claim + auto batched_transcript_eval = transcript_evaluations[0]; + auto batching_scalar = ipa_batching_challenge; + + std::vector batching_challenges = { FF::one() }; + for (size_t idx = 1; idx < transcript_commitments.size(); ++idx) { + batched_transcript_eval += batching_scalar * transcript_evaluations[idx]; + batching_challenges.emplace_back(batching_scalar); + batching_scalar *= ipa_batching_challenge; } - ASSERT(sumcheck_verified && multivariate_opening_verified && univariate_opening_verified); + auto batched_commitment = Commitment::batch_mul(transcript_commitments, batching_challenges); + + // Construct and verify the combined opening claim + OpeningClaim batched_univariate_claim = { { evaluation_challenge_x, batched_transcript_eval }, + batched_commitment }; + + std::array, 2> opening_claims = { multivariate_to_univariate_opening_claim, + batched_univariate_claim }; + + auto batched_opening_claim = + Shplonk::reduce_verification(key->pcs_verification_key->get_g1_identity(), opening_claims, transcript); + + auto batched_opening_verified = PCS::reduce_verify(key->pcs_verification_key, batched_opening_claim, transcript); + + ASSERT(sumcheck_verified && batched_opening_verified); } template class ECCVMRecursiveVerifier_>; diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/translator_prover.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/translator_prover.cpp index 7b3e8f7b74d..0d103c30291 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/translator_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/translator_prover.cpp @@ -183,8 +183,7 @@ void TranslatorProver::execute_pcs_rounds() key->polynomials.get_concatenated_constraints(), sumcheck_output.claimed_evaluations.get_concatenated_constraints(), key->polynomials.get_concatenation_groups()); - PCS::compute_opening_proof( - commitment_key, prover_opening_claim.opening_pair, prover_opening_claim.polynomial, transcript); + PCS::compute_opening_proof(commitment_key, prover_opening_claim, transcript); } HonkProof TranslatorProver::export_proof() diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/decider_prover.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/decider_prover.cpp index 1999ec92160..854ec137bf0 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/decider_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/decider_prover.cpp @@ -49,8 +49,7 @@ template void DeciderProver_::execute_pcs_rounds( sumcheck_output.challenge, commitment_key, transcript); - PCS::compute_opening_proof( - commitment_key, prover_opening_claim.opening_pair, prover_opening_claim.polynomial, transcript); + PCS::compute_opening_proof(commitment_key, prover_opening_claim, transcript); } template HonkProof DeciderProver_::export_proof() diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/avm_prover.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/avm_prover.cpp index ca3af5db56f..cd27e7479ee 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/avm_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/avm_prover.cpp @@ -974,8 +974,7 @@ void AvmProver::execute_pcs_rounds() sumcheck_output.challenge, commitment_key, transcript); - PCS::compute_opening_proof( - commitment_key, prover_opening_claim.opening_pair, prover_opening_claim.polynomial, transcript); + PCS::compute_opening_proof(commitment_key, prover_opening_claim, transcript); } HonkProof AvmProver::export_proof() From 5c3772f09e812a05882039ad888089c238f14001 Mon Sep 17 00:00:00 2001 From: esau <152162806+sklppy88@users.noreply.github.com> Date: Tue, 25 Jun 2024 16:20:19 +0200 Subject: [PATCH 4/9] feat: constrain event encryption and unify note and event emit api (#7171) resolves #6952 resolves #7160 --- boxes/boxes/react/src/contracts/src/main.nr | 6 +- boxes/boxes/vanilla/src/contracts/src/main.nr | 6 +- .../aztec/src/context/private_context.nr | 40 +------------ .../aztec-nr/aztec/src/encrypted_logs.nr | 1 + .../encrypted_event_emission.nr | 45 ++++++++++++++ .../encrypted_logs/encrypted_note_emission.nr | 4 +- .../aztec/src/encrypted_logs/incoming_body.nr | 4 ++ .../aztec/src/encrypted_logs/payload.nr | 60 ++++++++++++++++++- .../aztec/src/event/event_interface.nr | 1 + .../aztec-nr/aztec/src/oracle/logs_traits.nr | 25 +++++++- .../src/easy_private_uint.nr | 6 +- .../aztec-nr/value-note/src/utils.nr | 4 +- .../app_subscription_contract/src/main.nr | 6 +- .../contracts/card_game_contract/src/cards.nr | 4 +- .../contracts/child_contract/src/main.nr | 4 +- .../crowdfunding_contract/src/main.nr | 4 +- .../delegated_on_contract/src/main.nr | 4 +- .../docs_example_contract/src/main.nr | 14 ++--- .../ecdsa_account_contract/src/main.nr | 4 +- .../contracts/escrow_contract/src/main.nr | 4 +- .../inclusion_proofs_contract/src/main.nr | 4 +- .../pending_note_hashes_contract/src/main.nr | 20 +++---- .../schnorr_account_contract/src/main.nr | 4 +- .../static_child_contract/src/main.nr | 6 +- .../contracts/test_contract/src/main.nr | 48 ++++++++++----- .../contracts/test_log_contract/src/main.nr | 46 +++++--------- .../token_blacklist_contract/src/main.nr | 12 ++-- .../contracts/token_contract/src/main.nr | 18 +++--- .../aztec_macros/src/transforms/events.rs | 27 +++++++++ .../end-to-end/src/e2e_event_logs.test.ts | 24 -------- 30 files changed, 278 insertions(+), 177 deletions(-) create mode 100644 noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_event_emission.nr diff --git a/boxes/boxes/react/src/contracts/src/main.nr b/boxes/boxes/react/src/contracts/src/main.nr index 944c66a75e9..6cbe8afbba8 100644 --- a/boxes/boxes/react/src/contracts/src/main.nr +++ b/boxes/boxes/react/src/contracts/src/main.nr @@ -1,7 +1,7 @@ contract BoxReact { use dep::aztec::prelude::{AztecAddress, PrivateMutable, Map, NoteInterface, NoteHeader}; use dep::aztec::protocol_types::grumpkin_point::GrumpkinPoint; - use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_with_keys; + use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_note_with_keys; use dep::value_note::value_note::{ValueNote, VALUE_NOTE_LEN}; #[aztec(storage)] @@ -20,7 +20,7 @@ contract BoxReact { ) { let numbers = storage.numbers; let mut new_number = ValueNote::new(number, owner_npk_m_hash); - numbers.at(owner).initialize(&mut new_number).emit(encode_and_encrypt_with_keys(&mut context, owner_ovpk_m, owner_ivpk_m)); + numbers.at(owner).initialize(&mut new_number).emit(encode_and_encrypt_note_with_keys(&mut context, owner_ovpk_m, owner_ivpk_m)); } #[aztec(private)] @@ -33,7 +33,7 @@ contract BoxReact { ) { let numbers = storage.numbers; let mut new_number = ValueNote::new(number, owner_npk_m_hash); - numbers.at(owner).replace(&mut new_number).emit(encode_and_encrypt_with_keys(&mut context, owner_ovpk_m, owner_ivpk_m)); + numbers.at(owner).replace(&mut new_number).emit(encode_and_encrypt_note_with_keys(&mut context, owner_ovpk_m, owner_ivpk_m)); } unconstrained fn getNumber(owner: AztecAddress) -> pub ValueNote { diff --git a/boxes/boxes/vanilla/src/contracts/src/main.nr b/boxes/boxes/vanilla/src/contracts/src/main.nr index 424471e8bfd..c8090a00f88 100644 --- a/boxes/boxes/vanilla/src/contracts/src/main.nr +++ b/boxes/boxes/vanilla/src/contracts/src/main.nr @@ -1,7 +1,7 @@ contract Vanilla { use dep::aztec::prelude::{AztecAddress, PrivateMutable, Map, NoteInterface, NoteHeader}; use dep::aztec::protocol_types::grumpkin_point::GrumpkinPoint; - use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_with_keys; + use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_note_with_keys; use dep::value_note::value_note::{ValueNote, VALUE_NOTE_LEN}; #[aztec(storage)] @@ -20,7 +20,7 @@ contract Vanilla { ) { let numbers = storage.numbers; let mut new_number = ValueNote::new(number, owner_npk_m_hash); - numbers.at(owner).initialize(&mut new_number).emit(encode_and_encrypt_with_keys(&mut context, owner_ovpk_m, owner_ivpk_m)); + numbers.at(owner).initialize(&mut new_number).emit(encode_and_encrypt_note_with_keys(&mut context, owner_ovpk_m, owner_ivpk_m)); } #[aztec(private)] @@ -33,7 +33,7 @@ contract Vanilla { ) { let numbers = storage.numbers; let mut new_number = ValueNote::new(number, owner_npk_m_hash); - numbers.at(owner).replace(&mut new_number).emit(encode_and_encrypt_with_keys(&mut context, owner_ovpk_m, owner_ivpk_m)); + numbers.at(owner).replace(&mut new_number).emit(encode_and_encrypt_note_with_keys(&mut context, owner_ovpk_m, owner_ivpk_m)); } unconstrained fn getNumber(owner: AztecAddress) -> pub ValueNote { diff --git a/noir-projects/aztec-nr/aztec/src/context/private_context.nr b/noir-projects/aztec-nr/aztec/src/context/private_context.nr index 71d69f484a6..25bb33ba663 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -1,5 +1,3 @@ -use crate::encrypted_logs::{payload::compute_encrypted_note_log}; - use crate::{ context::{inputs::PrivateContextInputs, packed_returns::PackedReturns}, messaging::process_l1_to_l2_message, @@ -10,7 +8,7 @@ use crate::{ key_validation_request::get_key_validation_request, arguments, returns::pack_returns, call_private_function::call_private_function_internal, header::get_header_at, logs::{ - emit_encrypted_note_log, emit_encrypted_event_log, compute_encrypted_event_log, + emit_encrypted_note_log, emit_encrypted_event_log, emit_contract_class_unencrypted_log_private_internal, emit_unencrypted_log_private_internal }, logs_traits::{LensForEncryptedLog, ToBytesForUnencryptedLog}, @@ -276,10 +274,7 @@ impl PrivateContext { // --> might be a better approach to force devs to make a public function call that emits the log if needed then // it would be less easy to accidentally leak information. // If we decide to keep this function around would make sense to wait for traits and then merge it with emit_unencrypted_log. - pub fn emit_unencrypted_log( - &mut self, - log: T - ) where T: ToBytesForUnencryptedLog { + pub fn emit_unencrypted_log(&mut self, log: T) where T: ToBytesForUnencryptedLog { let event_selector = 5; // TODO: compute actual event selector. let contract_address = self.this_address(); let counter = self.next_counter(); @@ -313,36 +308,7 @@ impl PrivateContext { // NB: A randomness value of 0 signals that the kernels should not mask the contract address // used in siloing later on e.g. 'handshaking' contract w/ known address. - pub fn encrypt_and_emit_event( - &mut self, - randomness: Field, // Secret random value used later for masked_contract_address - event_type_id: Field, - ovpk_m: GrumpkinPoint, - ivpk_m: GrumpkinPoint, - preimage: [Field; N] - ) where [Field; N]: LensForEncryptedLog { - let ovsk_app = self.request_ovsk_app(ovpk_m.hash()); - let contract_address = self.this_address(); - - // We are currently just encrypting it unconstrained, but otherwise the same way as if it was a note. - let encrypted_log: [u8; M] = compute_encrypted_event_log( - contract_address, - randomness, - event_type_id, - ovsk_app, - ovpk_m, - ivpk_m, - preimage - ); - - self.emit_raw_event_log_with_masked_address(randomness, encrypted_log); - } - - pub fn emit_raw_event_log_with_masked_address( - &mut self, - randomness: Field, - encrypted_log: [u8; M] - ) { + pub fn emit_raw_event_log_with_masked_address(&mut self, randomness: Field, encrypted_log: [u8; M]) { let counter = self.next_counter(); let contract_address = self.this_address(); let len = encrypted_log.len() as Field + 4; diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs.nr index 2f1b93d9aad..dfc49fc0a86 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs.nr @@ -3,3 +3,4 @@ mod incoming_body; mod outgoing_body; mod payload; mod encrypted_note_emission; +mod encrypted_event_emission; diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_event_emission.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_event_emission.nr new file mode 100644 index 00000000000..a027c168515 --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_event_emission.nr @@ -0,0 +1,45 @@ +use crate::{ + context::PrivateContext, event::event_interface::EventInterface, + encrypted_logs::payload::compute_encrypted_event_log, oracle::logs_traits::LensForEncryptedEvent +}; +use dep::protocol_types::{address::AztecAddress, grumpkin_point::GrumpkinPoint}; + +fn emit_with_keys( + context: &mut PrivateContext, + randomness: Field, + event: Event, + ovpk: GrumpkinPoint, + ivpk: GrumpkinPoint +) where Event: EventInterface, [u8; NB]: LensForEncryptedEvent { + let contract_address: AztecAddress = context.this_address(); + let ovsk_app: Field = context.request_ovsk_app(ovpk.hash()); + + let encrypted_log: [u8; OB] = compute_encrypted_event_log(contract_address, randomness, ovsk_app, ovpk, ivpk, event); + + context.emit_raw_event_log_with_masked_address(randomness, encrypted_log); +} + +pub fn encode_and_encrypt_event( + context: &mut PrivateContext, + randomness: Field, + ov: AztecAddress, + iv: AztecAddress +) -> fn[(&mut PrivateContext, AztecAddress, AztecAddress, Field)](Event) -> () where Event: EventInterface, [u8; NB]: LensForEncryptedEvent { + | e: Event | { + let header = context.get_header(); + let ovpk = header.get_ovpk_m(context, ov); + let ivpk = header.get_ivpk_m(context, iv); + emit_with_keys(context, randomness, e, ovpk, ivpk); + } +} + +pub fn encode_and_encrypt_event_with_keys( + context: &mut PrivateContext, + randomness: Field, + ovpk: GrumpkinPoint, + ivpk: GrumpkinPoint +) -> fn[(&mut PrivateContext, Field, GrumpkinPoint, GrumpkinPoint)](Event) -> () where Event: EventInterface, [u8; NB]: LensForEncryptedEvent { + | e: Event | { + emit_with_keys(context, randomness, e, ovpk, ivpk); + } +} diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_note_emission.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_note_emission.nr index 12994a4b4fa..5b66e2e8027 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_note_emission.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_note_emission.nr @@ -33,7 +33,7 @@ fn emit_with_keys( context.emit_raw_note_log(note_hash_counter, encrypted_log); } -pub fn encode_and_encrypt( +pub fn encode_and_encrypt_note( context: &mut PrivateContext, ov: AztecAddress, iv: AztecAddress @@ -46,7 +46,7 @@ pub fn encode_and_encrypt( } } -pub fn encode_and_encrypt_with_keys( +pub fn encode_and_encrypt_note_with_keys( context: &mut PrivateContext, ovpk: GrumpkinPoint, ivpk: GrumpkinPoint diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr index ccb1abdb764..e95771baa30 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr @@ -205,6 +205,10 @@ mod test { buffer } + + fn emit(self, _emit: fn[Env](Self) -> ()) { + _emit(self); + } } #[test] diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr index 273142bca9d..16454145ec4 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr @@ -7,6 +7,7 @@ use dep::std::{embedded_curve_ops::{embedded_curve_add, EmbeddedCurvePoint}, fie use crate::oracle::unsafe_rand::unsafe_rand; +use crate::event::event_interface::EventInterface; use crate::note::note_interface::NoteInterface; use crate::encrypted_logs::{ @@ -14,6 +15,63 @@ use crate::encrypted_logs::{ outgoing_body::EncryptedLogOutgoingBody }; +pub fn compute_encrypted_event_log( + contract_address: AztecAddress, + randomness: Field, + ovsk_app: Field, + ovpk: GrumpkinPoint, + ivpk: GrumpkinPoint, + event: Event +) -> [u8; OB] where Event: EventInterface { + // @todo Need to draw randomness from the full domain of Fq not only Fr + let eph_sk: GrumpkinPrivateKey = fr_to_private_key(unsafe_rand()); + let eph_pk = eph_sk.derive_public_key(); + + // TODO: (#7177) This value needs to be populated! + let recipient = AztecAddress::from_field(0); + + let ivpk_app = compute_ivpk_app(ivpk, contract_address); + + let header = EncryptedLogHeader::new(contract_address); + + let incoming_header_ciphertext: [u8; 48] = header.compute_ciphertext(eph_sk, ivpk); + let outgoing_Header_ciphertext: [u8; 48] = header.compute_ciphertext(eph_sk, ovpk); + let incoming_body_ciphertext = EncryptedLogIncomingBody::from_event(event, randomness).compute_ciphertext(eph_sk, ivpk_app); + let outgoing_body_ciphertext: [u8; 176] = EncryptedLogOutgoingBody::new(eph_sk, recipient, ivpk_app).compute_ciphertext(fr_to_private_key(ovsk_app), eph_pk); + + let mut encrypted_bytes: [u8; OB] = [0; OB]; + // @todo We ignore the tags for now + + let eph_pk_bytes = eph_pk.to_be_bytes(); + for i in 0..64 { + encrypted_bytes[64 + i] = eph_pk_bytes[i]; + } + for i in 0..48 { + encrypted_bytes[128 + i] = incoming_header_ciphertext[i]; + encrypted_bytes[176 + i] = outgoing_Header_ciphertext[i]; + } + for i in 0..176 { + encrypted_bytes[224 + i] = outgoing_body_ciphertext[i]; + } + // Then we fill in the rest as the incoming body ciphertext + let size = OB - 400; + assert_eq(size, incoming_body_ciphertext.len(), "ciphertext length mismatch"); + for i in 0..size { + encrypted_bytes[400 + i] = incoming_body_ciphertext[i]; + } + + // Current unoptimized size of the encrypted log + // incoming_tag (32 bytes) + // outgoing_tag (32 bytes) + // eph_pk (64 bytes) + // incoming_header (48 bytes) + // outgoing_header (48 bytes) + // outgoing_body (176 bytes) + // incoming_body_fixed (64 bytes) + // incoming_body_variable (N * 32 bytes + 16 bytes padding) + encrypted_bytes +} + pub fn compute_encrypted_note_log( contract_address: AztecAddress, storage_slot: Field, @@ -26,7 +84,7 @@ pub fn compute_encrypted_note_log( let eph_sk: GrumpkinPrivateKey = fr_to_private_key(unsafe_rand()); let eph_pk = eph_sk.derive_public_key(); - // @todo This value needs to be populated! + // TODO: (#7177) This value needs to be populated! let recipient = AztecAddress::from_field(0); let ivpk_app = compute_ivpk_app(ivpk, contract_address); diff --git a/noir-projects/aztec-nr/aztec/src/event/event_interface.nr b/noir-projects/aztec-nr/aztec/src/event/event_interface.nr index 971aa460fd6..4505dedd1ab 100644 --- a/noir-projects/aztec-nr/aztec/src/event/event_interface.nr +++ b/noir-projects/aztec-nr/aztec/src/event/event_interface.nr @@ -6,4 +6,5 @@ trait EventInterface { fn private_to_be_bytes(self, randomness: Field) -> [u8; NB]; fn to_be_bytes(self) -> [u8; MB]; fn get_event_type_id() -> EventSelector; + fn emit(self, _emit: fn[Env](Self) -> ()); } diff --git a/noir-projects/aztec-nr/aztec/src/oracle/logs_traits.nr b/noir-projects/aztec-nr/aztec/src/oracle/logs_traits.nr index c6632f5a4d3..899f7f0d0d1 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/logs_traits.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/logs_traits.nr @@ -31,7 +31,6 @@ impl LensForEncryptedLog<3, 576> for [Field; 3] { impl LensForEncryptedLog<4, 608> for [Field; 4] { fn output_fields(self) -> [Field; 4] {[self[0]; 4]} fn output_bytes(self) -> [u8; 608] {[self[0] as u8; 608]} - } impl LensForEncryptedLog<5, 640> for [Field; 5] { fn output_fields(self) -> [Field; 5] {[self[0]; 5]} @@ -40,7 +39,31 @@ impl LensForEncryptedLog<5, 640> for [Field; 5] { impl LensForEncryptedLog<6, 672> for [Field; 6] { fn output_fields(self) -> [Field; 6] {[self[0]; 6]} fn output_bytes(self) -> [u8; 672] {[self[0] as u8; 672]} +} +trait LensForEncryptedEvent { + // N = event preimage input in bytes + // M = encryption output len in bytes (= 480 + M) + fn output(self: [u8; N]) -> [u8; M]; +} + +impl LensForEncryptedEvent<96, 512> for [u8; 96] { + fn output(self) -> [u8; 512] {[self[0] as u8; 512]} +} +impl LensForEncryptedEvent<128, 544> for [u8; 128] { + fn output(self) -> [u8; 544] {[self[0] as u8; 544]} +} +impl LensForEncryptedEvent<160, 576> for [u8; 160] { + fn output(self) -> [u8; 576] {[self[0] as u8; 576]} +} +impl LensForEncryptedEvent<192, 608> for [u8; 192] { + fn output(self) -> [u8; 608] {[self[0] as u8; 608]} +} +impl LensForEncryptedEvent<224, 640> for [u8; 224] { + fn output(self) -> [u8; 640] {[self[0] as u8; 640]} +} +impl LensForEncryptedEvent<256, 672> for [u8; 256] { + fn output(self) -> [u8; 672] {[self[0] as u8; 672]} } // This trait defines the length of the inputs in bytes to diff --git a/noir-projects/aztec-nr/easy-private-state/src/easy_private_uint.nr b/noir-projects/aztec-nr/easy-private-state/src/easy_private_uint.nr index ff23dd0e159..1a84b86acba 100644 --- a/noir-projects/aztec-nr/easy-private-state/src/easy_private_uint.nr +++ b/noir-projects/aztec-nr/easy-private-state/src/easy_private_uint.nr @@ -1,7 +1,7 @@ use dep::aztec::{ context::PrivateContext, protocol_types::{address::AztecAddress, grumpkin_point::GrumpkinPoint}, note::note_getter_options::NoteGetterOptions, state_vars::PrivateSet, - encrypted_logs::encrypted_note_emission::encode_and_encrypt + encrypted_logs::encrypted_note_emission::encode_and_encrypt_note }; use dep::value_note::{filter::filter_notes_min_sum, value_note::ValueNote}; @@ -30,7 +30,7 @@ impl EasyPrivateUint<&mut PrivateContext> { // Insert the new note to the owner's set of notes. // docs:start:insert - self.set.insert(&mut addend_note).emit(encode_and_encrypt(self.context, outgoing_viewer, owner)); + self.set.insert(&mut addend_note).emit(encode_and_encrypt_note(self.context, outgoing_viewer, owner)); // docs:end:insert } @@ -63,6 +63,6 @@ impl EasyPrivateUint<&mut PrivateContext> { // Creates change note for the owner. let result_value = minuend - subtrahend; let mut result_note = ValueNote::new(result_value as Field, owner_npk_m_hash); - self.set.insert(&mut result_note).emit(encode_and_encrypt(self.context, outgoing_viewer, owner)); + self.set.insert(&mut result_note).emit(encode_and_encrypt_note(self.context, outgoing_viewer, owner)); } } diff --git a/noir-projects/aztec-nr/value-note/src/utils.nr b/noir-projects/aztec-nr/value-note/src/utils.nr index 927f8325281..44094999c76 100644 --- a/noir-projects/aztec-nr/value-note/src/utils.nr +++ b/noir-projects/aztec-nr/value-note/src/utils.nr @@ -1,7 +1,7 @@ use dep::aztec::prelude::{AztecAddress, PrivateContext, PrivateSet, NoteGetterOptions}; use dep::aztec::note::note_getter_options::SortOrder; use dep::aztec::protocol_types::grumpkin_point::GrumpkinPoint; -use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt; +use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_note; use crate::{filter::filter_notes_min_sum, value_note::{ValueNote, VALUE_NOTE_LEN, VALUE_NOTE_BYTES_LEN}}; // Sort the note values (0th field) in descending order. @@ -23,7 +23,7 @@ pub fn increment( let mut note = ValueNote::new(amount, recipient_npk_m_hash); // Insert the new note to the owner's set of notes and emit the log if value is non-zero. - balance.insert(&mut note).emit(encode_and_encrypt(balance.context, outgoing_viewer, recipient)); + balance.insert(&mut note).emit(encode_and_encrypt_note(balance.context, outgoing_viewer, recipient)); } // Find some of the `owner`'s notes whose values add up to the `amount`. diff --git a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr index df46548453e..d9c5e6e6b1d 100644 --- a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/main.nr @@ -9,7 +9,7 @@ contract AppSubscription { AztecAddress, FunctionSelector, PrivateContext, NoteHeader, Map, PrivateMutable, PublicMutable, SharedImmutable }, - encrypted_logs::encrypted_note_emission::encode_and_encrypt, + encrypted_logs::encrypted_note_emission::encode_and_encrypt_note, protocol_types::{traits::is_empty, grumpkin_point::GrumpkinPoint} }, authwit::{auth_witness::get_auth_witness, auth::assert_current_call_valid_authwit}, @@ -45,7 +45,7 @@ contract AppSubscription { // We are emitting both the outgoing and the incoming logs to the subscriber here because passing a separate // outgoing_viewer arg to entrypoint function is impractical and the outgoing are not so valuable here. - storage.subscriptions.at(user_address).replace(&mut note).emit(encode_and_encrypt(&mut context, user_address, user_address)); + storage.subscriptions.at(user_address).replace(&mut note).emit(encode_and_encrypt_note(&mut context, user_address, user_address)); context.set_as_fee_payer(); @@ -116,7 +116,7 @@ contract AppSubscription { let subscriber_npk_m_hash = header.get_npk_m_hash(&mut context, subscriber_address); let mut subscription_note = SubscriptionNote::new(subscriber_npk_m_hash, expiry_block_number, tx_count); - storage.subscriptions.at(subscriber_address).initialize_or_replace(&mut subscription_note).emit(encode_and_encrypt(&mut context, context.msg_sender(), subscriber_address)); + storage.subscriptions.at(subscriber_address).initialize_or_replace(&mut subscription_note).emit(encode_and_encrypt_note(&mut context, context.msg_sender(), subscriber_address)); } unconstrained fn is_initialized(subscriber_address: AztecAddress) -> pub bool { diff --git a/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr b/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr index e8ae03dd953..7065267bc2e 100644 --- a/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr +++ b/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr @@ -6,7 +6,7 @@ use dep::aztec::{ traits::{ToField, Serialize, FromField}, grumpkin_point::GrumpkinPoint, constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL }, - encrypted_logs::encrypted_note_emission::encode_and_encrypt_with_keys, + encrypted_logs::encrypted_note_emission::encode_and_encrypt_note_with_keys, note::note_getter::view_notes, state_vars::PrivateSet, note::constants::MAX_NOTES_PER_PAGE }; use dep::std; @@ -114,7 +114,7 @@ impl Deck<&mut PrivateContext> { let mut inserted_cards = &[]; for card in cards { let mut card_note = CardNote::from_card(card, owner_npk_m_hash); - self.set.insert(&mut card_note.note).emit(encode_and_encrypt_with_keys(self.set.context, msg_sender_ovpk_m, owner_ivpk_m)); + self.set.insert(&mut card_note.note).emit(encode_and_encrypt_note_with_keys(self.set.context, msg_sender_ovpk_m, owner_ivpk_m)); inserted_cards = inserted_cards.push_back(card_note); } diff --git a/noir-projects/noir-contracts/contracts/child_contract/src/main.nr b/noir-projects/noir-contracts/contracts/child_contract/src/main.nr index ab9483cce2a..bd7220461a1 100644 --- a/noir-projects/noir-contracts/contracts/child_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/child_contract/src/main.nr @@ -6,7 +6,7 @@ contract Child { context::gas::GasOpts, protocol_types::{abis::call_context::CallContext, grumpkin_point::GrumpkinPoint}, note::{note_getter_options::NoteGetterOptions, note_header::NoteHeader}, - encrypted_logs::encrypted_note_emission::encode_and_encrypt + encrypted_logs::encrypted_note_emission::encode_and_encrypt_note }; use dep::value_note::value_note::ValueNote; @@ -56,7 +56,7 @@ contract Child { let owner_npk_m_hash = header.get_npk_m_hash(&mut context, owner); let mut note = ValueNote::new(new_value, owner_npk_m_hash); - storage.a_map_with_private_values.at(owner).insert(&mut note).emit(encode_and_encrypt(&mut context, owner, owner)); + storage.a_map_with_private_values.at(owner).insert(&mut note).emit(encode_and_encrypt_note(&mut context, owner, owner)); new_value } diff --git a/noir-projects/noir-contracts/contracts/crowdfunding_contract/src/main.nr b/noir-projects/noir-contracts/contracts/crowdfunding_contract/src/main.nr index 7ced056ff21..9e43661a329 100644 --- a/noir-projects/noir-contracts/contracts/crowdfunding_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/crowdfunding_contract/src/main.nr @@ -8,7 +8,7 @@ contract Crowdfunding { abis::function_selector::FunctionSelector, address::AztecAddress, traits::Serialize, grumpkin_point::GrumpkinPoint }, - encrypted_logs::encrypted_note_emission::encode_and_encrypt, + encrypted_logs::encrypted_note_emission::encode_and_encrypt_note, state_vars::{PrivateSet, PublicImmutable, SharedImmutable} }; use dep::value_note::value_note::ValueNote; @@ -81,7 +81,7 @@ contract Crowdfunding { // contract by proving that the hash of this note exists in the note hash tree. let donor_npk_m_hash = header.get_npk_m_hash(&mut context, donor); let mut note = ValueNote::new(amount as Field, donor_npk_m_hash); - storage.donation_receipts.insert(&mut note).emit(encode_and_encrypt(&mut context, donor, donor)); + storage.donation_receipts.insert(&mut note).emit(encode_and_encrypt_note(&mut context, donor, donor)); } // docs:end:donate diff --git a/noir-projects/noir-contracts/contracts/delegated_on_contract/src/main.nr b/noir-projects/noir-contracts/contracts/delegated_on_contract/src/main.nr index da7a24ce09b..7a848446055 100644 --- a/noir-projects/noir-contracts/contracts/delegated_on_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/delegated_on_contract/src/main.nr @@ -4,7 +4,7 @@ contract DelegatedOn { AztecAddress, FunctionSelector, NoteHeader, NoteGetterOptions, NoteViewerOptions, PublicMutable, PrivateSet, PrivateContext, Map }; - use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt; + use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_note; use dep::aztec::{protocol_types::grumpkin_point::GrumpkinPoint}; use dep::value_note::value_note::ValueNote; @@ -20,7 +20,7 @@ contract DelegatedOn { let owner_npk_m_hash = header.get_npk_m_hash(&mut context, owner); let mut note = ValueNote::new(new_value, owner_npk_m_hash); - storage.a_map_with_private_values.at(owner).insert(&mut note).emit(encode_and_encrypt(&mut context, context.msg_sender(), owner)); + storage.a_map_with_private_values.at(owner).insert(&mut note).emit(encode_and_encrypt_note(&mut context, context.msg_sender(), owner)); new_value } diff --git a/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr b/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr index 493bead7cab..22242e9404a 100644 --- a/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr @@ -18,7 +18,7 @@ contract DocsExample { PrivateContext, Map, PublicMutable, PublicImmutable, PrivateMutable, PrivateImmutable, PrivateSet, SharedImmutable, Deserialize }; - use dep::aztec::encrypted_logs::encrypted_note_emission::{encode_and_encrypt, encode_and_encrypt_with_keys}; + use dep::aztec::encrypted_logs::encrypted_note_emission::{encode_and_encrypt_note, encode_and_encrypt_note_with_keys}; use dep::aztec::note::note_getter_options::Comparator; use dep::aztec::protocol_types::grumpkin_point::GrumpkinPoint; // how to import methods from other files/folders within your workspace @@ -173,7 +173,7 @@ contract DocsExample { let msg_sender_npk_m_hash = header.get_npk_m_hash(&mut context, context.msg_sender()); let mut new_card = CardNote::new(points, randomness, msg_sender_npk_m_hash); - storage.private_immutable.initialize(&mut new_card).emit(encode_and_encrypt(&mut context, context.msg_sender(), context.msg_sender())); + storage.private_immutable.initialize(&mut new_card).emit(encode_and_encrypt_note(&mut context, context.msg_sender(), context.msg_sender())); } // docs:end:initialize-private-mutable @@ -185,7 +185,7 @@ contract DocsExample { let mut legendary_card = CardNote::new(points, randomness, msg_sender_npk_m_hash); // create and broadcast note - storage.legendary_card.initialize(&mut legendary_card).emit(encode_and_encrypt(&mut context, context.msg_sender(), context.msg_sender())); + storage.legendary_card.initialize(&mut legendary_card).emit(encode_and_encrypt_note(&mut context, context.msg_sender(), context.msg_sender())); } #[aztec(private)] @@ -197,7 +197,7 @@ contract DocsExample { for i in 0..amounts.len() { let mut note = CardNote::new(amounts[i], 1, msg_sender_npk_m_hash); - storage.set.insert(&mut note).emit(encode_and_encrypt_with_keys(&mut context, msg_sender_ovpk_m, msg_sender_ivpk_m)); + storage.set.insert(&mut note).emit(encode_and_encrypt_note_with_keys(&mut context, msg_sender_ovpk_m, msg_sender_ivpk_m)); } } @@ -207,7 +207,7 @@ contract DocsExample { let msg_sender_npk_m_hash = header.get_npk_m_hash(&mut context, context.msg_sender()); let mut note = CardNote::new(amount, randomness, msg_sender_npk_m_hash); - storage.set.insert(&mut note).emit(encode_and_encrypt(&mut context, context.msg_sender(), context.msg_sender())); + storage.set.insert(&mut note).emit(encode_and_encrypt_note(&mut context, context.msg_sender(), context.msg_sender())); } // docs:start:state_vars-NoteGetterOptionsComparatorExampleNoir @@ -229,7 +229,7 @@ contract DocsExample { let msg_sender_npk_m_hash = header.get_npk_m_hash(&mut context, context.msg_sender()); let mut new_card = CardNote::new(points, randomness, msg_sender_npk_m_hash); - storage.legendary_card.replace(&mut new_card).emit(encode_and_encrypt(&mut context, context.msg_sender(), context.msg_sender())); + storage.legendary_card.replace(&mut new_card).emit(encode_and_encrypt_note(&mut context, context.msg_sender(), context.msg_sender())); DocsExample::at(context.this_address()).update_leader(context.msg_sender(), points).enqueue(&mut context); } @@ -250,7 +250,7 @@ contract DocsExample { let mut new_card = CardNote::new(points, card.randomness, msg_sender_npk_m_hash); // docs:start:state_vars-PrivateMutableReplace - storage.legendary_card.replace(&mut new_card).emit(encode_and_encrypt(&mut context, context.msg_sender(), context.msg_sender())); + storage.legendary_card.replace(&mut new_card).emit(encode_and_encrypt_note(&mut context, context.msg_sender(), context.msg_sender())); // docs:end:state_vars-PrivateMutableReplace DocsExample::at(context.this_address()).update_leader(context.msg_sender(), points).enqueue(&mut context); diff --git a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr index 160776cc861..f4495e48217 100644 --- a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr @@ -4,7 +4,7 @@ mod ecdsa_public_key_note; // The signing key is stored in an immutable private note and should be different from the signing key. contract EcdsaAccount { use dep::aztec::prelude::{AztecAddress, FunctionSelector, NoteHeader, NoteGetterOptions, PrivateContext, PrivateImmutable}; - use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt; + use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_note; use dep::aztec::protocol_types::abis::call_context::CallContext; use dep::std; @@ -33,7 +33,7 @@ contract EcdsaAccount { // important. let mut pub_key_note = EcdsaPublicKeyNote::new(signing_pub_key_x, signing_pub_key_y, this_npk_m_hash); - storage.public_key.initialize(&mut pub_key_note).emit(encode_and_encrypt(&mut context, this, this)); + storage.public_key.initialize(&mut pub_key_note).emit(encode_and_encrypt_note(&mut context, this, this)); } // Note: If you globally change the entrypoint signature don't forget to update default_entrypoint.ts diff --git a/noir-projects/noir-contracts/contracts/escrow_contract/src/main.nr b/noir-projects/noir-contracts/contracts/escrow_contract/src/main.nr index 661283b1f85..27159437c1b 100644 --- a/noir-projects/noir-contracts/contracts/escrow_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/escrow_contract/src/main.nr @@ -1,7 +1,7 @@ // Sample escrow contract that stores a balance of a private token on behalf of an owner. contract Escrow { use dep::aztec::prelude::{AztecAddress, EthAddress, FunctionSelector, NoteHeader, PrivateContext, PrivateImmutable}; - use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt; + use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_note; use dep::address_note::address_note::AddressNote; @@ -20,7 +20,7 @@ contract Escrow { let owner_npk_m_hash = header.get_npk_m_hash(&mut context, owner); let mut note = AddressNote::new(owner, owner_npk_m_hash); - storage.owner.initialize(&mut note).emit(encode_and_encrypt(&mut context, context.msg_sender(), owner)); + storage.owner.initialize(&mut note).emit(encode_and_encrypt_note(&mut context, context.msg_sender(), owner)); } // Withdraws balance. Requires that msg.sender is the owner. diff --git a/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr b/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr index 372b11fbd5b..71cc8d803f7 100644 --- a/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr @@ -4,7 +4,7 @@ contract InclusionProofs { AztecAddress, EthAddress, FunctionSelector, NoteHeader, NoteGetterOptions, PrivateContext, Map, PrivateSet, PublicMutable }; - use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt; + use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_note; use dep::aztec::protocol_types::{grumpkin_point::GrumpkinPoint, contract_class_id::ContractClassId, header::Header}; use dep::aztec::{note::note_getter_options::NoteStatus}; @@ -36,7 +36,7 @@ contract InclusionProofs { let owner_npk_m_hash = header.get_npk_m_hash(&mut context, owner); let mut note = ValueNote::new(value, owner_npk_m_hash); - owner_private_values.insert(&mut note).emit(encode_and_encrypt(&mut context, context.msg_sender(), owner)); + owner_private_values.insert(&mut note).emit(encode_and_encrypt_note(&mut context, context.msg_sender(), owner)); } // docs:end:create_note diff --git a/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr b/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr index 6f5076d4ddf..c35c36d6eb5 100644 --- a/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr @@ -8,7 +8,7 @@ contract PendingNoteHashes { use dep::value_note::{balance_utils, filter::filter_notes_min_sum, value_note::{VALUE_NOTE_LEN, ValueNote}}; use dep::aztec::protocol_types::grumpkin_point::GrumpkinPoint; use dep::aztec::protocol_types::constants::{MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, MAX_NEW_NOTE_HASHES_PER_CALL}; - use dep::aztec::encrypted_logs::encrypted_note_emission::{encode_and_encrypt, encode_and_encrypt_with_keys}; + use dep::aztec::encrypted_logs::encrypted_note_emission::{encode_and_encrypt_note, encode_and_encrypt_note_with_keys}; use dep::aztec::note::note_emission::NoteEmission; #[aztec(storage)] @@ -36,7 +36,7 @@ contract PendingNoteHashes { let mut note = ValueNote::new(amount, owner_npk_m_hash); // Insert note - owner_balance.insert(&mut note).emit(encode_and_encrypt(&mut context, outgoing_viewer, owner)); + owner_balance.insert(&mut note).emit(encode_and_encrypt_note(&mut context, outgoing_viewer, owner)); let options = NoteGetterOptions::with_filter(filter_notes_min_sum, amount); // get note inserted above @@ -71,7 +71,7 @@ contract PendingNoteHashes { // Insert note let mut note = ValueNote::new(amount, owner_npk_m_hash); - owner_balance.insert(&mut note).emit(encode_and_encrypt(&mut context, context.msg_sender(), owner)); + owner_balance.insert(&mut note).emit(encode_and_encrypt_note(&mut context, context.msg_sender(), owner)); 0 } @@ -91,7 +91,7 @@ contract PendingNoteHashes { let mut note = ValueNote::new(amount, owner_npk_m_hash); // Insert note - owner_balance.insert(&mut note).emit(encode_and_encrypt(&mut context, outgoing_viewer, owner)); + owner_balance.insert(&mut note).emit(encode_and_encrypt_note(&mut context, outgoing_viewer, owner)); } // Nested/inner function to create and insert a note @@ -112,7 +112,7 @@ contract PendingNoteHashes { note.randomness = 2; // Insert note - owner_balance.insert(&mut note).emit(encode_and_encrypt(&mut context, outgoing_viewer, owner)); + owner_balance.insert(&mut note).emit(encode_and_encrypt_note(&mut context, outgoing_viewer, owner)); } // Nested/inner function to create and insert a note @@ -129,10 +129,10 @@ contract PendingNoteHashes { // Insert note let emission = owner_balance.insert(&mut note); - emission.emit(encode_and_encrypt(&mut context, outgoing_viewer, owner)); + emission.emit(encode_and_encrypt_note(&mut context, outgoing_viewer, owner)); // Emit note again - emission.emit(encode_and_encrypt(&mut context, outgoing_viewer, owner)); + emission.emit(encode_and_encrypt_note(&mut context, outgoing_viewer, owner)); } // Nested/inner function to get a note and confirm it matches the expected value @@ -351,7 +351,7 @@ contract PendingNoteHashes { let owner_ivpk_m = header.get_ivpk_m(&mut context, owner); let mut good_note = ValueNote::new(10, owner_npk_m_hash); // Insert good note with real log - owner_balance.insert(&mut good_note).emit(encode_and_encrypt(&mut context, outgoing_viewer, owner)); + owner_balance.insert(&mut good_note).emit(encode_and_encrypt_note(&mut context, outgoing_viewer, owner)); // We will emit a note log with an incorrect preimage to ensure the pxe throws // This note has not been inserted... @@ -360,7 +360,7 @@ contract PendingNoteHashes { let existing_note_header = good_note.get_header(); bad_note.set_header(existing_note_header); - NoteEmission::new(bad_note).emit(encode_and_encrypt_with_keys(&mut context, outgoing_viewer_ovpk_m, owner_ivpk_m)); + NoteEmission::new(bad_note).emit(encode_and_encrypt_note_with_keys(&mut context, outgoing_viewer_ovpk_m, owner_ivpk_m)); } #[contract_library_method] @@ -378,7 +378,7 @@ contract PendingNoteHashes { for i in 0..max_notes_per_call() { let mut note = ValueNote::new(i as Field, owner_npk_m_hash); - owner_balance.insert(&mut note).emit(encode_and_encrypt_with_keys(context, outgoing_viewer_ovpk_m, owner_ivpk_m)); + owner_balance.insert(&mut note).emit(encode_and_encrypt_note_with_keys(context, outgoing_viewer_ovpk_m, owner_ivpk_m)); } } diff --git a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr index f5661c26853..8632a64bc62 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr @@ -6,7 +6,7 @@ contract SchnorrAccount { use dep::std; use dep::aztec::prelude::{AztecAddress, FunctionSelector, NoteHeader, PrivateContext, PrivateImmutable}; - use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt; + use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_note; use dep::authwit::{ entrypoint::{app::AppPayload, fee::FeePayload}, account::AccountActions, auth_witness::get_auth_witness, auth::{compute_authwit_nullifier, compute_outer_authwit_hash} @@ -36,7 +36,7 @@ contract SchnorrAccount { // docs:start:initialize let mut pub_key_note = PublicKeyNote::new(signing_pub_key_x, signing_pub_key_y, this_npk_m_hash); - storage.signing_public_key.initialize(&mut pub_key_note).emit(encode_and_encrypt(&mut context, this, this)); + storage.signing_public_key.initialize(&mut pub_key_note).emit(encode_and_encrypt_note(&mut context, this, this)); // docs:end:initialize } diff --git a/noir-projects/noir-contracts/contracts/static_child_contract/src/main.nr b/noir-projects/noir-contracts/contracts/static_child_contract/src/main.nr index 9ed71ce290e..e0d113569fe 100644 --- a/noir-projects/noir-contracts/contracts/static_child_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/static_child_contract/src/main.nr @@ -5,7 +5,7 @@ contract StaticChild { use dep::aztec::{ context::{PublicContext, gas::GasOpts}, protocol_types::{abis::{call_context::CallContext}}, note::{note_getter_options::NoteGetterOptions, note_header::NoteHeader}, - encrypted_logs::encrypted_note_emission::encode_and_encrypt + encrypted_logs::encrypted_note_emission::encode_and_encrypt_note }; use dep::value_note::value_note::ValueNote; @@ -43,7 +43,7 @@ contract StaticChild { let header = context.get_header(); let owner_npk_m_hash = header.get_npk_m_hash(&mut context, owner); let mut note = ValueNote::new(new_value, owner_npk_m_hash); - storage.a_private_value.insert(&mut note).emit(encode_and_encrypt(&mut context, context.msg_sender(), owner)); + storage.a_private_value.insert(&mut note).emit(encode_and_encrypt_note(&mut context, context.msg_sender(), owner)); new_value } @@ -57,7 +57,7 @@ contract StaticChild { let header = context.get_header(); let owner_npk_m_hash = header.get_npk_m_hash(&mut context, owner); let mut note = ValueNote::new(new_value, owner_npk_m_hash); - storage.a_private_value.insert(&mut note).emit(encode_and_encrypt(&mut context, outgoing_viewer, owner)); + storage.a_private_value.insert(&mut note).emit(encode_and_encrypt_note(&mut context, outgoing_viewer, owner)); new_value } diff --git a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr index 0ec38297670..eb34674b095 100644 --- a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr @@ -7,7 +7,8 @@ contract Test { AztecAddress, EthAddress, FunctionSelector, NoteHeader, NoteGetterOptions, NoteViewerOptions, PrivateContext, PrivateImmutable, PrivateSet, SharedImmutable }; - use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt; + use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_note; + use dep::aztec::encrypted_logs::encrypted_event_emission::encode_and_encrypt_event_with_keys; use dep::aztec::protocol_types::{ abis::private_circuit_public_inputs::PrivateCircuitPublicInputs, @@ -41,7 +42,11 @@ contract Test { #[aztec(event)] struct ExampleEvent { - value: Field, + value0: Field, + value1: Field, + value2: Field, + value3: Field, + value4: Field, } #[aztec(storage)] @@ -97,7 +102,7 @@ contract Test { let owner_npk_m_hash = header.get_npk_m_hash(&mut context, owner); let mut note = ValueNote::new(value, owner_npk_m_hash); - create_note(&mut context, storage_slot, &mut note).emit(encode_and_encrypt(&mut context, outgoing_viewer, owner)); + create_note(&mut context, storage_slot, &mut note).emit(encode_and_encrypt_note(&mut context, outgoing_viewer, owner)); } #[aztec(private)] @@ -262,23 +267,34 @@ contract Test { let header = context.get_header(); let outgoing_viewer_ovpk_m = header.get_ovpk_m(&mut context, outgoing_viewer); let owner_ivpk_m = header.get_ivpk_m(&mut context, owner); - context.encrypt_and_emit_event( - 5, // testing only - this should be a secret random value to salt the addr - 1, - outgoing_viewer_ovpk_m, - owner_ivpk_m, - fields + + let event = ExampleEvent { value0: fields[0], value1: fields[1], value2: fields[2], value3: fields[3], value4: fields[4] }; + + event.emit( + encode_and_encrypt_event_with_keys( + &mut context, + // testing only - a secret random value is passed in here to salt / mask the address + 5, + outgoing_viewer_ovpk_m, + owner_ivpk_m + ) ); + // this contract has reached max number of functions, so using this one fn // to test nested and non nested encrypted logs if nest { Test::at(context.this_address()).emit_array_as_encrypted_log([0, 0, 0, 0, 0], owner, outgoing_viewer, false).call(&mut context); - context.encrypt_and_emit_event( - 0, // testing only - this signals to the kerels to not mask the address - 1, - outgoing_viewer_ovpk_m, - owner_ivpk_m, - [1, 2, 3, 4, 5] + + let otherEvent = ExampleEvent { value0: 1, value1: 2, value2: 3, value3: 4, value4: 5 }; + + otherEvent.emit( + encode_and_encrypt_event_with_keys( + &mut context, + // testing only - a randomness of 0 signals the kerels to not mask the address + 0, + outgoing_viewer_ovpk_m, + owner_ivpk_m + ) ); } } @@ -312,7 +328,7 @@ contract Test { let owner_npk_m_hash = header.get_npk_m_hash(&mut context, owner); let mut note = ValueNote::new(value + 1, owner_npk_m_hash); - create_note(&mut context, storage_slot, &mut note).emit(encode_and_encrypt(&mut context, context.msg_sender(), owner)); + create_note(&mut context, storage_slot, &mut note).emit(encode_and_encrypt_note(&mut context, context.msg_sender(), owner)); storage_slot += 1; Test::at(context.this_address()).call_create_note(value + 2, owner, outgoing_viewer, storage_slot).call(&mut context); } diff --git a/noir-projects/noir-contracts/contracts/test_log_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test_log_contract/src/main.nr index 9eb045f0f2f..f42cb2ffd7a 100644 --- a/noir-projects/noir-contracts/contracts/test_log_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test_log_contract/src/main.nr @@ -4,6 +4,7 @@ contract TestLog { use dep::value_note::value_note::ValueNote; use dep::aztec::encrypted_logs::incoming_body::EncryptedLogIncomingBody; use dep::aztec::event::event_interface::EventInterface; + use dep::aztec::encrypted_logs::encrypted_event_emission::{encode_and_encrypt_event, encode_and_encrypt_event_with_keys}; #[aztec(event)] struct ExampleEvent0 { @@ -39,45 +40,28 @@ contract TestLog { ).compute_ciphertext(secret, point).as_array() } - #[aztec(private)] - fn emit_encrypted_log(randomness: Field, event_type_id: Field, preimage: [Field; 6]) { - let header = context.get_header(); - let msg_sender_ivpk_m = header.get_ivpk_m(&mut context, context.msg_sender()); - let msg_sender_ovpk_m = header.get_ovpk_m(&mut context, context.msg_sender()); - - context.encrypt_and_emit_event( - randomness, - event_type_id, - msg_sender_ovpk_m, - msg_sender_ivpk_m, - preimage - ); - } - #[aztec(private)] fn emit_encrypted_events(randomness: [Field; 2], preimages: [Field; 4]) { - let header = context.get_header(); - let msg_sender_ivpk_m = header.get_ivpk_m(&mut context, context.msg_sender()); - let msg_sender_ovpk_m = header.get_ovpk_m(&mut context, context.msg_sender()); - let event0 = ExampleEvent0 { value0: preimages[0], value1: preimages[1] }; - context.encrypt_and_emit_event( - randomness[0], - ExampleEvent0::get_event_type_id().to_field(), - msg_sender_ovpk_m, - msg_sender_ivpk_m, - event0.serialize() + event0.emit( + encode_and_encrypt_event( + &mut context, + randomness[0], + context.msg_sender(), + context.msg_sender() + ) ); let event1 = ExampleEvent1 { value2: preimages[2], value3: preimages[3] }; - context.encrypt_and_emit_event( - randomness[1], - ExampleEvent1::get_event_type_id().to_field(), - msg_sender_ovpk_m, - msg_sender_ivpk_m, - event1.serialize() + event1.emit( + encode_and_encrypt_event( + &mut context, + randomness[1], + context.msg_sender(), + context.msg_sender() + ) ); } } diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr index defab65772e..114fa764515 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/main.nr @@ -13,7 +13,7 @@ contract TokenBlacklist { use dep::aztec::{ hash::compute_secret_hash, prelude::{AztecAddress, FunctionSelector, Map, NoteGetterOptions, PrivateSet, PublicMutable, SharedMutable}, - encrypted_logs::encrypted_note_emission::encode_and_encrypt + encrypted_logs::encrypted_note_emission::encode_and_encrypt_note }; use dep::authwit::{auth::{assert_current_call_valid_authwit, assert_current_call_valid_authwit_public}}; @@ -179,7 +179,7 @@ contract TokenBlacklist { // Add the token note to user's balances set let caller = context.msg_sender(); - storage.balances.add(to, U128::from_integer(amount)).emit(encode_and_encrypt(&mut context, caller, to)); + storage.balances.add(to, U128::from_integer(amount)).emit(encode_and_encrypt_note(&mut context, caller, to)); } #[aztec(private)] @@ -195,7 +195,7 @@ contract TokenBlacklist { assert(nonce == 0, "invalid nonce"); } - storage.balances.sub(from, U128::from_integer(amount)).emit(encode_and_encrypt(&mut context, from, from)); + storage.balances.sub(from, U128::from_integer(amount)).emit(encode_and_encrypt_note(&mut context, from, from)); TokenBlacklist::at(context.this_address())._increase_public_balance(to, amount).enqueue(&mut context); } @@ -215,8 +215,8 @@ contract TokenBlacklist { } let amount = U128::from_integer(amount); - storage.balances.sub(from, amount).emit(encode_and_encrypt(&mut context, from, from)); - storage.balances.add(to, amount).emit(encode_and_encrypt(&mut context, from, to)); + storage.balances.sub(from, amount).emit(encode_and_encrypt_note(&mut context, from, from)); + storage.balances.add(to, amount).emit(encode_and_encrypt_note(&mut context, from, to)); } #[aztec(private)] @@ -230,7 +230,7 @@ contract TokenBlacklist { assert(nonce == 0, "invalid nonce"); } - storage.balances.sub(from, U128::from_integer(amount)).emit(encode_and_encrypt(&mut context, from, from)); + storage.balances.sub(from, U128::from_integer(amount)).emit(encode_and_encrypt_note(&mut context, from, from)); TokenBlacklist::at(context.this_address())._reduce_total_supply(amount).enqueue(&mut context); } diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr index 2cea5e637f2..d13145c89e2 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr @@ -18,7 +18,7 @@ contract Token { use dep::aztec::{ hash::compute_secret_hash, prelude::{NoteGetterOptions, Map, PublicMutable, SharedImmutable, PrivateSet, AztecAddress}, - encrypted_logs::encrypted_note_emission::{encode_and_encrypt, encode_and_encrypt_with_keys} + encrypted_logs::encrypted_note_emission::{encode_and_encrypt_note, encode_and_encrypt_note_with_keys} }; // docs:start:import_authwit @@ -196,7 +196,7 @@ contract Token { #[aztec(private)] fn privately_mint_private_note(amount: Field) { let caller = context.msg_sender(); - storage.balances.add(caller, U128::from_integer(amount)).emit(encode_and_encrypt(&mut context, caller, caller)); + storage.balances.add(caller, U128::from_integer(amount)).emit(encode_and_encrypt_note(&mut context, caller, caller)); Token::at(context.this_address()).assert_minter_and_mint(context.msg_sender(), amount).enqueue(&mut context); } @@ -290,7 +290,7 @@ contract Token { // Note: Using context.msg_sender() as a sender below makes this incompatible with escrows because we send // outgoing logs to that address and to send outgoing logs you need to get a hold of ovsk_m. let from = context.msg_sender(); - storage.balances.add(to, U128::from_integer(amount)).emit(encode_and_encrypt(&mut context, from, to)); + storage.balances.add(to, U128::from_integer(amount)).emit(encode_and_encrypt_note(&mut context, from, to)); } // docs:end:redeem_shield @@ -303,7 +303,7 @@ contract Token { assert(nonce == 0, "invalid nonce"); } - storage.balances.sub(from, U128::from_integer(amount)).emit(encode_and_encrypt(&mut context, from, from)); + storage.balances.sub(from, U128::from_integer(amount)).emit(encode_and_encrypt_note(&mut context, from, from)); Token::at(context.this_address())._increase_public_balance(to, amount).enqueue(&mut context); } @@ -322,8 +322,8 @@ contract Token { let to_ivpk = header.get_ivpk_m(&mut context, to); let amount = U128::from_integer(amount); - storage.balances.sub(from, amount).emit(encode_and_encrypt_with_keys(&mut context, from_ovpk, from_ivpk)); - storage.balances.add(to, amount).emit(encode_and_encrypt_with_keys(&mut context, from_ovpk, to_ivpk)); + storage.balances.sub(from, amount).emit(encode_and_encrypt_note_with_keys(&mut context, from_ovpk, from_ivpk)); + storage.balances.add(to, amount).emit(encode_and_encrypt_note_with_keys(&mut context, from_ovpk, to_ivpk)); } // docs:end:transfer @@ -358,10 +358,10 @@ contract Token { let amount = U128::from_integer(amount); // docs:start:increase_private_balance // docs:start:encrypted - storage.balances.sub(from, amount).emit(encode_and_encrypt_with_keys(&mut context, from_ovpk, from_ivpk)); + storage.balances.sub(from, amount).emit(encode_and_encrypt_note_with_keys(&mut context, from_ovpk, from_ivpk)); // docs:end:encrypted // docs:end:increase_private_balance - storage.balances.add(to, amount).emit(encode_and_encrypt_with_keys(&mut context, from_ovpk, to_ivpk)); + storage.balances.add(to, amount).emit(encode_and_encrypt_note_with_keys(&mut context, from_ovpk, to_ivpk)); } // docs:start:burn @@ -373,7 +373,7 @@ contract Token { assert(nonce == 0, "invalid nonce"); } - storage.balances.sub(from, U128::from_integer(amount)).emit(encode_and_encrypt(&mut context, from, from)); + storage.balances.sub(from, U128::from_integer(amount)).emit(encode_and_encrypt_note(&mut context, from, from)); Token::at(context.this_address())._reduce_total_supply(amount).enqueue(&mut context); } diff --git a/noir/noir-repo/aztec_macros/src/transforms/events.rs b/noir/noir-repo/aztec_macros/src/transforms/events.rs index 748032f6300..05861b96eb4 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/events.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/events.rs @@ -67,6 +67,9 @@ pub fn generate_event_impls(module: &mut SortedModule) -> Result<(), AztecMacroE event_interface_trait_impl.items.push(TraitImplItem::Function( generate_fn_to_be_bytes(event_type.as_str(), event_byte_len)?, )); + event_interface_trait_impl + .items + .push(TraitImplItem::Function(generate_fn_emit(event_type.as_str())?)); submodule.contents.trait_impls.push(event_interface_trait_impl); let serialize_trait_impl = @@ -299,6 +302,30 @@ fn generate_fn_to_be_bytes( Ok(noir_fn) } +fn generate_fn_emit(event_type: &str) -> Result { + let function_source = format!( + " + fn emit(self: {event_type}, _emit: fn[Env](Self) -> ()) {{ + _emit(self); + }} + " + ) + .to_string(); + + let (function_ast, errors) = parse_program(&function_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementEventInterface { + secondary_message: Some(format!("Failed to parse Noir macro code (fn emit, implemented for EventInterface of {event_type}). This is either a bug in the compiler or the Noir macro code")), + }); + } + + let mut function_ast = function_ast.into_sorted(); + let mut noir_fn = function_ast.functions.remove(0); + noir_fn.def.visibility = ItemVisibility::Public; + Ok(noir_fn) +} + // We do this pass in the HIR to work around the "#[abi(tag)] attributes can only be used in contracts" error pub fn transform_event_abi( crate_id: &CrateId, diff --git a/yarn-project/end-to-end/src/e2e_event_logs.test.ts b/yarn-project/end-to-end/src/e2e_event_logs.test.ts index 3455b0166f6..b2fbe9d1dac 100644 --- a/yarn-project/end-to-end/src/e2e_event_logs.test.ts +++ b/yarn-project/end-to-end/src/e2e_event_logs.test.ts @@ -30,30 +30,6 @@ describe('Logs', () => { afterAll(() => teardown()); describe('functionality around emitting an encrypted log', () => { - it('emits a generic encrypted log and checks for correctness', async () => { - const randomness = Fr.random(); - const eventTypeId = Fr.random(); - const preimage = makeTuple(6, Fr.random); - - const tx = await testLogContract.methods.emit_encrypted_log(randomness, eventTypeId, preimage).send().wait(); - - const txEffect = await node.getTxEffect(tx.txHash); - - const encryptedLogs = txEffect!.encryptedLogs.unrollLogs(); - expect(encryptedLogs.length).toBe(1); - - const decryptedLog = TaggedLog.decryptAsIncoming( - encryptedLogs[0], - deriveMasterIncomingViewingSecretKey(wallets[0].getSecretKey()), - L1EventPayload, - ); - - expect(decryptedLog?.payload.contractAddress).toStrictEqual(testLogContract.address); - expect(decryptedLog?.payload.randomness).toStrictEqual(randomness); - expect(decryptedLog?.payload.eventTypeId).toStrictEqual(eventTypeId); - expect(decryptedLog?.payload.event.items).toStrictEqual(preimage); - }); - it('emits multiple events as encrypted logs and decodes them', async () => { const randomness = makeTuple(2, Fr.random); const preimage = makeTuple(4, Fr.random); From 9b4f56c89fb17eb3497987e6f9198441a4e89c56 Mon Sep 17 00:00:00 2001 From: maramihali Date: Tue, 25 Jun 2024 16:07:59 +0100 Subject: [PATCH 5/9] feat: Enable merge recursive verifier in Goblin recursive verifier (#7182) Used to be disable because of an issue in biggroup but got subsequently fixed, resolves https://github.com/AztecProtocol/barretenberg/issues/1024. ``` Number of gates in ClientIVCRecursiveVerifier Before: 12495384 After: 12638476 ``` --- .../verifier/client_ivc_recursive_verifier.cpp | 6 ++++++ .../verifier/client_ivc_recursive_verifier.test.cpp | 4 ++++ .../verifier/goblin_recursive_verifier.cpp | 12 ++++++++---- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/client_ivc_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/client_ivc_recursive_verifier.cpp index 0286a7dbc94..055fa5ca299 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/client_ivc_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/client_ivc_recursive_verifier.cpp @@ -2,6 +2,12 @@ namespace bb::stdlib::recursion::honk { +/** + * @brief Performs recursive verification of the Client IVC proof. + * + * @todo (https://github.com/AztecProtocol/barretenberg/issues/934): Add logic for accumulating the pairing points + * produced by the verifiers (and potentially IPA accumulators for ECCVM verifier) + */ void ClientIVCRecursiveVerifier::verify(const ClientIVC::Proof& proof) { // Perform recursive folding verification diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/client_ivc_recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/client_ivc_recursive_verifier.test.cpp index b7c5f01e502..4be2ca25960 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/client_ivc_recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/client_ivc_recursive_verifier.test.cpp @@ -88,6 +88,10 @@ TEST_F(ClientIVCRecursionTests, Basic) // Generate the recursive verification circuit verifier.verify(proof); + info("Recursive Verifier: num gates = ", builder->num_gates); + + EXPECT_EQ(builder->failed(), false) << builder->err(); + EXPECT_TRUE(CircuitChecker::check(*builder)); } diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/goblin_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/goblin_recursive_verifier.cpp index 692382da3a0..5ad5a190621 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/goblin_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/goblin_recursive_verifier.cpp @@ -2,6 +2,12 @@ namespace bb::stdlib::recursion::honk { +/** + * @brief Runs the Goblin recursive verifier consisting of ECCVM, Translator and Merge verifiers. + * + * @todo https://github.com/AztecProtocol/barretenberg/issues/934: Add logic for accumulating the pairing points + * produced by the translator and merge verifier (and potentially IPA accumulators for ECCVM verifier) + */ void GoblinRecursiveVerifier::verify(const GoblinProof& proof) { // Run the ECCVM recursive verifier @@ -28,9 +34,7 @@ void GoblinRecursiveVerifier::verify(const GoblinProof& proof) }; translator_verifier.verify_translation(translation_evaluations); - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1024): Perform recursive merge verification once it - // works with Ultra arithmetization - // MergeVerifier merge_verified{ builder }; - // [[maybe_unused]] auto merge_pairing_points = merge_verifier.verify_proof(proof.merge_proof); + MergeVerifier merge_verifier{ builder }; + merge_verifier.verify_proof(proof.merge_proof); } } // namespace bb::stdlib::recursion::honk \ No newline at end of file From 9aabc324040e84be9d61644d33948c818553f422 Mon Sep 17 00:00:00 2001 From: Jean M <132435771+jeanmon@users.noreply.github.com> Date: Tue, 25 Jun 2024 17:14:51 +0200 Subject: [PATCH 6/9] chore: Increase the timeout of the runner for full AVM workflow to 70 minutes (#7183) --- .github/workflows/vm_full_tests.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vm_full_tests.yml b/.github/workflows/vm_full_tests.yml index 1e912a05438..e1e135b0201 100644 --- a/.github/workflows/vm_full_tests.yml +++ b/.github/workflows/vm_full_tests.yml @@ -55,6 +55,7 @@ jobs: concurrency_key: avm-full-tests-x86 - name: "AVM Full Tests" working-directory: ./barretenberg/cpp/ - timeout-minutes: 90 - # limit our parallelism to half our cores - run: earthly-ci --no-output +vm-full-test --hardware_concurrency=64 + timeout-minutes: 70 + run: | + sudo shutdown -P 70 # hack until core part of the scripts + earthly-ci --no-output +vm-full-test --hardware_concurrency=64 # limit our parallelism to half our cores From 6f1212ff0d6bb7a326e571da2d49cfac75a8e5de Mon Sep 17 00:00:00 2001 From: ledwards2225 <98505400+ledwards2225@users.noreply.github.com> Date: Tue, 25 Jun 2024 08:41:46 -0700 Subject: [PATCH 7/9] feat: conventional lookups using log-deriv (#7020) Replaces the plookup based lookup argument in UltraHonk/MegaHonk with a log-derivative based argument. This is advantageous in a number of ways including 1) no more committing to expensive z_lookup poly (especially relevant for structured trace), 2) prover work is proportional to number of lookups not size of circuit.' Below are the benchmark results (master vs branch). Note that most of the benefit is derived from no longer needing to commit to `z_lookup` which was previously the most expensive single commitment. The Lookup relation also goes from being 45% of total accumulate work, to only 10%. ``` -------------------------------------------------------------------------------- Benchmark Time CPU Iterations UserCounters... -------------------------------------------------------------------------------- ClientIVCBench/Full/6 16151 ms 11247 ms 1 function ms % sum construct_circuits(t) 3241 20.23% ProverInstance(Circuit&)(t) 1575 9.83% ProtogalaxyProver::fold_instances(t) 7627 47.61% Decider::construct_proof(t) 503 3.14% ECCVMProver(CircuitBuilder&)(t) 173 1.08% ECCVMProver::construct_proof(t) 1904 11.88% TranslatorProver::construct_proof(t) 819 5.11% Goblin::merge(t) 177 1.11% Total time accounted for: 16019ms/16151ms = 99.18% Major contributors: function ms % sum commit(t) 4114 25.68% compute_combiner(t) 2824 17.63% compute_perturbator(t) 795 4.96% compute_univariate(t) 1149 7.17% Breakdown of ProtogalaxyProver::fold_instances: ProtoGalaxyProver_::preparation_round(t) 3721 48.79% ProtoGalaxyProver_::perturbator_round(t) 795 10.43% ProtoGalaxyProver_::combiner_quotient_round(t) 2826 37.05% ProtoGalaxyProver_::accumulator_update_round(t) 284 3.73% Relation contributions (times to be interpreted relatively): function ms % sum Arithmetic::accumulate(t) 1490 5.78% Permutation::accumulate(t) 6746 26.18% Lookup::accumulate(t) 11679 45.33% DeltaRange::accumulate(t) 977 3.79% Elliptic::accumulate(t) 635 2.46% Auxiliary::accumulate(t) 3812 14.79% EccOp::accumulate(t) 0 0.00% DatabusRead::accumulate(t) 1 0.01% PoseidonExt::accumulate(t) 110 0.43% PoseidonInt::accumulate(t) 316 1.23% ``` ``` -------------------------------------------------------------------------------- Benchmark Time CPU Iterations UserCounters... -------------------------------------------------------------------------------- ClientIVCBench/Full/6 15345 ms 10707 ms 1 function ms % sum construct_circuits(t) 3183 21.01% ProverInstance(Circuit&)(t) 1697 11.20% ProtogalaxyProver::fold_instances(t) 6754 44.57% Decider::construct_proof(t) 467 3.08% ECCVMProver(CircuitBuilder&)(t) 176 1.16% ECCVMProver::construct_proof(t) 1910 12.60% TranslatorProver::construct_proof(t) 815 5.38% Goblin::merge(t) 150 0.99% Total time accounted for: 15153ms/15345ms = 98.74% Major contributors: function ms % sum commit(t) 3379 22.30% compute_combiner(t) 2200 14.52% compute_perturbator(t) 739 4.87% compute_univariate(t) 1118 7.38% Breakdown of ProtogalaxyProver::fold_instances: ProtoGalaxyProver_::preparation_round(t) 3529 52.24% ProtoGalaxyProver_::perturbator_round(t) 739 10.94% ProtoGalaxyProver_::combiner_quotient_round(t) 2202 32.60% ProtoGalaxyProver_::accumulator_update_round(t) 285 4.22% Relation contributions (times to be interpreted relatively): function ms % sum Arithmetic::accumulate(t) 1474 9.69% Permutation::accumulate(t) 6515 42.84% Lookup::accumulate(t) 1622 10.67% DeltaRange::accumulate(t) 952 6.26% Elliptic::accumulate(t) 605 3.98% Auxiliary::accumulate(t) 3616 23.78% EccOp::accumulate(t) 0 0.00% DatabusRead::accumulate(t) 1 0.01% PoseidonExt::accumulate(t) 112 0.74% PoseidonInt::accumulate(t) 309 2.03% ``` --- .../gen_inner_proof_inputs_ultra_honk.sh | 2 +- .../relations_bench/relations.bench.cpp | 6 +- .../ultra_circuit_builder.test.cpp | 17 ++ .../src/barretenberg/eccvm/eccvm_prover.cpp | 1 - .../plonk/composer/composer_lib.hpp | 75 ++++++ .../plonk_honk_shared/CMakeLists.txt | 2 +- .../composer/composer_lib.hpp | 90 +++---- .../composer/composer_lib.test.cpp | 75 +++++- .../protogalaxy/protogalaxy.test.cpp | 114 +++++++-- .../protogalaxy/protogalaxy_prover.hpp | 1 - .../protogalaxy/protogalaxy_prover_impl.hpp | 7 +- .../relations/databus_lookup_relation.hpp | 16 +- .../relations/logderiv_lookup_relation.hpp | 207 ++++++++++++++++ .../relations/lookup_relation.hpp | 224 ------------------ .../ultra_relation_consistency.test.cpp | 79 ------ .../protogalaxy_recursive_verifier.cpp | 17 +- .../verifier/ultra_recursive_verifier.cpp | 13 +- .../honk_recursion/verifier/verifier.test.cpp | 2 +- .../grand_product_library.test.cpp | 133 ----------- .../stdlib_circuit_builders/mega_flavor.hpp | 143 +++++------ .../stdlib_circuit_builders/mock_circuits.hpp | 29 +++ .../plookup_tables/plookup_tables.cpp | 18 +- .../plookup_tables/types.hpp | 76 +++++- .../stdlib_circuit_builders/ultra_flavor.hpp | 163 ++++++------- .../ultra_recursive_flavor.hpp | 12 +- .../sumcheck/instance/prover_instance.hpp | 5 +- .../instance/prover_instance.test.cpp | 89 ------- .../barretenberg/sumcheck/sumcheck.test.cpp | 1 - .../ultra_honk/mega_transcript.test.cpp | 9 +- .../barretenberg/ultra_honk/oink_prover.cpp | 31 ++- .../barretenberg/ultra_honk/oink_verifier.cpp | 17 +- .../ultra_honk/relation_correctness.test.cpp | 56 ++--- .../barretenberg/ultra_honk/sumcheck.test.cpp | 8 +- ..._composer.test.cpp => ultra_honk.test.cpp} | 132 +++++++++-- .../ultra_honk/ultra_transcript.test.cpp | 9 +- .../barretenberg/vm/generated/avm_prover.cpp | 1 - .../verify_honk_proof/Prover.toml | 2 +- .../verify_honk_proof/src/main.nr | 2 +- 38 files changed, 960 insertions(+), 924 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/relations/logderiv_lookup_relation.hpp delete mode 100644 barretenberg/cpp/src/barretenberg/relations/lookup_relation.hpp delete mode 100644 barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.test.cpp rename barretenberg/cpp/src/barretenberg/ultra_honk/{ultra_composer.test.cpp => ultra_honk.test.cpp} (88%) diff --git a/barretenberg/acir_tests/gen_inner_proof_inputs_ultra_honk.sh b/barretenberg/acir_tests/gen_inner_proof_inputs_ultra_honk.sh index a013a7129b3..30548202ad3 100755 --- a/barretenberg/acir_tests/gen_inner_proof_inputs_ultra_honk.sh +++ b/barretenberg/acir_tests/gen_inner_proof_inputs_ultra_honk.sh @@ -3,7 +3,7 @@ # BIN: to specify a different binary to test with (e.g. bb.js or bb.js-dev). set -eu -BIN=${BIN:-../cpp/build-debug/bin/bb} +BIN=${BIN:-../cpp/build/bin/bb} CRS_PATH=~/.bb-crs BRANCH=master VERBOSE=${VERBOSE:-} diff --git a/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/relations.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/relations.bench.cpp index 6e7069f08ae..f735d2cfb19 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/relations.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/relations.bench.cpp @@ -65,7 +65,7 @@ BENCHMARK(execute_relation_for_pg_univariates>); BENCHMARK(execute_relation_for_pg_univariates>); BENCHMARK(execute_relation_for_pg_univariates>); -BENCHMARK(execute_relation_for_pg_univariates>); +BENCHMARK(execute_relation_for_pg_univariates>); BENCHMARK(execute_relation_for_pg_univariates>); // Goblin-Ultra only relations (PG prover combiner work) @@ -79,7 +79,7 @@ BENCHMARK(execute_relation_for_univariates>); BENCHMARK(execute_relation_for_univariates>); BENCHMARK(execute_relation_for_univariates>); -BENCHMARK(execute_relation_for_univariates>); +BENCHMARK(execute_relation_for_univariates>); BENCHMARK(execute_relation_for_univariates>); // Goblin-Ultra only relations (Sumcheck prover work) @@ -93,7 +93,7 @@ BENCHMARK(execute_relation_for_values>) BENCHMARK(execute_relation_for_values>); BENCHMARK(execute_relation_for_values>); BENCHMARK(execute_relation_for_values>); -BENCHMARK(execute_relation_for_values>); +BENCHMARK(execute_relation_for_values>); BENCHMARK(execute_relation_for_values>); // Goblin-Ultra only relations (verifier work) diff --git a/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_builder.test.cpp b/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_builder.test.cpp index 005a50ea156..5d1eec57741 100644 --- a/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_builder.test.cpp +++ b/barretenberg/cpp/src/barretenberg/circuit_checker/ultra_circuit_builder.test.cpp @@ -1,6 +1,7 @@ #include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" #include "barretenberg/circuit_checker/circuit_checker.hpp" #include "barretenberg/crypto/pedersen_commitment/pedersen.hpp" +#include "barretenberg/stdlib_circuit_builders/mock_circuits.hpp" #include "barretenberg/stdlib_circuit_builders/plookup_tables/fixed_base/fixed_base.hpp" #include @@ -104,6 +105,22 @@ TEST(ultra_circuit_constructor, create_gates_from_plookup_accumulators) EXPECT_EQ(result, true); } +TEST(ultra_circuit_constructor, bad_lookup_failure) +{ + UltraCircuitBuilder builder; + MockCircuits::add_lookup_gates(builder); + + // Erroneously set a non-zero wire value to zero in one of the lookup gates + for (auto& wire_3_witness_idx : builder.blocks.lookup.w_o()) { + if (wire_3_witness_idx != builder.zero_idx) { + wire_3_witness_idx = builder.zero_idx; + break; + } + } + + EXPECT_FALSE(CircuitChecker::check(builder)); +} + TEST(ultra_circuit_constructor, base_case) { UltraCircuitBuilder circuit_constructor = UltraCircuitBuilder(); diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp index 3215ffac416..7d049b16970 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp @@ -7,7 +7,6 @@ #include "barretenberg/honk/proof_system/permutation_library.hpp" #include "barretenberg/plonk_honk_shared/library/grand_product_library.hpp" #include "barretenberg/polynomials/polynomial.hpp" -#include "barretenberg/relations/lookup_relation.hpp" #include "barretenberg/relations/permutation_relation.hpp" #include "barretenberg/sumcheck/sumcheck.hpp" diff --git a/barretenberg/cpp/src/barretenberg/plonk/composer/composer_lib.hpp b/barretenberg/cpp/src/barretenberg/plonk/composer/composer_lib.hpp index 0ca1c00e747..88c518cb4b3 100644 --- a/barretenberg/cpp/src/barretenberg/plonk/composer/composer_lib.hpp +++ b/barretenberg/cpp/src/barretenberg/plonk/composer/composer_lib.hpp @@ -48,4 +48,79 @@ std::shared_ptr compute_verification_key_common( // silencing for now but need to figure out where to extract type of VerifierCrs from :-/ std::shared_ptr> const& vrs); +/** + * @brief Construct polynomials containing the sorted concatenation of the lookups and the lookup tables + * + * @tparam Flavor + * @param circuit + * @param dyadic_circuit_size + * @param additional_offset Additional space needed in polynomials to add randomness for zk (Plonk only) + * @return std::array + */ +template +std::array construct_sorted_list_polynomials(typename Flavor::CircuitBuilder& circuit, + const size_t dyadic_circuit_size, + size_t additional_offset = 0) +{ + using Polynomial = typename Flavor::Polynomial; + std::array sorted_polynomials; + // Initialise the sorted concatenated list polynomials for the lookup argument + for (auto& s_i : sorted_polynomials) { + s_i = Polynomial(dyadic_circuit_size); + } + + // The sorted list polynomials have (tables_size + lookups_size) populated entries. We define the index below so + // that these entries are written into the last indices of the polynomials. The values on the first + // dyadic_circuit_size - (tables_size + lookups_size) indices are automatically initialized to zero via the + // polynomial constructor. + size_t s_index = dyadic_circuit_size - (circuit.get_tables_size() + circuit.get_lookups_size()) - additional_offset; + ASSERT(s_index > 0); // We need at least 1 row of zeroes for the permutation argument + + for (auto& table : circuit.lookup_tables) { + const fr table_index(table.table_index); + auto& lookup_gates = table.lookup_gates; + for (size_t i = 0; i < table.size(); ++i) { + if (table.use_twin_keys) { + lookup_gates.push_back({ + { + table.column_1[i].from_montgomery_form().data[0], + table.column_2[i].from_montgomery_form().data[0], + }, + { + table.column_3[i], + 0, + }, + }); + } else { + lookup_gates.push_back({ + { + table.column_1[i].from_montgomery_form().data[0], + 0, + }, + { + table.column_2[i], + table.column_3[i], + }, + }); + } + } + +#ifdef NO_TBB + std::sort(lookup_gates.begin(), lookup_gates.end()); +#else + std::sort(std::execution::par_unseq, lookup_gates.begin(), lookup_gates.end()); +#endif + + for (const auto& entry : lookup_gates) { + const auto components = entry.to_table_components(table.use_twin_keys); + sorted_polynomials[0][s_index] = components[0]; + sorted_polynomials[1][s_index] = components[1]; + sorted_polynomials[2][s_index] = components[2]; + sorted_polynomials[3][s_index] = table_index; + ++s_index; + } + } + return sorted_polynomials; +} + } // namespace bb::plonk diff --git a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/CMakeLists.txt index d3024bcdbb1..7603c0f6775 100644 --- a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/CMakeLists.txt @@ -1 +1 @@ -barretenberg_module(plonk_honk_shared polynomials) \ No newline at end of file +barretenberg_module(plonk_honk_shared polynomials ultra_honk) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/composer/composer_lib.hpp b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/composer/composer_lib.hpp index 7745a853c58..d4e75dc9e14 100644 --- a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/composer/composer_lib.hpp +++ b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/composer/composer_lib.hpp @@ -2,6 +2,7 @@ #include "barretenberg/common/ref_array.hpp" #include "barretenberg/flavor/flavor.hpp" #include "barretenberg/polynomials/polynomial_store.hpp" +#include "barretenberg/stdlib_circuit_builders/plookup_tables/types.hpp" #include @@ -20,6 +21,7 @@ void construct_lookup_table_polynomials(RefArray // ^^^^^^^^^ ^^^^^^^^ ^^^^^^^ ^nonzero to ensure uniqueness and to avoid infinity commitments // | table randomness // ignored, as used for regular constraints and padding to the next power of 2. + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1033): construct tables and counts at top of trace ASSERT(dyadic_circuit_size > circuit.get_tables_size() + additional_offset); size_t offset = dyadic_circuit_size - circuit.get_tables_size() - additional_offset; @@ -37,78 +39,40 @@ void construct_lookup_table_polynomials(RefArray } /** - * @brief Construct polynomials containing the sorted concatenation of the lookups and the lookup tables - * - * @tparam Flavor - * @param circuit - * @param dyadic_circuit_size - * @param additional_offset Additional space needed in polynomials to add randomness for zk (Plonk only) - * @return std::array + * @brief Construct polynomial whose value at index i is the number of times the table entry at that index has been + * read. + * @details Read counts are needed for the log derivative lookup argument. The table polynomials are constructed as a + * concatenation of basic 3-column tables. Similarly, the read counts polynomial is constructed as the concatenation of + * read counts for the individual tables. */ template -std::array construct_sorted_list_polynomials(typename Flavor::CircuitBuilder& circuit, - const size_t dyadic_circuit_size, - size_t additional_offset = 0) +void construct_lookup_read_counts(typename Flavor::Polynomial& read_counts, + typename Flavor::Polynomial& read_tags, + typename Flavor::CircuitBuilder& circuit, + size_t dyadic_circuit_size) { - using Polynomial = typename Flavor::Polynomial; - std::array sorted_polynomials; - // Initialise the sorted concatenated list polynomials for the lookup argument - for (auto& s_i : sorted_polynomials) { - s_i = Polynomial(dyadic_circuit_size); - } - - // The sorted list polynomials have (tables_size + lookups_size) populated entries. We define the index below so - // that these entries are written into the last indices of the polynomials. The values on the first - // dyadic_circuit_size - (tables_size + lookups_size) indices are automatically initialized to zero via the - // polynomial constructor. - size_t s_index = dyadic_circuit_size - (circuit.get_tables_size() + circuit.get_lookups_size()) - additional_offset; - ASSERT(s_index > 0); // We need at least 1 row of zeroes for the permutation argument + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1033): construct tables and counts at top of trace + size_t offset = dyadic_circuit_size - circuit.get_tables_size(); + size_t table_offset = offset; // offset of the present table in the table polynomials + // loop over all tables used in the circuit; each table contains data about the lookups made on it for (auto& table : circuit.lookup_tables) { - const fr table_index(table.table_index); - auto& lookup_gates = table.lookup_gates; - for (size_t i = 0; i < table.size(); ++i) { - if (table.use_twin_keys) { - lookup_gates.push_back({ - { - table.column_1[i].from_montgomery_form().data[0], - table.column_2[i].from_montgomery_form().data[0], - }, - { - table.column_3[i], - 0, - }, - }); - } else { - lookup_gates.push_back({ - { - table.column_1[i].from_montgomery_form().data[0], - 0, - }, - { - table.column_2[i], - table.column_3[i], - }, - }); - } - } + table.initialize_index_map(); + + for (auto& gate_data : table.lookup_gates) { + // convert lookup gate data to an array of three field elements, one for each of the 3 columns + auto table_entry = gate_data.to_table_components(table.use_twin_keys); -#ifdef NO_TBB - std::sort(lookup_gates.begin(), lookup_gates.end()); -#else - std::sort(std::execution::par_unseq, lookup_gates.begin(), lookup_gates.end()); -#endif + // find the index of the entry in the table + auto index_in_table = table.index_map[table_entry]; - for (const auto& entry : lookup_gates) { - const auto components = entry.to_sorted_list_components(table.use_twin_keys); - sorted_polynomials[0][s_index] = components[0]; - sorted_polynomials[1][s_index] = components[1]; - sorted_polynomials[2][s_index] = components[2]; - sorted_polynomials[3][s_index] = table_index; - ++s_index; + // increment the read count at the corresponding index in the full polynomial + size_t index_in_poly = table_offset + index_in_table; + read_counts[index_in_poly]++; + read_tags[index_in_poly] = 1; // tag is 1 if entry has been read 1 or more times } + table_offset += table.size(); // set the offset of the next table within the polynomials } - return sorted_polynomials; } } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/composer/composer_lib.test.cpp b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/composer/composer_lib.test.cpp index 94219c72a48..33534bc958d 100644 --- a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/composer/composer_lib.test.cpp +++ b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/composer/composer_lib.test.cpp @@ -1,21 +1,78 @@ #include "barretenberg/plonk_honk_shared/composer/composer_lib.hpp" -#include "barretenberg/common/slab_allocator.hpp" -#include "barretenberg/plonk_honk_shared/types/circuit_type.hpp" #include "barretenberg/srs/factories/crs_factory.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_flavor.hpp" + #include #include using namespace bb; class ComposerLibTests : public ::testing::Test { + public: + using Flavor = UltraFlavor; + using FF = typename Flavor::FF; + protected: + static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); } +}; + +/** + * @brief A test to demonstrate that lookup read counts/tags are computed correctly for a simple 'hand-computable' case + * using the uint32 XOR table + * + */ +TEST_F(ComposerLibTests, LookupReadCounts) +{ + using Builder = UltraCircuitBuilder; using Flavor = UltraFlavor; using FF = typename Flavor::FF; - Flavor::CircuitBuilder circuit_constructor; - Flavor::ProvingKey proving_key = []() { - auto crs_factory = srs::factories::CrsFactory(); - auto crs = crs_factory.get_prover_crs(4); - return Flavor::ProvingKey(/*circuit_size=*/8, /*num_public_inputs=*/0); - }(); -}; \ No newline at end of file + using Polynomial = typename Flavor::Polynomial; + auto UINT32_XOR = plookup::MultiTableId::UINT32_XOR; + + Builder builder; + + // define some very simply inputs to XOR + FF left{ 1 }; + FF right{ 5 }; + + auto left_idx = builder.add_variable(left); + auto right_idx = builder.add_variable(right); + + // create a single lookup from the uint32 XOR table + auto accumulators = plookup::get_lookup_accumulators(UINT32_XOR, left, right, /*is_2_to_1_lookup*/ true); + builder.create_gates_from_plookup_accumulators(UINT32_XOR, accumulators, left_idx, right_idx); + + EXPECT_EQ(builder.lookup_tables.size(), 1); // we only used a single table + EXPECT_EQ(builder.lookup_tables[0].size(), 4096); // table has size 64*64 (6 bit operands) + + size_t circuit_size = 8192; + + Polynomial read_counts{ circuit_size }; + Polynomial read_tags{ circuit_size }; + + construct_lookup_read_counts(read_counts, read_tags, builder, circuit_size); + + // The table polys are constructed at the bottom of the trace, thus so to are the counts/tags + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1033): construct tables and counts at top of trace + size_t offset = circuit_size - builder.get_tables_size(); + + // The uint32 XOR lookup table is constructed for 6 bit operands via double for loop that iterates through the left + // operand externally (0 to 63) then the right operand internally (0 to 63). Computing (1 XOR 5) will thus result in + // 1 lookup from the (1*64 + 5)th index in the table and 5 lookups from the (0*64 + 0)th index (for the remaining 5 + // limbs that are all 0). The counts and tags at all other indices should be zero. + size_t idx = 0; + for (auto [count, tag] : zip_view(read_counts, read_tags)) { + if (idx == (0 + offset)) { + EXPECT_EQ(count, 5); + EXPECT_EQ(tag, 1); + } else if (idx == (69 + offset)) { + EXPECT_EQ(count, 1); + EXPECT_EQ(tag, 1); + } else { + EXPECT_EQ(count, 0); + EXPECT_EQ(tag, 0); + } + idx++; + } +} \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy.test.cpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy.test.cpp index 6c717c1d126..da09210655f 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy.test.cpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy.test.cpp @@ -136,12 +136,10 @@ template class ProtoGalaxyTests : public testing::Test { instance->relation_parameters.beta = FF::random_element(); instance->relation_parameters.gamma = FF::random_element(); - instance->proving_key.compute_sorted_accumulator_polynomials(instance->relation_parameters.eta, - instance->relation_parameters.eta_two, - instance->relation_parameters.eta_three); - if constexpr (IsGoblinFlavor) { - instance->proving_key.compute_logderivative_inverse(instance->relation_parameters); - } + instance->proving_key.add_ram_rom_memory_records_to_wire_4(instance->relation_parameters.eta, + instance->relation_parameters.eta_two, + instance->relation_parameters.eta_three); + instance->proving_key.compute_logderivative_inverses(instance->relation_parameters); instance->proving_key.compute_grand_product_polynomials(instance->relation_parameters); for (auto& alpha : instance->alphas) { @@ -311,21 +309,94 @@ template class ProtoGalaxyTests : public testing::Test { } /** - * @brief Testing one valid round of folding followed by the decider. - * @brief For additional robustness we give one of the circuits more public inputs than the other + * @brief Testing one valid round of folding (plus decider) for two inhomogeneous circuits + * @details For robustness we fold circuits with different numbers/types of gates (but the same dyadic size) * */ - static void test_full_protogalaxy_simple() + static void test_protogalaxy_inhomogeneous() { - // Construct a first circuit with some public inputs - Builder builder1; - construct_circuit(builder1); - bb::MockCircuits::add_arithmetic_gates_with_public_inputs(builder1, /*num_gates=*/4); + auto check_fold_and_decide = [](Builder& circuit_1, Builder& circuit_2) { + // Construct the prover/verifier instances for each + TupleOfInstances instances; + construct_prover_and_verifier_instance(instances, circuit_1); + construct_prover_and_verifier_instance(instances, circuit_2); + + // Perform prover and verifier folding + auto [prover_accumulator, verifier_accumulator] = fold_and_verify(get<0>(instances), get<1>(instances)); + check_accumulator_target_sum_manual(prover_accumulator, true); + + // Run decider + decide_and_verify(prover_accumulator, verifier_accumulator, true); + }; + + // One circuit has more arithmetic gates + { + // Construct two equivalent circuits + Builder builder1; + Builder builder2; + construct_circuit(builder1); + construct_circuit(builder2); + + // Add some arithmetic gates + bb::MockCircuits::add_arithmetic_gates(builder1, /*num_gates=*/4); + + check_fold_and_decide(builder1, builder2); + } + + // One circuit has more arithmetic gates with public inputs + { + // Construct two equivalent circuits + Builder builder1; + Builder builder2; + construct_circuit(builder1); + construct_circuit(builder2); + + // Add some arithmetic gates with public inputs to the first circuit + bb::MockCircuits::add_arithmetic_gates_with_public_inputs(builder1, /*num_gates=*/4); + + check_fold_and_decide(builder1, builder2); + } + + // One circuit has more lookup gates + { + // Construct two equivalent circuits + Builder builder1; + Builder builder2; + construct_circuit(builder1); + construct_circuit(builder2); + + // Add a different number of lookup gates to each circuit + bb::MockCircuits::add_lookup_gates(builder1, /*num_iterations=*/2); // 12 gates plus 4096 table + bb::MockCircuits::add_lookup_gates(builder2, /*num_iterations=*/1); // 6 gates plus 4096 table + + check_fold_and_decide(builder1, builder2); + } + } - // Construct a second circuit with no public inputs + /** + * @brief Ensure failure for a bad lookup gate in one of the circuits being folded + * + */ + static void test_protogalaxy_bad_lookup_failure() + { + // Construct two equivalent circuits + Builder builder1; Builder builder2; + construct_circuit(builder1); construct_circuit(builder2); + // Add a different number of lookup gates to each circuit + bb::MockCircuits::add_lookup_gates(builder1, /*num_iterations=*/2); // 12 gates plus 4096 table + bb::MockCircuits::add_lookup_gates(builder2, /*num_iterations=*/1); // 6 gates plus 4096 table + + // Erroneously set a non-zero wire value to zero in one of the lookup gates + for (auto& wire_3_witness_idx : builder1.blocks.lookup.w_o()) { + if (wire_3_witness_idx != builder1.zero_idx) { + wire_3_witness_idx = builder1.zero_idx; + break; + } + } + // Construct the prover/verifier instances for each TupleOfInstances instances; construct_prover_and_verifier_instance(instances, builder1); @@ -333,9 +404,11 @@ template class ProtoGalaxyTests : public testing::Test { // Perform prover and verifier folding auto [prover_accumulator, verifier_accumulator] = fold_and_verify(get<0>(instances), get<1>(instances)); - check_accumulator_target_sum_manual(prover_accumulator, true); - decide_and_verify(prover_accumulator, verifier_accumulator, true); + // Expect failure in manual target sum check and decider + bool expected_result = false; + check_accumulator_target_sum_manual(prover_accumulator, expected_result); + decide_and_verify(prover_accumulator, verifier_accumulator, expected_result); } /** @@ -517,9 +590,9 @@ TYPED_TEST(ProtoGalaxyTests, CombineAlpha) TestFixture::test_combine_alpha(); } -TYPED_TEST(ProtoGalaxyTests, FullProtogalaxySimple) +TYPED_TEST(ProtoGalaxyTests, ProtogalaxyInhomogeneous) { - TestFixture::test_full_protogalaxy_simple(); + TestFixture::test_protogalaxy_inhomogeneous(); } TYPED_TEST(ProtoGalaxyTests, FullProtogalaxyTest) @@ -546,6 +619,11 @@ TYPED_TEST(ProtoGalaxyTests, TamperedAccumulatorPolynomial) TestFixture::test_tampered_accumulator_polynomial(); } +TYPED_TEST(ProtoGalaxyTests, BadLookupFailure) +{ + TestFixture::test_protogalaxy_bad_lookup_failure(); +} + // We only fold one instance currently due to significant compile time added by multiple instances TYPED_TEST(ProtoGalaxyTests, Fold1Instance) { diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp index 3091c0259e2..cbfbe35cbda 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp @@ -364,7 +364,6 @@ template class ProtoGalaxyProver_ { const FF& scaling_factor) { using Relation = std::tuple_element_t; - // WORKTODO: disable skipping for the combiner for now.. // Check if the relation is skippable to speed up accumulation if constexpr (!isSkippable) { // If not, accumulate normally diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp index f38ff10b3e1..36774b7c25f 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp @@ -187,7 +187,12 @@ FoldingResult ProtoGalaxyProver_proving_key.circuit_size == instances[idx + 1]->proving_key.circuit_size); + if (instances[idx]->proving_key.circuit_size != instances[idx + 1]->proving_key.circuit_size) { + info("ProtogalaxyProver: circuit size mismatch!"); + info("Instance ", idx, " size = ", instances[idx]->proving_key.circuit_size); + info("Instance ", idx + 1, " size = ", instances[idx + 1]->proving_key.circuit_size); + ASSERT(false); + } } preparation_round(); perturbator_round(); diff --git a/barretenberg/cpp/src/barretenberg/relations/databus_lookup_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/databus_lookup_relation.hpp index 3c897ce3909..c0ec529cdd7 100644 --- a/barretenberg/cpp/src/barretenberg/relations/databus_lookup_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/databus_lookup_relation.hpp @@ -243,24 +243,24 @@ template class DatabusLookupRelationImpl { const auto inverses = View(BusData::inverses(in)); // Degree 1 const auto read_counts = View(BusData::read_counts(in)); // Degree 1 - const auto read_term = compute_read_term(in, params); // Degree 1 - const auto write_term = compute_write_term(in, params); // Degree 1 - const auto inverse_exists = compute_inverse_exists(in); // Degree 1 + const auto read_term = compute_read_term(in, params); // Degree 1 (2) + const auto write_term = compute_write_term(in, params); // Degree 1 (2) + const auto inverse_exists = compute_inverse_exists(in); // Degree 2 const auto read_selector = get_read_selector(in); // Degree 2 - const auto write_inverse = inverses * read_term; // Degree 2 - const auto read_inverse = inverses * write_term; // Degree 2 + const auto write_inverse = inverses * read_term; // Degree 2 (3) + const auto read_inverse = inverses * write_term; // Degree 2 (3) // Determine which pair of subrelations to update based on which bus column is being read constexpr size_t subrel_idx_1 = 2 * bus_idx; constexpr size_t subrel_idx_2 = 2 * bus_idx + 1; // Establish the correctness of the polynomial of inverses I. Note: inverses is computed so that the value is 0 - // if !inverse_exists. Degree 3 + // if !inverse_exists. Degree 3 (5) std::get(accumulator) += (read_term * write_term * inverses - inverse_exists) * scaling_factor; // Establish validity of the read. Note: no scaling factor here since this constraint is enforced across the - // entire trace, not on a per-row basis - std::get(accumulator) += read_selector * read_inverse - read_counts * write_inverse; // Degree 4 + // entire trace, not on a per-row basis. + std::get(accumulator) += read_selector * read_inverse - read_counts * write_inverse; // Deg 4 (5) } /** diff --git a/barretenberg/cpp/src/barretenberg/relations/logderiv_lookup_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/logderiv_lookup_relation.hpp new file mode 100644 index 00000000000..92078db8e85 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/relations/logderiv_lookup_relation.hpp @@ -0,0 +1,207 @@ +#pragma once +#include +#include + +#include "barretenberg/common/constexpr_utils.hpp" +#include "barretenberg/honk/proof_system/logderivative_library.hpp" +#include "barretenberg/polynomials/polynomial.hpp" +#include "barretenberg/polynomials/univariate.hpp" +#include "barretenberg/relations/relation_types.hpp" + +namespace bb { + +template class LogDerivLookupRelationImpl { + public: + using FF = FF_; + static constexpr size_t READ_TERMS = 1; + static constexpr size_t WRITE_TERMS = 1; + // 1 + polynomial degree of this relation + static constexpr size_t LENGTH = 5; // both subrelations are degree 4 + + static constexpr std::array SUBRELATION_PARTIAL_LENGTHS{ + LENGTH, // inverse construction sub-relation + LENGTH // log derivative lookup argument sub-relation + }; + + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1036): Scrutinize these adjustment factors. Counting + // degrees suggests the first subrelation should require an adjustment of 2. + static constexpr std::array TOTAL_LENGTH_ADJUSTMENTS{ + 1, // inverse construction sub-relation + 1 // log derivative lookup argument sub-relation + }; + + static constexpr std::array SUBRELATION_LINEARLY_INDEPENDENT = { true, false }; + + template inline static bool skip(const AllEntities& in) + { + // Ensure the input does not contain a lookup gate or data that is being read + return in.q_lookup.is_zero() && in.lookup_read_counts.is_zero(); + } + + /** + * @brief Does the provided row contain data relevant to table lookups; Used to determine whether the polynomial of + * inverses must be computed at a given row + * @details In order to avoid unnecessary computation, the polynomial of inverses I is only computed for rows at + * which the lookup relation is "active". It is active if either (1) the present row contains a lookup gate (i.e. + * q_lookup == 1), or (2) the present row contains table data that has been looked up in this circuit + * (lookup_read_tags == 1, or equivalently, if the row in consideration has index i, the data in polynomials table_i + * has been utlized in the circuit). + * + */ + template static bool operation_exists_at_row(const AllValues& row) + { + // is the row a lookup gate or does it contain table data that has been read at some point in this circuit + return (row.q_lookup == 1) || (row.lookup_read_tags == 1); + } + + // Get the inverse polynomial for this relation + template static auto& get_inverse_polynomial(AllEntities& in) { return in.lookup_inverses; } + + // Used in the inverse correctness subrelation; facilitates only computing inverses where necessary + template + static Accumulator compute_inverse_exists(const AllEntities& in) + { + using View = typename Accumulator::View; + + const auto row_has_write = View(in.lookup_read_tags); + const auto row_has_read = View(in.q_lookup); + return row_has_write + row_has_read - (row_has_write * row_has_read); + } + + template + static Accumulator lookup_read_counts(const AllEntities& in) + { + using View = typename Accumulator::View; + return Accumulator(View(in.lookup_read_counts)); + } + + // Compute table_1 + gamma + table_2 * eta + table_3 * eta_2 + table_4 * eta_3 + template + static Accumulator compute_write_term(const AllEntities& in, const Parameters& params) + { + using View = typename Accumulator::View; + using ParameterView = GetParameterView; + + static_assert(write_index < WRITE_TERMS); + + const auto& gamma = ParameterView(params.gamma); + const auto& eta = ParameterView(params.eta); + const auto& eta_two = ParameterView(params.eta_two); + const auto& eta_three = ParameterView(params.eta_three); + + auto table_1 = View(in.table_1); + auto table_2 = View(in.table_2); + auto table_3 = View(in.table_3); + auto table_4 = View(in.table_4); + + return table_1 + gamma + table_2 * eta + table_3 * eta_two + table_4 * eta_three; + } + + template + static Accumulator compute_read_term(const AllEntities& in, const Parameters& params) + { + using View = typename Accumulator::View; + using ParameterView = GetParameterView; + + const auto& gamma = ParameterView(params.gamma); + const auto& eta = ParameterView(params.eta); + const auto& eta_two = ParameterView(params.eta_two); + const auto& eta_three = ParameterView(params.eta_three); + + auto w_1 = View(in.w_l); + auto w_2 = View(in.w_r); + auto w_3 = View(in.w_o); + + auto w_1_shift = View(in.w_l_shift); + auto w_2_shift = View(in.w_r_shift); + auto w_3_shift = View(in.w_o_shift); + + auto table_index = View(in.q_o); + auto negative_column_1_step_size = View(in.q_r); + auto negative_column_2_step_size = View(in.q_m); + auto negative_column_3_step_size = View(in.q_c); + + // The wire values for lookup gates are accumulators structured in such a way that the differences w_i - + // step_size*w_i_shift result in values present in column i of a corresponding table. See the documentation in + // method get_lookup_accumulators() in for a detailed explanation. + auto derived_table_entry_1 = w_1 + gamma + negative_column_1_step_size * w_1_shift; + auto derived_table_entry_2 = w_2 + negative_column_2_step_size * w_2_shift; + auto derived_table_entry_3 = w_3 + negative_column_3_step_size * w_3_shift; + + // (w_1 + q_2*w_1_shift) + η(w_2 + q_m*w_2_shift) + η₂(w_3 + q_c*w_3_shift) + η₃q_index. + // deg 2 or 3 + return derived_table_entry_1 + derived_table_entry_2 * eta + derived_table_entry_3 * eta_two + + table_index * eta_three; + } + + /** + * @brief Log-derivative style lookup argument for conventional lookups form tables with 3 or fewer columns + * @details The identity to be checked is of the form + * + * \sum{i=0}^{n-1} \frac{read_counts_i}{write_term_i} - \frac{q_lookup}{read_term_i} = 0 + * + * where write_term = table_col_1 + \gamma + table_col_2 * \eta_1 + table_col_3 * \eta_2 + table_index * \eta_3 + * and read_term = derived_table_entry_1 + \gamma + derived_table_entry_2 * \eta_1 + derived_table_entry_3 * \eta_2 + * + table_index * \eta_3, with derived_table_entry_i = w_i - col_step_size_i\cdot w_i_shift. (The table entries + * must be 'derived' from wire values in this way since the stored witnesses are actually successive accumulators, + * the differences of which are equal to entries in a table. This is an efficiency trick to avoid using additional + * gates to reconstruct full size values from the limbs contained in tables). + * + * In practice this identity is expressed in terms of polynomials by defining a polynomial of inverses I_i = + * \frac{1}{read_term_i\cdot write_term_i} then rewriting the above identity as + * + * (1) \sum{i=0}^{n-1} (read_counts_i\cdot I_i\cdot read_term_i) - (q_lookup\cdot I_i\cdot write_term_i) = 0 + * + * This requires a second subrelation to check that polynomial I was computed correctly. For all i, it must hold + * that + * + * (2) I_i\cdot read_term_i\cdot write_term_i - 1 = 0 + * + * Note that (1) is 'linearly dependent' in the sense that it holds only as a sum across the entire execution trace. + * (2) on the other hand holds independently at every row. Finally, note that to avoid unnecessary computation, we + * only compute I_i at indices where the relation is 'active', i.e. on rows which either contain a lookup gate or + * table data that has been read. For inactive rows i, we set I_i = 0. We can thus rewrite (2) as + * + * (2) I_i\cdot read_term_i\cdot write_term_i - is_active_i + * + * where is_active = q_lookup + read_tags - q_lookup\cdot read_tags + * + * and read_tags is a polynomial taking boolean values indicating whether the table entry at the corresponding row + * has been read or not. + * @note This relation utilizes functionality in the log-derivative library to compute the polynomial of inverses + * + */ + template + static void accumulate(ContainerOverSubrelations& accumulator, + const AllEntities& in, + const Parameters& params, + const FF& scaling_factor) + { + BB_OP_COUNT_TIME_NAME("Lookup::accumulate"); + using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; + using View = typename Accumulator::View; + + const auto inverses = View(in.lookup_inverses); // Degree 1 + const auto read_counts = View(in.lookup_read_counts); // Degree 1 + const auto read_selector = View(in.q_lookup); // Degree 1 + const auto inverse_exists = compute_inverse_exists(in); // Degree 2 + const auto read_term = compute_read_term(in, params); // Degree 2 (3) + const auto write_term = compute_write_term(in, params); // Degree 1 (2) + const auto write_inverse = inverses * read_term; // Degree 3 (4) + const auto read_inverse = inverses * write_term; // Degree 2 (3) + + // Establish the correctness of the polynomial of inverses I. Note: inverses is computed so that the value is 0 + // if !inverse_exists. + // Degrees: 2 (3) 1 (2) 1 1 + std::get<0>(accumulator) += (read_term * write_term * inverses - inverse_exists) * scaling_factor; // Deg 4 (6) + + // Establish validity of the read. Note: no scaling factor here since this constraint is 'linearly dependent, + // i.e. enforced across the entire trace, not on a per-row basis. + // Degrees: 1 2 (3) 1 3 (4) + std::get<1>(accumulator) += read_selector * read_inverse - read_counts * write_inverse; // Deg 4 (5) + } +}; + +template using LogDerivLookupRelation = Relation>; + +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/relations/lookup_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/lookup_relation.hpp deleted file mode 100644 index 46b70df7cab..00000000000 --- a/barretenberg/cpp/src/barretenberg/relations/lookup_relation.hpp +++ /dev/null @@ -1,224 +0,0 @@ -#pragma once -#include "barretenberg/relations/relation_types.hpp" - -namespace bb { - -/** - * @brief LookupRelationImpl defines the algebra for the lookup polynomial: - * - * ∏ (1 + β) ⋅ (q_lookup*f_k + γ) ⋅ (t_k + βt_{k+1} + γ(1 + β)) - * Z_lookup(g^j) = -------------------------------------------------------------------------- - * ∏ (s_k + βs_{k+1} + γ(1 + β)) - * - * - * The method `compute_numerator_term` computes polynomials f, t and incorporate them into terms that are ultimately - * needed to construct the grand product polynomial Z_lookup(X): Note 1: In the above, 't' is associated with table - * values (and is not to be confused with the quotient polynomial, also refered to as 't' elsewhere). Polynomial 's' is - * the sorted concatenation of the witnesses and the table values. - * - * @tparam FF parametrises the prime field class being used - */ -template class LookupRelationImpl { - public: - using FF = FF_; - - static constexpr std::array SUBRELATION_PARTIAL_LENGTHS{ - 6, // grand product construction sub-relation - 3 // left-shiftable polynomial sub-relation - }; - - static constexpr std::array TOTAL_LENGTH_ADJUSTMENTS{ - 4, // grand product construction sub-relation - 0 // left-shiftable polynomial sub-relation - }; - - /** - * @brief Returns true if the contribution from all subrelations for the provided inputs is identically zero - * - */ - template inline static bool skip([[maybe_unused]] const AllEntities& in) - { - // TODO(https://github.com/AztecProtocol/barretenberg/issues/952): figure out why skip condition described in - // issue causes failures in acir tests. - return false; - } - - /** - * @brief Get the grand product polynomial object (either from the proving key or AllEntities depending on context) - * - * @param input - * @return auto& either std::span or Flavor::Polynomial depending on context - */ - inline static auto& get_grand_product_polynomial(auto& input) { return input.z_lookup; } - - /** - * @brief Get the shifted grand product polynomial object (either from the proving key or AllEntities depending on - * context) - * - * @param input - * @return auto& either std::span or Flavor::Polynomial depending on context - */ - inline static auto& get_shifted_grand_product_polynomial(auto& input) { return input.z_lookup_shift; } - - /** - * @brief Compute numerator term of the lookup relation: - * - * N_{index} = (1 + β) ⋅ ∏ (q_lookup*f_k + γ) ⋅ (t_k + βt_{k+1} + γ(1 + β)) - * - * @tparam AccumulatorTypes - * @param in - * @param relation_parameters - * @param index If calling this method over vector inputs, index >= 0 - */ - template - inline static Accumulator compute_grand_product_numerator(const AllEntities& in, const Parameters& params) - { - using View = typename Accumulator::View; - using ParameterView = GetParameterView; - - const auto& beta = ParameterView(params.beta); - const auto& gamma = ParameterView(params.gamma); - const auto& eta = ParameterView(params.eta); - const auto& eta_two = ParameterView(params.eta_two); - const auto& eta_three = ParameterView(params.eta_three); - - const auto one_plus_beta = beta + FF(1); - const auto gamma_by_one_plus_beta = gamma * one_plus_beta; - - auto w_1 = View(in.w_l); - auto w_2 = View(in.w_r); - auto w_3 = View(in.w_o); - - auto w_1_shift = View(in.w_l_shift); - auto w_2_shift = View(in.w_r_shift); - auto w_3_shift = View(in.w_o_shift); - - auto table_1 = View(in.table_1); - auto table_2 = View(in.table_2); - auto table_3 = View(in.table_3); - auto table_4 = View(in.table_4); - - auto table_1_shift = View(in.table_1_shift); - auto table_2_shift = View(in.table_2_shift); - auto table_3_shift = View(in.table_3_shift); - auto table_4_shift = View(in.table_4_shift); - - auto table_index = View(in.q_o); - auto column_1_step_size = View(in.q_r); - auto column_2_step_size = View(in.q_m); - auto column_3_step_size = View(in.q_c); - auto q_lookup = View(in.q_lookup); - - // (w_1 + q_2*w_1_shift) + η(w_2 + q_m*w_2_shift) + η₂(w_3 + q_c*w_3_shift) + η₃q_index. - // deg 2 or 3 - auto wire_accum = (w_1 + column_1_step_size * w_1_shift) + (w_2 + column_2_step_size * w_2_shift) * eta + - (w_3 + column_3_step_size * w_3_shift) * eta_two + table_index * eta_three; - - // t_1 + ηt_2 + η₂t_3 + η₃t_4 - // deg 1 or 2 - auto table_accum = table_1 + table_2 * eta + table_3 * eta_two + table_4 * eta_three; - - // t_1_shift + ηt_2_shift + η₂t_3_shift + η₃t_4_shift - // deg 1 or 2 - auto table_accum_shift = - table_1_shift + table_2_shift * eta + table_3_shift * eta_two + table_4_shift * eta_three; - - auto tmp = (q_lookup * wire_accum + gamma); // deg 3 or 4 - tmp *= (table_accum + table_accum_shift * beta + gamma_by_one_plus_beta); // 1 or 3 - tmp *= one_plus_beta; // deg 0 or 1 - return tmp; // deg 4 or 8 - } - - /** - * @brief Compute denominator term of the lookup relation: - * - * (s_k + βs_{k+1} + γ(1 + β)) - * - * @tparam AccumulatorTypes - * @param in - * @param relation_parameters - * @param index - */ - template - inline static Accumulator compute_grand_product_denominator(const AllEntities& in, const Parameters& params) - { - - using View = typename Accumulator::View; - using ParameterView = GetParameterView; - - const auto& beta = ParameterView(params.beta); - const auto& gamma = ParameterView(params.gamma); - - const auto one_plus_beta = beta + FF(1); - const auto gamma_by_one_plus_beta = gamma * one_plus_beta; // deg 0 or 2 - - // Contribution (1) - auto s_accum = View(in.sorted_accum); - auto s_accum_shift = View(in.sorted_accum_shift); - - auto tmp = (s_accum + s_accum_shift * beta + gamma_by_one_plus_beta); // 1 or 2 - return tmp; - } - - /** - * @brief Compute contribution of the lookup grand prod relation for a given edge (internal function) - * - * @details This the relation confirms faithful calculation of the lookup grand - * product polynomial Z_lookup. The contribution is - * z_lookup * (1 + β) * [q_lookup * f + γ] * (t_accum_k + βt_accum_{k+1} + γ(1 + β)) - - * z_lookup_shift * (s_accum_k + βs_accum_{k+1} + γ(1 + β)) - * where - * f = (w_1 + q_2*w_1_shift) + η(w_2 + q_m*w_2_shift) + η²(w_3 + q_c*w_3_shift) + η³q_index, - * t_accum = table_1 + ηtable_2 + η²table_3 + η³table_4, and - * s_accum = s_1 + ηs_2 + η²s_3 + η³s_4. - * Note: Selectors q_2, q_m and q_c are repurposed as 'column step size' for lookup gates. - * - * @param evals transformed to `evals + C(in(X)...)*scaling_factor` - * @param in an std::array containing the fully extended Univariate edges. - * @param parameters contains beta, gamma, and public_input_delta, .... - * @param scaling_factor optional term to scale the evaluation before adding to evals. - */ - template - inline static void accumulate(ContainerOverSubrelations& accumulators, - const AllEntities& in, - const Parameters& params, - const FF& scaling_factor) - { - BB_OP_COUNT_TIME_NAME("Lookup::accumulate"); - { - using Accumulator = std::tuple_element_t<0, ContainerOverSubrelations>; - using View = typename Accumulator::View; - using ParameterView = GetParameterView; - - const auto& grand_product_delta = ParameterView(params.lookup_grand_product_delta); - - auto z_lookup = View(in.z_lookup); - auto z_lookup_shift = View(in.z_lookup_shift); - - auto lagrange_first = View(in.lagrange_first); - auto lagrange_last = View(in.lagrange_last); - - const auto lhs = compute_grand_product_numerator(in, params); // deg 4 or 8 - const auto rhs = compute_grand_product_denominator(in, params); // deg 1 or 2 - - // (deg 5 or 9) - (deg 3 or 5) - const auto tmp = - lhs * (z_lookup + lagrange_first) - rhs * (z_lookup_shift + lagrange_last * grand_product_delta); - std::get<0>(accumulators) += tmp * scaling_factor; - }; - - { - using Accumulator = std::tuple_element_t<1, ContainerOverSubrelations>; - using View = typename Accumulator::View; - auto z_lookup_shift = View(in.z_lookup_shift); - auto lagrange_last = View(in.lagrange_last); - - // Contribution (2) - std::get<1>(accumulators) += (lagrange_last * z_lookup_shift) * scaling_factor; - }; - }; -}; - -template using LookupRelation = Relation>; - -} // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/relations/ultra_relation_consistency.test.cpp b/barretenberg/cpp/src/barretenberg/relations/ultra_relation_consistency.test.cpp index 0406f82bcef..b40b45da3c6 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ultra_relation_consistency.test.cpp +++ b/barretenberg/cpp/src/barretenberg/relations/ultra_relation_consistency.test.cpp @@ -16,7 +16,6 @@ #include "barretenberg/relations/auxiliary_relation.hpp" #include "barretenberg/relations/delta_range_constraint_relation.hpp" #include "barretenberg/relations/elliptic_relation.hpp" -#include "barretenberg/relations/lookup_relation.hpp" #include "barretenberg/relations/permutation_relation.hpp" #include "barretenberg/relations/poseidon2_external_relation.hpp" #include "barretenberg/relations/poseidon2_internal_relation.hpp" @@ -204,84 +203,6 @@ TEST_F(UltraRelationConsistency, UltraPermutationRelation) run_test(/*random_inputs=*/true); }; -TEST_F(UltraRelationConsistency, LookupRelation) -{ - const auto run_test = [](bool random_inputs) { - using Relation = LookupRelation; - using SumcheckArrayOfValuesOverSubrelations = typename Relation::SumcheckArrayOfValuesOverSubrelations; - - const InputElements input_elements = random_inputs ? InputElements::get_random() : InputElements::get_special(); - const auto& w_1 = input_elements.w_l; - const auto& w_2 = input_elements.w_r; - const auto& w_3 = input_elements.w_o; - - const auto& w_1_shift = input_elements.w_l_shift; - const auto& w_2_shift = input_elements.w_r_shift; - const auto& w_3_shift = input_elements.w_o_shift; - - const auto& table_1 = input_elements.table_1; - const auto& table_2 = input_elements.table_2; - const auto& table_3 = input_elements.table_3; - const auto& table_4 = input_elements.table_4; - - const auto& table_1_shift = input_elements.table_1_shift; - const auto& table_2_shift = input_elements.table_2_shift; - const auto& table_3_shift = input_elements.table_3_shift; - const auto& table_4_shift = input_elements.table_4_shift; - - const auto& s_accum = input_elements.sorted_accum; - const auto& s_accum_shift = input_elements.sorted_accum_shift; - const auto& z_lookup = input_elements.z_lookup; - const auto& z_lookup_shift = input_elements.z_lookup_shift; - - const auto& table_index = input_elements.q_o; - const auto& column_1_step_size = input_elements.q_r; - const auto& column_2_step_size = input_elements.q_m; - const auto& column_3_step_size = input_elements.q_c; - const auto& q_lookup = input_elements.q_lookup; - - const auto& lagrange_first = input_elements.lagrange_first; - const auto& lagrange_last = input_elements.lagrange_last; - - SumcheckArrayOfValuesOverSubrelations expected_values; - - const auto parameters = RelationParameters::get_random(); - - const auto eta = parameters.eta; - const auto eta_two = parameters.eta_two; - const auto eta_three = parameters.eta_three; - const auto beta = parameters.beta; - const auto gamma = parameters.gamma; - auto grand_product_delta = parameters.lookup_grand_product_delta; - - // Extract the extended edges for manual computation of relation contribution - auto one_plus_beta = FF::one() + beta; - auto gamma_by_one_plus_beta = gamma * one_plus_beta; - - auto wire_accum = (w_1 + column_1_step_size * w_1_shift) + (w_2 + column_2_step_size * w_2_shift) * eta + - (w_3 + column_3_step_size * w_3_shift) * eta_two + table_index * eta_three; - - auto table_accum = table_1 + table_2 * eta + table_3 * eta_two + table_4 * eta_three; - auto table_accum_shift = - table_1_shift + table_2_shift * eta + table_3_shift * eta_two + table_4_shift * eta_three; - - // Contribution 1 - auto contribution_1 = (z_lookup + lagrange_first) * (q_lookup * wire_accum + gamma) * - (table_accum + table_accum_shift * beta + gamma_by_one_plus_beta) * one_plus_beta; - contribution_1 -= (z_lookup_shift + lagrange_last * grand_product_delta) * - (s_accum + s_accum_shift * beta + gamma_by_one_plus_beta); - expected_values[0] = contribution_1; - - // Contribution 2 - auto contribution_2 = z_lookup_shift * lagrange_last; - expected_values[1] = contribution_2; - - validate_relation_execution(expected_values, input_elements, parameters); - }; - run_test(/*random_inputs=*/false); - run_test(/*random_inputs=*/true); -}; - TEST_F(UltraRelationConsistency, DeltaRangeConstraintRelation) { const auto run_test = [](bool random_inputs) { diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/protogalaxy_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/protogalaxy_recursive_verifier.cpp index daedb38fd71..0a1b48068bd 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/protogalaxy_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/protogalaxy_recursive_verifier.cpp @@ -53,17 +53,26 @@ void ProtoGalaxyRecursiveVerifier_::receive_and_finalise_inst domain_separator + "_" + labels.return_data_read_counts); } - // Get challenge for sorted list batching and wire four memory records commitment + // Get eta challenges auto [eta, eta_two, eta_three] = transcript->template get_challenges( domain_separator + "_eta", domain_separator + "_eta_two", domain_separator + "_eta_three"); - witness_commitments.sorted_accum = - transcript->template receive_from_prover(domain_separator + "_" + labels.sorted_accum); + + // Receive commitments to lookup argument polynomials + witness_commitments.lookup_read_counts = + transcript->template receive_from_prover(domain_separator + "_" + labels.lookup_read_counts); + witness_commitments.lookup_read_tags = + transcript->template receive_from_prover(domain_separator + "_" + labels.lookup_read_tags); + + // Receive commitments to wire 4 witness_commitments.w_4 = transcript->template receive_from_prover(domain_separator + "_" + labels.w_4); // Get permutation challenges and commitment to permutation and lookup grand products auto [beta, gamma] = transcript->template get_challenges(domain_separator + "_beta", domain_separator + "_gamma"); + witness_commitments.lookup_inverses = transcript->template receive_from_prover( + domain_separator + "_" + commitment_labels.lookup_inverses); + // If Goblin (i.e. using DataBus) receive commitments to log-deriv inverses polynomial if constexpr (IsGoblinFlavor) { witness_commitments.calldata_inverses = transcript->template receive_from_prover( @@ -74,8 +83,6 @@ void ProtoGalaxyRecursiveVerifier_::receive_and_finalise_inst witness_commitments.z_perm = transcript->template receive_from_prover(domain_separator + "_" + labels.z_perm); - witness_commitments.z_lookup = - transcript->template receive_from_prover(domain_separator + "_" + labels.z_lookup); // Compute correction terms for grand products const FF public_input_delta = diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/ultra_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/ultra_recursive_verifier.cpp index a0cc18d3141..f609464efec 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/ultra_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/ultra_recursive_verifier.cpp @@ -91,19 +91,25 @@ std::array UltraRecursiveVerifier_::ve transcript->template receive_from_prover(commitment_labels.return_data_read_counts); } - // Get challenge for sorted list batching and wire four memory records + // Get eta challenges; used in RAM/ROM memory records and log derivative lookup argument auto [eta, eta_two, eta_three] = transcript->template get_challenges("eta", "eta_two", "eta_three"); relation_parameters.eta = eta; relation_parameters.eta_two = eta_two; relation_parameters.eta_three = eta_three; - // Get commitments to sorted list accumulator and fourth wire - commitments.sorted_accum = transcript->template receive_from_prover(commitment_labels.sorted_accum); + // Get commitments to lookup argument polynomials and fourth wire + commitments.lookup_read_counts = + transcript->template receive_from_prover(commitment_labels.lookup_read_counts); + commitments.lookup_read_tags = + transcript->template receive_from_prover(commitment_labels.lookup_read_tags); commitments.w_4 = transcript->template receive_from_prover(commitment_labels.w_4); // Get permutation challenges auto [beta, gamma] = transcript->template get_challenges("beta", "gamma"); + commitments.lookup_inverses = + transcript->template receive_from_prover(commitment_labels.lookup_inverses); + // If Goblin (i.e. using DataBus) receive commitments to log-deriv inverses polynomial if constexpr (IsGoblinFlavor) { commitments.calldata_inverses = @@ -123,7 +129,6 @@ std::array UltraRecursiveVerifier_::ve // Get commitment to permutation and lookup grand products commitments.z_perm = transcript->template receive_from_prover(commitment_labels.z_perm); - commitments.z_lookup = transcript->template receive_from_prover(commitment_labels.z_lookup); // Execute Sumcheck Verifier and extract multivariate opening point u = (u_0, ..., u_{d-1}) and purported // multivariate evaluations at u diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/verifier.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/verifier.test.cpp index f8658a39fe3..605b44b702f 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/verifier.test.cpp @@ -227,7 +227,7 @@ template class RecursiveVerifierTest : public testing // Arbitrarily tamper with the proof to be verified inner_prover.transcript->deserialize_full_transcript(); - inner_prover.transcript->sorted_accum_comm = InnerCommitment::one() * InnerFF::random_element(); + inner_prover.transcript->z_perm_comm = InnerCommitment::one() * InnerFF::random_element(); inner_prover.transcript->serialize_full_transcript(); inner_proof = inner_prover.export_proof(); diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/grand_product_library.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/grand_product_library.test.cpp index 9ab7c789f67..0bf80a8db8c 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/grand_product_library.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/grand_product_library.test.cpp @@ -146,134 +146,6 @@ template class GrandProductTests : public testing::Test { // Check consistency between locally computed z_perm and the one computed by the prover library EXPECT_EQ(prover_polynomials.z_perm, z_permutation_expected); }; - - /** - * @brief Check consistency of the computation of the lookup grand product polynomial z_lookup. - * @details This test compares a simple, unoptimized, easily readable calculation of the grand product z_lookup - * to the optimized implementation used by the prover. It's purpose is to provide confidence that some optimization - * introduced into the calculation has not changed the result. - * @note This test does confirm the correctness of z_lookup, only that the two implementations yield an - * identical result. - */ - static void test_lookup_grand_product_construction() - { - using Flavor = UltraFlavor; - using ProverPolynomials = typename Flavor::ProverPolynomials; - - // Set a mock circuit size - static const size_t circuit_size = 8; - - // Construct a ProverPolynomials object with completely random polynomials - ProverPolynomials prover_polynomials; - for (auto& poly : prover_polynomials.get_unshifted()) { - poly = get_random_polynomial(circuit_size); - poly[0] = 0; // for shiftability - } - prover_polynomials.set_shifted(); - - // Get random challenges - auto beta = FF::random_element(); - auto gamma = FF::random_element(); - auto eta = FF::random_element(); - auto eta_two = FF::random_element(); - auto eta_three = FF::random_element(); - - RelationParameters params{ - .eta = eta, - .eta_two = eta_two, - .eta_three = eta_three, - .beta = beta, - .gamma = gamma, - .public_input_delta = 1, - .lookup_grand_product_delta = 1, - }; - - // Method 1: Compute z_lookup using the prover library method - constexpr size_t LOOKUP_RELATION_INDEX = 1; - using LHS = typename std::tuple_element::type; - using RHS = LookupRelation; - static_assert(std::same_as); - compute_grand_product(prover_polynomials, params); - - // Method 2: Compute the lookup grand product polynomial Z_lookup: - // - // ∏(1 + β) ⋅ ∏(q_lookup*f_k + γ) ⋅ ∏(t_k + βt_{k+1} + γ(1 + β)) - // Z_lookup(X_j) = ----------------------------------------------------------------- - // ∏(s_k + βs_{k+1} + γ(1 + β)) - // - // in a way that is simple to read (but inefficient). See prover library method for more details. - - std::array accumulators; - for (size_t i = 0; i < 4; ++i) { - accumulators[i] = Polynomial{ circuit_size }; - } - - // Step (1) - - auto wires = prover_polynomials.get_wires(); - auto tables = prover_polynomials.get_tables(); - auto sorted_batched = prover_polynomials.sorted_accum; - auto column_1_step_size = prover_polynomials.q_r; - auto column_2_step_size = prover_polynomials.q_m; - auto column_3_step_size = prover_polynomials.q_c; - auto lookup_index_selector = prover_polynomials.q_o; - auto lookup_selector = prover_polynomials.q_lookup; - - // Note: block_mask is used for efficient modulus, i.e. i % N := i & (N-1), for N = 2^k - const size_t block_mask = circuit_size - 1; - // Initialize 't(X)' to be used in an expression of the form t(X) + β*t(Xω) - FF table_i = tables[0][0] + tables[1][0] * eta + tables[2][0] * eta_two + tables[3][0] * eta_three; - for (size_t i = 0; i < circuit_size; ++i) { - size_t shift_idx = (i + 1) & block_mask; - - // f = (w_1 + q_2*w_1(Xω)) + η(w_2 + q_m*w_2(Xω)) + η²(w_3 + q_c*w_3(Xω)) + η³q_index. - FF f_i = (wires[0][i] + wires[0][shift_idx] * column_1_step_size[i]) + - (wires[1][i] + wires[1][shift_idx] * column_2_step_size[i]) * eta + - (wires[2][i] + wires[2][shift_idx] * column_3_step_size[i]) * eta_two + - eta_three * lookup_index_selector[i]; - - // q_lookup * f + γ - accumulators[0][i] = lookup_selector[i] * f_i + gamma; - - // t = t_1 + ηt_2 + η²t_3 + η³t_4 - FF table_i_plus_1 = tables[0][shift_idx] + eta * tables[1][shift_idx] + eta_two * tables[2][shift_idx] + - eta_three * tables[3][shift_idx]; - - // t + βt(Xω) + γ(1 + β) - accumulators[1][i] = table_i + table_i_plus_1 * beta + gamma * (FF::one() + beta); - - // (1 + β) - accumulators[2][i] = FF::one() + beta; - - // s + βs(Xω) + γ(1 + β) - accumulators[3][i] = sorted_batched[i] + beta * sorted_batched[shift_idx] + gamma * (FF::one() + beta); - - // Set t(X_i) for next iteration - table_i = table_i_plus_1; - } - - // Step (2) - for (auto& accum : accumulators) { - for (size_t i = 0; i < circuit_size - 1; ++i) { - accum[i + 1] *= accum[i]; - } - } - - // Step (3) - Polynomial z_lookup_expected(circuit_size); - z_lookup_expected[0] = FF::zero(); // Z_lookup_0 = 0 - - // Compute the numerator in accumulators[0]; The denominator is in accumulators[3] - for (size_t i = 0; i < circuit_size - 1; ++i) { - accumulators[0][i] *= accumulators[1][i] * accumulators[2][i]; - } - // Compute Z_lookup_i, i = [1, n-1] - for (size_t i = 0; i < circuit_size - 1; ++i) { - z_lookup_expected[i + 1] = accumulators[0][i] / accumulators[3][i]; - } - - EXPECT_EQ(prover_polynomials.z_lookup, z_lookup_expected); - }; }; using FieldTypes = testing::Types; @@ -283,8 +155,3 @@ TYPED_TEST(GrandProductTests, GrandProductPermutation) { TestFixture::template test_permutation_grand_product_construction(); } - -TYPED_TEST(GrandProductTests, GrandProductLookup) -{ - TestFixture::test_lookup_grand_product_construction(); -} diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp index 267c8643f4f..45467f78325 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp @@ -13,7 +13,7 @@ #include "barretenberg/relations/delta_range_constraint_relation.hpp" #include "barretenberg/relations/ecc_op_queue_relation.hpp" #include "barretenberg/relations/elliptic_relation.hpp" -#include "barretenberg/relations/lookup_relation.hpp" +#include "barretenberg/relations/logderiv_lookup_relation.hpp" #include "barretenberg/relations/permutation_relation.hpp" #include "barretenberg/relations/poseidon2_external_relation.hpp" #include "barretenberg/relations/poseidon2_internal_relation.hpp" @@ -39,24 +39,23 @@ class MegaFlavor { static constexpr size_t NUM_WIRES = CircuitBuilder::NUM_WIRES; // The number of multivariate polynomials on which a sumcheck prover sumcheck operates (including shifts). We often // need containers of this size to hold related data, so we choose a name more agnostic than `NUM_POLYNOMIALS`. - // Note: this number does not include the individual sorted list polynomials. - static constexpr size_t NUM_ALL_ENTITIES = 58; + static constexpr size_t NUM_ALL_ENTITIES = 57; // The number of polynomials precomputed to describe a circuit and to aid a prover in constructing a satisfying // assignment of witnesses. We again choose a neutral name. static constexpr size_t NUM_PRECOMPUTED_ENTITIES = 30; // The total number of witness entities not including shifts. - static constexpr size_t NUM_WITNESS_ENTITIES = 17; + static constexpr size_t NUM_WITNESS_ENTITIES = 18; // Total number of folded polynomials, which is just all polynomials except the shifts static constexpr size_t NUM_FOLDED_ENTITIES = NUM_PRECOMPUTED_ENTITIES + NUM_WITNESS_ENTITIES; - using GrandProductRelations = std::tuple, bb::LookupRelation>; + using GrandProductRelations = std::tuple>; // define the tuple of Relations that comprise the Sumcheck relation // Note: made generic for use in MegaRecursive. template using Relations_ = std::tuple, bb::UltraPermutationRelation, - bb::LookupRelation, + bb::LogDerivLookupRelation, bb::DeltaRangeConstraintRelation, bb::EllipticRelation, bb::AuxiliaryRelation, @@ -66,8 +65,6 @@ class MegaFlavor { bb::Poseidon2InternalRelation>; using Relations = Relations_; - using LogDerivLookupRelation = bb::DatabusLookupRelation; - static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); static constexpr size_t MAX_TOTAL_RELATION_LENGTH = compute_max_total_relation_length(); @@ -179,19 +176,20 @@ class MegaFlavor { template class DerivedEntities { public: DEFINE_FLAVOR_MEMBERS(DataType, - sorted_accum, // column 4 - z_perm, // column 5 - z_lookup, // column 6 - ecc_op_wire_1, // column 7 - ecc_op_wire_2, // column 8 - ecc_op_wire_3, // column 9 - ecc_op_wire_4, // column 10 - calldata, // column 11 - calldata_read_counts, // column 12 - calldata_inverses, // column 13 - return_data, // column 14 - return_data_read_counts, // column 15 - return_data_inverses); // column 16 + z_perm, // column 4 + lookup_inverses, // column 5 + lookup_read_counts, // column 6 + lookup_read_tags, // column 7 + ecc_op_wire_1, // column 8 + ecc_op_wire_2, // column 9 + ecc_op_wire_3, // column 10 + ecc_op_wire_4, // column 11 + calldata, // column 12 + calldata_read_counts, // column 13 + calldata_inverses, // column 14 + return_data, // column 15 + return_data_read_counts, // column 16 + return_data_inverses); // column 17 }; /** @@ -214,9 +212,10 @@ class MegaFlavor { this->w_r, this->w_o, this->w_4, - this->sorted_accum, this->z_perm, - this->z_lookup, + this->lookup_inverses, + this->lookup_read_counts, + this->lookup_read_tags, this->ecc_op_wire_1, this->ecc_op_wire_2, this->ecc_op_wire_3, @@ -232,18 +231,15 @@ class MegaFlavor { template class ShiftedEntities { public: DEFINE_FLAVOR_MEMBERS(DataType, - table_1_shift, // column 0 - table_2_shift, // column 1 - table_3_shift, // column 2 - table_4_shift, // column 3 - w_l_shift, // column 4 - w_r_shift, // column 5 - w_o_shift, // column 6 - w_4_shift, // column 7 - sorted_accum_shift, // column 8 - z_perm_shift, // column 9 - z_lookup_shift // column 10 - ) + table_1_shift, // column 0 + table_2_shift, // column 1 + table_3_shift, // column 2 + table_4_shift, // column 3 + w_l_shift, // column 4 + w_r_shift, // column 5 + w_o_shift, // column 6 + w_4_shift, // column 7 + z_perm_shift) // column 8 }; public: @@ -281,8 +277,8 @@ class MegaFlavor { auto get_witness() { return WitnessEntities::get_all(); }; auto get_to_be_shifted() { - return RefArray{ this->table_1, this->table_2, this->table_3, this->table_4, this->w_l, this->w_r, - this->w_o, this->w_4, this->sorted_accum, this->z_perm, this->z_lookup }; + return RefArray{ this->table_1, this->table_2, this->table_3, this->table_4, this->w_l, + this->w_r, this->w_o, this->w_4, this->z_perm }; }; auto get_precomputed() { return PrecomputedEntities::get_all(); } auto get_shifted() { return ShiftedEntities::get_all(); }; @@ -351,44 +347,8 @@ class MegaFlavor { std::vector memory_read_records; std::vector memory_write_records; - std::array sorted_polynomials; ProverPolynomials polynomials; // storage for all polynomials evaluated by the prover - void compute_sorted_accumulator_polynomials(const FF& eta, const FF& eta_two, const FF& eta_three) - { - // Compute sorted witness-table accumulator - compute_sorted_list_accumulator(eta, eta_two, eta_three); - - // Finalize fourth wire polynomial by adding lookup memory records - add_plookup_memory_records_to_wire_4(eta, eta_two, eta_three); - } - - /** - * @brief Construct sorted list accumulator polynomial 's'. - * - * @details Compute s = s_1 + η*s_2 + η²*s_3 + η³*s_4 (via Horner) where s_i are the - * sorted concatenated witness/table polynomials - * - * @param key proving key - * @param sorted_list_polynomials sorted concatenated witness/table polynomials - * @param eta random challenge - * @return Polynomial - */ - void compute_sorted_list_accumulator(const FF& eta, const FF& eta_two, const FF& eta_three) - { - - auto& sorted_list_accumulator = polynomials.sorted_accum; - - // Construct s via Horner, i.e. s = s_1 + η(s_2 + η(s_3 + η*s_4)) - for (size_t i = 0; i < this->circuit_size; ++i) { - FF T0 = sorted_polynomials[3][i] * eta_three; - T0 += sorted_polynomials[2][i] * eta_two; - T0 += sorted_polynomials[1][i] * eta; - T0 += sorted_polynomials[0][i]; - sorted_list_accumulator[i] = T0; - } - } - /** * @brief Add plookup memory records to the fourth wire polynomial * @@ -398,7 +358,7 @@ class MegaFlavor { * @tparam Flavor * @param eta challenge produced after commitment to first three wire polynomials */ - void add_plookup_memory_records_to_wire_4(const FF& eta, const FF& eta_two, const FF& eta_three) + void add_ram_rom_memory_records_to_wire_4(const FF& eta, const FF& eta_two, const FF& eta_three) { // The plookup memory record values are computed at the indicated indices as // w4 = w3 * eta^3 + w2 * eta^2 + w1 * eta + read_write_flag; @@ -422,14 +382,18 @@ class MegaFlavor { } /** - * @brief Compute the inverse polynomial used in the databus log derivative lookup argument + * @brief Compute the inverse polynomials used in the log derivative lookup relations * * @tparam Flavor * @param beta * @param gamma */ - void compute_logderivative_inverse(const RelationParameters& relation_parameters) + void compute_logderivative_inverses(const RelationParameters& relation_parameters) { + // Compute inverses for conventional lookups + compute_logderivative_inverse>( + this->polynomials, relation_parameters, this->circuit_size); + // Compute inverses for calldata reads DatabusLookupRelation::compute_logderivative_inverse( this->polynomials, relation_parameters, this->circuit_size); @@ -440,7 +404,7 @@ class MegaFlavor { } /** - * @brief Computes public_input_delta, lookup_grand_product_delta, the z_perm and z_lookup polynomials + * @brief Computes public_input_delta and the permutation grand product polynomial * * @param relation_parameters */ @@ -677,8 +641,9 @@ class MegaFlavor { w_o = "W_O"; w_4 = "W_4"; z_perm = "Z_PERM"; - z_lookup = "Z_LOOKUP"; - sorted_accum = "SORTED_ACCUM"; + lookup_inverses = "LOOKUP_INVERSES"; + lookup_read_counts = "LOOKUP_READ_COUNTS"; + lookup_read_tags = "LOOKUP_READ_TAGS"; ecc_op_wire_1 = "ECC_OP_WIRE_1"; ecc_op_wire_2 = "ECC_OP_WIRE_2"; ecc_op_wire_3 = "ECC_OP_WIRE_3"; @@ -768,9 +733,10 @@ class MegaFlavor { this->w_r = commitments.w_r; this->w_o = commitments.w_o; this->w_4 = commitments.w_4; - this->sorted_accum = commitments.sorted_accum; this->z_perm = commitments.z_perm; - this->z_lookup = commitments.z_lookup; + this->lookup_inverses = commitments.lookup_inverses; + this->lookup_read_counts = commitments.lookup_read_counts; + this->lookup_read_tags = commitments.lookup_read_tags; this->ecc_op_wire_1 = commitments.ecc_op_wire_1; this->ecc_op_wire_2 = commitments.ecc_op_wire_2; this->ecc_op_wire_3 = commitments.ecc_op_wire_3; @@ -811,10 +777,11 @@ class MegaFlavor { Commitment return_data_comm; Commitment return_data_read_counts_comm; Commitment return_data_inverses_comm; - Commitment sorted_accum_comm; Commitment w_4_comm; Commitment z_perm_comm; - Commitment z_lookup_comm; + Commitment lookup_inverses_comm; + Commitment lookup_read_counts_comm; + Commitment lookup_read_tags_comm; std::vector> sumcheck_univariates; std::array sumcheck_evaluations; std::vector zm_cq_comms; @@ -867,10 +834,11 @@ class MegaFlavor { return_data_comm = deserialize_from_buffer(proof_data, num_frs_read); return_data_read_counts_comm = deserialize_from_buffer(proof_data, num_frs_read); return_data_inverses_comm = deserialize_from_buffer(proof_data, num_frs_read); - sorted_accum_comm = deserialize_from_buffer(proof_data, num_frs_read); + lookup_read_counts_comm = deserialize_from_buffer(proof_data, num_frs_read); + lookup_read_tags_comm = deserialize_from_buffer(proof_data, num_frs_read); w_4_comm = deserialize_from_buffer(proof_data, num_frs_read); + lookup_inverses_comm = deserialize_from_buffer(proof_data, num_frs_read); z_perm_comm = deserialize_from_buffer(proof_data, num_frs_read); - z_lookup_comm = deserialize_from_buffer(proof_data, num_frs_read); for (size_t i = 0; i < log_n; ++i) { sumcheck_univariates.push_back( deserialize_from_buffer>(proof_data, @@ -908,10 +876,11 @@ class MegaFlavor { serialize_to_buffer(return_data_comm, proof_data); serialize_to_buffer(return_data_read_counts_comm, proof_data); serialize_to_buffer(return_data_inverses_comm, proof_data); - serialize_to_buffer(sorted_accum_comm, proof_data); + serialize_to_buffer(lookup_read_counts_comm, proof_data); + serialize_to_buffer(lookup_read_tags_comm, proof_data); serialize_to_buffer(w_4_comm, proof_data); + serialize_to_buffer(lookup_inverses_comm, proof_data); serialize_to_buffer(z_perm_comm, proof_data); - serialize_to_buffer(z_lookup_comm, proof_data); for (size_t i = 0; i < log_n; ++i) { serialize_to_buffer(sumcheck_univariates[i], proof_data); } diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mock_circuits.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mock_circuits.hpp index 5d5b3c03953..0682a8a44ab 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mock_circuits.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mock_circuits.hpp @@ -58,6 +58,35 @@ class MockCircuits { } } + /** + * @brief Add lookup gates using the uint32 XOR lookup table (table size 4096) + * @brief Each iteration adds 6 lookup gates and results in a minimum circuit size of 4096 + * + * @param builder + * @param num_gates + */ + template static void add_lookup_gates(Builder& builder, size_t num_iterations = 1) + { + auto UINT32_XOR = plookup::MultiTableId::UINT32_XOR; + + // Each iteration adds 6 lookup gates (due to six 6-bit limbs); the first adds a table of size 4096 + for (size_t i = 0; i < num_iterations; ++i) { + // define some arbitrary inputs to uint32 XOR + uint32_t left_value = engine.get_random_uint32(); + uint32_t right_value = engine.get_random_uint32(); + + fr left = fr{ left_value, 0, 0, 0 }.to_montgomery_form(); + fr right = fr{ right_value, 0, 0, 0 }.to_montgomery_form(); + + auto left_idx = builder.add_variable(left); + auto right_idx = builder.add_variable(right); + + // perform lookups from the uint32 XOR table + auto accumulators = plookup::get_lookup_accumulators(UINT32_XOR, left, right, /*is_2_to_1_lookup*/ true); + builder.create_gates_from_plookup_accumulators(UINT32_XOR, accumulators, left_idx, right_idx); + } + } + /** * @brief Populate a builder with a specified number of arithmetic gates; includes a PI * diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/plookup_tables/plookup_tables.cpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/plookup_tables/plookup_tables.cpp index b897fc8c309..0440eb17b9c 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/plookup_tables/plookup_tables.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/plookup_tables/plookup_tables.cpp @@ -143,14 +143,14 @@ const MultiTable& get_multitable(const MultiTableId id) /** * @brief Given a table ID and the key(s) for a key-value lookup, return the lookup accumulators - * @details In general the number of bits in key/value is greater than what can be efficiently supported in lookup - * tables. For this reason we actually perform lookups on the corresponding limbs. However, since we're interested in - * the full values and not the limbs, its convenient to structure the witnesses of lookup gates to store the former. - * This way we don't have to waste gates reaccumulating the limbs to compute the actual value of interest. The way to do - * this is to populate the wires with 'accumulator' values such that the first gate in the series contains the full - * accumulated values, and successive gates contain prior stages of the accumulator such that wire_i - r*wire_{i-1} = - * v_i, where r = num limb bits and v_i is a limb that explicitly appears in one of the lookup tables. See the detailed - * comment block below for more explanation. + * @details In general the number of bits in original key/value is greater than what can be efficiently supported in + * lookup tables. For this reason we actually perform lookups on the corresponding limbs. However, since we're + * interested in the original values and not the limbs, its convenient to structure the witnesses of lookup gates to + * store the former. This way we don't have to waste gates reaccumulating the limbs to compute the actual value of + * interest. The way to do this is to populate the wires with 'accumulator' values such that the first gate in the + * series contains the full accumulated values, and successive gates contain prior stages of the accumulator such that + * wire_i - r*wire_{i-1} = v_i, where r = num limb bits and v_i is a limb that explicitly appears in one of the lookup + * tables. See the detailed comment block below for more explanation. * * @param id * @param key_a @@ -176,7 +176,7 @@ ReadData get_lookup_accumulators(const MultiTableId id, std::vector column_3_raw_values; for (size_t i = 0; i < num_lookups; ++i) { - // compute the value(s) corresponding to the key(s) using on the i-th basic table query function + // compute the value(s) corresponding to the key(s) using the i-th basic table query function const auto values = multi_table.get_table_values[i]({ key_a_slices[i], key_b_slices[i] }); // store all query data in raw columns and key entry column_1_raw_values.emplace_back(key_a_slices[i]); diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/plookup_tables/types.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/plookup_tables/types.hpp index c41d4e94670..259082820eb 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/plookup_tables/types.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/plookup_tables/types.hpp @@ -122,8 +122,8 @@ enum MultiTableId { }; /** - * @brief Container for managing multiple BasicTables plus the data needed to combine basic table outputs (limbs) into - * accumulators. Does not store actual raw table data. + * @brief Container for managing multiple BasicTables plus the data needed to combine basic table outputs (e.g. limbs) + * into accumulators. Does not store actual raw table data. * @details As a simple example, consider using lookups to compute XOR on uint32_t inputs. To do this we decompose the * inputs into 6 limbs and use a BasicTable for 6-bit XOR lookups. In this case the MultiTable simply manages 6 basic * tables, all of which are the XOR BasicTable. (In many cases all of the BasicTables managed by a MultiTable are @@ -213,7 +213,7 @@ struct MultiTable { // std::array value{ bb::fr(0), bb::fr(0) }; // bool operator<(const KeyEntry& other) const { return key < other.key; } -// std::array to_sorted_list_components(const bool use_two_keys) const +// std::array to_table_components(const bool use_two_keys) const // { // return { // key[0], @@ -248,7 +248,7 @@ struct MultiTable { // return (key.from_montgomery_form() < other.key.from_montgomery_form()); // } -// std::array to_sorted_list_components() const { return { key, values[0], values[0] }; } +// std::array to_table_components() const { return { key, values[0], values[0] }; } // } // BasicTableId id; @@ -268,6 +268,66 @@ struct MultiTable { // } +/** + * @brief A map from 'entry' to 'index' where entry is a row in a BasicTable and index is the row at which that entry + * exists in the table + * @details Such a map is needed to in order to construct read_counts (the polynomial containing the number of reads + * from each entry in a table) for the log-derivative lookup argument. A BasicTable essentially consists of 3 columns, + * and 'lookups' are recorded as rows in this table. The index at which this data exists in the table is not explicitly + * known at the time of lookup gate creation. This map can be used to construct read counts from the set of lookups that + * have been performed via an operation like read_counts[index_map[lookup_data]]++ + * + */ +struct LookupHashTable { + using FF = bb::fr; + using Key = std::array; // an entry in a lookup table + using Value = size_t; // the index of an entry in a lookup table + + // Define a simple hash on three field elements + struct HashFunction { + FF mult_const; + FF const_sqr; + + HashFunction() + : mult_const(FF(uint256_t(0x1337, 0x1336, 0x1335, 0x1334))) + , const_sqr(mult_const.sqr()) + {} + + size_t operator()(const Key& entry) const + { + FF result = entry[0] + mult_const * entry[1] + const_sqr * entry[2]; + return static_cast(result.reduce_once().data[0]); + } + }; + + std::unordered_map index_map; + + LookupHashTable() = default; + + // Initialize the entry-index map with the columns of a table + void initialize(std::vector& column_1, std::vector& column_2, std::vector& column_3) + { + for (size_t i = 0; i < column_1.size(); ++i) { + index_map[{ column_1[i], column_2[i], column_3[i] }] = i; + } + } + + // Given an entry in the table, return its index in the table + Value operator[](const Key& key) const + { + auto it = index_map.find(key); + if (it != index_map.end()) { + return it->second; + } else { + info("LookupHashTable: Key not found!"); + ASSERT(false); + return 0; + } + } + + bool operator==(const LookupHashTable& other) const = default; +}; + /** * @brief A basic table from which we can perform lookups (for example, an xor table) * @details Also stores the lookup gate data for all lookups performed on this table @@ -289,7 +349,8 @@ struct BasicTable { return key[0] < other.key[0] || ((key[0] == other.key[0]) && key[1] < other.key[1]); } - std::array to_sorted_list_components(const bool use_two_keys) const + // Express the key-value pair as the entries of a 3-column row in a table + std::array to_table_components(const bool use_two_keys) const { return { bb::fr(key[0]), @@ -313,6 +374,11 @@ struct BasicTable { std::vector column_3; std::vector lookup_gates; // wire data for all lookup gates created for lookups on this table + // Map from a table entry to its index in the table; used for constructing read counts + LookupHashTable index_map; + + void initialize_index_map() { index_map.initialize(column_1, column_2, column_3); } + std::array (*get_values_from_key)(const std::array); bool operator==(const BasicTable& other) const = default; diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_flavor.hpp index 4671a3cb58d..6ca68d7037e 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_flavor.hpp @@ -12,7 +12,7 @@ #include "barretenberg/relations/auxiliary_relation.hpp" #include "barretenberg/relations/delta_range_constraint_relation.hpp" #include "barretenberg/relations/elliptic_relation.hpp" -#include "barretenberg/relations/lookup_relation.hpp" +#include "barretenberg/relations/logderiv_lookup_relation.hpp" #include "barretenberg/relations/permutation_relation.hpp" #include "barretenberg/relations/relation_parameters.hpp" #include "barretenberg/relations/ultra_arithmetic_relation.hpp" @@ -36,23 +36,22 @@ class UltraFlavor { static constexpr size_t NUM_WIRES = CircuitBuilder::NUM_WIRES; // The number of multivariate polynomials on which a sumcheck prover sumcheck operates (including shifts). We often // need containers of this size to hold related data, so we choose a name more agnostic than `NUM_POLYNOMIALS`. - // Note: this number does not include the individual sorted list polynomials. - static constexpr size_t NUM_ALL_ENTITIES = 43; + static constexpr size_t NUM_ALL_ENTITIES = 42; // The number of polynomials precomputed to describe a circuit and to aid a prover in constructing a satisfying // assignment of witnesses. We again choose a neutral name. static constexpr size_t NUM_PRECOMPUTED_ENTITIES = 25; // The total number of witness entities not including shifts. - static constexpr size_t NUM_WITNESS_ENTITIES = 7; + static constexpr size_t NUM_WITNESS_ENTITIES = 8; // Total number of folded polynomials, which is just all polynomials except the shifts static constexpr size_t NUM_FOLDED_ENTITIES = NUM_PRECOMPUTED_ENTITIES + NUM_WITNESS_ENTITIES; - using GrandProductRelations = std::tuple, bb::LookupRelation>; + using GrandProductRelations = std::tuple>; // define the tuple of Relations that comprise the Sumcheck relation // Note: made generic for use in MegaRecursive. template using Relations_ = std::tuple, bb::UltraPermutationRelation, - bb::LookupRelation, + bb::LogDerivLookupRelation, bb::DeltaRangeConstraintRelation, bb::EllipticRelation, bb::AuxiliaryRelation>; @@ -144,17 +143,18 @@ class UltraFlavor { template class WitnessEntities { public: DEFINE_FLAVOR_MEMBERS(DataType, - w_l, // column 0 - w_r, // column 1 - w_o, // column 2 - w_4, // column 3 - sorted_accum, // column 4 - z_perm, // column 5 - z_lookup) // column 6 + w_l, // column 0 + w_r, // column 1 + w_o, // column 2 + w_4, // column 3 + z_perm, // column 4 + lookup_inverses, // column 5 + lookup_read_counts, // column 6 + lookup_read_tags) // column 7 auto get_wires() { return RefArray{ w_l, w_r, w_o, w_4 }; }; - MSGPACK_FIELDS(w_l, w_r, w_o, w_4, sorted_accum, z_perm, z_lookup); + MSGPACK_FIELDS(w_l, w_r, w_o, w_4, z_perm, lookup_inverses, lookup_read_counts, lookup_read_tags); }; /** @@ -163,22 +163,20 @@ class UltraFlavor { template class ShiftedEntities { public: DEFINE_FLAVOR_MEMBERS(DataType, - table_1_shift, // column 0 - table_2_shift, // column 1 - table_3_shift, // column 2 - table_4_shift, // column 3 - w_l_shift, // column 4 - w_r_shift, // column 5 - w_o_shift, // column 6 - w_4_shift, // column 7 - sorted_accum_shift, // column 8 - z_perm_shift, // column 9 - z_lookup_shift) // column 10 + table_1_shift, // column 0 + table_2_shift, // column 1 + table_3_shift, // column 2 + table_4_shift, // column 3 + w_l_shift, // column 4 + w_r_shift, // column 5 + w_o_shift, // column 6 + w_4_shift, // column 7 + z_perm_shift) // column 10 auto get_shifted() { - return RefArray{ table_1_shift, table_2_shift, table_3_shift, table_4_shift, w_l_shift, w_r_shift, - w_o_shift, w_4_shift, sorted_accum_shift, z_perm_shift, z_lookup_shift }; + return RefArray{ table_1_shift, table_2_shift, table_3_shift, table_4_shift, w_l_shift, + w_r_shift, w_o_shift, w_4_shift, z_perm_shift }; }; }; @@ -213,8 +211,8 @@ class UltraFlavor { auto get_witness() { return WitnessEntities::get_all(); }; auto get_to_be_shifted() { - return RefArray{ this->table_1, this->table_2, this->table_3, this->table_4, this->w_l, this->w_r, - this->w_o, this->w_4, this->sorted_accum, this->z_perm, this->z_lookup }; + return RefArray{ this->table_1, this->table_2, this->table_3, this->table_4, this->w_l, + this->w_r, this->w_o, this->w_4, this->z_perm }; }; auto get_shifted() { return ShiftedEntities::get_all(); }; }; @@ -239,7 +237,8 @@ class UltraFlavor { // Define all operations as default, except copy construction/assignment ProverPolynomials() = default; ProverPolynomials(size_t circuit_size) - { // Initialize all unshifted polynomials to the zero polynomial and initialize the shifted polys + { // Initialize all unshifted polynomials to the zero polynomial and initialize the + // shifted polys for (auto& poly : get_unshifted()) { poly = Polynomial{ circuit_size }; } @@ -284,57 +283,22 @@ class UltraFlavor { std::vector memory_read_records; std::vector memory_write_records; - std::array sorted_polynomials; ProverPolynomials polynomials; // storage for all polynomials evaluated by the prover - void compute_sorted_accumulator_polynomials(const FF& eta, const FF& eta_two, const FF& eta_three) - { - // Compute sorted witness-table accumulator - compute_sorted_list_accumulator(eta, eta_two, eta_three); - - // Finalize fourth wire polynomial by adding lookup memory records - add_plookup_memory_records_to_wire_4(eta, eta_two, eta_three); - } - /** - * @brief Construct sorted list accumulator polynomial 's'. + * @brief Add RAM/ROM memory records to the fourth wire polynomial * - * @details Compute s = s_1 + η*s_2 + η²*s_3 + η³*s_4 (via Horner) where s_i are the - * sorted concatenated witness/table polynomials - * - * @param key proving key - * @param sorted_list_polynomials sorted concatenated witness/table polynomials - * @param eta random challenge - * @return Polynomial - */ - void compute_sorted_list_accumulator(const FF& eta, const FF& eta_two, const FF& eta_three) - { - auto& sorted_list_accumulator = polynomials.sorted_accum; - - // Construct s via Horner, i.e. s = s_1 + η(s_2 + η(s_3 + η*s_4)) - for (size_t i = 0; i < this->circuit_size; ++i) { - FF T0 = sorted_polynomials[3][i] * eta_three; - T0 += sorted_polynomials[2][i] * eta_two; - T0 += sorted_polynomials[1][i] * eta; - T0 += sorted_polynomials[0][i]; - sorted_list_accumulator[i] = T0; - } - } - - /** - * @brief Add plookup memory records to the fourth wire polynomial - * - * @details This operation must be performed after the first three wires have been committed to, hence the - * dependence on the `eta` challenge. + * @details This operation must be performed after the first three wires have been + * committed to, hence the dependence on the `eta` challenge. * * @tparam Flavor * @param eta challenge produced after commitment to first three wire polynomials */ - void add_plookup_memory_records_to_wire_4(const FF& eta, const FF& eta_two, const FF& eta_three) + void add_ram_rom_memory_records_to_wire_4(const FF& eta, const FF& eta_two, const FF& eta_three) { - // The plookup memory record values are computed at the indicated indices as + // The memory record values are computed at the indicated indices as // w4 = w3 * eta^3 + w2 * eta^2 + w1 * eta + read_write_flag; - // (See plookup_auxiliary_widget.hpp for details) + // (See the Auxiliary relation for details) auto wires = polynomials.get_wires(); // Compute read record values @@ -354,7 +318,21 @@ class UltraFlavor { } /** - * @brief Computes public_input_delta, lookup_grand_product_delta, the z_perm and z_lookup polynomials + * @brief Compute the inverse polynomial used in the log derivative lookup argument + * + * @tparam Flavor + * @param beta + * @param gamma + */ + void compute_logderivative_inverses(const RelationParameters& relation_parameters) + { + // Compute inverses for conventional lookups + compute_logderivative_inverse>( + this->polynomials, relation_parameters, this->circuit_size); + } + + /** + * @brief Computes public_input_delta and the permutation grand product polynomial * * @param relation_parameters */ @@ -402,7 +380,8 @@ class UltraFlavor { commitment = proving_key.commitment_key->commit(polynomial); } } - // TODO(https://github.com/AztecProtocol/barretenberg/issues/964): Clean the boilerplate up. + // TODO(https://github.com/AztecProtocol/barretenberg/issues/964): Clean the boilerplate + // up. VerificationKey(const uint64_t circuit_size, const uint64_t num_public_inputs, const uint64_t pub_inputs_offset, @@ -529,7 +508,8 @@ class UltraFlavor { PartiallyEvaluatedMultivariates() = default; PartiallyEvaluatedMultivariates(const size_t circuit_size) { - // Storage is only needed after the first partial evaluation, hence polynomials of size (n / 2) + // Storage is only needed after the first partial evaluation, hence polynomials of + // size (n / 2) for (auto& poly : this->get_all()) { poly = Polynomial(circuit_size / 2); } @@ -573,8 +553,9 @@ class UltraFlavor { w_o = "W_O"; w_4 = "W_4"; z_perm = "Z_PERM"; - z_lookup = "Z_LOOKUP"; - sorted_accum = "SORTED_ACCUM"; + lookup_inverses = "LOOKUP_INVERSES"; + lookup_read_counts = "LOOKUP_READ_COUNTS"; + lookup_read_tags = "LOOKUP_READ_TAGS"; q_c = "Q_C"; q_l = "Q_L"; @@ -646,10 +627,11 @@ class UltraFlavor { this->w_l = commitments.w_l; this->w_r = commitments.w_r; this->w_o = commitments.w_o; - this->sorted_accum = commitments.sorted_accum; + this->lookup_inverses = commitments.lookup_inverses; + this->lookup_read_counts = commitments.lookup_read_counts; + this->lookup_read_tags = commitments.lookup_read_tags; this->w_4 = commitments.w_4; this->z_perm = commitments.z_perm; - this->z_lookup = commitments.z_lookup; } } }; @@ -670,10 +652,11 @@ class UltraFlavor { Commitment w_l_comm; Commitment w_r_comm; Commitment w_o_comm; - Commitment sorted_accum_comm; + Commitment lookup_read_counts_comm; + Commitment lookup_read_tags_comm; Commitment w_4_comm; Commitment z_perm_comm; - Commitment z_lookup_comm; + Commitment lookup_inverses_comm; std::vector> sumcheck_univariates; std::array sumcheck_evaluations; std::vector zm_cq_comms; @@ -703,8 +686,9 @@ class UltraFlavor { }; /** - * @brief Takes a FULL Ultra proof and deserializes it into the public member variables that compose the - * structure. Must be called in order to access the structure of the proof. + * @brief Takes a FULL Ultra proof and deserializes it into the public member variables + * that compose the structure. Must be called in order to access the structure of the + * proof. * */ void deserialize_full_transcript() @@ -722,10 +706,11 @@ class UltraFlavor { w_l_comm = deserialize_from_buffer(proof_data, num_frs_read); w_r_comm = deserialize_from_buffer(proof_data, num_frs_read); w_o_comm = deserialize_from_buffer(proof_data, num_frs_read); - sorted_accum_comm = deserialize_from_buffer(proof_data, num_frs_read); + lookup_read_counts_comm = deserialize_from_buffer(proof_data, num_frs_read); + lookup_read_tags_comm = deserialize_from_buffer(proof_data, num_frs_read); w_4_comm = deserialize_from_buffer(proof_data, num_frs_read); + lookup_inverses_comm = deserialize_from_buffer(proof_data, num_frs_read); z_perm_comm = deserialize_from_buffer(proof_data, num_frs_read); - z_lookup_comm = deserialize_from_buffer(proof_data, num_frs_read); for (size_t i = 0; i < log_n; ++i) { sumcheck_univariates.push_back( deserialize_from_buffer>(proof_data, @@ -739,8 +724,9 @@ class UltraFlavor { kzg_w_comm = deserialize_from_buffer(proof_data, num_frs_read); } /** - * @brief Serializes the structure variables into a FULL Ultra proof. Should be called only if - * deserialize_full_transcript() was called and some transcript variable was modified. + * @brief Serializes the structure variables into a FULL Ultra proof. Should be called + * only if deserialize_full_transcript() was called and some transcript variable was + * modified. * */ void serialize_full_transcript() @@ -757,10 +743,11 @@ class UltraFlavor { serialize_to_buffer(w_l_comm, proof_data); serialize_to_buffer(w_r_comm, proof_data); serialize_to_buffer(w_o_comm, proof_data); - serialize_to_buffer(sorted_accum_comm, proof_data); + serialize_to_buffer(lookup_read_counts_comm, proof_data); + serialize_to_buffer(lookup_read_tags_comm, proof_data); serialize_to_buffer(w_4_comm, proof_data); + serialize_to_buffer(lookup_inverses_comm, proof_data); serialize_to_buffer(z_perm_comm, proof_data); - serialize_to_buffer(z_lookup_comm, proof_data); for (size_t i = 0; i < log_n; ++i) { serialize_to_buffer(sumcheck_univariates[i], proof_data); } diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp index a73509fe018..195097c2c7f 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp @@ -11,7 +11,6 @@ #include "barretenberg/relations/auxiliary_relation.hpp" #include "barretenberg/relations/delta_range_constraint_relation.hpp" #include "barretenberg/relations/elliptic_relation.hpp" -#include "barretenberg/relations/lookup_relation.hpp" #include "barretenberg/relations/permutation_relation.hpp" #include "barretenberg/relations/ultra_arithmetic_relation.hpp" #include "barretenberg/srs/factories/crs_factory.hpp" @@ -63,12 +62,12 @@ template class UltraRecursiveFlavor_ { // The number of multivariate polynomials on which a sumcheck prover sumcheck operates (including shifts). We often // need containers of this size to hold related data, so we choose a name more agnostic than `NUM_POLYNOMIALS`. // Note: this number does not include the individual sorted list polynomials. - static constexpr size_t NUM_ALL_ENTITIES = 43; + static constexpr size_t NUM_ALL_ENTITIES = UltraFlavor::NUM_ALL_ENTITIES; // The number of polynomials precomputed to describe a circuit and to aid a prover in constructing a satisfying // assignment of witnesses. We again choose a neutral name. - static constexpr size_t NUM_PRECOMPUTED_ENTITIES = 25; + static constexpr size_t NUM_PRECOMPUTED_ENTITIES = UltraFlavor::NUM_PRECOMPUTED_ENTITIES; // The total number of witness entities not including shifts. - static constexpr size_t NUM_WITNESS_ENTITIES = 7; + static constexpr size_t NUM_WITNESS_ENTITIES = UltraFlavor::NUM_WITNESS_ENTITIES; // define the tuple of Relations that comprise the Sumcheck relation using Relations = UltraFlavor::Relations_; @@ -238,10 +237,11 @@ template class UltraRecursiveFlavor_ { this->w_l = commitments.w_l; this->w_r = commitments.w_r; this->w_o = commitments.w_o; - this->sorted_accum = commitments.sorted_accum; + this->lookup_inverses = commitments.lookup_inverses; + this->lookup_read_counts = commitments.lookup_read_counts; + this->lookup_read_tags = commitments.lookup_read_tags; this->w_4 = commitments.w_4; this->z_perm = commitments.z_perm; - this->z_lookup = commitments.z_lookup; } } }; diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.hpp b/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.hpp index 3a5de3c3c5e..d3a9d0bf8ee 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.hpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.hpp @@ -81,7 +81,10 @@ template class ProverInstance_ { construct_lookup_table_polynomials(proving_key.polynomials.get_tables(), circuit, dyadic_circuit_size); - proving_key.sorted_polynomials = construct_sorted_list_polynomials(circuit, dyadic_circuit_size); + construct_lookup_read_counts(proving_key.polynomials.lookup_read_counts, + proving_key.polynomials.lookup_read_tags, + circuit, + dyadic_circuit_size); std::span public_wires_source = proving_key.polynomials.w_r; diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.test.cpp b/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.test.cpp deleted file mode 100644 index 4a8b5d36734..00000000000 --- a/barretenberg/cpp/src/barretenberg/sumcheck/instance/prover_instance.test.cpp +++ /dev/null @@ -1,89 +0,0 @@ - -#include "prover_instance.hpp" -#include "barretenberg/ecc/curves/bn254/bn254.hpp" -#include "barretenberg/plonk_honk_shared/library/grand_product_library.hpp" -#include "barretenberg/polynomials/polynomial.hpp" -#include "barretenberg/srs/factories/file_crs_factory.hpp" -#include -using namespace bb; - -template class InstanceTests : public testing::Test { - using FF = typename Flavor::FF; - using Polynomial = bb::Polynomial; - using Builder = typename Flavor::CircuitBuilder; - - public: - /** - * @brief Get a random polynomial - * - * @param size - * @return Polynomial - */ - static constexpr Polynomial get_random_polynomial(size_t size) - { - Polynomial random_polynomial{ size }; - for (auto& coeff : random_polynomial) { - coeff = FF::random_element(); - } - return random_polynomial; - } - - static void populate_span(auto& polynomial_view, const auto& polynomial) - { - ASSERT(polynomial_view.size() <= polynomial.size()); - for (size_t idx = 0; idx < polynomial.size(); idx++) { - polynomial_view[idx] = polynomial[idx]; - } - }; - /** - * @brief Check consistency of the computation of the sorted list accumulator - * @details This test compares a simple, unoptimized, easily readable calculation of the sorted list accumulator - * to the optimized implementation used by the prover. It's purpose is to provide confidence that some optimization - * introduced into the calculation has not changed the result. - * @note This test does confirm the correctness of the sorted list accumulator, only that the two implementations - * yield an identical result. - */ - static void test_sorted_list_accumulator_construction() - { - srs::init_crs_factory("../srs_db/ignition"); - - // Construct a simple circuit of size n = 8 (i.e. the minimum circuit size) - Builder builder; - - auto a = 2; - builder.add_variable(a); - - builder.add_gates_to_ensure_all_polys_are_non_zero(); - builder.finalize_circuit(); - auto instance = ProverInstance_(builder); - - // Get random challenge eta - auto eta = FF::random_element(); - auto eta_two = FF::random_element(); - auto eta_three = FF::random_element(); - - auto sorted_list_polynomials = instance.proving_key.sorted_polynomials; - - // Method 1: computed sorted list accumulator polynomial using prover library method - instance.proving_key.compute_sorted_list_accumulator(eta, eta_two, eta_three); - auto sorted_list_accumulator = instance.proving_key.polynomials.sorted_accum; - - // Compute s = s_1 + η*s_2 + η²*s_3 + η³*s_4 - Polynomial sorted_list_accumulator_expected{ sorted_list_polynomials[0] }; - for (size_t i = 0; i < instance.proving_key.circuit_size; ++i) { - sorted_list_accumulator_expected[i] += sorted_list_polynomials[1][i] * eta + - sorted_list_polynomials[2][i] * eta_two + - sorted_list_polynomials[3][i] * eta_three; - } - - EXPECT_EQ(sorted_list_accumulator, sorted_list_accumulator_expected); - }; -}; - -using FlavorTypes = testing::Types; -TYPED_TEST_SUITE(InstanceTests, FlavorTypes); - -TYPED_TEST(InstanceTests, SortedListAccumulator) -{ - TestFixture::test_sorted_list_accumulator_construction(); -} diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.test.cpp b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.test.cpp index 05c0938fa10..bfc9b0facac 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.test.cpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.test.cpp @@ -3,7 +3,6 @@ #include "barretenberg/relations/auxiliary_relation.hpp" #include "barretenberg/relations/delta_range_constraint_relation.hpp" #include "barretenberg/relations/elliptic_relation.hpp" -#include "barretenberg/relations/lookup_relation.hpp" #include "barretenberg/relations/permutation_relation.hpp" #include "barretenberg/relations/ultra_arithmetic_relation.hpp" #include "barretenberg/stdlib_circuit_builders/plookup_tables/fixed_base/fixed_base.hpp" diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/mega_transcript.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/mega_transcript.test.cpp index 4b9f122c966..8dfc816e01a 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/mega_transcript.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/mega_transcript.test.cpp @@ -65,15 +65,16 @@ class MegaTranscriptTests : public ::testing::Test { manifest_expected.add_challenge(round, "eta", "eta_two", "eta_three"); round++; - manifest_expected.add_entry(round, "SORTED_ACCUM", frs_per_G); + manifest_expected.add_entry(round, "LOOKUP_READ_COUNTS", frs_per_G); + manifest_expected.add_entry(round, "LOOKUP_READ_TAGS", frs_per_G); manifest_expected.add_entry(round, "W_4", frs_per_G); manifest_expected.add_challenge(round, "beta", "gamma"); round++; + manifest_expected.add_entry(round, "LOOKUP_INVERSES", frs_per_G); manifest_expected.add_entry(round, "CALLDATA_INVERSES", frs_per_G); manifest_expected.add_entry(round, "RETURN_DATA_INVERSES", frs_per_G); manifest_expected.add_entry(round, "Z_PERM", frs_per_G); - manifest_expected.add_entry(round, "Z_LOOKUP", frs_per_G); for (size_t i = 0; i < NUM_SUBRELATIONS - 1; i++) { std::string label = "alpha_" + std::to_string(i); @@ -242,7 +243,7 @@ TEST_F(MegaTranscriptTests, StructureTest) Flavor::Commitment one_group_val = Flavor::Commitment::one(); FF rand_val = FF::random_element(); - prover.transcript->sorted_accum_comm = one_group_val * rand_val; // choose random object to modify + prover.transcript->z_perm_comm = one_group_val * rand_val; // choose random object to modify EXPECT_TRUE(verifier.verify_proof( prover.export_proof())); // we have not serialized it back to the proof so it should still be fine @@ -250,5 +251,5 @@ TEST_F(MegaTranscriptTests, StructureTest) EXPECT_FALSE(verifier.verify_proof(prover.export_proof())); // the proof is now wrong after serializing it prover.transcript->deserialize_full_transcript(); - EXPECT_EQ(static_cast(prover.transcript->sorted_accum_comm), one_group_val * rand_val); + EXPECT_EQ(static_cast(prover.transcript->z_perm_comm), one_group_val * rand_val); } diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.cpp index f0c15496c95..6626b48623f 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_prover.cpp @@ -1,4 +1,5 @@ #include "barretenberg/ultra_honk/oink_prover.hpp" +#include "barretenberg/relations/logderiv_lookup_relation.hpp" namespace bb { @@ -109,21 +110,25 @@ template void OinkProver::execute_wire_commitment */ template void OinkProver::execute_sorted_list_accumulator_round() { - + // Get eta challenges auto [eta, eta_two, eta_three] = transcript->template get_challenges( domain_separator + "eta", domain_separator + "eta_two", domain_separator + "eta_three"); relation_parameters.eta = eta; relation_parameters.eta_two = eta_two; relation_parameters.eta_three = eta_three; - proving_key.compute_sorted_accumulator_polynomials( + proving_key.add_ram_rom_memory_records_to_wire_4( relation_parameters.eta, relation_parameters.eta_two, relation_parameters.eta_three); - // Commit to the sorted witness-table accumulator and the finalized (i.e. with memory records) fourth wire - // polynomial - witness_commitments.sorted_accum = commitment_key->commit(proving_key.polynomials.sorted_accum); + + // Commit to lookup argument polynomials and the finalized (i.e. with memory records) fourth wire polynomial + witness_commitments.lookup_read_counts = commitment_key->commit(proving_key.polynomials.lookup_read_counts); + witness_commitments.lookup_read_tags = commitment_key->commit(proving_key.polynomials.lookup_read_tags); witness_commitments.w_4 = commitment_key->commit(proving_key.polynomials.w_4); - transcript->send_to_verifier(domain_separator + commitment_labels.sorted_accum, witness_commitments.sorted_accum); + transcript->send_to_verifier(domain_separator + commitment_labels.lookup_read_counts, + witness_commitments.lookup_read_counts); + transcript->send_to_verifier(domain_separator + commitment_labels.lookup_read_tags, + witness_commitments.lookup_read_tags); transcript->send_to_verifier(domain_separator + commitment_labels.w_4, witness_commitments.w_4); } @@ -136,10 +141,16 @@ template void OinkProver::execute_log_derivative_ auto [beta, gamma] = transcript->template get_challenges(domain_separator + "beta", domain_separator + "gamma"); relation_parameters.beta = beta; relation_parameters.gamma = gamma; - if constexpr (IsGoblinFlavor) { - // Compute and commit to the logderivative inverse used in DataBus - proving_key.compute_logderivative_inverse(relation_parameters); + // Compute the inverses used in log-derivative lookup relations + proving_key.compute_logderivative_inverses(relation_parameters); + + witness_commitments.lookup_inverses = commitment_key->commit(proving_key.polynomials.lookup_inverses); + transcript->send_to_verifier(domain_separator + commitment_labels.lookup_inverses, + witness_commitments.lookup_inverses); + + // If Mega, commit to the databus inverse polynomials and send + if constexpr (IsGoblinFlavor) { witness_commitments.calldata_inverses = commitment_key->commit(proving_key.polynomials.calldata_inverses); witness_commitments.return_data_inverses = commitment_key->commit(proving_key.polynomials.return_data_inverses); transcript->send_to_verifier(domain_separator + commitment_labels.calldata_inverses, @@ -158,10 +169,8 @@ template void OinkProver::execute_grand_product_c proving_key.compute_grand_product_polynomials(relation_parameters); witness_commitments.z_perm = commitment_key->commit(proving_key.polynomials.z_perm); - witness_commitments.z_lookup = commitment_key->commit(proving_key.polynomials.z_lookup); transcript->send_to_verifier(domain_separator + commitment_labels.z_perm, witness_commitments.z_perm); - transcript->send_to_verifier(domain_separator + commitment_labels.z_lookup, witness_commitments.z_lookup); } template typename Flavor::RelationSeparator OinkProver::generate_alphas_round() diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_verifier.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_verifier.cpp index 69a2b20a57b..0a5a1810e4c 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/oink_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/oink_verifier.cpp @@ -87,15 +87,18 @@ template void OinkVerifier::execute_wire_commitme */ template void OinkVerifier::execute_sorted_list_accumulator_round() { - // Get challenge for sorted list batching and wire four memory records + // Get eta challenges auto [eta, eta_two, eta_three] = transcript->template get_challenges( domain_separator + "eta", domain_separator + "eta_two", domain_separator + "eta_three"); relation_parameters.eta = eta; relation_parameters.eta_two = eta_two; relation_parameters.eta_three = eta_three; - // Get commitments to sorted list accumulator and fourth wire - witness_comms.sorted_accum = - transcript->template receive_from_prover(domain_separator + comm_labels.sorted_accum); + + // Get commitments to lookup argument polynomials and fourth wire + witness_comms.lookup_read_counts = + transcript->template receive_from_prover(domain_separator + comm_labels.lookup_read_counts); + witness_comms.lookup_read_tags = + transcript->template receive_from_prover(domain_separator + comm_labels.lookup_read_tags); witness_comms.w_4 = transcript->template receive_from_prover(domain_separator + comm_labels.w_4); } @@ -109,6 +112,10 @@ template void OinkVerifier::execute_log_derivativ auto [beta, gamma] = transcript->template get_challenges(domain_separator + "beta", domain_separator + "gamma"); relation_parameters.beta = beta; relation_parameters.gamma = gamma; + + witness_comms.lookup_inverses = + transcript->template receive_from_prover(domain_separator + comm_labels.lookup_inverses); + // If Goblin (i.e. using DataBus) receive commitments to log-deriv inverses polynomials if constexpr (IsGoblinFlavor) { witness_comms.calldata_inverses = @@ -137,8 +144,6 @@ template void OinkVerifier::execute_grand_product // Get commitment to permutation and lookup grand products witness_comms.z_perm = transcript->template receive_from_prover(domain_separator + comm_labels.z_perm); - witness_comms.z_lookup = - transcript->template receive_from_prover(domain_separator + comm_labels.z_lookup); } template typename Flavor::RelationSeparator OinkVerifier::generate_alphas_round() diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/relation_correctness.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/relation_correctness.test.cpp index b928a0bcd04..cedd1fd4bb2 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/relation_correctness.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/relation_correctness.test.cpp @@ -4,7 +4,7 @@ #include "barretenberg/relations/delta_range_constraint_relation.hpp" #include "barretenberg/relations/ecc_op_queue_relation.hpp" #include "barretenberg/relations/elliptic_relation.hpp" -#include "barretenberg/relations/lookup_relation.hpp" +#include "barretenberg/relations/logderiv_lookup_relation.hpp" #include "barretenberg/relations/permutation_relation.hpp" #include "barretenberg/relations/relation_parameters.hpp" #include "barretenberg/relations/ultra_arithmetic_relation.hpp" @@ -29,9 +29,8 @@ void ensure_non_zero(auto& polynomial) * @brief Check that a given relation is satified for a set of polynomials * * @tparam relation_idx Index into a tuple of provided relations - * @tparam Flavor */ -template void check_relation(auto circuit_size, auto& polynomials, auto params) +template void check_relation(auto circuit_size, auto& polynomials, auto params) { for (size_t i = 0; i < circuit_size; i++) { // Define the appropriate SumcheckArrayOfValuesOverSubrelations type for this relation and initialize to zero @@ -273,9 +272,10 @@ TEST_F(UltraRelationCorrectnessTests, Ultra) instance->relation_parameters.beta = FF::random_element(); instance->relation_parameters.gamma = FF::random_element(); - instance->proving_key.compute_sorted_accumulator_polynomials(instance->relation_parameters.eta, - instance->relation_parameters.eta_two, - instance->relation_parameters.eta_three); + instance->proving_key.add_ram_rom_memory_records_to_wire_4(instance->relation_parameters.eta, + instance->relation_parameters.eta_two, + instance->relation_parameters.eta_three); + instance->proving_key.compute_logderivative_inverses(instance->relation_parameters); instance->proving_key.compute_grand_product_polynomials(instance->relation_parameters); // Check that selectors are nonzero to ensure corresponding relation has nontrivial contribution @@ -285,18 +285,15 @@ TEST_F(UltraRelationCorrectnessTests, Ultra) ensure_non_zero(proving_key.polynomials.q_elliptic); ensure_non_zero(proving_key.polynomials.q_aux); - // Construct the round for applying sumcheck relations and results for storing computed results - using Relations = typename Flavor::Relations; - auto& prover_polynomials = instance->proving_key.polynomials; auto params = instance->relation_parameters; // Check that each relation is satisfied across each row of the prover polynomials - check_relation>(circuit_size, prover_polynomials, params); - check_relation>(circuit_size, prover_polynomials, params); - check_relation>(circuit_size, prover_polynomials, params); - check_relation>(circuit_size, prover_polynomials, params); - check_relation>(circuit_size, prover_polynomials, params); - check_relation>(circuit_size, prover_polynomials, params); + check_relation>(circuit_size, prover_polynomials, params); + check_relation>(circuit_size, prover_polynomials, params); + check_relation>(circuit_size, prover_polynomials, params); + check_relation>(circuit_size, prover_polynomials, params); + check_relation>(circuit_size, prover_polynomials, params); + check_linearly_dependent_relation>(circuit_size, prover_polynomials, params); } TEST_F(UltraRelationCorrectnessTests, Mega) @@ -328,10 +325,10 @@ TEST_F(UltraRelationCorrectnessTests, Mega) instance->relation_parameters.beta = FF::random_element(); instance->relation_parameters.gamma = FF::random_element(); - instance->proving_key.compute_sorted_accumulator_polynomials(instance->relation_parameters.eta, - instance->relation_parameters.eta_two, - instance->relation_parameters.eta_three); - instance->proving_key.compute_logderivative_inverse(instance->relation_parameters); + instance->proving_key.add_ram_rom_memory_records_to_wire_4(instance->relation_parameters.eta, + instance->relation_parameters.eta_two, + instance->relation_parameters.eta_three); + instance->proving_key.compute_logderivative_inverses(instance->relation_parameters); instance->proving_key.compute_grand_product_polynomials(instance->relation_parameters); // Check that selectors are nonzero to ensure corresponding relation has nontrivial contribution @@ -351,19 +348,18 @@ TEST_F(UltraRelationCorrectnessTests, Mega) ensure_non_zero(proving_key.polynomials.return_data_read_counts); ensure_non_zero(proving_key.polynomials.return_data_inverses); - // Construct the round for applying sumcheck relations and results for storing computed results - using Relations = typename Flavor::Relations; auto& prover_polynomials = instance->proving_key.polynomials; auto params = instance->relation_parameters; // Check that each relation is satisfied across each row of the prover polynomials - check_relation>(circuit_size, prover_polynomials, params); - check_relation>(circuit_size, prover_polynomials, params); - check_relation>(circuit_size, prover_polynomials, params); - check_relation>(circuit_size, prover_polynomials, params); - check_relation>(circuit_size, prover_polynomials, params); - check_relation>(circuit_size, prover_polynomials, params); - check_relation>(circuit_size, prover_polynomials, params); - check_linearly_dependent_relation>( - circuit_size, prover_polynomials, params); + check_relation>(circuit_size, prover_polynomials, params); + check_relation>(circuit_size, prover_polynomials, params); + check_relation>(circuit_size, prover_polynomials, params); + check_relation>(circuit_size, prover_polynomials, params); + check_relation>(circuit_size, prover_polynomials, params); + check_relation>(circuit_size, prover_polynomials, params); + check_relation>(circuit_size, prover_polynomials, params); + check_relation>(circuit_size, prover_polynomials, params); + check_linearly_dependent_relation>(circuit_size, prover_polynomials, params); + check_linearly_dependent_relation>(circuit_size, prover_polynomials, params); } \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/sumcheck.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/sumcheck.test.cpp index 5962b8ba212..665538278c1 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/sumcheck.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/sumcheck.test.cpp @@ -5,7 +5,6 @@ #include "barretenberg/relations/auxiliary_relation.hpp" #include "barretenberg/relations/delta_range_constraint_relation.hpp" #include "barretenberg/relations/elliptic_relation.hpp" -#include "barretenberg/relations/lookup_relation.hpp" #include "barretenberg/relations/permutation_relation.hpp" #include "barretenberg/relations/ultra_arithmetic_relation.hpp" #include "barretenberg/stdlib_circuit_builders/plookup_tables/fixed_base/fixed_base.hpp" @@ -157,9 +156,10 @@ TEST_F(SumcheckTestsRealCircuit, Ultra) instance->relation_parameters.beta = FF::random_element(); instance->relation_parameters.gamma = FF::random_element(); - instance->proving_key.compute_sorted_accumulator_polynomials(instance->relation_parameters.eta, - instance->relation_parameters.eta_two, - instance->relation_parameters.eta_three); + instance->proving_key.add_ram_rom_memory_records_to_wire_4(instance->relation_parameters.eta, + instance->relation_parameters.eta_two, + instance->relation_parameters.eta_three); + instance->proving_key.compute_logderivative_inverses(instance->relation_parameters); instance->proving_key.compute_grand_product_polynomials(instance->relation_parameters); auto prover_transcript = Transcript::prover_init_empty(); diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_composer.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_honk.test.cpp similarity index 88% rename from barretenberg/cpp/src/barretenberg/ultra_honk/ultra_composer.test.cpp rename to barretenberg/cpp/src/barretenberg/ultra_honk/ultra_honk.test.cpp index b04bd0b7fd1..31d0423fd99 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_honk.test.cpp @@ -48,7 +48,7 @@ void ensure_non_zero(auto& polynomial) ASSERT_TRUE(has_non_zero_coefficient); } -class UltraHonkComposerTests : public ::testing::Test { +class UltraHonkTests : public ::testing::Test { protected: static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); } }; @@ -60,7 +60,7 @@ class UltraHonkComposerTests : public ::testing::Test { * to achieve non-zero polynomials * */ -TEST_F(UltraHonkComposerTests, ANonZeroPolynomialIsAGoodPolynomial) +TEST_F(UltraHonkTests, ANonZeroPolynomialIsAGoodPolynomial) { auto circuit_builder = UltraCircuitBuilder(); @@ -86,7 +86,7 @@ TEST_F(UltraHonkComposerTests, ANonZeroPolynomialIsAGoodPolynomial) * @brief Test proof construction/verification for a structured execution trace * */ -TEST_F(UltraHonkComposerTests, StructuredTrace) +TEST_F(UltraHonkTests, StructuredTrace) { auto builder = UltraCircuitBuilder(); size_t num_gates = 3; @@ -109,7 +109,7 @@ TEST_F(UltraHonkComposerTests, StructuredTrace) * @brief Test simple circuit with public inputs * */ -TEST_F(UltraHonkComposerTests, PublicInputs) +TEST_F(UltraHonkTests, PublicInputs) { auto builder = UltraCircuitBuilder(); size_t num_gates = 10; @@ -120,7 +120,7 @@ TEST_F(UltraHonkComposerTests, PublicInputs) prove_and_verify(builder, /*expected_result=*/true); } -TEST_F(UltraHonkComposerTests, XorConstraint) +TEST_F(UltraHonkTests, XorConstraint) { auto circuit_builder = UltraCircuitBuilder(); @@ -147,7 +147,7 @@ TEST_F(UltraHonkComposerTests, XorConstraint) prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkComposerTests, create_gates_from_plookup_accumulators) +TEST_F(UltraHonkTests, create_gates_from_plookup_accumulators) { auto circuit_builder = UltraCircuitBuilder(); @@ -207,7 +207,91 @@ TEST_F(UltraHonkComposerTests, create_gates_from_plookup_accumulators) prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkComposerTests, test_no_lookup_proof) +/** + * @brief Test various failure modes for the lookup relation via bad input polynomials + * + */ +TEST_F(UltraHonkTests, LookupFailure) +{ + // Construct a circuit with lookup and arithmetic gates + auto construct_circuit_with_lookups = []() { + UltraCircuitBuilder builder; + + MockCircuits::add_lookup_gates(builder); + MockCircuits::add_arithmetic_gates(builder); + + return builder; + }; + + auto prove_and_verify = [](auto& instance) { + UltraProver prover(instance); + auto verification_key = std::make_shared(instance->proving_key); + UltraVerifier verifier(verification_key); + auto proof = prover.construct_proof(); + return verifier.verify_proof(proof); + }; + + // Ensure the unaltered test circuit is valid + { + auto builder = construct_circuit_with_lookups(); + + auto instance = std::make_shared(builder); + + EXPECT_TRUE(prove_and_verify(instance)); + } + + // Failure mode 1: bad read counts/tags + { + auto builder = construct_circuit_with_lookups(); + + auto instance = std::make_shared(builder); + auto& polynomials = instance->proving_key.polynomials; + + // Erroneously update the read counts/tags at an arbitrary index + // Note: updating only one or the other may not cause failure due to the design of the relation algebra. For + // example, the inverse is only computed if read tags is non-zero, otherwise the inverse at the row in question + // will be zero. So if read counts is incremented at some arbitrary index but read tags is not, the inverse will + // be 0 and the erroneous read_counts value will get multiplied by 0 in the relation. This is expected behavior. + polynomials.lookup_read_counts[25] = 1; + polynomials.lookup_read_tags[25] = 1; + + EXPECT_FALSE(prove_and_verify(instance)); + } + + // Failure mode 2: bad lookup gate wire value + { + auto builder = construct_circuit_with_lookups(); + + auto instance = std::make_shared(builder); + auto& polynomials = instance->proving_key.polynomials; + + // Find a lookup gate and alter one of the wire values + for (auto [q_lookup, wire_3] : zip_view(polynomials.q_lookup, polynomials.w_o)) { + if (!q_lookup.is_zero()) { + wire_3 += 1; + break; + } + } + + EXPECT_FALSE(prove_and_verify(instance)); + } + + // Failure mode 3: erroneous lookup gate + { + auto builder = construct_circuit_with_lookups(); + + auto instance = std::make_shared(builder); + auto& polynomials = instance->proving_key.polynomials; + + // Turn the lookup selector on for an arbitrary row where it is not already active + EXPECT_TRUE(polynomials.q_lookup[25] != 1); + polynomials.q_lookup[25] = 1; + + EXPECT_FALSE(prove_and_verify(instance)); + } +} + +TEST_F(UltraHonkTests, test_no_lookup_proof) { auto circuit_builder = UltraCircuitBuilder(); @@ -229,7 +313,7 @@ TEST_F(UltraHonkComposerTests, test_no_lookup_proof) prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkComposerTests, test_elliptic_gate) +TEST_F(UltraHonkTests, test_elliptic_gate) { typedef grumpkin::g1::affine_element affine_element; typedef grumpkin::g1::element element; @@ -262,7 +346,7 @@ TEST_F(UltraHonkComposerTests, test_elliptic_gate) prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkComposerTests, non_trivial_tag_permutation) +TEST_F(UltraHonkTests, non_trivial_tag_permutation) { auto circuit_builder = UltraCircuitBuilder(); fr a = fr::random_element(); @@ -289,7 +373,7 @@ TEST_F(UltraHonkComposerTests, non_trivial_tag_permutation) prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkComposerTests, non_trivial_tag_permutation_and_cycles) +TEST_F(UltraHonkTests, non_trivial_tag_permutation_and_cycles) { auto circuit_builder = UltraCircuitBuilder(); fr a = fr::random_element(); @@ -326,7 +410,7 @@ TEST_F(UltraHonkComposerTests, non_trivial_tag_permutation_and_cycles) prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkComposerTests, bad_tag_permutation) +TEST_F(UltraHonkTests, bad_tag_permutation) { { auto circuit_builder = UltraCircuitBuilder(); @@ -369,7 +453,7 @@ TEST_F(UltraHonkComposerTests, bad_tag_permutation) } } -TEST_F(UltraHonkComposerTests, sort_widget) +TEST_F(UltraHonkTests, sort_widget) { auto circuit_builder = UltraCircuitBuilder(); fr a = fr::one(); @@ -386,7 +470,7 @@ TEST_F(UltraHonkComposerTests, sort_widget) prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkComposerTests, sort_with_edges_gate) +TEST_F(UltraHonkTests, sort_with_edges_gate) { fr a = fr::one(); fr b = fr(2); @@ -476,7 +560,7 @@ TEST_F(UltraHonkComposerTests, sort_with_edges_gate) } } -TEST_F(UltraHonkComposerTests, range_constraint) +TEST_F(UltraHonkTests, range_constraint) { { auto circuit_builder = UltraCircuitBuilder(); @@ -545,7 +629,7 @@ TEST_F(UltraHonkComposerTests, range_constraint) } } -TEST_F(UltraHonkComposerTests, range_with_gates) +TEST_F(UltraHonkTests, range_with_gates) { auto circuit_builder = UltraCircuitBuilder(); auto idx = add_variables(circuit_builder, { 1, 2, 3, 4, 5, 6, 7, 8 }); @@ -563,7 +647,7 @@ TEST_F(UltraHonkComposerTests, range_with_gates) prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkComposerTests, range_with_gates_where_range_is_not_a_power_of_two) +TEST_F(UltraHonkTests, range_with_gates_where_range_is_not_a_power_of_two) { auto circuit_builder = UltraCircuitBuilder(); auto idx = add_variables(circuit_builder, { 1, 2, 3, 4, 5, 6, 7, 8 }); @@ -581,7 +665,7 @@ TEST_F(UltraHonkComposerTests, range_with_gates_where_range_is_not_a_power_of_tw prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkComposerTests, sort_widget_complex) +TEST_F(UltraHonkTests, sort_widget_complex) { { @@ -607,7 +691,7 @@ TEST_F(UltraHonkComposerTests, sort_widget_complex) } } -TEST_F(UltraHonkComposerTests, sort_widget_neg) +TEST_F(UltraHonkTests, sort_widget_neg) { auto circuit_builder = UltraCircuitBuilder(); fr a = fr::one(); @@ -624,7 +708,7 @@ TEST_F(UltraHonkComposerTests, sort_widget_neg) prove_and_verify(circuit_builder, /*expected_result=*/false); } -TEST_F(UltraHonkComposerTests, composed_range_constraint) +TEST_F(UltraHonkTests, composed_range_constraint) { auto circuit_builder = UltraCircuitBuilder(); auto c = fr::random_element(); @@ -637,7 +721,7 @@ TEST_F(UltraHonkComposerTests, composed_range_constraint) prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkComposerTests, non_native_field_multiplication) +TEST_F(UltraHonkTests, non_native_field_multiplication) { using fq = fq; auto circuit_builder = UltraCircuitBuilder(); @@ -693,7 +777,7 @@ TEST_F(UltraHonkComposerTests, non_native_field_multiplication) prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkComposerTests, rom) +TEST_F(UltraHonkTests, rom) { auto circuit_builder = UltraCircuitBuilder(); @@ -734,7 +818,7 @@ TEST_F(UltraHonkComposerTests, rom) prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkComposerTests, ram) +TEST_F(UltraHonkTests, ram) { auto circuit_builder = UltraCircuitBuilder(); @@ -797,7 +881,7 @@ TEST_F(UltraHonkComposerTests, ram) prove_and_verify(circuit_builder, /*expected_result=*/true); } -TEST_F(UltraHonkComposerTests, range_checks_on_duplicates) +TEST_F(UltraHonkTests, range_checks_on_duplicates) { auto circuit_builder = UltraCircuitBuilder(); @@ -836,7 +920,7 @@ TEST_F(UltraHonkComposerTests, range_checks_on_duplicates) // range constrained, do not break the set equivalence checks because of indices mismatch. // 2^14 is DEFAULT_PLOOKUP_RANGE_BITNUM i.e. the maximum size before a variable gets sliced // before range constraints are applied to it. -TEST_F(UltraHonkComposerTests, range_constraint_small_variable) +TEST_F(UltraHonkTests, range_constraint_small_variable) { auto circuit_builder = UltraCircuitBuilder(); diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_transcript.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_transcript.test.cpp index 952894e4a36..df541aeb2df 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_transcript.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_transcript.test.cpp @@ -58,13 +58,14 @@ class UltraTranscriptTests : public ::testing::Test { manifest_expected.add_challenge(round, "eta", "eta_two", "eta_three"); round++; - manifest_expected.add_entry(round, "SORTED_ACCUM", frs_per_G); + manifest_expected.add_entry(round, "LOOKUP_READ_COUNTS", frs_per_G); + manifest_expected.add_entry(round, "LOOKUP_READ_TAGS", frs_per_G); manifest_expected.add_entry(round, "W_4", frs_per_G); manifest_expected.add_challenge(round, "beta", "gamma"); round++; + manifest_expected.add_entry(round, "LOOKUP_INVERSES", frs_per_G); manifest_expected.add_entry(round, "Z_PERM", frs_per_G); - manifest_expected.add_entry(round, "Z_LOOKUP", frs_per_G); for (size_t i = 0; i < NUM_SUBRELATIONS - 1; i++) { std::string label = "alpha_" + std::to_string(i); @@ -226,7 +227,7 @@ TEST_F(UltraTranscriptTests, StructureTest) Flavor::Commitment one_group_val = Flavor::Commitment::one(); FF rand_val = FF::random_element(); - prover.transcript->sorted_accum_comm = one_group_val * rand_val; // choose random object to modify + prover.transcript->z_perm_comm = one_group_val * rand_val; // choose random object to modify EXPECT_TRUE(verifier.verify_proof( prover.export_proof())); // we have not serialized it back to the proof so it should still be fine @@ -234,5 +235,5 @@ TEST_F(UltraTranscriptTests, StructureTest) EXPECT_FALSE(verifier.verify_proof(prover.export_proof())); // the proof is now wrong after serializing it prover.transcript->deserialize_full_transcript(); - EXPECT_EQ(static_cast(prover.transcript->sorted_accum_comm), one_group_val * rand_val); + EXPECT_EQ(static_cast(prover.transcript->z_perm_comm), one_group_val * rand_val); } diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/avm_prover.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/avm_prover.cpp index cd27e7479ee..3ddd8f14aca 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/avm_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/avm_prover.cpp @@ -7,7 +7,6 @@ #include "barretenberg/honk/proof_system/permutation_library.hpp" #include "barretenberg/plonk_honk_shared/library/grand_product_library.hpp" #include "barretenberg/polynomials/polynomial.hpp" -#include "barretenberg/relations/lookup_relation.hpp" #include "barretenberg/relations/permutation_relation.hpp" #include "barretenberg/sumcheck/sumcheck.hpp" diff --git a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml index 1ebc77c5a5f..4619fd298dd 100644 --- a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml @@ -1,4 +1,4 @@ key_hash = "0x096129b1c6e108252fc5c829c4cc9b7e8f0d1fd9f29c2532b563d6396645e08f" -proof = ["0x0000000000000000000000000000000000000000000000000000000000000020","0x0000000000000000000000000000000000000000000000000000000000000011","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000042ab5d6d1986846cf","0x00000000000000000000000000000000000000000000000b75c020998797da78","0x0000000000000000000000000000000000000000000000005a107acb64952eca","0x000000000000000000000000000000000000000000000000000031e97a575e9d","0x00000000000000000000000000000000000000000000000b5666547acf8bd5a4","0x00000000000000000000000000000000000000000000000c410db10a01750aeb","0x00000000000000000000000000000000000000000000000d722669117f9758a4","0x000000000000000000000000000000000000000000000000000178cbf4206471","0x000000000000000000000000000000000000000000000000e91b8a11e7842c38","0x000000000000000000000000000000000000000000000007fd51009034b3357f","0x000000000000000000000000000000000000000000000009889939f81e9c7402","0x0000000000000000000000000000000000000000000000000000f94656a2ca48","0x000000000000000000000000000000000000000000000006fb128b46c1ddb67f","0x0000000000000000000000000000000000000000000000093fe27776f50224bd","0x000000000000000000000000000000000000000000000004a0c80c0da527a081","0x0000000000000000000000000000000000000000000000000001b52c2020d746","0x0000000000000000000000000000005a9bae947e1e91af9e4033d8d6aa6ed632","0x000000000000000000000000000000000025e485e013446d4ac7981c88ba6ecc","0x000000000000000000000000000000ff1e0496e30ab24a63b32b2d1120b76e62","0x00000000000000000000000000000000001afe0a8a685d7cd85d1010e55d9d7c","0x000000000000000000000000000000b0804efd6573805f991458295f510a2004","0x00000000000000000000000000000000000c81a178016e2fe18605022d5a8b0e","0x000000000000000000000000000000eba51e76eb1cfff60a53a0092a3c3dea47","0x000000000000000000000000000000000022e7466247b533282f5936ac4e6c15","0x00000000000000000000000000000071b1d76edf770edff98f00ff4deec264cd","0x00000000000000000000000000000000001e48128e68794d8861fcbb2986a383","0x000000000000000000000000000000d3a2af4915ae6d86b097adc377fafda2d4","0x000000000000000000000000000000000006359de9ca452dab3a4f1f8d9c9d98","0x00000000000000000000000000000044d7ca77b464f03aa44f6f8d49a0d3ada5","0x00000000000000000000000000000000002a36959f550517d82d0af666bcd7dc","0x0000000000000000000000000000000566b28c19f0b1732b95e0381bc5d6dbdd","0x00000000000000000000000000000000002511360b7a8c6a823559f0ac9eb02b","0x000000000000000000000000000000f968b227a358a305607f3efc933823d288","0x00000000000000000000000000000000000eaf8adb390375a76d95e918b65e08","0x000000000000000000000000000000bb34b4b447aae56f5e24f81c3acd6d547f","0x00000000000000000000000000000000002175d012746260ebcfe339a91a81e1","0x00000000000000000000000000000058035b1ed115023f42bf4ee93d2dc29dcb","0x00000000000000000000000000000000002de4b004225be4e68938b0db546287","0x0000000000000000000000000000003d18d72585ef033ab3663d1944abb2054a","0x0000000000000000000000000000000000149a1974c0c2b5f0639970cda1af83","0x000000000000000000000000000000bb1eb2b1fc10b55295ed6c1ae54e8a40da","0x000000000000000000000000000000000026da80059472ac8c64e437d6fe6134","0x000000000000000000000000000000d1f101b72ee710423ca44548910676a4fe","0x00000000000000000000000000000000000323378ad6b5aec67af99e522095a0","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x2622384e4b4688a3ad115007c1c09de1a141aeca06c31925898cf746038a5897","0x2f743e893a3880004db1ff3492279d89c025b9815f16e129d15f7a3687b6f833","0x03e05487307f18e3afb90cc524e56809e478039d317a3757433bfc8e06a32b73","0x099ba7011747dd2d8b5ac03ed02b93c9803d51899677409931d5b1571c3041b5","0x189ef108e334c5173619eac1067b99526a5cc6e47cbffaa3c117f0c3eb8bebd4","0x0b5f77b69ac2955ecc44a73e18b2ea8403224cf769657d53acc9a5d302d0b86e","0x1b81353a160e985e8a1fb09d3a3827fe68d03585757530dcec1b8038ac829a21","0x175e75cef1b974011de38e6e631f42bffd4dcb6fad6680930388cffaa60d940e","0x1631945a2aa39032cfa8cf379d18a983d4b5a487adab67252c6514b35bc88095","0x181b639e465a6f9842c5d75f6f5b855a065f498595146df3bd2b9c0ef66042a1","0x0c6e5af7add3e12f610c13d8066896d08882a7c50cfe33676fda8a75e250e9b9","0x28f94cd060c45a2e6b423831302deb456d0964879db5008a2be0957a2c749e2a","0x1c81fb20cea508580aa962e5b4736a43382816e7abac7e478e6c080cf896798d","0x23dea53784aa14dcf7e1cce5ee480796e67b2dd69a8e20c5c09558001640edfa","0x149c2548f8b0d96fefecab53e31aa3902341c903fa0ef863ef64610315de993b","0x16ad81b3129ccebe1682d14b726bc9b86acd0f0be8c304594ce5a87e756add27","0x2c1ef938516edccc0cd1d4d812644d72b6ead3c85e1c8500fc54e77e5652b23f","0x0eecb7fba3395b21197cb24bb9b733b1985d81f35a1ee944714ffd781a7bd136","0x06e2a96ecf1e8419198eca10133954f3560102467f40a234cf071d23c6cf411a","0x1e6bfa2adcbdc50313408ef28a77b76dd915fa372c093c4484ba662695a3eadc","0x28ccaf4d4759c1f4bb49429b961a59cdefbc445017ffa807e90c54b27e1ee657","0x22803d537311e757a146ae7a2fc396d42d67f27e73efca82e3e324dc493da4de","0x196255f687cede05f326204bfaead7a54f8d48b67ce8522cb8af6a7fffaffcb6","0x147ea42988386b944f006be242ccc6b099fadd7f450955d252768667bbaee4f9","0x1f9ccb05e508b1d08c79c11acbc0677fdc18d5d40827e2e1eaae60fee51b940f","0x28ea76870d22eea72821da25f9b7a89341347afcd6c077387986a82dc8afa833","0x0e6ef82d3e5a318a9c6233dffbb00d130599f4ac979a89b034ce9d930b11165a","0x2e97fa9299a218c982504199ada3278270b9cb566bf46fe1ecc1d151e06b8745","0x1a41ac9b1032ac24c11720407c253a866e9c75a4ec233f15f968b206ea1e5d0e","0x0b31b541bb044c1bc2428c2a57ba29438f620050d1628389ff1fa90c494d7c58","0x050fec8d69f182768a9b34eca8c3f4695dad8bc20a10904090cfe18777d44d25","0x069283ac40daaafff76c3679f54a0aa773c8d71152fbb9c3219906113fc4f683","0x25c3ec4e8b90214aafe3b5416abf11a98bd34b8acb449df8424f159ddf858bc1","0x1a3884f3a922d0da758cb7ed9a5ddc3c3c2132dde8d913753fa3e6b766be5697","0x222d05a0fce0565bf9cc490f97bd4eff53858f2ca6afe9d91c5c8d7de8076f39","0x054698b045b439467a3067a8dc2b4d020b2bb44df3d98a19f9cfb04c9ee5ffd1","0x0e39d66cded0f3df40e04124e36c827bcaf15fbe9fb6e9bbc3af889f8bd1ebf0","0x145aea47dc97ec35ac67f135aac37f8bc6eaf149551a2f48901529d10e25c860","0x1894877b2769ae2c288738f8aa33acfc7ca9a7d1e26a76908ca2909bf25aa59a","0x27e8c702be67be467f052abd180464a468b7d5d5d8a4961e56e8561f7863c91a","0x0326d3e4607d54a30c7fa99d1609f386aeb8c8094cabd7397246074f634dcec8","0x17eb8f62b5ba2dad391e3f81d3a6b9d03ff723a7d6a4d77b98b18ddd0debf4fd","0x1a5d3e8a27c1f69d6e4558b3c89cd9347c62182ce90fb6e34392bc4e7b7c178c","0x2293034bed3d33d5ad0d150f64d493c9be554f640103621f9ae56034a7323d84","0x13d75ffbb9d2ceb2daa6d42f3618d4ea9775befa1cf5f9df141dfebf794abc35","0x2ec339c42fbb2d50221ec907779e72be3eab2960d110a90d36cc6c0afcf5857e","0x15e9c913fa84a2657571831d5d7a90f6534ca67a1617b4063fa5bf09f46cd7a2","0x10f56fbe9fefd59d2acd49fa641fedcfb65d96d54cf47207e2c8ab34f22bbabe","0x117fa3859a400040ebe8dee4a60ddcb04484ff5cfb5294c6530354c3c8cb35f3","0x123260b824df2f8bbe6a351ba2fa94c61aa754741eb198b768a699b2d1cc2b6f","0x1e51d9a653adc6b67287d35bb60584261f57363177c6b54a56dbd39834d851ba","0x18a9b2e2fce77bdb5e41215e2caeb7e77e946dbb2f381c8e7974709e03a6c216","0x2b2640870195a40e374cfa834e37ad9a5e17cb687bd2119a63ac02c3769b0f1e","0x2da73263fef362dfc79dd1066fd7ec294b765e2533f3ac4320e8d1540f2639a8","0x0cc9f299e5291bb1bc0951ce510a634c418af9f9802a291fe6d951768c0a1b2d","0x02a940acb788df42cc9219531776d45465be19087fc3f523fe92df771e5efc10","0x2d5976cc5540e761824bdacf69a2dddabe104fdbb235985ae9080b488f642fa9","0x284c18d1574d2cb7b4ee45b3ff30176eff2ab9c7b7f60cd8a87cef599379244d","0x12a38d659bf38da09af8f445505baa16bcb036d83173f6f45a7e46cac511e5a1","0x0852ef710b2396ba5b7fd69a95b336908d3a368262ec41e0d972564f784201a4","0x240c467a31ed3bb7c4cef09407750d2d89b3750e6cebb4aaa9d0f1f92be77249","0x04edf7595087745abc11fe7780afd4754c5013725653a4cec31f039b77e7b3c7","0x080d04b50ae3acd787f33f8f4a639a58677b5c04ef8a352fd4dd9236883f0e81","0x0cd745e7540fe230038f024ab1269177599ad94e8d8099a010eb7eebd3e41ec8","0x25e2394f90f5b3e3046b8876a6b3ef19a03ef9e9aeae4813fcb14907decc0393","0x03df12a6e39c606d70d3d470aff710d9daa86dece773a6f6f057725b57d6d115","0x0f744082aecf54f55db19dfbe56a81c17b3eb48417305c129beb6c97a22c705b","0x244a80d6d82e82fc416e8e4694deb4e08b81c32bb90cb2f96ff3f687298322d1","0x251eb4d8692f49523e3972096264ee770b295fb62a970fbfdd8aa1fff661ef50","0x0c4d9200120430618493a9151d632faa95c9ae842b7d97103a4afb3330cafbed","0x09e970a55dd7335db16a3823b6489c77cb7785f674cb7c924994ee121122e514","0x19e5bd1113959463be673ee72103bfe7559f423c632fbf9701ff099e165c429b","0x071eb2916ba30652a328c98353f69f239c41a4913c34931f18e91e5414b3270a","0x2a0cd2ebac904b7ebd82b6509dfcaf9ecf32175758c691d01f4fb32dad6371c4","0x1aa43a3009417d95904ebecd4189545e52ca7e9c7dfa3bde5f255ddefed5c754","0x29fd7a93212d60af81b810dad13a240bbbe16966a4977408b1d64c5d692b50b4","0x000000000000000000000000000000bef7cad70fa62891e6329cb7c17d0c5459","0x0000000000000000000000000000000000209177f2a04609421c1f23c04b454e","0x00000000000000000000000000000060dec389686170618e2490100f3fcf39e2","0x0000000000000000000000000000000000213368873145aad5f93798c31730af","0x000000000000000000000000000000c0f21a470488d9cbe53650d941c25cd569","0x000000000000000000000000000000000016d6f88e6b319553f5948886a6bd5e","0x000000000000000000000000000000d6dbb8a54a071e01c46d648c8c555ec352","0x0000000000000000000000000000000000130a7ce06ad74eb6c83f5565e2f821","0x00000000000000000000000000000058ca3aa788bd6ff37a5da3ecefdc896601","0x00000000000000000000000000000000001381bddcf8fb976cc52fee0d920598","0x00000000000000000000000000000082bdd94acd10edf22e09b1a42be500f8f8","0x00000000000000000000000000000000002f27815e28b2bc0699336893abdc0f","0x000000000000000000000000000000eb1d6973a54f8848f4c0630370d6181e49","0x000000000000000000000000000000000000129c1889d64ab66303bf17bfc864","0x000000000000000000000000000000155918aa9f6d352b847bf860a261266282","0x0000000000000000000000000000000000216e687d2f85a811f67573cbf311ba","0x0000000000000000000000000000002d2662f79a7ba21a95f44e67ed0b5abf3b","0x00000000000000000000000000000000001351870a81dc6edff235df110fe798","0x000000000000000000000000000000b113a55b86f59b21fe419ed8518dfddfc6","0x00000000000000000000000000000000002f26cd920f79b0d72a49897acc521c","0x0000000000000000000000000000002a4e1689c65dcae73ed1a33b03c611a7fe","0x00000000000000000000000000000000001c5093a8ae791c00fdd763c95800c5","0x0000000000000000000000000000006231d049ec3683c06ec6b00348e0669c61","0x0000000000000000000000000000000000237bfd7ec06c28f22ce84db9bb17ed","0x0000000000000000000000000000008afa7fa0842467bded20491950c3c1cde0","0x00000000000000000000000000000000000194ab5c71154605b8483cb40d00b8","0x00000000000000000000000000000066709af193591e93e8be3b833f63cb8597","0x000000000000000000000000000000000008ab9091bb9225b00ca0c011dff12f"] +proof = ["0x0000000000000000000000000000000000000000000000000000000000000020","0x0000000000000000000000000000000000000000000000000000000000000011","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000042ab5d6d1986846cf","0x00000000000000000000000000000000000000000000000b75c020998797da78","0x0000000000000000000000000000000000000000000000005a107acb64952eca","0x000000000000000000000000000000000000000000000000000031e97a575e9d","0x00000000000000000000000000000000000000000000000b5666547acf8bd5a4","0x00000000000000000000000000000000000000000000000c410db10a01750aeb","0x00000000000000000000000000000000000000000000000d722669117f9758a4","0x000000000000000000000000000000000000000000000000000178cbf4206471","0x000000000000000000000000000000000000000000000000e91b8a11e7842c38","0x000000000000000000000000000000000000000000000007fd51009034b3357f","0x000000000000000000000000000000000000000000000009889939f81e9c7402","0x0000000000000000000000000000000000000000000000000000f94656a2ca48","0x000000000000000000000000000000000000000000000006fb128b46c1ddb67f","0x0000000000000000000000000000000000000000000000093fe27776f50224bd","0x000000000000000000000000000000000000000000000004a0c80c0da527a081","0x0000000000000000000000000000000000000000000000000001b52c2020d746","0x0000000000000000000000000000005a9bae947e1e91af9e4033d8d6aa6ed632","0x000000000000000000000000000000000025e485e013446d4ac7981c88ba6ecc","0x000000000000000000000000000000ff1e0496e30ab24a63b32b2d1120b76e62","0x00000000000000000000000000000000001afe0a8a685d7cd85d1010e55d9d7c","0x000000000000000000000000000000b0804efd6573805f991458295f510a2004","0x00000000000000000000000000000000000c81a178016e2fe18605022d5a8b0e","0x000000000000000000000000000000eba51e76eb1cfff60a53a0092a3c3dea47","0x000000000000000000000000000000000022e7466247b533282f5936ac4e6c15","0x00000000000000000000000000000071b1d76edf770edff98f00ff4deec264cd","0x00000000000000000000000000000000001e48128e68794d8861fcbb2986a383","0x000000000000000000000000000000d3a2af4915ae6d86b097adc377fafda2d4","0x000000000000000000000000000000000006359de9ca452dab3a4f1f8d9c9d98","0x0000000000000000000000000000000d9d719a8b9f020ad3642d60fe704e696f","0x00000000000000000000000000000000000ddfdbbdefc4ac1580ed38e12cfa49","0x0000000000000000000000000000008289fe9754ce48cd01b7be96a861b5e157","0x00000000000000000000000000000000000ff3e0896bdea021253b3d360fa678","0x0000000000000000000000000000000d9d719a8b9f020ad3642d60fe704e696f","0x00000000000000000000000000000000000ddfdbbdefc4ac1580ed38e12cfa49","0x0000000000000000000000000000008289fe9754ce48cd01b7be96a861b5e157","0x00000000000000000000000000000000000ff3e0896bdea021253b3d360fa678","0x000000000000000000000000000000f968b227a358a305607f3efc933823d288","0x00000000000000000000000000000000000eaf8adb390375a76d95e918b65e08","0x000000000000000000000000000000bb34b4b447aae56f5e24f81c3acd6d547f","0x00000000000000000000000000000000002175d012746260ebcfe339a91a81e1","0x0000000000000000000000000000005b739ed2075f2b046062b8fc6a2d1e9863","0x00000000000000000000000000000000001285cd1030d338c0e1603b4da2c838","0x00000000000000000000000000000027447d6c281eb38b2b937af4a516d60c04","0x000000000000000000000000000000000019bc3d980465fbb4a656a74296fc58","0x000000000000000000000000000000b484788ace8f7df86dd5e325d2e9b12599","0x00000000000000000000000000000000000a2ca0d10eb7b767114ae230b728d3","0x000000000000000000000000000000c6dfc7092f16f95795e437664498b88d53","0x0000000000000000000000000000000000131067b4e4d95a4f6f8cf5c9b5450a","0x0f413f22eec51f2a02800e0cafaeec1d92d744fbbaef213c687b9edabd6985f5","0x21230f4ff26c80ffb5d037a9d1d26c3f955ca34cbeca4f54db6656b932967a0c","0x0521f877fe35535767f99597cc50effbd283dcae6812ee0a7620d796ccbfd642","0x202b01350a9cc5c20ec0f3eaada338c0a3b793811bd539418ffa3cc4302615e2","0x2d1214d9b0d41058ad4a172d9c0aecc5bdabe95e687c3465050c6b5396509be4","0x1113b344a151b0af091cb28d728b752ebb4865da6cd7ee68471b961ca5cf69b9","0x2aa66d0954bb83e17bd5c9928d3aa7a7df75d741d409f7c15ba596804ba643fb","0x2e26bc7a530771ef7a95d5360d537e41cf94d8a0942764ff09881c107f91a106","0x0f14f32b921bb63ad1df00adab7c82af58ea8aa7f353f14b281208d8c5fab504","0x13429515c0c53b6502bbcdf545defb3cb69a986c9263e070fcbb397391aae1a3","0x1f21cac5e2f262afc1006a21454cc6bcb018c44e53ad8ab61cebbac99e539176","0x2a9886a6ddc8a61b097c668cd362fc8acdee8dde74f7b1af192c3e060bb2948f","0x2d718181e408ead2e9bcd30a84ad1fccbaf8d48ab6d1820bad4933d284b503c4","0x2634c1aafc902f14508f34d3d7e9d485f42d1a4c95b5a1ef73711ed0d3c68d77","0x092ede9777e6472ce5ffd8c963d466006189e960e2c591d338dc8d4af1a057fb","0x1cba45b17fd24f1cb1b4ab7b83eee741f6c77ba70a497dc4de259eceb7d5ea26","0x246e887c7bf2e17f919b2393b6e9b00b33e8822d862544a775aac05cb7bff710","0x04c3f539fe8689971948afcb437f1ecbd444a5bddaca1c8a450348dcd8480047","0x20c6a423ae4fd58e8951aa378d02d77baf90508ceb48856db2319d70938b186e","0x1bcf8786b554b3316d8ebdbc9d006a4e5d4865aad512ffd404b7f83550d3d030","0x09ab038260518f0970564afcd6bf22e2abf6b1fa5e12a327bbf195b6ca5edd78","0x1024e32554746f89c195286ba6ccfc9765e5d14bbe8064bc6fdf22d16ec6b495","0x17706656f8dbd7e47bb257a6428f0cb7278ea02fa9e6ce431d7bcc9133fba9c7","0x25a3e8a33c15ef2a4dd16313a6049bf1d468b4cdc141f238f2d51a1e8e1c22b3","0x1198863f08006edb27aee23164fb117a4ddec1bf1ed89807aa907e5cd24bf068","0x1862b4856b5b4d4a064f873e221703e4e2cd1ebfca1337dedca56485c38ed5a0","0x062214af1ea6dd6bf8895b92d394571c43970b6f967e1c794624d96071b25ad3","0x1e5be9428ddcf1f9b0cbafc28101e792ec5cf73852b0cd0b84fbff71b4490e09","0x2d4189bea5b1e30f63c64bd26df82f18bcaf885ec8887b54634b2557869ce87f","0x0f2e5d9a908850e9d44925e17d8b12d1adb1ed029799c9b5858598504242bbc0","0x3050dc85746a57931d99f3f35e77c2ba561fba0baa018b79ff1fd544026833ae","0x2a591a32437e5e0b875a137fd868bd1b6dbc003ff1b661f26e00627cc7c5cf47","0x27946841e1670ad9c65717016d0cedf524724217236e81b9fd0a264a36ebfb0e","0x0fc396e9d19d6e68e289602e292ee345542d0d28bf6de34fa62cc577cbdfb1df","0x08e7433a07a44c0c9c4dd4b273a2685bbd1a91fd5cf2b43409458fab42a23e1b","0x12bd9bfb029c3503a5c6deea87b0a0f11bb9f7ea584af2d48f3e48d7e09247ae","0x2ccc4810748c0a82dfc0f063d0b8c7999ffe9474653080e6ef92b3cb7a428784","0x08eb574d7fecadadb508c8bd35fdad06b99110609d679763c2e3645229b1b95a","0x0f1a65e747c8021ed7c454a4be1e89b1bce66ead9ed980fa98a7a050eafe98a1","0x1c8ff9e36684ec71614dee4c17859b06c742089f6029d3694a16e00dac9b57f1","0x0303101a8ba712aeca4da85b767ab8d3ecf489ec7d746f8ee20041717cc000e9","0x0aaf64c65e7088e5596108c9601467911fea809ca6540d79af77e6e66e36cd99","0x17caf164ce74ea7edfb1390e07763d2197797ec26661b92cde18a98d61d2fddc","0x18cb055c7ad6d01437725bb457681d81f3ecadc4f35d838a3c13daf25a44456a","0x2d78602b8bbcd32b36a99a6e2d248e7fe044ef1b50813133370412f9ef5299f0","0x2b139276ea86d426a115479e4154f72a6bd83a6253bf13e9670dc6b4664378f0","0x127c7837b384902c39a104036c09546728571c46c8166b1b9b13b3a615ebb781","0x05faa4816f83cf0189a482ad943c94b9ec6474002f2b327f8698763ad0ea0985","0x2f90359cc30ee693fb3aced96523cf7aebd152c22329eee56a398d9a4ac0628e","0x0a71beaf17a59c5a238f04c1f203848d87502c5057a78c13f0cfb0f9876e7714","0x2696c1e6d089556adaeb95c8a5e3065b00a393a38c2d69e9bd6ce8cdc49d87da","0x1f3d165a7dc6564a036e451eb9cb7f1e1cb1e6d29daa75e3f135ea3e58a79ccd","0x1473a660819bdd838d56122b72b32b267211e9f1103239480ec50fa85c9e1035","0x0a8ccaeb22451f391b3fc3467c8e6e900270a7afb7b510e8acf5a4f06f1c0888","0x03b3080afc0658cc87e307758cebc171921f43eca159b9dedf7f72aa8dd926bd","0x2dd7d6663fa0e1755dfafac352c361fcd64c7f4d53627e3646870ac169cc4a07","0x1ec54b883f5f35ccad0e75695af20790d9860104095bab34c9bf01628dd40cb9","0x193dff50f83c241f7a9e087a29ce72ecf3f6d8563593f786dcd04c32bcfd4ced","0x135122c0dae26cda8ca1c09de8225064ad86d10423ab0aaa53b481aa4626e1d6","0x08d5a56cbfab5aeed56d3cdd7fb6b30fc26b0c1a5b63fccd7fa44c53ba6fd35a","0x0d12f126dfa2daad3726d00ca339284cc22e36c6d81bb7a4b95c6f9598b60e7c","0x2e8b24bbdf2fd839d3c7cae1f0eeb96bfcfaeef30b27476f2fafcb17da78cd5e","0x2364acfe0cea39b7f749c5f303b99504977357925f810f684c60f35d16315211","0x06ca062eb70b8c51cfac35345e7b6b51f33a8ec9ebe204fb9b4911200bf508b7","0x266c0aa1ccb97186815bf69084f600d06ddd934e59a38dfe602ee5d6b9487f22","0x1d817537a49c6d0e3b4b65c6665334b91d7593142e60065048be9e55ceb5e7ab","0x05e9b7256a368df053c691952b59e9327a7c12ed322bbd6f72c669b9b9c26d49","0x05e9b7256a368df053c691952b59e9327a7c12ed322bbd6f72c669b9b9c26d49","0x25b77026673a1e613e50df0e88fb510973739d5f9064bd364079a9f884209632","0x25c9bc7a3f6aae3d43ff68b5614b34b5eaceff37157b37347995d231784ac1fd","0x085f69baef22680ae15f4801ef4361ebe9c7fc24a94b5bc2527dce8fb705439e","0x0d7c6b9ce31bfc32238a205455baf5ffe99cd30eb0f7bb5b504e1d4501e01382","0x1001a8cc4bc1221c814fba0eddcf3c40619b133373640c600de5bed0a0a05b10","0x20f5894be90e52977cb70f4f4cbd5101693db0360848939750db7e91109d54b6","0x22c09cb26db43f0599408b4daed0f4f496c66424e6affa41c14387d8e0af851b","0x24e5f41357798432426a9549d71e8cc681eaebacbe87f6e3bf38e85de5aa2f3d","0x06eb90100c736fbf2b87432d7821ecdc0b365024739bc36363d48b905973f5b9","0x0000000000000000000000000000007f36e0b4f59927ebbb2302e76cbe8bd44e","0x00000000000000000000000000000000001b95777c6c98640c80638c195909ca","0x0000000000000000000000000000006d4b1ad71244248cb2070fbbbb0ac9df88","0x00000000000000000000000000000000001abada4d5d816a67b6fc75746cb723","0x000000000000000000000000000000465811089df032ceb5269254547a101e57","0x000000000000000000000000000000000011a4a909c59776a6df9c7615e8e87d","0x000000000000000000000000000000311f6f724e7199351c9774225f15c25f20","0x00000000000000000000000000000000001ddba8eb0ab208ad3d96c70941fcbc","0x0000000000000000000000000000000dfa80bdf5be151b21ad89466b7201b63d","0x000000000000000000000000000000000015ca7dc258adab8ea406d94e00c56d","0x000000000000000000000000000000507ea3454165f92295b6e435c7d30d14f0","0x00000000000000000000000000000000002f522608db7b7d389d1df67eab104d","0x000000000000000000000000000000950102cce743fadb23965fc72e31efd36c","0x000000000000000000000000000000000018b4a7ec90df68dfe97d3c5367d1bf","0x000000000000000000000000000000118d90258b25dba8bc0f99d9f7547c6a62","0x000000000000000000000000000000000012d78638701da6322abbf325693b0f","0x000000000000000000000000000000144743e0d082f35295b51561af65f94c6b","0x00000000000000000000000000000000002322a615615e5405836374bb3c5336","0x000000000000000000000000000000e6f08dd5904ee42f826cde680919b41a96","0x00000000000000000000000000000000002d3f823ea255b68465e4b5360bf864","0x00000000000000000000000000000076d4db93683b6363ae92a5a20d8bb9922e","0x00000000000000000000000000000000002f8a7009cac72c9599b81cb9054308","0x00000000000000000000000000000085c12dd2be9f2b29e54c1a4bc3cbf9b6ce","0x000000000000000000000000000000000024e3688a1f4f50b0c6bd6c068f32b2","0x00000000000000000000000000000023a2015e7ea351e444c9405adfbd81e84d","0x00000000000000000000000000000000001fb3e4228c15dc4380db796925ec49","0x000000000000000000000000000000834ad9406b8ded7208b872373be7445e47","0x0000000000000000000000000000000000267544d6a9f5cc46d10555f2617c65"] public_inputs = ["0x0000000000000000000000000000000000000000000000000000000000000003"] verification_key = ["0x0000000000000000000000000000000000000000000000000000000000000020","0x0000000000000000000000000000000000000000000000000000000000000011","0x0000000000000000000000000000000000000000000000000000000000000001","0x00000000000000000000000000000060e430ad1c23bfcf3514323aae3f206e84","0x00000000000000000000000000000000001b5c3ff4c2458d8f481b1c068f27ae","0x000000000000000000000000000000bb510ab2112def34980e4fc6998ad9dd16","0x00000000000000000000000000000000000576e7c105b43e061e13cb877fefe1","0x000000000000000000000000000000ced074785d11857b065d8199e6669a601c","0x00000000000000000000000000000000000053b48a4098c1c0ae268f273952f7","0x000000000000000000000000000000d1d4b26e941db8168cee8f6de548ae0fd8","0x00000000000000000000000000000000001a9adf5a6dadc3d948bb61dfd63f4c","0x0000000000000000000000000000009ce1faac6f8de6ebb18f1db17372c82ad5","0x00000000000000000000000000000000002002681bb417184b2df070a16a3858","0x000000000000000000000000000000161baa651a8092e0e84725594de5aba511","0x00000000000000000000000000000000000be0064399c2a1efff9eb0cdcb2223","0x0000000000000000000000000000008673be6fd1bdbe980a29d8c1ded54381e7","0x000000000000000000000000000000000008a5158a7d9648cf1d234524c9fa0c","0x0000000000000000000000000000002b4fce6e4b1c72062b296d49bca2aa4130","0x00000000000000000000000000000000002e45a9eff4b6769e55fb710cded44f","0x00000000000000000000000000000072b85bf733758b76bcf97333efb85a23e3","0x000000000000000000000000000000000017da0ea508994fc82862715e4b5592","0x00000000000000000000000000000094fa74695cf058dba8ff35aec95456c6c3","0x0000000000000000000000000000000000211acddb851061c24b8f159e832bd1","0x000000000000000000000000000000303b5e5c531384b9a792e11702ad3bcab0","0x00000000000000000000000000000000000d336dff51a60b8833d5d7f6d4314c","0x0000000000000000000000000000009f825dde88092070747180d581c342444a","0x0000000000000000000000000000000000237fbd6511a03cca8cac01b555fe01","0x0000000000000000000000000000007c313205159495df6d8de292079a4844ff","0x000000000000000000000000000000000018facdfc468530dd45e8f7a1d38ce9","0x0000000000000000000000000000000d1ce33446fc3dc4ab40ca38d92dac74e1","0x00000000000000000000000000000000000852d8e3e0e8f4435af3e94222688b","0x0000000000000000000000000000006c04ee19ec1dfec87ed47d6d04aa158de2","0x000000000000000000000000000000000013240f97a584b45184c8ec31319b5f","0x000000000000000000000000000000cefb5d240b07ceb4be26ea429b6dc9d9e0","0x00000000000000000000000000000000002dad22022121d689f57fb38ca21349","0x000000000000000000000000000000c9f189f2a91aeb664ce376d8b157ba98f8","0x00000000000000000000000000000000002531a51ad54f124d58094b219818d2","0x000000000000000000000000000000ef1e6db71809307f677677e62b4163f556","0x0000000000000000000000000000000000272da4396fb2a7ee0638b9140e523d","0x0000000000000000000000000000002e54c0244a7732c87bc4712a76dd8c83fb","0x000000000000000000000000000000000007db77b3e04b7eba9643da57cbbe4d","0x000000000000000000000000000000e0dfe1ddd7f74ae0d636c910c3e85830d8","0x00000000000000000000000000000000000466fa9b57ec4664abd1505b490862","0x0000000000000000000000000000009ee55ae8a32fe5384c79907067cc27192e","0x00000000000000000000000000000000000799d0e465cec07ecb5238c854e830","0x0000000000000000000000000000001d5910ad361e76e1c241247a823733c39f","0x00000000000000000000000000000000002b03f2ccf7507564da2e6678bef8fe","0x000000000000000000000000000000231147211b3c75e1f47d150e4bbd2fb22e","0x00000000000000000000000000000000000d19ee104a10d3c701cfd87473cbbe","0x0000000000000000000000000000006705f3f382637d00f698e2c5c94ed05ae9","0x00000000000000000000000000000000000b9c792da28bb60601dd7ce4b74e68","0x000000000000000000000000000000ac5acc8cc21e4ddb225c510670f80c80b3","0x00000000000000000000000000000000002da9d3fa57343e6998aba19429b9fa","0x0000000000000000000000000000004bacbf54b7c17a560df0af18b6d0d527be","0x00000000000000000000000000000000000faea33aeca2025b22c288964b21eb","0x000000000000000000000000000000492e756298d68d6e95de096055cc0336c3","0x00000000000000000000000000000000001a12a12f004859e5a3675c7315121b","0x000000000000000000000000000000893d521d512f30e6d32afbbc0cecd8ee00","0x00000000000000000000000000000000001674b3c1ef12c6da690631e0d86c04","0x000000000000000000000000000000aa6cb02a52e7a613873d4ac9b411349945","0x00000000000000000000000000000000001ecb1fe9c493add46751f9940f73e1","0x00000000000000000000000000000045b3d362ca82cba69fb2b9c733a5b8c351","0x000000000000000000000000000000000019a683586af466e331945b732d2f8c","0x000000000000000000000000000000fc79b052dfdfe67c0ecfc06b4267ffd694","0x00000000000000000000000000000000001336a70c396393038d5e9913744ac2","0x0000000000000000000000000000005450d29af1e9438e91cd33ddeb2548226e","0x000000000000000000000000000000000000993a602891cfd0e6f6ecf7404933","0x000000000000000000000000000000498efddab90a32e9b2db729ed6e9b40192","0x00000000000000000000000000000000002425efebe9628c63ca6fc28bdb5901","0x000000000000000000000000000000d8488157f875a21ab5f93f1c2b641f3de9","0x0000000000000000000000000000000000290f95ada3936604dc4b14df7504e3","0x0000000000000000000000000000005d6902187f3ed60dcce06fca211b40329a","0x00000000000000000000000000000000002b5870a6ba0b20aaa0178e5adfbc36","0x000000000000000000000000000000e5c2519171fa0e548fc3c4966ffc1ce570","0x00000000000000000000000000000000001cb8d8f4793b7debbdc429389dbf2d","0x000000000000000000000000000000a3ee22dd60456277b86c32a18982dcb185","0x00000000000000000000000000000000002493c99a3d068b03f8f2b8d28b57ce","0x000000000000000000000000000000f6c3731486320082c20ec71bbdc92196c1","0x00000000000000000000000000000000001ded39c4c8366469843cd63f09ecac","0x000000000000000000000000000000494997477ab161763e46601d95844837ef","0x00000000000000000000000000000000002e0cddbc5712d79b59cb3b41ebbcdd","0x000000000000000000000000000000426db4c64531d350750df62dbbc41a1bd9","0x0000000000000000000000000000000000303126892f664d8d505964d14315ec","0x00000000000000000000000000000076a6b2c6040c0c62bd59acfe3e3e125672","0x000000000000000000000000000000000000874a5ad262eecc6b565e0b085074","0x000000000000000000000000000000ef082fb517183c9c6841c2b8ef2ca1df04","0x0000000000000000000000000000000000127b2a745a1b74968c3edc18982b9b","0x000000000000000000000000000000c9efd4f8c3d56e1eb23d789a8f710d5be6","0x000000000000000000000000000000000015a18748490ff4c2b1871081954e86","0x000000000000000000000000000000a0011ef987dc016ab110eacd554a1d8bbf","0x00000000000000000000000000000000002097c84955059442a95df075833071","0x000000000000000000000000000000d38e9426ad3085b68b00a93c17897c2877","0x00000000000000000000000000000000002aecd48089890ea0798eb952c66824","0x00000000000000000000000000000078d8a9ce405ce559f441f2e71477ff3ddb","0x00000000000000000000000000000000001216bdb2f0d961bb8a7a23331d2150","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x000000000000000000000000000000ee40d90bea71fba7a412dd61fcf34e8ceb","0x0000000000000000000000000000000000140b0936c323fd2471155617b6af56","0x0000000000000000000000000000002b90071823185c5ff8e440fd3d73b6fefc","0x00000000000000000000000000000000002b6c10790a5f6631c87d652e059df4"] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr index d25fd804ce4..c534b07fc77 100644 --- a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr @@ -6,7 +6,7 @@ fn main( // This is the proof without public inputs attached. // // This means: the size of this does not change with the number of public inputs. - proof: [Field; 153], + proof: [Field; 156], public_inputs: pub [Field; 1], // This is currently not public. It is fine given that the vk is a part of the circuit definition. // I believe we want to eventually make it public too though. From 80d26d883154b81f0f92664d61c907bcfe46509b Mon Sep 17 00:00:00 2001 From: spypsy Date: Tue, 25 Jun 2024 17:52:43 +0100 Subject: [PATCH 8/9] fix(ci): don't run npm_deploy l1-contracts (#7187) --- .circleci/config.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8c3765900aa..2322e6159b3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -440,16 +440,6 @@ jobs: command: | should_release || exit 0 yarn-project/deploy_npm.sh latest - - run: - name: "Release canary to NPM: l1-contracts" - command: | - should_release || exit 0 - deploy_npm l1-contracts canary - - run: - name: "Release latest to NPM: l1-contracts" - command: | - should_release || exit 0 - deploy_npm l1-contracts latest - run: name: "Update aztec-up" command: | From 4c1997fdc989fc12c0fb50f690558fccab8e0cd1 Mon Sep 17 00:00:00 2001 From: Cody Gunton Date: Tue, 25 Jun 2024 13:05:34 -0400 Subject: [PATCH 9/9] feat: Full test skips public simulation (#7186) The e2e prover test skips public simulation. Change suggested by @sirasistant --- yarn-project/end-to-end/src/e2e_prover/full.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_prover/full.test.ts b/yarn-project/end-to-end/src/e2e_prover/full.test.ts index bbc1c8b7404..485c6d3d847 100644 --- a/yarn-project/end-to-end/src/e2e_prover/full.test.ts +++ b/yarn-project/end-to-end/src/e2e_prover/full.test.ts @@ -60,8 +60,8 @@ describe('full_prover', () => { logger.info(`Verifying private kernel tail proof`); await expect(t.circuitProofVerifier?.verifyProof(privateTx)).resolves.not.toThrow(); - const sentPrivateTx = privateInteraction.send(); - const sentPublicTx = publicInteraction.send(); + const sentPrivateTx = privateInteraction.send({ skipPublicSimulation: true }); + const sentPublicTx = publicInteraction.send({ skipPublicSimulation: true }); await Promise.all([ sentPrivateTx.wait({ timeout: 1200, interval: 10 }), sentPublicTx.wait({ timeout: 1200, interval: 10 }),