|
| 1 | +use crate::{authwit::utils::{compute_inner_authwit_hash, IS_VALID_SELECTOR}, context::{gas::GasOpts, PublicContext}}; |
| 2 | +use crate::protocol::{abis::function_selector::FunctionSelector, address::AztecAddress, traits::ToField}; |
| 3 | +use standard_addresses::AUTH_REGISTRY_ADDRESS; |
| 4 | + |
| 5 | +/// Public-flow authwit helpers. |
| 6 | +/// |
| 7 | +/// In public, we cannot use oracles to "ask" the user for an authentication witness like we do in private, since the |
| 8 | +/// public execution is performed by the sequencer. Instead, an account uses a "registry" to record the messages they |
| 9 | +/// have approved. To approve a message, `Alice Account` makes a `set_authorized` call to the registry, recording a |
| 10 | +/// `message_hash -> true` mapping for `Alice Contract`. Every account has its own map in the registry, so `Alice` |
| 11 | +/// cannot approve a message for `Bob`. |
| 12 | +/// |
| 13 | +/// The `Token` contract can then try to "spend" the approval by calling `consume` on the registry. If the message was |
| 14 | +/// approved, the value is updated to `false`, and we return the success flag. For more information on the registry, |
| 15 | +/// see `main.nr` in `auth_registry_contract`. |
| 16 | +/// |
| 17 | +/// Person Contract Contract Contract Contract |
| 18 | +/// Alice Alice Account Registry Token DeFi |
| 19 | +/// | | | | | |
| 20 | +/// | Registry.set_authorized(..., true) | | | |
| 21 | +/// |----------------->| | | | |
| 22 | +/// | | set_authorized(..., true) | | |
| 23 | +/// | |------------------->| | | |
| 24 | +/// | | | | | |
| 25 | +/// | | set authorized to true | | |
| 26 | +/// | | | | | |
| 27 | +/// | | | | | |
| 28 | +/// | Defi.deposit(Token, 1000) | | | |
| 29 | +/// |----------------->| | | | |
| 30 | +/// | | deposit(Token, 1000) | | |
| 31 | +/// | |-------------------------------------------------------------->| |
| 32 | +/// | | | | | |
| 33 | +/// | | | transfer(Alice, Defi, 1000) | |
| 34 | +/// | | | |<---------------------| |
| 35 | +/// | | | | | |
| 36 | +/// | | | Check if Defi may call transfer(Alice, Defi, 1000) |
| 37 | +/// | | |<------------------| | |
| 38 | +/// | | | | | |
| 39 | +/// | | throw if invalid AuthWit | | |
| 40 | +/// | | | | | |
| 41 | +/// | | | | | |
| 42 | +/// | | set authorized to false | | |
| 43 | +/// | | | | | |
| 44 | +/// | | | | | |
| 45 | +/// | | | AuthWit validity | | |
| 46 | +/// | | |------------------>| | |
| 47 | +/// | | | | | |
| 48 | +/// | | | | transfer(Alice, Defi, 1000) |
| 49 | +/// | | | |<-------------------->| |
| 50 | +/// | | | | | |
| 51 | +/// | | | | success | |
| 52 | +/// | | | |--------------------->| |
| 53 | +/// | | | | | |
| 54 | +/// | | | | deposit(Token, 1000) |
| 55 | +/// | | | | | |
| 56 | + |
| 57 | +/// Assert that `on_behalf_of` has authorized the current call in the authentication registry |
| 58 | +/// |
| 59 | +/// Compute the `inner_hash` using the `msg_sender`, `selector` and `args_hash` and then make a call out to the |
| 60 | +/// `on_behalf_of` contract to verify that the `inner_hash` is valid. |
| 61 | +/// |
| 62 | +/// Note that the authentication registry will take the `msg_sender` into account as the consumer, so this will only |
| 63 | +/// work if the `msg_sender` is the same as the `consumer` when the `message_hash` was inserted into the registry. |
| 64 | +/// |
| 65 | +/// @param on_behalf_of The address that has allegedly authorized the current call |
| 66 | +pub unconstrained fn assert_current_call_valid_authwit_public(context: PublicContext, on_behalf_of: AztecAddress) { |
| 67 | + let inner_hash = compute_inner_authwit_hash([ |
| 68 | + context.maybe_msg_sender().unwrap().to_field(), |
| 69 | + context.selector().to_field(), |
| 70 | + context.get_args_hash(), |
| 71 | + ]); |
| 72 | + assert_inner_hash_valid_authwit_public(context, on_behalf_of, inner_hash); |
| 73 | +} |
| 74 | + |
| 75 | +/// Assert that `on_behalf_of` has authorized a specific `inner_hash` in the authentication registry |
| 76 | +/// |
| 77 | +/// Compute the `inner_hash` using the `msg_sender`, `selector` and `args_hash` and then make a call out to the |
| 78 | +/// `on_behalf_of` contract to verify that the `inner_hash` is valid. |
| 79 | +/// |
| 80 | +/// Note that the authentication registry will take the `msg_sender` into account as the consumer, so this will only |
| 81 | +/// work if the `msg_sender` is the same as the `consumer` when the `message_hash` was inserted into the registry. |
| 82 | +/// |
| 83 | +/// @param on_behalf_of The address that has allegedly authorized the `inner_hash` |
| 84 | +pub unconstrained fn assert_inner_hash_valid_authwit_public( |
| 85 | + context: PublicContext, |
| 86 | + on_behalf_of: AztecAddress, |
| 87 | + inner_hash: Field, |
| 88 | +) { |
| 89 | + let results: [Field] = context.call_public_function( |
| 90 | + AUTH_REGISTRY_ADDRESS, |
| 91 | + comptime { FunctionSelector::from_signature("consume((Field),Field)") }, |
| 92 | + [on_behalf_of.to_field(), inner_hash], |
| 93 | + GasOpts::default(), |
| 94 | + ); |
| 95 | + assert(results.len() == 1, "Invalid response from registry"); |
| 96 | + assert(results[0] == IS_VALID_SELECTOR, "Message not authorized by account"); |
| 97 | +} |
| 98 | + |
| 99 | +/// Helper function to set the authorization status of a message hash |
| 100 | +/// |
| 101 | +/// Wraps a public call to the authentication registry to set the authorization status of a `message_hash` |
| 102 | +/// |
| 103 | +/// @param message_hash The hash of the message to authorize @param authorize True if the message should be authorized, |
| 104 | +/// false if it should be revoked |
| 105 | +pub unconstrained fn set_authorized(context: PublicContext, message_hash: Field, authorize: bool) { |
| 106 | + let res = context.call_public_function( |
| 107 | + AUTH_REGISTRY_ADDRESS, |
| 108 | + comptime { FunctionSelector::from_signature("set_authorized(Field,bool)") }, |
| 109 | + [message_hash, authorize as Field], |
| 110 | + GasOpts::default(), |
| 111 | + ); |
| 112 | + assert(res.len() == 0); |
| 113 | +} |
| 114 | + |
| 115 | +/// Helper function to reject all authwits |
| 116 | +/// |
| 117 | +/// Wraps a public call to the authentication registry to set the `reject_all` flag |
| 118 | +/// |
| 119 | +/// @param reject True if all authwits should be rejected, false otherwise |
| 120 | +pub unconstrained fn set_reject_all(context: PublicContext, reject: bool) { |
| 121 | + let res = context.call_public_function( |
| 122 | + AUTH_REGISTRY_ADDRESS, |
| 123 | + comptime { FunctionSelector::from_signature("set_reject_all(bool)") }, |
| 124 | + [reject as Field], |
| 125 | + GasOpts::default(), |
| 126 | + ); |
| 127 | + assert(res.len() == 0); |
| 128 | +} |
0 commit comments