Skip to content

FOC Economic Security & Adversarial Testing Vectors #217

@parthshah1

Description

@parthshah1

Summary

Add adversarial stress vectors to the FOC Antithesis harness that actively attack the PDP/FWSS/FilecoinPay payment pipeline from the perspective of dishonest clients and SPs. These vectors go beyond happy-path testing, they attempt to steal funds, bypass signature verification, exploit insolvency edge cases, and overwhelm the system with rapid requests.

Motivation

The M4.2 mainnet GA milestone (March 31, 2026) ships with several open items including incomplete audits, unresolved debt handling design, and known audit findings with fix PRs still open.

We need continuous adversarial testing to catch regressions and find new bugs before GA.

What We Test

Active Attacks (stress engine)

A dedicated secondary client wallet with minimal USDFC (0.06) acts as the attacker. It's isolated from the legitimate FOC lifecycle to avoid false positives.

Attack Persona What it does What a finding looks like
Empty Dataset Fee Deadbeat client Creates empty datasets via Curio HTTP. Reads client USDFC before and after. Checks funds decreased. Funds unchanged after confirmed dataset creation means the FWSS burn rail is not extracting the sybil fee. SP is drained 0.1 FIL per request while the client pays nothing.
Insolvency Creation Deadbeat client Withdraws all available USDFC from FilecoinPayV1, making the client insolvent. Then attempts to create a new dataset with zero available funds. Re-funds the wallet afterwards for future probes. Dataset creation succeeds with zero available funds means the validatePayerOperatorApprovalAndFunds check is not enforced. SP provides storage service with zero payment guarantee.
Cross-Payer Replay Replay attacker Signs a CreateDataSet EIP-712 message with the attacker's key but encodes the victim (primary FOC client) as the payer in the extraData. Submits via Curio HTTP. Reads victim's funds before and after. Victim's funds decrease without the victim ever signing means the EIP-712 signature verification does not bind the signer to the payer field. Any address can be charged by anyone who can craft valid extraData.
Burst Creation Spammer Fires 3-5 dataset creation requests in rapid succession via Curio HTTP without waiting for on-chain confirmation between requests. Each uses a unique clientDataSetId and fresh EIP-712 signature. Checks how many were accepted and whether fees were charged. All requests accepted without throttling means no rate limiting exists on the Curio PDP API. An attacker can drain SP FIL at HTTP request speed.

Passive Invariants (foc-sidecar)

The sidecar runs 8 safety checks every 4 seconds against on-chain state:

Check Type What it catches
FPV1 solvency Always FilecoinPay contract holds less USDFC than the sum of all payer account balances
Rail-to-dataset mapping Always Payment rail's reverse mapping points to wrong or deleted dataset
Provider ID consistency Always On-chain provider ID from registry doesn't match the ID from DataSetCreated event
Proofset liveness Always Active (non-deleted) dataset reports dataSetLive = false on-chain
Deleted dataset not live Always Deleted dataset still reports dataSetLive = true on-chain
Proving advancement Sometimes Challenge epoch and proven epoch advance over time (proving pipeline working)
Piece accounting Always activePieceCount > leafCount for a dataset (should be impossible)
Rate consistency Always Dataset with active pieces has zero payment rate on its PDP rail

Architecture

  • Single deck entry DoPDPGriefingProbe with random internal dispatch across attack vectors
  • Secondary client wallet removed from general wallet pool (prevents nonce collision with other stress vectors)
  • All attack assertions use assert.Sometimes (tolerant of Antithesis fault injection where any tx can fail)
  • Read-only access to FOC primary dataset state (no interference with legitimate FOC lifecycle)
  • Sidecar assertions use assert.Always for safety invariants

Future Vectors

  • Settlement drain: Client withdraws USDFC right before settlement epoch, SP's settle tx reverts
  • Unilateral SP termination: SP terminates client's dataset without consent
  • Nonce replay after deletion: Reuse clientDataSetId after dataset is deleted
  • Settlement overpayment: Verify settlement math doesn't overcharge via integer truncation
  • Stale rail detection: Create and delete datasets to produce orphan rails, verify Curio doesn't get stuck (regression for pdpv0: stuck settlement transactions after confirmed on-chain but failed DB update filecoin-project/curio#922)

Branch

parth/sybil-fee

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions