4.3 KiB
Watchtower and Indexer
Optional off-chain components for payment channels: watchtower (dispute response) and indexer (channel listing).
Watchtower
Purpose
When a channel is in Dispute, the party that did not initiate the close has a limited time (challenge window) to submit a newer signed state via challengeClose. If they are offline, a malicious counterparty could submit an old state and wait for the deadline, then finalizeClose and take more than their share.
A watchtower watches the chain for ChallengeSubmitted (and channel close activity) for channels where a designated participant is involved. If it sees a close that uses an older state than the one the watchtower holds, it submits challengeClose with the latest signed state before the deadline.
Design
- Inputs: RPC URL, list of (channelId, latest signed state: nonce, balanceA, balanceB, sigs) for channels the user cares about, and a key that can sign txs (or the watchtower runs on behalf of the user and has delegated authority).
- Logic: Subscribe to or poll PaymentChannelManager for
ChallengeSubmittedandChannelClosed. For each channel in the list, ifChallengeSubmittedshows a dispute withnonceless than the watchtower’s latest nonce, callchallengeClosewith the stored state beforedisputeDeadline. - Security: The key that signs
challengeClosemust be secure (env var, HSM, or user-backed key). Prefer a dedicated key with no other funds.
Implementation options
- Small service: e.g.
services/watchtower/in this repo: Node.js or Python script that uses ethers/web3.py, reads config from env, and runs a loop or event subscription. - Separate repo: Standalone watchtower service that supports this contract’s ABI and events.
No watchtower implementation is included in this repo; this doc is the specification. Deploy and run a watchtower only if users need automated dispute response.
Indexer
Purpose
The frontend “Channels” tab lists channels by calling getChannelCount and getChannelIdByIndex, then getChannel for each. That works for a small number of channels. For many channels or for filtering by participant, an indexer can:
- Index
ChannelOpenedandChannelClosed(and optionallyChallengeSubmitted). - Expose an API: e.g. “channels where address X is participantA or participantB”, “channels in status Open/Dispute”.
Design
- Events:
ChannelOpened(channelId, participantA, participantB, depositA, depositB),ChannelClosed(channelId, balanceA, balanceB, cooperative),ChallengeSubmitted(channelId, nonce, balanceA, balanceB, newDeadline). - Storage: DB (e.g. Postgres) with tables for channels and events, keyed by chain and channelId.
- API: REST or GraphQL: e.g.
GET /channels?chainId=138&participant=0x....
Implementation options
- Existing Chain-138 / Mainnet indexer: If the project already has an indexer for the chain, add handlers for PaymentChannelManager events and expose channel queries.
- Subgraph (The Graph): Create a subgraph for PaymentChannelManager and query channels by participant.
- Custom service: Small service that subscribes to logs, writes to DB, and serves HTTP.
Document the indexer API and deployment in ops/deployment docs when one is added. Until then, the frontend uses on-chain getChannelCount + getChannelIdByIndex + getChannel only.
Implementation status
- Watchtower: A minimal implementation lives in
services/watchtower/. It subscribes toChallengeSubmittedon PaymentChannelManager and callschallengeClosewith a newer state from a JSON file before the dispute deadline. Seeservices/watchtower/README.md. - Indexer: No indexer or subgraph is implemented yet. Channels are enumerated on-chain by the frontend. To add "channels by participant", implement a custom indexer or The Graph subgraph as described above and expose an API (e.g.
GET /channels?participant=0x...).
Transaction mirroring (no change)
Channel transactions on Chain-138 are normal txs. The existing Transaction Mirroring Service mirrors them to TransactionMirror on Mainnet. No change to that service or contract is required. State anchoring of Chain-138 (including PaymentChannelManager state) is already done by the State Anchoring Service and MainnetTether.