> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/zkp2p/zkp2p-contracts/llms.txt
> Use this file to discover all available pages before exploring further.

# NullifierRegistry

> Registry for tracking used nullifiers to prevent double-spending

## Overview

The `NullifierRegistry` is a critical security contract that tracks nullifiers used in zero-knowledge proofs to prevent double-spending attacks. It maintains a record of all nullifiers that have been used to claim payments, ensuring each proof can only be used once.

**Contract Location:** `contracts/registries/NullifierRegistry.sol`

## Purpose

The NullifierRegistry serves as a double-spend prevention mechanism:

* **Nullifier Tracking**: Records nullifiers from ZK proofs to prevent reuse
* **Access Control**: Restricts write access to authorized contracts only
* **Global State**: Provides a shared nullifier registry across all payment verifiers
* **Security Enforcement**: Prevents the same off-chain payment proof from being used multiple times

## Key Concepts

### What is a Nullifier?

A nullifier is a unique identifier derived from a zero-knowledge proof that represents a specific off-chain payment. Once a nullifier is recorded, any attempt to use the same proof again can be detected and rejected.

### Writer Permissions

The registry implements a permission system where only authorized "writer" contracts (typically payment verifiers) can add nullifiers.

## State Variables

```solidity theme={null}
mapping(bytes32 => bool) public isNullified;
mapping(address => bool) public isWriter;
address[] public writers;
```

* `isNullified`: Tracks whether a specific nullifier has been used
* `isWriter`: Maps addresses to their write permission status
* `writers`: Array of all addresses with write permissions

## Core Functions

### Adding Nullifiers

```solidity theme={null}
function addNullifier(bytes32 _nullifier) external onlyWriter
```

Records a nullifier as used. This is called by payment verifier contracts after successfully verifying a proof.

**Parameters:**

* `_nullifier`: The nullifier hash from the ZK proof

**Requirements:**

* Caller must be a registered writer
* Nullifier must not already exist

**Emits:** `NullifierAdded(bytes32 nullifier, address indexed writer)`

**Reference:** `contracts/registries/NullifierRegistry.sol:41`

### Managing Write Permissions

```solidity theme={null}
function addWritePermission(address _newWriter) external onlyOwner
function removeWritePermission(address _removedWriter) external onlyOwner
```

Grant or revoke write permissions to addresses (typically payment verifier contracts).

**Parameters:**

* `_newWriter` / `_removedWriter`: Address to grant or revoke permissions

**Requirements:**

* Only callable by contract owner
* Writer must not already have/lack permissions (prevents duplicates)

**Emits:** `WriterAdded(address writer)` or `WriterRemoved(address writer)`

**Reference:** `contracts/registries/NullifierRegistry.sol:56` and `:70`

### Querying Writers

```solidity theme={null}
function getWriters() external view returns(address[] memory)
```

Returns array of all addresses with write permissions.

**Reference:** `contracts/registries/NullifierRegistry.sol:81`

## Integration with Core Contracts

### Payment Verifiers

Payment verifier contracts (e.g., `UnifiedPaymentVerifier`) are the primary users of the NullifierRegistry:

1. Verifier receives a ZK proof containing a nullifier
2. Verifier checks if nullifier has been used: `isNullified[nullifier]`
3. If proof is valid and nullifier is unused, verifier calls `addNullifier()`
4. Nullifier is permanently marked as used

**Reference:** `contracts/unifiedVerifier/BaseUnifiedPaymentVerifier.sol:64`

### Orchestrator Registry

Payment verifiers that write to the NullifierRegistry must be authorized by being granted write permissions during deployment or configuration.

**Reference:** `contracts/unifiedVerifier/UnifiedPaymentVerifier.sol:98`

## Access Control

### Modifiers

```solidity theme={null}
modifier onlyWriter() {
    require(isWriter[msg.sender], "Only addresses with write permissions can call");
    _;
}
```

Restricts `addNullifier()` to authorized writer contracts only.

**Reference:** `contracts/registries/NullifierRegistry.sol:21`

### Owner Functions

* `addWritePermission()`: Add authorized writers
* `removeWritePermission()`: Remove writer access

### Writer Functions

* `addNullifier()`: Record used nullifiers

### Public View Functions

* `isNullified[nullifier]`: Check if nullifier has been used (publicly accessible)
* `isWriter[address]`: Check if address has write permissions
* `getWriters()`: Get list of all writers

## Events

```solidity theme={null}
event NullifierAdded(bytes32 nullifier, address indexed writer);
event WriterAdded(address writer);
event WriterRemoved(address writer);
```

These events enable:

* Tracking which verifier added which nullifiers
* Monitoring changes to write permissions
* Auditing nullifier usage patterns

## Security Model

### Double-Spend Prevention

The registry prevents double-spending through:

1. **Uniqueness Enforcement**: Each nullifier can only be added once
2. **Revert on Duplicate**: Attempting to add an existing nullifier will revert
3. **Permanent Record**: Nullifiers cannot be removed once added

### Access Control Security

1. **Restricted Writers**: Only authorized contracts can add nullifiers
2. **Owner-Controlled**: Only owner can modify writer permissions
3. **No Removal**: Nullifiers are permanent (cannot be deleted)

### Integration Security

Proper integration requires:

1. Verifiers must check `isNullified[nullifier]` before processing
2. Verifiers must call `addNullifier()` after successful verification
3. Writer permissions must only be granted to audited verifier contracts

## Usage Pattern

Typical flow in a payment verifier:

```solidity theme={null}
// 1. Extract nullifier from proof
bytes32 nullifier = proof.nullifier;

// 2. Check if already used
require(!nullifierRegistry.isNullified(nullifier), "Nullifier already used");

// 3. Verify ZK proof
require(verifyProof(proof), "Invalid proof");

// 4. Record nullifier
nullifierRegistry.addNullifier(nullifier);

// 5. Process payment
// ...
```

## Deployment Considerations

1. **Single Registry**: Typically one NullifierRegistry per chain for all payment methods
2. **Writer Setup**: Grant write permissions to all payment verifier contracts
3. **Immutable Nullifiers**: Once deployed, nullifiers cannot be removed (plan for long-term storage)
4. **Gas Optimization**: Uses OpenZeppelin's `AddressArrayUtils` for efficient array operations

## Related Contracts

* [PaymentVerifierRegistry](/contracts/payment-verifier-registry) - Manages verifier contracts that write to this registry
* [UnifiedPaymentVerifier](/contracts/unified-payment-verifier) - Primary writer to this registry
* [OrchestratorRegistry](/contracts/orchestrator-registry) - Controls which orchestrators can authorize verifiers
