From 948ec383b30c4f467b6da6591fa518ce793fc54d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Bene=C5=A1?= Date: Tue, 21 May 2024 12:05:44 +0200 Subject: [PATCH] feat: generic key validation request (#6474) --- .../circuits/private-function.md | 2 +- .../circuits/private-kernel-initial.mdx | 16 +- .../circuits/private-kernel-inner.mdx | 10 +- .../circuits/private-kernel-reset.md | 9 +- .../circuits/private-kernel-tail.md | 2 +- docs/docs/protocol-specs/constants.md | 4 +- .../src/core/libraries/ConstantsGen.sol | 13 +- .../aztec-nr/address-note/src/address_note.nr | 2 +- .../aztec/src/context/private_context.nr | 58 +-- noir-projects/aztec-nr/aztec/src/keys.nr | 1 + .../aztec-nr/aztec/src/keys/constants.nr | 19 + .../aztec-nr/aztec/src/keys/getters.nr | 13 +- .../aztec-nr/aztec/src/keys/public_keys.nr | 12 +- noir-projects/aztec-nr/aztec/src/oracle.nr | 2 +- .../src/oracle/key_validation_request.nr | 17 + .../aztec/src/oracle/nullifier_keys.nr | 21 -- .../aztec-nr/value-note/src/value_note.nr | 2 +- .../src/subscription_note.nr | 2 +- .../src/types/card_note.nr | 2 +- .../src/ecdsa_public_key_note.nr | 2 +- .../src/public_key_note.nr | 2 +- .../contracts/test_contract/src/main.nr | 5 + .../src/types/token_note.nr | 2 +- .../token_contract/src/types/token_note.nr | 2 +- .../kernel_circuit_public_inputs_composer.nr | 2 +- ...e_kernel_circuit_public_inputs_composer.nr | 14 +- .../src/private_kernel_init.nr | 12 +- .../src/private_kernel_reset.nr | 30 +- .../src/private_kernel_tail.nr | 12 +- .../src/private_kernel_tail_to_public.nr | 14 +- .../src/tests/validate_arrays.nr | 27 +- .../src/main.nr | 4 +- .../crates/private-kernel-reset/src/main.nr | 4 +- .../private_validation_request_processor.nr | 25 +- .../crates/reset-kernel-lib/src/reset.nr | 2 +- .../src/reset/key_validation_hint.nr | 62 ++++ .../src/reset/nullifier_key.nr | 66 ---- .../crates/types/src/abis.nr | 2 +- .../types/src/abis/key_validation_request.nr | 101 +++++ .../abis/nullifier_key_validation_request.nr | 101 ----- .../src/abis/private_circuit_public_inputs.nr | 30 +- .../validation_requests.nr | 17 +- .../validation_requests_builder.nr | 11 +- .../crates/types/src/constants.nr | 14 +- .../crates/types/src/tests/fixture_builder.nr | 27 +- .../private_circuit_public_inputs_builder.nr | 16 +- .../circuit-types/src/keys/key_store.ts | 53 ++- yarn-project/circuits.js/src/constants.gen.ts | 12 +- ...x.test.ts.snap => derivation.test.ts.snap} | 0 .../{index.test.ts => derivation.test.ts} | 2 +- .../circuits.js/src/keys/derivation.ts | 102 ++++++ yarn-project/circuits.js/src/keys/index.ts | 94 +---- .../circuits.js/src/keys/key_types.ts | 5 + yarn-project/circuits.js/src/keys/utils.ts | 8 + .../src/scripts/generate_reset_variants.ts | 12 +- yarn-project/circuits.js/src/structs/index.ts | 2 +- ...ate_kernel_reset_circuit_private_inputs.ts | 46 ++- .../src/structs/key_validation_request.ts | 92 +++++ .../nullifier_key_validation_request.ts | 101 ----- .../structs/private_circuit_public_inputs.ts | 21 +- .../src/structs/read_request_hints/index.ts | 2 +- .../read_request_hints/key_validation_hint.ts | 31 ++ .../read_request_hints/nullifier_key_hint.ts | 20 - .../src/structs/validation_requests.ts | 19 +- .../circuits.js/src/tests/factories.ts | 30 +- yarn-project/end-to-end/Earthfile | 3 + .../end-to-end/src/e2e_key_registry.test.ts | 67 +--- yarn-project/end-to-end/src/e2e_keys.test.ts | 128 +++++++ .../key-store/src/test_key_store.test.ts | 56 ++- yarn-project/key-store/src/test_key_store.ts | 345 ++++++++---------- .../src/type_conversion.ts | 116 +++--- yarn-project/pxe/src/kernel_oracle/index.ts | 6 +- .../build_private_kernel_reset_hints.ts | 37 +- .../src/kernel_prover/proving_data_oracle.ts | 12 +- .../pxe/src/simulator_oracle/index.ts | 9 +- .../simulator/src/acvm/oracle/oracle.ts | 12 +- .../simulator/src/acvm/oracle/typed_oracle.ts | 13 +- .../simulator/src/client/db_oracle.ts | 12 +- .../src/client/private_execution.test.ts | 37 +- .../simulator/src/client/simulator.test.ts | 9 +- .../simulator/src/client/view_data_oracle.ts | 14 +- 81 files changed, 1218 insertions(+), 1123 deletions(-) create mode 100644 noir-projects/aztec-nr/aztec/src/keys/constants.nr create mode 100644 noir-projects/aztec-nr/aztec/src/oracle/key_validation_request.nr delete mode 100644 noir-projects/aztec-nr/aztec/src/oracle/nullifier_keys.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/key_validation_hint.nr delete mode 100644 noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/nullifier_key.nr create mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/abis/key_validation_request.nr delete mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/abis/nullifier_key_validation_request.nr rename yarn-project/circuits.js/src/keys/__snapshots__/{index.test.ts.snap => derivation.test.ts.snap} (100%) rename yarn-project/circuits.js/src/keys/{index.test.ts => derivation.test.ts} (97%) create mode 100644 yarn-project/circuits.js/src/keys/derivation.ts create mode 100644 yarn-project/circuits.js/src/keys/key_types.ts create mode 100644 yarn-project/circuits.js/src/keys/utils.ts create mode 100644 yarn-project/circuits.js/src/structs/key_validation_request.ts delete mode 100644 yarn-project/circuits.js/src/structs/nullifier_key_validation_request.ts create mode 100644 yarn-project/circuits.js/src/structs/read_request_hints/key_validation_hint.ts delete mode 100644 yarn-project/circuits.js/src/structs/read_request_hints/nullifier_key_hint.ts create mode 100644 yarn-project/end-to-end/src/e2e_keys.test.ts diff --git a/docs/docs/protocol-specs/circuits/private-function.md b/docs/docs/protocol-specs/circuits/private-function.md index 613282ef1b7..d137504d227 100644 --- a/docs/docs/protocol-specs/circuits/private-function.md +++ b/docs/docs/protocol-specs/circuits/private-function.md @@ -51,7 +51,7 @@ The public inputs of _every_ private function _must_ adhere to the following ABI | `encrypted_note_preimage_hashes` | [[`EncryptedNotePreimageHash`](#encryptednotepreimagehash); [`MAX_ENCRYPTED_NOTE_PREIMAGE_HASHES_PER_CALL`](../constants.md#circuit-constants)] | Hashes of the encrypted note preimages emitted in this function call. | | `note_hash_read_requests` | [[`ReadRequest`](#readrequest); [`MAX_NOTE_HASH_READ_REQUESTS_PER_CALL`](../constants.md#circuit-constants)] | Requests to prove the note hashes being read exist. | | `nullifier_read_requests` | [[`ReadRequest`](#readrequest); [`MAX_NULLIFIER_READ_REQUESTS_PER_CALL`](../constants.md#circuit-constants)] | Requests to prove the nullifiers being read exist. | -| `nullifier_key_validation_requests` | [[`ParentSecretKeyValidationRequest`](#parentsecretkeyvalidationrequest); [`MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL`](../constants.md#circuit-constants)] | Requests to validate nullifier keys used in this function call. | +| `key_validation_requests` | [[`ParentSecretKeyValidationRequest`](#parentsecretkeyvalidationrequest); [`MAX_KEY_VALIDATION_REQUESTS_PER_CALL`](../constants.md#circuit-constants)] | Requests to validate keys used in this function call. | | `public_call_requests` | [[`PublicCallRequest`](#publiccallrequest); [`MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL`](../constants.md#circuit-constants)] | Requests to call public functions. | | `private_call_requests` | [[`PrivateCallRequest`](#privatecallrequest); [`MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL`](../constants.md#circuit-constants)] | Requests to call Private functions. | | `counter_start` | `u32` | Counter at which the function call was initiated. | diff --git a/docs/docs/protocol-specs/circuits/private-kernel-initial.mdx b/docs/docs/protocol-specs/circuits/private-kernel-initial.mdx index 843f516772e..a7e2fde94a5 100644 --- a/docs/docs/protocol-specs/circuits/private-kernel-initial.mdx +++ b/docs/docs/protocol-specs/circuits/private-kernel-initial.mdx @@ -221,7 +221,7 @@ This circuit verifies that the values in [`private_inputs`](#private-inputs)[`.p - `value`, `contract_address`, `counter` - `nullifier_read_requests` - `value`, `contract_address`, `counter` -- `nullifier_key_validation_request_contexts` +- `key_validation_request_contexts` - `parent_public_key`, `hardened_child_secret_key` - `unencrypted_log_hash_contexts` - `hash`, `length`, `counter` @@ -254,7 +254,7 @@ This circuit verifies that the values in [`private_inputs`](#private-inputs)[`.p - `note_hash_contexts` - `nullifier_contexts` - `l2_to_l1_message_contexts` - - `nullifier_key_validation_request_contexts` + - `key_validation_request_contexts` - `unencrypted_log_hash_contexts` - `encrypted_log_hash_contexts` - `encrypted_note_preimage_hash_contexts` @@ -452,7 +452,7 @@ class PrivateFunctionPublicInputs { l2_to_l1_messages: List~field~ note_hash_read_requests: List~ReadRequest~ nullifier_read_requests: List~ReadRequest~ - nullifier_key_validation_requests: List~ParentSecretKeyValidationRequest~ + key_validation_requests: List~ParentSecretKeyValidationRequest~ unencrypted_log_hashes: List~UnencryptedLogHash~ encrypted_log_hashes: List~EncryptedLogHash~ encrypted_note_preimage_hashes: List~EncryptedNotePreimageHash~ @@ -470,7 +470,7 @@ PrivateFunctionPublicInputs *-- NoteHash: note_hashes PrivateFunctionPublicInputs *-- Nullifier: nullifiers PrivateFunctionPublicInputs *-- ReadRequest: note_hash_read_requests PrivateFunctionPublicInputs *-- ReadRequest: nullifier_read_requests -PrivateFunctionPublicInputs *-- ParentSecretKeyValidationRequest: nullifier_key_validation_requests +PrivateFunctionPublicInputs *-- ParentSecretKeyValidationRequest: key_validation_requests PrivateFunctionPublicInputs *-- UnencryptedLogHash: unencrypted_log_hashes PrivateFunctionPublicInputs *-- EncryptedLogHash: encrypted_log_hashes PrivateFunctionPublicInputs *-- EncryptedNotePreimageHash: encrypted_note_preimage_hashes @@ -529,7 +529,7 @@ class ParentSecretKeyValidationRequest { parent_public_key: GrumpkinPoint hardened_child_secret_key: fq } -ParentSecretKeyValidationRequest ..> ParentSecretKeyValidationRequestContext: nullifier_key_validation_requests\n->nullifier_key_validation_request_contexts +ParentSecretKeyValidationRequest ..> ParentSecretKeyValidationRequestContext: key_validation_requests\n->key_validation_request_contexts class UnencryptedLogHash { hash: field @@ -661,7 +661,7 @@ class TransientAccumulatedData { note_hash_contexts: List~NoteHashContext~ nullifier_contexts: List~NullifierContext~ l2_to_l1_message_contexts: List~L2ToL1MessageContext~ - nullifier_key_validation_request_contexts: List~ParentSecretKeyValidationRequestContext~ + key_validation_request_contexts: List~ParentSecretKeyValidationRequestContext~ unencrypted_log_hash_contexts: List~UnencryptedLogHashContext~ encrypted_log_hash_contexts: List~EncryptedLogHashContext~ encrypted_note_preimage_hash_contexts: List~EncryptedNotePreimageHashContext~ @@ -675,7 +675,7 @@ NullifierContext --* TransientAccumulatedData: nullifier_contexts L2ToL1MessageContext --* TransientAccumulatedData: l2_to_l1_message_contexts ReadRequest --* TransientAccumulatedData: note_hash_read_requests ReadRequest --* TransientAccumulatedData: nullifier_read_requests -ParentSecretKeyValidationRequestContext --* TransientAccumulatedData: nullifier_key_validation_request_contexts +ParentSecretKeyValidationRequestContext --* TransientAccumulatedData: key_validation_request_contexts UnencryptedLogHashContext --* TransientAccumulatedData: unencrypted_log_hash_contexts EncryptedLogHashContext --* TransientAccumulatedData: encrypted_log_hash_contexts EncryptedNotePreimageHashContext --* TransientAccumulatedData: encrypted_note_preimage_hash_contexts @@ -776,7 +776,7 @@ Would it be accurate to describe this as `AccumulatedTransientSideEffects`, perh | `encrypted_note_preimage_hash_contexts` | [[`EncryptedNotePreimageHashContext`](#encryptednotepreimagehash); [`MAX_ENCRYPTED_NOTE_PREIMAGE_HASHES_PER_TX`](../constants.md#circuit-constants)] | Hashes of the encrypted note preimages with extra data aiding verification. | | `note_hash_read_requests` | [[`ReadRequest`](./private-function#readrequest); [`MAX_NOTE_HASH_READ_REQUESTS_PER_TX`](../constants.md#circuit-constants)] | Requests to prove the note hashes being read exist. | | `nullifier_read_requests` | [[`ReadRequest`](./private-function#readrequest); [`MAX_NULLIFIER_READ_REQUESTS_PER_TX`](../constants.md#circuit-constants)] | Requests to prove the nullifiers being read exist. | -| `nullifier_key_validation_request_contexts` | [[`ParentSecretKeyValidationRequestContext`](#parentsecretkeyvalidationrequestcontext); [`MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX`](../constants.md#circuit-constants)] | Requests to validate nullifier keys. | +| `key_validation_request_contexts` | [[`ParentSecretKeyValidationRequestContext`](#parentsecretkeyvalidationrequestcontext); [`MAX_KEY_VALIDATION_REQUESTS_PER_TX`](../constants.md#circuit-constants)] | Requests to validate nullifier keys. | | `public_call_request_contexts` | [[`PublicCallRequestContext`](./public-kernel-tail.md#publiccallrequestcontext); [`MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX`](../constants.md#circuit-constants)] | Requests to call publics functions. | | `private_call_request_stack` | [[`PrivateCallRequestContext`](#privatecallrequestcontext); [`MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX`](../constants.md#circuit-constants)] | Requests to call private functions. Pushed to the stack in reverse order so that they will be executed in chronological order. | diff --git a/docs/docs/protocol-specs/circuits/private-kernel-inner.mdx b/docs/docs/protocol-specs/circuits/private-kernel-inner.mdx index 7041d3a6999..78d97022a67 100644 --- a/docs/docs/protocol-specs/circuits/private-kernel-inner.mdx +++ b/docs/docs/protocol-specs/circuits/private-kernel-inner.mdx @@ -251,7 +251,7 @@ class PrivateFunctionPublicInputs { encrypted_note_preimage_hashes: List~EncryptedNotePreimageHash~ note_hash_read_requests: List~ReadRequest~ nullifier_read_requests: List~ReadRequest~ - nullifier_key_validation_requests: List~ParentSecretKeyValidationRequest~ + key_validation_requests: List~ParentSecretKeyValidationRequest~ public_call_requests: List~PublicCallRequest~ private_call_requests: List~PrivateCallRequest~ counter_start: u32 @@ -266,7 +266,7 @@ PrivateFunctionPublicInputs *-- NoteHash: note_hashes PrivateFunctionPublicInputs *-- Nullifier: nullifiers PrivateFunctionPublicInputs *-- ReadRequest: note_hash_read_requests PrivateFunctionPublicInputs *-- ReadRequest: nullifier_read_requests -PrivateFunctionPublicInputs *-- ParentSecretKeyValidationRequest: nullifier_key_validation_requests +PrivateFunctionPublicInputs *-- ParentSecretKeyValidationRequest: key_validation_requests PrivateFunctionPublicInputs *-- UnencryptedLogHash: unencrypted_log_hashes PrivateFunctionPublicInputs *-- EncryptedLogHash: encrypted_log_hashes PrivateFunctionPublicInputs *-- EncryptedNotePreimageHash: encrypted_note_preimage_hashes @@ -338,7 +338,7 @@ class ParentSecretKeyValidationRequest { parent_public_key: GrumpkinPoint hardened_child_secret_key: fq } -ParentSecretKeyValidationRequest ..> ParentSecretKeyValidationRequestContext: nullifier_key_validation_requests\n->nullifier_key_validation_request_contexts +ParentSecretKeyValidationRequest ..> ParentSecretKeyValidationRequestContext: key_validation_requests\n->key_validation_request_contexts class UnencryptedLogHash { hash: field @@ -475,7 +475,7 @@ class TransientAccumulatedData { encrypted_note_preimage_hash_contexts: List~EncryptedNotePreimageHashContext~ note_hash_read_requests: List~ReadRequest~ nullifier_read_requests: List~ReadRequest~ - nullifier_key_validation_request_contexts: List~ParentSecretKeyValidationRequestContext~ + key_validation_request_contexts: List~ParentSecretKeyValidationRequestContext~ public_call_request_contexts: List~PublicCallRequestContext~ private_call_request_stack: List~PrivateCallRequestContext~ } @@ -484,7 +484,7 @@ NullifierContext --* TransientAccumulatedData: nullifier_contexts L2ToL1MessageContext --* TransientAccumulatedData: l2_to_l1_message_contexts ReadRequest --* TransientAccumulatedData: note_hash_read_requests ReadRequest --* TransientAccumulatedData: nullifier_read_requests -ParentSecretKeyValidationRequestContext --* TransientAccumulatedData: nullifier_key_validation_request_contexts +ParentSecretKeyValidationRequestContext --* TransientAccumulatedData: key_validation_request_contexts UnencryptedLogHashContext --* TransientAccumulatedData: unencrypted_log_hash_contexts EncryptedLogHashContext --* TransientAccumulatedData: encrypted_log_hash_contexts EncryptedNotePreimageHashContext --* TransientAccumulatedData: encrypted_note_preimage_hash_contexts diff --git a/docs/docs/protocol-specs/circuits/private-kernel-reset.md b/docs/docs/protocol-specs/circuits/private-kernel-reset.md index 0b0dd79141b..01b374e88b9 100644 --- a/docs/docs/protocol-specs/circuits/private-kernel-reset.md +++ b/docs/docs/protocol-specs/circuits/private-kernel-reset.md @@ -83,11 +83,11 @@ This reset circuit validates the correct derivation of secret keys used in priva Initialize `requests_kept` to `0`. -For each `request` at index `i` in `nullifier_key_validation_request_contexts`, locate the `master_secret_key` at `master_secret_keys[i]`, provided as [hints](#hints-for-nullifier-key-validation-request-reset-private-kernel-circuit) through `private_inputs`. +For each `request` at index `i` in `key_validation_request_contexts`, locate the `master_secret_key` at `master_secret_keys[i]` and the relevant `app_secret_key` generator at `app_secret_keys_generators[i]`, provided as [hints](#hints-for-nullifier-key-validation-request-reset-private-kernel-circuit) through `private_inputs`. 1. If `master_secret_key == 0`, ensure the request remain within the `public_inputs`.: - - `public_inputs.transient_accumulated_data.nullifier_key_validation_request_contexts[requests_kept] == request` + - `public_inputs.transient_accumulated_data.key_validation_request_contexts[requests_kept] == request` - Increase `requests_kept` by 1: `requests_kept += 1` 2. Else: @@ -199,7 +199,7 @@ All arrays in the `transient_accumulated_data` in the [`public_inputs`](#public- 1. [Read request reset circuit](#note-hash-read-request-reset-private-kernel-circuit) (for note hashes): `note_hash_read_requests` 2. [Read request reset circuit](#nullifier-read-request-reset-private-kernel-circuit) (for nullifiers): `nullifier_read_requests` -3. [Parent secret key validation request reset circuit](#nullifier-key-validation-request-reset-private-kernel-circuit) (for nullifier keys): `nullifier_key_validation_request_contexts` +3. [Parent secret key validation request reset circuit](#nullifier-key-validation-request-reset-private-kernel-circuit) (for nullifier keys): `key_validation_request_contexts` 4. [Transient note reset circuit](#transient-note-reset-private-kernel-circuit): `note_hash_contexts` and `nullifier_contexts` #### Verifying other data. @@ -236,7 +236,8 @@ The format aligns with the [`PreviousKernel`](./private-kernel-inner#previousker | Field | Type | Description | | -------------------- | ---------------------------------------------------------------------------------------------- | --------------------------------------- | -| `master_secret_keys` | [`field`; [`MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX`](../constants.md#circuit-constants)] | Master secret keys for the secret keys. | +| `master_secret_keys` | [`field`; [`MAX_KEY_VALIDATION_REQUESTS_PER_TX`](../constants.md#circuit-constants)] | Master secret to try to derive app secret keys and pub keys from. | +| `app_secret_keys_generators` | [`field`; [`MAX_KEY_VALIDATION_REQUESTS_PER_TX`](../constants.md#circuit-constants)] | App secret key generators to assist with ^. | ### _Hints_ for [Transient Note Reset Private Kernel Circuit](#transient-note-reset-private-kernel-circuit) diff --git a/docs/docs/protocol-specs/circuits/private-kernel-tail.md b/docs/docs/protocol-specs/circuits/private-kernel-tail.md index 1c66ca85802..dc842b8994a 100644 --- a/docs/docs/protocol-specs/circuits/private-kernel-tail.md +++ b/docs/docs/protocol-specs/circuits/private-kernel-tail.md @@ -30,7 +30,7 @@ It checks the data within [`private_inputs`](#private-inputs)[`.previous_kernel` - `note_hash_read_requests` - `nullifier_read_requests` - - `nullifier_key_validation_request_contexts` + - `key_validation_request_contexts` - The `nullifier_counter` associated with each note hash in `note_hash_contexts`. - The `note_hash_counter` associated with each nullifier in `nullifier_contexts`. diff --git a/docs/docs/protocol-specs/constants.md b/docs/docs/protocol-specs/constants.md index 969a98ffae4..2be04c2b645 100644 --- a/docs/docs/protocol-specs/constants.md +++ b/docs/docs/protocol-specs/constants.md @@ -52,7 +52,7 @@ The statically-sized nature the kernel & rollup circuits will restrict the quant | `MAX_ENCRYPTED_NOTE_PREIMAGE_HASHES_PER_CALL` | 128 | | `MAX_NOTE_HASH_READ_REQUESTS_PER_CALL` | 128 | | `MAX_NULLIFIER_READ_REQUESTS_PER_CALL` | 128 | -| `MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL` | 1 | TODO: we shouldn't need this, given the reset circuit. | +| `MAX_KEY_VALIDATION_REQUESTS_PER_CALL | 16 | TODO: we shouldn't need this, given the reset circuit. | | `MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL` | 32 | | `MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL` | 32 | @@ -71,7 +71,7 @@ The statically-sized nature the kernel & rollup circuits will restrict the quant | `MAX_ENCRYPTED_NOTE_PREIMAGE_HASHES_PER_TX` | 128 | | `MAX_OPTIONALLY_REVEALED_DATA_LENGTH_PER_TX` | 4 | | `MAX_NOTE_HASH_READ_REQUESTS_PER_TX` | 128 | TODO: we shouldn't need this, given the reset circuit. | -| `MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX` | 4 | TODO: we shouldn't need this, given the reset circuit. | +| `MAX_KEY_VALIDATION_REQUESTS_PER_TX` | 64 | TODO: we shouldn't need this, given the reset circuit. | | `MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX` | 32 | | `MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX` | 32 | diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index cd657af88c3..72293011b05 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -25,7 +25,7 @@ library Constants { uint256 internal constant MAX_NOTE_HASH_READ_REQUESTS_PER_CALL = 32; uint256 internal constant MAX_NULLIFIER_READ_REQUESTS_PER_CALL = 32; uint256 internal constant MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL = 32; - uint256 internal constant MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL = 16; + uint256 internal constant MAX_KEY_VALIDATION_REQUESTS_PER_CALL = 16; uint256 internal constant MAX_NOTE_ENCRYPTED_LOGS_PER_CALL = 16; uint256 internal constant MAX_ENCRYPTED_LOGS_PER_CALL = 4; uint256 internal constant MAX_UNENCRYPTED_LOGS_PER_CALL = 4; @@ -39,7 +39,7 @@ library Constants { uint256 internal constant MAX_NOTE_HASH_READ_REQUESTS_PER_TX = 128; uint256 internal constant MAX_NULLIFIER_READ_REQUESTS_PER_TX = 128; uint256 internal constant MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX = 128; - uint256 internal constant MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX = 64; + uint256 internal constant MAX_KEY_VALIDATION_REQUESTS_PER_TX = 64; uint256 internal constant MAX_NOTE_ENCRYPTED_LOGS_PER_TX = 64; uint256 internal constant MAX_ENCRYPTED_LOGS_PER_TX = 8; uint256 internal constant MAX_UNENCRYPTED_LOGS_PER_TX = 8; @@ -119,9 +119,8 @@ library Constants { uint256 internal constant L2_TO_L1_MESSAGE_LENGTH = 3; uint256 internal constant SCOPED_L2_TO_L1_MESSAGE_LENGTH = L2_TO_L1_MESSAGE_LENGTH + 1; uint256 internal constant MAX_BLOCK_NUMBER_LENGTH = 2; - uint256 internal constant NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH = 3; - uint256 internal constant SCOPED_NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH = - NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH + 1; + uint256 internal constant KEY_VALIDATION_REQUEST_LENGTH = 3; + uint256 internal constant SCOPED_KEY_VALIDATION_REQUEST_LENGTH = KEY_VALIDATION_REQUEST_LENGTH + 1; uint256 internal constant PARTIAL_STATE_REFERENCE_LENGTH = 6; uint256 internal constant READ_REQUEST_LENGTH = 2; uint256 internal constant LOG_HASH_LENGTH = 3; @@ -146,7 +145,7 @@ library Constants { uint256 internal constant PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = CALL_CONTEXT_LENGTH + 4 + MAX_BLOCK_NUMBER_LENGTH + (READ_REQUEST_LENGTH * MAX_NOTE_HASH_READ_REQUESTS_PER_CALL) + (READ_REQUEST_LENGTH * MAX_NULLIFIER_READ_REQUESTS_PER_CALL) - + (NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH * MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL) + + (KEY_VALIDATION_REQUEST_LENGTH * MAX_KEY_VALIDATION_REQUESTS_PER_CALL) + (NOTE_HASH_LENGTH * MAX_NEW_NOTE_HASHES_PER_CALL) + (NULLIFIER_LENGTH * MAX_NEW_NULLIFIERS_PER_CALL) + (PRIVATE_CALL_REQUEST_LENGTH * MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL) @@ -175,7 +174,7 @@ library Constants { + (SCOPED_READ_REQUEST_LEN * MAX_NOTE_HASH_READ_REQUESTS_PER_TX) + (SCOPED_READ_REQUEST_LEN * MAX_NULLIFIER_READ_REQUESTS_PER_TX) + (SCOPED_READ_REQUEST_LEN * MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX) - + (SCOPED_NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH * MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX) + + (SCOPED_KEY_VALIDATION_REQUEST_LENGTH * MAX_KEY_VALIDATION_REQUESTS_PER_TX) + (PUBLIC_DATA_READ_LENGTH * MAX_PUBLIC_DATA_READS_PER_TX); uint256 internal constant PUBLIC_DATA_UPDATE_REQUEST_LENGTH = 2; uint256 internal constant COMBINED_ACCUMULATED_DATA_LENGTH = MAX_NEW_NOTE_HASHES_PER_TX diff --git a/noir-projects/aztec-nr/address-note/src/address_note.nr b/noir-projects/aztec-nr/address-note/src/address_note.nr index df33796ed35..e2a243fdf10 100644 --- a/noir-projects/aztec-nr/address-note/src/address_note.nr +++ b/noir-projects/aztec-nr/address-note/src/address_note.nr @@ -4,7 +4,7 @@ use dep::aztec::{ grumpkin_point::GrumpkinPoint, hash::poseidon2_hash }, note::{note_header::NoteHeader, note_interface::NoteInterface, utils::compute_note_hash_for_consumption}, - oracle::unsafe_rand::unsafe_rand, oracle::nullifier_keys::get_nsk_app, context::PrivateContext + oracle::unsafe_rand::unsafe_rand, keys::getters::get_nsk_app, context::PrivateContext }; global ADDRESS_NOTE_LEN: Field = 3; 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 db4b82441c1..26199d2f93e 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -2,9 +2,10 @@ use crate::{ context::{inputs::PrivateContextInputs, interface::ContextInterface}, messaging::process_l1_to_l2_message, hash::{hash_args_array, ArgsHasher, compute_unencrypted_log_hash}, + keys::constants::{NULLIFIER_INDEX, OUTGOING_INDEX, NUM_KEY_TYPES, sk_generators}, note::{note_interface::NoteInterface, utils::compute_note_hash_for_insertion}, oracle::{ - nullifier_keys::get_nullifier_key_validation_request, arguments, returns, + key_validation_request::get_key_validation_request, arguments, returns, call_private_function::call_private_function_internal, header::get_header_at, logs::{emit_encrypted_log, emit_encrypted_note_log, compute_encrypted_log}, logs_traits::{LensForEncryptedLog, ToBytesForUnencryptedLog}, @@ -18,7 +19,7 @@ use dep::protocol_types::{ hash::sha256_to_field, abis::{ caller_context::CallerContext, function_selector::FunctionSelector, - max_block_number::MaxBlockNumber, nullifier_key_validation_request::NullifierKeyValidationRequest, + max_block_number::MaxBlockNumber, key_validation_request::KeyValidationRequest, private_call_request::PrivateCallRequest, private_circuit_public_inputs::PrivateCircuitPublicInputs, public_call_stack_item::PublicCallStackItem, read_request::ReadRequest, note_hash::NoteHash, nullifier::Nullifier, log_hash::{LogHash, NoteLogHash} @@ -28,8 +29,8 @@ use dep::protocol_types::{ MAX_NEW_NOTE_HASHES_PER_CALL, MAX_NEW_L2_TO_L1_MSGS_PER_CALL, MAX_NEW_NULLIFIERS_PER_CALL, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, MAX_NULLIFIER_READ_REQUESTS_PER_CALL, - MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, MAX_ENCRYPTED_LOGS_PER_CALL, - MAX_UNENCRYPTED_LOGS_PER_CALL, MAX_NOTE_ENCRYPTED_LOGS_PER_CALL + MAX_KEY_VALIDATION_REQUESTS_PER_CALL, MAX_ENCRYPTED_LOGS_PER_CALL, MAX_UNENCRYPTED_LOGS_PER_CALL, + MAX_NOTE_ENCRYPTED_LOGS_PER_CALL }, contrakt::{storage_read::StorageRead, storage_update_request::StorageUpdateRequest}, grumpkin_private_key::GrumpkinPrivateKey, grumpkin_point::GrumpkinPoint, header::Header, @@ -53,7 +54,8 @@ struct PrivateContext { note_hash_read_requests: BoundedVec, nullifier_read_requests: BoundedVec, - nullifier_key_validation_requests: BoundedVec, + key_validation_requests: BoundedVec, + app_secret_keys_generators: BoundedVec, new_note_hashes: BoundedVec, new_nullifiers: BoundedVec, @@ -71,7 +73,10 @@ struct PrivateContext { encrypted_logs_hashes: BoundedVec, unencrypted_logs_hashes: BoundedVec, - last_nullifier_key_validation_request: Option, + // Contains the last key validation request for each key type. This is used to cache the last request and avoid + // fetching the same request multiple times. + // The index of the array corresponds to the key type (0 nullifier, 1 incoming, 2 outgoing, 3 tagging). + last_key_validation_requests: [Option; NUM_KEY_TYPES], } impl ContextInterface for PrivateContext { @@ -120,7 +125,8 @@ impl PrivateContext { max_block_number: MaxBlockNumber::empty(), note_hash_read_requests: BoundedVec::new(), nullifier_read_requests: BoundedVec::new(), - nullifier_key_validation_requests: BoundedVec::new(), + key_validation_requests: BoundedVec::new(), + app_secret_keys_generators: BoundedVec::new(), new_note_hashes: BoundedVec::new(), new_nullifiers: BoundedVec::new(), historical_header: inputs.historical_header, @@ -131,7 +137,7 @@ impl PrivateContext { note_encrypted_logs_hashes: BoundedVec::new(), encrypted_logs_hashes: BoundedVec::new(), unencrypted_logs_hashes: BoundedVec::new(), - last_nullifier_key_validation_request: Option::none() + last_key_validation_requests: [Option::none(); NUM_KEY_TYPES] } } @@ -162,7 +168,7 @@ impl PrivateContext { max_block_number: self.max_block_number, note_hash_read_requests: self.note_hash_read_requests.storage, nullifier_read_requests: self.nullifier_read_requests.storage, - nullifier_key_validation_requests: self.nullifier_key_validation_requests.storage, + key_validation_requests: self.key_validation_requests.storage, new_note_hashes: self.new_note_hashes.storage, new_nullifiers: self.new_nullifiers.storage, private_call_requests: self.private_call_requests.storage, @@ -204,21 +210,30 @@ impl PrivateContext { } pub fn request_nsk_app(&mut self, npk_m_hash: Field) -> Field { - let cached_request = self.last_nullifier_key_validation_request.unwrap_or(NullifierKeyValidationRequest::empty()); + self.request_sk_app(npk_m_hash, NULLIFIER_INDEX) + } + + pub fn request_ovsk_app(&mut self, ovpk_m_hash: Field) -> Field { + self.request_sk_app(ovpk_m_hash, OUTGOING_INDEX) + } + + fn request_sk_app(&mut self, pk_m_hash: Field, key_index: Field) -> Field { + let cached_request = self.last_key_validation_requests[key_index].unwrap_or(KeyValidationRequest::empty()); - if cached_request.master_nullifier_public_key.hash() == npk_m_hash { + if cached_request.pk_m.hash() == pk_m_hash { // We get a match so the cached request is the latest one - cached_request.app_nullifier_secret_key + cached_request.sk_app } else { // We didn't get a match meaning the cached result is stale. We fetch new values from oracle and instruct // protocol circuits to validate them by storing the validation request in context. - let request = get_nullifier_key_validation_request(npk_m_hash); - // We constrain that the npk_m_hash matches the one in the request (otherwise we could get an arbitrary - // valid key request and not the one corresponding to npk_m_hash). - assert(request.master_nullifier_public_key.hash() == npk_m_hash); - self.nullifier_key_validation_requests.push(request); - self.last_nullifier_key_validation_request = Option::some(request); - request.app_nullifier_secret_key + let request = get_key_validation_request(pk_m_hash, key_index); + // We constrain that the pk_m_hash matches the one in the request (otherwise we could get an arbitrary + // valid key request and not the one corresponding to pk_m_hash). + assert(request.pk_m.hash() == pk_m_hash); + self.key_validation_requests.push(request); + self.app_secret_keys_generators.push(sk_generators[key_index]); + self.last_key_validation_requests[key_index] = Option::some(request); + request.sk_app } } @@ -659,7 +674,8 @@ impl Empty for PrivateContext { max_block_number: MaxBlockNumber::empty(), note_hash_read_requests: BoundedVec::new(), nullifier_read_requests: BoundedVec::new(), - nullifier_key_validation_requests: BoundedVec::new(), + key_validation_requests: BoundedVec::new(), + app_secret_keys_generators: BoundedVec::new(), new_note_hashes: BoundedVec::new(), new_nullifiers: BoundedVec::new(), private_call_requests: BoundedVec::new(), @@ -670,7 +686,7 @@ impl Empty for PrivateContext { note_encrypted_logs_hashes: BoundedVec::new(), encrypted_logs_hashes: BoundedVec::new(), unencrypted_logs_hashes: BoundedVec::new(), - last_nullifier_key_validation_request: Option::none(), + last_key_validation_requests: [Option::none(); NUM_KEY_TYPES] } } } diff --git a/noir-projects/aztec-nr/aztec/src/keys.nr b/noir-projects/aztec-nr/aztec/src/keys.nr index f47c5eea375..2470a1bafeb 100644 --- a/noir-projects/aztec-nr/aztec/src/keys.nr +++ b/noir-projects/aztec-nr/aztec/src/keys.nr @@ -1,3 +1,4 @@ +mod constants; mod getters; mod point_to_symmetric_key; mod public_keys; diff --git a/noir-projects/aztec-nr/aztec/src/keys/constants.nr b/noir-projects/aztec-nr/aztec/src/keys/constants.nr new file mode 100644 index 00000000000..24444f576bf --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/keys/constants.nr @@ -0,0 +1,19 @@ +use dep::protocol_types::constants::{GENERATOR_INDEX__NSK_M, GENERATOR_INDEX__IVSK_M, GENERATOR_INDEX__OVSK_M, GENERATOR_INDEX__TSK_M}; + +// Note: In fetch_key_from_registry we expect that the shared mutable slot is index * 2 + 1 for the x coordinate and +// index * 2 + 2 for the y coordinate. For example, the npk_m x coordinates will be stored in a map at storage slot +// 0 * 2 + 1 = 1, and the npk_m y coordinates at 2 * 2 + 2 = 6. If this changes the function will need to be +// refactored. +global NULLIFIER_INDEX = 0; +global INCOMING_INDEX = 1; +global OUTGOING_INDEX = 2; +global TAGGING_INDEX = 3; + +global NUM_KEY_TYPES = 4; + +global sk_generators = [ + GENERATOR_INDEX__NSK_M, + GENERATOR_INDEX__IVSK_M, + GENERATOR_INDEX__OVSK_M, + GENERATOR_INDEX__TSK_M +]; diff --git a/noir-projects/aztec-nr/aztec/src/keys/getters.nr b/noir-projects/aztec-nr/aztec/src/keys/getters.nr index c0b2feffc12..d085c309d7c 100644 --- a/noir-projects/aztec-nr/aztec/src/keys/getters.nr +++ b/noir-projects/aztec-nr/aztec/src/keys/getters.nr @@ -1,10 +1,11 @@ use dep::protocol_types::{ - abis::nullifier_key_validation_request::NullifierKeyValidationRequest, address::AztecAddress, + abis::key_validation_request::KeyValidationRequest, address::AztecAddress, constants::CANONICAL_KEY_REGISTRY_ADDRESS, grumpkin_point::GrumpkinPoint }; use crate::{ - context::PrivateContext, oracle::{keys::get_public_keys_and_partial_address}, - keys::public_keys::{PublicKeys, NULLIFIER_INDEX, INCOMING_INDEX}, + context::PrivateContext, + oracle::{keys::get_public_keys_and_partial_address, key_validation_request::get_key_validation_request}, + keys::{public_keys::PublicKeys, constants::{NULLIFIER_INDEX, INCOMING_INDEX}}, state_vars::{ map::derive_storage_slot_in_map, shared_mutable::shared_mutable_private_getter::SharedMutablePrivateGetter @@ -89,3 +90,9 @@ fn fetch_and_constrain_keys(address: AztecAddress) -> PublicKeys { public_keys } + +// A helper function since requesting nsk_app is very common +// TODO(#6543) +pub fn get_nsk_app(npk_m_hash: Field) -> Field { + get_key_validation_request(npk_m_hash, NULLIFIER_INDEX).sk_app +} diff --git a/noir-projects/aztec-nr/aztec/src/keys/public_keys.nr b/noir-projects/aztec-nr/aztec/src/keys/public_keys.nr index ffdff5aa001..e6c82b833d0 100644 --- a/noir-projects/aztec-nr/aztec/src/keys/public_keys.nr +++ b/noir-projects/aztec-nr/aztec/src/keys/public_keys.nr @@ -2,15 +2,7 @@ use dep::protocol_types::{ address::PublicKeysHash, constants::GENERATOR_INDEX__PUBLIC_KEYS_HASH, hash::poseidon2_hash, grumpkin_point::GrumpkinPoint, traits::{Deserialize, Serialize} }; - -// Note: In fetch_key_from_registry we expect that the shared mutable slot is index * 2 + 1 for the x coordinate and -// index * 2 + 2 for the y coordinate. For example, the npk_m x coordinates will be stored in a map at storage slot -// 0 * 2 + 1 = 1, and the npk_m y coordinates at 2 * 2 + 2 = 6. If this changes the function will need to be -// refactored. -global NULLIFIER_INDEX = 0; -global INCOMING_INDEX = 1; -global OUTGOING_INDEX = 2; -global TAGGING_INDEX = 3; +use crate::keys::constants::{NUM_KEY_TYPES, NULLIFIER_INDEX, INCOMING_INDEX, OUTGOING_INDEX}; global PUBLIC_KEYS_LENGTH = 8; @@ -41,7 +33,7 @@ impl PublicKeys { } pub fn get_key_by_index(self, index: Field) -> GrumpkinPoint { - assert(index as u8 < 4, "Invalid key index"); + assert(index as u8 < NUM_KEY_TYPES, "Invalid key index"); if index == NULLIFIER_INDEX { self.npk_m } else if index == INCOMING_INDEX { diff --git a/noir-projects/aztec-nr/aztec/src/oracle.nr b/noir-projects/aztec-nr/aztec/src/oracle.nr index 2d3cc6dd1f9..a2b8a1198f6 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle.nr @@ -11,7 +11,7 @@ mod get_nullifier_membership_witness; mod get_public_data_witness; mod get_membership_witness; mod keys; -mod nullifier_keys; +mod key_validation_request; mod get_sibling_path; mod unsafe_rand; mod enqueue_public_function_call; diff --git a/noir-projects/aztec-nr/aztec/src/oracle/key_validation_request.nr b/noir-projects/aztec-nr/aztec/src/oracle/key_validation_request.nr new file mode 100644 index 00000000000..1df79eef12f --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/oracle/key_validation_request.nr @@ -0,0 +1,17 @@ +use dep::protocol_types::{ + grumpkin_point::GrumpkinPoint, + abis::key_validation_request::{KeyValidationRequest, KEY_VALIDATION_REQUEST_LENGTH} +}; + +#[oracle(getKeyValidationRequest)] +fn get_key_validation_request_oracle(_pk_m_hash: Field, _key_index: Field) -> [Field; KEY_VALIDATION_REQUEST_LENGTH] {} + +unconstrained fn get_key_validation_request_internal(npk_m_hash: Field, key_index: Field) -> KeyValidationRequest { + let result = get_key_validation_request_oracle(npk_m_hash, key_index); + KeyValidationRequest::deserialize(result) +} + +pub fn get_key_validation_request(pk_m_hash: Field, key_index: Field) -> KeyValidationRequest { + get_key_validation_request_internal(pk_m_hash, key_index) +} + diff --git a/noir-projects/aztec-nr/aztec/src/oracle/nullifier_keys.nr b/noir-projects/aztec-nr/aztec/src/oracle/nullifier_keys.nr deleted file mode 100644 index 0eb33d7cb15..00000000000 --- a/noir-projects/aztec-nr/aztec/src/oracle/nullifier_keys.nr +++ /dev/null @@ -1,21 +0,0 @@ -use dep::protocol_types::{grumpkin_point::GrumpkinPoint, abis::nullifier_key_validation_request::NullifierKeyValidationRequest}; - -#[oracle(getNullifierKeys)] -fn get_nullifier_key_validation_request_oracle(_npk_m_hash: Field) -> [Field; 3] {} - -unconstrained fn get_nullifier_key_validation_request_internal(npk_m_hash: Field) -> NullifierKeyValidationRequest { - let result = get_nullifier_key_validation_request_oracle(npk_m_hash); - NullifierKeyValidationRequest { - master_nullifier_public_key: GrumpkinPoint { x: result[0], y: result[1] }, - app_nullifier_secret_key: result[2] - } -} - -// We get the full struct Nullifier Keys here -pub fn get_nullifier_key_validation_request(npk_m_hash: Field) -> NullifierKeyValidationRequest { - get_nullifier_key_validation_request_internal(npk_m_hash) -} - -pub fn get_nsk_app(npk_m_hash: Field) -> Field { - get_nullifier_key_validation_request_internal(npk_m_hash).app_nullifier_secret_key -} diff --git a/noir-projects/aztec-nr/value-note/src/value_note.nr b/noir-projects/aztec-nr/value-note/src/value_note.nr index 7e057b8912e..bdc4fcc33b6 100644 --- a/noir-projects/aztec-nr/value-note/src/value_note.nr +++ b/noir-projects/aztec-nr/value-note/src/value_note.nr @@ -5,7 +5,7 @@ use dep::aztec::{ constants::GENERATOR_INDEX__NOTE_NULLIFIER, hash::poseidon2_hash }, note::{note_header::NoteHeader, note_interface::NoteInterface, utils::compute_note_hash_for_consumption}, - oracle::unsafe_rand::unsafe_rand, oracle::nullifier_keys::get_nsk_app, context::PrivateContext + oracle::unsafe_rand::unsafe_rand, keys::getters::get_nsk_app, context::PrivateContext }; global VALUE_NOTE_LEN: Field = 3; // 3 plus a header. diff --git a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr index 7de5917b11d..e1c40e40f45 100644 --- a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr +++ b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr @@ -2,7 +2,7 @@ use dep::aztec::prelude::{AztecAddress, PrivateContext, NoteHeader, NoteInterfac use dep::aztec::{ keys::getters::get_ivpk_m, protocol_types::{constants::GENERATOR_INDEX__NOTE_NULLIFIER, grumpkin_point::GrumpkinPoint, hash::poseidon2_hash}, - note::utils::compute_note_hash_for_consumption, oracle::nullifier_keys::get_nsk_app + note::utils::compute_note_hash_for_consumption, keys::getters::get_nsk_app }; global SUBSCRIPTION_NOTE_LEN: Field = 3; diff --git a/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr b/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr index 5efd1e93879..8664cbad9bb 100644 --- a/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr +++ b/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr @@ -1,7 +1,7 @@ use dep::aztec::prelude::{AztecAddress, NoteInterface, NoteHeader, PrivateContext}; use dep::aztec::{ keys::getters::get_ivpk_m, note::{utils::compute_note_hash_for_consumption}, - oracle::nullifier_keys::get_nsk_app, + keys::getters::get_nsk_app, protocol_types::{ traits::Empty, grumpkin_point::GrumpkinPoint, constants::GENERATOR_INDEX__NOTE_NULLIFIER, hash::poseidon2_hash diff --git a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr index f716dc5a5f8..30121822ee7 100644 --- a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr +++ b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr @@ -2,7 +2,7 @@ use dep::aztec::prelude::{AztecAddress, FunctionSelector, NoteHeader, NoteInterf use dep::aztec::{ keys::getters::get_ivpk_m, note::utils::compute_note_hash_for_consumption, - oracle::nullifier_keys::get_nsk_app, + keys::getters::get_nsk_app, protocol_types::{constants::GENERATOR_INDEX__NOTE_NULLIFIER, grumpkin_point::GrumpkinPoint, hash::poseidon2_hash} }; diff --git a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr index df6e72b61b3..a24b9a1fe8a 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr @@ -1,6 +1,6 @@ use dep::aztec::prelude::{AztecAddress, NoteHeader, NoteInterface, PrivateContext}; use dep::aztec::{ - note::utils::compute_note_hash_for_consumption, oracle::nullifier_keys::get_nsk_app, + note::utils::compute_note_hash_for_consumption, keys::getters::get_nsk_app, protocol_types::{constants::GENERATOR_INDEX__NOTE_NULLIFIER, grumpkin_point::GrumpkinPoint, hash::poseidon2_hash} }; 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 03c7f86ccb7..95467448503 100644 --- a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr @@ -50,6 +50,11 @@ contract Test { example_set: PrivateSet, } + #[aztec(private)] + fn get_ovsk_app(ovpk_m_hash: Field) -> Field { + context.request_ovsk_app(ovpk_m_hash) + } + #[aztec(private)] fn get_master_incoming_viewing_public_key(address: AztecAddress) -> [Field; 2] { let pub_key = get_ivpk_m(&mut context, address); diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr index 204023a21ab..ec62873e67b 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr @@ -2,7 +2,7 @@ use dep::aztec::{ prelude::{AztecAddress, NoteHeader, NoteInterface, PrivateContext}, protocol_types::{constants::GENERATOR_INDEX__NOTE_NULLIFIER, grumpkin_point::GrumpkinPoint, hash::poseidon2_hash}, note::utils::compute_note_hash_for_consumption, oracle::unsafe_rand::unsafe_rand, - oracle::nullifier_keys::get_nsk_app + keys::getters::get_nsk_app }; trait OwnedNote { diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr index 204023a21ab..ec62873e67b 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr @@ -2,7 +2,7 @@ use dep::aztec::{ prelude::{AztecAddress, NoteHeader, NoteInterface, PrivateContext}, protocol_types::{constants::GENERATOR_INDEX__NOTE_NULLIFIER, grumpkin_point::GrumpkinPoint, hash::poseidon2_hash}, note::utils::compute_note_hash_for_consumption, oracle::unsafe_rand::unsafe_rand, - oracle::nullifier_keys::get_nsk_app + keys::getters::get_nsk_app }; trait OwnedNote { diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/kernel_circuit_public_inputs_composer.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/kernel_circuit_public_inputs_composer.nr index da0ce3470db..16f812eb38b 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/kernel_circuit_public_inputs_composer.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/kernel_circuit_public_inputs_composer.nr @@ -257,7 +257,7 @@ impl KernelCircuitPublicInputsComposer { ); assert_eq( - array_length(self.previous_kernel.public_inputs.validation_requests.nullifier_key_validation_requests), 0, "Non empty nullifier key validation requests" + array_length(self.previous_kernel.public_inputs.validation_requests.key_validation_requests), 0, "Non empty key validation requests" ); } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_circuit_public_inputs_composer.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_circuit_public_inputs_composer.nr index e0dfcb2e78f..878dedef4da 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_circuit_public_inputs_composer.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_circuit_public_inputs_composer.nr @@ -62,7 +62,7 @@ impl PrivateKernelCircuitPublicInputsComposer { public_inputs.validation_requests.max_block_number = start.for_rollup.max_block_number; public_inputs.validation_requests.note_hash_read_requests = array_to_bounded_vec(start.note_hash_read_requests); public_inputs.validation_requests.nullifier_read_requests = array_to_bounded_vec(start.nullifier_read_requests); - public_inputs.validation_requests.nullifier_key_validation_requests = array_to_bounded_vec(start.nullifier_key_validation_requests); + public_inputs.validation_requests.key_validation_requests = array_to_bounded_vec(start.key_validation_requests); let start = previous_kernel_public_inputs.end; public_inputs.end.new_note_hashes = array_to_bounded_vec(start.new_note_hashes); @@ -101,7 +101,7 @@ impl PrivateKernelCircuitPublicInputsComposer { self.propagate_max_block_number(source); self.propagate_note_hash_read_requests(source); self.propagate_nullifier_read_requests(source); - self.propagate_nullifier_key_validation_requests(source); + self.propagate_key_validation_requests(source); self.propagate_note_hashes(source); self.propagate_nullifiers(source); self.propagate_l2_to_l1_messages(source); @@ -143,12 +143,12 @@ impl PrivateKernelCircuitPublicInputsComposer { } } - fn propagate_nullifier_key_validation_requests(&mut self, source: DataSource) { - let nullifier_key_validation_requests = source.private_call_public_inputs.nullifier_key_validation_requests; - for i in 0..nullifier_key_validation_requests.len() { - let request = nullifier_key_validation_requests[i]; + fn propagate_key_validation_requests(&mut self, source: DataSource) { + let key_validation_requests = source.private_call_public_inputs.key_validation_requests; + for i in 0..key_validation_requests.len() { + let request = key_validation_requests[i]; if !is_empty(request) { - self.public_inputs.validation_requests.nullifier_key_validation_requests.push(request.scope(source.storage_contract_address)); + self.public_inputs.validation_requests.key_validation_requests.push(request.scope(source.storage_contract_address)); } } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr index 61ed5046436..e0dde7101af 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr @@ -50,7 +50,7 @@ mod tests { use dep::types::{ abis::{ kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, note_hash::NoteHash, - nullifier_key_validation_request::NullifierKeyValidationRequest, + key_validation_request::KeyValidationRequest, private_kernel::private_call_data::PrivateCallData, read_request::ReadRequest, nullifier::Nullifier, log_hash::{LogHash, NoteLogHash} }, @@ -197,17 +197,17 @@ mod tests { } #[test] - fn propagate_nullifier_key_validation_requests() { + fn propagate_key_validation_requests() { let mut builder = PrivateKernelInitInputsBuilder::new(); - let request_0 = NullifierKeyValidationRequest { master_nullifier_public_key: GrumpkinPoint { x: 1, y: 2 }, app_nullifier_secret_key: 3 }; - builder.private_call.public_inputs.nullifier_key_validation_requests.push(request_0); + let request_0 = KeyValidationRequest { pk_m: GrumpkinPoint { x: 1, y: 2 }, sk_app: 3 }; + builder.private_call.public_inputs.key_validation_requests.push(request_0); let public_inputs = builder.execute(); - assert_eq(array_length(public_inputs.validation_requests.nullifier_key_validation_requests), 1); + assert_eq(array_length(public_inputs.validation_requests.key_validation_requests), 1); - let request = public_inputs.validation_requests.nullifier_key_validation_requests[0]; + let request = public_inputs.validation_requests.key_validation_requests[0]; assert_eq(request.request, request_0); assert_eq( request.contract_address, builder.private_call.public_inputs.call_context.storage_contract_address diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr index d43ae5e907d..5aa62789ee5 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr @@ -1,7 +1,7 @@ use crate::private_kernel_circuit_public_inputs_composer::PrivateKernelCircuitPublicInputsComposer; use dep::reset_kernel_lib::{ NoteHashReadRequestHints, NullifierReadRequestHints, PrivateValidationRequestProcessor, - verify_squashed_transient_note_hashes_and_nullifiers, reset::nullifier_key::NullifierKeyHint + verify_squashed_transient_note_hashes_and_nullifiers, reset::key_validation_hint::KeyValidationHint }; use dep::types::{ abis::{ @@ -11,8 +11,8 @@ use dep::types::{ }, constants::{ MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, - MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX, - MAX_UNENCRYPTED_LOGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX + MAX_KEY_VALIDATION_REQUESTS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX, + MAX_NOTE_ENCRYPTED_LOGS_PER_TX }, grumpkin_private_key::GrumpkinPrivateKey, utils::arrays::array_length, traits::is_empty, PrivateKernelCircuitPublicInputs @@ -25,22 +25,22 @@ struct PrivateKernelResetOutputs { note_encrypted_log_hashes: [NoteLogHash; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], } -struct PrivateKernelResetHints { +struct PrivateKernelResetHints { transient_nullifier_indexes_for_note_hashes: [u64; MAX_NEW_NOTE_HASHES_PER_TX], transient_note_hash_indexes_for_nullifiers: [u64; MAX_NEW_NULLIFIERS_PER_TX], transient_note_hash_indexes_for_logs: [u64; MAX_NOTE_ENCRYPTED_LOGS_PER_TX], note_hash_read_request_hints: NoteHashReadRequestHints, nullifier_read_request_hints: NullifierReadRequestHints, - master_nullifier_secret_keys: [NullifierKeyHint; NLL_KEYS], + key_validation_hints: [KeyValidationHint; KEY_VALIDATION_REQUESTS], } -struct PrivateKernelResetCircuitPrivateInputs { +struct PrivateKernelResetCircuitPrivateInputs { previous_kernel: PrivateKernelData, outputs: PrivateKernelResetOutputs, - hints: PrivateKernelResetHints, + hints: PrivateKernelResetHints, } -impl PrivateKernelResetCircuitPrivateInputs { +impl PrivateKernelResetCircuitPrivateInputs { pub fn execute(self) -> PrivateKernelCircuitPublicInputs { let mut previous_public_inputs = self.previous_kernel.public_inputs; @@ -55,7 +55,7 @@ impl Pri nullifier_read_request_hints: self.hints.nullifier_read_request_hints, pending_nullifiers: previous_public_inputs.end.new_nullifiers, nullifier_tree_root: previous_public_inputs.constants.historical_header.state.partial.nullifier_tree.root, - master_nullifier_secret_keys: self.hints.master_nullifier_secret_keys + key_validation_hints: self.hints.key_validation_hints, }.validate(); verify_squashed_transient_note_hashes_and_nullifiers( @@ -87,11 +87,11 @@ mod tests { squash_transient_data::{squash_transient_note_hashes, squash_transient_nullifiers, squash_transient_logs} }, reset::read_request::{PendingReadHint, ReadRequestState, ReadRequestStatus}, - reset::nullifier_key::NullifierKeyHint + reset::key_validation_hint::KeyValidationHint }; use dep::types::constants::{ MAX_NOTE_HASH_READ_REQUESTS_PER_TX, MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, - MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, + MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_KEY_VALIDATION_REQUESTS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX, DA_BYTES_PER_FIELD }; @@ -179,7 +179,7 @@ mod tests { transient_note_hash_indexes_for_logs: self.transient_note_hash_indexes_for_logs, note_hash_read_request_hints: self.note_hash_read_request_hints_builder.to_hints(), nullifier_read_request_hints: self.nullifier_read_request_hints_builder.to_hints(), - master_nullifier_secret_keys: [NullifierKeyHint::empty(); MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX] + key_validation_hints: [KeyValidationHint::empty(); MAX_KEY_VALIDATION_REQUESTS_PER_TX] }; let kernel = PrivateKernelResetCircuitPrivateInputs { previous_kernel: self.previous_kernel.to_private_kernel_data(), outputs, hints }; @@ -300,15 +300,15 @@ mod tests { let remaining_nullifier_rr_index = builder.previous_kernel.add_read_request_for_pending_nullifier(1); let nullifier_rr = builder.previous_kernel.nullifier_read_requests.storage[remaining_nullifier_rr_index]; - let nk_validation_index = builder.previous_kernel.add_request_for_nullifier_key_validation(GrumpkinPoint::new(1, 2), 27); - let nk_validation = builder.previous_kernel.nullifier_key_validation_requests.storage[nk_validation_index]; + let key_validation_index = builder.previous_kernel.add_request_for_key_validation(GrumpkinPoint::new(1, 2), 27); + let key_validation = builder.previous_kernel.key_validation_requests.storage[key_validation_index]; // Check that they have been propagated to the next kernel let result = builder.execute(); assert_eq(result.validation_requests.note_hash_read_requests[0], note_hash_rr); assert_eq(result.validation_requests.nullifier_read_requests[0], nullifier_rr); - assert_eq(result.validation_requests.nullifier_key_validation_requests[0], nk_validation); + assert_eq(result.validation_requests.key_validation_requests[0], key_validation); } #[test] diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail.nr index b35cb1e6d86..ffd088d8464 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail.nr @@ -7,8 +7,8 @@ use dep::types::{ }, constants::{ MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, - MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX, - MAX_UNENCRYPTED_LOGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX + MAX_KEY_VALIDATION_REQUESTS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX, + MAX_NOTE_ENCRYPTED_LOGS_PER_TX }, grumpkin_private_key::GrumpkinPrivateKey, utils::arrays::array_length, traits::is_empty }; @@ -72,7 +72,7 @@ mod tests { }; use dep::types::constants::{ MAX_NOTE_HASH_READ_REQUESTS_PER_TX, MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, - MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, + MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_KEY_VALIDATION_REQUESTS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX, DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE }; @@ -331,10 +331,10 @@ mod tests { builder.failed(); } - #[test(should_fail_with="Non empty nullifier key validation requests")] - unconstrained fn non_empty_nullifier_key_validations() { + #[test(should_fail_with="Non empty key validation requests")] + unconstrained fn non_empty_key_validations() { let mut builder = PrivateKernelTailInputsBuilder::new(); - let _void = builder.previous_kernel.add_request_for_nullifier_key_validation(GrumpkinPoint::new(1, 2), 27); + let _void = builder.previous_kernel.add_request_for_key_validation(GrumpkinPoint::new(1, 2), 27); builder.failed(); } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail_to_public.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail_to_public.nr index 8e8993d4bb2..cc688c36dec 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail_to_public.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail_to_public.nr @@ -7,8 +7,8 @@ use dep::types::{ }, constants::{ MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, - MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX, - MAX_UNENCRYPTED_LOGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX + MAX_KEY_VALIDATION_REQUESTS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX, + MAX_NOTE_ENCRYPTED_LOGS_PER_TX }, grumpkin_private_key::GrumpkinPrivateKey, utils::arrays::array_length, traits::is_empty }; @@ -71,8 +71,8 @@ mod tests { }; use dep::types::constants::{ MAX_NOTE_HASH_READ_REQUESTS_PER_TX, MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, - MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, - DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE, MAX_NOTE_ENCRYPTED_LOGS_PER_TX + MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_KEY_VALIDATION_REQUESTS_PER_TX, DA_BYTES_PER_FIELD, + DA_GAS_PER_BYTE, MAX_NOTE_ENCRYPTED_LOGS_PER_TX }; use dep::types::{ abis::{ @@ -374,10 +374,10 @@ mod tests { builder.failed(); } - #[test(should_fail_with="Non empty nullifier key validation requests")] - unconstrained fn non_empty_nullifier_key_validations() { + #[test(should_fail_with="Non empty key validation requests")] + unconstrained fn non_empty_key_validations() { let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); - let _void = builder.previous_kernel.add_request_for_nullifier_key_validation(GrumpkinPoint::new(1, 2), 27); + let _void = builder.previous_kernel.add_request_for_key_validation(GrumpkinPoint::new(1, 2), 27); builder.failed(); } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/validate_arrays.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/validate_arrays.nr index dbc5f629877..0d5cd45d2c7 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/validate_arrays.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/validate_arrays.nr @@ -1,8 +1,8 @@ use crate::tests::private_call_data_validator_builder::PrivateCallDataValidatorBuilder; use dep::types::{ abis::{ - caller_context::CallerContext, note_hash::NoteHash, nullifier::Nullifier, - private_call_request::PrivateCallRequest, read_request::ReadRequest, + key_validation_request::KeyValidationRequest, caller_context::CallerContext, note_hash::NoteHash, + nullifier::Nullifier, private_call_request::PrivateCallRequest, read_request::ReadRequest, log_hash::{LogHash, NoteLogHash} }, address::EthAddress, grumpkin_point::GrumpkinPoint, messaging::l2_to_l1_message::L2ToL1Message @@ -36,20 +36,19 @@ fn validate_arrays_malformed_nullifier_read_requests_fails() { builder.validate(); } -// Enable this test if MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL is greater than 1. -// #[test(should_fail_with="invalid array")] -// fn validate_arrays_malformed_nullifier_key_validation_requests_fails() { -// let mut builder = PrivateCallDataValidatorBuilder::new(); +#[test(should_fail_with="invalid array")] +fn validate_arrays_malformed_key_validation_requests_fails() { + let mut builder = PrivateCallDataValidatorBuilder::new(); -// builder.private_call.public_inputs.nullifier_key_validation_requests.extend_from_array( -// [ -// NullifierKeyValidationRequest::empty(), -// NullifierKeyValidationRequest { master_nullifier_public_key: GrumpkinPoint { x: 12, y: 34 }, app_nullifier_secret_key: 5 } -// ] -// ); + builder.private_call.public_inputs.key_validation_requests.extend_from_array( + [ + KeyValidationRequest::empty(), + KeyValidationRequest { pk_m: GrumpkinPoint { x: 12, y: 34 }, sk_app: 5 } + ] + ); -// builder.validate(); -// } + builder.validate(); +} #[test(should_fail_with="invalid array")] fn validate_arrays_malformed_note_hashes_fails() { diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-reset-simulated/src/main.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-reset-simulated/src/main.nr index 2d4947cfcef..507705d905f 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-reset-simulated/src/main.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-reset-simulated/src/main.nr @@ -3,7 +3,7 @@ use dep::types::{ PrivateKernelCircuitPublicInputs, constants::{ MAX_NOTE_HASH_READ_REQUESTS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX, - MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX + MAX_KEY_VALIDATION_REQUESTS_PER_TX } }; @@ -11,7 +11,7 @@ global NOTE_HASH_PENDING_AMOUNT = MAX_NOTE_HASH_READ_REQUESTS_PER_TX; // 128 global NOTE_HASH_SETTLED_AMOUNT = MAX_NOTE_HASH_READ_REQUESTS_PER_TX; global NULLIFIER_PENDING_AMOUNT = MAX_NULLIFIER_READ_REQUESTS_PER_TX; // 8 global NULLIFIER_SETTLED_AMOUNT = MAX_NULLIFIER_READ_REQUESTS_PER_TX; -global NULLIFIER_KEYS = MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX; // 4 +global NULLIFIER_KEYS = MAX_KEY_VALIDATION_REQUESTS_PER_TX; // 4 unconstrained fn main(input: PrivateKernelResetCircuitPrivateInputs) -> pub PrivateKernelCircuitPublicInputs { input.execute() diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-reset/src/main.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-reset/src/main.nr index d39fcc38ecf..a8757a84662 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-reset/src/main.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-reset/src/main.nr @@ -3,7 +3,7 @@ use dep::types::{ PrivateKernelCircuitPublicInputs, constants::{ MAX_NOTE_HASH_READ_REQUESTS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX, - MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX + MAX_KEY_VALIDATION_REQUESTS_PER_TX } }; @@ -11,7 +11,7 @@ global NOTE_HASH_PENDING_AMOUNT = MAX_NOTE_HASH_READ_REQUESTS_PER_TX; // 128 global NOTE_HASH_SETTLED_AMOUNT = MAX_NOTE_HASH_READ_REQUESTS_PER_TX; global NULLIFIER_PENDING_AMOUNT = MAX_NULLIFIER_READ_REQUESTS_PER_TX; // 8 global NULLIFIER_SETTLED_AMOUNT = MAX_NULLIFIER_READ_REQUESTS_PER_TX; -global NULLIFIER_KEYS = MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX; // 4 +global NULLIFIER_KEYS = MAX_KEY_VALIDATION_REQUESTS_PER_TX; // 4 #[recursive] fn main(input: PrivateKernelResetCircuitPrivateInputs) -> pub PrivateKernelCircuitPublicInputs { diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/private_validation_request_processor.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/private_validation_request_processor.nr index 877112c0dd1..dbafe5b01ba 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/private_validation_request_processor.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/private_validation_request_processor.nr @@ -1,23 +1,22 @@ use crate::{ note_hash_read_request_reset::NoteHashReadRequestHints, nullifier_read_request_reset::NullifierReadRequestHints, reset::read_request::reset_read_requests, - reset::nullifier_key::{NullifierKeyHint, reset_nullifier_keys} + reset::key_validation_hint::{KeyValidationHint, reset_key_validation_requests} }; use dep::types::{ abis::{ note_hash::ScopedNoteHash, nullifier::ScopedNullifier, validation_requests::ValidationRequests, - read_request::ScopedReadRequest, - nullifier_key_validation_request::ScopedNullifierKeyValidationRequest + read_request::ScopedReadRequest, key_validation_request::ScopedKeyValidationRequest }, constants::{ - MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, + MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_KEY_VALIDATION_REQUESTS_PER_TX, GENERATOR_INDEX__NSK_M, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX }, grumpkin_private_key::GrumpkinPrivateKey, hash::poseidon2_hash, traits::is_empty, utils::arrays::filter_array_to_bounded_vec }; -struct PrivateValidationRequestProcessor { +struct PrivateValidationRequestProcessor { validation_requests: ValidationRequests, note_hash_read_request_hints: NoteHashReadRequestHints, pending_note_hashes: [ScopedNoteHash; MAX_NEW_NOTE_HASHES_PER_TX], @@ -25,14 +24,14 @@ struct PrivateValidationRequestProcessor, pending_nullifiers: [ScopedNullifier; MAX_NEW_NULLIFIERS_PER_TX], nullifier_tree_root: Field, - master_nullifier_secret_keys: [NullifierKeyHint; NLL_KEYS], + key_validation_hints: [KeyValidationHint; KEY_VALIDATION_REQUESTS], } -impl PrivateValidationRequestProcessor { +impl PrivateValidationRequestProcessor { pub fn validate(self) -> ValidationRequests { let remaining_note_hash_read_requests = self.validate_note_hash_read_requests(); let remaining_nullifier_read_requests = self.validate_nullifier_read_requests(); - let remaining_nullifier_key_validation_requests = self.validate_nullifier_keys(); + let remaining_key_validation_requests = self.validate_keys(); ValidationRequests { for_rollup: self.validation_requests.for_rollup, @@ -40,7 +39,7 @@ impl Pri public_data_reads: self.validation_requests.public_data_reads, note_hash_read_requests: remaining_note_hash_read_requests.storage, nullifier_read_requests: remaining_nullifier_read_requests.storage, - nullifier_key_validation_requests: remaining_nullifier_key_validation_requests.storage + key_validation_requests: remaining_key_validation_requests.storage } } @@ -66,10 +65,10 @@ impl Pri ) } - fn validate_nullifier_keys(self) -> BoundedVec { - reset_nullifier_keys( - self.validation_requests.nullifier_key_validation_requests, - self.master_nullifier_secret_keys + fn validate_keys(self) -> BoundedVec { + reset_key_validation_requests( + self.validation_requests.key_validation_requests, + self.key_validation_hints ) } } diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset.nr index 740cbc359fb..283e01ab190 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset.nr @@ -2,4 +2,4 @@ mod mutable_data_read_request; mod non_existent_read_request; mod read_request; mod transient_data; -mod nullifier_key; +mod key_validation_hint; diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/key_validation_hint.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/key_validation_hint.nr new file mode 100644 index 00000000000..d902e36ee1b --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/key_validation_hint.nr @@ -0,0 +1,62 @@ +use dep::types::{ + traits::{Empty, is_empty}, abis::{key_validation_request::ScopedKeyValidationRequest}, + constants::MAX_KEY_VALIDATION_REQUESTS_PER_TX, grumpkin_private_key::GrumpkinPrivateKey, + hash::poseidon2_hash, utils::arrays::filter_array_to_bounded_vec +}; + +struct KeyValidationHint { + sk_m: GrumpkinPrivateKey, + sk_app_generator_index: Field, + request_index: u64, +} + +impl Empty for KeyValidationHint { + fn empty() -> Self { + KeyValidationHint { + sk_m: GrumpkinPrivateKey::empty(), + sk_app_generator_index: 0, + request_index: 0, + } + } +} + +impl Eq for KeyValidationHint { + fn eq(self, other: Self) -> bool { + self.sk_m.eq(other.sk_m) & self.request_index.eq(other.request_index) + } + +} + +pub fn reset_key_validation_requests( + key_validation_requests: [ScopedKeyValidationRequest; MAX_KEY_VALIDATION_REQUESTS_PER_TX], + key_validation_hints: [KeyValidationHint; N] +) -> BoundedVec { + let mut should_propagate = key_validation_requests.map(|req| !is_empty(req)); + for i in 0..N { + let hint = key_validation_hints[i]; + if !is_empty(hint) { + let scoped_request = key_validation_requests[hint.request_index]; + let contract_address = scoped_request.contract_address; + let request = scoped_request.request; + let sk_m = hint.sk_m; + let sk_app_generator_index = hint.sk_app_generator_index; + + // First we check that derived public key matches master public key from request + let pk_m = sk_m.derive_public_key(); + assert( + pk_m.eq(request.pk_m), "Failed to derive matching master public key from the secret key." + ); + + // Then we check that siloing the master secret key with the contract address gives the app secret key + + let sk_app = poseidon2_hash([sk_m.high, sk_m.low, contract_address.to_field(), sk_app_generator_index]); + assert( + sk_app.eq(request.sk_app), "Failed to derive matching app secret key from the secret key." + ); + + should_propagate[hint.request_index] = false; + } + } + + filter_array_to_bounded_vec(key_validation_requests, should_propagate) +} diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/nullifier_key.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/nullifier_key.nr deleted file mode 100644 index 91207ddb717..00000000000 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/nullifier_key.nr +++ /dev/null @@ -1,66 +0,0 @@ -use crate::{nullifier_read_request_reset::NullifierReadRequestHints}; -use dep::types::{ - traits::{Empty, is_empty}, - abis::{nullifier_key_validation_request::ScopedNullifierKeyValidationRequest}, - constants::{MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, GENERATOR_INDEX__NSK_M}, - grumpkin_private_key::GrumpkinPrivateKey, hash::poseidon2_hash, - utils::arrays::filter_array_to_bounded_vec -}; - -struct NullifierKeyHint { - private_key: GrumpkinPrivateKey, - request_index: u64, -} - -impl Empty for NullifierKeyHint { - fn empty() -> Self { - NullifierKeyHint { - private_key: GrumpkinPrivateKey::empty(), - request_index: 0, - } - } -} - -impl Eq for NullifierKeyHint { - fn eq(self, other: Self) -> bool { - self.private_key.eq(other.private_key) & self.request_index.eq(other.request_index) - } - -} - -pub fn reset_nullifier_keys( - nullifier_key_validation_requests: [ScopedNullifierKeyValidationRequest; MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX], - key_hints: [NullifierKeyHint; N] -) -> BoundedVec { - let mut should_propagate = nullifier_key_validation_requests.map(|req| !is_empty(req)); - for i in 0..N { - let hint = key_hints[i]; - if !is_empty(hint) { - let scoped_request = nullifier_key_validation_requests[hint.request_index]; - let contract_address = scoped_request.contract_address; - let request = scoped_request.request; - let master_nullifier_secret_key = hint.private_key; - - // First we check that derived public key matches master nullifier public key from request - let master_nullifier_public_key = master_nullifier_secret_key.derive_public_key(); - assert( - master_nullifier_public_key.eq(request.master_nullifier_public_key), "Failed to derive matching master nullifier public key from the secret key." - ); - - // Then we check that siloing the master secret key with the contract address gives the app nullifier secret key - - let app_nullifier_secret_key = poseidon2_hash( - [ - master_nullifier_secret_key.high, master_nullifier_secret_key.low, contract_address.to_field(), GENERATOR_INDEX__NSK_M - ] - ); - assert( - app_nullifier_secret_key.eq(request.app_nullifier_secret_key), "Failed to derive matching app nullifier secret key from the secret key." - ); - - should_propagate[hint.request_index] = false; - } - } - - filter_array_to_bounded_vec(nullifier_key_validation_requests, should_propagate) -} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis.nr index dca4e80ae20..3b0e0938572 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis.nr @@ -16,7 +16,7 @@ mod read_request; mod log_hash; mod note_hash; mod nullifier; -mod nullifier_key_validation_request; +mod key_validation_request; mod public_data_read; mod public_data_update_request; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/key_validation_request.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/key_validation_request.nr new file mode 100644 index 00000000000..4bf6ade35d2 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/key_validation_request.nr @@ -0,0 +1,101 @@ +use dep::std::cmp::Eq; +use crate::{ + address::AztecAddress, + constants::{SCOPED_KEY_VALIDATION_REQUEST_LENGTH, KEY_VALIDATION_REQUEST_LENGTH}, + traits::{Empty, Serialize, Deserialize}, grumpkin_point::GrumpkinPoint, + utils::{arrays::array_concat, reader::Reader} +}; + +struct KeyValidationRequest { + pk_m: GrumpkinPoint, + sk_app: Field, // not a grumpkin scalar because it's output of poseidon2 +} + +impl Eq for KeyValidationRequest { + fn eq(self, request: KeyValidationRequest) -> bool { + (request.pk_m.eq(self.pk_m)) + & (request.sk_app.eq(self.sk_app)) + } +} + +impl Empty for KeyValidationRequest { + fn empty() -> Self { + KeyValidationRequest { + pk_m: GrumpkinPoint::zero(), + sk_app: 0, + } + } +} + +impl Serialize for KeyValidationRequest { + fn serialize(self) -> [Field; KEY_VALIDATION_REQUEST_LENGTH] { + [ + self.pk_m.x, + self.pk_m.y, + self.sk_app, + ] + } +} + +impl Deserialize for KeyValidationRequest { + fn deserialize(fields: [Field; KEY_VALIDATION_REQUEST_LENGTH]) -> Self { + Self { + pk_m: GrumpkinPoint::new(fields[0], fields[1]), + sk_app: fields[2], + } + } +} + +impl KeyValidationRequest { + pub fn scope(self, contract_address: AztecAddress) -> ScopedKeyValidationRequest { + ScopedKeyValidationRequest { request: self, contract_address } + } +} + +struct ScopedKeyValidationRequest { + request: KeyValidationRequest, + contract_address: AztecAddress, +} + +impl Eq for ScopedKeyValidationRequest { + fn eq(self, other: ScopedKeyValidationRequest) -> bool { + (self.request.eq(other.request)) + & (self.contract_address.eq(other.contract_address)) + } +} + +impl Empty for ScopedKeyValidationRequest { + fn empty() -> Self { + ScopedKeyValidationRequest { + request: KeyValidationRequest::empty(), + contract_address: AztecAddress::zero(), + } + } +} + +impl Serialize for ScopedKeyValidationRequest { + fn serialize(self) -> [Field; SCOPED_KEY_VALIDATION_REQUEST_LENGTH] { + array_concat(self.request.serialize(), [self.contract_address.to_field()]) + } +} + +impl Deserialize for ScopedKeyValidationRequest { + fn deserialize(fields: [Field; SCOPED_KEY_VALIDATION_REQUEST_LENGTH]) -> Self { + let mut reader = Reader::new(fields); + let res = Self { + request: reader.read_struct(KeyValidationRequest::deserialize), + contract_address: reader.read_struct(AztecAddress::deserialize), + }; + reader.finish(); + res + } +} + +#[test] +fn serialization_of_empty() { + let item = ScopedKeyValidationRequest::empty(); + let serialized = item.serialize(); + let deserialized = ScopedKeyValidationRequest::deserialize(serialized); + assert(item.eq(deserialized)); +} + diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/nullifier_key_validation_request.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/nullifier_key_validation_request.nr deleted file mode 100644 index 0424f6a3d7e..00000000000 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/nullifier_key_validation_request.nr +++ /dev/null @@ -1,101 +0,0 @@ -use dep::std::cmp::Eq; -use crate::{ - address::AztecAddress, - constants::{SCOPED_NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH, NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH}, - traits::{Empty, Serialize, Deserialize}, grumpkin_point::GrumpkinPoint, - utils::{arrays::array_concat, reader::Reader} -}; - -struct NullifierKeyValidationRequest { - master_nullifier_public_key: GrumpkinPoint, - app_nullifier_secret_key: Field, // not a grumpkin scalar because it's output of poseidon2 -} - -impl Eq for NullifierKeyValidationRequest { - fn eq(self, request: NullifierKeyValidationRequest) -> bool { - (request.master_nullifier_public_key.eq(self.master_nullifier_public_key)) - & (request.app_nullifier_secret_key.eq(self.app_nullifier_secret_key)) - } -} - -impl Empty for NullifierKeyValidationRequest { - fn empty() -> Self { - NullifierKeyValidationRequest { - master_nullifier_public_key: GrumpkinPoint::zero(), - app_nullifier_secret_key: 0, - } - } -} - -impl Serialize for NullifierKeyValidationRequest { - fn serialize(self) -> [Field; NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH] { - [ - self.master_nullifier_public_key.x, - self.master_nullifier_public_key.y, - self.app_nullifier_secret_key, - ] - } -} - -impl Deserialize for NullifierKeyValidationRequest { - fn deserialize(fields: [Field; NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH]) -> Self { - Self { - master_nullifier_public_key: GrumpkinPoint::new(fields[0], fields[1]), - app_nullifier_secret_key: fields[2], - } - } -} - -impl NullifierKeyValidationRequest { - pub fn scope(self, contract_address: AztecAddress) -> ScopedNullifierKeyValidationRequest { - ScopedNullifierKeyValidationRequest { request: self, contract_address } - } -} - -struct ScopedNullifierKeyValidationRequest { - request: NullifierKeyValidationRequest, - contract_address: AztecAddress, -} - -impl Eq for ScopedNullifierKeyValidationRequest { - fn eq(self, other: ScopedNullifierKeyValidationRequest) -> bool { - (self.request.eq(other.request)) - & (self.contract_address.eq(other.contract_address)) - } -} - -impl Empty for ScopedNullifierKeyValidationRequest { - fn empty() -> Self { - ScopedNullifierKeyValidationRequest { - request: NullifierKeyValidationRequest::empty(), - contract_address: AztecAddress::zero(), - } - } -} - -impl Serialize for ScopedNullifierKeyValidationRequest { - fn serialize(self) -> [Field; SCOPED_NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH] { - array_concat(self.request.serialize(), [self.contract_address.to_field()]) - } -} - -impl Deserialize for ScopedNullifierKeyValidationRequest { - fn deserialize(fields: [Field; SCOPED_NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH]) -> Self { - let mut reader = Reader::new(fields); - let res = Self { - request: reader.read_struct(NullifierKeyValidationRequest::deserialize), - contract_address: reader.read_struct(AztecAddress::deserialize), - }; - reader.finish(); - res - } -} - -#[test] -fn serialization_of_empty() { - let item = ScopedNullifierKeyValidationRequest::empty(); - let serialized = item.serialize(); - let deserialized = ScopedNullifierKeyValidationRequest::deserialize(serialized); - assert(item.eq(deserialized)); -} - diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_circuit_public_inputs.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_circuit_public_inputs.nr index 4f26052d44a..f9819b856b9 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_circuit_public_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_circuit_public_inputs.nr @@ -1,17 +1,17 @@ use crate::{ abis::{ call_context::CallContext, max_block_number::MaxBlockNumber, gas_settings::GasSettings, - nullifier_key_validation_request::NullifierKeyValidationRequest, note_hash::NoteHash, - nullifier::Nullifier, private_call_request::PrivateCallRequest, read_request::ReadRequest, + key_validation_request::KeyValidationRequest, note_hash::NoteHash, nullifier::Nullifier, + private_call_request::PrivateCallRequest, read_request::ReadRequest, log_hash::{LogHash, NoteLogHash} }, constants::{ MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, MAX_NULLIFIER_READ_REQUESTS_PER_CALL, - MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, MAX_NEW_NOTE_HASHES_PER_CALL, - MAX_NEW_NULLIFIERS_PER_CALL, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, - MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_NEW_L2_TO_L1_MSGS_PER_CALL, - PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH, GENERATOR_INDEX__PRIVATE_CIRCUIT_PUBLIC_INPUTS, - MAX_ENCRYPTED_LOGS_PER_CALL, MAX_UNENCRYPTED_LOGS_PER_CALL, MAX_NOTE_ENCRYPTED_LOGS_PER_CALL + MAX_KEY_VALIDATION_REQUESTS_PER_CALL, MAX_NEW_NOTE_HASHES_PER_CALL, MAX_NEW_NULLIFIERS_PER_CALL, + MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, + MAX_NEW_L2_TO_L1_MSGS_PER_CALL, PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH, + GENERATOR_INDEX__PRIVATE_CIRCUIT_PUBLIC_INPUTS, MAX_ENCRYPTED_LOGS_PER_CALL, + MAX_UNENCRYPTED_LOGS_PER_CALL, MAX_NOTE_ENCRYPTED_LOGS_PER_CALL }, header::Header, hash::pedersen_hash, messaging::l2_to_l1_message::L2ToL1Message, traits::{Deserialize, Hash, Serialize, Empty}, utils::reader::Reader, @@ -21,7 +21,7 @@ use crate::{ struct PrivateCircuitPublicInputsArrayLengths { note_hash_read_requests: u64, nullifier_read_requests: u64, - nullifier_key_validation_requests: u64, + key_validation_requests: u64, new_note_hashes: u64, new_nullifiers: u64, new_l2_to_l1_msgs: u64, @@ -37,7 +37,7 @@ impl PrivateCircuitPublicInputsArrayLengths { PrivateCircuitPublicInputsArrayLengths { note_hash_read_requests: validate_array(public_inputs.note_hash_read_requests), nullifier_read_requests: validate_array(public_inputs.nullifier_read_requests), - nullifier_key_validation_requests: validate_array(public_inputs.nullifier_key_validation_requests), + key_validation_requests: validate_array(public_inputs.key_validation_requests), new_note_hashes: validate_array(public_inputs.new_note_hashes), new_nullifiers: validate_array(public_inputs.new_nullifiers), new_l2_to_l1_msgs: validate_array(public_inputs.new_l2_to_l1_msgs), @@ -63,7 +63,7 @@ struct PrivateCircuitPublicInputs { note_hash_read_requests: [ReadRequest; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL], nullifier_read_requests: [ReadRequest; MAX_NULLIFIER_READ_REQUESTS_PER_CALL], - nullifier_key_validation_requests: [NullifierKeyValidationRequest; MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL], + key_validation_requests: [KeyValidationRequest; MAX_KEY_VALIDATION_REQUESTS_PER_CALL], new_note_hashes: [NoteHash; MAX_NEW_NOTE_HASHES_PER_CALL], new_nullifiers: [Nullifier; MAX_NEW_NULLIFIERS_PER_CALL], @@ -97,7 +97,7 @@ impl Eq for PrivateCircuitPublicInputs { (self.max_block_number == other.max_block_number) & (self.note_hash_read_requests == other.note_hash_read_requests) & (self.nullifier_read_requests == other.nullifier_read_requests) & - (self.nullifier_key_validation_requests == other.nullifier_key_validation_requests) & + (self.key_validation_requests == other.key_validation_requests) & (self.new_note_hashes == other.new_note_hashes) & (self.new_nullifiers == other.new_nullifiers) & (self.private_call_requests == other.private_call_requests) & @@ -131,8 +131,8 @@ impl Serialize for PrivateCircuitPublicInp for i in 0..self.nullifier_read_requests.len() { fields.extend_from_array(self.nullifier_read_requests[i].serialize()); } - for i in 0..self.nullifier_key_validation_requests.len() { - fields.extend_from_array(self.nullifier_key_validation_requests[i].serialize()); + for i in 0..self.key_validation_requests.len() { + fields.extend_from_array(self.key_validation_requests[i].serialize()); } for i in 0..self.new_note_hashes.len() { fields.extend_from_array(self.new_note_hashes[i].serialize()); @@ -181,7 +181,7 @@ impl Deserialize for PrivateCircuitPublicI max_block_number: reader.read_struct(MaxBlockNumber::deserialize), note_hash_read_requests: reader.read_struct_array(ReadRequest::deserialize, [ReadRequest::empty(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]), nullifier_read_requests: reader.read_struct_array(ReadRequest::deserialize, [ReadRequest::empty(); MAX_NULLIFIER_READ_REQUESTS_PER_CALL]), - nullifier_key_validation_requests: reader.read_struct_array(NullifierKeyValidationRequest::deserialize, [NullifierKeyValidationRequest::empty(); MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL]), + key_validation_requests: reader.read_struct_array(KeyValidationRequest::deserialize, [KeyValidationRequest::empty(); MAX_KEY_VALIDATION_REQUESTS_PER_CALL]), new_note_hashes: reader.read_struct_array(NoteHash::deserialize, [NoteHash::empty(); MAX_NEW_NOTE_HASHES_PER_CALL]), new_nullifiers: reader.read_struct_array(Nullifier::deserialize, [Nullifier::empty(); MAX_NEW_NULLIFIERS_PER_CALL]), private_call_requests: reader.read_struct_array(PrivateCallRequest::deserialize, [PrivateCallRequest::empty(); MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL]), @@ -219,7 +219,7 @@ impl Empty for PrivateCircuitPublicInputs { max_block_number: MaxBlockNumber::empty(), note_hash_read_requests: [ReadRequest::empty(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL], nullifier_read_requests: [ReadRequest::empty(); MAX_NULLIFIER_READ_REQUESTS_PER_CALL], - nullifier_key_validation_requests: [NullifierKeyValidationRequest::empty(); MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL], + key_validation_requests: [KeyValidationRequest::empty(); MAX_KEY_VALIDATION_REQUESTS_PER_CALL], new_note_hashes: [NoteHash::empty(); MAX_NEW_NOTE_HASHES_PER_CALL], new_nullifiers: [Nullifier::empty(); MAX_NEW_NULLIFIERS_PER_CALL], private_call_requests: [PrivateCallRequest::empty(); MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL], diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/validation_requests/validation_requests.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/validation_requests/validation_requests.nr index 2cc7fabdb0c..31bcf1f6ec8 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/validation_requests/validation_requests.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/validation_requests/validation_requests.nr @@ -1,13 +1,12 @@ use crate::{ abis::{ - max_block_number::MaxBlockNumber, - nullifier_key_validation_request::ScopedNullifierKeyValidationRequest, + max_block_number::MaxBlockNumber, key_validation_request::ScopedKeyValidationRequest, public_data_read::PublicDataRead, read_request::ScopedReadRequest, validation_requests::rollup_validation_requests::RollupValidationRequests }, constants::{ MAX_NOTE_HASH_READ_REQUESTS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX, - MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX, MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, + MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX, MAX_KEY_VALIDATION_REQUESTS_PER_TX, MAX_PUBLIC_DATA_READS_PER_TX, VALIDATION_REQUESTS_LENGTH }, traits::{Serialize, Deserialize, Empty}, utils::reader::Reader @@ -19,7 +18,7 @@ struct ValidationRequests { note_hash_read_requests: [ScopedReadRequest; MAX_NOTE_HASH_READ_REQUESTS_PER_TX], nullifier_read_requests: [ScopedReadRequest; MAX_NULLIFIER_READ_REQUESTS_PER_TX], nullifier_non_existent_read_requests: [ScopedReadRequest; MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX], - nullifier_key_validation_requests: [ScopedNullifierKeyValidationRequest; MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX], + key_validation_requests: [ScopedKeyValidationRequest; MAX_KEY_VALIDATION_REQUESTS_PER_TX], public_data_reads: [PublicDataRead; MAX_PUBLIC_DATA_READS_PER_TX], } @@ -41,8 +40,8 @@ impl Serialize for ValidationRequests { fields.extend_from_array(self.nullifier_non_existent_read_requests[i].serialize()); } - for i in 0..MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX { - fields.extend_from_array(self.nullifier_key_validation_requests[i].serialize()); + for i in 0..MAX_KEY_VALIDATION_REQUESTS_PER_TX { + fields.extend_from_array(self.key_validation_requests[i].serialize()); } for i in 0..MAX_PUBLIC_DATA_READS_PER_TX { @@ -64,7 +63,7 @@ impl Deserialize for ValidationRequests { note_hash_read_requests: reader.read_struct_array(ScopedReadRequest::deserialize, [ScopedReadRequest::empty(); MAX_NOTE_HASH_READ_REQUESTS_PER_TX]), nullifier_read_requests: reader.read_struct_array(ScopedReadRequest::deserialize, [ScopedReadRequest::empty(); MAX_NULLIFIER_READ_REQUESTS_PER_TX]), nullifier_non_existent_read_requests: reader.read_struct_array(ScopedReadRequest::deserialize, [ScopedReadRequest::empty(); MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX]), - nullifier_key_validation_requests: reader.read_struct_array(ScopedNullifierKeyValidationRequest::deserialize, [ScopedNullifierKeyValidationRequest::empty(); MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX]), + key_validation_requests: reader.read_struct_array(ScopedKeyValidationRequest::deserialize, [ScopedKeyValidationRequest::empty(); MAX_KEY_VALIDATION_REQUESTS_PER_TX]), public_data_reads: reader.read_struct_array(PublicDataRead::deserialize, [PublicDataRead::empty(); MAX_PUBLIC_DATA_READS_PER_TX]), }; @@ -81,7 +80,7 @@ impl Empty for ValidationRequests { note_hash_read_requests: [ScopedReadRequest::empty(); MAX_NOTE_HASH_READ_REQUESTS_PER_TX], nullifier_read_requests: [ScopedReadRequest::empty(); MAX_NULLIFIER_READ_REQUESTS_PER_TX], nullifier_non_existent_read_requests: [ScopedReadRequest::empty(); MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX], - nullifier_key_validation_requests: [ScopedNullifierKeyValidationRequest::empty(); MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX], + key_validation_requests: [ScopedKeyValidationRequest::empty(); MAX_KEY_VALIDATION_REQUESTS_PER_TX], public_data_reads: [PublicDataRead::empty(); MAX_PUBLIC_DATA_READS_PER_TX], } } @@ -93,7 +92,7 @@ impl Eq for ValidationRequests { (self.note_hash_read_requests == other.note_hash_read_requests) & (self.nullifier_read_requests == other.nullifier_read_requests) & (self.nullifier_non_existent_read_requests == other.nullifier_non_existent_read_requests) & - (self.nullifier_key_validation_requests == other.nullifier_key_validation_requests) & + (self.key_validation_requests == other.key_validation_requests) & (self.public_data_reads == other.public_data_reads) } } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/validation_requests/validation_requests_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/validation_requests/validation_requests_builder.nr index 6fe9d71310a..b271d6e7d27 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/validation_requests/validation_requests_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/validation_requests/validation_requests_builder.nr @@ -1,14 +1,13 @@ use crate::{ abis::{ - max_block_number::MaxBlockNumber, - nullifier_key_validation_request::ScopedNullifierKeyValidationRequest, + max_block_number::MaxBlockNumber, key_validation_request::ScopedKeyValidationRequest, public_data_read::PublicDataRead, read_request::ScopedReadRequest, validation_requests::validation_requests::ValidationRequests, validation_requests::rollup_validation_requests::RollupValidationRequests }, constants::{ MAX_NOTE_HASH_READ_REQUESTS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX, - MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX, MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, + MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX, MAX_KEY_VALIDATION_REQUESTS_PER_TX, MAX_PUBLIC_DATA_READS_PER_TX }, traits::Empty @@ -19,7 +18,7 @@ struct ValidationRequestsBuilder { note_hash_read_requests: BoundedVec, nullifier_read_requests: BoundedVec, nullifier_non_existent_read_requests: BoundedVec, - nullifier_key_validation_requests: BoundedVec, + key_validation_requests: BoundedVec, public_data_reads: BoundedVec, } @@ -30,7 +29,7 @@ impl ValidationRequestsBuilder { note_hash_read_requests: self.note_hash_read_requests.storage, nullifier_read_requests: self.nullifier_read_requests.storage, nullifier_non_existent_read_requests: self.nullifier_non_existent_read_requests.storage, - nullifier_key_validation_requests: self.nullifier_key_validation_requests.storage, + key_validation_requests: self.key_validation_requests.storage, public_data_reads: self.public_data_reads.storage } } @@ -47,7 +46,7 @@ impl Empty for ValidationRequestsBuilder { note_hash_read_requests: BoundedVec::new(), nullifier_read_requests: BoundedVec::new(), nullifier_non_existent_read_requests: BoundedVec::new(), - nullifier_key_validation_requests: BoundedVec::new(), + key_validation_requests: BoundedVec::new(), public_data_reads: BoundedVec::new(), } } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index 2c2703974af..72b4fe23927 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -32,7 +32,7 @@ global MAX_PUBLIC_DATA_READS_PER_CALL: u64 = 16; global MAX_NOTE_HASH_READ_REQUESTS_PER_CALL: u64 = 32; global MAX_NULLIFIER_READ_REQUESTS_PER_CALL: u64 = 32; global MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL: u64 = 32; -global MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL: u64 = 16; +global MAX_KEY_VALIDATION_REQUESTS_PER_CALL: u64 = 16; global MAX_NOTE_ENCRYPTED_LOGS_PER_CALL: u64 = 16; global MAX_ENCRYPTED_LOGS_PER_CALL: u64 = 4; // If modifying, update DEPLOYER_CONTRACT_ADDRESS. global MAX_UNENCRYPTED_LOGS_PER_CALL: u64 = 4; // If modifying, update DEPLOYER_CONTRACT_ADDRESS. @@ -48,7 +48,9 @@ global MAX_NEW_L2_TO_L1_MSGS_PER_TX: u64 = 2; global MAX_NOTE_HASH_READ_REQUESTS_PER_TX: u64 = 128; global MAX_NULLIFIER_READ_REQUESTS_PER_TX: u64 = 128; global MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX: u64 = 128; -global MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX: u64 = 64; +// TODO: for large multisends we might run out of key validation requests here but not dealing with this now as +// databus will hopefully make the issue go away. +global MAX_KEY_VALIDATION_REQUESTS_PER_TX: u64 = 64; global MAX_NOTE_ENCRYPTED_LOGS_PER_TX: u64 = 64; global MAX_ENCRYPTED_LOGS_PER_TX: u64 = 8; global MAX_UNENCRYPTED_LOGS_PER_TX: u64 = 8; @@ -158,8 +160,8 @@ global L1_TO_L2_MESSAGE_LENGTH: u64 = 6; global L2_TO_L1_MESSAGE_LENGTH: u64 = 3; global SCOPED_L2_TO_L1_MESSAGE_LENGTH = L2_TO_L1_MESSAGE_LENGTH + 1; global MAX_BLOCK_NUMBER_LENGTH: u64 = 2; // 1 for the option flag, 1 for the value -global NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH = 3; -global SCOPED_NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH = NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH + 1; +global KEY_VALIDATION_REQUEST_LENGTH = 3; +global SCOPED_KEY_VALIDATION_REQUEST_LENGTH = KEY_VALIDATION_REQUEST_LENGTH + 1; global PARTIAL_STATE_REFERENCE_LENGTH: u64 = 6; global READ_REQUEST_LENGTH = 2; global LOG_HASH_LENGTH = 3; @@ -178,14 +180,14 @@ global TX_CONTEXT_LENGTH: u64 = 2 + GAS_SETTINGS_LENGTH; global TX_REQUEST_LENGTH: u64 = 2 + TX_CONTEXT_LENGTH + FUNCTION_DATA_LENGTH; global TOTAL_FEES_LENGTH = 1; global HEADER_LENGTH: u64 = APPEND_ONLY_TREE_SNAPSHOT_LENGTH + CONTENT_COMMITMENT_LENGTH + STATE_REFERENCE_LENGTH + GLOBAL_VARIABLES_LENGTH + TOTAL_FEES_LENGTH; -global PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH: u64 = CALL_CONTEXT_LENGTH + 4 + MAX_BLOCK_NUMBER_LENGTH + (READ_REQUEST_LENGTH * MAX_NOTE_HASH_READ_REQUESTS_PER_CALL) + (READ_REQUEST_LENGTH * MAX_NULLIFIER_READ_REQUESTS_PER_CALL) + (NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH * MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL) + (NOTE_HASH_LENGTH * MAX_NEW_NOTE_HASHES_PER_CALL) + (NULLIFIER_LENGTH * MAX_NEW_NULLIFIERS_PER_CALL) + (PRIVATE_CALL_REQUEST_LENGTH * MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL) + MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL + 1 + (L2_TO_L1_MESSAGE_LENGTH * MAX_NEW_L2_TO_L1_MSGS_PER_CALL) + 2 + (NOTE_LOG_HASH_LENGTH * MAX_NOTE_ENCRYPTED_LOGS_PER_CALL) + (LOG_HASH_LENGTH * MAX_ENCRYPTED_LOGS_PER_CALL) + (LOG_HASH_LENGTH * MAX_UNENCRYPTED_LOGS_PER_CALL) + HEADER_LENGTH + TX_CONTEXT_LENGTH; +global PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH: u64 = CALL_CONTEXT_LENGTH + 4 + MAX_BLOCK_NUMBER_LENGTH + (READ_REQUEST_LENGTH * MAX_NOTE_HASH_READ_REQUESTS_PER_CALL) + (READ_REQUEST_LENGTH * MAX_NULLIFIER_READ_REQUESTS_PER_CALL) + (KEY_VALIDATION_REQUEST_LENGTH * MAX_KEY_VALIDATION_REQUESTS_PER_CALL) + (NOTE_HASH_LENGTH * MAX_NEW_NOTE_HASHES_PER_CALL) + (NULLIFIER_LENGTH * MAX_NEW_NULLIFIERS_PER_CALL) + (PRIVATE_CALL_REQUEST_LENGTH * MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL) + MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL + 1 + (L2_TO_L1_MESSAGE_LENGTH * MAX_NEW_L2_TO_L1_MSGS_PER_CALL) + 2 + (NOTE_LOG_HASH_LENGTH * MAX_NOTE_ENCRYPTED_LOGS_PER_CALL) + (LOG_HASH_LENGTH * MAX_ENCRYPTED_LOGS_PER_CALL) + (LOG_HASH_LENGTH * MAX_UNENCRYPTED_LOGS_PER_CALL) + HEADER_LENGTH + TX_CONTEXT_LENGTH; global PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH: u64 = CALL_CONTEXT_LENGTH + 2 + (READ_REQUEST_LENGTH * MAX_NULLIFIER_READ_REQUESTS_PER_CALL) + (READ_REQUEST_LENGTH * MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL) + (CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH * MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL) + (CONTRACT_STORAGE_READ_LENGTH * MAX_PUBLIC_DATA_READS_PER_CALL) + MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL + (NOTE_HASH_LENGTH * MAX_NEW_NOTE_HASHES_PER_CALL) + (NULLIFIER_LENGTH * MAX_NEW_NULLIFIERS_PER_CALL) + (L2_TO_L1_MESSAGE_LENGTH * MAX_NEW_L2_TO_L1_MSGS_PER_CALL) + 2 + (LOG_HASH_LENGTH * MAX_UNENCRYPTED_LOGS_PER_CALL) + HEADER_LENGTH + GLOBAL_VARIABLES_LENGTH + AZTEC_ADDRESS_LENGTH + /* revert_code */ 1 + 2 * GAS_LENGTH + /* transaction_fee */ 1; global PRIVATE_CALL_STACK_ITEM_LENGTH: u64 = AZTEC_ADDRESS_LENGTH + FUNCTION_DATA_LENGTH + PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH; global PUBLIC_CONTEXT_INPUTS_LENGTH: u64 = CALL_CONTEXT_LENGTH + HEADER_LENGTH + GLOBAL_VARIABLES_LENGTH + GAS_LENGTH + 2; global SCOPED_READ_REQUEST_LEN = READ_REQUEST_LENGTH + 1; global PUBLIC_DATA_READ_LENGTH = 2; -global VALIDATION_REQUESTS_LENGTH = ROLLUP_VALIDATION_REQUESTS_LENGTH + (SCOPED_READ_REQUEST_LEN * MAX_NOTE_HASH_READ_REQUESTS_PER_TX) + (SCOPED_READ_REQUEST_LEN * MAX_NULLIFIER_READ_REQUESTS_PER_TX) + (SCOPED_READ_REQUEST_LEN * MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX) + (SCOPED_NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH * MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX) + (PUBLIC_DATA_READ_LENGTH * MAX_PUBLIC_DATA_READS_PER_TX); +global VALIDATION_REQUESTS_LENGTH = ROLLUP_VALIDATION_REQUESTS_LENGTH + (SCOPED_READ_REQUEST_LEN * MAX_NOTE_HASH_READ_REQUESTS_PER_TX) + (SCOPED_READ_REQUEST_LEN * MAX_NULLIFIER_READ_REQUESTS_PER_TX) + (SCOPED_READ_REQUEST_LEN * MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX) + (SCOPED_KEY_VALIDATION_REQUEST_LENGTH * MAX_KEY_VALIDATION_REQUESTS_PER_TX) + (PUBLIC_DATA_READ_LENGTH * MAX_PUBLIC_DATA_READS_PER_TX); global PUBLIC_DATA_UPDATE_REQUEST_LENGTH = 2; global COMBINED_ACCUMULATED_DATA_LENGTH = MAX_NEW_NOTE_HASHES_PER_TX + MAX_NEW_NULLIFIERS_PER_TX + MAX_NEW_L2_TO_L1_MSGS_PER_TX + 5 + (MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * PUBLIC_DATA_UPDATE_REQUEST_LENGTH) + GAS_LENGTH; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr index ea2db5f580a..c80959c409b 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr @@ -8,7 +8,7 @@ use crate::{ kernel_data::KernelData, public_kernel_data::PublicKernelData, max_block_number::MaxBlockNumber, private_kernel_data::PrivateKernelData, note_hash::{NoteHash, ScopedNoteHash}, nullifier::{Nullifier, ScopedNullifier}, - nullifier_key_validation_request::{ScopedNullifierKeyValidationRequest, NullifierKeyValidationRequest}, + key_validation_request::{ScopedKeyValidationRequest, KeyValidationRequest}, private_call_request::{PrivateCallRequest, ScopedPrivateCallRequest}, public_data_read::PublicDataRead, public_data_update_request::PublicDataUpdateRequest, read_request::{ReadRequest, ScopedReadRequest}, log_hash::{LogHash, NoteLogHash}, @@ -20,9 +20,8 @@ use crate::{ MAX_PUBLIC_DATA_READS_PER_TX, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX, - MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX, MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, - VK_TREE_HEIGHT, MAX_ENCRYPTED_LOGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX, - MAX_NOTE_ENCRYPTED_LOGS_PER_TX + MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX, MAX_KEY_VALIDATION_REQUESTS_PER_TX, VK_TREE_HEIGHT, + MAX_ENCRYPTED_LOGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX }, hash::silo_nullifier, header::Header, messaging::l2_to_l1_message::{L2ToL1Message, ScopedL2ToL1Message}, @@ -65,7 +64,7 @@ struct FixtureBuilder { note_hash_read_requests: BoundedVec, nullifier_read_requests: BoundedVec, nullifier_non_existent_read_requests: BoundedVec, - nullifier_key_validation_requests: BoundedVec, + key_validation_requests: BoundedVec, public_data_reads: BoundedVec, // Proof. @@ -111,7 +110,7 @@ impl FixtureBuilder { note_hash_read_requests: BoundedVec::new(), nullifier_read_requests: BoundedVec::new(), nullifier_non_existent_read_requests: BoundedVec::new(), - nullifier_key_validation_requests: BoundedVec::new(), + key_validation_requests: BoundedVec::new(), public_data_reads: BoundedVec::new(), proof: NestedRecursiveProof::empty(), vk: VerificationKey::empty(), @@ -185,7 +184,7 @@ impl FixtureBuilder { note_hash_read_requests: self.note_hash_read_requests, nullifier_read_requests: self.nullifier_read_requests, nullifier_non_existent_read_requests: self.nullifier_non_existent_read_requests, - nullifier_key_validation_requests: self.nullifier_key_validation_requests, + key_validation_requests: self.key_validation_requests, public_data_reads: self.public_data_reads }; validation_requests.finish() @@ -412,14 +411,10 @@ impl FixtureBuilder { new_read_request_index } - pub fn add_request_for_nullifier_key_validation( - &mut self, - master_nullifier_public_key: GrumpkinPoint, - app_nullifier_secret_key: Field - ) -> u64 { - let new_request_index = self.nullifier_key_validation_requests.len(); - let request = NullifierKeyValidationRequest { master_nullifier_public_key, app_nullifier_secret_key }; - self.nullifier_key_validation_requests.push(request.scope(self.storage_contract_address)); + pub fn add_request_for_key_validation(&mut self, pk_m: GrumpkinPoint, sk_app: Field) -> u64 { + let new_request_index = self.key_validation_requests.len(); + let request = KeyValidationRequest { pk_m, sk_app }; + self.key_validation_requests.push(request.scope(self.storage_contract_address)); new_request_index } @@ -548,7 +543,7 @@ impl Empty for FixtureBuilder { note_hash_read_requests: BoundedVec::new(), nullifier_read_requests: BoundedVec::new(), nullifier_non_existent_read_requests: BoundedVec::new(), - nullifier_key_validation_requests: BoundedVec::new(), + key_validation_requests: BoundedVec::new(), public_data_reads: BoundedVec::new(), proof: NestedRecursiveProof::empty(), vk: VerificationKey::empty(), diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/private_circuit_public_inputs_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/private_circuit_public_inputs_builder.nr index 6976b98d761..be489e0894b 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/private_circuit_public_inputs_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/private_circuit_public_inputs_builder.nr @@ -2,7 +2,7 @@ use crate::{ abis::{ call_context::CallContext, call_request::CallRequest, caller_context::CallerContext, gas_settings::GasSettings, gas::Gas, max_block_number::MaxBlockNumber, note_hash::NoteHash, - nullifier::Nullifier, nullifier_key_validation_request::NullifierKeyValidationRequest, + nullifier::Nullifier, key_validation_request::KeyValidationRequest, private_call_request::PrivateCallRequest, private_circuit_public_inputs::PrivateCircuitPublicInputs, read_request::ReadRequest, log_hash::{LogHash, NoteLogHash} }, @@ -12,10 +12,10 @@ use crate::{ use crate::{ constants::{ MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, MAX_NULLIFIER_READ_REQUESTS_PER_CALL, - MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, MAX_NEW_NOTE_HASHES_PER_CALL, - MAX_NEW_NULLIFIERS_PER_CALL, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, - MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_NEW_L2_TO_L1_MSGS_PER_CALL, MAX_ENCRYPTED_LOGS_PER_CALL, - MAX_UNENCRYPTED_LOGS_PER_CALL, MAX_NOTE_ENCRYPTED_LOGS_PER_CALL + MAX_KEY_VALIDATION_REQUESTS_PER_CALL, MAX_NEW_NOTE_HASHES_PER_CALL, MAX_NEW_NULLIFIERS_PER_CALL, + MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, + MAX_NEW_L2_TO_L1_MSGS_PER_CALL, MAX_ENCRYPTED_LOGS_PER_CALL, MAX_UNENCRYPTED_LOGS_PER_CALL, + MAX_NOTE_ENCRYPTED_LOGS_PER_CALL }, traits::Empty }; @@ -34,7 +34,7 @@ struct PrivateCircuitPublicInputsBuilder { note_hash_read_requests: BoundedVec, nullifier_read_requests: BoundedVec, - nullifier_key_validation_requests: BoundedVec, + key_validation_requests: BoundedVec, new_note_hashes: BoundedVec, new_nullifiers: BoundedVec, @@ -224,7 +224,7 @@ impl PrivateCircuitPublicInputsBuilder { max_block_number: self.max_block_number, note_hash_read_requests: self.note_hash_read_requests.storage, nullifier_read_requests: self.nullifier_read_requests.storage, - nullifier_key_validation_requests: self.nullifier_key_validation_requests.storage, + key_validation_requests: self.key_validation_requests.storage, new_note_hashes: self.new_note_hashes.storage, new_nullifiers: self.new_nullifiers.storage, private_call_requests: self.private_call_requests.storage, @@ -260,7 +260,7 @@ impl Empty for PrivateCircuitPublicInputsBuilder { max_block_number: MaxBlockNumber::empty(), note_hash_read_requests: BoundedVec::new(), nullifier_read_requests: BoundedVec::new(), - nullifier_key_validation_requests: BoundedVec::new(), + key_validation_requests: BoundedVec::new(), new_note_hashes: BoundedVec::new(), new_nullifiers: BoundedVec::new(), private_call_requests: BoundedVec::new(), diff --git a/yarn-project/circuit-types/src/keys/key_store.ts b/yarn-project/circuit-types/src/keys/key_store.ts index 1dbeeb7831c..62b5874c55b 100644 --- a/yarn-project/circuit-types/src/keys/key_store.ts +++ b/yarn-project/circuit-types/src/keys/key_store.ts @@ -4,6 +4,8 @@ import { type Fq, type Fr, type GrumpkinPrivateKey, + type KeyGenerator, + type KeyValidationRequest, type PartialAddress, type PublicKey, } from '@aztec/circuits.js'; @@ -32,14 +34,6 @@ export interface KeyStore { */ getAccounts(): Promise; - /** - * Gets the master nullifier public key for a given master nullifier public key hash. - * @throws If the account corresponding to the master nullifier public key hash does not exist in the key store. - * @param npkMHash - The master nullifier public key hash. - * @returns The master nullifier public key for the account. - */ - getMasterNullifierPublicKey(npkMHash: Fr): Promise; - /** * Gets the master incoming viewing public key for a given account. * @throws If the account does not exist in the key store. @@ -64,15 +58,6 @@ export interface KeyStore { */ getMasterTaggingPublicKey(account: AztecAddress): Promise; - /** - * Derives and returns the application nullifier secret key for a given master nullifier public key hash. - * @throws If the account corresponding to the master nullifier public key hash does not exist in the key store. - * @param npkMHash - The master nullifier public key hash. - * @param app - The application address to retrieve the nullifier secret key for. - * @returns A Promise that resolves to the application nullifier secret key. - */ - getAppNullifierSecretKey(npkMHash: Fr, app: AztecAddress): Promise; - /** * Retrieves application incoming viewing secret key. * @throws If the account does not exist in the key store. @@ -92,14 +77,13 @@ export interface KeyStore { getAppOutgoingViewingSecretKey(account: AztecAddress, app: AztecAddress): Promise; /** - * Retrieves the master nullifier secret key (nsk_m) corresponding to the specified master nullifier public key - * (Npk_m). + * Retrieves the sk_m for the pk_m and a generator index of the key type. * @throws If the provided public key is not associated with any of the registered accounts. - * @param masterNullifierPublicKey - The master nullifier public key to get secret key for. - * @returns A Promise that resolves to the master nullifier secret key. - * @dev Used when feeding the master nullifier secret key to the kernel circuit for nullifier keys verification. + * @param masterPublicKey - The master public key to get secret key for. + * @returns A Promise that resolves to sk_m. + * @dev Used when feeding the sk_m to the kernel circuit for keys verification. */ - getMasterNullifierSecretKeyForPublicKey(masterNullifierPublicKey: PublicKey): Promise; + getMasterSecretKeyAndAppKeyGenerator(masterPublicKey: PublicKey): Promise<[GrumpkinPrivateKey, KeyGenerator]>; /** * Retrieves the master incoming viewing secret key (ivsk_m) corresponding to the specified master incoming viewing @@ -112,12 +96,25 @@ export interface KeyStore { getMasterIncomingViewingSecretKeyForPublicKey(masterIncomingViewingPublicKey: PublicKey): Promise; /** - * Retrieves public keys hash of the account - * @throws If the provided account address is not associated with any of the registered accounts. - * @param account - The account address to get public keys hash for. - * @returns A Promise that resolves to the public keys hash. + * Gets the key validation request for a given master public key hash and contract address. + * @throws If the account corresponding to the master public key hash does not exist in the key store. + * @param pkMHash - The master public key hash. + * @param contractAddress - The contract address to silo the secret key in the the key validation request with. + * @returns The key validation request. */ - getPublicKeysHash(account: AztecAddress): Promise; + getKeyValidationRequest(pkMHash: Fr, contractAddress: AztecAddress): Promise; + /** + * Rotates the master nullifier key for the specified account. + * + * @dev This function updates the secret and public keys associated with the account. + * It appends a new secret key to the existing secret keys, derives the + * corresponding public key, and updates the stored keys accordingly. + * + * @param account - The account address for which the master nullifier key is being rotated. + * @param newSecretKey - (Optional) A new secret key of type Fq. If not provided, a random key is generated. + * @throws If the account does not have existing nullifier secret keys or public keys. + * @returns A Promise that resolves when the key rotation is complete. + */ rotateMasterNullifierKey(account: AztecAddress, secretKey: Fq): Promise; } diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index ca2a8f9deb5..33264c6e445 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -11,7 +11,7 @@ export const MAX_PUBLIC_DATA_READS_PER_CALL = 16; export const MAX_NOTE_HASH_READ_REQUESTS_PER_CALL = 32; export const MAX_NULLIFIER_READ_REQUESTS_PER_CALL = 32; export const MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL = 32; -export const MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL = 16; +export const MAX_KEY_VALIDATION_REQUESTS_PER_CALL = 16; export const MAX_NOTE_ENCRYPTED_LOGS_PER_CALL = 16; export const MAX_ENCRYPTED_LOGS_PER_CALL = 4; export const MAX_UNENCRYPTED_LOGS_PER_CALL = 4; @@ -25,7 +25,7 @@ export const MAX_NEW_L2_TO_L1_MSGS_PER_TX = 2; export const MAX_NOTE_HASH_READ_REQUESTS_PER_TX = 128; export const MAX_NULLIFIER_READ_REQUESTS_PER_TX = 128; export const MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX = 128; -export const MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX = 64; +export const MAX_KEY_VALIDATION_REQUESTS_PER_TX = 64; export const MAX_NOTE_ENCRYPTED_LOGS_PER_TX = 64; export const MAX_ENCRYPTED_LOGS_PER_TX = 8; export const MAX_UNENCRYPTED_LOGS_PER_TX = 8; @@ -102,8 +102,8 @@ export const L1_TO_L2_MESSAGE_LENGTH = 6; export const L2_TO_L1_MESSAGE_LENGTH = 3; export const SCOPED_L2_TO_L1_MESSAGE_LENGTH = L2_TO_L1_MESSAGE_LENGTH + 1; export const MAX_BLOCK_NUMBER_LENGTH = 2; -export const NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH = 3; -export const SCOPED_NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH = NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH + 1; +export const KEY_VALIDATION_REQUEST_LENGTH = 3; +export const SCOPED_KEY_VALIDATION_REQUEST_LENGTH = KEY_VALIDATION_REQUEST_LENGTH + 1; export const PARTIAL_STATE_REFERENCE_LENGTH = 6; export const READ_REQUEST_LENGTH = 2; export const LOG_HASH_LENGTH = 3; @@ -133,7 +133,7 @@ export const PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = MAX_BLOCK_NUMBER_LENGTH + READ_REQUEST_LENGTH * MAX_NOTE_HASH_READ_REQUESTS_PER_CALL + READ_REQUEST_LENGTH * MAX_NULLIFIER_READ_REQUESTS_PER_CALL + - NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH * MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL + + KEY_VALIDATION_REQUEST_LENGTH * MAX_KEY_VALIDATION_REQUESTS_PER_CALL + NOTE_HASH_LENGTH * MAX_NEW_NOTE_HASHES_PER_CALL + NULLIFIER_LENGTH * MAX_NEW_NULLIFIERS_PER_CALL + PRIVATE_CALL_REQUEST_LENGTH * MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL + @@ -176,7 +176,7 @@ export const VALIDATION_REQUESTS_LENGTH = SCOPED_READ_REQUEST_LEN * MAX_NOTE_HASH_READ_REQUESTS_PER_TX + SCOPED_READ_REQUEST_LEN * MAX_NULLIFIER_READ_REQUESTS_PER_TX + SCOPED_READ_REQUEST_LEN * MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX + - SCOPED_NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH * MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX + + SCOPED_KEY_VALIDATION_REQUEST_LENGTH * MAX_KEY_VALIDATION_REQUESTS_PER_TX + PUBLIC_DATA_READ_LENGTH * MAX_PUBLIC_DATA_READS_PER_TX; export const PUBLIC_DATA_UPDATE_REQUEST_LENGTH = 2; export const COMBINED_ACCUMULATED_DATA_LENGTH = diff --git a/yarn-project/circuits.js/src/keys/__snapshots__/index.test.ts.snap b/yarn-project/circuits.js/src/keys/__snapshots__/derivation.test.ts.snap similarity index 100% rename from yarn-project/circuits.js/src/keys/__snapshots__/index.test.ts.snap rename to yarn-project/circuits.js/src/keys/__snapshots__/derivation.test.ts.snap diff --git a/yarn-project/circuits.js/src/keys/index.test.ts b/yarn-project/circuits.js/src/keys/derivation.test.ts similarity index 97% rename from yarn-project/circuits.js/src/keys/index.test.ts rename to yarn-project/circuits.js/src/keys/derivation.test.ts index de6d68d1610..f41aa0c0d3f 100644 --- a/yarn-project/circuits.js/src/keys/index.test.ts +++ b/yarn-project/circuits.js/src/keys/derivation.test.ts @@ -2,7 +2,7 @@ import { Fr, Point } from '@aztec/foundation/fields'; import { updateInlineTestData } from '@aztec/foundation/testing'; import { PublicKeys } from '../types/public_keys.js'; -import { computeAddress } from './index.js'; +import { computeAddress } from './derivation.js'; describe('🔑', () => { it('computing public keys hash matches Noir', () => { diff --git a/yarn-project/circuits.js/src/keys/derivation.ts b/yarn-project/circuits.js/src/keys/derivation.ts new file mode 100644 index 00000000000..e6d008c132c --- /dev/null +++ b/yarn-project/circuits.js/src/keys/derivation.ts @@ -0,0 +1,102 @@ +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { poseidon2Hash, sha512ToGrumpkinScalar } from '@aztec/foundation/crypto'; +import { Fq, type Fr, type GrumpkinScalar } from '@aztec/foundation/fields'; + +import { Grumpkin } from '../barretenberg/crypto/grumpkin/index.js'; +import { GeneratorIndex } from '../constants.gen.js'; +import { GrumpkinPrivateKey } from '../types/grumpkin_private_key.js'; +import { type PublicKey } from '../types/public_key.js'; +import { PublicKeys } from '../types/public_keys.js'; +import { type KeyPrefix } from './key_types.js'; +import { getKeyGenerator } from './utils.js'; + +const curve = new Grumpkin(); + +export function computeAppNullifierSecretKey(masterNullifierSecretKey: GrumpkinPrivateKey, app: AztecAddress): Fr { + return poseidon2Hash([masterNullifierSecretKey.high, masterNullifierSecretKey.low, app, GeneratorIndex.NSK_M]); +} + +export function computeAppSecretKey(skM: GrumpkinPrivateKey, app: AztecAddress, keyPrefix: KeyPrefix): Fr { + const generator = getKeyGenerator(keyPrefix); + return poseidon2Hash([skM.high, skM.low, app, generator]); +} + +export function computeIvpkApp(ivpk: PublicKey, address: AztecAddress) { + const I = Fq.fromBuffer(poseidon2Hash([address.toField(), ivpk.x, ivpk.y, GeneratorIndex.IVSK_M]).toBuffer()); + return curve.add(curve.mul(Grumpkin.generator, I), ivpk); +} + +export function computeIvskApp(ivsk: GrumpkinPrivateKey, address: AztecAddress) { + const ivpk = curve.mul(Grumpkin.generator, ivsk); + const I = Fq.fromBuffer(poseidon2Hash([address.toField(), ivpk.x, ivpk.y, GeneratorIndex.IVSK_M]).toBuffer()); + return new Fq((I.toBigInt() + ivsk.toBigInt()) % Fq.MODULUS); +} + +export function computeOvskApp(ovsk: GrumpkinPrivateKey, address: AztecAddress) { + return GrumpkinPrivateKey.fromBuffer( + poseidon2Hash([address.toField(), ovsk.high, ovsk.low, GeneratorIndex.OVSK_M]).toBuffer(), + ); +} + +export function deriveMasterNullifierSecretKey(secretKey: Fr): GrumpkinScalar { + return sha512ToGrumpkinScalar([secretKey, GeneratorIndex.NSK_M]); +} + +export function deriveMasterIncomingViewingSecretKey(secretKey: Fr): GrumpkinScalar { + return sha512ToGrumpkinScalar([secretKey, GeneratorIndex.IVSK_M]); +} + +export function deriveMasterOutgoingViewingSecretKey(secretKey: Fr): GrumpkinScalar { + return sha512ToGrumpkinScalar([secretKey, GeneratorIndex.OVSK_M]); +} + +export function deriveSigningKey(secretKey: Fr): GrumpkinScalar { + // TODO(#5837): come up with a standard signing key derivation scheme instead of using ivsk_m as signing keys here + return sha512ToGrumpkinScalar([secretKey, GeneratorIndex.IVSK_M]); +} + +export function computeAddress(publicKeysHash: Fr, partialAddress: Fr) { + const addressFr = poseidon2Hash([publicKeysHash, partialAddress, GeneratorIndex.CONTRACT_ADDRESS_V1]); + return AztecAddress.fromField(addressFr); +} + +export function derivePublicKeyFromSecretKey(secretKey: Fq) { + const curve = new Grumpkin(); + return curve.mul(curve.generator(), secretKey); +} + +/** + * Computes secret and public keys and public keys hash from a secret key. + * @param secretKey - The secret key to derive keys from. + * @returns The derived keys. + */ +export function deriveKeys(secretKey: Fr) { + // First we derive master secret keys - we use sha512 here because this derivation will never take place + // in a circuit + const masterNullifierSecretKey = deriveMasterNullifierSecretKey(secretKey); + const masterIncomingViewingSecretKey = deriveMasterIncomingViewingSecretKey(secretKey); + const masterOutgoingViewingSecretKey = deriveMasterOutgoingViewingSecretKey(secretKey); + const masterTaggingSecretKey = sha512ToGrumpkinScalar([secretKey, GeneratorIndex.TSK_M]); + + // Then we derive master public keys + const masterNullifierPublicKey = derivePublicKeyFromSecretKey(masterNullifierSecretKey); + const masterIncomingViewingPublicKey = derivePublicKeyFromSecretKey(masterIncomingViewingSecretKey); + const masterOutgoingViewingPublicKey = derivePublicKeyFromSecretKey(masterOutgoingViewingSecretKey); + const masterTaggingPublicKey = derivePublicKeyFromSecretKey(masterTaggingSecretKey); + + // We hash the public keys to get the public keys hash + const publicKeys = new PublicKeys( + masterNullifierPublicKey, + masterIncomingViewingPublicKey, + masterOutgoingViewingPublicKey, + masterTaggingPublicKey, + ); + + return { + masterNullifierSecretKey, + masterIncomingViewingSecretKey, + masterOutgoingViewingSecretKey, + masterTaggingSecretKey, + publicKeys, + }; +} diff --git a/yarn-project/circuits.js/src/keys/index.ts b/yarn-project/circuits.js/src/keys/index.ts index 2dc73210e08..0befa5d93fa 100644 --- a/yarn-project/circuits.js/src/keys/index.ts +++ b/yarn-project/circuits.js/src/keys/index.ts @@ -1,91 +1,3 @@ -import { AztecAddress } from '@aztec/foundation/aztec-address'; -import { poseidon2Hash, sha512ToGrumpkinScalar } from '@aztec/foundation/crypto'; -import { Fq, type Fr, type GrumpkinScalar } from '@aztec/foundation/fields'; - -import { Grumpkin } from '../barretenberg/crypto/grumpkin/index.js'; -import { GeneratorIndex } from '../constants.gen.js'; -import { GrumpkinPrivateKey } from '../types/grumpkin_private_key.js'; -import { type PublicKey } from '../types/public_key.js'; -import { PublicKeys } from '../types/public_keys.js'; - -const curve = new Grumpkin(); - -export function computeAppNullifierSecretKey(masterNullifierSecretKey: GrumpkinPrivateKey, app: AztecAddress): Fr { - return poseidon2Hash([masterNullifierSecretKey.high, masterNullifierSecretKey.low, app, GeneratorIndex.NSK_M]); -} - -export function computeIvpkApp(ivpk: PublicKey, address: AztecAddress) { - const I = Fq.fromBuffer(poseidon2Hash([address.toField(), ivpk.x, ivpk.y, GeneratorIndex.IVSK_M]).toBuffer()); - return curve.add(curve.mul(Grumpkin.generator, I), ivpk); -} - -export function computeIvskApp(ivsk: GrumpkinPrivateKey, address: AztecAddress) { - const ivpk = curve.mul(Grumpkin.generator, ivsk); - const I = Fq.fromBuffer(poseidon2Hash([address.toField(), ivpk.x, ivpk.y, GeneratorIndex.IVSK_M]).toBuffer()); - return new Fq((I.toBigInt() + ivsk.toBigInt()) % Fq.MODULUS); -} - -export function computeOvskApp(ovsk: GrumpkinPrivateKey, address: AztecAddress) { - return GrumpkinPrivateKey.fromBuffer( - poseidon2Hash([address.toField(), ovsk.high, ovsk.low, GeneratorIndex.OVSK_M]).toBuffer(), - ); -} - -export function deriveMasterNullifierSecretKey(secretKey: Fr): GrumpkinScalar { - return sha512ToGrumpkinScalar([secretKey, GeneratorIndex.NSK_M]); -} - -export function deriveMasterIncomingViewingSecretKey(secretKey: Fr): GrumpkinScalar { - return sha512ToGrumpkinScalar([secretKey, GeneratorIndex.IVSK_M]); -} - -export function deriveSigningKey(secretKey: Fr): GrumpkinScalar { - // TODO(#5837): come up with a standard signing key derivation scheme instead of using ivsk_m as signing keys here - return sha512ToGrumpkinScalar([secretKey, GeneratorIndex.IVSK_M]); -} - -export function computeAddress(publicKeysHash: Fr, partialAddress: Fr) { - const addressFr = poseidon2Hash([publicKeysHash, partialAddress, GeneratorIndex.CONTRACT_ADDRESS_V1]); - return AztecAddress.fromField(addressFr); -} - -export function derivePublicKeyFromSecretKey(secretKey: Fq) { - const curve = new Grumpkin(); - return curve.mul(curve.generator(), secretKey); -} - -/** - * Computes secret and public keys and public keys hash from a secret key. - * @param secretKey - The secret key to derive keys from. - * @returns The derived keys. - */ -export function deriveKeys(secretKey: Fr) { - // First we derive master secret keys - we use sha512 here because this derivation will never take place - // in a circuit - const masterNullifierSecretKey = deriveMasterNullifierSecretKey(secretKey); - const masterIncomingViewingSecretKey = deriveMasterIncomingViewingSecretKey(secretKey); - const masterOutgoingViewingSecretKey = sha512ToGrumpkinScalar([secretKey, GeneratorIndex.OVSK_M]); - const masterTaggingSecretKey = sha512ToGrumpkinScalar([secretKey, GeneratorIndex.TSK_M]); - - // Then we derive master public keys - const masterNullifierPublicKey = derivePublicKeyFromSecretKey(masterNullifierSecretKey); - const masterIncomingViewingPublicKey = derivePublicKeyFromSecretKey(masterIncomingViewingSecretKey); - const masterOutgoingViewingPublicKey = derivePublicKeyFromSecretKey(masterOutgoingViewingSecretKey); - const masterTaggingPublicKey = derivePublicKeyFromSecretKey(masterTaggingSecretKey); - - // We hash the public keys to get the public keys hash - const publicKeys = new PublicKeys( - masterNullifierPublicKey, - masterIncomingViewingPublicKey, - masterOutgoingViewingPublicKey, - masterTaggingPublicKey, - ); - - return { - masterNullifierSecretKey, - masterIncomingViewingSecretKey, - masterOutgoingViewingSecretKey, - masterTaggingSecretKey, - publicKeys, - }; -} +export * from './derivation.js'; +export * from './key_types.js'; +export * from './utils.js'; diff --git a/yarn-project/circuits.js/src/keys/key_types.ts b/yarn-project/circuits.js/src/keys/key_types.ts new file mode 100644 index 00000000000..3ccc908c14d --- /dev/null +++ b/yarn-project/circuits.js/src/keys/key_types.ts @@ -0,0 +1,5 @@ +import { type GeneratorIndex } from '../constants.gen.js'; + +export type KeyGenerator = GeneratorIndex.NSK_M | GeneratorIndex.IVSK_M | GeneratorIndex.OVSK_M | GeneratorIndex.TSK_M; +export type KeyPrefix = 'n' | 'iv' | 'ov' | 't'; +export const KEY_PREFIXES: KeyPrefix[] = ['n', 'iv', 'ov', 't']; diff --git a/yarn-project/circuits.js/src/keys/utils.ts b/yarn-project/circuits.js/src/keys/utils.ts new file mode 100644 index 00000000000..c7c9657917d --- /dev/null +++ b/yarn-project/circuits.js/src/keys/utils.ts @@ -0,0 +1,8 @@ +import { GeneratorIndex } from '../constants.gen.js'; +import { type KeyGenerator, type KeyPrefix } from './key_types.js'; + +export function getKeyGenerator(prefix: KeyPrefix): KeyGenerator { + // We get enum key by capitalizing key prefix and concatenating it with 'SK_M' + const enumKey = `${prefix.toUpperCase()}SK_M`; + return GeneratorIndex[enumKey as keyof typeof GeneratorIndex] as KeyGenerator; +} diff --git a/yarn-project/circuits.js/src/scripts/generate_reset_variants.ts b/yarn-project/circuits.js/src/scripts/generate_reset_variants.ts index 90380d66b08..0de55dd2b8b 100644 --- a/yarn-project/circuits.js/src/scripts/generate_reset_variants.ts +++ b/yarn-project/circuits.js/src/scripts/generate_reset_variants.ts @@ -2,8 +2,8 @@ import fs from 'fs'; import path from 'path'; import { + MAX_KEY_VALIDATION_REQUESTS_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, - MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX, } from '../constants.gen.js'; @@ -23,7 +23,7 @@ const prelude = ` // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. import { MAX_NOTE_HASH_READ_REQUESTS_PER_TX, - MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, + MAX_KEY_VALIDATION_REQUESTS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX, } from '../../constants.gen.js'; import type { PrivateKernelResetCircuitPrivateInputs } from './private_kernel_reset_circuit_private_inputs.js'; @@ -49,7 +49,7 @@ function buildPrivateResetVariantsObject(variants: ResetVariant[]): string { NOTE_HASH_SETTLED_AMOUNT: MAX_NOTE_HASH_READ_REQUESTS_PER_TX, NULLIFIER_PENDING_AMOUNT: MAX_NULLIFIER_READ_REQUESTS_PER_TX, NULLIFIER_SETTLED_AMOUNT: MAX_NULLIFIER_READ_REQUESTS_PER_TX, - NULLIFIER_KEYS: MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, + NULLIFIER_KEYS: MAX_KEY_VALIDATION_REQUESTS_PER_TX, }, } as const;\n`; return output; @@ -77,7 +77,7 @@ function buildPrivateResetVariantsType(variants: ResetVariant[]): string { typeof MAX_NOTE_HASH_READ_REQUESTS_PER_TX, typeof MAX_NULLIFIER_READ_REQUESTS_PER_TX, typeof MAX_NULLIFIER_READ_REQUESTS_PER_TX, - typeof MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, + typeof MAX_KEY_VALIDATION_REQUESTS_PER_TX, 'full' >;`; return output; @@ -97,8 +97,8 @@ function validateVariants(variants: ResetVariant[]) { if (variant.replacements.NULLIFIER_SETTLED_AMOUNT > MAX_NULLIFIER_READ_REQUESTS_PER_TX) { throw new Error(`NULLIFIER_SETTLED_AMOUNT must be less than ${MAX_NULLIFIER_READ_REQUESTS_PER_TX}`); } - if (variant.replacements.NULLIFIER_KEYS > MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX) { - throw new Error(`NULLIFIER_KEYS must be less than ${MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX}`); + if (variant.replacements.NULLIFIER_KEYS > MAX_KEY_VALIDATION_REQUESTS_PER_TX) { + throw new Error(`NULLIFIER_KEYS must be less than ${MAX_KEY_VALIDATION_REQUESTS_PER_TX}`); } } } diff --git a/yarn-project/circuits.js/src/structs/index.ts b/yarn-project/circuits.js/src/structs/index.ts index 9d763fe68ff..3603206f712 100644 --- a/yarn-project/circuits.js/src/structs/index.ts +++ b/yarn-project/circuits.js/src/structs/index.ts @@ -42,7 +42,7 @@ export * from './membership_witness.js'; export * from './non_existent_read_request_hints.js'; export * from './note_hash.js'; export * from './nullifier.js'; -export * from './nullifier_key_validation_request.js'; +export * from './key_validation_request.js'; export * from './parity/base_parity_inputs.js'; export * from './parity/parity_public_inputs.js'; export * from './parity/root_parity_input.js'; diff --git a/yarn-project/circuits.js/src/structs/kernel/private_kernel_reset_circuit_private_inputs.ts b/yarn-project/circuits.js/src/structs/kernel/private_kernel_reset_circuit_private_inputs.ts index 4c15cca12fa..a35c33f9730 100644 --- a/yarn-project/circuits.js/src/structs/kernel/private_kernel_reset_circuit_private_inputs.ts +++ b/yarn-project/circuits.js/src/structs/kernel/private_kernel_reset_circuit_private_inputs.ts @@ -10,8 +10,8 @@ import { NoteLogHash } from '../log_hash.js'; import { ScopedNoteHash } from '../note_hash.js'; import { ScopedNullifier } from '../nullifier.js'; import { + KeyValidationHint, type NoteHashReadRequestHints, - NullifierKeyHint, type NullifierReadRequestHints, noteHashReadRequestHintsFromBuffer, nullifierReadRequestHintsFromBuffer, @@ -44,7 +44,7 @@ export class PrivateKernelResetHints< NH_RR_SETTLED extends number, NLL_RR_PENDING extends number, NLL_RR_SETTLED extends number, - NLL_KEYS extends number, + KEY_VALIDATION_REQUESTS extends number, > { constructor( /** @@ -67,11 +67,10 @@ export class PrivateKernelResetHints< * Contains hints for the nullifier read requests to locate corresponding pending or settled nullifiers. */ public nullifierReadRequestHints: NullifierReadRequestHints, - /** - * The master nullifier secret keys for the nullifier key validation requests. + * Contains hints for key validation request. */ - public masterNullifierSecretKeys: Tuple, + public keyValidationHints: Tuple, ) {} toBuffer() { @@ -81,7 +80,7 @@ export class PrivateKernelResetHints< this.transientNoteHashIndexesForLogs, this.noteHashReadRequestHints, this.nullifierReadRequestHints, - this.masterNullifierSecretKeys, + this.keyValidationHints, ); } @@ -90,19 +89,19 @@ export class PrivateKernelResetHints< NEW_NH_RR_SETTLED extends number, NEW_NLL_RR_PENDING extends number, NEW_NLL_RR_SETTLED extends number, - NEW_NLL_KEYS extends number, + NEW_KEY_VALIDATION_REQUESTS extends number, >( numNoteHashReadRequestPending: NEW_NH_RR_PENDING, numNoteHashReadRequestSettled: NEW_NH_RR_SETTLED, numNullifierReadRequestPending: NEW_NLL_RR_PENDING, numNullifierReadRequestSettled: NEW_NLL_RR_SETTLED, - numNullifierKeys: NEW_NLL_KEYS, + numKeyValidationRequests: NEW_KEY_VALIDATION_REQUESTS, ): PrivateKernelResetHints< NEW_NH_RR_PENDING, NEW_NH_RR_SETTLED, NEW_NLL_RR_PENDING, NEW_NLL_RR_SETTLED, - NEW_NLL_KEYS + NEW_KEY_VALIDATION_REQUESTS > { return new PrivateKernelResetHints( this.transientNullifierIndexesForNoteHashes, @@ -110,7 +109,10 @@ export class PrivateKernelResetHints< this.transientNoteHashIndexesForLogs, this.noteHashReadRequestHints.trimToSizes(numNoteHashReadRequestPending, numNoteHashReadRequestSettled), this.nullifierReadRequestHints.trimToSizes(numNullifierReadRequestPending, numNullifierReadRequestSettled), - this.masterNullifierSecretKeys.slice(0, numNullifierKeys) as Tuple, + this.keyValidationHints.slice(0, numKeyValidationRequests) as Tuple< + KeyValidationHint, + NEW_KEY_VALIDATION_REQUESTS + >, ); } /** @@ -123,15 +125,15 @@ export class PrivateKernelResetHints< NH_RR_SETTLED extends number, NLL_RR_PENDING extends number, NLL_RR_SETTLED extends number, - NLL_KEYS extends number, + KEY_VALIDATION_REQUESTS extends number, >( buffer: Buffer | BufferReader, numNoteHashReadRequestPending: NH_RR_PENDING, numNoteHashReadRequestSettled: NH_RR_SETTLED, numNullifierReadRequestPending: NLL_RR_PENDING, numNullifierReadRequestSettled: NLL_RR_SETTLED, - numNullifierKeys: NLL_KEYS, - ): PrivateKernelResetHints { + numNullifierKeys: KEY_VALIDATION_REQUESTS, + ): PrivateKernelResetHints { const reader = BufferReader.asReader(buffer); return new PrivateKernelResetHints( reader.readNumbers(MAX_NEW_NOTE_HASHES_PER_TX), @@ -145,7 +147,7 @@ export class PrivateKernelResetHints< fromBuffer: buf => nullifierReadRequestHintsFromBuffer(buf, numNullifierReadRequestPending, numNullifierReadRequestSettled), }), - reader.readArray(numNullifierKeys, NullifierKeyHint), + reader.readArray(numNullifierKeys, KeyValidationHint), ); } } @@ -158,7 +160,7 @@ export class PrivateKernelResetCircuitPrivateInputs< NH_RR_SETTLED extends number, NLL_RR_PENDING extends number, NLL_RR_SETTLED extends number, - NLL_KEYS extends number, + KEY_VALIDATION_REQUESTS extends number, TAG extends string, > { constructor( @@ -167,7 +169,13 @@ export class PrivateKernelResetCircuitPrivateInputs< */ public previousKernel: PrivateKernelData, public outputs: PrivateKernelResetOutputs, - public hints: PrivateKernelResetHints, + public hints: PrivateKernelResetHints< + NH_RR_PENDING, + NH_RR_SETTLED, + NLL_RR_PENDING, + NLL_RR_SETTLED, + KEY_VALIDATION_REQUESTS + >, public sizeTag: TAG, ) {} @@ -193,7 +201,7 @@ export class PrivateKernelResetCircuitPrivateInputs< NH_RR_SETTLED extends number, NLL_RR_PENDING extends number, NLL_RR_SETTLED extends number, - NLL_KEYS extends number, + KEY_VALIDATION_REQUESTS extends number, TAG extends string, >( buffer: Buffer | BufferReader, @@ -201,14 +209,14 @@ export class PrivateKernelResetCircuitPrivateInputs< numNoteHashReadRequestSettled: NH_RR_SETTLED, numNullifierReadRequestPending: NLL_RR_PENDING, numNullifierReadRequestSettled: NLL_RR_SETTLED, - numNullifierKeys: NLL_KEYS, + numNullifierKeys: KEY_VALIDATION_REQUESTS, sizeTag: TAG, ): PrivateKernelResetCircuitPrivateInputs< NH_RR_PENDING, NH_RR_SETTLED, NLL_RR_PENDING, NLL_RR_SETTLED, - NLL_KEYS, + KEY_VALIDATION_REQUESTS, TAG > { const reader = BufferReader.asReader(buffer); diff --git a/yarn-project/circuits.js/src/structs/key_validation_request.ts b/yarn-project/circuits.js/src/structs/key_validation_request.ts new file mode 100644 index 00000000000..b76d7e1cf7f --- /dev/null +++ b/yarn-project/circuits.js/src/structs/key_validation_request.ts @@ -0,0 +1,92 @@ +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { Fr, Point } from '@aztec/foundation/fields'; +import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; + +import { KEY_VALIDATION_REQUEST_LENGTH, SCOPED_KEY_VALIDATION_REQUEST_LENGTH } from '../constants.gen.js'; + +/** + * Request for validating keys used in the app. + */ +export class KeyValidationRequest { + constructor( + /** + * Master public key (pk_m) corresponding to the same underlying secret as app secret key bellow. + */ + public readonly masterPublicKey: Point, + /** + * App-siloed secret key (sk_app) corresponding to the same underlying secret as master public key above. + */ + public readonly appSecretKey: Fr, + ) {} + + toBuffer() { + return serializeToBuffer(this.masterPublicKey, this.appSecretKey); + } + + static fromBuffer(buffer: Buffer | BufferReader) { + const reader = BufferReader.asReader(buffer); + return new KeyValidationRequest(Point.fromBuffer(reader), Fr.fromBuffer(reader)); + } + + toFields(): Fr[] { + const fields = [this.masterPublicKey.toFields(), this.appSecretKey].flat(); + if (fields.length !== KEY_VALIDATION_REQUEST_LENGTH) { + throw new Error( + `Invalid number of fields for KeyValidationRequest. Expected ${KEY_VALIDATION_REQUEST_LENGTH}, got ${fields.length}`, + ); + } + return fields; + } + + static fromFields(fields: Fr[] | FieldReader): KeyValidationRequest { + const reader = FieldReader.asReader(fields); + return new KeyValidationRequest(Point.fromFields(reader), reader.readField()); + } + + isEmpty() { + return this.masterPublicKey.isZero() && this.appSecretKey.isZero(); + } + + static empty() { + return new KeyValidationRequest(Point.ZERO, Fr.ZERO); + } +} + +/** + * Request for validating keys used in the app. + */ +export class ScopedKeyValidationRequest { + constructor(public readonly request: KeyValidationRequest, public readonly contractAddress: AztecAddress) {} + + toBuffer() { + return serializeToBuffer(this.request, this.contractAddress); + } + + static fromBuffer(buffer: Buffer | BufferReader) { + const reader = BufferReader.asReader(buffer); + return new ScopedKeyValidationRequest(KeyValidationRequest.fromBuffer(reader), AztecAddress.fromBuffer(reader)); + } + + toFields(): Fr[] { + const fields = [...this.request.toFields(), this.contractAddress]; + if (fields.length !== SCOPED_KEY_VALIDATION_REQUEST_LENGTH) { + throw new Error( + `Invalid number of fields for ScopedKeyValidationRequest. Expected ${SCOPED_KEY_VALIDATION_REQUEST_LENGTH}, got ${fields.length}`, + ); + } + return fields; + } + + static fromFields(fields: Fr[] | FieldReader): ScopedKeyValidationRequest { + const reader = FieldReader.asReader(fields); + return new ScopedKeyValidationRequest(KeyValidationRequest.fromFields(reader), AztecAddress.fromFields(reader)); + } + + isEmpty() { + return this.request.isEmpty() && this.contractAddress.isZero(); + } + + static empty() { + return new ScopedKeyValidationRequest(KeyValidationRequest.empty(), AztecAddress.ZERO); + } +} diff --git a/yarn-project/circuits.js/src/structs/nullifier_key_validation_request.ts b/yarn-project/circuits.js/src/structs/nullifier_key_validation_request.ts deleted file mode 100644 index 2d1fa9813b5..00000000000 --- a/yarn-project/circuits.js/src/structs/nullifier_key_validation_request.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { AztecAddress } from '@aztec/foundation/aztec-address'; -import { Fr, Point } from '@aztec/foundation/fields'; -import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; - -import { - NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH, - SCOPED_NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH, -} from '../constants.gen.js'; - -/** - * Request for validating a nullifier key pair used in the app. - */ -export class NullifierKeyValidationRequest { - constructor( - /** - * Public key of the nullifier key (Npk_m). - */ - public readonly masterNullifierPublicKey: Point, - /** - * App-siloed nullifier secret key (nsk_app*). - */ - public readonly appNullifierSecretKey: Fr, - ) {} - - toBuffer() { - return serializeToBuffer(this.masterNullifierPublicKey, this.appNullifierSecretKey); - } - - static fromBuffer(buffer: Buffer | BufferReader) { - const reader = BufferReader.asReader(buffer); - return new NullifierKeyValidationRequest(Point.fromBuffer(reader), Fr.fromBuffer(reader)); - } - - toFields(): Fr[] { - const fields = [this.masterNullifierPublicKey.toFields(), this.appNullifierSecretKey].flat(); - if (fields.length !== NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH) { - throw new Error( - `Invalid number of fields for NullifierKeyValidationRequest. Expected ${NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH}, got ${fields.length}`, - ); - } - return fields; - } - - static fromFields(fields: Fr[] | FieldReader): NullifierKeyValidationRequest { - const reader = FieldReader.asReader(fields); - return new NullifierKeyValidationRequest(Point.fromFields(reader), reader.readField()); - } - - isEmpty() { - return this.masterNullifierPublicKey.isZero() && this.appNullifierSecretKey.isZero(); - } - - static empty() { - return new NullifierKeyValidationRequest(Point.ZERO, Fr.ZERO); - } -} - -/** - * Request for validating a nullifier key pair used in the app. - */ -export class ScopedNullifierKeyValidationRequest { - constructor(public readonly request: NullifierKeyValidationRequest, public readonly contractAddress: AztecAddress) {} - - toBuffer() { - return serializeToBuffer(this.request, this.contractAddress); - } - - static fromBuffer(buffer: Buffer | BufferReader) { - const reader = BufferReader.asReader(buffer); - return new ScopedNullifierKeyValidationRequest( - NullifierKeyValidationRequest.fromBuffer(reader), - AztecAddress.fromBuffer(reader), - ); - } - - toFields(): Fr[] { - const fields = [...this.request.toFields(), this.contractAddress]; - if (fields.length !== SCOPED_NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH) { - throw new Error( - `Invalid number of fields for ScopedNullifierKeyValidationRequest. Expected ${SCOPED_NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH}, got ${fields.length}`, - ); - } - return fields; - } - - static fromFields(fields: Fr[] | FieldReader): ScopedNullifierKeyValidationRequest { - const reader = FieldReader.asReader(fields); - return new ScopedNullifierKeyValidationRequest( - NullifierKeyValidationRequest.fromFields(reader), - AztecAddress.fromFields(reader), - ); - } - - isEmpty() { - return this.request.isEmpty() && this.contractAddress.isZero(); - } - - static empty() { - return new ScopedNullifierKeyValidationRequest(NullifierKeyValidationRequest.empty(), AztecAddress.ZERO); - } -} diff --git a/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts b/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts index 4f59fd5d599..1495944a878 100644 --- a/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts +++ b/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts @@ -14,12 +14,12 @@ import { type FieldsOf } from '@aztec/foundation/types'; import { GeneratorIndex, MAX_ENCRYPTED_LOGS_PER_CALL, + MAX_KEY_VALIDATION_REQUESTS_PER_CALL, MAX_NEW_L2_TO_L1_MSGS_PER_CALL, MAX_NEW_NOTE_HASHES_PER_CALL, MAX_NEW_NULLIFIERS_PER_CALL, MAX_NOTE_ENCRYPTED_LOGS_PER_CALL, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, - MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, MAX_NULLIFIER_READ_REQUESTS_PER_CALL, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, @@ -29,12 +29,12 @@ import { import { Header } from '../structs/header.js'; import { isEmptyArray } from '../utils/index.js'; import { CallContext } from './call_context.js'; +import { KeyValidationRequest } from './key_validation_request.js'; import { L2ToL1Message } from './l2_to_l1_message.js'; import { LogHash, NoteLogHash } from './log_hash.js'; import { MaxBlockNumber } from './max_block_number.js'; import { NoteHash } from './note_hash.js'; import { Nullifier } from './nullifier.js'; -import { NullifierKeyValidationRequest } from './nullifier_key_validation_request.js'; import { PrivateCallRequest } from './private_call_request.js'; import { ReadRequest } from './read_request.js'; import { TxContext } from './tx_context.js'; @@ -77,12 +77,9 @@ export class PrivateCircuitPublicInputs { */ public nullifierReadRequests: Tuple, /** - * Nullifier key validation requests created by the corresponding function call. + * Key validation requests created by the corresponding function call. */ - public nullifierKeyValidationRequests: Tuple< - NullifierKeyValidationRequest, - typeof MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL - >, + public keyValidationRequests: Tuple, /** * New note hashes created by the corresponding function call. */ @@ -169,7 +166,7 @@ export class PrivateCircuitPublicInputs { reader.readObject(MaxBlockNumber), reader.readArray(MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, ReadRequest), reader.readArray(MAX_NULLIFIER_READ_REQUESTS_PER_CALL, ReadRequest), - reader.readArray(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, NullifierKeyValidationRequest), + reader.readArray(MAX_KEY_VALIDATION_REQUESTS_PER_CALL, KeyValidationRequest), reader.readArray(MAX_NEW_NOTE_HASHES_PER_CALL, NoteHash), reader.readArray(MAX_NEW_NULLIFIERS_PER_CALL, Nullifier), reader.readArray(MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, PrivateCallRequest), @@ -197,7 +194,7 @@ export class PrivateCircuitPublicInputs { reader.readObject(MaxBlockNumber), reader.readArray(MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, ReadRequest), reader.readArray(MAX_NULLIFIER_READ_REQUESTS_PER_CALL, ReadRequest), - reader.readArray(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, NullifierKeyValidationRequest), + reader.readArray(MAX_KEY_VALIDATION_REQUESTS_PER_CALL, KeyValidationRequest), reader.readArray(MAX_NEW_NOTE_HASHES_PER_CALL, NoteHash), reader.readArray(MAX_NEW_NULLIFIERS_PER_CALL, Nullifier), reader.readArray(MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, PrivateCallRequest), @@ -228,7 +225,7 @@ export class PrivateCircuitPublicInputs { MaxBlockNumber.empty(), makeTuple(MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, ReadRequest.empty), makeTuple(MAX_NULLIFIER_READ_REQUESTS_PER_CALL, ReadRequest.empty), - makeTuple(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, NullifierKeyValidationRequest.empty), + makeTuple(MAX_KEY_VALIDATION_REQUESTS_PER_CALL, KeyValidationRequest.empty), makeTuple(MAX_NEW_NOTE_HASHES_PER_CALL, NoteHash.empty), makeTuple(MAX_NEW_NULLIFIERS_PER_CALL, Nullifier.empty), makeTuple(MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, PrivateCallRequest.empty), @@ -256,7 +253,7 @@ export class PrivateCircuitPublicInputs { this.maxBlockNumber.isEmpty() && isEmptyArray(this.noteHashReadRequests) && isEmptyArray(this.nullifierReadRequests) && - isEmptyArray(this.nullifierKeyValidationRequests) && + isEmptyArray(this.keyValidationRequests) && isEmptyArray(this.newNoteHashes) && isEmptyArray(this.newNullifiers) && isEmptyArray(this.privateCallRequests) && @@ -286,7 +283,7 @@ export class PrivateCircuitPublicInputs { fields.maxBlockNumber, fields.noteHashReadRequests, fields.nullifierReadRequests, - fields.nullifierKeyValidationRequests, + fields.keyValidationRequests, fields.newNoteHashes, fields.newNullifiers, fields.privateCallRequests, diff --git a/yarn-project/circuits.js/src/structs/read_request_hints/index.ts b/yarn-project/circuits.js/src/structs/read_request_hints/index.ts index c140118984c..6f64a279458 100644 --- a/yarn-project/circuits.js/src/structs/read_request_hints/index.ts +++ b/yarn-project/circuits.js/src/structs/read_request_hints/index.ts @@ -1,4 +1,4 @@ export * from './note_hash_read_request_hints.js'; export * from './nullifier_read_request_hints.js'; export * from './read_request_hints.js'; -export * from './nullifier_key_hint.js'; +export * from './key_validation_hint.js'; diff --git a/yarn-project/circuits.js/src/structs/read_request_hints/key_validation_hint.ts b/yarn-project/circuits.js/src/structs/read_request_hints/key_validation_hint.ts new file mode 100644 index 00000000000..92656cf2b6c --- /dev/null +++ b/yarn-project/circuits.js/src/structs/read_request_hints/key_validation_hint.ts @@ -0,0 +1,31 @@ +import { Fr } from '@aztec/foundation/fields'; +import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; + +import { GrumpkinPrivateKey } from '../../types/grumpkin_private_key.js'; + +export class KeyValidationHint { + constructor( + /** Master secret key used to derive sk_app and pk_m. */ + public skM: GrumpkinPrivateKey, + /** + * Generator index used to generate sk_app. + * Note: Ideally KeyGenerator type would be used here but then we would have incompatibility with the empty method. + */ + public skAppGeneratorIndex: Fr, + /** Index of the request in the array of hints. */ + public requestIndex: number, + ) {} + + static fromBuffer(buffer: Buffer | BufferReader) { + const reader = BufferReader.asReader(buffer); + return new KeyValidationHint(reader.readObject(GrumpkinPrivateKey), reader.readObject(Fr), reader.readNumber()); + } + + toBuffer() { + return serializeToBuffer(this.skM, this.skAppGeneratorIndex, this.requestIndex); + } + + static empty() { + return new KeyValidationHint(GrumpkinPrivateKey.zero(), Fr.ZERO, 0); + } +} diff --git a/yarn-project/circuits.js/src/structs/read_request_hints/nullifier_key_hint.ts b/yarn-project/circuits.js/src/structs/read_request_hints/nullifier_key_hint.ts deleted file mode 100644 index cb9d55f81e4..00000000000 --- a/yarn-project/circuits.js/src/structs/read_request_hints/nullifier_key_hint.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; - -import { GrumpkinPrivateKey } from '../../types/grumpkin_private_key.js'; - -export class NullifierKeyHint { - constructor(public privateKey: GrumpkinPrivateKey, public requestIndex: number) {} - - static fromBuffer(buffer: Buffer | BufferReader) { - const reader = BufferReader.asReader(buffer); - return new NullifierKeyHint(reader.readObject(GrumpkinPrivateKey), reader.readNumber()); - } - - toBuffer() { - return serializeToBuffer(this.privateKey, this.requestIndex); - } - - static empty() { - return new NullifierKeyHint(GrumpkinPrivateKey.zero(), 0); - } -} diff --git a/yarn-project/circuits.js/src/structs/validation_requests.ts b/yarn-project/circuits.js/src/structs/validation_requests.ts index 6d33a5b5865..23b99426104 100644 --- a/yarn-project/circuits.js/src/structs/validation_requests.ts +++ b/yarn-project/circuits.js/src/structs/validation_requests.ts @@ -4,13 +4,13 @@ import { BufferReader, type Tuple, serializeToBuffer } from '@aztec/foundation/s import { inspect } from 'util'; import { + MAX_KEY_VALIDATION_REQUESTS_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, - MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_PUBLIC_DATA_READS_PER_TX, } from '../constants.gen.js'; -import { ScopedNullifierKeyValidationRequest } from './nullifier_key_validation_request.js'; +import { ScopedKeyValidationRequest } from './key_validation_request.js'; import { PublicDataRead } from './public_data_read_request.js'; import { ScopedReadRequest } from './read_request.js'; import { RollupValidationRequests } from './rollup_validation_requests.js'; @@ -41,12 +41,9 @@ export class ValidationRequests { typeof MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX >, /** - * All the nullifier key validation requests made in this transaction. + * All the key validation requests made in this transaction. */ - public nullifierKeyValidationRequests: Tuple< - ScopedNullifierKeyValidationRequest, - typeof MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX - >, + public keyValidationRequests: Tuple, /** * All the public data reads made in this transaction. */ @@ -59,7 +56,7 @@ export class ValidationRequests { this.noteHashReadRequests, this.nullifierReadRequests, this.nullifierNonExistentReadRequests, - this.nullifierKeyValidationRequests, + this.keyValidationRequests, this.publicDataReads, ); } @@ -80,7 +77,7 @@ export class ValidationRequests { reader.readArray(MAX_NOTE_HASH_READ_REQUESTS_PER_TX, ScopedReadRequest), reader.readArray(MAX_NULLIFIER_READ_REQUESTS_PER_TX, ScopedReadRequest), reader.readArray(MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX, ScopedReadRequest), - reader.readArray(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, ScopedNullifierKeyValidationRequest), + reader.readArray(MAX_KEY_VALIDATION_REQUESTS_PER_TX, ScopedKeyValidationRequest), reader.readArray(MAX_PUBLIC_DATA_READS_PER_TX, PublicDataRead), ); } @@ -100,7 +97,7 @@ export class ValidationRequests { makeTuple(MAX_NOTE_HASH_READ_REQUESTS_PER_TX, ScopedReadRequest.empty), makeTuple(MAX_NULLIFIER_READ_REQUESTS_PER_TX, ScopedReadRequest.empty), makeTuple(MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX, ScopedReadRequest.empty), - makeTuple(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, ScopedNullifierKeyValidationRequest.empty), + makeTuple(MAX_KEY_VALIDATION_REQUESTS_PER_TX, ScopedKeyValidationRequest.empty), makeTuple(MAX_PUBLIC_DATA_READS_PER_TX, PublicDataRead.empty), ); } @@ -120,7 +117,7 @@ export class ValidationRequests { .filter(x => !x.isEmpty()) .map(h => inspect(h)) .join(', ')}], - nullifierKeyValidationRequests: [${this.nullifierKeyValidationRequests + keyValidationRequests: [${this.keyValidationRequests .filter(x => !x.isEmpty()) .map(h => inspect(h)) .join(', ')}], diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index b287c870912..336963a8fee 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -34,11 +34,14 @@ import { G1AffineElement, type GrumpkinPrivateKey, GrumpkinScalar, + KeyValidationRequest, L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH, L2ToL1Message, LogHash, MAX_ENCRYPTED_LOGS_PER_CALL, MAX_ENCRYPTED_LOGS_PER_TX, + MAX_KEY_VALIDATION_REQUESTS_PER_CALL, + MAX_KEY_VALIDATION_REQUESTS_PER_TX, MAX_NEW_L2_TO_L1_MSGS_PER_CALL, MAX_NEW_L2_TO_L1_MSGS_PER_TX, MAX_NEW_NOTE_HASHES_PER_CALL, @@ -49,8 +52,6 @@ import { MAX_NOTE_ENCRYPTED_LOGS_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, - MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, - MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL, MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_CALL, @@ -78,7 +79,6 @@ import { NoteHash, NoteLogHash, Nullifier, - NullifierKeyValidationRequest, NullifierLeafPreimage, NullifierNonExistentReadRequestHintsBuilder, NullifierReadRequestHintsBuilder, @@ -119,7 +119,7 @@ import { RootParityInputs, RootRollupInputs, RootRollupPublicInputs, - ScopedNullifierKeyValidationRequest, + ScopedKeyValidationRequest, ScopedReadRequest, StateDiffHints, StateReference, @@ -209,16 +209,16 @@ function makeScopedReadRequest(n: number): ScopedReadRequest { } /** - * Creates arbitrary NullifierKeyValidationRequest from the given seed. - * @param seed - The seed to use for generating the NullifierKeyValidationRequest. - * @returns A NullifierKeyValidationRequest. + * Creates arbitrary KeyValidationRequest from the given seed. + * @param seed - The seed to use for generating the KeyValidationRequest. + * @returns A KeyValidationRequest. */ -function makeNullifierKeyValidationRequest(seed: number): NullifierKeyValidationRequest { - return new NullifierKeyValidationRequest(makePoint(seed), fr(seed + 2)); +function makeKeyValidationRequests(seed: number): KeyValidationRequest { + return new KeyValidationRequest(makePoint(seed), fr(seed + 2)); } -function makeScopedNullifierKeyValidationRequest(seed: number): ScopedNullifierKeyValidationRequest { - return new ScopedNullifierKeyValidationRequest(makeNullifierKeyValidationRequest(seed), makeAztecAddress(seed + 4)); +function makeScopedKeyValidationRequests(seed: number): ScopedKeyValidationRequest { + return new ScopedKeyValidationRequest(makeKeyValidationRequests(seed), makeAztecAddress(seed + 4)); } /** @@ -279,7 +279,7 @@ export function makeValidationRequests(seed = 1) { makeTuple(MAX_NOTE_HASH_READ_REQUESTS_PER_TX, makeScopedReadRequest, seed + 0x80), makeTuple(MAX_NULLIFIER_READ_REQUESTS_PER_TX, makeScopedReadRequest, seed + 0x90), makeTuple(MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX, makeScopedReadRequest, seed + 0x95), - makeTuple(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, makeScopedNullifierKeyValidationRequest, seed + 0x100), + makeTuple(MAX_KEY_VALIDATION_REQUESTS_PER_TX, makeScopedKeyValidationRequests, seed + 0x100), makeTuple(MAX_PUBLIC_DATA_READS_PER_TX, makePublicDataRead, seed + 0xe00), ); } @@ -759,11 +759,7 @@ export function makePrivateCircuitPublicInputs(seed = 0): PrivateCircuitPublicIn minRevertibleSideEffectCounter: fr(0), noteHashReadRequests: makeTuple(MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, makeReadRequest, seed + 0x300), nullifierReadRequests: makeTuple(MAX_NULLIFIER_READ_REQUESTS_PER_CALL, makeReadRequest, seed + 0x310), - nullifierKeyValidationRequests: makeTuple( - MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, - makeNullifierKeyValidationRequest, - seed + 0x320, - ), + keyValidationRequests: makeTuple(MAX_KEY_VALIDATION_REQUESTS_PER_CALL, makeKeyValidationRequests, seed + 0x320), newNoteHashes: makeTuple(MAX_NEW_NOTE_HASHES_PER_CALL, makeNoteHash, seed + 0x400), newNullifiers: makeTuple(MAX_NEW_NULLIFIERS_PER_CALL, makeNullifier, seed + 0x500), privateCallRequests: makeTuple(MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, makePrivateCallRequest, seed + 0x600), diff --git a/yarn-project/end-to-end/Earthfile b/yarn-project/end-to-end/Earthfile index f3e80c9d51e..e7a65e3e0a7 100644 --- a/yarn-project/end-to-end/Earthfile +++ b/yarn-project/end-to-end/Earthfile @@ -94,6 +94,9 @@ e2e-escrow-contract: e2e-key-registry: DO +E2E_TEST --test=./src/e2e_key_registry.test.ts +e2e-keys: + DO +E2E_TEST --test=./src/e2e_keys.test.ts + e2e-lending-contract: DO +E2E_TEST --test=./src/e2e_lending_contract.test.ts diff --git a/yarn-project/end-to-end/src/e2e_key_registry.test.ts b/yarn-project/end-to-end/src/e2e_key_registry.test.ts index 8f482b6c075..5a21e4d70bb 100644 --- a/yarn-project/end-to-end/src/e2e_key_registry.test.ts +++ b/yarn-project/end-to-end/src/e2e_key_registry.test.ts @@ -1,16 +1,5 @@ -import { createAccounts } from '@aztec/accounts/testing'; -import { type AccountWallet, AztecAddress, type AztecNode, Fr, type L2Block, type PXE } from '@aztec/aztec.js'; -import { - CompleteAddress, - GeneratorIndex, - INITIAL_L2_BLOCK_NUM, - Point, - PublicKeys, - computeAppNullifierSecretKey, - deriveMasterNullifierSecretKey, -} from '@aztec/circuits.js'; -import { siloNullifier } from '@aztec/circuits.js/hash'; -import { poseidon2Hash } from '@aztec/foundation/crypto'; +import { type AccountWallet, AztecAddress, Fr, type PXE } from '@aztec/aztec.js'; +import { CompleteAddress, Point, PublicKeys } from '@aztec/circuits.js'; import { KeyRegistryContract, TestContract } from '@aztec/noir-contracts.js'; import { getCanonicalKeyRegistryAddress } from '@aztec/protocol-contracts/key-registry'; @@ -26,7 +15,6 @@ describe('Key Registry', () => { let keyRegistry: KeyRegistryContract; let pxe: PXE; - let aztecNode: AztecNode; let testContract: TestContract; jest.setTimeout(TIMEOUT); @@ -37,7 +25,7 @@ describe('Key Registry', () => { const account = CompleteAddress.random(); beforeAll(async () => { - ({ aztecNode, teardown, pxe, wallets } = await setup(2)); + ({ teardown, pxe, wallets } = await setup(2)); keyRegistry = await KeyRegistryContract.at(getCanonicalKeyRegistryAddress(), wallets[0]); testContract = await TestContract.deploy(wallets[0]).send().deployed(); @@ -226,53 +214,4 @@ describe('Key Registry', () => { .wait(); }); }); - - describe('using nsk_app to detect nullification', () => { - // This test checks that it possible to detect that a note has been nullified just by using nsk_app. Note that this - // only works for non-transient note as transient notes never emit a note hash which makes it impossible to brute - // force their nullifier. This makes this scheme a bit useless in practice. - it('nsk_app and contract address are enough to detect note nullification', async () => { - const secret = Fr.random(); - const [account] = await createAccounts(pxe, 1, [secret]); - - const masterNullifierSecretKey = deriveMasterNullifierSecretKey(secret); - const nskApp = computeAppNullifierSecretKey(masterNullifierSecretKey, testContract.address); - - const noteValue = 5; - const noteOwner = account.getAddress(); - const noteStorageSlot = 12; - - await testContract.methods.call_create_note(noteValue, noteOwner, noteStorageSlot).send().wait(); - - expect(await getNumNullifiedNotes(nskApp, testContract.address)).toEqual(0); - - await testContract.methods.call_destroy_note(noteStorageSlot).send().wait(); - - expect(await getNumNullifiedNotes(nskApp, testContract.address)).toEqual(1); - }); - - const getNumNullifiedNotes = async (nskApp: Fr, contractAddress: AztecAddress) => { - // 1. Get all the note hashes - const blocks = await aztecNode.getBlocks(INITIAL_L2_BLOCK_NUM, 1000); - const noteHashes = blocks.flatMap((block: L2Block) => - block.body.txEffects.flatMap(txEffect => txEffect.noteHashes), - ); - // 2. Get all the seen nullifiers - const nullifiers = blocks.flatMap((block: L2Block) => - block.body.txEffects.flatMap(txEffect => txEffect.nullifiers), - ); - // 3. Derive all the possible nullifiers using nskApp - const derivedNullifiers = noteHashes.map(noteHash => { - const innerNullifier = poseidon2Hash([noteHash, nskApp, GeneratorIndex.NOTE_NULLIFIER]); - return siloNullifier(contractAddress, innerNullifier); - }); - // 4. Count the number of derived nullifiers that are in the nullifiers array - return derivedNullifiers.reduce((count, derived) => { - if (nullifiers.some(nullifier => nullifier.equals(derived))) { - count++; - } - return count; - }, 0); - }; - }); }); diff --git a/yarn-project/end-to-end/src/e2e_keys.test.ts b/yarn-project/end-to-end/src/e2e_keys.test.ts new file mode 100644 index 00000000000..41ae31de443 --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_keys.test.ts @@ -0,0 +1,128 @@ +import { createAccounts } from '@aztec/accounts/testing'; +import { + type AccountWallet, + type AztecAddress, + type AztecNode, + Fr, + type L2Block, + type PXE, + type Wallet, +} from '@aztec/aztec.js'; +import { + GeneratorIndex, + INITIAL_L2_BLOCK_NUM, + computeAppNullifierSecretKey, + computeAppSecretKey, + deriveMasterNullifierSecretKey, + deriveMasterOutgoingViewingSecretKey, + derivePublicKeyFromSecretKey, +} from '@aztec/circuits.js'; +import { siloNullifier } from '@aztec/circuits.js/hash'; +import { poseidon2Hash } from '@aztec/foundation/crypto'; +import { TestContract } from '@aztec/noir-contracts.js'; + +import { jest } from '@jest/globals'; + +import { setup } from './fixtures/utils.js'; + +const TIMEOUT = 120_000; + +describe('Key Registry', () => { + jest.setTimeout(TIMEOUT); + + let aztecNode: AztecNode; + let pxe: PXE; + let teardown: () => Promise; + + let testContract: TestContract; + + const secret = Fr.random(); + let account: AccountWallet; + + beforeAll(async () => { + let wallet: Wallet; + ({ aztecNode, pxe, teardown, wallet } = await setup(2)); + testContract = await TestContract.deploy(wallet).send().deployed(); + + [account] = await createAccounts(pxe, 1, [secret]); + }); + + afterAll(() => teardown()); + + describe('using nsk_app to detect nullification', () => { + // This test checks that it possible to detect that a note has been nullified just by using nsk_app. Note that + // this only works for non-transient notes as transient ones never emit a note hash which makes it impossible + // to brute force their nullifier. + // This might seem to make the scheme useless in practice. This could not be the case because if you have + // a note of funds, when you create the transient you are nullifying that note. So even if I cannot see when you + // nullified the transient ones, I can see that you nullified the first. + // + // E.g.: Say you have a note A, which is 10 $, you nullify it (I can see) and create B and C, that you then spend. + // I cannot see B and C, but I saw A, so I knew that you did something with those funds. + // + // There are some examples where the action is fully hidden though. One of those examples is shielding where you + // instantly consume the note after creating it. In this case, the nullifier is never emitted and hence the action + // is impossible to detect with this scheme. + // Another example is a withdraw is withdrawing from DeFi and then immediately spending the funds. In this case, + // we would need nsk_app and the contract address of the DeFi contract to detect the nullification of the initial + // note. + it('nsk_app and contract address are enough to detect note nullification', async () => { + const masterNullifierSecretKey = deriveMasterNullifierSecretKey(secret); + const nskApp = computeAppNullifierSecretKey(masterNullifierSecretKey, testContract.address); + + const noteValue = 5; + const noteOwner = account.getAddress(); + const noteStorageSlot = 12; + + await testContract.methods.call_create_note(noteValue, noteOwner, noteStorageSlot).send().wait(); + + expect(await getNumNullifiedNotes(nskApp, testContract.address)).toEqual(0); + + await testContract.methods.call_destroy_note(noteStorageSlot).send().wait(); + + expect(await getNumNullifiedNotes(nskApp, testContract.address)).toEqual(1); + }); + + const getNumNullifiedNotes = async (nskApp: Fr, contractAddress: AztecAddress) => { + // 1. Get all the note hashes + const blocks = await aztecNode.getBlocks(INITIAL_L2_BLOCK_NUM, 1000); + const noteHashes = blocks.flatMap((block: L2Block) => + block.body.txEffects.flatMap(txEffect => txEffect.noteHashes), + ); + // 2. Get all the seen nullifiers + const nullifiers = blocks.flatMap((block: L2Block) => + block.body.txEffects.flatMap(txEffect => txEffect.nullifiers), + ); + // 3. Derive all the possible nullifiers using nskApp + const derivedNullifiers = noteHashes.map(noteHash => { + const innerNullifier = poseidon2Hash([noteHash, nskApp, GeneratorIndex.NOTE_NULLIFIER]); + return siloNullifier(contractAddress, innerNullifier); + }); + // 4. Count the number of derived nullifiers that are in the nullifiers array + return derivedNullifiers.reduce((count, derived) => { + if (nullifiers.some(nullifier => nullifier.equals(derived))) { + count++; + } + return count; + }, 0); + }; + }); + + describe('ovsk_app', () => { + it('gets ovsk_app', async () => { + // Derive the ovpk_m_hash from the account secret + const ovskM = deriveMasterOutgoingViewingSecretKey(secret); + const ovpkMHash = derivePublicKeyFromSecretKey(ovskM).hash(); + + // Compute the expected ovsk_app + const expectedOvskApp = computeAppSecretKey(ovskM, testContract.address, 'ov'); + + // Get the ovsk_app via the test contract + const ovskAppBigInt = await testContract.methods.get_ovsk_app(ovpkMHash).simulate(); + const ovskApp = new Fr(ovskAppBigInt); + + // Check that the ovsk_app is as expected + expect(ovskApp).toEqual(expectedOvskApp); + }); + }); +}); diff --git a/yarn-project/key-store/src/test_key_store.test.ts b/yarn-project/key-store/src/test_key_store.test.ts index 1dde6a3f02b..436b71771e5 100644 --- a/yarn-project/key-store/src/test_key_store.test.ts +++ b/yarn-project/key-store/src/test_key_store.test.ts @@ -2,6 +2,7 @@ import { AztecAddress, Fq, Fr, + GeneratorIndex, computeAppNullifierSecretKey, deriveKeys, derivePublicKeyFromSecretKey, @@ -27,7 +28,10 @@ describe('TestKeyStore', () => { `"0x1a8a9a1d91cbb353d8df4f1bbfd0283f7fc63766f671edd9443a1270a7b2a954"`, ); - const masterNullifierPublicKey = await keyStore.getMasterNullifierPublicKey(computedMasterNullifierPublicKeyHash); + const { masterPublicKey: masterNullifierPublicKey } = await keyStore.getKeyValidationRequest( + computedMasterNullifierPublicKeyHash, + AztecAddress.random(), // Address is random because we are not interested in the app secret key here + ); expect(masterNullifierPublicKey.toString()).toMatchInlineSnapshot( `"0x2ef5d15dd65d29546680ab72846fb071f41cb9f2a0212215e6c560e29df4ff650ce764818364b376be92dc2f49577fe440e64a16012584f7c4ee94f7edbc323a"`, ); @@ -47,21 +51,15 @@ describe('TestKeyStore', () => { `"0x076429010fdebfa522b053267f654a4c5daf18589915d96f7e5001d63ea2033f27f915f254560c84450aa38e93c3162be52492d05b316e75f542e3b302117360"`, ); - const publicKeysHash = await keyStore.getPublicKeysHash(accountAddress); - expect(publicKeysHash.toString()).toMatchInlineSnapshot( - `"0x1ba15945655812587b5c16a6a8125193c901c2c31a4ac4edaed202726c0d4c89"`, - ); - // Arbitrary app contract address const appAddress = AztecAddress.fromBigInt(624n); - const appNullifierSecretKey = await keyStore.getAppNullifierSecretKey( - computedMasterNullifierPublicKeyHash, - appAddress, - ); + const { masterPublicKey: obtainedMasterNullifierPublicKey, appSecretKey: appNullifierSecretKey } = + await keyStore.getKeyValidationRequest(computedMasterNullifierPublicKeyHash, appAddress); expect(appNullifierSecretKey.toString()).toMatchInlineSnapshot( `"0x230a44dfe7cfec7a735c89f7289c5cb5d2c3dc0bf5d3505917fd2476f67873a8"`, ); + expect(obtainedMasterNullifierPublicKey).toEqual(masterNullifierPublicKey); const appIncomingViewingSecretKey = await keyStore.getAppIncomingViewingSecretKey(accountAddress, appAddress); expect(appIncomingViewingSecretKey.toString()).toMatchInlineSnapshot( @@ -80,10 +78,22 @@ describe('TestKeyStore', () => { ); // Manages to find master nullifer secret key for pub key - const masterNullifierSecretKey = await keyStore.getMasterNullifierSecretKeyForPublicKey(masterNullifierPublicKey); + const [masterNullifierSecretKey, generator] = await keyStore.getMasterSecretKeyAndAppKeyGenerator( + masterNullifierPublicKey, + ); expect(masterNullifierSecretKey.toString()).toMatchInlineSnapshot( `"0x0fde74d5e504c73b58aad420dd72590fc6004571411e7f77c45378714195a52b"`, ); + expect(generator).toBe(GeneratorIndex.NSK_M); + + // Manages to find master incoming viewing secret key for pub key + const [masterIncomingViewingSecretKey, generatorIncoming] = await keyStore.getMasterSecretKeyAndAppKeyGenerator( + masterIncomingViewingPublicKey, + ); + expect(masterIncomingViewingSecretKey.toString()).toMatchInlineSnapshot( + `"0x1f1f43082427fed511393bbabf8a471eb87af09f0e95bb740dc33e1ced1a54c1"`, + ); + expect(generatorIncoming).toBe(GeneratorIndex.IVSK_M); }); it('nullifier key rotation tests', async () => { @@ -118,35 +128,41 @@ describe('TestKeyStore', () => { await keyStore.rotateMasterNullifierKey(accountAddress, newMasterNullifierSecretKeys[2]); // We make sure we can get master nullifier public keys with master nullifier public key hashes - expect(await keyStore.getMasterNullifierPublicKey(newComputedMasterNullifierPublicKeyHashes[2])).toEqual( - newDerivedMasterNullifierPublicKeys[2], + const { masterPublicKey: masterNullifierPublicKey2 } = await keyStore.getKeyValidationRequest( + newComputedMasterNullifierPublicKeyHashes[2], + AztecAddress.random(), // Address is random because we are not interested in the app secret key here ); - expect(await keyStore.getMasterNullifierPublicKey(newComputedMasterNullifierPublicKeyHashes[1])).toEqual( - newDerivedMasterNullifierPublicKeys[1], + expect(masterNullifierPublicKey2).toEqual(newDerivedMasterNullifierPublicKeys[2]); + const { masterPublicKey: masterNullifierPublicKey1 } = await keyStore.getKeyValidationRequest( + newComputedMasterNullifierPublicKeyHashes[1], + AztecAddress.random(), // Address is random because we are not interested in the app secret key here ); - expect(await keyStore.getMasterNullifierPublicKey(newComputedMasterNullifierPublicKeyHashes[0])).toEqual( - newDerivedMasterNullifierPublicKeys[0], + expect(masterNullifierPublicKey1).toEqual(newDerivedMasterNullifierPublicKeys[1]); + const { masterPublicKey: masterNullifierPublicKey0 } = await keyStore.getKeyValidationRequest( + newComputedMasterNullifierPublicKeyHashes[0], + AztecAddress.random(), // Address is random because we are not interested in the app secret key here ); + expect(masterNullifierPublicKey0).toEqual(newDerivedMasterNullifierPublicKeys[0]); // Arbitrary app contract address const appAddress = AztecAddress.fromBigInt(624n); // We make sure we can get app nullifier secret keys with master nullifier public key hashes - const appNullifierSecretKey0 = await keyStore.getAppNullifierSecretKey( + const { appSecretKey: appNullifierSecretKey0 } = await keyStore.getKeyValidationRequest( newComputedMasterNullifierPublicKeyHashes[0], appAddress, ); expect(appNullifierSecretKey0.toString()).toMatchInlineSnapshot( `"0x296e42f1039b62290372d608fcab55b00a3f96c1c8aa347b2a830639c5a12757"`, ); - const appNullifierSecretKey1 = await keyStore.getAppNullifierSecretKey( + const { appSecretKey: appNullifierSecretKey1 } = await keyStore.getKeyValidationRequest( newComputedMasterNullifierPublicKeyHashes[1], appAddress, ); expect(appNullifierSecretKey1.toString()).toMatchInlineSnapshot( `"0x019f2a705b68683f1d86da639a543411fa779af41896c3920d0c2d5226c686dd"`, ); - const appNullifierSecretKey2 = await keyStore.getAppNullifierSecretKey( + const { appSecretKey: appNullifierSecretKey2 } = await keyStore.getKeyValidationRequest( newComputedMasterNullifierPublicKeyHashes[2], appAddress, ); diff --git a/yarn-project/key-store/src/test_key_store.ts b/yarn-project/key-store/src/test_key_store.ts index 6e5416be404..9877e8181cd 100644 --- a/yarn-project/key-store/src/test_key_store.ts +++ b/yarn-project/key-store/src/test_key_store.ts @@ -7,14 +7,20 @@ import { GeneratorIndex, type GrumpkinPrivateKey, GrumpkinScalar, + KEY_PREFIXES, + type KeyGenerator, + type KeyPrefix, + KeyValidationRequest, type PartialAddress, Point, computeAddress, - computeAppNullifierSecretKey, + computeAppSecretKey, deriveKeys, derivePublicKeyFromSecretKey, + getKeyGenerator, } from '@aztec/circuits.js'; import { poseidon2Hash } from '@aztec/foundation/crypto'; +import { type Bufferable, serializeToBuffer } from '@aztec/foundation/serialize'; import { type AztecKVStore, type AztecMap } from '@aztec/kv-store'; /** @@ -54,31 +60,34 @@ export class TestKeyStore implements KeyStore { } = deriveKeys(sk); const publicKeysHash = publicKeys.hash(); - const accountAddress = computeAddress(publicKeysHash, partialAddress); - - // We save the keys to db associated with the account address - await this.#keys.set(`${accountAddress.toString()}-public_keys_hash`, publicKeysHash.toBuffer()); - - // Naming of keys is as follows ${from}-${to}_m - await this.#keys.set(`${accountAddress.toString()}-ivsk_m`, masterIncomingViewingSecretKey.toBuffer()); - await this.#keys.set(`${accountAddress.toString()}-ovsk_m`, masterOutgoingViewingSecretKey.toBuffer()); - await this.#keys.set(`${accountAddress.toString()}-tsk_m`, masterTaggingSecretKey.toBuffer()); - // The key of the following is different from the others because the buffer can store multiple keys - await this.#keys.set(`${accountAddress.toString()}-ns_keys_m`, masterNullifierSecretKey.toBuffer()); - - await this.#keys.set(`${accountAddress.toString()}-np_keys_m`, publicKeys.masterNullifierPublicKey.toBuffer()); - await this.#keys.set(`${accountAddress.toString()}-ivpk_m`, publicKeys.masterIncomingViewingPublicKey.toBuffer()); - await this.#keys.set(`${accountAddress.toString()}-ovpk_m`, publicKeys.masterOutgoingViewingPublicKey.toBuffer()); - await this.#keys.set(`${accountAddress.toString()}-tpk_m`, publicKeys.masterTaggingPublicKey.toBuffer()); - - // We store a npk_m_hash-account_address map to make address easy to obtain with the hash later on + const account = computeAddress(publicKeysHash, partialAddress); + + // Naming of keys is as follows ${account}-${n/iv/ov/t}${sk/pk}_m + await this.#keys.set(`${account.toString()}-ivsk_m`, masterIncomingViewingSecretKey.toBuffer()); + await this.#keys.set(`${account.toString()}-ovsk_m`, masterOutgoingViewingSecretKey.toBuffer()); + await this.#keys.set(`${account.toString()}-tsk_m`, masterTaggingSecretKey.toBuffer()); + await this.#keys.set(`${account.toString()}-nsk_m`, masterNullifierSecretKey.toBuffer()); + + await this.#keys.set(`${account.toString()}-npk_m`, publicKeys.masterNullifierPublicKey.toBuffer()); + await this.#keys.set(`${account.toString()}-ivpk_m`, publicKeys.masterIncomingViewingPublicKey.toBuffer()); + await this.#keys.set(`${account.toString()}-ovpk_m`, publicKeys.masterOutgoingViewingPublicKey.toBuffer()); + await this.#keys.set(`${account.toString()}-tpk_m`, publicKeys.masterTaggingPublicKey.toBuffer()); + + // We store pk_m_hash under `account-{n/iv/ov/t}pk_m_hash` key to be able to obtain address and key prefix + // using the #getKeyPrefixAndAccount function later on + await this.#keys.set(`${account.toString()}-npk_m_hash`, publicKeys.masterNullifierPublicKey.hash().toBuffer()); + await this.#keys.set( + `${account.toString()}-ivpk_m_hash`, + publicKeys.masterIncomingViewingPublicKey.hash().toBuffer(), + ); await this.#keys.set( - `${publicKeys.masterNullifierPublicKey.hash().toString()}-npk_m_hash`, - accountAddress.toBuffer(), + `${account.toString()}-ovpk_m_hash`, + publicKeys.masterOutgoingViewingPublicKey.hash().toBuffer(), ); + await this.#keys.set(`${account.toString()}-tpk_m_hash`, publicKeys.masterTaggingPublicKey.hash().toBuffer()); // At last, we return the newly derived account address - return Promise.resolve(new CompleteAddress(accountAddress, publicKeys, partialAddress)); + return Promise.resolve(new CompleteAddress(account, publicKeys, partialAddress)); } /** @@ -93,43 +102,72 @@ export class TestKeyStore implements KeyStore { } /** - * Gets the master nullifier public key for a given master nullifier public key hash. - * @throws If the account corresponding to the master nullifier public key hash does not exist in the key store. - * @param npkMHash - The master nullifier public key hash. - * @returns The master nullifier public key for the account. + * Gets the key validation request for a given master public key hash and contract address. + * @throws If the account corresponding to the master public key hash does not exist in the key store. + * @param pkMHash - The master public key hash. + * @param contractAddress - The contract address to silo the secret key in the the key validation request with. + * @returns The key validation request. */ - public getMasterNullifierPublicKey(npkMHash: Fr): Promise { - // Get the address for npk_m_hash - const accountAddressBuffer = this.#keys.get(`${npkMHash.toString()}-npk_m_hash`); - if (!accountAddressBuffer) { - throw new Error(`Could no find address for master nullifier public key hash ${npkMHash}.`); + public getKeyValidationRequest(pkMHash: Fr, contractAddress: AztecAddress): Promise { + const [keyPrefix, account] = this.#getKeyPrefixAndAccount(pkMHash); + + // Now we find the master public key for the account + // Since each public keys buffer contains multiple public keys, we need to find the one that matches the hash. + // Then we store the index of the key in the buffer to be able to quickly obtain the corresponding secret key. + let pkM: PublicKey | undefined; + let keyIndexInBuffer = 0; + { + const pkMsBuffer = this.#keys.get(`${account.toString()}-${keyPrefix}pk_m`); + if (!pkMsBuffer) { + throw new Error( + `Could not find ${keyPrefix}pk_m for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`, + ); + } + + // Now we iterate over the public keys in the buffer to find the one that matches the hash + const numKeys = pkMsBuffer.byteLength / Point.SIZE_IN_BYTES; + for (; keyIndexInBuffer < numKeys; keyIndexInBuffer++) { + const foundPkM = Point.fromBuffer( + pkMsBuffer.subarray(keyIndexInBuffer * Point.SIZE_IN_BYTES, (keyIndexInBuffer + 1) * Point.SIZE_IN_BYTES), + ); + if (foundPkM.hash().equals(pkMHash)) { + pkM = foundPkM; + break; + } + } + + if (!pkM) { + throw new Error(`Could not find ${keyPrefix}pkM for ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`); + } } - const accountAddress = AztecAddress.fromBuffer(accountAddressBuffer); - // Get the master nullifier public keys buffer for the account - const masterNullifierPublicKeysBuffer = this.#keys.get(`${accountAddress.toString()}-np_keys_m`); - if (!masterNullifierPublicKeysBuffer) { - throw new Error( - `Could not find master nullifier public key for account ${accountAddress.toString()} whose address was successfully obtained with npk_m_hash ${npkMHash.toString()}.`, + // Now we find the secret key for the public key + let skM: GrumpkinPrivateKey | undefined; + { + const skMsBuffer = this.#keys.get(`${account.toString()}-${keyPrefix}sk_m`); + if (!skMsBuffer) { + throw new Error( + `Could not find ${keyPrefix}sk_m for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`, + ); + } + + skM = GrumpkinScalar.fromBuffer( + skMsBuffer.subarray( + keyIndexInBuffer * GrumpkinScalar.SIZE_IN_BYTES, + (keyIndexInBuffer + 1) * GrumpkinScalar.SIZE_IN_BYTES, + ), ); } - // We check that the buffer's length is a multiple of Point.SIZE_IN_BYTES - if (masterNullifierPublicKeysBuffer.byteLength % Point.SIZE_IN_BYTES !== 0) { - throw new Error("Master nullifier public key buffer's length is not a multiple of Point.SIZE_IN_BYTES."); + // We sanity check that it's possible to derive the public key from the secret key + if (!derivePublicKeyFromSecretKey(skM).equals(pkM)) { + throw new Error(`Could not derive ${keyPrefix}pkM from ${keyPrefix}skM.`); } - // Now we iterate over the public keys in the buffer to find the one that matches the hash - const numKeys = masterNullifierPublicKeysBuffer.byteLength / Point.SIZE_IN_BYTES; - for (let i = 0; i < numKeys; i++) { - const masterNullifierPublicKey = Point.fromBuffer( - masterNullifierPublicKeysBuffer.subarray(i * Point.SIZE_IN_BYTES, (i + 1) * Point.SIZE_IN_BYTES), - ); - if (masterNullifierPublicKey.hash().equals(npkMHash)) { - return Promise.resolve(masterNullifierPublicKey); - } - } - throw new Error(`Could not find master nullifier public key for npk_m_hash ${npkMHash.toString()}.`); + // At last we silo the secret key and return the key validation request + const skApp = computeAppSecretKey(skM, contractAddress, keyPrefix!); + + return Promise.resolve(new KeyValidationRequest(pkM, skApp)); } /** @@ -180,50 +218,6 @@ export class TestKeyStore implements KeyStore { return Promise.resolve(Point.fromBuffer(masterTaggingPublicKeyBuffer)); } - /** - * Derives and returns the application nullifier secret key for a given master nullifier public key hash. - * @throws If the account corresponding to the master nullifier public key hash does not exist in the key store. - * @param npkMHash - The master nullifier public key hash. - * @param app - The application address to retrieve the nullifier secret key for. - * @returns A Promise that resolves to the application nullifier secret key. - */ - public getAppNullifierSecretKey(npkMHash: Fr, app: AztecAddress): Promise { - // First we get the account address for npk_m_hash - const accountAddressBuffer = this.#keys.get(`${npkMHash.toString()}-npk_m_hash`); - if (!accountAddressBuffer) { - throw new Error(`Could no find address for master nullifier public key hash ${npkMHash}.`); - } - - // Now we get the master nullifier secret keys for the account - const masterNullifierSecretKeysBuffer = this.#keys.get( - `${AztecAddress.fromBuffer(accountAddressBuffer).toString()}-ns_keys_m`, - ); - if (!masterNullifierSecretKeysBuffer) { - throw new Error( - `Could not find master nullifier secret keys for account ${AztecAddress.fromBuffer( - accountAddressBuffer, - ).toString()}`, - ); - } - - // Now we iterate over all the secret keys to find the one that matches the hash - const numKeys = masterNullifierSecretKeysBuffer.byteLength / GrumpkinScalar.SIZE_IN_BYTES; - for (let i = 0; i < numKeys; i++) { - const secretKey = GrumpkinScalar.fromBuffer( - masterNullifierSecretKeysBuffer.subarray( - i * GrumpkinScalar.SIZE_IN_BYTES, - (i + 1) * GrumpkinScalar.SIZE_IN_BYTES, - ), - ); - const publicKey = derivePublicKeyFromSecretKey(secretKey); - if (publicKey.hash().equals(npkMHash)) { - return Promise.resolve(computeAppNullifierSecretKey(secretKey, app)); - } - } - - throw new Error(`Could not find master nullifier secret key for npk_m_hash ${npkMHash.toString()}.`); - } - /** * Retrieves application incoming viewing secret key. * @throws If the account does not exist in the key store. @@ -277,68 +271,44 @@ export class TestKeyStore implements KeyStore { } /** - * Retrieves the master nullifier secret key (nsk_m) corresponding to the specified master nullifier public key - * (Npk_m). + * Retrieves the sk_m for the pk_m and a generator index of the key type. * @throws If the provided public key is not associated with any of the registered accounts. - * @param masterNullifierPublicKey - The master nullifier public key to get secret key for. - * @returns A Promise that resolves to the master nullifier secret key. - * @dev Used when feeding the master nullifier secret key to the kernel circuit for nullifier keys verification. + * @param pkM - The master public key to get secret key for. + * @returns A Promise that resolves to sk_m. + * @dev Used when feeding the sk_m to the kernel circuit for keys verification. */ - public getMasterNullifierSecretKeyForPublicKey(masterNullifierPublicKey: PublicKey): Promise { - // We get the account address associated with the master nullifier public key hash - const accountAddressBuffer = this.#keys.get(`${masterNullifierPublicKey.hash().toString()}-npk_m_hash`); - if (!accountAddressBuffer) { - throw new Error( - `Could not find account address for master nullifier public key ${masterNullifierPublicKey.toString()}`, - ); - } - const accountAddress = AztecAddress.fromBuffer(accountAddressBuffer); - - // We fetch the public keys and find this specific public key's position in the buffer - const masterNullifierPublicKeysBuffer = this.#keys.get(`${accountAddress.toString()}-np_keys_m`); - if (!masterNullifierPublicKeysBuffer) { - throw new Error(`Could not find master nullifier public keys for account ${accountAddress.toString()}`); - } - - // We check that the buffer's length is a multiple of Point.SIZE_IN_BYTES - if (masterNullifierPublicKeysBuffer.byteLength % Point.SIZE_IN_BYTES !== 0) { - throw new Error("Master nullifier public key buffer's length is not a multiple of Point.SIZE_IN_BYTES."); - } - - // Now we iterate over the public keys in the buffer to find the one that matches the hash - const numKeys = masterNullifierPublicKeysBuffer.byteLength / Point.SIZE_IN_BYTES; - let keyIndex = -1; - for (let i = 0; i < numKeys; i++) { - const publicKey = Point.fromBuffer( - masterNullifierPublicKeysBuffer.subarray(i * Point.SIZE_IN_BYTES, (i + 1) * Point.SIZE_IN_BYTES), - ); - if (publicKey.equals(masterNullifierPublicKey)) { - keyIndex = i; - break; + public getMasterSecretKeyAndAppKeyGenerator(pkM: PublicKey): Promise<[GrumpkinPrivateKey, KeyGenerator]> { + const [keyPrefix, account] = this.#getKeyPrefixAndAccount(pkM); + + // We get the secret keys buffer and iterate over the values in the buffer to find the one that matches pkM + let sk: GrumpkinScalar | undefined; + { + const secretKeysBuffer = this.#keys.get(`${account.toString()}-${keyPrefix}sk_m`); + if (!secretKeysBuffer) { + throw new Error( + `Could not find ${keyPrefix}sk_m for ${keyPrefix}pk_m ${pkM.toString()}. This should not happen.`, + ); } - } - // Now we fetch the secret keys buffer and extract the secret key at the same index - const masterNullifierSecretKeysBuffer = this.#keys.get(`${accountAddress.toString()}-ns_keys_m`); - if (!masterNullifierSecretKeysBuffer) { - throw new Error(`Could not find master nullifier secret keys for account ${accountAddress.toString()}`); - } - - // We extract the secret key from the buffer - const secretKeyBuffer = masterNullifierSecretKeysBuffer.subarray( - keyIndex * GrumpkinScalar.SIZE_IN_BYTES, - (keyIndex + 1) * GrumpkinScalar.SIZE_IN_BYTES, - ); - const secretKey = GrumpkinScalar.fromBuffer(secretKeyBuffer); + const numKeys = secretKeysBuffer.byteLength / GrumpkinScalar.SIZE_IN_BYTES; + for (let i = 0; i < numKeys; i++) { + const foundSk = GrumpkinScalar.fromBuffer( + secretKeysBuffer.subarray(i * GrumpkinScalar.SIZE_IN_BYTES, (i + 1) * GrumpkinScalar.SIZE_IN_BYTES), + ); + if (derivePublicKeyFromSecretKey(foundSk).equals(pkM)) { + sk = foundSk; + break; + } + } - // We sanity check that it's possible to derive the public key from the secret key - if (!derivePublicKeyFromSecretKey(secretKey).equals(masterNullifierPublicKey)) { - throw new Error( - `Could not find master nullifier secret key for public key ${masterNullifierPublicKey.toString()}`, - ); + if (!sk) { + throw new Error(`Could not find ${keyPrefix}skM for ${keyPrefix}pkM ${pkM.toString()} in secret keys buffer.`); + } } - return Promise.resolve(secretKey); + // Now we determine the key type and return generator accordingly + const generator = getKeyGenerator(keyPrefix); + return Promise.resolve([sk, generator]); } /** @@ -356,11 +326,11 @@ export class TestKeyStore implements KeyStore { for (const [key, value] of this.#keys.entries()) { if (value.equals(masterIncomingViewingPublicKey.toBuffer())) { // We extract the account address from the map key - const accountAddress = key.split('-')[0]; + const account = key.split('-')[0]; // We fetch the secret key and return it - const masterIncomingViewingSecretKeyBuffer = this.#keys.get(`${accountAddress.toString()}-ivsk_m`); + const masterIncomingViewingSecretKeyBuffer = this.#keys.get(`${account.toString()}-ivsk_m`); if (!masterIncomingViewingSecretKeyBuffer) { - throw new Error(`Could not find master incoming viewing secret key for account ${accountAddress.toString()}`); + throw new Error(`Could not find master incoming viewing secret key for account ${account.toString()}`); } return Promise.resolve(GrumpkinScalar.fromBuffer(masterIncomingViewingSecretKeyBuffer)); } @@ -371,22 +341,6 @@ export class TestKeyStore implements KeyStore { ); } - /** - * Retrieves public keys hash of the account - * @throws If the provided account address is not associated with any of the registered accounts. - * @param account - The account address to get public keys hash for. - * @returns A Promise that resolves to the public keys hash. - */ - public async getPublicKeysHash(account: AztecAddress): Promise { - const publicKeysHashBuffer = this.#keys.get(`${account.toString()}-public_keys_hash`); - if (!publicKeysHashBuffer) { - throw new Error( - `Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`, - ); - } - return Promise.resolve(Fr.fromBuffer(publicKeysHashBuffer)); - } - /** * Rotates the master nullifier key for the specified account. * @@ -400,28 +354,47 @@ export class TestKeyStore implements KeyStore { * @returns A Promise that resolves when the key rotation is complete. */ public async rotateMasterNullifierKey(account: AztecAddress, newSecretKey: Fq = Fq.random()) { - // We append the secret key to the original secret key - const secretKeysBuffer = this.#keys.get(`${account.toString()}-ns_keys_m`); - if (!secretKeysBuffer) { - throw new Error(`Could not find nullifier secret keys for account ${account.toString()}`); - } - - // We append the new secret key to the buffer of secret keys - const newSecretKeysBuffer = Buffer.concat([secretKeysBuffer, newSecretKey.toBuffer()]); - await this.#keys.set(`${account.toString()}-ns_keys_m`, newSecretKeysBuffer); + // We append the secret key to the array of secret keys + await this.#appendValue(`${account.toString()}-nsk_m`, newSecretKey); // Now we derive the public key from the new secret key and append it to the buffer of original public keys const newPublicKey = derivePublicKeyFromSecretKey(newSecretKey); - const publicKeysBuffer = this.#keys.get(`${account.toString()}-np_keys_m`); - if (!publicKeysBuffer) { - throw new Error(`Could not find nullifier public keys for account ${account.toString()}`); + await this.#appendValue(`${account.toString()}-npk_m`, newPublicKey); + + // At last we store npk_m_hash under `account-npk_m_hash` key to be able to obtain address and key prefix + // using the #getKeyPrefixAndAccount function later on + await this.#appendValue(`${account.toString()}-npk_m_hash`, newPublicKey.hash()); + } + + /** + * Gets the key prefix and account address for a given value. + * @returns A tuple containing the key prefix and account address. + * @dev Note that this is quite inefficient but it should not matter because there should never be too many keys + * in the key store. + */ + #getKeyPrefixAndAccount(value: Bufferable): [KeyPrefix, AztecAddress] { + const valueBuffer = serializeToBuffer(value); + for (const [key, val] of this.#keys.entries()) { + // `val` can contain multiple values due to key rotation so we check if the value is in the buffer instead + // of just calling `.equals(...)` + if (val.includes(valueBuffer)) { + for (const prefix of KEY_PREFIXES) { + if (key.includes(`-${prefix}`)) { + const account = AztecAddress.fromString(key.split('-')[0]); + return [prefix, account]; + } + } + } } + throw new Error(`Could not find key prefix.`); + } - // We append the new public key to the buffer of public keys - const newPublicKeysBuffer = Buffer.concat([publicKeysBuffer, newPublicKey.toBuffer()]); - await this.#keys.set(`${account.toString()}-np_keys_m`, newPublicKeysBuffer); + async #appendValue(key: string, value: Bufferable) { + const currentValue = this.#keys.get(key); + if (!currentValue) { + throw new Error(`Could not find current value for key ${key}`); + } - // We store a npk_m_hash-account_address map to make address easy to obtain with the hash later on - await this.#keys.set(`${newPublicKey.hash().toString()}-npk_m_hash`, account.toBuffer()); + await this.#keys.set(key, serializeToBuffer([currentValue, value])); } } diff --git a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts index e7b3f99a30f..e9461fbcb8f 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -27,16 +27,18 @@ import { Header, KernelCircuitPublicInputs, type KernelData, + type KeyValidationHint, + KeyValidationRequest, L2ToL1Message, type LeafDataReadHint, LogHash, MAX_ENCRYPTED_LOGS_PER_TX, + MAX_KEY_VALIDATION_REQUESTS_PER_TX, MAX_NEW_L2_TO_L1_MSGS_PER_TX, MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, - MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, @@ -56,7 +58,6 @@ import { type NoteHashReadRequestHints, NoteLogHash, Nullifier, - NullifierKeyValidationRequest, type NullifierLeafPreimage, type NullifierNonExistentReadRequestHints, type NullifierReadRequestHints, @@ -109,10 +110,10 @@ import { type RootParityInputs, type RootRollupInputs, RootRollupPublicInputs, + ScopedKeyValidationRequest, ScopedL2ToL1Message, ScopedNoteHash, ScopedNullifier, - ScopedNullifierKeyValidationRequest, ScopedPrivateCallRequest, ScopedReadRequest, type SettledReadHint, @@ -150,6 +151,8 @@ import type { Header as HeaderNoir, KernelCircuitPublicInputs as KernelCircuitPublicInputsNoir, KernelData as KernelDataNoir, + KeyValidationHint as KeyValidationHintNoir, + KeyValidationRequest as KeyValidationRequestsNoir, L2ToL1Message as L2ToL1MessageNoir, LeafDataReadHint as LeafDataReadHintNoir, LogHash as LogHashNoir, @@ -165,8 +168,6 @@ import type { NoteHashReadRequestHints as NoteHashReadRequestHintsNoir, NoteHashSettledReadHint as NoteHashSettledReadHintNoir, NoteLogHash as NoteLogHashNoir, - NullifierKeyHint as NullifierKeyHintNoir, - NullifierKeyValidationRequest as NullifierKeyValidationRequestNoir, NullifierLeafPreimage as NullifierLeafPreimageNoir, Nullifier as NullifierNoir, NullifierNonExistentReadRequestHints as NullifierNonExistentReadRequestHintsNoir, @@ -216,9 +217,9 @@ import type { RootRollupInputs as RootRollupInputsNoir, RootRollupParityInput as RootRollupParityInputNoir, RootRollupPublicInputs as RootRollupPublicInputsNoir, + ScopedKeyValidationRequest as ScopedKeyValidationRequestsNoir, ScopedL2ToL1Message as ScopedL2ToL1MessageNoir, ScopedNoteHash as ScopedNoteHashNoir, - ScopedNullifierKeyValidationRequest as ScopedNullifierKeyValidationRequestNoir, ScopedNullifier as ScopedNullifierNoir, ScopedPrivateCallRequest as ScopedPrivateCallRequestNoir, ScopedReadRequest as ScopedReadRequestNoir, @@ -308,16 +309,14 @@ export function mapGrumpkinPrivateKeyToNoir(privateKey: GrumpkinPrivateKey): Gru } /** - * Maps a NullifierKeyHint to noir. - * @param hint - The nullifier key hint. - * @returns The nullifier key hint mapped to noir types. + * Maps a KeyValidationHint to noir. + * @param hint - The key validation hint. + * @returns The key validation hint mapped to noir types. */ -export function mapNullifierKeyHintToNoir(hint: { - privateKey: GrumpkinPrivateKey; - requestIndex: number; -}): NullifierKeyHintNoir { +export function mapKeyValidationHintToNoir(hint: KeyValidationHint): KeyValidationHintNoir { return { - private_key: mapGrumpkinPrivateKeyToNoir(hint.privateKey), + sk_m: mapGrumpkinPrivateKeyToNoir(hint.skM), + sk_app_generator_index: mapFieldToNoir(new Fr(hint.skAppGeneratorIndex)), request_index: mapNumberToNoir(hint.requestIndex), }; } @@ -747,47 +746,36 @@ export function mapScopedReadRequestFromNoir(scoped: ScopedReadRequestNoir): Sco } /** - * Maps a NullifierKeyValidationRequest to a noir NullifierKeyValidationRequest. - * @param request - The NullifierKeyValidationRequest. - * @returns The noir NullifierKeyValidationRequest. + * Maps a KeyValidationRequest to a noir KeyValidationRequest. + * @param request - The KeyValidationRequest. + * @returns The noir KeyValidationRequest. */ -export function mapNullifierKeyValidationRequestToNoir( - request: NullifierKeyValidationRequest, -): NullifierKeyValidationRequestNoir { +export function mapKeyValidationRequestsToNoir(request: KeyValidationRequest): KeyValidationRequestsNoir { return { - master_nullifier_public_key: mapPointToNoir(request.masterNullifierPublicKey), - app_nullifier_secret_key: mapFieldToNoir(request.appNullifierSecretKey), + pk_m: mapPointToNoir(request.masterPublicKey), + sk_app: mapFieldToNoir(request.appSecretKey), }; } /** - * Maps a noir NullifierKeyValidationRequest to NullifierKeyValidationRequest. - * @param request - The noir NullifierKeyValidationRequest. - * @returns The TS NullifierKeyValidationRequest. + * Maps a noir KeyValidationRequest to KeyValidationRequest. + * @param request - The noir KeyValidationRequest. + * @returns The TS KeyValidationRequest. */ -export function mapNullifierKeyValidationRequestFromNoir( - request: NullifierKeyValidationRequestNoir, -): NullifierKeyValidationRequest { - return new NullifierKeyValidationRequest( - mapPointFromNoir(request.master_nullifier_public_key), - mapFieldFromNoir(request.app_nullifier_secret_key), - ); +export function mapKeyValidationRequestsFromNoir(request: KeyValidationRequestsNoir): KeyValidationRequest { + return new KeyValidationRequest(mapPointFromNoir(request.pk_m), mapFieldFromNoir(request.sk_app)); } -function mapScopedNullifierKeyValidationRequestToNoir( - request: ScopedNullifierKeyValidationRequest, -): ScopedNullifierKeyValidationRequestNoir { +function mapScopedKeyValidationRequestsToNoir(request: ScopedKeyValidationRequest): ScopedKeyValidationRequestsNoir { return { - request: mapNullifierKeyValidationRequestToNoir(request.request), + request: mapKeyValidationRequestsToNoir(request.request), contract_address: mapAztecAddressToNoir(request.contractAddress), }; } -function mapScopedNullifierKeyValidationRequestFromNoir( - request: ScopedNullifierKeyValidationRequestNoir, -): ScopedNullifierKeyValidationRequest { - return new ScopedNullifierKeyValidationRequest( - mapNullifierKeyValidationRequestFromNoir(request.request), +function mapScopedKeyValidationRequestsFromNoir(request: ScopedKeyValidationRequestsNoir): ScopedKeyValidationRequest { + return new ScopedKeyValidationRequest( + mapKeyValidationRequestsFromNoir(request.request), mapAztecAddressFromNoir(request.contract_address), ); } @@ -842,10 +830,7 @@ export function mapPrivateCircuitPublicInputsToNoir( returns_hash: mapFieldToNoir(privateCircuitPublicInputs.returnsHash), note_hash_read_requests: mapTuple(privateCircuitPublicInputs.noteHashReadRequests, mapReadRequestToNoir), nullifier_read_requests: mapTuple(privateCircuitPublicInputs.nullifierReadRequests, mapReadRequestToNoir), - nullifier_key_validation_requests: mapTuple( - privateCircuitPublicInputs.nullifierKeyValidationRequests, - mapNullifierKeyValidationRequestToNoir, - ), + key_validation_requests: mapTuple(privateCircuitPublicInputs.keyValidationRequests, mapKeyValidationRequestsToNoir), new_note_hashes: mapTuple(privateCircuitPublicInputs.newNoteHashes, mapNoteHashToNoir), new_nullifiers: mapTuple(privateCircuitPublicInputs.newNullifiers, mapNullifierToNoir), private_call_requests: mapTuple(privateCircuitPublicInputs.privateCallRequests, mapPrivateCallRequestToNoir), @@ -1112,10 +1097,7 @@ function mapValidationRequestsToNoir(requests: ValidationRequests): ValidationRe requests.nullifierNonExistentReadRequests, mapScopedReadRequestToNoir, ), - nullifier_key_validation_requests: mapTuple( - requests.nullifierKeyValidationRequests, - mapScopedNullifierKeyValidationRequestToNoir, - ), + key_validation_requests: mapTuple(requests.keyValidationRequests, mapScopedKeyValidationRequestsToNoir), public_data_reads: mapTuple(requests.publicDataReads, mapPublicDataReadToNoir), }; } @@ -1139,9 +1121,9 @@ function mapValidationRequestsFromNoir(requests: ValidationRequestsNoir): Valida mapScopedReadRequestFromNoir, ), mapTupleFromNoir( - requests.nullifier_key_validation_requests, - MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, - mapScopedNullifierKeyValidationRequestFromNoir, + requests.key_validation_requests, + MAX_KEY_VALIDATION_REQUESTS_PER_TX, + mapScopedKeyValidationRequestsFromNoir, ), mapTupleFromNoir(requests.public_data_reads, MAX_PUBLIC_DATA_READS_PER_TX, mapPublicDataReadFromNoir), ); @@ -1558,10 +1540,16 @@ function mapPrivateKernelResetHintsToNoir< NH_RR_SETTLED extends number, NLL_RR_PENDING extends number, NLL_RR_SETTLED extends number, - NLL_KEYS extends number, + KEY_VALIDATION_REQUESTS extends number, >( - inputs: PrivateKernelResetHints, -): PrivateKernelResetHintsNoir { + inputs: PrivateKernelResetHints< + NH_RR_PENDING, + NH_RR_SETTLED, + NLL_RR_PENDING, + NLL_RR_SETTLED, + KEY_VALIDATION_REQUESTS + >, +): PrivateKernelResetHintsNoir { return { transient_nullifier_indexes_for_note_hashes: mapTuple( inputs.transientNullifierIndexesForNoteHashes, @@ -1571,9 +1559,9 @@ function mapPrivateKernelResetHintsToNoir< transient_note_hash_indexes_for_logs: mapTuple(inputs.transientNoteHashIndexesForLogs, mapNumberToNoir), note_hash_read_request_hints: mapNoteHashReadRequestHintsToNoir(inputs.noteHashReadRequestHints), nullifier_read_request_hints: mapNullifierReadRequestHintsToNoir(inputs.nullifierReadRequestHints), - master_nullifier_secret_keys: inputs.masterNullifierSecretKeys.map(mapNullifierKeyHintToNoir) as FixedLengthArray< - NullifierKeyHintNoir, - NLL_KEYS + key_validation_hints: inputs.keyValidationHints.map(mapKeyValidationHintToNoir) as FixedLengthArray< + KeyValidationHintNoir, + KEY_VALIDATION_REQUESTS >, }; } @@ -1583,7 +1571,7 @@ export function mapPrivateKernelResetCircuitPrivateInputsToNoir< NH_RR_SETTLED extends number, NLL_RR_PENDING extends number, NLL_RR_SETTLED extends number, - NLL_KEYS extends number, + KEY_VALIDATION_REQUESTS extends number, TAG extends string, >( inputs: PrivateKernelResetCircuitPrivateInputs< @@ -1591,10 +1579,16 @@ export function mapPrivateKernelResetCircuitPrivateInputsToNoir< NH_RR_SETTLED, NLL_RR_PENDING, NLL_RR_SETTLED, - NLL_KEYS, + KEY_VALIDATION_REQUESTS, TAG >, -): PrivateKernelResetCircuitPrivateInputsNoir { +): PrivateKernelResetCircuitPrivateInputsNoir< + NH_RR_PENDING, + NH_RR_SETTLED, + NLL_RR_PENDING, + NLL_RR_SETTLED, + KEY_VALIDATION_REQUESTS +> { return { previous_kernel: mapPrivateKernelDataToNoir(inputs.previousKernel), outputs: mapPrivateKernelResetOutputsToNoir(inputs.outputs), diff --git a/yarn-project/pxe/src/kernel_oracle/index.ts b/yarn-project/pxe/src/kernel_oracle/index.ts index 26728bd6be2..3a3d1010997 100644 --- a/yarn-project/pxe/src/kernel_oracle/index.ts +++ b/yarn-project/pxe/src/kernel_oracle/index.ts @@ -3,6 +3,8 @@ import { type AztecAddress, type Fr, type FunctionSelector, + type GrumpkinPrivateKey, + type KeyGenerator, MembershipWitness, type NOTE_HASH_TREE_HEIGHT, type Point, @@ -67,8 +69,8 @@ export class KernelOracle implements ProvingDataOracle { return header.state.partial.noteHashTree.root; } - public getMasterNullifierSecretKey(nullifierPublicKey: Point) { - return this.keyStore.getMasterNullifierSecretKeyForPublicKey(nullifierPublicKey); + public getMasterSecretKeyAndAppKeyGenerator(masterPublicKey: Point): Promise<[GrumpkinPrivateKey, KeyGenerator]> { + return this.keyStore.getMasterSecretKeyAndAppKeyGenerator(masterPublicKey); } public async getFunctionName(contractAddress: AztecAddress, selector: FunctionSelector): Promise { diff --git a/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_reset_hints.ts b/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_reset_hints.ts index 6eef626d510..f77d2055bc6 100644 --- a/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_reset_hints.ts +++ b/yarn-project/pxe/src/kernel_prover/private_inputs_builders/build_private_kernel_reset_hints.ts @@ -1,21 +1,21 @@ import { - type Fr, + Fr, + KeyValidationHint, + MAX_KEY_VALIDATION_REQUESTS_PER_TX, MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX, MAX_NOTE_HASH_READ_REQUESTS_PER_TX, - MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX, MembershipWitness, NULLIFIER_TREE_HEIGHT, - NullifierKeyHint, PRIVATE_RESET_VARIANTS, type PrivateKernelData, PrivateKernelResetCircuitPrivateInputs, type PrivateKernelResetCircuitPrivateInputsVariants, PrivateKernelResetHints, + type ScopedKeyValidationRequest, type ScopedNullifier, - type ScopedNullifierKeyValidationRequest, type ScopedReadRequest, buildNoteHashReadRequestHints, buildNullifierReadRequestHints, @@ -60,30 +60,25 @@ function getNullifierReadRequestHints, +async function getMasterSecretKeysAndAppKeyGenerators( + keyValidationRequests: Tuple, oracle: ProvingDataOracle, ) { - const keys = makeTuple(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, NullifierKeyHint.empty); + const keysHints = makeTuple(MAX_KEY_VALIDATION_REQUESTS_PER_TX, KeyValidationHint.empty); let keyIndex = 0; - for (let i = 0; i < nullifierKeyValidationRequests.length; ++i) { - const request = nullifierKeyValidationRequests[i].request; + for (let i = 0; i < keyValidationRequests.length; ++i) { + const request = keyValidationRequests[i].request; if (request.isEmpty()) { break; } - keys[keyIndex] = new NullifierKeyHint( - await oracle.getMasterNullifierSecretKey(request.masterNullifierPublicKey), - i, - ); + const [secretKeys, appKeyGenerator] = await oracle.getMasterSecretKeyAndAppKeyGenerator(request.masterPublicKey); + keysHints[keyIndex] = new KeyValidationHint(secretKeys, new Fr(appKeyGenerator), i); keyIndex++; } return { keysCount: keyIndex, - keys, + keysHints, }; } @@ -119,8 +114,8 @@ export async function buildPrivateKernelResetInputs( MAX_NULLIFIER_READ_REQUESTS_PER_TX, ); - const { keysCount: nullifierKeysCount, keys: masterNullifierSecretKeys } = await getMasterNullifierSecretKeys( - publicInputs.validationRequests.nullifierKeyValidationRequests, + const { keysCount, keysHints } = await getMasterSecretKeysAndAppKeyGenerators( + publicInputs.validationRequests.keyValidationRequests, oracle, ); @@ -151,7 +146,7 @@ export async function buildPrivateKernelResetInputs( hintSizes.NOTE_HASH_SETTLED_AMOUNT >= noteHashSettledReadHints && hintSizes.NULLIFIER_PENDING_AMOUNT >= nullifierPendingReadHints && hintSizes.NULLIFIER_SETTLED_AMOUNT >= nullifierSettledReadHints && - hintSizes.NULLIFIER_KEYS >= nullifierKeysCount + hintSizes.NULLIFIER_KEYS >= keysCount ) { privateInputs = new PrivateKernelResetCircuitPrivateInputs( previousKernelData, @@ -162,7 +157,7 @@ export async function buildPrivateKernelResetInputs( transientNoteHashIndexesForLogs, noteHashReadRequestHints, nullifierReadRequestHints, - masterNullifierSecretKeys, + keysHints, ).trimToSizes( hintSizes.NOTE_HASH_PENDING_AMOUNT, hintSizes.NOTE_HASH_SETTLED_AMOUNT, diff --git a/yarn-project/pxe/src/kernel_prover/proving_data_oracle.ts b/yarn-project/pxe/src/kernel_prover/proving_data_oracle.ts index f1dc0b39dda..9a0df980702 100644 --- a/yarn-project/pxe/src/kernel_prover/proving_data_oracle.ts +++ b/yarn-project/pxe/src/kernel_prover/proving_data_oracle.ts @@ -4,6 +4,7 @@ import { type Fr, type FunctionSelector, type GrumpkinPrivateKey, + type KeyGenerator, type MembershipWitness, type NOTE_HASH_TREE_HEIGHT, type Point, @@ -70,12 +71,13 @@ export interface ProvingDataOracle { getNoteHashTreeRoot(): Promise; /** - * Get the master secret key of the nullifier public key. - * - * @param nullifierPublicKey - The nullifier public key. - * @returns the master nullifier secret key. + * Retrieves the sk_m for the pk_m and a generator index of the key type. + * @throws If the provided public key is not associated with any of the registered accounts. + * @param masterPublicKey - The master public key to get secret key for. + * @returns A Promise that resolves to sk_m and the corresponding app key generator. + * @dev Used when feeding the sk_m to the kernel circuit for keys verification. */ - getMasterNullifierSecretKey(nullifierPublicKey: Point): Promise; + getMasterSecretKeyAndAppKeyGenerator(masterPublicKey: Point): Promise<[GrumpkinPrivateKey, KeyGenerator]>; getFunctionName(contractAddress: AztecAddress, selector: FunctionSelector): Promise; } diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 0aafc050919..8e81ce7974a 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -14,12 +14,13 @@ import { type Fr, type FunctionSelector, type Header, + type KeyValidationRequest, type L1_TO_L2_MSG_TREE_HEIGHT, } from '@aztec/circuits.js'; import { computeL1ToL2MessageNullifier } from '@aztec/circuits.js/hash'; import { type FunctionArtifact, getFunctionArtifact } from '@aztec/foundation/abi'; import { createDebugLogger } from '@aztec/foundation/log'; -import { type DBOracle, MessageLoadOracleInputs, type NullifierKeys } from '@aztec/simulator'; +import { type DBOracle, MessageLoadOracleInputs } from '@aztec/simulator'; import { type ContractInstance } from '@aztec/types/contracts'; import { type ContractDataOracle } from '../contract_data_oracle/index.js'; @@ -37,10 +38,8 @@ export class SimulatorOracle implements DBOracle { private log = createDebugLogger('aztec:pxe:simulator_oracle'), ) {} - async getNullifierKeys(npkMHash: Fr, contractAddress: AztecAddress): Promise { - const masterNullifierPublicKey = await this.keyStore.getMasterNullifierPublicKey(npkMHash); - const appNullifierSecretKey = await this.keyStore.getAppNullifierSecretKey(npkMHash, contractAddress); - return { masterNullifierPublicKey, appNullifierSecretKey }; + getKeyValidationRequest(pkMHash: Fr, contractAddress: AztecAddress): Promise { + return this.keyStore.getKeyValidationRequest(pkMHash, contractAddress); } async getCompleteAddress(account: AztecAddress): Promise { diff --git a/yarn-project/simulator/src/acvm/oracle/oracle.ts b/yarn-project/simulator/src/acvm/oracle/oracle.ts index 26d8de73567..8ff5628a12a 100644 --- a/yarn-project/simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/oracle.ts @@ -40,16 +40,12 @@ export class Oracle { return unpacked.map(toACVMField); } - async getNullifierKeys([masterNullifierPublicKeyHash]: ACVMField[]): Promise { - const { masterNullifierPublicKey, appNullifierSecretKey } = await this.typedOracle.getNullifierKeys( - fromACVMField(masterNullifierPublicKeyHash), + async getKeyValidationRequest([masterPublicKeyHash]: ACVMField[]): Promise { + const { masterPublicKey, appSecretKey } = await this.typedOracle.getKeyValidationRequest( + fromACVMField(masterPublicKeyHash), ); - return [ - toACVMField(masterNullifierPublicKey.x), - toACVMField(masterNullifierPublicKey.y), - toACVMField(appNullifierSecretKey), - ]; + return [toACVMField(masterPublicKey.x), toACVMField(masterPublicKey.y), toACVMField(appSecretKey)]; } async getContractInstance([address]: ACVMField[]) { diff --git a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts index 9c39638adea..3ad314f41a4 100644 --- a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts @@ -11,6 +11,7 @@ import { } from '@aztec/circuit-types'; import { type Header, + type KeyValidationRequest, type L1_TO_L2_MSG_TREE_HEIGHT, type PrivateCallStackItem, type PublicCallRequest, @@ -20,14 +21,6 @@ import { type AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; import { type ContractInstance } from '@aztec/types/contracts'; -/** Nullifier keys which both correspond to the same master nullifier secret key. */ -export interface NullifierKeys { - /** Master nullifier public key. */ - masterNullifierPublicKey: PublicKey; - /** App nullifier secret key. */ - appNullifierSecretKey: Fr; -} - /** * Information about a note needed during execution. */ @@ -89,8 +82,8 @@ export abstract class TypedOracle { throw new OracleMethodNotAvailableError('unpackReturns'); } - getNullifierKeys(_npkMHash: Fr): Promise { - throw new OracleMethodNotAvailableError('getNullifierKeys'); + getKeyValidationRequest(_pkMHash: Fr): Promise { + throw new OracleMethodNotAvailableError('getKeyValidationRequest'); } getContractInstance(_address: AztecAddress): Promise { diff --git a/yarn-project/simulator/src/client/db_oracle.ts b/yarn-project/simulator/src/client/db_oracle.ts index f36616c2303..35b18deab2a 100644 --- a/yarn-project/simulator/src/client/db_oracle.ts +++ b/yarn-project/simulator/src/client/db_oracle.ts @@ -5,13 +5,13 @@ import { type NullifierMembershipWitness, type PublicDataWitness, } from '@aztec/circuit-types'; -import { type CompleteAddress, type Header } from '@aztec/circuits.js'; +import { type CompleteAddress, type Header, type KeyValidationRequest } from '@aztec/circuits.js'; import { type FunctionArtifact, type FunctionSelector } from '@aztec/foundation/abi'; import { type AztecAddress } from '@aztec/foundation/aztec-address'; import { type Fr } from '@aztec/foundation/fields'; import { type ContractInstance } from '@aztec/types/contracts'; -import { type NoteData, type NullifierKeys } from '../acvm/index.js'; +import { type NoteData } from '../acvm/index.js'; import { type CommitmentsDB } from '../public/db.js'; /** @@ -66,12 +66,12 @@ export interface DBOracle extends CommitmentsDB { popCapsule(): Promise; /** - * Retrieve nullifier keys associated with a specific master nullifier public key and app address. - * @param npkMHash - The master nullifier public key hash. + * Retrieve keys associated with a specific master public key and app address. + * @param pkMHash - The master public key hash. * @returns A Promise that resolves to nullifier keys. - * @throws If the nullifier keys are not registered in the key store. + * @throws If the keys are not registered in the key store. */ - getNullifierKeys(npkMHash: Fr, contractAddress: AztecAddress): Promise; + getKeyValidationRequest(pkMHash: Fr, contractAddress: AztecAddress): Promise; /** * Retrieves a set of notes stored in the database for a given contract address and storage slot. diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index a3136d2530f..6a22436e8ab 100644 --- a/yarn-project/simulator/src/client/private_execution.test.ts +++ b/yarn-project/simulator/src/client/private_execution.test.ts @@ -17,6 +17,7 @@ import { GeneratorIndex, type GrumpkinPrivateKey, Header, + KeyValidationRequest, L1_TO_L2_MSG_TREE_HEIGHT, NOTE_HASH_TREE_HEIGHT, PUBLIC_DATA_TREE_HEIGHT, @@ -192,21 +193,27 @@ describe('Private Execution test suite', () => { beforeEach(async () => { trees = {}; oracle = mock(); - oracle.getNullifierKeys.mockImplementation((masterNullifierPublicKeyHash: Fr, contractAddress: AztecAddress) => { - if (masterNullifierPublicKeyHash.equals(ownerCompleteAddress.publicKeys.masterNullifierPublicKey.hash())) { - return Promise.resolve({ - masterNullifierPublicKey: ownerCompleteAddress.publicKeys.masterNullifierPublicKey, - appNullifierSecretKey: computeAppNullifierSecretKey(ownerMasterNullifierSecretKey, contractAddress), - }); - } - if (masterNullifierPublicKeyHash.equals(recipientCompleteAddress.publicKeys.masterNullifierPublicKey.hash())) { - return Promise.resolve({ - masterNullifierPublicKey: recipientCompleteAddress.publicKeys.masterNullifierPublicKey, - appNullifierSecretKey: computeAppNullifierSecretKey(recipientMasterNullifierSecretKey, contractAddress), - }); - } - throw new Error(`Unknown master nullifier public key hash: ${masterNullifierPublicKeyHash}`); - }); + oracle.getKeyValidationRequest.mockImplementation( + (masterNullifierPublicKeyHash: Fr, contractAddress: AztecAddress) => { + if (masterNullifierPublicKeyHash.equals(ownerCompleteAddress.publicKeys.masterNullifierPublicKey.hash())) { + return Promise.resolve( + new KeyValidationRequest( + ownerCompleteAddress.publicKeys.masterNullifierPublicKey, + computeAppNullifierSecretKey(ownerMasterNullifierSecretKey, contractAddress), + ), + ); + } + if (masterNullifierPublicKeyHash.equals(recipientCompleteAddress.publicKeys.masterNullifierPublicKey.hash())) { + return Promise.resolve( + new KeyValidationRequest( + recipientCompleteAddress.publicKeys.masterNullifierPublicKey, + computeAppNullifierSecretKey(recipientMasterNullifierSecretKey, contractAddress), + ), + ); + } + throw new Error(`Unknown master nullifier public key hash: ${masterNullifierPublicKeyHash}`); + }, + ); // We call insertLeaves here with no leaves to populate empty public data tree root --> this is necessary to be // able to get ivpk_m during execution diff --git a/yarn-project/simulator/src/client/simulator.test.ts b/yarn-project/simulator/src/client/simulator.test.ts index f4cdbd7c97b..63c468804b2 100644 --- a/yarn-project/simulator/src/client/simulator.test.ts +++ b/yarn-project/simulator/src/client/simulator.test.ts @@ -1,5 +1,5 @@ import { type AztecNode, CompleteAddress, Note } from '@aztec/circuit-types'; -import { GeneratorIndex, computeAppNullifierSecretKey, deriveKeys } from '@aztec/circuits.js'; +import { GeneratorIndex, KeyValidationRequest, computeAppNullifierSecretKey, deriveKeys } from '@aztec/circuits.js'; import { computeInnerNoteHash, computeNoteContentHash, @@ -42,10 +42,9 @@ describe('Simulator', () => { oracle = mock(); node = mock(); - oracle.getNullifierKeys.mockResolvedValue({ - masterNullifierPublicKey: ownerMasterNullifierPublicKey, - appNullifierSecretKey, - }); + oracle.getKeyValidationRequest.mockResolvedValue( + new KeyValidationRequest(ownerMasterNullifierPublicKey, appNullifierSecretKey), + ); oracle.getCompleteAddress.mockResolvedValue(ownerCompleteAddress); simulator = new AcirSimulator(oracle, node); diff --git a/yarn-project/simulator/src/client/view_data_oracle.ts b/yarn-project/simulator/src/client/view_data_oracle.ts index 68479c6266b..47a75f4ad2c 100644 --- a/yarn-project/simulator/src/client/view_data_oracle.ts +++ b/yarn-project/simulator/src/client/view_data_oracle.ts @@ -7,14 +7,14 @@ import { type NullifierMembershipWitness, type PublicDataWitness, } from '@aztec/circuit-types'; -import { type Header } from '@aztec/circuits.js'; +import { type Header, type KeyValidationRequest } from '@aztec/circuits.js'; import { siloNullifier } from '@aztec/circuits.js/hash'; import { type AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; import { applyStringFormatting, createDebugLogger } from '@aztec/foundation/log'; import { type ContractInstance } from '@aztec/types/contracts'; -import { type NoteData, type NullifierKeys, TypedOracle } from '../acvm/index.js'; +import { type NoteData, TypedOracle } from '../acvm/index.js'; import { type DBOracle } from './db_oracle.js'; import { pickNotes } from './pick_notes.js'; @@ -35,13 +35,13 @@ export class ViewDataOracle extends TypedOracle { } /** - * Retrieve nullifier keys associated with a specific master nullifier public key and app address. - * @param npkMHash - The master nullifier public key hash. + * Retrieve keys associated with a specific master public key and app address. + * @param pkMHash - The master public key hash. * @returns A Promise that resolves to nullifier keys. - * @throws If the nullifier keys are not registered in the key store. + * @throws If the keys are not registered in the key store. */ - public override getNullifierKeys(npkMHash: Fr): Promise { - return this.db.getNullifierKeys(npkMHash, this.contractAddress); + public override getKeyValidationRequest(pkMHash: Fr): Promise { + return this.db.getKeyValidationRequest(pkMHash, this.contractAddress); } /**