diff --git a/docs/docs/guides/smart_contracts/writing_contracts/authwit.md b/docs/docs/guides/smart_contracts/writing_contracts/authwit.md index 18b21cd75b7..8f74c3bf340 100644 --- a/docs/docs/guides/smart_contracts/writing_contracts/authwit.md +++ b/docs/docs/guides/smart_contracts/writing_contracts/authwit.md @@ -74,7 +74,7 @@ As part of `AuthWit` we are assuming that the `on_behalf_of` implements the priv ```rust #[aztec(private)] -fn spend_private_authwit(inner_hash: Field) -> Field; +fn verify_private_authwit(inner_hash: Field) -> Field; ``` For public authwit, we have a shared registry that is used, there we are using a `consume` function. @@ -101,10 +101,8 @@ To make it convenient to compute the message hashes in TypeScript, the `aztec.js For private calls where we allow execution on behalf of others, we generally want to check if the current call is authenticated by `on_behalf_of`. To easily do so, we can use the `assert_current_call_valid_authwit` which fetches information from the current context without us needing to provide much beyond the `on_behalf_of`. -This function will then make a to `on_behalf_of` to execute the `spend_private_authwit` function which validates that the call is authenticated. -The `on_behalf_of` should assert that we are indeed authenticated and then emit a nullifier when we are spending the authwit to prevent replay attacks. -If the return value is not as expected, we throw an error. -This is to cover the case where the `on_behalf_of` might implemented some function with the same selector as the `spend_private_authwit` that could be used to authenticate unintentionally. +This function will then make a call to `on_behalf_of` to execute the `verify_private_authwit` function which validates that the call is authenticated. +The `on_behalf_of` should assert that we are indeed authenticated and then return the `IS_VALID` selector. If the return value is not as expected, we throw an error. This is to cover the case where the `on_behalf_of` might implemented some function with the same selector as the `verify_private_authwit` that could be used to authenticate unintentionally. #### Example diff --git a/docs/docs/guides/smart_contracts/writing_contracts/common_patterns/index.md b/docs/docs/guides/smart_contracts/writing_contracts/common_patterns/index.md index 5e0997a595e..a44b376779a 100644 --- a/docs/docs/guides/smart_contracts/writing_contracts/common_patterns/index.md +++ b/docs/docs/guides/smart_contracts/writing_contracts/common_patterns/index.md @@ -31,7 +31,7 @@ E.g. you don't want a user to subscribe once they have subscribed already. Or yo Emit a nullifier in your function. By adding this nullifier into the tree, you prevent another nullifier from being added again. This is also why in authwit, we emit a nullifier, to prevent someone from reusing their approval. -#include_code spend_private_authwit /noir-projects/aztec-nr/authwit/src/account.nr rust +#include_code verify_private_authwit /noir-projects/aztec-nr/authwit/src/account.nr rust Note be careful to ensure that the nullifier is not deterministic and that no one could do a preimage analysis attack. More in [the anti pattern section on deterministic nullifiers](#deterministic-nullifiers) diff --git a/docs/docs/migration_notes.md b/docs/docs/migration_notes.md index 6fed5c9c3b5..8c71f6eb6d7 100644 --- a/docs/docs/migration_notes.md +++ b/docs/docs/migration_notes.md @@ -9,11 +9,11 @@ Aztec is in full-speed development. Literally every version breaks compatibility ## TBD ### [Aztec.nr] changes to `NoteInterface` + `compute_nullifier` function was renamed to `compute_note_hash_and_nullifier` and now the function has to return not only the nullifier but also the note hash used to compute the nullifier. The same change was done to `compute_nullifier_without_context` function. These changes were done because having the note hash exposed allowed us to not having to re-compute it again in `destroy_note` function of Aztec.nr which led to significant decrease in gate counts (see the [optimization PR](https://github.com/AztecProtocol/aztec-packages/pull/7103) for more details). - ```diff - impl NoteInterface for ValueNote { - fn compute_nullifier(self, context: &mut PrivateContext) -> Field { @@ -89,10 +89,46 @@ To further reduce gate count, you can iterate over `options.limit` instead of `m + for i in 0..options.limit { ``` +### [Aztec.nr] static private authwit + +The private authwit validation is now making a static call to the account contract instead of passing over control flow. This is to ensure that it cannot be used for re-entry. + +To make this change however, we cannot allow emitting a nullifier from the account contract, since that would break the static call. Instead, we will be changing the `spend_private_authwit` to a `verify_private_authwit` and in the `auth` library emit the nullifier. This means that the "calling" contract will now be emitting the nullifier, and not the account. For example, for a token contract, the nullifier is now emitted by the token contract. However, as this is done inside the `auth` library, the token contract doesn't need to change much. + +The biggest difference is related to "cancelling" an authwit. Since it is no longer in the account contract, you cannot just emit a nullifier from it anymore. Instead it must rely on the token contract providing functionality for cancelling. + +There are also a few general changes to how authwits are generated, namely to more easily support the data required for a validity lookup now. Previously we could lookup the `message_hash` directly at the account contract, now we instead need to use the `inner_hash` and the contract of the consumer to figure out if it have already been emitted. + +A minor extension have been made to the authwit creations to make it easier to sign a specific a hash with a specific caller, e.g., the `inner_hash` can be provided as `{consumer, inner_hash}` to the `createAuthWit` where it previously needed to do a couple of manual steps to compute the outer hash. The `computeOuterAuthWitHash` have been amde internal and the `computeAuthWitMessageHash` can instead be used to compute the values similarly to other authwit computations. + +```diff +const innerHash = computeInnerAuthWitHash([Fr.ZERO, functionSelector.toField(), entrypointPackedArgs.hash]); +-const outerHash = computeOuterAuthWitHash( +- this.dappEntrypointAddress, +- new Fr(this.chainId), +- new Fr(this.version), +- innerHash, +-); ++const outerHash = computeAuthWitMessageHash( ++ { consumer: this.dappEntrypointAddress, innerHash }, ++ { chainId: new Fr(this.chainId), version: new Fr(this.version) }, ++); +``` + +If the wallet is used to compute the authwit, it will populate the chain id and version instead of requiring it to be provided by tha actor. + +```diff +const innerHash = computeInnerAuthWitHash([Fr.fromString('0xdead')]); +-const outerHash = computeOuterAuthWitHash(wallets[1].getAddress(), chainId, version, innerHash); +-const witness = await wallets[0].createAuthWit(outerHash); ++ const witness = await wallets[0].createAuthWit({ comsumer: accounts[1].address, inner_hash }); +``` + ## 0.43.0 ### [Aztec.nr] break `token.transfer()` into `transfer` and `transferFrom` -Earlier we had just one function - `transfer()` which used authwits to handle the case where a contract/user wants to transfer funds on behalf of another user. + +Earlier we had just one function - `transfer()` which used authwits to handle the case where a contract/user wants to transfer funds on behalf of another user. To reduce circuit sizes and proof times, we are breaking up `transfer` and introducing a dedicated `transferFrom()` function like in the ERC20 standard. ### [Aztec.nr] `options.limit` has to be constant @@ -909,9 +945,9 @@ After: ```rust #[aztec(private)] -fn spend_private_authwit(inner_hash: Field) -> Field { +fn verify_private_authwit(inner_hash: Field) -> Field { let actions = AccountActions::private(&mut context, ACCOUNT_ACTIONS_STORAGE_SLOT, is_valid_impl); - actions.spend_private_authwit(inner_hash) + actions.verify_private_authwit(inner_hash) } #[aztec(public)] diff --git a/noir-projects/aztec-nr/authwit/src/account.nr b/noir-projects/aztec-nr/authwit/src/account.nr index c00592520a3..3f63137f08c 100644 --- a/noir-projects/aztec-nr/authwit/src/account.nr +++ b/noir-projects/aztec-nr/authwit/src/account.nr @@ -31,8 +31,8 @@ impl AccountActions<&mut PrivateContext> { } // docs:end:entrypoint - // docs:start:spend_private_authwit - pub fn spend_private_authwit(self, inner_hash: Field) -> Field { + // docs:start:verify_private_authwit + pub fn verify_private_authwit(self, inner_hash: Field) -> Field { // The `inner_hash` is "siloed" with the `msg_sender` to ensure that only it can // consume the message. // This ensures that contracts cannot consume messages that are not intended for them. @@ -44,8 +44,7 @@ impl AccountActions<&mut PrivateContext> { ); let valid_fn = self.is_valid_impl; assert(valid_fn(self.context, message_hash) == true, "Message not authorized by account"); - self.context.push_new_nullifier(message_hash, 0); IS_VALID_SELECTOR } - // docs:end:spend_private_authwit + // docs:end:verify_private_authwit } diff --git a/noir-projects/aztec-nr/authwit/src/auth.nr b/noir-projects/aztec-nr/authwit/src/auth.nr index b24bad76c17..18342ce4f7b 100644 --- a/noir-projects/aztec-nr/authwit/src/auth.nr +++ b/noir-projects/aztec-nr/authwit/src/auth.nr @@ -1,6 +1,9 @@ use dep::aztec::protocol_types::{ abis::function_selector::FunctionSelector, address::AztecAddress, - constants::{GENERATOR_INDEX__AUTHWIT_INNER, GENERATOR_INDEX__AUTHWIT_OUTER, CANONICAL_AUTH_REGISTRY_ADDRESS}, + constants::{ + GENERATOR_INDEX__AUTHWIT_INNER, GENERATOR_INDEX__AUTHWIT_OUTER, GENERATOR_INDEX__AUTHWIT_NULLIFIER, + CANONICAL_AUTH_REGISTRY_ADDRESS +}, hash::pedersen_hash }; use dep::aztec::{prelude::Deserialize, context::{PrivateContext, PublicContext, gas::GasOpts}, hash::hash_args_array}; @@ -10,20 +13,36 @@ global IS_VALID_SELECTOR = 0xabf64ad4; // 4 first bytes of keccak256("IS_VALID() // docs:start:assert_current_call_valid_authwit // Assert that `on_behalf_of` have authorized the current call with a valid authentication witness pub fn assert_current_call_valid_authwit(context: &mut PrivateContext, on_behalf_of: AztecAddress) { - let function_selector = FunctionSelector::from_signature("spend_private_authwit(Field)"); let inner_hash = compute_inner_authwit_hash([context.msg_sender().to_field(), context.selector().to_field(), context.args_hash]); - let result: Field = context.call_private_function(on_behalf_of, function_selector, [inner_hash]).unpack_into(); - assert(result == IS_VALID_SELECTOR, "Message not authorized by account"); + assert_inner_hash_valid_authwit(context, on_behalf_of, inner_hash); } // docs:end:assert_current_call_valid_authwit +pub fn assert_inner_hash_valid_authwit(context: &mut PrivateContext, on_behalf_of: AztecAddress, inner_hash: Field) { + // We perform a static call here and not a standard one to ensure that the account contract cannot re-enter. + let result: Field = context.static_call_private_function( + on_behalf_of, + FunctionSelector::from_signature("verify_private_authwit(Field)"), + [inner_hash] + ).unpack_into(); + assert(result == IS_VALID_SELECTOR, "Message not authorized by account"); + // Compute the nullifier, similar computation to the outer hash, but without the chain_id and version. + // Those should already be handled in the verification, so we just need something to nullify, that allow same inner_hash for multiple actors. + let nullifier = compute_authwit_nullifier(on_behalf_of, inner_hash); + context.push_new_nullifier(nullifier, 0); +} + // docs:start:assert_current_call_valid_authwit_public // Assert that `on_behalf_of` have authorized the current call in a public context pub fn assert_current_call_valid_authwit_public(context: &mut PublicContext, on_behalf_of: AztecAddress) { let inner_hash = compute_inner_authwit_hash( [(*context).msg_sender().to_field(), (*context).selector().to_field(), (*context).get_args_hash()] ); + assert_inner_hash_valid_authwit_public(context, on_behalf_of, inner_hash); +} +// docs:end:assert_current_call_valid_authwit_public +pub fn assert_inner_hash_valid_authwit_public(context: &mut PublicContext, on_behalf_of: AztecAddress, inner_hash: Field) { let result: Field = context.call_public_function( AztecAddress::from_field(CANONICAL_AUTH_REGISTRY_ADDRESS), FunctionSelector::from_signature("consume((Field),Field)"), @@ -32,7 +51,6 @@ pub fn assert_current_call_valid_authwit_public(context: &mut PublicContext, on_ ).deserialize_into(); assert(result == IS_VALID_SELECTOR, "Message not authorized by account"); } -// docs:end:assert_current_call_valid_authwit_public // docs:start:compute_call_authwit_hash // Compute the message hash to be used by an authentication witness @@ -54,6 +72,13 @@ pub fn compute_inner_authwit_hash(args: [Field; N]) -> Field { pedersen_hash(args, GENERATOR_INDEX__AUTHWIT_INNER) } +pub fn compute_authwit_nullifier(on_behalf_of: AztecAddress, inner_hash: Field) -> Field { + pedersen_hash( + [on_behalf_of.to_field(), inner_hash], + GENERATOR_INDEX__AUTHWIT_NULLIFIER + ) +} + pub fn compute_outer_authwit_hash( consumer: AztecAddress, chain_id: Field, diff --git a/noir-projects/aztec-nr/aztec/src/context/unconstrained_context.nr b/noir-projects/aztec-nr/aztec/src/context/unconstrained_context.nr index 514995eedd8..8811a048c0d 100644 --- a/noir-projects/aztec-nr/aztec/src/context/unconstrained_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/unconstrained_context.nr @@ -2,7 +2,9 @@ use dep::protocol_types::address::AztecAddress; struct UnconstrainedContext { block_number: u32, - contract_address: AztecAddress, + contract_address: AztecAddress, + version: Field, + chain_id: Field, } impl UnconstrainedContext { @@ -13,7 +15,9 @@ impl UnconstrainedContext { // available. let block_number = block_number_oracle(); let contract_address = contract_address_oracle(); - Self { block_number, contract_address } + let chain_id = chain_id_oracle(); + let version = version_oracle(); + Self { block_number, contract_address, version, chain_id } } fn block_number(self) -> u32 { @@ -23,6 +27,14 @@ impl UnconstrainedContext { fn this_address(self) -> AztecAddress { self.contract_address } + + fn version(self) -> Field { + self.version + } + + fn chain_id(self) -> Field { + self.chain_id + } } #[oracle(getContractAddress)] @@ -30,3 +42,9 @@ unconstrained fn contract_address_oracle() -> AztecAddress {} #[oracle(getBlockNumber)] unconstrained fn block_number_oracle() -> u32 {} + +#[oracle(getChainId)] +unconstrained fn chain_id_oracle() -> Field {} + +#[oracle(getVersion)] +unconstrained fn version_oracle() -> Field {} diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/header.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/header.nr index 98c2d42d5b9..8819906d2b0 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/header.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/header.nr @@ -44,7 +44,7 @@ fn test_encrypted_log_header() { let ciphertext = header.compute_ciphertext(secret, point); let expected_header_ciphertext = [ - 131, 119, 105, 129, 244, 32, 151, 205, 12, 99, 93, 62, 10, 180, 72, 21, 179, 36, 250, 95, 56, 167, 171, 16, 195, 164, 223, 57, 75, 5, 24, 119, 198, 34, 99, 189, 193, 183, 227, 43, 79, 204, 214, 89, 221, 153, 246, 64 + 228, 9, 65, 81, 62, 59, 249, 207, 90, 196, 206, 72, 39, 199, 82, 196, 23, 131, 32, 226, 26, 176, 43, 39, 239, 177, 177, 192, 85, 216, 17, 15, 18, 187, 35, 225, 135, 192, 63, 88, 29, 173, 232, 46, 72, 82, 187, 139 ]; assert_eq(ciphertext, expected_header_ciphertext); diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr index 871f5fd7771..f858636b4ab 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr @@ -131,7 +131,7 @@ mod test { let ciphertext = body.compute_ciphertext(eph_sk, ivpk_app); let expected_note_body_ciphertext = [ - 131, 119, 105, 129, 244, 32, 151, 205, 12, 99, 93, 62, 10, 180, 72, 21, 47, 232, 95, 17, 240, 230, 80, 129, 174, 158, 23, 76, 114, 185, 43, 18, 254, 148, 147, 230, 66, 216, 167, 62, 180, 213, 238, 33, 108, 29, 84, 139, 99, 206, 212, 253, 92, 116, 137, 31, 0, 104, 45, 91, 250, 109, 141, 114, 189, 53, 35, 60, 108, 156, 170, 206, 150, 114, 150, 187, 198, 13, 62, 153, 133, 13, 169, 167, 242, 221, 40, 168, 186, 203, 104, 82, 47, 238, 142, 179, 90, 37, 9, 70, 245, 176, 122, 247, 42, 87, 75, 7, 20, 89, 166, 123, 14, 26, 230, 156, 49, 94, 0, 94, 72, 58, 171, 239, 115, 174, 155, 7, 151, 17, 60, 206, 193, 134, 70, 87, 215, 88, 21, 194, 63, 26, 106, 105, 124, 213, 252, 152, 192, 71, 115, 13, 181, 5, 169, 15, 170, 196, 174, 228, 170, 192, 91, 76, 110, 220, 89, 47, 248, 144, 189, 251, 167, 149, 248, 226 + 228, 9, 65, 81, 62, 59, 249, 207, 90, 196, 206, 72, 39, 199, 82, 196, 63, 127, 188, 251, 150, 188, 238, 205, 3, 86, 102, 164, 175, 12, 137, 158, 163, 111, 205, 10, 229, 230, 46, 202, 110, 107, 156, 180, 67, 192, 161, 201, 48, 153, 169, 1, 25, 182, 93, 39, 39, 207, 251, 218, 234, 147, 156, 13, 110, 180, 190, 199, 41, 6, 211, 203, 176, 110, 165, 186, 110, 127, 199, 22, 201, 149, 92, 249, 219, 68, 145, 68, 179, 29, 233, 34, 98, 123, 197, 234, 169, 53, 44, 14, 81, 60, 92, 27, 250, 134, 49, 248, 57, 119, 236, 118, 158, 104, 82, 243, 98, 164, 60, 72, 74, 27, 177, 194, 221, 225, 193, 150, 67, 235, 205, 106, 150, 24, 126, 186, 220, 178, 199, 189, 113, 54, 181, 55, 46, 15, 236, 236, 9, 159, 5, 172, 237, 154, 110, 50, 241, 64, 92, 13, 37, 53, 20, 140, 42, 146, 229, 63, 97, 25, 159, 63, 235, 104, 68, 100 ]; assert_eq(expected_note_body_ciphertext.len(), ciphertext.len()); @@ -206,7 +206,7 @@ mod test { let ciphertext = body.compute_ciphertext(eph_sk, ivpk_app); let expected_event_body_ciphertext = [ - 131, 119, 105, 129, 244, 32, 151, 205, 12, 99, 93, 62, 10, 180, 72, 21, 47, 232, 95, 17, 240, 230, 80, 129, 174, 158, 23, 76, 114, 185, 43, 18, 254, 148, 147, 230, 66, 216, 167, 62, 180, 213, 238, 33, 108, 29, 84, 139, 157, 165, 187, 138, 35, 3, 236, 75, 197, 105, 102, 247, 224, 253, 13, 217, 145, 62, 96, 167, 93, 23, 18, 198, 187, 91, 8, 3, 197, 195, 127, 9, 218, 111, 125, 97, 141, 129, 142, 1, 230, 108, 35, 211, 170, 170, 170, 249, 249, 104, 68, 191, 245, 207, 182, 245, 248, 82, 175, 83, 155, 138, 208, 65, 31, 129, 251, 242, 219, 76, 17, 61, 178, 187, 108, 114, 177, 215, 175, 189, 166, 221, 94, 9, 22, 57, 151, 204, 57, 220, 129, 243, 217, 18, 101, 128, 229, 40, 254, 175, 2, 21, 31, 198, 18, 152, 169, 32, 113, 92, 37, 65, 169, 119, 95, 149, 239, 8, 23, 182, 22, 209, 207, 120, 133, 90, 252, 106 + 228, 9, 65, 81, 62, 59, 249, 207, 90, 196, 206, 72, 39, 199, 82, 196, 63, 127, 188, 251, 150, 188, 238, 205, 3, 86, 102, 164, 175, 12, 137, 158, 163, 111, 205, 10, 229, 230, 46, 202, 110, 107, 156, 180, 67, 192, 161, 201, 66, 122, 29, 35, 42, 33, 153, 216, 199, 208, 103, 207, 126, 153, 189, 136, 19, 220, 238, 15, 169, 29, 255, 11, 123, 107, 70, 192, 53, 40, 36, 93, 187, 32, 123, 136, 104, 23, 229, 245, 152, 90, 84, 2, 136, 112, 42, 27, 82, 214, 104, 14, 250, 48, 199, 245, 88, 22, 200, 77, 38, 51, 127, 56, 138, 255, 16, 46, 179, 129, 215, 185, 185, 116, 148, 16, 133, 62, 56, 180, 10, 132, 109, 77, 206, 199, 21, 167, 7, 163, 171, 158, 244, 23, 18, 121, 108, 42, 107, 7, 48, 84, 212, 104, 39, 16, 109, 7, 108, 129, 60, 80, 112, 241, 223, 140, 186, 158, 38, 74, 230, 213, 159, 175, 142, 228, 128, 160 ]; assert_eq(expected_event_body_ciphertext.len(), ciphertext.len()); diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/outgoing_body.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/outgoing_body.nr index 4aa90d6d282..460cc73bb85 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/outgoing_body.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/outgoing_body.nr @@ -99,7 +99,7 @@ mod test { let ciphertext = body.compute_ciphertext(sender_ovsk_app, eph_pk); let expected_outgoing_body_ciphertext = [ - 126, 10, 214, 39, 130, 143, 96, 143, 79, 143, 22, 36, 55, 41, 234, 255, 226, 26, 138, 236, 91, 188, 204, 216, 172, 133, 134, 69, 161, 237, 134, 5, 75, 192, 10, 6, 229, 54, 194, 56, 103, 243, 57, 248, 147, 237, 4, 3, 39, 28, 226, 30, 237, 228, 212, 115, 246, 244, 105, 39, 129, 119, 126, 207, 176, 14, 75, 134, 241, 23, 2, 187, 239, 86, 47, 56, 239, 20, 92, 176, 70, 12, 219, 226, 150, 70, 192, 43, 125, 53, 230, 153, 135, 228, 210, 197, 76, 123, 185, 190, 61, 172, 29, 168, 241, 191, 205, 71, 136, 72, 52, 115, 232, 246, 87, 42, 50, 150, 134, 108, 225, 90, 191, 191, 182, 150, 124, 147, 78, 249, 144, 111, 122, 187, 187, 5, 249, 167, 186, 14, 228, 128, 158, 138, 55, 99, 228, 46, 219, 187, 248, 122, 70, 31, 39, 209, 127, 23, 244, 84, 14, 93, 86, 208, 155, 151, 238, 70, 63, 3, 137, 59, 206, 230, 4, 20 + 127, 84, 96, 176, 101, 107, 236, 57, 68, 8, 53, 202, 138, 74, 186, 54, 74, 193, 245, 7, 109, 59, 218, 33, 1, 31, 205, 225, 241, 209, 64, 222, 94, 245, 4, 150, 47, 241, 187, 64, 152, 20, 102, 158, 200, 217, 213, 82, 1, 240, 170, 185, 51, 80, 27, 109, 63, 231, 235, 120, 174, 44, 133, 248, 10, 97, 60, 40, 222, 190, 147, 76, 187, 48, 91, 206, 48, 106, 56, 118, 38, 127, 82, 4, 182, 188, 44, 224, 31, 129, 47, 107, 134, 252, 20, 25, 122, 191, 158, 69, 35, 255, 215, 171, 196, 45, 91, 184, 83, 80, 238, 201, 1, 233, 235, 159, 171, 130, 158, 64, 176, 165, 132, 30, 84, 81, 71, 195, 145, 47, 82, 247, 210, 192, 23, 4, 220, 90, 56, 109, 46, 105, 79, 251, 165, 141, 185, 233, 191, 118, 219, 153, 191, 162, 99, 238, 241, 249, 9, 74, 210, 241, 54, 28, 126, 226, 85, 235, 174, 75, 239, 207, 100, 184, 248, 194 ]; for i in 0..expected_outgoing_body_ciphertext.len() { diff --git a/noir-projects/aztec-nr/aztec/src/keys/point_to_symmetric_key.nr b/noir-projects/aztec-nr/aztec/src/keys/point_to_symmetric_key.nr index 934306e32ab..4fa31d5813e 100644 --- a/noir-projects/aztec-nr/aztec/src/keys/point_to_symmetric_key.nr +++ b/noir-projects/aztec-nr/aztec/src/keys/point_to_symmetric_key.nr @@ -34,7 +34,7 @@ fn check_point_to_symmetric_key() { let key = point_to_symmetric_key(secret, point); // The following value gets updated when running encrypt_buffer.test.ts with AZTEC_GENERATE_TEST_DATA=1 let expected_key = [ - 198, 74, 242, 51, 177, 36, 183, 8, 2, 246, 197, 138, 59, 166, 86, 96, 155, 50, 186, 34, 242, 3, 208, 144, 161, 64, 69, 165, 70, 57, 226, 139 + 49, 167, 146, 222, 151, 129, 138, 184, 87, 210, 245, 249, 99, 100, 1, 59, 223, 180, 5, 99, 14, 7, 177, 236, 159, 203, 231, 72, 220, 180, 241, 23 ]; assert_eq(key, expected_key); } 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 e6c82b833d0..fe65ff9e37e 100644 --- a/noir-projects/aztec-nr/aztec/src/keys/public_keys.nr +++ b/noir-projects/aztec-nr/aztec/src/keys/public_keys.nr @@ -82,7 +82,8 @@ fn compute_public_keys_hash() { }; let actual = keys.hash(); - let expected_public_keys_hash = 0x1936abe4f6a920d16a9f6917f10a679507687e2cd935dd1f1cdcb1e908c027f3; + let expected_public_keys_hash = 0x2406c1c88b7afc13052335bb9af43fd35034b5ba0a9caab76eda2833cf8ec717; + assert(actual.to_field() == expected_public_keys_hash); } diff --git a/noir-projects/noir-contracts/Nargo.toml b/noir-projects/noir-contracts/Nargo.toml index 53dd5747074..4e0dae683c9 100644 --- a/noir-projects/noir-contracts/Nargo.toml +++ b/noir-projects/noir-contracts/Nargo.toml @@ -3,6 +3,7 @@ members = [ "contracts/app_subscription_contract", "contracts/auth_contract", "contracts/auth_registry_contract", + "contracts/auth_wit_test_contract", "contracts/avm_initializer_test_contract", "contracts/avm_test_contract", "contracts/fpc_contract", diff --git a/noir-projects/noir-contracts/contracts/auth_wit_test_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/auth_wit_test_contract/Nargo.toml new file mode 100644 index 00000000000..dc0fb24920c --- /dev/null +++ b/noir-projects/noir-contracts/contracts/auth_wit_test_contract/Nargo.toml @@ -0,0 +1,9 @@ +[package] +name = "auth_wit_test_contract" +authors = [""] +compiler_version = ">=0.25.0" +type = "contract" + +[dependencies] +aztec = { path = "../../../aztec-nr/aztec" } +authwit = { path = "../../../aztec-nr/authwit" } diff --git a/noir-projects/noir-contracts/contracts/auth_wit_test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/auth_wit_test_contract/src/main.nr new file mode 100644 index 00000000000..997d53439a6 --- /dev/null +++ b/noir-projects/noir-contracts/contracts/auth_wit_test_contract/src/main.nr @@ -0,0 +1,14 @@ +contract AuthWitTest { + use dep::aztec::protocol_types::address::AztecAddress; + use dep::authwit::auth::{assert_inner_hash_valid_authwit, assert_inner_hash_valid_authwit_public}; + + #[aztec(private)] + fn consume(on_behalf_of: AztecAddress, inner_hash: Field) { + assert_inner_hash_valid_authwit(&mut context, on_behalf_of, inner_hash); + } + + #[aztec(public)] + fn consume_public(on_behalf_of: AztecAddress, inner_hash: Field) { + assert_inner_hash_valid_authwit_public(&mut context, on_behalf_of, inner_hash); + } +} diff --git a/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr b/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr index 9653946054c..493bead7cab 100644 --- a/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/docs_example_contract/src/main.nr @@ -257,7 +257,8 @@ contract DocsExample { } #[aztec(private)] - fn spend_private_authwit(inner_hash: Field) -> Field { + #[aztec(view)] + fn verify_private_authwit(inner_hash: Field) -> Field { 1 } diff --git a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr index 0bb6c9c0076..160776cc861 100644 --- a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr @@ -45,15 +45,10 @@ contract EcdsaAccount { #[aztec(private)] #[aztec(noinitcheck)] - fn spend_private_authwit(inner_hash: Field) -> Field { + #[aztec(view)] + fn verify_private_authwit(inner_hash: Field) -> Field { let actions = AccountActions::init(&mut context, is_valid_impl); - actions.spend_private_authwit(inner_hash) - } - - #[aztec(private)] - #[aztec(internal)] - fn cancel_authwit(outer_hash: Field) { - context.push_new_nullifier(outer_hash, 0); + actions.verify_private_authwit(inner_hash) } #[contract_library_method] diff --git a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr index 9334077bf57..f5661c26853 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/main.nr @@ -9,7 +9,7 @@ contract SchnorrAccount { use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt; use dep::authwit::{ entrypoint::{app::AppPayload, fee::FeePayload}, account::AccountActions, - auth_witness::get_auth_witness + auth_witness::get_auth_witness, auth::{compute_authwit_nullifier, compute_outer_authwit_hash} }; use dep::aztec::hash::compute_siloed_nullifier; use dep::aztec::oracle::get_nullifier_membership_witness::get_low_nullifier_membership_witness; @@ -50,15 +50,10 @@ contract SchnorrAccount { #[aztec(private)] #[aztec(noinitcheck)] - fn spend_private_authwit(inner_hash: Field) -> Field { + #[aztec(view)] + fn verify_private_authwit(inner_hash: Field) -> Field { let actions = AccountActions::init(&mut context, is_valid_impl); - actions.spend_private_authwit(inner_hash) - } - - #[aztec(private)] - #[aztec(internal)] - fn cancel_authwit(outer_hash: Field) { - context.push_new_nullifier(outer_hash, 0); + actions.verify_private_authwit(inner_hash) } #[contract_library_method] @@ -90,11 +85,15 @@ contract SchnorrAccount { /** * @notice Helper function to check validity of private authwitnesses + * @param consumer The address of the consumer of the message * @param message_hash The message hash of the message to check the validity * @return True if the message_hash can be consumed, false otherwise */ - unconstrained fn lookup_validity(message_hash: Field) -> pub bool { + unconstrained fn lookup_validity(consumer: AztecAddress, inner_hash: Field) -> pub bool { let public_key = storage.signing_public_key.view_note(); + + let message_hash = compute_outer_authwit_hash(consumer, context.chain_id(), context.version(), inner_hash); + let witness: [Field; 64] = get_auth_witness(message_hash); let mut signature: [u8; 64] = [0; 64]; for i in 0..64 { @@ -107,14 +106,12 @@ contract SchnorrAccount { message_hash.to_be_bytes(32) ); - let block_number = context.block_number(); - let myself = context.this_address(); - // Compute the nullifier and check if it is spent // This will BLINDLY TRUST the oracle, but the oracle is us, and // it is not as part of execution of the contract, so we are good. - let siloed_nullifier = compute_siloed_nullifier(myself, message_hash); - let lower_wit = get_low_nullifier_membership_witness(block_number, siloed_nullifier); + let nullifier = compute_authwit_nullifier(context.this_address(), inner_hash); + let siloed_nullifier = compute_siloed_nullifier(consumer, nullifier); + let lower_wit = get_low_nullifier_membership_witness(context.block_number(), siloed_nullifier); let is_spent = lower_wit.leaf_preimage.nullifier == siloed_nullifier; !is_spent & valid_in_private diff --git a/noir-projects/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr index 3441779536b..8d776eab233 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr @@ -20,15 +20,10 @@ contract SchnorrHardcodedAccount { } #[aztec(private)] - fn spend_private_authwit(inner_hash: Field) -> Field { + #[aztec(view)] + fn verify_private_authwit(inner_hash: Field) -> Field { let actions = AccountActions::init(&mut context, is_valid_impl); - actions.spend_private_authwit(inner_hash) - } - - #[aztec(private)] - #[aztec(internal)] - fn cancel_authwit(outer_hash: Field) { - context.push_new_nullifier(outer_hash, 0); + actions.verify_private_authwit(inner_hash) } // docs:start:is-valid diff --git a/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/main.nr index ca795fca252..fbf81afb5fc 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_single_key_account_contract/src/main.nr @@ -16,15 +16,10 @@ contract SchnorrSingleKeyAccount { } #[aztec(private)] - fn spend_private_authwit(inner_hash: Field) -> Field { + #[aztec(view)] + fn verify_private_authwit(inner_hash: Field) -> Field { let actions = AccountActions::init(&mut context, is_valid_impl); - actions.spend_private_authwit(inner_hash) - } - - #[aztec(private)] - #[aztec(internal)] - fn cancel_authwit(outer_hash: Field) { - context.push_new_nullifier(outer_hash, 0); + actions.verify_private_authwit(inner_hash) } #[contract_library_method] diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr index b263abeff01..36390dda0be 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr @@ -21,7 +21,7 @@ contract Token { }; // docs:start:import_authwit - use dep::authwit::{auth::{assert_current_call_valid_authwit, assert_current_call_valid_authwit_public}}; + use dep::authwit::auth::{assert_current_call_valid_authwit, assert_current_call_valid_authwit_public, compute_authwit_nullifier}; // docs:end:import_authwit use crate::types::{transparent_note::TransparentNote, token_note::{TokenNote, TOKEN_NOTE_LEN}, balances_map::BalancesMap}; @@ -326,6 +326,17 @@ contract Token { } // docs:end:transfer + /** + * Cancel a private authentication witness. + * @param inner_hash The inner hash of the authwit to cancel. + */ + #[aztec(private)] + fn cancel_authwit(inner_hash: Field) { + let on_behalf_of = context.msg_sender(); + let nullifier = compute_authwit_nullifier(on_behalf_of, inner_hash); + context.push_new_nullifier(nullifier, 0); + } + #[aztec(private)] fn transfer_from(from: AztecAddress, to: AztecAddress, amount: Field, nonce: Field) { // docs:start:assert_current_call_valid_authwit 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 2d48394e3af..8d450bce84d 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -299,13 +299,14 @@ global GENERATOR_INDEX__PUBLIC_CIRCUIT_PUBLIC_INPUTS = 43; global GENERATOR_INDEX__FUNCTION_ARGS = 44; global GENERATOR_INDEX__AUTHWIT_INNER = 45; global GENERATOR_INDEX__AUTHWIT_OUTER = 46; +global GENERATOR_INDEX__AUTHWIT_NULLIFIER = 47; // Key related generators follow -global GENERATOR_INDEX__NSK_M = 47; -global GENERATOR_INDEX__IVSK_M = 48; -global GENERATOR_INDEX__OVSK_M = 49; -global GENERATOR_INDEX__TSK_M = 50; -global GENERATOR_INDEX__PUBLIC_KEYS_HASH = 51; -global GENERATOR_INDEX__NOTE_NULLIFIER = 52; -global GENERATOR_INDEX__INNER_NOTE_HASH = 53; -global GENERATOR_INDEX__NOTE_CONTENT_HASH = 54; -global GENERATOR_INDEX__SYMMETRIC_KEY: u8 = 55; +global GENERATOR_INDEX__NSK_M = 48; +global GENERATOR_INDEX__IVSK_M = 49; +global GENERATOR_INDEX__OVSK_M = 50; +global GENERATOR_INDEX__TSK_M = 51; +global GENERATOR_INDEX__PUBLIC_KEYS_HASH = 52; +global GENERATOR_INDEX__NOTE_NULLIFIER = 53; +global GENERATOR_INDEX__INNER_NOTE_HASH = 54; +global GENERATOR_INDEX__NOTE_CONTENT_HASH = 55; +global GENERATOR_INDEX__SYMMETRIC_KEY: u8 = 56; diff --git a/yarn-project/aztec.js/src/account/interface.ts b/yarn-project/aztec.js/src/account/interface.ts index 8919c3aa403..cafef217f9e 100644 --- a/yarn-project/aztec.js/src/account/interface.ts +++ b/yarn-project/aztec.js/src/account/interface.ts @@ -1,44 +1,25 @@ -import { type AuthWitness, type CompleteAddress, type FunctionCall } from '@aztec/circuit-types'; +import { type AuthWitness, type CompleteAddress } from '@aztec/circuit-types'; import { type AztecAddress } from '@aztec/circuits.js'; import { type Fq, type Fr } from '@aztec/foundation/fields'; -import { type ContractFunctionInteraction } from '../contract/contract_function_interaction.js'; import { type EntrypointInterface } from '../entrypoint/entrypoint.js'; // docs:start:account-interface /** Creates authorization witnesses. */ export interface AuthWitnessProvider { /** - * Computes an authentication witness from either a message hash or an intent (caller and an action). - * If a message hash is provided, it will create a witness for that directly. - * Otherwise, it will compute the message hash using the caller and the action of the intent. - * @param messageHashOrIntent - The message hash or the intent (caller and action) to approve - * @param chainId - The chain id for the message, will default to the current chain id - * @param version - The version for the message, will default to the current protocol version + * Computes an authentication witness from either a message hash + * @param messageHash - The message hash to approve * @returns The authentication witness */ - createAuthWit( - messageHashOrIntent: - | Fr - | Buffer - | { - /** The caller to approve */ - caller: AztecAddress; - /** The action to approve */ - action: ContractFunctionInteraction | FunctionCall; - /** The chain id to approve */ - chainId?: Fr; - /** The version to approve */ - version?: Fr; - }, - ): Promise; + createAuthWit(messageHash: Fr | Buffer): Promise; } /** * Handler for interfacing with an account. Knows how to create transaction execution * requests and authorize actions for its corresponding account. */ -export interface AccountInterface extends AuthWitnessProvider, EntrypointInterface { +export interface AccountInterface extends EntrypointInterface, AuthWitnessProvider { /** Returns the complete address for this account. */ getCompleteAddress(): CompleteAddress; diff --git a/yarn-project/aztec.js/src/account/wallet.ts b/yarn-project/aztec.js/src/account/wallet.ts index d9d78aea434..5dc257bca01 100644 --- a/yarn-project/aztec.js/src/account/wallet.ts +++ b/yarn-project/aztec.js/src/account/wallet.ts @@ -1,8 +1,13 @@ -import { type PXE } from '@aztec/circuit-types'; +import { type AuthWitness, type PXE } from '@aztec/circuit-types'; +import { type IntentAction, type IntentInnerHash } from '../utils/authwit.js'; import { type AccountInterface, type AccountKeyRotationInterface } from './interface.js'; /** * The wallet interface. */ -export type Wallet = AccountInterface & PXE & AccountKeyRotationInterface; +export type Wallet = AccountInterface & + PXE & + AccountKeyRotationInterface & { + createAuthWit(intent: IntentInnerHash | IntentAction): Promise; + }; diff --git a/yarn-project/aztec.js/src/fee/private_fee_payment_method.ts b/yarn-project/aztec.js/src/fee/private_fee_payment_method.ts index 10e1b36de4c..93618e26136 100644 --- a/yarn-project/aztec.js/src/fee/private_fee_payment_method.ts +++ b/yarn-project/aztec.js/src/fee/private_fee_payment_method.ts @@ -6,7 +6,6 @@ import { type AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; import { type Wallet } from '../account/wallet.js'; -import { computeAuthWitMessageHash } from '../utils/authwit.js'; import { type FeePaymentMethod } from './fee_payment_method.js'; /** @@ -55,11 +54,9 @@ export class PrivateFeePaymentMethod implements FeePaymentMethod { async getFunctionCalls(gasSettings: GasSettings): Promise { const nonce = Fr.random(); const maxFee = gasSettings.getFeeLimit(); - const messageHash = computeAuthWitMessageHash( - this.paymentContract, - this.wallet.getChainId(), - this.wallet.getVersion(), - { + await this.wallet.createAuthWit({ + caller: this.paymentContract, + action: { name: 'unshield', args: [this.wallet.getCompleteAddress().address, this.paymentContract, maxFee, nonce], selector: FunctionSelector.fromSignature('unshield((Field),(Field),Field,Field)'), @@ -68,8 +65,7 @@ export class PrivateFeePaymentMethod implements FeePaymentMethod { to: this.asset, returnTypes: [], }, - ); - await this.wallet.createAuthWit(messageHash); + }); const secretHashForRebate = computeSecretHash(this.rebateSecret); diff --git a/yarn-project/aztec.js/src/fee/public_fee_payment_method.ts b/yarn-project/aztec.js/src/fee/public_fee_payment_method.ts index 32e10f31be7..dae20d5fe8a 100644 --- a/yarn-project/aztec.js/src/fee/public_fee_payment_method.ts +++ b/yarn-project/aztec.js/src/fee/public_fee_payment_method.ts @@ -4,7 +4,6 @@ import { FunctionSelector, FunctionType } from '@aztec/foundation/abi'; import { type AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; -import { computeAuthWitMessageHash } from '../utils/authwit.js'; import { type AccountWallet } from '../wallet/account_wallet.js'; import { type FeePaymentMethod } from './fee_payment_method.js'; @@ -47,23 +46,25 @@ export class PublicFeePaymentMethod implements FeePaymentMethod { getFunctionCalls(gasSettings: GasSettings): Promise { const nonce = Fr.random(); const maxFee = gasSettings.getFeeLimit(); - const messageHash = computeAuthWitMessageHash( - this.paymentContract, - this.wallet.getChainId(), - this.wallet.getVersion(), - { - name: 'transfer_public', - args: [this.wallet.getAddress(), this.paymentContract, maxFee, nonce], - selector: FunctionSelector.fromSignature('transfer_public((Field),(Field),Field,Field)'), - type: FunctionType.PUBLIC, - isStatic: false, - to: this.asset, - returnTypes: [], - }, - ); return Promise.resolve([ - this.wallet.setPublicAuthWit(messageHash, true).request(), + this.wallet + .setPublicAuthWit( + { + caller: this.paymentContract, + action: { + name: 'transfer_public', + args: [this.wallet.getAddress(), this.paymentContract, maxFee, nonce], + selector: FunctionSelector.fromSignature('transfer_public((Field),(Field),Field,Field)'), + type: FunctionType.PUBLIC, + isStatic: false, + to: this.asset, + returnTypes: [], + }, + }, + true, + ) + .request(), { name: 'fee_entrypoint_public', to: this.paymentContract, diff --git a/yarn-project/aztec.js/src/index.ts b/yarn-project/aztec.js/src/index.ts index 8fabb0e8f91..f3bd1a2aada 100644 --- a/yarn-project/aztec.js/src/index.ts +++ b/yarn-project/aztec.js/src/index.ts @@ -46,8 +46,8 @@ export { FunctionSelectorLike, WrappedFieldLike, computeAuthWitMessageHash, + computeInnerAuthWitHashFromAction, computeInnerAuthWitHash, - computeOuterAuthWitHash, generatePublicKey, waitForAccountSynch, waitForPXE, diff --git a/yarn-project/aztec.js/src/utils/authwit.ts b/yarn-project/aztec.js/src/utils/authwit.ts index 41655da79fb..e3df2b9a527 100644 --- a/yarn-project/aztec.js/src/utils/authwit.ts +++ b/yarn-project/aztec.js/src/utils/authwit.ts @@ -1,32 +1,77 @@ import { type FunctionCall, PackedValues } from '@aztec/circuit-types'; -import { type AztecAddress, type Fr, GeneratorIndex } from '@aztec/circuits.js'; +import { type AztecAddress, Fr, GeneratorIndex } from '@aztec/circuits.js'; import { pedersenHash } from '@aztec/foundation/crypto'; +import { ContractFunctionInteraction } from '../contract/contract_function_interaction.js'; + +/** Metadata for the intent */ +export type IntentMetadata = { + /** The chain id to approve */ + chainId: Fr; + /** The version to approve */ + version: Fr; +}; + +/** Intent with an inner hash */ +export type IntentInnerHash = { + /** The consumer */ + consumer: AztecAddress; + /** The action to approve */ + innerHash: Buffer | Fr; +}; + +/** Intent with an action */ +export type IntentAction = { + /** The caller to approve */ + caller: AztecAddress; + /** The action to approve */ + action: ContractFunctionInteraction | FunctionCall; +}; + // docs:start:authwit_computeAuthWitMessageHash /** - * Compute an authentication witness message hash from a caller and a request - * H(target: AztecAddress, chainId: Field, version: Field, H(caller: AztecAddress, selector: Field, args_hash: Field)) - * Example usage would be `bob` authenticating `alice` to perform a transfer of `10` - * tokens from his account to herself: - * H(token, 1, 1, H(alice, transfer_selector, H(bob, alice, 10, nonce))) - * `bob` then signs the message hash and gives it to `alice` who can then perform the - * action. - * @param caller - The caller approved to make the call - * @param chainId - The chain id for the message - * @param version - The version for the message - * @param action - The request to be made (function call) - * @returns The message hash for the witness + * Compute an authentication witness message hash from an intent and metadata + * + * If using the `IntentInnerHash`, the consumer is the address that can "consume" the authwit, for token approvals it is the token contract itself. + * The `innerHash` itself will be the message that a contract is allowed to execute. + * At the point of "approval checking", the validating contract (account for private and registry for public) will be computing the message hash + * (`H(consumer, chainid, version, inner_hash)`) where the all but the `inner_hash` is injected from the context (consumer = msg_sender), + * and use it for the authentication check. + * Therefore, any allowed `innerHash` will therefore also have information around where it can be spent (version, chainId) and who can spend it (consumer). + * + * If using the `IntentAction`, the caller is the address that is making the call, for a token approval from Alice to Bob, this would be Bob. + * The action is then used along with the `caller` to compute the `innerHash` and the consumer. + * + * + * @param intent - The intent to approve (consumer and innerHash or caller and action) + * The consumer is the address that can "consume" the authwit, for token approvals it is the token contract itself. + * The caller is the address that is making the call, for a token approval from Alice to Bob, this would be Bob. + * The caller becomes part of the `inner_hash` and is dealt with entirely in application logic. + * @param metadata - The metadata for the intent (chainId, version) + * @returns The message hash for the action */ -export const computeAuthWitMessageHash = (caller: AztecAddress, chainId: Fr, version: Fr, action: FunctionCall) => { - return computeOuterAuthWitHash( - action.to.toField(), - chainId, - version, - computeInnerAuthWitHash([caller.toField(), action.selector.toField(), PackedValues.fromValues(action.args).hash]), - ); +export const computeAuthWitMessageHash = (intent: IntentInnerHash | IntentAction, metadata: IntentMetadata) => { + const chainId = metadata.chainId; + const version = metadata.version; + + if ('caller' in intent) { + const action = intent.action instanceof ContractFunctionInteraction ? intent.action.request() : intent.action; + return computeOuterAuthWitHash( + action.to.toField(), + chainId, + version, + computeInnerAuthWitHashFromAction(intent.caller, action), + ); + } else { + const inner = Buffer.isBuffer(intent.innerHash) ? Fr.fromBuffer(intent.innerHash) : intent.innerHash; + return computeOuterAuthWitHash(intent.consumer, chainId, version, inner); + } }; // docs:end:authwit_computeAuthWitMessageHash +export const computeInnerAuthWitHashFromAction = (caller: AztecAddress, action: FunctionCall) => + computeInnerAuthWitHash([caller.toField(), action.selector.toField(), PackedValues.fromValues(action.args).hash]); + /** * Compute the inner hash for an authentication witness. * This is the "intent" of the message, before siloed with the consumer. @@ -53,6 +98,6 @@ export const computeInnerAuthWitHash = (args: Fr[]) => { * @param innerHash - The inner hash for the witness * @returns The outer hash for the witness */ -export const computeOuterAuthWitHash = (consumer: AztecAddress, chainId: Fr, version: Fr, innerHash: Fr) => { +const computeOuterAuthWitHash = (consumer: AztecAddress, chainId: Fr, version: Fr, innerHash: Fr) => { return pedersenHash([consumer.toField(), chainId, version, innerHash], GeneratorIndex.AUTHWIT_OUTER); }; diff --git a/yarn-project/aztec.js/src/wallet/account_wallet.ts b/yarn-project/aztec.js/src/wallet/account_wallet.ts index 4452b15f953..bf1098e4896 100644 --- a/yarn-project/aztec.js/src/wallet/account_wallet.ts +++ b/yarn-project/aztec.js/src/wallet/account_wallet.ts @@ -1,4 +1,4 @@ -import { type AuthWitness, type FunctionCall, type PXE, type TxExecutionRequest } from '@aztec/circuit-types'; +import { type AuthWitness, type PXE, type TxExecutionRequest } from '@aztec/circuit-types'; import { AztecAddress, CANONICAL_KEY_REGISTRY_ADDRESS, Fq, Fr, derivePublicKeyFromSecretKey } from '@aztec/circuits.js'; import { type ABIParameterVisibility, type FunctionAbi, FunctionType } from '@aztec/foundation/abi'; import { AuthRegistryAddress } from '@aztec/protocol-contracts/auth-registry'; @@ -6,7 +6,12 @@ import { AuthRegistryAddress } from '@aztec/protocol-contracts/auth-registry'; import { type AccountInterface } from '../account/interface.js'; import { ContractFunctionInteraction } from '../contract/contract_function_interaction.js'; import { type ExecutionRequestInit } from '../entrypoint/entrypoint.js'; -import { computeAuthWitMessageHash } from '../utils/authwit.js'; +import { + type IntentAction, + type IntentInnerHash, + computeAuthWitMessageHash, + computeInnerAuthWitHashFromAction, +} from '../utils/authwit.js'; import { BaseWallet } from './base_wallet.js'; /** @@ -30,28 +35,25 @@ export class AccountWallet extends BaseWallet { } /** - * Computes an authentication witness from either a message or a caller and an action. - * If a message is provided, it will create a witness for the message directly. - * Otherwise, it will compute the message using the caller and the action. - * @param messageHashOrIntent - The message or the caller and action to approve + * Computes an authentication witness from either a message hash or an intent. + * + * If a message hash is provided, it will create a witness for the hash directly. + * Otherwise, it will compute the message hash using the intent, along with the + * chain id and the version values provided by the wallet. + * + * @param messageHashOrIntent - The message hash of the intent to approve * @returns The authentication witness */ - async createAuthWit( - messageHashOrIntent: - | Fr - | Buffer - | { - /** The caller to approve */ - caller: AztecAddress; - /** The action to approve */ - action: ContractFunctionInteraction | FunctionCall; - /** The chain id to approve */ - chainId?: Fr; - /** The version to approve */ - version?: Fr; - }, - ): Promise { - const messageHash = this.getMessageHash(messageHashOrIntent); + async createAuthWit(messageHashOrIntent: Fr | Buffer | IntentAction | IntentInnerHash): Promise { + let messageHash: Fr; + if (Buffer.isBuffer(messageHashOrIntent)) { + messageHash = Fr.fromBuffer(messageHashOrIntent); + } else if (messageHashOrIntent instanceof Fr) { + messageHash = messageHashOrIntent; + } else { + messageHash = this.getMessageHash(messageHashOrIntent); + } + const witness = await this.account.createAuthWit(messageHash); await this.pxe.addAuthWitness(witness); return witness; @@ -59,129 +61,92 @@ export class AccountWallet extends BaseWallet { /** * Returns a function interaction to set a message hash as authorized or revoked in this account. + * * Public calls can then consume this authorization. - * @param messageHashOrIntent - The message or the caller and action to authorize/revoke + * + * @param messageHashOrIntent - The message hash or intent to authorize/revoke * @param authorized - True to authorize, false to revoke authorization. * @returns - A function interaction. */ public setPublicAuthWit( - messageHashOrIntent: - | Fr - | Buffer - | { - /** The caller to approve */ - caller: AztecAddress; - /** The action to approve */ - action: ContractFunctionInteraction | FunctionCall; - /** The chain id to approve */ - chainId?: Fr; - /** The version to approve */ - version?: Fr; - }, + messageHashOrIntent: Fr | Buffer | IntentInnerHash | IntentAction, authorized: boolean, ): ContractFunctionInteraction { - const message = this.getMessageHash(messageHashOrIntent); + let messageHash: Fr; + if (Buffer.isBuffer(messageHashOrIntent)) { + messageHash = Fr.fromBuffer(messageHashOrIntent); + } else if (messageHashOrIntent instanceof Fr) { + messageHash = messageHashOrIntent; + } else { + messageHash = this.getMessageHash(messageHashOrIntent); + } + return new ContractFunctionInteraction(this, AuthRegistryAddress, this.getSetAuthorizedAbi(), [ - message, + messageHash, authorized, ]); } - /** - * Returns a function interaction to cancel a message hash as authorized or revoked. - * @param messageHashOrIntent - The message or the caller and action to revoke - * @returns - A function interaction. - */ - public cancelPublicAuthWit( - messageHashOrIntent: - | Fr - | Buffer - | { - /** The caller to approve */ - caller: AztecAddress; - /** The action to approve */ - action: ContractFunctionInteraction | FunctionCall; - /** The chain id to approve */ - chainId?: Fr; - /** The version to approve */ - version?: Fr; - }, - ): ContractFunctionInteraction { - return this.setPublicAuthWit(messageHashOrIntent, false); + private getInnerHashAndConsumer(intent: IntentInnerHash | IntentAction): { + /** The inner hash */ + innerHash: Fr; + /** The consumer of the authwit */ + consumer: AztecAddress; + } { + if ('caller' in intent && 'action' in intent) { + const action = intent.action instanceof ContractFunctionInteraction ? intent.action.request() : intent.action; + return { + innerHash: computeInnerAuthWitHashFromAction(intent.caller, action), + consumer: action.to, + }; + } else if (Buffer.isBuffer(intent.innerHash)) { + return { innerHash: Fr.fromBuffer(intent.innerHash), consumer: intent.consumer }; + } + return { innerHash: intent.innerHash, consumer: intent.consumer }; } /** - * Returns the message hash for the given message or authwit input. - * @param messageHashOrIntent - The message hash or the caller and action to authorize + * Returns the message hash for the given intent + * + * @param intent - A tuple of (consumer and inner hash) or (caller and action) * @returns The message hash */ - private getMessageHash( - messageHashOrIntent: - | Fr - | Buffer - | { - /** The caller to approve */ - caller: AztecAddress; - /** The action to approve */ - action: ContractFunctionInteraction | FunctionCall; - /** The chain id to approve */ - chainId?: Fr; - /** The version to approve */ - version?: Fr; - }, - ): Fr { - if (Buffer.isBuffer(messageHashOrIntent)) { - return Fr.fromBuffer(messageHashOrIntent); - } else if (messageHashOrIntent instanceof Fr) { - return messageHashOrIntent; - } else { - return computeAuthWitMessageHash( - messageHashOrIntent.caller, - messageHashOrIntent.chainId || this.getChainId(), - messageHashOrIntent.version || this.getVersion(), - messageHashOrIntent.action instanceof ContractFunctionInteraction - ? messageHashOrIntent.action.request() - : messageHashOrIntent.action, - ); - } + private getMessageHash(intent: IntentInnerHash | IntentAction): Fr { + const chainId = this.getChainId(); + const version = this.getVersion(); + return computeAuthWitMessageHash(intent, { chainId, version }); } /** * Lookup the validity of an authwit in private and public contexts. - * If the authwit have been consumed already (nullifier spent), will return false in both contexts. - * @param target - The target contract address - * @param messageHashOrIntent - The message hash or the caller and action to authorize/revoke + * + * Uses the chain id and version of the wallet. + * + * @param onBehalfOf - The address of the "approver" + * @param intent - The consumer and inner hash or the caller and action to lookup + * * @returns - A struct containing the validity of the authwit in private and public contexts. */ async lookupValidity( - target: AztecAddress, - messageHashOrIntent: - | Fr - | Buffer - | { - /** The caller to approve */ - caller: AztecAddress; - /** The action to approve */ - action: ContractFunctionInteraction | FunctionCall; - /** The chain id to approve */ - chainId?: Fr; - /** The version to approve */ - version?: Fr; - }, + onBehalfOf: AztecAddress, + intent: IntentInnerHash | IntentAction, ): Promise<{ /** boolean flag indicating if the authwit is valid in private context */ isValidInPrivate: boolean; /** boolean flag indicating if the authwit is valid in public context */ isValidInPublic: boolean; }> { - const messageHash = this.getMessageHash(messageHashOrIntent); + const { innerHash, consumer } = this.getInnerHashAndConsumer(intent); + + const messageHash = this.getMessageHash(intent); const results = { isValidInPrivate: false, isValidInPublic: false }; // Check private const witness = await this.getAuthWitness(messageHash); if (witness !== undefined) { - results.isValidInPrivate = (await new ContractFunctionInteraction(this, target, this.getLookupValidityAbi(), [ - messageHash, + results.isValidInPrivate = (await new ContractFunctionInteraction(this, onBehalfOf, this.getLookupValidityAbi(), [ + consumer, + innerHash, ]).simulate()) as boolean; } @@ -190,7 +155,7 @@ export class AccountWallet extends BaseWallet { this, AuthRegistryAddress, this.getIsConsumableAbi(), - [target, messageHash], + [onBehalfOf, messageHash], ).simulate()) as boolean; return results; @@ -220,31 +185,6 @@ export class AccountWallet extends BaseWallet { await interaction.send().wait(); } - /** - * Returns a function interaction to cancel a message hash as authorized in this account. - * @param messageHashOrIntent - The message or the caller and action to authorize/revoke - * @returns - A function interaction. - */ - public cancelAuthWit( - messageHashOrIntent: - | Fr - | Buffer - | { - /** The caller to approve */ - caller: AztecAddress; - /** The action to approve */ - action: ContractFunctionInteraction | FunctionCall; - /** The chain id to approve */ - chainId?: Fr; - /** The version to approve */ - version?: Fr; - }, - ): ContractFunctionInteraction { - const message = this.getMessageHash(messageHashOrIntent); - const args = [message]; - return new ContractFunctionInteraction(this, this.getAddress(), this.getCancelAuthwitAbi(), args); - } - /** Returns the complete address of the account that implements this wallet. */ public getCompleteAddress() { return this.account.getCompleteAddress(); @@ -278,24 +218,6 @@ export class AccountWallet extends BaseWallet { }; } - private getCancelAuthwitAbi(): FunctionAbi { - return { - name: 'cancel_authwit', - isInitializer: false, - functionType: FunctionType.PRIVATE, - isInternal: true, - isStatic: false, - parameters: [ - { - name: 'message_hash', - type: { kind: 'field' }, - visibility: 'private' as ABIParameterVisibility, - }, - ], - returnTypes: [], - }; - } - private getLookupValidityAbi(): FunctionAbi { return { name: 'lookup_validity', diff --git a/yarn-project/aztec.js/src/wallet/base_wallet.ts b/yarn-project/aztec.js/src/wallet/base_wallet.ts index 974895fa96e..247b509fbaf 100644 --- a/yarn-project/aztec.js/src/wallet/base_wallet.ts +++ b/yarn-project/aztec.js/src/wallet/base_wallet.ts @@ -2,7 +2,6 @@ import { type AuthWitness, type EventMetadata, type ExtendedNote, - type FunctionCall, type GetUnencryptedLogsResponse, type IncomingNotesFilter, type L2Block, @@ -32,8 +31,8 @@ import { type ContractClassWithId, type ContractInstanceWithAddress } from '@azt import { type NodeInfo } from '@aztec/types/interfaces'; import { type Wallet } from '../account/wallet.js'; -import { type ContractFunctionInteraction } from '../contract/contract_function_interaction.js'; import { type ExecutionRequestInit } from '../entrypoint/entrypoint.js'; +import { type IntentAction, type IntentInnerHash } from '../utils/authwit.js'; /** * A base class for Wallet implementations @@ -49,21 +48,7 @@ export abstract class BaseWallet implements Wallet { abstract createTxExecutionRequest(exec: ExecutionRequestInit): Promise; - abstract createAuthWit( - messageHashOrIntent: - | Fr - | Buffer - | { - /** The caller to approve */ - caller: AztecAddress; - /** The action to approve */ - action: ContractFunctionInteraction | FunctionCall; - /** The chain id to approve */ - chainId?: Fr; - /** The version to approve */ - version?: Fr; - }, - ): Promise; + abstract createAuthWit(intent: Fr | Buffer | IntentInnerHash | IntentAction): Promise; abstract rotateNullifierKeys(newNskM: Fq): Promise; diff --git a/yarn-project/aztec.js/src/wallet/signerless_wallet.ts b/yarn-project/aztec.js/src/wallet/signerless_wallet.ts index bba8c3ec66e..f69c78d5f33 100644 --- a/yarn-project/aztec.js/src/wallet/signerless_wallet.ts +++ b/yarn-project/aztec.js/src/wallet/signerless_wallet.ts @@ -3,6 +3,7 @@ import { type CompleteAddress, type Fq, type Fr } from '@aztec/circuits.js'; import { DefaultEntrypoint } from '../entrypoint/default_entrypoint.js'; import { type EntrypointInterface, type ExecutionRequestInit } from '../entrypoint/entrypoint.js'; +import { type IntentAction, type IntentInnerHash } from '../utils/authwit.js'; import { BaseWallet } from './base_wallet.js'; /** @@ -12,7 +13,6 @@ export class SignerlessWallet extends BaseWallet { constructor(pxe: PXE, private entrypoint?: EntrypointInterface) { super(pxe); } - async createTxExecutionRequest(execution: ExecutionRequestInit): Promise { let entrypoint = this.entrypoint; if (!entrypoint) { @@ -39,7 +39,7 @@ export class SignerlessWallet extends BaseWallet { throw new Error('SignerlessWallet: Method getCompleteAddress not implemented.'); } - createAuthWit(_messageHash: Fr): Promise { + createAuthWit(_intent: Fr | Buffer | IntentInnerHash | IntentAction): Promise { throw new Error('SignerlessWallet: Method createAuthWit not implemented.'); } diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index 77ce004e452..f226f6d2e16 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -206,13 +206,14 @@ export enum GeneratorIndex { FUNCTION_ARGS = 44, AUTHWIT_INNER = 45, AUTHWIT_OUTER = 46, - NSK_M = 47, - IVSK_M = 48, - OVSK_M = 49, - TSK_M = 50, - PUBLIC_KEYS_HASH = 51, - NOTE_NULLIFIER = 52, - INNER_NOTE_HASH = 53, - NOTE_CONTENT_HASH = 54, - SYMMETRIC_KEY = 55, + AUTHWIT_NULLIFIER = 47, + NSK_M = 48, + IVSK_M = 49, + OVSK_M = 50, + TSK_M = 51, + PUBLIC_KEYS_HASH = 52, + NOTE_NULLIFIER = 53, + INNER_NOTE_HASH = 54, + NOTE_CONTENT_HASH = 55, + SYMMETRIC_KEY = 56, } diff --git a/yarn-project/circuits.js/src/contract/__snapshots__/contract_address.test.ts.snap b/yarn-project/circuits.js/src/contract/__snapshots__/contract_address.test.ts.snap index 2d413b4089c..10e93a7af31 100644 --- a/yarn-project/circuits.js/src/contract/__snapshots__/contract_address.test.ts.snap +++ b/yarn-project/circuits.js/src/contract/__snapshots__/contract_address.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`ContractAddress computeContractAddressFromInstance 1`] = `"0x0bed63221d281713007bfb0c063e1f61d0646404fb3701b99bb92f41b6390604"`; +exports[`ContractAddress computeContractAddressFromInstance 1`] = `"0x2a192ee63791ad5e219b63db872bf54ba245afbc2c1287f4ba036b8f58fad740"`; exports[`ContractAddress computeInitializationHash 1`] = `Fr<0x109865e4b959adba34b722e72a69baaf9ee78e31bb1042318f0d91006ed86780>`; diff --git a/yarn-project/circuits.js/src/keys/derivation.test.ts b/yarn-project/circuits.js/src/keys/derivation.test.ts index f41aa0c0d3f..c3c1e0bb59d 100644 --- a/yarn-project/circuits.js/src/keys/derivation.test.ts +++ b/yarn-project/circuits.js/src/keys/derivation.test.ts @@ -11,7 +11,7 @@ describe('🔑', () => { const masterOutgoingViewingPublicKey = new Point(new Fr(5), new Fr(6)); const masterTaggingPublicKey = new Point(new Fr(7), new Fr(8)); - const expected = Fr.fromString('0x1936abe4f6a920d16a9f6917f10a679507687e2cd935dd1f1cdcb1e908c027f3'); + const expected = Fr.fromString('0x2406c1c88b7afc13052335bb9af43fd35034b5ba0a9caab76eda2833cf8ec717'); expect( new PublicKeys( masterNullifierPublicKey, diff --git a/yarn-project/circuits.js/src/structs/complete_address.test.ts b/yarn-project/circuits.js/src/structs/complete_address.test.ts index 25c0de180c8..4fd6d528283 100644 --- a/yarn-project/circuits.js/src/structs/complete_address.test.ts +++ b/yarn-project/circuits.js/src/structs/complete_address.test.ts @@ -38,11 +38,11 @@ describe('CompleteAddress', () => { // docs:start:instantiate-complete-address // Typically a recipient would share their complete address with the sender const completeAddressFromString = CompleteAddress.fromString( - '0x09bc7031bb21627cce6aac1dc710ecc92acd8475149c530a4bb57df63d9d6fe902a9372135ce5b49b46102732fabd742c31642543396013dde5b460075864607264c605bc115c6cb92a4db0a6b893fd3777341078693d0af22e3ff53f4c2ee2a2fae73914fc50d325e2707a8e996f1ad498429f715f998225dc6bd2ede05aaee055ee137d28b634322e0ea98afc42dfc48833e8d2879c34d23d6d1d337069cca212af0f28b7865b339e202a0077fd3bd8dddc472d055945ad99c02dcccd28bb22bb3585fca3e5751c9913521a390458d63e4d9b292e4872582f3b13da214470c14083a4567cf4f1e92696e6c01923bc6a8b414159446268b12fe8669ce44f1f5196561aca6c654d2405a5653002cba5552b50b6ce1afc9515ed6682507abcb3010040d791aeb30138efc9c7d36b47684af2f26f686672448349f05934ae7bbbf', + '0x1de12596818ab6bc3584b943f791b206ff588d3c307358ab6918f59ed7d381bc02a9372135ce5b49b46102732fabd742c31642543396013dde5b460075864607264c605bc115c6cb92a4db0a6b893fd3777341078693d0af22e3ff53f4c2ee2a2fae73914fc50d325e2707a8e996f1ad498429f715f998225dc6bd2ede05aaee055ee137d28b634322e0ea98afc42dfc48833e8d2879c34d23d6d1d337069cca212af0f28b7865b339e202a0077fd3bd8dddc472d055945ad99c02dcccd28bb22bb3585fca3e5751c9913521a390458d63e4d9b292e4872582f3b13da214470c14083a4567cf4f1e92696e6c01923bc6a8b414159446268b12fe8669ce44f1f5196561aca6c654d2405a5653002cba5552b50b6ce1afc9515ed6682507abcb3010040d791aeb30138efc9c7d36b47684af2f26f686672448349f05934ae7bbbf', ); // Alternatively, a recipient could share the individual components with the sender - const address = Fr.fromString('0x09bc7031bb21627cce6aac1dc710ecc92acd8475149c530a4bb57df63d9d6fe9'); + const address = Fr.fromString('0x1de12596818ab6bc3584b943f791b206ff588d3c307358ab6918f59ed7d381bc'); const npkM = Point.fromString( '0x02a9372135ce5b49b46102732fabd742c31642543396013dde5b460075864607264c605bc115c6cb92a4db0a6b893fd3777341078693d0af22e3ff53f4c2ee2a', ); diff --git a/yarn-project/end-to-end/src/e2e_authwit.test.ts b/yarn-project/end-to-end/src/e2e_authwit.test.ts index 8200ce331b7..471aca74685 100644 --- a/yarn-project/end-to-end/src/e2e_authwit.test.ts +++ b/yarn-project/end-to-end/src/e2e_authwit.test.ts @@ -1,5 +1,5 @@ -import { type AccountWallet, Fr, computeInnerAuthWitHash, computeOuterAuthWitHash } from '@aztec/aztec.js'; -import { AuthRegistryContract, SchnorrAccountContract } from '@aztec/noir-contracts.js'; +import { type AccountWallet, Fr, computeAuthWitMessageHash, computeInnerAuthWitHash } from '@aztec/aztec.js'; +import { AuthRegistryContract, AuthWitTestContract } from '@aztec/noir-contracts.js'; import { getCanonicalAuthRegistry } from '@aztec/protocol-contracts/auth-registry'; import { jest } from '@jest/globals'; @@ -16,6 +16,7 @@ describe('e2e_authwit_tests', () => { let chainId: Fr; let version: Fr; + let auth: AuthWitTestContract; beforeAll(async () => { ({ wallets } = await setup(2)); @@ -26,164 +27,122 @@ describe('e2e_authwit_tests', () => { const nodeInfo = await wallets[0].getNodeInfo(); chainId = new Fr(nodeInfo.chainId); version = new Fr(nodeInfo.protocolVersion); + + auth = await AuthWitTestContract.deploy(wallets[0]).send().deployed(); }); describe('Private', () => { describe('arbitrary data', () => { it('happy path', async () => { + // What are we doing here: + // 1. We compute an inner hash which is here just a hash of random data + // 2. We then compute the outer, which is binding it to a "consumer" (here the "auth" contract) + // 3. We then create an authwit for this outer hash. + // 4. We add this authwit to the wallet[1] + // 5. We check that the authwit is valid in private for wallet[0] (check that it is signed by 0) + // 6. We check that the authwit is NOT valid in private for wallet[1] (check that it is not signed by 1) + // docs:start:compute_inner_authwit_hash const innerHash = computeInnerAuthWitHash([Fr.fromString('0xdead')]); // docs:end:compute_inner_authwit_hash // docs:start:compute_outer_authwit_hash - const outerHash = computeOuterAuthWitHash(wallets[1].getAddress(), chainId, version, innerHash); + + const intent = { consumer: auth.address, innerHash }; // docs:end:compute_outer_authwit_hash // docs:start:create_authwit - const witness = await wallets[0].createAuthWit(outerHash); + const witness = await wallets[0].createAuthWit(intent); // docs:end:create_authwit await wallets[1].addAuthWitness(witness); // Check that the authwit is valid in private for wallets[0] - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerHash)).toEqual({ + expect(await wallets[0].lookupValidity(wallets[0].getAddress(), intent)).toEqual({ isValidInPrivate: true, isValidInPublic: false, }); // Check that the authwit is NOT valid in private for wallets[1] - expect(await wallets[0].lookupValidity(wallets[1].getAddress(), outerHash)).toEqual({ + expect(await wallets[0].lookupValidity(wallets[1].getAddress(), intent)).toEqual({ isValidInPrivate: false, isValidInPublic: false, }); - const c = await SchnorrAccountContract.at(wallets[0].getAddress(), wallets[0]); - await c.withWallet(wallets[1]).methods.spend_private_authwit(innerHash).send().wait(); + // Consume the inner hash using the wallets[0] as the "on behalf of". + await auth.withWallet(wallets[1]).methods.consume(wallets[0].getAddress(), innerHash).send().wait(); - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerHash)).toEqual({ + expect(await wallets[0].lookupValidity(wallets[0].getAddress(), intent)).toEqual({ isValidInPrivate: false, isValidInPublic: false, }); - }); + // Try to consume the same authwit again, it should fail + await expect( + auth.withWallet(wallets[1]).methods.consume(wallets[0].getAddress(), innerHash).send().wait(), + ).rejects.toThrow(DUPLICATE_NULLIFIER_ERROR); + }); describe('failure case', () => { - it('cancel before usage', async () => { - const innerHash = computeInnerAuthWitHash([Fr.fromString('0xdead'), Fr.fromString('0xbeef')]); - const outerHash = computeOuterAuthWitHash(wallets[1].getAddress(), chainId, version, innerHash); - - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerHash)).toEqual({ - isValidInPrivate: false, - isValidInPublic: false, - }); - - const witness = await wallets[0].createAuthWit(outerHash); - await wallets[1].addAuthWitness(witness); - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerHash)).toEqual({ - isValidInPrivate: true, - isValidInPublic: false, - }); - await wallets[0].cancelAuthWit(outerHash).send().wait(); - - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerHash)).toEqual({ - isValidInPrivate: false, - isValidInPublic: false, - }); - - const c = await SchnorrAccountContract.at(wallets[0].getAddress(), wallets[0]); - const txCancelledAuthwit = c.withWallet(wallets[1]).methods.spend_private_authwit(innerHash).send(); - - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerHash)).toEqual({ - isValidInPrivate: false, - isValidInPublic: false, - }); - - // The transaction should be dropped because of a cancelled authwit (duplicate nullifier) - await expect(txCancelledAuthwit.wait()).rejects.toThrow(DUPLICATE_NULLIFIER_ERROR); - }); - it('invalid chain id', async () => { - const invalidChainId = Fr.random(); - const innerHash = computeInnerAuthWitHash([Fr.fromString('0xdead'), Fr.fromString('0xbeef')]); - const outerHash = computeOuterAuthWitHash(wallets[1].getAddress(), invalidChainId, version, innerHash); - const outerCorrectHash = computeOuterAuthWitHash(wallets[1].getAddress(), chainId, version, innerHash); + const intent = { consumer: auth.address, innerHash }; - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerHash)).toEqual({ - isValidInPrivate: false, - isValidInPublic: false, - }); + const messageHash = computeAuthWitMessageHash(intent, { chainId: Fr.random(), version }); + const expectedMessageHash = computeAuthWitMessageHash(intent, { chainId, version }); - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerCorrectHash)).toEqual({ + expect(await wallets[0].lookupValidity(wallets[0].getAddress(), intent)).toEqual({ isValidInPrivate: false, isValidInPublic: false, }); - const witness = await wallets[0].createAuthWit(outerHash); + const witness = await wallets[0].createAuthWit(messageHash); await wallets[1].addAuthWitness(witness); - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerHash)).toEqual({ - isValidInPrivate: true, - isValidInPublic: false, - }); - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerCorrectHash)).toEqual({ + + // We should NOT see it as valid, even though we have the authwit, since the chain id is wrong + expect(await wallets[0].lookupValidity(wallets[0].getAddress(), intent)).toEqual({ isValidInPrivate: false, isValidInPublic: false, }); - const c = await SchnorrAccountContract.at(wallets[0].getAddress(), wallets[0]); - const txCancelledAuthwit = c.withWallet(wallets[1]).methods.spend_private_authwit(innerHash).send(); + // The transaction should be dropped because of the invalid chain id + await expect( + auth.withWallet(wallets[1]).methods.consume(wallets[0].getAddress(), innerHash).simulate(), + ).rejects.toThrow(`Unknown auth witness for message hash ${expectedMessageHash.toString()}`); - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerHash)).toEqual({ - isValidInPrivate: true, - isValidInPublic: false, - }); - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerCorrectHash)).toEqual({ + expect(await wallets[0].lookupValidity(wallets[0].getAddress(), intent)).toEqual({ isValidInPrivate: false, isValidInPublic: false, }); - - // The transaction should be dropped because of the invalid chain id - await expect(txCancelledAuthwit.wait()).rejects.toThrow(DUPLICATE_NULLIFIER_ERROR); }); it('invalid version', async () => { - const invalidVersion = Fr.random(); - const innerHash = computeInnerAuthWitHash([Fr.fromString('0xdead'), Fr.fromString('0xbeef')]); - const outerHash = computeOuterAuthWitHash(wallets[1].getAddress(), chainId, invalidVersion, innerHash); - const outerCorrectHash = computeOuterAuthWitHash(wallets[1].getAddress(), chainId, version, innerHash); + const intent = { consumer: auth.address, innerHash }; - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerHash)).toEqual({ - isValidInPrivate: false, - isValidInPublic: false, - }); + const messageHash = computeAuthWitMessageHash(intent, { chainId, version: Fr.random() }); - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerCorrectHash)).toEqual({ + const expectedMessageHash = computeAuthWitMessageHash(intent, { chainId, version }); + + expect(await wallets[0].lookupValidity(wallets[0].getAddress(), intent)).toEqual({ isValidInPrivate: false, isValidInPublic: false, }); - const witness = await wallets[0].createAuthWit(outerHash); + const witness = await wallets[0].createAuthWit(messageHash); await wallets[1].addAuthWitness(witness); - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerHash)).toEqual({ - isValidInPrivate: true, - isValidInPublic: false, - }); - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerCorrectHash)).toEqual({ + + // We should NOT see it as valid, even though we have the authwit, since the version is wrong + expect(await wallets[0].lookupValidity(wallets[0].getAddress(), intent)).toEqual({ isValidInPrivate: false, isValidInPublic: false, }); - const c = await SchnorrAccountContract.at(wallets[0].getAddress(), wallets[0]); - const txCancelledAuthwit = c.withWallet(wallets[1]).methods.spend_private_authwit(innerHash).send(); + // The transaction should be dropped because of the invalid version + await expect( + auth.withWallet(wallets[1]).methods.consume(wallets[0].getAddress(), innerHash).simulate(), + ).rejects.toThrow(`Unknown auth witness for message hash ${expectedMessageHash.toString()}`); - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerHash)).toEqual({ - isValidInPrivate: true, - isValidInPublic: false, - }); - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerCorrectHash)).toEqual({ + expect(await wallets[0].lookupValidity(wallets[0].getAddress(), intent)).toEqual({ isValidInPrivate: false, isValidInPublic: false, }); - - // The transaction should be dropped because of the invalid version - await expect(txCancelledAuthwit.wait()).rejects.toThrow(DUPLICATE_NULLIFIER_ERROR); }); }); }); @@ -193,16 +152,18 @@ describe('e2e_authwit_tests', () => { describe('arbitrary data', () => { it('happy path', async () => { const innerHash = computeInnerAuthWitHash([Fr.fromString('0xdead'), Fr.fromString('0x01')]); - const outerHash = computeOuterAuthWitHash(wallets[1].getAddress(), chainId, version, innerHash); - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerHash)).toEqual({ + + const intent = { consumer: wallets[1].getAddress(), innerHash }; + + expect(await wallets[0].lookupValidity(wallets[0].getAddress(), intent)).toEqual({ isValidInPrivate: false, isValidInPublic: false, }); // docs:start:set_public_authwit - await wallets[0].setPublicAuthWit(outerHash, true).send().wait(); + await wallets[0].setPublicAuthWit(intent, true).send().wait(); // docs:end:set_public_authwit - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerHash)).toEqual({ + expect(await wallets[0].lookupValidity(wallets[0].getAddress(), intent)).toEqual({ isValidInPrivate: false, isValidInPublic: true, }); @@ -210,7 +171,7 @@ describe('e2e_authwit_tests', () => { const registry = await AuthRegistryContract.at(getCanonicalAuthRegistry().instance.address, wallets[1]); await registry.methods.consume(wallets[0].getAddress(), innerHash).send().wait(); - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerHash)).toEqual({ + expect(await wallets[0].lookupValidity(wallets[0].getAddress(), intent)).toEqual({ isValidInPrivate: false, isValidInPublic: false, }); @@ -219,23 +180,23 @@ describe('e2e_authwit_tests', () => { describe('failure case', () => { it('cancel before usage', async () => { const innerHash = computeInnerAuthWitHash([Fr.fromString('0xdead'), Fr.fromString('0x02')]); - const outerHash = computeOuterAuthWitHash(wallets[1].getAddress(), chainId, version, innerHash); + const intent = { consumer: auth.address, innerHash }; - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerHash)).toEqual({ + expect(await wallets[0].lookupValidity(wallets[0].getAddress(), intent)).toEqual({ isValidInPrivate: false, isValidInPublic: false, }); - await wallets[0].setPublicAuthWit(outerHash, true).send().wait(); + await wallets[0].setPublicAuthWit(intent, true).send().wait(); - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerHash)).toEqual({ + expect(await wallets[0].lookupValidity(wallets[0].getAddress(), intent)).toEqual({ isValidInPrivate: false, isValidInPublic: true, }); - await wallets[0].cancelPublicAuthWit(outerHash).send().wait(); + await wallets[0].setPublicAuthWit(intent, false).send().wait(); - expect(await wallets[0].lookupValidity(wallets[0].getAddress(), outerHash)).toEqual({ + expect(await wallets[0].lookupValidity(wallets[0].getAddress(), intent)).toEqual({ isValidInPrivate: false, isValidInPublic: false, }); diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/burn.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/burn.test.ts index 5e336d1b63d..6c6d95ab7be 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/burn.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/burn.test.ts @@ -194,10 +194,8 @@ describe('e2e_blacklist_token_contract burn', () => { // We need to compute the message we want to sign and add it to the wallet as approved const action = asset.withWallet(wallets[1]).methods.burn(wallets[0].getAddress(), amount, nonce); const messageHash = computeAuthWitMessageHash( - wallets[1].getAddress(), - wallets[0].getChainId(), - wallets[0].getVersion(), - action.request(), + { caller: wallets[1].getAddress(), action: action.request() }, + { chainId: wallets[0].getChainId(), version: wallets[0].getVersion() }, ); await expect(action.prove()).rejects.toThrow(`Unknown auth witness for message hash ${messageHash.toString()}`); @@ -212,10 +210,8 @@ describe('e2e_blacklist_token_contract burn', () => { // We need to compute the message we want to sign and add it to the wallet as approved const action = asset.withWallet(wallets[2]).methods.burn(wallets[0].getAddress(), amount, nonce); const expectedMessageHash = computeAuthWitMessageHash( - wallets[2].getAddress(), - wallets[0].getChainId(), - wallets[0].getVersion(), - action.request(), + { caller: wallets[2].getAddress(), action: action.request() }, + { chainId: wallets[0].getChainId(), version: wallets[0].getVersion() }, ); const witness = await wallets[0].createAuthWit({ caller: wallets[1].getAddress(), action }); diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_private.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_private.test.ts index ac8176b9e68..75f8c919bad 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_private.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_private.test.ts @@ -136,10 +136,8 @@ describe('e2e_blacklist_token_contract transfer private', () => { .withWallet(wallets[1]) .methods.transfer(wallets[0].getAddress(), wallets[1].getAddress(), amount, nonce); const messageHash = computeAuthWitMessageHash( - wallets[1].getAddress(), - wallets[0].getChainId(), - wallets[0].getVersion(), - action.request(), + { caller: wallets[1].getAddress(), action: action.request() }, + { chainId: wallets[0].getChainId(), version: wallets[0].getVersion() }, ); await expect(action.prove()).rejects.toThrow(`Unknown auth witness for message hash ${messageHash.toString()}`); @@ -156,10 +154,8 @@ describe('e2e_blacklist_token_contract transfer private', () => { .withWallet(wallets[2]) .methods.transfer(wallets[0].getAddress(), wallets[1].getAddress(), amount, nonce); const expectedMessageHash = computeAuthWitMessageHash( - wallets[2].getAddress(), - wallets[0].getChainId(), - wallets[0].getVersion(), - action.request(), + { caller: wallets[2].getAddress(), action: action.request() }, + { chainId: wallets[0].getChainId(), version: wallets[0].getVersion() }, ); const witness = await wallets[0].createAuthWit({ caller: wallets[1].getAddress(), action }); diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/unshielding.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/unshielding.test.ts index 8d11daf3c5e..4d877859f53 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/unshielding.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/unshielding.test.ts @@ -113,10 +113,8 @@ describe('e2e_blacklist_token_contract unshielding', () => { .withWallet(wallets[2]) .methods.unshield(wallets[0].getAddress(), wallets[1].getAddress(), amount, nonce); const expectedMessageHash = computeAuthWitMessageHash( - wallets[2].getAddress(), - wallets[0].getChainId(), - wallets[0].getVersion(), - action.request(), + { caller: wallets[2].getAddress(), action: action.request() }, + { chainId: wallets[0].getChainId(), version: wallets[0].getVersion() }, ); // Both wallets are connected to same node and PXE so we could just insert directly diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts index 4ba3f4990f0..f65ba985a72 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts @@ -201,10 +201,11 @@ describe('e2e_cross_chain_messaging', () => { const withdrawAmount = 9n; const nonce = Fr.random(); const expectedBurnMessageHash = computeAuthWitMessageHash( - l2Bridge.address, - user1Wallet.getChainId(), - user1Wallet.getVersion(), - l2Token.methods.burn(user1Wallet.getAddress(), withdrawAmount, nonce).request(), + { + caller: l2Bridge.address, + action: l2Token.methods.burn(user1Wallet.getAddress(), withdrawAmount, nonce).request(), + }, + { chainId: user1Wallet.getChainId(), version: user1Wallet.getVersion() }, ); // Should fail as owner has not given approval to bridge burn their funds. await expect( diff --git a/yarn-project/end-to-end/src/e2e_fees/failures.test.ts b/yarn-project/end-to-end/src/e2e_fees/failures.test.ts index 160c49e6d2c..ceeee8d7655 100644 --- a/yarn-project/end-to-end/src/e2e_fees/failures.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/failures.test.ts @@ -6,7 +6,6 @@ import { FunctionSelector, PublicFeePaymentMethod, TxStatus, - computeAuthWitMessageHash, } from '@aztec/aztec.js'; import { Gas, GasSettings } from '@aztec/circuits.js'; import { FunctionType } from '@aztec/foundation/abi'; @@ -234,25 +233,27 @@ class BuggedSetupFeePaymentMethod extends PublicFeePaymentMethod { override getFunctionCalls(gasSettings: GasSettings): Promise { const maxFee = gasSettings.getFeeLimit(); const nonce = Fr.random(); - const messageHash = computeAuthWitMessageHash( - this.paymentContract, - this.wallet.getChainId(), - this.wallet.getVersion(), - { - name: 'transfer_public', - args: [this.wallet.getAddress(), this.paymentContract, maxFee, nonce], - selector: FunctionSelector.fromSignature('transfer_public((Field),(Field),Field,Field)'), - type: FunctionType.PUBLIC, - isStatic: false, - to: this.asset, - returnTypes: [], - }, - ); const tooMuchFee = new Fr(maxFee.toBigInt() * 2n); return Promise.resolve([ - this.wallet.setPublicAuthWit(messageHash, true).request(), + this.wallet + .setPublicAuthWit( + { + caller: this.paymentContract, + action: { + name: 'transfer_public', + args: [this.wallet.getAddress(), this.paymentContract, maxFee, nonce], + selector: FunctionSelector.fromSignature('transfer_public((Field),(Field),Field,Field)'), + type: FunctionType.PUBLIC, + isStatic: false, + to: this.asset, + returnTypes: [], + }, + }, + true, + ) + .request(), { name: 'fee_entrypoint_public', to: this.paymentContract, diff --git a/yarn-project/end-to-end/src/e2e_lending_contract.test.ts b/yarn-project/end-to-end/src/e2e_lending_contract.test.ts index 7e22eecf358..9d1c0007931 100644 --- a/yarn-project/end-to-end/src/e2e_lending_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_lending_contract.test.ts @@ -5,7 +5,6 @@ import { ExtendedNote, Fr, Note, - computeAuthWitMessageHash, computeSecretHash, } from '@aztec/aztec.js'; import { LendingContract, PriceFeedContract, TokenContract } from '@aztec/noir-contracts.js'; @@ -320,17 +319,19 @@ describe('e2e_lending_contract', () => { it('Repay: 🍌 -> 🏦', async () => { const repayAmount = 20n; - const nonce = Fr.random(); - const messageHash = computeAuthWitMessageHash( - lendingContract.address, - wallet.getChainId(), - wallet.getVersion(), - stableCoin.methods.burn_public(lendingAccount.address, repayAmount, nonce).request(), - ); // Add it to the wallet as approved - await wallet.setPublicAuthWit(messageHash, true).send().wait(); + await wallet + .setPublicAuthWit( + { + caller: lendingContract.address, + action: stableCoin.methods.burn_public(lendingAccount.address, repayAmount, nonce).request(), + }, + true, + ) + .send() + .wait(); await lendingSim.progressTime(TIME_JUMP); lendingSim.repayPublic(lendingAccount.address, lendingAccount.address.toField(), repayAmount); diff --git a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/deposits.test.ts b/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/deposits.test.ts index 306e9e7bb06..60e6edc6c3f 100644 --- a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/deposits.test.ts +++ b/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging/deposits.test.ts @@ -1,4 +1,4 @@ -import { Fr, computeAuthWitMessageHash } from '@aztec/aztec.js'; +import { Fr } from '@aztec/aztec.js'; import { NO_L1_TO_L2_MSG_ERROR } from '../fixtures/fixtures.js'; import { PublicCrossChainMessagingContractTest } from './public_cross_chain_messaging_contract_test.js'; @@ -7,7 +7,6 @@ describe('e2e_public_cross_chain_messaging deposits', () => { const t = new PublicCrossChainMessagingContractTest('deposits'); let { - wallets, crossChainTestHarness, ethAccount, aztecNode, @@ -23,7 +22,7 @@ describe('e2e_public_cross_chain_messaging deposits', () => { await t.applyBaseSnapshots(); await t.setup(); // Have to destructure again to ensure we have latest refs. - ({ wallets, crossChainTestHarness, user1Wallet, user2Wallet } = t); + ({ crossChainTestHarness, user1Wallet, user2Wallet } = t); ethAccount = crossChainTestHarness.ethAccount; aztecNode = crossChainTestHarness.aztecNode; @@ -75,13 +74,16 @@ describe('e2e_public_cross_chain_messaging deposits', () => { // 4. Give approval to bridge to burn owner's funds: const withdrawAmount = 9n; const nonce = Fr.random(); - const burnMessageHash = computeAuthWitMessageHash( - l2Bridge.address, - wallets[0].getChainId(), - wallets[0].getVersion(), - l2Token.methods.burn_public(ownerAddress, withdrawAmount, nonce).request(), - ); - await user1Wallet.setPublicAuthWit(burnMessageHash, true).send().wait(); + await user1Wallet + .setPublicAuthWit( + { + caller: l2Bridge.address, + action: l2Token.methods.burn_public(ownerAddress, withdrawAmount, nonce).request(), + }, + true, + ) + .send() + .wait(); // 5. Withdraw owner's funds from L2 to L1 logger.verbose('5. Withdraw owner funds from L2 to L1'); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/burn.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/burn.test.ts index f736b47a64c..1c89c74ac20 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/burn.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/burn.test.ts @@ -187,10 +187,8 @@ describe('e2e_token_contract burn', () => { // We need to compute the message we want to sign and add it to the wallet as approved const action = asset.withWallet(wallets[1]).methods.burn(accounts[0].address, amount, nonce); const messageHash = computeAuthWitMessageHash( - accounts[1].address, - wallets[0].getChainId(), - wallets[0].getVersion(), - action.request(), + { caller: accounts[1].address, action: action.request() }, + { chainId: wallets[0].getChainId(), version: wallets[0].getVersion() }, ); await expect(action.simulate()).rejects.toThrow( @@ -207,10 +205,8 @@ describe('e2e_token_contract burn', () => { // We need to compute the message we want to sign and add it to the wallet as approved const action = asset.withWallet(wallets[2]).methods.burn(accounts[0].address, amount, nonce); const expectedMessageHash = computeAuthWitMessageHash( - accounts[2].address, - wallets[0].getChainId(), - wallets[0].getVersion(), - action.request(), + { caller: accounts[2].address, action: action.request() }, + { chainId: wallets[0].getChainId(), version: wallets[0].getVersion() }, ); const witness = await wallets[0].createAuthWit({ caller: accounts[1].address, action }); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/transfer_private.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/transfer_private.test.ts index a85f2de98fe..4d1536e4df4 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/transfer_private.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/transfer_private.test.ts @@ -1,4 +1,10 @@ -import { AztecAddress, CompleteAddress, Fr, computeAuthWitMessageHash } from '@aztec/aztec.js'; +import { + AztecAddress, + CompleteAddress, + Fr, + computeAuthWitMessageHash, + computeInnerAuthWitHashFromAction, +} from '@aztec/aztec.js'; import { DUPLICATE_NULLIFIER_ERROR } from '../fixtures/fixtures.js'; import { TokenContractTest } from './token_contract_test.js'; @@ -147,10 +153,11 @@ describe('e2e_token_contract transfer private', () => { .withWallet(wallets[1]) .methods.transfer_from(accounts[0].address, accounts[1].address, amount, nonce); const messageHash = computeAuthWitMessageHash( - accounts[1].address, - wallets[0].getChainId(), - wallets[0].getVersion(), - action.request(), + { caller: accounts[1].address, action: action.request() }, + { + chainId: wallets[0].getChainId(), + version: wallets[0].getVersion(), + }, ); await expect(action.simulate()).rejects.toThrow( @@ -169,10 +176,11 @@ describe('e2e_token_contract transfer private', () => { .withWallet(wallets[2]) .methods.transfer_from(accounts[0].address, accounts[1].address, amount, nonce); const expectedMessageHash = computeAuthWitMessageHash( - accounts[2].address, - wallets[0].getChainId(), - wallets[0].getVersion(), - action.request(), + { caller: accounts[2].address, action: action.request() }, + { + chainId: wallets[0].getChainId(), + version: wallets[0].getVersion(), + }, ); const witness = await wallets[0].createAuthWit({ caller: accounts[1].address, action }); @@ -195,44 +203,33 @@ describe('e2e_token_contract transfer private', () => { .withWallet(wallets[1]) .methods.transfer_from(accounts[0].address, accounts[1].address, amount, nonce); - const witness = await wallets[0].createAuthWit({ caller: accounts[1].address, action }); - await wallets[1].addAuthWitness(witness); - - await wallets[0].cancelAuthWit(witness.requestHash).send().wait(); - - // Perform the transfer, should fail because nullifier already emitted - const txCancelledAuthwit = asset - .withWallet(wallets[1]) - .methods.transfer_from(accounts[0].address, accounts[1].address, amount, nonce) - .send(); - await expect(txCancelledAuthwit.wait()).rejects.toThrowError(DUPLICATE_NULLIFIER_ERROR); - }); + const intent = { caller: accounts[1].address, action }; - it('transfer on behalf of other, cancelled authwit, flow 2', async () => { - const balance0 = await asset.methods.balance_of_private(accounts[0].address).simulate(); - const amount = balance0 / 2n; - const nonce = Fr.random(); - expect(amount).toBeGreaterThan(0n); + const witness = await wallets[0].createAuthWit(intent); + await wallets[1].addAuthWitness(witness); - // We need to compute the message we want to sign and add it to the wallet as approved - const action = asset - .withWallet(wallets[1]) - .methods.transfer_from(accounts[0].address, accounts[1].address, amount, nonce); + expect(await wallets[0].lookupValidity(wallets[0].getAddress(), intent)).toEqual({ + isValidInPrivate: true, + isValidInPublic: false, + }); - const witness = await wallets[0].createAuthWit({ caller: accounts[1].address, action }); - await wallets[1].addAuthWitness(witness); + const innerHash = computeInnerAuthWitHashFromAction(accounts[1].address, action.request()); + await asset.withWallet(wallets[0]).methods.cancel_authwit(innerHash).send().wait(); - await wallets[0].cancelAuthWit({ caller: accounts[1].address, action }).send().wait(); + expect(await wallets[0].lookupValidity(wallets[0].getAddress(), intent)).toEqual({ + isValidInPrivate: false, + isValidInPublic: false, + }); // Perform the transfer, should fail because nullifier already emitted const txCancelledAuthwit = asset .withWallet(wallets[1]) .methods.transfer_from(accounts[0].address, accounts[1].address, amount, nonce) .send(); - await expect(txCancelledAuthwit.wait()).rejects.toThrow(DUPLICATE_NULLIFIER_ERROR); + await expect(txCancelledAuthwit.wait()).rejects.toThrowError(DUPLICATE_NULLIFIER_ERROR); }); - it('transfer on behalf of other, invalid spend_private_authwit on "from"', async () => { + it('transfer on behalf of other, invalid verify_private_authwit on "from"', async () => { const nonce = Fr.random(); // Should fail as the returned value from the badAccount is malformed diff --git a/yarn-project/end-to-end/src/e2e_token_contract/transfer_public.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/transfer_public.test.ts index c828a6bdb15..5ba38158564 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/transfer_public.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/transfer_public.test.ts @@ -1,4 +1,4 @@ -import { Fr, computeAuthWitMessageHash } from '@aztec/aztec.js'; +import { Fr } from '@aztec/aztec.js'; import { U128_UNDERFLOW_ERROR } from '../fixtures/fixtures.js'; import { TokenContractTest } from './token_contract_test.js'; @@ -188,7 +188,7 @@ describe('e2e_token_contract transfer public', () => { await wallets[0].setPublicAuthWit({ caller: accounts[1].address, action }, true).send().wait(); - await wallets[0].cancelPublicAuthWit({ caller: accounts[1].address, action }).send().wait(); + await wallets[0].setPublicAuthWit({ caller: accounts[1].address, action }, false).send().wait(); await expect( asset @@ -212,40 +212,7 @@ describe('e2e_token_contract transfer public', () => { await wallets[0].setPublicAuthWit({ caller: accounts[1].address, action }, false).send().wait(); - await expect( - asset - .withWallet(wallets[1]) - .methods.transfer_public(accounts[0].address, accounts[1].address, amount, nonce) - .simulate(), - ).rejects.toThrowError(/unauthorized/); - }); - - it('transfer on behalf of other, cancelled authwit, flow 3', async () => { - const balance0 = await asset.methods.balance_of_public(accounts[0].address).simulate(); - const amount = balance0 / 2n; - expect(amount).toBeGreaterThan(0n); - const nonce = Fr.random(); - - const action = asset - .withWallet(wallets[1]) - .methods.transfer_public(accounts[0].address, accounts[1].address, amount, nonce); - const messageHash = computeAuthWitMessageHash( - accounts[1].address, - wallets[0].getChainId(), - wallets[0].getVersion(), - action.request(), - ); - - await wallets[0].setPublicAuthWit(messageHash, true).send().wait(); - - await wallets[0].cancelPublicAuthWit(messageHash).send().wait(); - - await expect( - asset - .withWallet(wallets[1]) - .methods.transfer_public(accounts[0].address, accounts[1].address, amount, nonce) - .simulate(), - ).rejects.toThrow(/unauthorized/); + await expect(action.simulate()).rejects.toThrow(/unauthorized/); }); it('transfer on behalf of other, invalid spend_public_authwit on "from"', async () => { diff --git a/yarn-project/end-to-end/src/e2e_token_contract/unshielding.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/unshielding.test.ts index d52b3ce214e..6507f4aeeca 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/unshielding.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/unshielding.test.ts @@ -111,10 +111,8 @@ describe('e2e_token_contract unshielding', () => { .withWallet(wallets[2]) .methods.unshield(accounts[0].address, accounts[1].address, amount, nonce); const expectedMessageHash = computeAuthWitMessageHash( - accounts[2].address, - wallets[0].getChainId(), - wallets[0].getVersion(), - action.request(), + { caller: accounts[2].address, action }, + { chainId: wallets[0].getChainId(), version: wallets[0].getVersion() }, ); // Both wallets are connected to same node and PXE so we could just insert directly diff --git a/yarn-project/end-to-end/src/shared/browser.ts b/yarn-project/end-to-end/src/shared/browser.ts index 86e10d41786..143698eb996 100644 --- a/yarn-project/end-to-end/src/shared/browser.ts +++ b/yarn-project/end-to-end/src/shared/browser.ts @@ -145,14 +145,14 @@ export const browserTestSuite = ( it('Can access CompleteAddress class in browser', async () => { const result: string = await page.evaluate(() => { const completeAddress = window.AztecJs.CompleteAddress.fromString( - '0x06f73ae2ba011a157808a670dd52231347a3b46897ea00945d69fb35d08e68d02c93b9572b35f9c9e07e9003ae1ca444442a165f927bce00e347dab57cc19391148730d0deec722eb6c54747df7345bc2ab3bd8e81f438b17b81ccabd9e6a3ac0708920251ccaf6664d769cbc47c8d767f64912639e13d9f9e441b225066161900c48a65eea83f1dbf217c43daf1be6ba9cefd2754f07e3cc13e81e5432e47f30dfb47c8b1e11368bec638fd9d22c696bf9c323a0fd09050745f4b7cf150bfa529a9f3062ee5f9d0a099ac53b4e1130653fb797ed2b59914a8915951d13ad8252521211957a854707af85ad40e9ab4d474a4fcbdcbe7a47866cae0db4fd86ed2261669d85a9cfbd09365a6db5d7acfe5560104a0cb893a375d6c08ffb9cbb8270be446a16361f271ac11899ee19f990c68035da18703ba00c8e9773dfe6a784a', + '0x0f4b920040c48062d5cd72f0f1b6f331468940ab8651420de8080dfc7fa0f3dc2c93b9572b35f9c9e07e9003ae1ca444442a165f927bce00e347dab57cc19391148730d0deec722eb6c54747df7345bc2ab3bd8e81f438b17b81ccabd9e6a3ac0708920251ccaf6664d769cbc47c8d767f64912639e13d9f9e441b225066161900c48a65eea83f1dbf217c43daf1be6ba9cefd2754f07e3cc13e81e5432e47f30dfb47c8b1e11368bec638fd9d22c696bf9c323a0fd09050745f4b7cf150bfa529a9f3062ee5f9d0a099ac53b4e1130653fb797ed2b59914a8915951d13ad8252521211957a854707af85ad40e9ab4d474a4fcbdcbe7a47866cae0db4fd86ed2261669d85a9cfbd09365a6db5d7acfe5560104a0cb893a375d6c08ffb9cbb8270be446a16361f271ac11899ee19f990c68035da18703ba00c8e9773dfe6a784a', ); // NOTE: browser does not know how to serialize CompleteAddress for return, so return a string // otherwise returning a CompleteAddress makes result undefined. return completeAddress.toString(); }); expect(result).toBe( - '0x06f73ae2ba011a157808a670dd52231347a3b46897ea00945d69fb35d08e68d02c93b9572b35f9c9e07e9003ae1ca444442a165f927bce00e347dab57cc19391148730d0deec722eb6c54747df7345bc2ab3bd8e81f438b17b81ccabd9e6a3ac0708920251ccaf6664d769cbc47c8d767f64912639e13d9f9e441b225066161900c48a65eea83f1dbf217c43daf1be6ba9cefd2754f07e3cc13e81e5432e47f30dfb47c8b1e11368bec638fd9d22c696bf9c323a0fd09050745f4b7cf150bfa529a9f3062ee5f9d0a099ac53b4e1130653fb797ed2b59914a8915951d13ad8252521211957a854707af85ad40e9ab4d474a4fcbdcbe7a47866cae0db4fd86ed2261669d85a9cfbd09365a6db5d7acfe5560104a0cb893a375d6c08ffb9cbb8270be446a16361f271ac11899ee19f990c68035da18703ba00c8e9773dfe6a784a', + '0x0f4b920040c48062d5cd72f0f1b6f331468940ab8651420de8080dfc7fa0f3dc2c93b9572b35f9c9e07e9003ae1ca444442a165f927bce00e347dab57cc19391148730d0deec722eb6c54747df7345bc2ab3bd8e81f438b17b81ccabd9e6a3ac0708920251ccaf6664d769cbc47c8d767f64912639e13d9f9e441b225066161900c48a65eea83f1dbf217c43daf1be6ba9cefd2754f07e3cc13e81e5432e47f30dfb47c8b1e11368bec638fd9d22c696bf9c323a0fd09050745f4b7cf150bfa529a9f3062ee5f9d0a099ac53b4e1130653fb797ed2b59914a8915951d13ad8252521211957a854707af85ad40e9ab4d474a4fcbdcbe7a47866cae0db4fd86ed2261669d85a9cfbd09365a6db5d7acfe5560104a0cb893a375d6c08ffb9cbb8270be446a16361f271ac11899ee19f990c68035da18703ba00c8e9773dfe6a784a', ); }); diff --git a/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts b/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts index f62fa9bc3c5..6caf5b59482 100644 --- a/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts +++ b/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts @@ -425,15 +425,24 @@ export const uniswapL1L2TestSuite = ( // 3. Owner gives uniswap approval to transfer funds on its behalf const nonceForWETHTransferApproval = new Fr(1n); - const transferMessageHash = computeAuthWitMessageHash( - uniswapL2Contract.address, - ownerWallet.getChainId(), - ownerWallet.getVersion(), - wethCrossChainHarness.l2Token.methods - .transfer_public(ownerAddress, uniswapL2Contract.address, wethAmountToBridge, nonceForWETHTransferApproval) - .request(), - ); - await ownerWallet.setPublicAuthWit(transferMessageHash, true).send().wait(); + + await ownerWallet + .setPublicAuthWit( + { + caller: uniswapL2Contract.address, + action: wethCrossChainHarness.l2Token.methods + .transfer_public( + ownerAddress, + uniswapL2Contract.address, + wethAmountToBridge, + nonceForWETHTransferApproval, + ) + .request(), + }, + true, + ) + .send() + .wait(); // 4. Swap on L1 - sends L2 to L1 message to withdraw WETH to L1 and another message to swap assets. const [secretForDepositingSwappedDai, secretHashForDepositingSwappedDai] = @@ -456,13 +465,7 @@ export const uniswapL1L2TestSuite = ( ownerEthAddress, nonceForSwap, ); - const swapMessageHash = computeAuthWitMessageHash( - sponsorAddress, - ownerWallet.getChainId(), - ownerWallet.getVersion(), - action.request(), - ); - await ownerWallet.setPublicAuthWit(swapMessageHash, true).send().wait(); + await ownerWallet.setPublicAuthWit({ caller: sponsorAddress, action }, true).send().wait(); // 4.2 Call swap_public from user2 on behalf of owner const uniswapL2Interaction = await action.send().wait(); @@ -619,13 +622,13 @@ export const uniswapL1L2TestSuite = ( const nonceForWETHUnshieldApproval = new Fr(2n); const expectedMessageHash = computeAuthWitMessageHash( - uniswapL2Contract.address, - ownerWallet.getChainId(), - ownerWallet.getVersion(), - - wethCrossChainHarness.l2Token.methods - .unshield(ownerAddress, uniswapL2Contract.address, wethAmountToBridge, nonceForWETHUnshieldApproval) - .request(), + { + caller: uniswapL2Contract.address, + action: wethCrossChainHarness.l2Token.methods + .unshield(ownerAddress, uniswapL2Contract.address, wethAmountToBridge, nonceForWETHUnshieldApproval) + .request(), + }, + { chainId: ownerWallet.getChainId(), version: ownerWallet.getVersion() }, ); await expect( @@ -694,16 +697,23 @@ export const uniswapL1L2TestSuite = ( // 2. Give approval to uniswap to transfer funds to itself const nonceForWETHTransferApproval = new Fr(2n); - const transferMessageHash = computeAuthWitMessageHash( - uniswapL2Contract.address, - ownerWallet.getChainId(), - ownerWallet.getVersion(), - - wethCrossChainHarness.l2Token.methods - .transfer_public(ownerAddress, uniswapL2Contract.address, wethAmountToBridge, nonceForWETHTransferApproval) - .request(), - ); - await ownerWallet.setPublicAuthWit(transferMessageHash, true).send().wait(); + await ownerWallet + .setPublicAuthWit( + { + caller: uniswapL2Contract.address, + action: wethCrossChainHarness.l2Token.methods + .transfer_public( + ownerAddress, + uniswapL2Contract.address, + wethAmountToBridge, + nonceForWETHTransferApproval, + ) + .request(), + }, + true, + ) + .send() + .wait(); // No approval to call `swap` but should work even without it: const [_, secretHashForDepositingSwappedDai] = daiCrossChainHarness.generateClaimSecret(); @@ -750,13 +760,7 @@ export const uniswapL1L2TestSuite = ( ownerEthAddress, nonceForSwap, ); - const swapMessageHash = computeAuthWitMessageHash( - approvedUser, - ownerWallet.getChainId(), - ownerWallet.getVersion(), - action.request(), - ); - await ownerWallet.setPublicAuthWit(swapMessageHash, true).send().wait(); + await ownerWallet.setPublicAuthWit({ caller: approvedUser, action }, true).send().wait(); await expect(action.simulate()).rejects.toThrow(/unauthorized/); }); @@ -765,15 +769,23 @@ export const uniswapL1L2TestSuite = ( // swap should fail since no transfer approval to uniswap: const nonceForWETHTransferApproval = new Fr(4n); - const transferMessageHash = computeAuthWitMessageHash( - uniswapL2Contract.address, - ownerWallet.getChainId(), - ownerWallet.getVersion(), - wethCrossChainHarness.l2Token.methods - .transfer_public(ownerAddress, uniswapL2Contract.address, wethAmountToBridge, nonceForWETHTransferApproval) - .request(), - ); - await ownerWallet.setPublicAuthWit(transferMessageHash, true).send().wait(); + await ownerWallet + .setPublicAuthWit( + { + caller: uniswapL2Contract.address, + action: wethCrossChainHarness.l2Token.methods + .transfer_public( + ownerAddress, + uniswapL2Contract.address, + wethAmountToBridge, + nonceForWETHTransferApproval, + ) + .request(), + }, + true, + ) + .send() + .wait(); await expect( uniswapL2Contract.methods @@ -931,15 +943,23 @@ export const uniswapL1L2TestSuite = ( // Owner gives uniswap approval to transfer funds on its behalf const nonceForWETHTransferApproval = new Fr(5n); - const transferMessageHash = computeAuthWitMessageHash( - uniswapL2Contract.address, - ownerWallet.getChainId(), - ownerWallet.getVersion(), - wethCrossChainHarness.l2Token.methods - .transfer_public(ownerAddress, uniswapL2Contract.address, wethAmountToBridge, nonceForWETHTransferApproval) - .request(), - ); - await ownerWallet.setPublicAuthWit(transferMessageHash, true).send().wait(); + await ownerWallet + .setPublicAuthWit( + { + caller: uniswapL2Contract.address, + action: wethCrossChainHarness.l2Token.methods + .transfer_public( + ownerAddress, + uniswapL2Contract.address, + wethAmountToBridge, + nonceForWETHTransferApproval, + ) + .request(), + }, + true, + ) + .send() + .wait(); // Call swap_public on L2 const secretHashForDepositingSwappedDai = Fr.random(); diff --git a/yarn-project/entrypoints/src/dapp_entrypoint.ts b/yarn-project/entrypoints/src/dapp_entrypoint.ts index 18a217f2eda..f680cb26e28 100644 --- a/yarn-project/entrypoints/src/dapp_entrypoint.ts +++ b/yarn-project/entrypoints/src/dapp_entrypoint.ts @@ -1,4 +1,4 @@ -import { computeInnerAuthWitHash, computeOuterAuthWitHash } from '@aztec/aztec.js'; +import { computeAuthWitMessageHash, computeInnerAuthWitHash } from '@aztec/aztec.js'; import { type AuthWitnessProvider } from '@aztec/aztec.js/account'; import { type EntrypointInterface, EntrypointPayload, type ExecutionRequestInit } from '@aztec/aztec.js/entrypoint'; import { PackedValues, TxExecutionRequest } from '@aztec/circuit-types'; @@ -34,11 +34,9 @@ export class DefaultDappEntrypoint implements EntrypointInterface { const functionSelector = FunctionSelector.fromNameAndParameters(abi.name, abi.parameters); const innerHash = computeInnerAuthWitHash([Fr.ZERO, functionSelector.toField(), entrypointPackedArgs.hash]); - const outerHash = computeOuterAuthWitHash( - this.dappEntrypointAddress, - new Fr(this.chainId), - new Fr(this.version), - innerHash, + const outerHash = computeAuthWitMessageHash( + { consumer: this.dappEntrypointAddress, innerHash }, + { chainId: new Fr(this.chainId), version: new Fr(this.version) }, ); const authWitness = await this.userAuthWitnessProvider.createAuthWit(outerHash); diff --git a/yarn-project/key-store/src/key_store.test.ts b/yarn-project/key-store/src/key_store.test.ts index 5c13479d406..dc8edfa7275 100644 --- a/yarn-project/key-store/src/key_store.test.ts +++ b/yarn-project/key-store/src/key_store.test.ts @@ -24,7 +24,7 @@ describe('KeyStore', () => { const { address: accountAddress } = await keyStore.addAccount(sk, partialAddress); expect(accountAddress.toString()).toMatchInlineSnapshot( - `"0x1a8a9a1d91cbb353d8df4f1bbfd0283f7fc63766f671edd9443a1270a7b2a954"`, + `"0x15565e4a5f3aff35f8eafa364cec1c11aaa84a5f7fcdf64a373614fdc8add52e"`, ); const { pkM: masterNullifierPublicKey } = await keyStore.getKeyValidationRequest( @@ -32,22 +32,22 @@ describe('KeyStore', () => { AztecAddress.random(), // Address is random because we are not interested in the app secret key here ); expect(masterNullifierPublicKey.toString()).toMatchInlineSnapshot( - `"0x2ef5d15dd65d29546680ab72846fb071f41cb9f2a0212215e6c560e29df4ff650ce764818364b376be92dc2f49577fe440e64a16012584f7c4ee94f7edbc323a"`, + `"0x1c088f4e4a711f236a88b55da9ddf388de0bc00d56a5ceca96cea3a5cbe75bf32db0a333ba30c36b844d9fc6d2fb0de8d10e4371f0c5baebae452d90ff366798"`, ); const masterIncomingViewingPublicKey = await keyStore.getMasterIncomingViewingPublicKey(accountAddress); expect(masterIncomingViewingPublicKey.toString()).toMatchInlineSnapshot( - `"0x1c088f4e4a711f236a88b55da9ddf388de0bc00d56a5ceca96cea3a5cbe75bf32db0a333ba30c36b844d9fc6d2fb0de8d10e4371f0c5baebae452d90ff366798"`, + `"0x232d0b445d097fbc2046012c3fc474f6a9beef97eda1d8d1f2487dbe501ee1e70e8db9a824531a14e8717dee54cbb7abfec29a88c550a49617258bd6fd858242"`, ); const masterOutgoingViewingPublicKey = await keyStore.getMasterOutgoingViewingPublicKey(accountAddress); expect(masterOutgoingViewingPublicKey.toString()).toMatchInlineSnapshot( - `"0x232d0b445d097fbc2046012c3fc474f6a9beef97eda1d8d1f2487dbe501ee1e70e8db9a824531a14e8717dee54cbb7abfec29a88c550a49617258bd6fd858242"`, + `"0x076429010fdebfa522b053267f654a4c5daf18589915d96f7e5001d63ea2033f27f915f254560c84450aa38e93c3162be52492d05b316e75f542e3b302117360"`, ); const masterTaggingPublicKey = await keyStore.getMasterTaggingPublicKey(accountAddress); expect(masterTaggingPublicKey.toString()).toMatchInlineSnapshot( - `"0x076429010fdebfa522b053267f654a4c5daf18589915d96f7e5001d63ea2033f27f915f254560c84450aa38e93c3162be52492d05b316e75f542e3b302117360"`, + `"0x07cec19d32f1cbaaacf16edc081021b696c86dff14160779373ffc77b04568e7076f25b0e7f0d02fd6433d788483e2262c1e45c5962790b40d1cd7efbd5253d3"`, ); // Arbitrary app contract address @@ -56,36 +56,36 @@ describe('KeyStore', () => { const { pkM: obtainedMasterNullifierPublicKey, skApp: appNullifierSecretKey } = await keyStore.getKeyValidationRequest(computedMasterNullifierPublicKeyHash, appAddress); expect(appNullifierSecretKey.toString()).toMatchInlineSnapshot( - `"0x230a44dfe7cfec7a735c89f7289c5cb5d2c3dc0bf5d3505917fd2476f67873a8"`, + `"0x0084c92262407236c992dcea10cf3406a642074cad6c6034d2990ffb073207a7"`, ); expect(obtainedMasterNullifierPublicKey).toEqual(masterNullifierPublicKey); const appIncomingViewingSecretKey = await keyStore.getAppIncomingViewingSecretKey(accountAddress, appAddress); expect(appIncomingViewingSecretKey.toString()).toMatchInlineSnapshot( - `"0x0084c92262407236c992dcea10cf3406a642074cad6c6034d2990ffb073207a7"`, + `"0x2639b26510f9d30b7e173d301b263b246b7a576186be1f44cd7c86bc06773f8a"`, ); const appOutgoingViewingSecretKey = await keyStore.getAppOutgoingViewingSecretKey(accountAddress, appAddress); expect(appOutgoingViewingSecretKey.toString()).toMatchInlineSnapshot( - `"0x2639b26510f9d30b7e173d301b263b246b7a576186be1f44cd7c86bc06773f8a"`, + `"0x13b400d2fccab28a04a4df9fe541d242e6b518d03137ef0ffa57c3d98cc56e67"`, ); // Returned accounts are as expected const accounts = await keyStore.getAccounts(); expect(accounts.toString()).toMatchInlineSnapshot( - `"0x1a8a9a1d91cbb353d8df4f1bbfd0283f7fc63766f671edd9443a1270a7b2a954"`, + `"0x15565e4a5f3aff35f8eafa364cec1c11aaa84a5f7fcdf64a373614fdc8add52e"`, ); // Manages to find master nullifer secret key for pub key const masterNullifierSecretKey = await keyStore.getMasterSecretKey(masterNullifierPublicKey); expect(masterNullifierSecretKey.toString()).toMatchInlineSnapshot( - `"0x0fde74d5e504c73b58aad420dd72590fc6004571411e7f77c45378714195a52b"`, + `"0x1f1f43082427fed511393bbabf8a471eb87af09f0e95bb740dc33e1ced1a54c1"`, ); // Manages to find master incoming viewing secret key for pub key const masterIncomingViewingSecretKey = await keyStore.getMasterSecretKey(masterIncomingViewingPublicKey); expect(masterIncomingViewingSecretKey.toString()).toMatchInlineSnapshot( - `"0x1f1f43082427fed511393bbabf8a471eb87af09f0e95bb740dc33e1ced1a54c1"`, + `"0x1d1d920024dd64e019c23de36d27aefe4d9d4d05983b99cf85bea9e85fd60020"`, ); }); @@ -98,7 +98,7 @@ describe('KeyStore', () => { const { address: accountAddress } = await keyStore.addAccount(sk, partialAddress); expect(accountAddress.toString()).toMatchInlineSnapshot( - `"0x1a8a9a1d91cbb353d8df4f1bbfd0283f7fc63766f671edd9443a1270a7b2a954"`, + `"0x15565e4a5f3aff35f8eafa364cec1c11aaa84a5f7fcdf64a373614fdc8add52e"`, ); // Arbitrary fixed values @@ -146,21 +146,21 @@ describe('KeyStore', () => { appAddress, ); expect(appNullifierSecretKey0.toString()).toMatchInlineSnapshot( - `"0x296e42f1039b62290372d608fcab55b00a3f96c1c8aa347b2a830639c5a12757"`, + `"0x21e3ca4bc7ae2b5e9fe343f4eec5c0aa7391857333821a4b0a1c7d4cb0055bf0"`, ); const { skApp: appNullifierSecretKey1 } = await keyStore.getKeyValidationRequest( newComputedMasterNullifierPublicKeyHashes[1], appAddress, ); expect(appNullifierSecretKey1.toString()).toMatchInlineSnapshot( - `"0x019f2a705b68683f1d86da639a543411fa779af41896c3920d0c2d5226c686dd"`, + `"0x0900aea4825d057e5bc916063a535520a7c6283740eaf218cd6961b10cba46fd"`, ); const { skApp: appNullifierSecretKey2 } = await keyStore.getKeyValidationRequest( newComputedMasterNullifierPublicKeyHashes[2], appAddress, ); expect(appNullifierSecretKey2.toString()).toMatchInlineSnapshot( - `"0x117445c8819c06b9a0889e5cce1f550e32ec6993c23f57bc9fc5cda05df520ae"`, + `"0x27ccbe41ff5f33fa78348533da9d4a79e8fea8805771e61748ea42be4202f168"`, ); expect(appNullifierSecretKey0).toEqual(computeAppNullifierSecretKey(newMasterNullifierSecretKeys[0], appAddress)); diff --git a/yarn-project/simulator/src/acvm/oracle/oracle.ts b/yarn-project/simulator/src/acvm/oracle/oracle.ts index 590f0542e1b..9655a0103cd 100644 --- a/yarn-project/simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/oracle.ts @@ -49,6 +49,14 @@ export class Oracle { return toACVMField(await this.typedOracle.getContractAddress()); } + async getVersion(): Promise { + return toACVMField(await this.typedOracle.getVersion()); + } + + async getChainId(): Promise { + return toACVMField(await this.typedOracle.getChainId()); + } + async getKeyValidationRequest([pkMHash]: ACVMField[]): Promise { const { pkM, skApp } = await this.typedOracle.getKeyValidationRequest(fromACVMField(pkMHash)); diff --git a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts index 41fd2f7e37b..887a9b5f601 100644 --- a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts @@ -90,6 +90,14 @@ export abstract class TypedOracle { throw new OracleMethodNotAvailableError('getContractAddress'); } + getChainId(): Promise { + throw new OracleMethodNotAvailableError('getChainId'); + } + + getVersion(): Promise { + throw new OracleMethodNotAvailableError('getVersion'); + } + getKeyValidationRequest(_pkMHash: Fr): Promise { throw new OracleMethodNotAvailableError('getKeyValidationRequest'); } diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index 5946f7c0528..478685fe7d6 100644 --- a/yarn-project/simulator/src/client/private_execution.test.ts +++ b/yarn-project/simulator/src/client/private_execution.test.ts @@ -847,7 +847,6 @@ describe('Private Execution test suite', () => { const secret = new Fr(1n); const secretHash = computeSecretHash(secret); const note = new Note([secretHash]); - // @todo @LHerskind (#6001) Need to investigate why this was working with `new Fr(5)` as the `example_set = 2` should have caused a failure. const storageSlot = TestContractArtifact.storageLayout['example_set'].slot; oracle.getNotes.mockResolvedValue([ { diff --git a/yarn-project/simulator/src/client/unconstrained_execution.test.ts b/yarn-project/simulator/src/client/unconstrained_execution.test.ts index 0f97b50ec42..fbca7486e34 100644 --- a/yarn-project/simulator/src/client/unconstrained_execution.test.ts +++ b/yarn-project/simulator/src/client/unconstrained_execution.test.ts @@ -20,6 +20,8 @@ describe('Unconstrained Execution test suite', () => { node = mock(); node.getBlockNumber.mockResolvedValue(42); + node.getChainId.mockResolvedValue(1); + node.getVersion.mockResolvedValue(1); acirSimulator = 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 f70e8db09b8..fd1710205dc 100644 --- a/yarn-project/simulator/src/client/view_data_oracle.ts +++ b/yarn-project/simulator/src/client/view_data_oracle.ts @@ -42,6 +42,14 @@ export class ViewDataOracle extends TypedOracle { return Promise.resolve(this.contractAddress); } + public override getChainId(): Promise { + return Promise.resolve(this.aztecNode.getChainId().then(id => new Fr(id))); + } + + public override getVersion(): Promise { + return Promise.resolve(this.aztecNode.getVersion().then(v => new Fr(v))); + } + /** * Retrieve keys associated with a specific master public key and app address. * @param pkMHash - The master public key hash. diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index 09115f3ebab..d3139ee1007 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -67,6 +67,9 @@ export class TXE implements TypedOracle { private contractDataOracle: ContractDataOracle; + private version: Fr = Fr.ONE; + private chainId: Fr = Fr.ONE; + constructor( private logger: Logger, private trees: MerkleTrees, @@ -82,6 +85,22 @@ export class TXE implements TypedOracle { // Utils + getChainId(): Promise { + return Promise.resolve(this.chainId); + } + + getVersion(): Promise { + return Promise.resolve(this.version); + } + + setChainId(chainId: Fr) { + this.chainId = chainId; + } + + setVersion(version: Fr) { + this.version = version; + } + getMsgSender() { return this.msgSender; }