> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.adid.dev/llms.txt.
> For full documentation content, see https://docs.adid.dev/llms-full.txt.

# Revocation Registry Smart Contract

The Revocation Registry manages credential revocation status on the ADI blockchain. It supports single and batch revocation operations with reason codes, and provides on-chain verification of credential status.

## Contract Address

Deployed to ADI devnet. See `deployments/` for current addresses.

## Interface

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IRevocationRegistry {
    struct RevocationRecord {
        bool revoked;
        string reason;
        address issuer;
        uint256 revokedAt;
    }

    event CredentialRevoked(
        string indexed credentialIdHash,
        string credentialId,
        address indexed issuer,
        string reason,
        uint256 timestamp
    );

    function revokeCredential(string calldata credentialId, string calldata reason) external;

    function isRevoked(string calldata credentialId)
        external view returns (bool revoked, string memory reason, uint256 revokedAt);

    function batchRevoke(string[] calldata credentialIds, string calldata reason) external;
}
```

## Key Functions

### revokeCredential

Revokes a single credential. The caller (`msg.sender`) is recorded as the issuer. If already revoked by the same issuer, the reason and timestamp are updated.

```solidity
function revokeCredential(string calldata credentialId, string calldata reason) external;
```

| Parameter      | Type     | Description                                      |
| -------------- | -------- | ------------------------------------------------ |
| `credentialId` | `string` | The credential identifier (e.g., `urn:uuid:...`) |
| `reason`       | `string` | Human-readable revocation reason                 |

**Reverts if:**

* `credentialId` is empty
* Credential was already revoked by a different address

### isRevoked

Check whether a credential has been revoked. This is a `view` function (no gas cost for external calls).

```solidity
function isRevoked(string calldata credentialId)
    external view returns (bool revoked, string memory reason, uint256 revokedAt);
```

| Return      | Type      | Description                                     |
| ----------- | --------- | ----------------------------------------------- |
| `revoked`   | `bool`    | Whether the credential is revoked               |
| `reason`    | `string`  | The revocation reason (empty if not revoked)    |
| `revokedAt` | `uint256` | Unix timestamp of revocation (0 if not revoked) |

### batchRevoke

Revoke multiple credentials in a single transaction. All credentials receive the same reason string.

```solidity
function batchRevoke(string[] calldata credentialIds, string calldata reason) external;
```

| Parameter       | Type       | Description                               |
| --------------- | ---------- | ----------------------------------------- |
| `credentialIds` | `string[]` | Array of credential identifiers (max 100) |
| `reason`        | `string`   | Revocation reason applied to all          |

**Reverts if:**

* Array is empty
* Array exceeds 100 entries
* Any credential was already revoked by a different address

### getIssuer

Returns the address that revoked a credential.

```solidity
function getIssuer(string calldata credentialId) external view returns (address);
```

Returns the zero address if the credential has not been revoked.

## Revocation Reason Codes

The API layer maps numeric reason codes to human-readable strings before calling the contract:

| Code | Label                    | Description                                |
| ---- | ------------------------ | ------------------------------------------ |
| 0    | `unspecified`            | No specific reason given                   |
| 1    | `key_compromise`         | Issuer's signing key was compromised       |
| 2    | `issuer_compromise`      | Issuer entity was compromised              |
| 3    | `affiliation_changed`    | Subject's affiliation has changed          |
| 4    | `superseded`             | Credential replaced by a newer version     |
| 5    | `cessation_of_operation` | Issuer has ceased operations               |
| 6    | `agent_decommissioned`   | AI agent has been decommissioned           |
| 7    | `privilege_withdrawn`    | Privileges granted by credential withdrawn |
| 8    | `fraud_detected`         | Fraudulent credential detected             |

## Storage

Credential IDs are hashed with `keccak256` before use as mapping keys:

```solidity
mapping(bytes32 => RevocationRecord) private _revocations;

