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

# Verification engine

# VerificationEngine

The `VerificationEngine` (Solidity name `NotareumVerificationEngine`) drives the attestation lifecycle for registered resources. A resource owner requests verification at a target level; validators from the staking contract submit attestations; when quorum is met the engine finalizes the request and records the resulting verification level on the resource. The contract is the sole mutator of verification state.

## Verification levels

```solidity theme={"system"}
enum VerificationLevel {
    Unverified,     // 0: registered only
    Basic,          // 1: 3 validators, 67% quorum
    Enhanced,       // 2: 7 validators, 75% quorum
    Institutional   // 3: 15 validators, 75% quorum
}
```

Higher levels require more validators and stricter quorum, reducing the probability of a coordinated bad attestation but raising the fee and latency.

## Request lifecycle

```mermaid theme={"system"}
stateDiagram-v2
    [*] --> Open: requestVerification
    Open --> Quorum: submitAttestation × n
    Quorum --> Finalized: finalizeRequest
    Finalized --> [*]
    Open --> Expired: deadline passed
    Finalized --> Disputed: openDispute (within window)
    Disputed --> Finalized: resolveDispute (innocent)
    Disputed --> Reverted: resolveDispute (guilty)
```

Requests that never reach quorum before `deadline` expire; the fee is partially refunded (see [FeeManager](fee-manager.md)). Finalized requests enter a fixed dispute window (default 72 hours); a guilty ruling during the window reverts the verification level and slashes offending validators.

## Data structures

```solidity theme={"system"}
enum RequestStatus { Open, Finalized, Expired, Disputed, Reverted }

struct VerificationRequest {
    bytes32 resourceId;
    address requester;
    VerificationLevel targetLevel;
    uint64 createdAt;
    uint64 deadline;
    uint32 attestCount;
    uint32 approveCount;
    RequestStatus status;
    uint256 feePaid;
}

struct Attestation {
    address validator;
    uint64 submittedAt;
    bool approve;         // true = approve, false = reject
    bytes32 evidenceHash; // keccak256 of off-chain evidence payload
}
```

Attestations are indexed per `(requestId, validator)` with a uniqueness guard.

## Functions

### `requestVerification`

```solidity theme={"system"}
function requestVerification(
    bytes32 resourceId,
    VerificationLevel targetLevel
) external payable returns (bytes32 requestId);
```

Opens a verification request for a registered resource at the target level. The caller pays the level-dependent fee (see [FeeManager](fee-manager.md)). Reverts if:

* The resource does not exist.
* The resource is revoked.
* A non-expired open request already exists for the resource.
* The fee is insufficient.

Emits `VerificationRequested(requestId, resourceId, requester, targetLevel, deadline)`.

The `deadline` is `block.timestamp + requestTTL[targetLevel]`, where the TTL defaults to 7 days for Basic, 10 days for Enhanced, 14 days for Institutional.

### `submitAttestation`

```solidity theme={"system"}
function submitAttestation(
    bytes32 requestId,
    bool approve,
    bytes32 evidenceHash
) external;
```

Submits one validator's attestation. Reverts if:

* The caller is not a registered validator in good standing (checked via `ValidatorStaking.isActive(msg.sender)`).
* The caller has already attested for this `requestId`.
* The validator's tier is below the minimum for `targetLevel`.
* The request is not in `Open` state.
* The request deadline has passed.

The validator's tier must meet the minimum for the target level:

| Target level  | Minimum validator tier |
| ------------- | ---------------------- |
| Basic         | Basic                  |
| Enhanced      | Professional           |
| Institutional | Enterprise             |

Emits `AttestationSubmitted(requestId, validator, approve, evidenceHash)`.

### `finalizeRequest`

```solidity theme={"system"}
function finalizeRequest(bytes32 requestId) external;
```

Callable by anyone once quorum is reached. Computes whether the request met both the required `totalValidators` and the required approval ratio:

```solidity theme={"system"}
function quorumParams(VerificationLevel level) public pure returns (
    uint32 totalValidators,
    uint32 approvalNumer,
    uint32 approvalDenom
) {
    if (level == VerificationLevel.Basic)
        return (3, 2, 3);       // 67%
    if (level == VerificationLevel.Enhanced)
        return (7, 3, 4);       // 75%
    if (level == VerificationLevel.Institutional)
        return (15, 3, 4);      // 75%
    revert("level");
}
```

A request finalizes as **approved** when:

```solidity theme={"system"}
attestCount >= totalValidators &&
approveCount * approvalDenom >= attestCount * approvalNumer
```

