graphprotocol / graphprotocol/dipper

Add defence-in-depth confirmation that protocol-managed cancellations took effect

Open
#638 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
0
Forks
1
Avg merge
37m
Merged PRs (30d)
3

Description

Context

In protocol-managed mode, dipper cancels an indexing agreement by calling the manager contract's cancelAgreement, passing a 32-byte terms hash that identifies which version to cancel; the contract acts only if that hash matches the one it stored at acceptance. This mode currently ships off by default behind a switch; a planned follow-up removes the self-payer path and makes it the only way agreements are funded.

The gap

Verified against the contract source: when the supplied hash is wrong or stale, the contract does not reject the call — it silently does nothing and the transaction still succeeds, emitting no event that distinguishes a no-op from a real cancel. So a cancellation that quietly did nothing would be recorded by dipper as successful: it marks the agreement cancelled while it stays live on-chain and keeps paying.

Why this is defence-in-depth, not a primary fix

The primary protections are already in place: dipper fetches the EIP-712 domain from the contract at startup (so it can't drift), the agreement struct is pinned by a drift test, and a misconfigured collector address would fail loudly at offer time before any agreement exists to cancel. A silent no-op therefore only triggers in narrow residual cases — e.g. the contract changing how it derives the hash, or the terms changing between when dipper stored them and when it cancels.

When it matters

Dormant while the mode stays off behind its switch. Once the contract path becomes the only one and is deployed against real funds, there is no fallback and this confirmation is the only detection for a silently-failed cancel. Recommended to land before that deployment — not a blocker for the code that introduces the path.

Proposed work

All three build on one missing capability — reading agreement state back from the collector.

  1. Confirm the cancel took effect. After sending a protocol-managed cancel, wait for the receipt, then read RecurringCollector.getAgreement(agreementId).state and require CanceledByPayer before marking the agreement cancelled. If not, leave it live for the next sweep to retry.
  2. Surface the failure. When the read shows the cancel didn't take, log a warning/error with the agreement id so operators see a stuck cancellation instead of silence.
  3. Recover a lost terms hash. When an agreement is missing its stored hash (e.g. pre-migration rows), read the authoritative value from getAgreementDetails(agreementId).versionHash and backfill it.

Minor cleanups (deferred)

  • The comment on the terms-hash computation says the value is "the hash dipper signs over," but in protocol-managed mode nothing is signed — the hash is only a cancel identifier. Reword, and note a mismatch fails silently.
  • Some test fakes return an "already cancelled" result the real contract never produces; give them a real state field so they model the contract's actual no-op behaviour.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the protocol-managed cancellation path that calls the manager contract's cancelAgreement. Read the receipt, then query RecurringCollector.getAgreement(agreementId).state and getAgreementDetails(agreementId).versionHash to cover cancellation confirmation and missing-hash recovery. Done means failed cancellations remain live with an agreement-id warning, successful ones reach CanceledByPayer, and missing hashes can be backfilled.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
blockchain, payments
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.