> Aztec.nr API reference, v6.0.0 (mainnet and testnet). Index: https://docs.aztec.network/aztec-nr-api/v6.0.0/llms.txt

# Global IS_VALID_SELECTOR

Module: `noir_aztec::authwit::auth`

```rust
pub global IS_VALID_SELECTOR: Field;
```

Authentication witness helper library

Authentication Witness is a scheme for authenticating actions on Aztec, so users can allow third-parties (e.g. protocols or other users) to execute an action on their behalf.

This library provides helper functions to manage such witnesses. The authentication witness, is some "witness" (data) that authenticates a `message_hash`. The simplest example of an authentication witness, is a signature. The signature is the "evidence", that the signer has seen the message, agrees with it, and has allowed it. It does not need to be a signature. It could be any kind of "proof" that the message is allowed. Another proof could be knowing some kind of secret, or having some kind of "token" that allows the message.

The `message_hash` is a hash of the following structure: hash(consumer, chain_id, version, inner_hash)

- consumer: the address of the contract that is "consuming" the message,
- chain_id: the chain id of the chain that the message is being consumed on,
- version: the version of the chain that the message is being consumed on,
- inner_hash: the hash of the "inner" message that is being consumed, this is the "actual" message or action.

While the `inner_hash` could be anything, such as showing you signed a specific message, it will often be a hash of the "action" to approve, along with who made the call. As part of this library, we provide a few helper functions to deal with such messages.

For example, we provide helper function that is used for checking that the message is an encoding of the current call. This can be used to let some contract "allow" another contract to act on its behalf, as long as it can show that it is acting on behalf of the contract.

If we take a case of allowing a contract to transfer tokens on behalf of an account, the `inner_hash` can be derived as: inner_hash = hash(caller, "transfer", hash(to, amount))

Where the `caller` would be the address of the contract that is trying to transfer the tokens, and `to` and `amount` the arguments for the transfer.

Note that we have both a `caller` and a `consumer`, the `consumer` will be the contract that is consuming the message, in the case of the transfer, it would be the `Token` contract itself, while the caller, will be the actor that is allowed to transfer the tokens.

The authentication mechanism works differently in public and private contexts. In private, we recall that everything is executed on the user's device, so we can use `oracles` to "ask" the user (not contract) for information. In public we cannot do this, since it is executed by the sequencer (someone else). Therefore we can instead use a "registry" to store the messages that we have approved.

A simple example would be a "token" that is being "pulled" from one account into another. We will first outline how this would look in private, and then in public later.

Say that a user `Alice` wants to deposit some tokens into a DeFi protocol (say a DEX). `Alice` would make a `deposit` transaction, that she is executing using her account contract. The account would call the `DeFi` contract to execute `deposit`, which would try to pull funds from the `Token` contract. Since the `DeFi` contract is trying to pull funds from an account that is not its own, it needs to convince the `Token` contract that it is allowed to do so.

This is where the authentication witness comes in The `Token` contract computes a `message_hash` from the `transfer` call, and then asks `Alice Account` contract to verify that the `DeFi` contract is allowed to execute that call.

`Alice Account` contract can then ask `Alice` if she wants to allow the `DeFi` contract to pull funds from her account. If she does, she will sign the `message_hash` and return the signature to the `Alice Account` which will validate it and return success to the `Token` contract which will then allow the `DeFi` contract to pull funds from `Alice`.

To ensure that the same "approval" cannot be used multiple times, we also compute a `nullifier` for the authentication witness, and emit it from the `Token` contract (consumer).

Note that we can do this flow as we are in private were we can do oracle calls out from contracts.

Person Contract Contract Contract Alice Alice Account Token DeFi | | | | | Defi.deposit(Token, 1000) | | |----------------->| | | | | deposit(Token, 1000) | | |---------------------------------------->| | | | | | | | transfer(Alice, Defi, 1000) | | |<---------------------| | | | | | | Check if Defi may call transfer(Alice, Defi, 1000) | |<-----------------| | | | | | | Please give me AuthWit for DeFi | | | calling transfer(Alice, Defi, 1000) | | |<-----------------| | | | | | | | | | | | AuthWit for transfer(Alice, Defi, 1000) | |----------------->| | | | | AuthWit validity | | | |----------------->| | | | | | | | throw if invalid AuthWit | | | | | | | emit AuthWit nullifier | | | | | | | transfer(Alice, Defi, 1000) | | | | | | | | | | | | success | | | |--------------------->| | | | | | | | | | | | deposit(Token, 1000) | | | | | | | |

If we instead were in public, we cannot do the same flow. Instead we would use an authentication registry to store the messages that we have approved.

To approve a message, `Alice Account` can make a `set_authorized` call to the registry, to set a `message_hash` as authorized. This is essentially a mapping from `message_hash` to `true` for `Alice Contract`. Every account has its own map in the registry, so `Alice` cannot approve a message for `Bob`.

The `Token` contract can then try to "spend" the approval by calling `consume` on the registry. If the message was approved, the value is updated to `false`, and we return the success flag. For more information on the registry, see `main.nr` in `auth_registry_contract`.

Person Contract Contract Contract Contract Alice Alice Account Registry Token DeFi | | | | | | Registry.set_authorized(..., true) | | | |----------------->| | | | | | set_authorized(..., true) | | | |------------------->| | | | | | | | | | set authorized to true | | | | | | | | | | | | | Defi.deposit(Token, 1000) | | | |----------------->| | | | | | deposit(Token, 1000) | | | |-------------------------------------------------------------->| | | | | | | | | transfer(Alice, Defi, 1000) | | | | |<---------------------| | | | | | | | | Check if Defi may call transfer(Alice, Defi, 1000) | | |<------------------| | | | | | | | | throw if invalid AuthWit | | | | | | | | | | | | | | set authorized to false | | | | | | | | | | | | | | | AuthWit validity | | | | |------------------>| | | | | | | | | | | transfer(Alice, Defi, 1000) | | | |<-------------------->| | | | | | | | | | success | | | | |--------------------->| | | | | | | | | | deposit(Token, 1000) | | | | |

\--- FAQ --- Q: Why are we using a success flag of `poseidon2_hash_bytes("IS_VALID()")` instead of just returning a boolean? A: We want to make sure that we don't accidentally return `true` if there is a collision in the function selector. By returning a hash of `IS_VALID()`, it becomes very unlikely that there is both a collision and we return a success flag.

Q: Why are we using static calls? A: We are using static calls to ensure that the account contract cannot re-enter. If it was a normal call, it could make a new call and do a re-entry attack. Using a static ensures that it cannot update any state.

Q: Would it not be cheaper to use a nullifier instead of updating state in public? A: At a quick glance, a public state update + nullifier is 96 bytes, but two state updates are 128, so it would be cheaper to use a nullifier, if this is the way it would always be done. However, if both the approval and the consumption is done in the same transaction, then we will be able to squash the updates (only final tx state diff is posted to DA), and now it is cheaper.

Q: Why is the chain id and the version part of the message hash? A: The chain id and the version is part of the message hash to ensure that the message is only valid on a specific chain to avoid a case where the same message could be used across multiple chains.