On approval the engine calls into `NotaRegistry.setVerificationLevel(resourceId, targetLevel)`. On rejection the resource stays at its prior level. Validators receive their attestation rewards from `ValidatorStaking.creditAttestationReward`. Emits `VerificationFinalized(requestId, status, finalLevel)`.

### `openDispute`

```solidity theme={"system"}
function openDispute(bytes32 requestId, bytes32 evidenceHash) external payable;
```

Within the dispute window, any veNOTA holder can open a dispute by posting a bond (the bond scales with `targetLevel`). The engine transitions the request to `Disputed`. Governance (or a fast-track dispute committee) resolves via `resolveDispute`.

### `resolveDispute`

```solidity theme={"system"}
function resolveDispute(
    bytes32 requestId,
    bool disputantWins
) external; // role: DISPUTE_JUDGE
```

If the disputant wins, the engine reverts the verification level, triggers slashing on the majority-approving validators via `SlashingManager.slash(validator, reason)`, and returns the bond plus a portion of the slashed amount to the disputant. If the disputant loses, the bond is split between the approving validators and the treasury.

## Quorum math, formally

For a target level with `n` required validators and ratio `r/d`:

```
approve_needed = ceil(attestCount * r / d)
quorum_met = (attestCount >= n) AND (approveCount >= approve_needed)
```

Using integer comparisons avoids rounding pitfalls. The engine checks `approveCount * d >= attestCount * r` which is exact.

### Byzantine fault margin

With `n = 15` and honest-validator probability `p_h = 0.95`, the probability that at least 4 of 15 validators are Byzantine (breaking 75% approval) is:

```
P_fail = sum(k=4..15, C(15,k) * (1-p_h)^k * p_h^(15-k))
```

At `p_h = 0.95`, `P_fail ≈ 7.4e-4`. With the Institutional tier's economic stake of 1M NOTA per validator, the capital required to mount a successful attack exceeds 4M NOTA across coordinated validators, and the SlashingManager slashes the full stake on successful dispute.

## Dispute window

Dispute windows default to:

* Basic: 24 hours
* Enhanced: 48 hours
* Institutional: 72 hours

During the window, the verification level is live but marked `disputable: true` in view functions. Wallets rendering a resource mid-window can choose to surface a "verification pending confirmation" signal. After the window closes with no disputes, the status becomes permanent (modulo owner revocation or later slashing events).

## Events

```solidity theme={"system"}
event VerificationRequested(
    bytes32 indexed requestId,
    bytes32 indexed resourceId,
    address indexed requester,
    VerificationLevel targetLevel,
    uint64 deadline
);
event AttestationSubmitted(
    bytes32 indexed requestId,
    address indexed validator,
    bool approve,
    bytes32 evidenceHash
);
event VerificationFinalized(
    bytes32 indexed requestId,
    RequestStatus status,
    VerificationLevel finalLevel
);
event DisputeOpened(
    bytes32 indexed requestId,
    address indexed disputant,
    uint256 bond
);
event DisputeResolved(
    bytes32 indexed requestId,
    bool disputantWins
);
```

## Gas notes

* `requestVerification`: ≈120k gas (single SSTORE + fee transfer).
* `submitAttestation`: ≈95k gas per call (struct write + counter updates).
* `finalizeRequest`: ≈140k gas base + ≈20k per attestation in the reward distribution loop. For Institutional (15 validators), expect ≈440k gas.

## SDK usage

```typescript theme={"system"}
import { Notareum } from "@notareum/sdk";

const ntm = Notareum({ provider, signer, contracts });

// Owner opens a request
const reqTx = await ntm.verification.requestVerification(resourceId, {
  level: "enhanced",
});
const { requestId } = await reqTx.wait();

// Validator submits
await ntm.verification.submitAttestation(requestId, {
  approve: true,
  evidenceHash: keccak256("...evidence..."),
});

// Anyone finalizes once quorum is met
await ntm.verification.finalizeRequest(requestId);

// Read current state
const state = await ntm.verification.getRequest(requestId);
```

## Invariants

1. A validator submits at most one attestation per `requestId`.
2. A request transitions out of `Open` exactly once.
3. `finalizeRequest` never increases a resource's level on rejection; it only increases on approval.
4. Slashing on dispute targets only the validators whose attestation matches the disproven side.

## Related pages

* [NotaRegistry](nota-registry.md)
* [ValidatorStaking](validator-staking.md)
* [SlashingManager](slashing-manager.md)
* [FeeManager](fee-manager.md)
* [Verification Engine protocol doc](../protocol/verification-engine.md)
