# Anonymity Revokers

Anonymity Revoker (AR in short) is a role that helps protecting the Shielder from bad actors. The main idea is that an AR would deanonymize all actions within the Shielder of a recognized bad actor. The typical scenario we have in mind here is as follows:

1. Illicit funds are detected on chain that have been used to interact with the Shielder. For instance the funds come from a well known hack.
2. A specific Shielder transaction, say a deposit, is determined to be using illicit funds.&#x20;
3. A governance process decides whether to deanonymize the user behind this deposit. And if it decides YES, then a request to deanonymize is sent to the AR.
4. The AR reveals the transaction, and as a consequence (because of how the solution is built technically) this allows everyone to see the details of all other transactions performed by the illicit user, as if they were not using the Shielder at all.&#x20;

## Anonymity Revoker Key

The AR holds a private key `AR_sk` for asymmetric encryption and the corresponding public key `AR_pk` is a known parameter of the Shielder. The encryption scheme in use must be snark-friendly. Indeed, some transactions will need to include proofs of statements of the form:

```
Enc(AR_pk, m) = c
```

where `m` is some private input and `c` is public input. For a description of the concrete scheme that we use refer to [snark-friendly-asymmetric-encryption](https://docs.alephzero.org/aleph-zero/protocol-details/shielder/snark-friendly-asymmetric-encryption "mention").

## User Transactions

For a ZK-ID `id` we define by `key(id)` a procedure `key: Scalar -> Scalar` that produces a symmetric encryption key out of the `id`. The `key` map should be one-way. The simplest example would be to use `key(id) = hash(id)` but there might be other constraints that might force us to use a different key derivation.&#x20;

When making their first transaction, the user includes on chain (in the form of an event) an encrypted version of the key `key(id)` using the Anonymity Revoker's key `AR_pk` , thus `Enc(AR_pk, key(id)) = e_key` and importantly `e_key`must be proved correct via a snark (so `e_key`must enter as a public input in the circuit for creating new accounts).  Note, since some accounts were created in [version-0.1.0](https://docs.alephzero.org/aleph-zero/protocol-details/shielder/version-0.1.0 "mention") the migration transaction should expose `e_key`instead for these accounts.

Moreover, whenever the user makes a different kind of transaction (not their first), the following data is included&#x20;

* `mac = (r, hash(r, key(id))) : (Scalar, Scalar)` -- the HMAC "signature" of the user under a random nonce. This is to be able to identify the user's transactions among all other transactions knowing the `key(id)`.&#x20;
* `e_op = SymEnc(key(id), op_priv)` -- this is an encryption of the private part of the operation `op_priv: OpPriv` the user is performing on its account, encrypted using a symmetric (snark-friendly) encryption scheme (see [snark-friendly-symmetric-encryption](https://docs.alephzero.org/aleph-zero/protocol-details/shielder/snark-friendly-symmetric-encryption "mention")) using key `key(id)`. Note: `op_priv`is empty in [version-0.1.0](https://docs.alephzero.org/aleph-zero/protocol-details/shielder/version-0.1.0 "mention") hence this field can be also omitted.&#x20;

## Modification to Transactions

Note that adding the encryptions to the transaction requires some changes to what we introduced in [notes-and-accounts](https://docs.alephzero.org/aleph-zero/protocol-details/shielder/notes-and-accounts "mention") and [zk-id-and-registrars](https://docs.alephzero.org/aleph-zero/protocol-details/shielder/zk-id-and-registrars "mention") -- for completeness we repeat the parts that change including the necessary modifications. The main idea though is simple: in the `new_note`transaction we want to reveal our `key(id)`but only to the AR, and in the `update_note` transaction we need to put constraints checking the correctness of encryption and forming the mac.

#### Changes to new\_note

We skip the part about registrar, because it's optional and it doesn't change

```
relation R_new_note

inputs:
    - h_note: Scalar,
    - nullifier_create: Scalar,
    - ar_pk: ASPkey,
    - e_key: ASCipherText,
    
witnesses:
    - note: Note,
    - trapdoor, nullifier: Scalar,
    - id: Scalar,

constraints:
    1. k = key(id)
    2. e_key = Enc(AR_pk, k)
    3. h_note = hash(note)
    4. note = Note { id, trapdoor, nullifier, h_acc }
    5. acc = Account::new(date)
    6. h_acc = Account::hash(acc) 
    7. nullifier_create = hash(id, NULL) // NULL is a special field element
```

In the above `ASPkey`is the type holding public key of the asymmetric encryption primitive and `ASCipherText`is the type of ciphertexts produced using the asymmetric encryption primitive.

Similarly we update the corresponding transaction

```
transaction new_note

inputs:
    - proof_new: ZkProof,
    - h_note: Scalar,
    - nullifier_create: Scalar,
    - e_key: ASCipherText,
    
execution:
    - v_new = ZK-Verifier(R_new_note) // initialize verifier for the relation R_new_note
    - // Below ar_pk should be the AR pub key held in the contract
    - assert: v_new.verify(proof_new; (h_note, nullifier_create, ar_pk, e_key))
    - assert: nullifier_create is not in shielder.nullifier_create_set
    - shielder.notes.add_leaf(h_note)
    - shielder.nullifier_create_set.add(nullifier_create)
```

#### Changes to update\_note

```
relation R_update_note_op

inputs:
    - op_pub: OpPub // the public part of  the operation to be performed
    - h_note_new: Scalar,
    - merkle_root: Scalar,
    - h_nullifier_old: Scalar,
    - mac: (Scalar, Scalar),
    - e_op: Scalar^n
    
witnesses:
    - note_new, note_old: Note,
    - trapdoor_new, trapdor_old: Scalar
    - nullifier_new, nullifier_old: Scalar,
    - proof: MerkleProof
    - id: Scalar
        
constraints:
    1. h_note_new = hash(note_new)
    2. note_new = Note { id, trapdoor_new, nullifier_new, h_acc_new }
    3. h_note_old = hash(note_old)
    4. note_old = Note { id, trapdoor_old, nullifier_old, h_acc_old }
    5. h_nullifier_old = hash(nullifier_old)
    6. verify_merkle_proof(merkle_root, h_note_old, proof)
    7. op = combine(op_pub, op_priv)
    8. R_update_account_op(op, h_acc_old, h_acc_new)
    9. k = key(id)
    10. mac = (r, hash(r, k))
    11. e_op = SymEnc(k, op_priv)
```

```
transaction update_note_op

inputs:
    - op_pub: OpPub,
    - proof: ZkProof,
    - h_nullifier_old: Scalar,
    - merkle_root: Scalar,
    - h_note_new: Scalar,
    - mac: (Scalar, Scalar),
    - e_op: Scalar^n
    
execution:
    - shielder.public_exec(op_pub)
    - assert: merkle_root is the current or historical root of shielder.notes
    - assert: h_nullifier_old not in shielder.nullifier_set
    - v = ZK-Verifier(R_update_note_op) // initialize verifier for the relation R_update_note_op
    - assert: v.verify(proof; (op_pub, h_note_new, merkle_root, h_nullifier_old, mac, e_op))
    - shielder.notes.add_leaf(h_note_new)
    - shielder.nullifier_set.add(h_nullifier_old)
```

## Revoking Anonymity&#x20;

In case the anonymity revocation procedure is triggered on a transaction `tx`, the AR proceeds in two steps:\
1\) Find the `key(id)`of the user (account) who created this transaction.

2\) Find all transactions created by the user with the `key(id)`found in step 1).&#x20;

We now explain both steps in detail.

## Step 1)

Each transaction has a `mac=(r,c)`attached to it, which is essentially a symmetric signature of a user, but to recognize the user we must know the associated key. To find out the valid key `k`such that&#x20;

`mac = (r, c) = (r, hash(r, k))`

the AR can simply collect ALL the keys that have been registered by users in their `new_note`transactions, and then find the unique key `k`that satisfies `hash(r, k) = c`.&#x20;

Note that (although this is not crucial) there exists exactly one such user `id`whose `key(id)`satisfies the above — this is because we guarantee that there are no duplicates among `id'`s (by using the nullifiers upon creating the account) and `key(id)` is a deterministic hash-based procedure to generate a key, hence the collision-free property of the hash function guarantees there are no collisions among `key(id)`s either.&#x20;

Having found the key `key(id)`the AR can reveal the key publicly — this way each 3rd party observer will be able to perform step 2). Note that, importantly, given just `key(id)`the 3rd party observer can verify that the reveal is correct, because they can simply verify that the `mac`is valid. In particular, they don't need access to the AR's key.

## Step 2)

The AR, or any interested 3rd party observer, given `key(id)`can now find all transactions issued by used `id`. The method is very simple:&#x20;

* Loop over ALL transactions sent to the Shielder, and check which of the macs were generated using this particular `key(id)`  — these are exactly the transactions we are looking for.&#x20;

## Complete Deanonymization&#x20;

After performing the above steps 1) and 2) one can recover the complete history of this user's account and in particular recover the current state, and see all the new transactions (that they might send in the future) of the user in the plain.&#x20;

### Simpler variant of Revoking

We note that Shielder in Version 0.0.1 is released with a simpler AR scheme based on PoW. The details are presented in [pow-anonymity-revoking](https://docs.alephzero.org/aleph-zero/protocol-details/shielder/pow-anonymity-revoking "mention").
