Skip to content

v2 contracts: fixes for the 20.10 self-audit (not deployed) - #3

Open
predge-ai wants to merge 20 commits into
mainfrom
v2/contracts
Open

predge-ai wants to merge 20 commits into
mainfrom
v2/contracts

Conversation

@predge-ai

Copy link
Copy Markdown
Member

Redesigned contract set (contracts/v2) fixing the 20.10.2026 self-audit findings H-01..H-03, M-01..M-05, L-01..L-07 and the extra findings from PR #2. Nothing is deployed and v1 files are untouched.

  • Contracts: AgentJobV2, PredgeValidatorBondV2, PredgeRefundArbiterV2, PredgeAgentValidatorV2, PredgeSettlementV2 (+ demo-only MockRefundProtocolV2). solc 0.8.26 pinned, optimizer 200, cancun.
  • Tests (FOUNDRY_CONFIG=foundry-v2.toml forge test): 131 tests incl. fuzz and invariants; PoCs run each v1 attack against v1 (succeeds) and v2 (reverts); 100% line/statement/branch/function coverage.
  • Tooling: node deploy.mjs --chain <c> --set v2 [--fork]; fork e2e on Arc and Base runs the honest path plus the attack sequences; owner identities come from config/v2.config.json (all null until the owner fills them); v2 never reads ~/.predge-deployer.
  • Uses a separate foundry-v2.toml so it does not collide with the v1 suite in PR test: Foundry suite, invariants, mutation-tested; CI #2.

Not for merge before the external scan.

…lement) with Foundry unit, fuzz, invariant and PoC tests
…ain bond floors, plan tests, mutation script
…shed 20.10

Owner decision 20.10.2026: every bond equals the per-chain floor (~$5); the bps
mechanism stays in the contracts and the Safe can raise it via setParams later.
Arc 5 USDC (18-dec native), Base 0.002 ETH. Contracts unchanged (scanned edd1c46).
Foundry v2 132/132, node 20/20, Arc and Base fork e2e PASS with these params.
deployer 0xbB7A0c24677339045434273FFC2266B99E1e1301 (gas only, no role)
operator 0x8ffFB76c4329DDC8ee4a859DDF55AB48ba25e292 (hot ops key)
Keys generated locally, never committed; nonce 0 on Arc and Base. owner (Safe) still null.
Contracts:
- 5.1.1 (High): the client fixes `expected` in the job specHash
  (bond.specHashFor(keccak256(abi.encode(expected, salt)))); stakeAndCommit
  reverts CommitmentNotBound otherwise. "Delivered" now requires bytes hashed
  on-chain (hashedOnChain) equal to expected; a bare claimed hash never counts.
- 5.2.2 / 5.3.8 (Medium/Low): release forfeits the bond to the provider when a
  submitted job expired with no verdict (new terminal status Forfeited).
- 5.2.3 (Medium): ClientIsOperator in stakeAndCommit and recordScore.
- 5.2.1 (Medium): rule requires releaseTimestamp > now + window +
  RESOLUTION_TIMEOUT for both directions.
- 5.3.12: setLockupSeconds bounded to (window + RESOLUTION_TIMEOUT, 365 days].
- 5.3.3: registry requestHash includes agentId.
- 5.3.4: createJob rejects evaluator == client or provider.
- 5.3.10: the responding operator is recorded as validator.
- NatSpec corrected (Operated rotation, bond honest limits, job evaluator).

Tests: existing suites moved to the client-bound spec and byte delivery;
OlympixRegressions.t.sol replays each Olympix PoC path against the fixes;
new invariants for client-bound commitments and forfeiture.
Constructor guards and RESOLUTION_TIMEOUT are now asserted in named test
functions with no setUp, so a mutant that makes every deployment revert fails
a named test rather than only setUp(). The three storage->memory mutants in
PredgeAgentValidatorV2 view functions are equivalent (read-only, gas only).
smoke-v2 uses the client-bound specHash, delivers bytes, checks
specHashFor/requestHashFor(agentId, digest), and adds the 5.1.1 and 5.2.2
attack sequences. README states that the deployed Arc/Base v2 instances are
the scanned code and do not include these fixes.
…vidence (option a)

PredgeValidatorBondV2.recordScore now reverts VerdictContradictsEvidence unless
score 100 <=> (hashedOnChain && deliverable == client-committed expected).
A false verdict cannot be recorded at any bond size, including bondBps = 0
(closes Olympix 5.1.2 by construction).

- remove the unreachable challenge path: challenge, wouldSlash, reclaim,
  Slashed/Reclaimed, slashCount, disputeWindow and its bounds
- bond is credited back to the staker with the verdict; it backs liveness only
  (silence after a submission still forfeits it to the provider, 5.2.2)
- add verdictFor(jobId, expected); constructor (owner, operator, job, minBond, bondBps)
- tests: unit + fuzz proving no false verdict can be recorded, invariant
  everyRecordedVerdictIsTrue; 100% coverage on contracts/v2
- set 2026-10-23-v2.1 with nonceBase 6: the 20.10 deployer creates v2.1 at
  nonces 6..11, same addresses on Arc and Base
- fork e2e updated (false pass / false fail / leaked hash must revert)
- scripts/v2-post-deploy.mjs: archive v2.0 records as *.v2-0.json, retry
  verification, mark v2.0 addresses deprecated in README
fix(v2.1): Olympix BugPoCer findings + option (a) false verdicts revert
deployments: v2.1 live on Arc and Base
…ism, Sonic, Unichain (same addresses as Arc/Base) (#7)
PredgeRefundArbiterV2 cannot drive Circle's RefundProtocol directly: Circle
has no paymentInfo (it exposes payments(id) with to/refundTo), amounts are in
USDC 6dp while the bond is sized in native wei, and Circle's arbiter is fixed
in its constructor. CircleRefundProtocolAdapter is set as Circle's arbiter,
accepts writes only from the bound Predge arbiter, maps refundTo->payer,
to->payee, scales the amount, and treats an already-refunded payment as done.

Adds our own ICircleRefundProtocol (signatures only, read from the public
source), a local MockCircleRefundProtocol mirroring its behaviour, MockUSDC,
14 Foundry tests (release, refund on verdict, dispute overturned/upheld, edge
cases) and an Arc testnet deploy script (simulation only, nothing broadcast).
The deployed arbiter and the v2 plan are unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant