paritytech / paritytech/web3-storage
Formal verification: let's start with the challenge protocol and see
@ilchu is already working on this.
Since Jul 15, 2026.
- Dominant language
- Rust
- Stars
- 12
- Forks
- 3
- Avg merge
- 2d 2h
- Merged PRs (30d)
- 33
Description
Motivation
The economic security of the system rests on one claim: an honest provider is never slashed, and a provider that lost data cannot escape slashing. This claim depends on interleavings of extrinsics, deadlines, and on_finalize sweeps — exactly the class of bugs unit tests are weakest at, and model checkers are built for. Quint (Informal Systems' TLA+ successor) lets us specify the challenge state machine, check invariants exhaustively over small instances, and later replay generated traces against the real pallet.
Current state
- The challenge flow was recently overhauled (#125) and now has three entry points (
challenge_checkpoint,challenge_offchain,challenge_replica), a singlerespond_to_challengewith four response variants, and a timeout path inon_finalize(pallet/src/impls/challenges.rs). - Challenges are deadline-bucketed via
ChallengeId { deadline, index }with aMaxChallengesPerDeadlinecap (primitives/src/lib.rs). - Four slash reasons exist (
Timeout,InvalidProof,InvalidDeletionClaim,InvalidSupersededClaim) — each represents a known escape hatch that was closed manually; nothing systematically searches for the next one. - Test coverage is example-based (
pallet/src/tests/); no formal spec of the protocol exists anywhere in the repo.
Proposed solution
Add a Quint spec specs/quint/challenges.qnt modeling the challenge lifecycle as a state machine:
- State: providers (stake, ghost flag
hasData), one bucket, open challenges by deadline, balances. Crypto is abstracted — "proof verifies" becomes a nondeterministic boolean tied to the ghost flag; MMR/Merkle internals stay out of scope. - Actions: the three challenge extrinsics,
respond_to_challenge(all four variants), andadvanceBlockmodeling theon_finalizetimeout sweep. - Invariants:
honestNeverSlashed— a provider with the data that responds before the deadline is never slashed.challengeResolvesOnce— no challenge is both defended and timed out.dishonestAlwaysSlashable— a provider without the data has no move sequence (respondDeleted, claimSuperseded, stall, deregister) that keeps full stake.depositConservation— challenger deposits are always either refunded or accounted for, never leaked.
Small instances (2 providers, ~3-block timeout) are sufficient — the bugs this finds are logical, not scale-dependent. Checked via quint run (simulation) and quint verify (bounded model checking / Apalache), wired into CI.
Follow-up (out of scope here): extend the spec to funds conservation across agreements/checkpoints, provider deregistration vs. open challenges, and ITF trace replay against the pallet mock as a model-based test.
TODO
-
specs/quint/challenges.qnt— state, actions, invariants above -
quint run+quint verifypassing on a small instance - CI job running the spec
- Document any invariant violations found as separate issues
Related issues
- #125 — challenge flow overhaul (the protocol being specified)
- #44 — production readiness
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.