> For the complete documentation index, see [llms.txt](https://docs.arcv.network/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.arcv.network/5.-smart-contracts-and-arc-network-settlement/5.3-security-and-test-suite.md).

# 5.3 Security Invariants & Test Suite

The repository contains an executable local Foundry suite for ARCVDataBountyRegistry. It establishes specific behavior under its test inputs. It is not a formal verification result, an independent security audit, or evidence that the contract has been deployed correctly on Arc.

A clean compilation during this documentation update compiled 27 Solidity files with Solc 0.8.28. One test suite passed all 17 tests, with zero failures and zero skipped tests. One of those tests is parameterized and executed 256 fuzz cases. The 27-file count includes dependencies and test code; it is not a count of 27 distinct deployed protocol contracts.

## Reproducible verification configuration

| Setting              | Value                                                  |
| -------------------- | ------------------------------------------------------ |
| Foundry executable   | Forge 1.7.1                                            |
| Compiler             | Solc 0.8.28                                            |
| EVM target           | Cancun                                                 |
| Optimizer            | Enabled, 200 runs for the ordinary build/test run      |
| OpenZeppelin         | Contracts 5.4.0                                        |
| Test source          | contracts/test/ARCVDataBountyRegistry.t.sol            |
| Test fixture         | Fresh registry and synthetic funded accounts per test  |
| Fuzz configuration   | 256 runs                                               |
| Network dependencies | No live Arc RPC or Arweave gateway used by these tests |

From the repository root, initialize pinned dependencies before entering the Foundry workspace:

```bash
git submodule update --init --recursive
cd contracts
forge test --force -vv
forge fmt --check
forge coverage --report summary
```

The optional Windows executable supplied by the contracts package can be used from that directory:

```powershell
.\node_modules\@foundry-rs\forge-win32-amd64\bin\forge.exe test --force -vv
.\node_modules\@foundry-rs\forge-win32-amd64\bin\forge.exe fmt --check
.\node_modules\@foundry-rs\forge-win32-amd64\bin\forge.exe coverage --report summary
```

The ordinary test run and formatting check passed. The sandbox could not persist Foundry's global signature cache; those warnings did not prevent compilation or tests. Coverage uses different instrumentation settings and must be interpreted separately from optimized production-bytecode behavior.

## Executable test matrix

The names below are the actual test functions. “Covered” means the stated assertions execute, not that every variation of the property has been proven.

| Test                                                | Exercised behavior                                                                                              | Important boundary                                                                          |
| --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| testLabEscrowsNativeUSDC                            | Stores sponsor, target, reward, schema and escrow; checks native balance and totalEscrow                        | One ordinary funded example                                                                 |
| testBundlerRegistersCanonicalProvenance             | Persists supplied bounty, digest, storage ID, recipient and Pending state                                       | Does not retrieve or hash Arweave bytes                                                     |
| testDuplicateHashAcrossBountiesReverts              | Same digest rejected across two campaigns                                                                       | Same registry deployment only                                                               |
| testDuplicateArweaveTxReverts                       | Reused storage identifier rejected with a different digest                                                      | Tests identifier reuse, not canonical decoding                                              |
| testValidatorDisbursesExactlyOnce                   | Transfers fixed reward, clears liabilities, rejects repeat payment                                              | One successful payout sequence                                                              |
| testNonValidatorCannotPay                           | Unauthorized caller receives role error                                                                         | One representative unauthorized account                                                     |
| testNonBundlerCannotSubmit                          | Unauthorized caller cannot register                                                                             | Does not prove off-chain relayer security                                                   |
| testNonValidatorCannotReject                        | Unauthorized caller cannot reject                                                                               | One representative role boundary                                                            |
| testRejectFreesCapacityButPreservesReplayProtection | Full capacity rejects another record; rejection frees slot; digest remains reserved; rejected record cannot pay | Transaction-ID persistence is source behavior, not separately asserted here after rejection |
| testPayoutFailureRollsBackAccounting                | Reverting recipient leaves Pending state, registry balance and totalEscrow intact                               | Not every intermediate counter is separately asserted                                       |
| testReentrancyCannotDoublePay                       | Role-authorized recipient callback fails to reenter payout and receives one reward                              | Same-record reentry; not exhaustive cross-function analysis                                 |
| testInvalidSpecificationsRevert                     | Zero target, zero reward and empty schema reject                                                                | Deterministic examples, not broad input fuzzing                                             |
| testFuzzRequiresExactEscrow(uint96)                 | 256 varied deposit amounts unequal to required value revert                                                     | Does not fuzz reward/count multiplication                                                   |
| testInvalidSubmissionMetadataReverts                | Unknown bounty, zero recipient, zero digest and short invalid storage ID reject                                 | Does not cover every character/length combination                                           |
| testEmptyRejectionReasonReverts                     | Empty rejection reason rejected                                                                                 | Does not validate semantic quality of a nonempty reason                                     |
| testOnlyAdminCanConfigureExternalToken              | Admin updates reference; non-admin and zero address rejected                                                    | Nonzero address is not checked for token code                                               |
| testConstructorRejectsZeroAddresses                 | Zero initial admin or token address rejected                                                                    | Inherited role grants have different behavior                                               |

The suite combines unit-level checks with interactions among the registry and local helper recipient contracts. It is not an end-to-end integration test with the workbench, an actual autonomous validator, a native-USDC deployment, or permanent storage.

No test asserts that a model's judgment is correct. The registry accepts the authorized operator's decision. No test establishes a refund path, fee cap, pause, upgrade mechanism, or deployed address because those capabilities are absent.

## Fuzz campaign scope

The single fuzz test chooses a uint96 deposit amount and assumes that it differs from the fixed reward. It funds the test sponsor with that amount, calls createBounty for one payable sample, and expects IncorrectEscrow with the exact expected and received amounts.

```
targetCount = 1
rewardPerSample = 0.25 * 10^18
amount ranges over uint96
assumption: amount != rewardPerSample
expected result: IncorrectEscrow(rewardPerSample, amount)
```

The 256 cases sample that property. They do not exhaust the uint96 domain, vary targetCount, reach uint256 multiplication extremes, or explore arbitrary transaction sequences. In particular, this is not evidence of fuzzed overflow/underflow protection across all accounting operations.

Zero target and zero reward are covered by named deterministic tests. Solidity checked arithmetic prevents silent wrapping in source, but the suite does not contain a dedicated maximum-target multiplication-overflow test. Do not broaden a passing exact-deposit fuzz result into a claim of complete arithmetic verification.

For repeatability, a release run can pin a fuzz seed and retain the Foundry version and output. A seed reproduces a particular campaign under compatible tooling; it does not turn random testing into a proof.

## Coverage instrumentation results

The local coverage command reported the following for the registry source:

| Metric     | Tool-reported result |
| ---------- | -------------------- |
| Lines      | 98.33% — 59/60       |
| Statements | 97.53% — 79/81       |
| Branches   | 87.50% — 14/16       |
| Functions  | 100.00% — 7/7        |

The coverage run also emitted source-anchor mapping warnings affecting registry and test-helper locations. These figures are diagnostic observations, not certified coverage percentages. The instrumentation disabled optimizer settings and viaIR for coverage analysis. Resolve mapping warnings and inspect uncovered branches before using these numbers as a release gate.

Function coverage does not mean all arguments, role combinations, callbacks, or state histories have been tested. A line can execute without its effects being asserted. Branch coverage does not establish that adversarial behavior is impossible. The named test assertions remain the more precise evidence for this documentation release.

## Accounting invariants and their proof obligations

For each bounty b, let N\_b be its target, r\_b its reward, p\_b its pending count, a\_b its paid count, and E\_b its remaining escrow:

```
0 <= p_b + a_b <= N_b
E_b = (N_b - a_b) * r_b
totalEscrow = sum_b(E_b)
address(registry).balance >= totalEscrow
```

Creation establishes E\_b=N\_b\*r\_b. Registration changes p\_b but not E\_b. Payment changes p\_b by -1, a\_b by +1, and E\_b by -r\_b. Rejection changes p\_b by -1 without changing E\_b. These transition equations explain the intended accounting; they are not a machine-checked induction proof.

Unexpected forced native transfers may increase the contract balance without increasing liabilities. Therefore balance equality is valid only in controlled histories without such transfers; the general solvency relation is greater than or equal to totalEscrow.

Further safety properties include:

| Property                                        | Required evidence                                                    |
| ----------------------------------------------- | -------------------------------------------------------------------- |
| A dataset pays at most once                     | Pending-to-Paid guard, replay tests and adversarial sequence testing |
| Failed payout is atomic                         | Reverting-recipient tests covering status and all counters           |
| Rejected commitments remain reserved            | Tests for both hash and storage-ID mappings                          |
| Campaigns cannot spend each other's liabilities | Multi-campaign interleaving invariants                               |
| Identifiers do not reset                        | Stateful tests across reject/pay sequences                           |
| Authorization cannot be bypassed                | Role grant/revoke/renounce and callback scenarios                    |

No stateful invariant harness or formal verification configuration for this registry is established by the current suite. Such a harness should generate sequences of campaign creation, submission, rejection, payout, role changes, and forced transfers while tracking an independent reference ledger.

## Reentrancy and external-call boundary

validateAndDisburse is the only native disbursement function. It uses nonReentrant and changes status and liability counters before calling the recipient. A recipient failure triggers PayoutFailed, reverting all transaction-local effects.

The reentrant helper is granted validator authority, so its callback reaches the payout guard instead of failing solely for lack of a role. That is useful adversarial coverage. It still targets the same dataset and does not enumerate all cross-function interactions.

Other functions are not uniformly guarded. They have no direct native-value payout, but a recipient holding additional roles may call them during its callback. Their independent authorization and accounting behavior must be analyzed; “ReentrancyGuard protects every state-changing function” is not an accurate description.

There is no withdrawal function to protect. A permanently reverting recipient can leave a dataset Pending. A validator can reject it, but cannot amend its recipient or refund the sponsor. This is a liveness limitation, not an accounting underflow.

## Input validation and authority gaps

Zero initial admin and token addresses are rejected by the constructor; zero token updates are rejected; zero and registry-self recipients are rejected on submission. A nonzero external token reference is not checked for deployed code or ERC-20 conformance.

Inherited AccessControl grantRole permits the zero address. There is no two-step administrator transfer, last-admin protection, enforced multisignature, or recovery delay. The constructor's zero-address test must not be generalized to all role-management operations.

There is no protocol fee or fee setter, so there is no fee hard cap. The relevant production requirement is to implement and test a bounded fee mechanism before claiming one. A future cap must define its basis, maximum, rounding, governance delay, and whether existing campaigns can change terms.

The absence of cancellation and expiry means funds can remain locked indefinitely. This directly contradicts the proposed reclaimability invariant. Funding a production registry requires resolving that design decision; documentation cannot supply a missing withdrawal path.

## Required release evidence

Before describing a production release as verified, establish independently reviewed deployed bytecode, constructor configuration, role membership, and operational key custody. Exercise a bounded native-USDC campaign on the intended network. Verify archival bytes, inclusion evidence, validator receipts, successful payout, and recovery from partial failure.

Expand tests for maximum arithmetic inputs, storage-ID character boundaries, self-recipient rejection, role rotation, stale/unknown dataset IDs, callback combinations, and interleaved campaigns. Add explicit liveness tests when expiry/refund logic exists. Model fee conservation only after a real fee implementation exists.

Formal verification would require explicit properties, an execution model, assumptions, and solver/proof artifacts. A 17/17 Foundry result and 256 fuzz cases do not meet that definition. The [developer interface reference](/5.-smart-contracts-and-arc-network-settlement/5.4-deployments-and-interfaces.md) therefore documents only the callable implementation and clearly separates future compatibility requirements.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.arcv.network/5.-smart-contracts-and-arc-network-settlement/5.3-security-and-test-suite.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
