# Registering applications

Before the UPA can aggregate proofs for an application, it must know about the application's verifying key(s).

The UPA contracts expose a `registerVK` method, which accepts the verifying key to be used, stores it on-chain (for use during censorship claims), and emits an event informing aggregators of the key data.

The guide here assumes that you have correctly installed the development environment, and have a `upa.instance` file pointing to a Saturn deployment. See the [setup guide](https://docs.nebra.one/developer-guide/setup) for details.

## Converting to UPA-compatible format

Verification keys must be in the UPA-compatible format before registration.&#x20;

### SnarkJS

#### Exporting the verifying key

This may be done with a command of the form:

```bash
yarn snarkjs zkey export verificationkey path/to/circuit.zkey app_vk.json
```

#### Converting the verifying key via the `upa` tool

The key retrieved above can be converted to the UPA format as follows:

```bash
upa convert vk-snarkjs --snarkjs-vk app_vk.json --vk-file app_vk.upa.json
```

#### Converting the verifying key via the typescript sdk

The UPA sdk supports conversion from snarjks `zkey`s to the UPA-compatible `Groth16VerifyingKey`:

```typescript
import { Groth16VerifyingKey } from "@nebrazkp/upa/sdk";

const vkSnarkjs = snarkjs.zkey.exportVerificationKey( ... );
const vk = Groth16VerifyingKey.from_snarkjs(vkSnarkjs);
```

### Gnark

#### Exporting the verifying key

You can modify your circuit compilation code to save the verifying key to a file:

```go
import ("encoding/json")

_, vk, _ := groth16.Setup(ccs)
vkJSON, _ := json.MarshalIndent(vk, "", "    ")
_ = os.WriteFile("gnark_vk.json", vkJSON, 0644)
```

where `ccs` is the `gnark` `ConstraintSystem` of your circuit.

**Note:** Currently, the NEBRA UPA only supports `gnark` configurations for which:

* The circuit has zero or one Pedersen commitment points, that is, the field `Commitments` of your proofs is a vector of length 0 or 1.
* Public inputs are not committed into the commitment point. That is, the `PublicAndCommitmentCommitted`array in the verifying key must be empty.

#### Converting the verifying key via the upa tool

The key retrieved above can be converted to the UPA format as follows:

```sh
upa convert vk-gnark --gnark-vk gnark_vk.json --vk-file app_vk.upa.json
```

If the verifying key belongs to a circuit which uses a Pedersen commitment, you must add the flag

```bash
--has-commitment
```

to the command above. Your circuit uses a Pedersen commitment if the field `Commitments` of your proof is a vector of length 1.

#### Converting the verifying key via the typescript sdk

```typescript
import { Groth16VerifyingKey } from "@nebrazkp/upa/sdk";

const vk = Groth16VerifyingKey.from_gnark(vkGnark, hasCommitment);
```

where `vkGnark` can be obtained by parsing the `gnark_vk.json` file extracted above. For example:

```typescript
import type GnarkVerificationKey from "@nebrazkp/upa/sdk";

const vkGnark = JSON.parse(
    fs.readFileSync("path/to/gnark_vk.json", "ascii")
  ) as GnarkVerificationKey;
```

## Registering via the `upa` tool

Once you have the file `app_vk.upa.json` with the UPA-compatible verifying key, you can register it with the following command:

```bash
upa registervk app_vk.upa.json
```

The circuit Id for this key is then output to stdout. Record this for use in your application. It can be recomputed via

```bash
upa compute circuit-id app_vk.upa.json
```

## Registering from Typescript

(See the [setup guide](https://docs.nebra.one/developer-guide/setup) for instructions on creating a `UpaClient`)

Let `vk` be the variable holding a `Groth16VerifyingKey`, generated e.g. from [snarkjs](#converting-the-verifying-key-via-the-typescript-sdk) or [gnark](#converting-the-verifying-key-via-the-typescript-sdk-1) with the UPA sdk. You can easily register the verifying key with the `UpaClient`:

```typescript
// Register the verifying key (upaClient is assumed to be
// correctly initialized)
const txResponse = await upaClient.registerVK(vk);
```

The circuit Id can also be computed from the `VerifyingKey` from the typescript sdk.

```typescript
import { utils } from "@nebrazkp/upa/sdk";

await utils.computeCircuitId(vk);
```

## What is Circuit Id?

*Circuit Id* is a unique identifier, assigned to each circuit when its verifying key is registered with the UPA contracts. It is computed (deterministically) as the keccak hash of the verifying key contents, with a domain tag. The UPA contract store a map from *Circuit Id* to Verifying Key data, for use during censorship claims. When submitting proofs to UPA, applications specify the *Circuit Id* for each proof being submitted.

*Circuit Id*s are also used to compute the unique *Proof Id* for each proof submitted to the UPA (where *Proof Id* is the Keccak digest of the Circuit Id followed by the public inputs).

Aggregators use the *Circuit Id* to look up the corresponding Verifying Key to be used as witness data in the aggregation proof. The aggregation proof then attests to the set of *Proof Id*s that appear in the aggregation. When application contracts query UPA to determine the validity of a given proof, the *Proof Id* is computed and used to check whether a batch including that proof has been verified.

> **NOTE**: The *Circuit Id* is required when proofs are submitted to NEBRA UPA.

See `upa compute circuit-id --help`.

## A note about G2 formats

> Most developers will not have to deal with the details of this, but it can be helpful to be aware of the following potential pitfall.

Some attributes of the Groth16 Verifying Key are elements of the so-called `G2` curve group. The details can be found elsewhere (e.g. in the `upa-sdk` reference documentation), but it is important to be aware that there are two incompatible formats for these points. Most off-chain libraries, including `snarkjs`, use a *natural* ordering of coordinates in `G2`, while the EVM expects them to be reversed.

The UPA SDK and contracts work as follows:

* All sdk functions and types use the *natural* ordering, including `Groth16VerifyingKey`, `Groth16Proof` etc, compatible with snarkjs. These types generally expose a static constructor such as `Groth16VerifyingKey.from_snarkjs()`that accepts the snarkjs version, and a method `solidity()` which returns the data as expected *by the NEBRA* contract.
* Conversion should generally be handled automatically by the UPA SDK and tools, and the SDK uses types where possible to catch any compatibility problems. For reference:
  * VerifyingKeys are passed to the UPA contracts with G2 coordinates in the *natural* order, since they are generally only used by off-chain tools. (On-chain verification only happens in the case of censorship claims).
  * Proofs are passed to the UPA with G2 coordinates in the *EVM* order. This is because this is the the form in which application generally pass proofs to their contracts. This simplifies the integration of UPA into existing applications, since no conversion is required.
  * The `solidity()` method on `application` objects adheres to the convention described above, that is, `vk.solidity()` is compatible with the UPA contracts, but **may not be compatible with on-chain Groth16 verifiers**.
* For most applications, the Verifying Key is embedded automatically in a verification contract, and the application does not have to interact with it. The above pitfalls are therefore only expected to be relevant to applications with custom pipelines for their circuits.