function _hash(string calldata credentialId) internal pure returns (bytes32) {
    return keccak256(abi.encodePacked(credentialId));
}
```

## Security

* **ReentrancyGuard**: All state-mutating functions use OpenZeppelin's `nonReentrant` modifier.
* **Issuer enforcement**: Only the original revoker can update the reason on an already-revoked credential.
* **Batch limit**: Maximum 100 credentials per batch to prevent gas limit issues.

## Interaction Examples

### JavaScript (ethers.js)

```javascript
const { ethers } = require('ethers');

const revocationRegistry = new ethers.Contract(
  REVOCATION_REGISTRY_ADDRESS,
  IRevocationRegistryABI,
  signer
);

// Check if a credential is revoked
const [revoked, reason, revokedAt] = await revocationRegistry.isRevoked(
  'urn:uuid:12345678-1234-1234-1234-123456789abc'
);
console.log('Revoked:', revoked, 'Reason:', reason);

// Revoke a single credential
const tx = await revocationRegistry.revokeCredential(
  'urn:uuid:12345678-1234-1234-1234-123456789abc',
  'key_compromise'
);
await tx.wait();

// Batch revoke (e.g., agent decommissioning)
const credentialIds = [
  'urn:uuid:agent-cred-001',
  'urn:uuid:agent-cred-002',
  'urn:uuid:agent-cred-003',
];
const batchTx = await revocationRegistry.batchRevoke(
  credentialIds,
  'agent_decommissioned'
);
await batchTx.wait();

// Get the issuer who revoked
const issuerAddress = await revocationRegistry.getIssuer(
  'urn:uuid:12345678-1234-1234-1234-123456789abc'
);
```

### Go (API integration)

The `RevocationService` in `packages/api/pkg/credential/revocation.go` provides a Go interface that coordinates between the local PostgreSQL database and the on-chain contract:

```go
svc := credential.NewRevocationService(store, chainClient, statusMgr, logger)

// Revoke with reason code
entry, err := svc.RevokeCredential(ctx, credentialID, issuerDID, credential.ReasonKeyCompromise)

// Check combined status (DB + on-chain)
result, err := svc.CheckRevocationStatus(ctx, credentialID)
fmt.Println("Revoked:", result.Revoked, "In DB:", result.RevokedInDB, "On-chain:", result.RevokedOnChain)

// Batch revoke
entries, err := svc.BatchRevoke(ctx, credentialIDs, issuerDID, credential.ReasonAgentDecommissioned)
```

## StatusList2021 Integration

The platform also supports the W3C StatusList2021 bitstring-based status tracking via `packages/api/pkg/credential/status.go`. Each credential is assigned an index in a bitstring, and the status list is published as a Verifiable Credential.

```go
sl := credential.NewStatusList2021(issuerDID, credential.StatusPurposeRevocation, 16384)

// Allocate an index for a new credential
index, _ := sl.AllocateIndex()

// Revoke by setting the bit
sl.SetStatus(index, true)

// Generate the status list credential for publication
statusListVC := sl.GenerateStatusListCredential()
```

## Gas Costs

| Operation                 | Estimated Gas     |
| ------------------------- | ----------------- |
| `revokeCredential`        | \~50,000          |
| `batchRevoke` (10 items)  | \~120,000         |
| `batchRevoke` (100 items) | \~800,000         |
| `isRevoked`               | 0 (view function) |
| `getIssuer`               | 0 (view function) |

## Related Files

| File                                                               | Description                            |
| ------------------------------------------------------------------ | -------------------------------------- |
| `packages/blockchain/contracts/RevocationRegistry.sol`             | Solidity contract implementation       |
| `packages/blockchain/contracts/interfaces/IRevocationRegistry.sol` | Contract interface                     |
| `packages/api/pkg/credential/revocation.go`                        | Go revocation service                  |
| `packages/api/pkg/credential/status.go`                            | StatusList2021 implementation          |
| `packages/api/internal/service/vc_service.go`                      | VC service with revocation methods     |
| `packages/api/internal/handler/vc.go`                              | HTTP handlers for revocation endpoints |