VerificationEngine
TheVerificationEngine (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
Request lifecycle
Requests that never reach quorum beforedeadline expire; the fee is partially refunded (see FeeManager). 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
(requestId, validator) with a uniqueness guard.
Functions
requestVerification
- The resource does not exist.
- The resource is revoked.
- A non-expired open request already exists for the resource.
- The fee is insufficient.
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
- 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
Openstate. - The request deadline has passed.
Emits
AttestationSubmitted(requestId, validator, approve, evidenceHash).
finalizeRequest
totalValidators and the required approval ratio:
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
targetLevel). The engine transitions the request to Disputed. Governance (or a fast-track dispute committee) resolves via resolveDispute.
resolveDispute
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 withn required validators and ratio r/d:
approveCount * d >= attestCount * r which is exact.
Byzantine fault margin
Withn = 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_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
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
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
Invariants
- A validator submits at most one attestation per
requestId. - A request transitions out of
Openexactly once. finalizeRequestnever increases a resource’s level on rejection; it only increases on approval.- Slashing on dispute targets only the validators whose attestation matches the disproven side.

