The goal of this repo is to detect misses by the Wormhole network and provide a visualization of Wormhole messages and misses.
The approach of this code is to crawl for Wormhole transactions on-chain and query if the corresponding VAA is available. This differs fundamentally from wormhole-explorer, in that the explorer provides what did happen in the network, while this code can capture what didn't.
- Detection - discover messages from the source chain, check if they have VAAs
- Alerting - send alerts when VAAs are missing for some period of time
- Recovery - automatically request that a guardian trigger re-observation on the missed VAA, developing an SLA
Note: this repo uses npm workspaces
npm ciWatches each blockchain for new logs. Will need to expand to all chains and also check if the VAA is available.
Compare the most recent "finalized" block to the most recent fetched block (or the block that the wormhole contract was deployed) and fetches, at most, the 100 earliest blocks (for timestamps) and logs (for messages). A benefit of this approach is that it should be flexible enough for all finality types and it provides for automatic backfilling and outage recovery.
You'll need a .env with an infura URL, or something that supports "finalized" block calls on Eth.
npm run dev -w watcherTwo options to load and save data:
- local .json file —
DB_SOURCE=local(default). Optionally setJSON_DB_FILEfor the file path. - Google Firestore —
DB_SOURCE=firestoreplus:FIRESTORE_ACCOUNT_KEY_PATH=/path/to/service-account.jsonFIRESTORE_LATEST_COLLECTION— per-chain last-processed-block pointerFIRESTORE_MISSING_VAAS_COLLECTION— miss trackingFIRESTORE_SIGNED_VAAS_COLLECTION— signed VAA archive
The first time the Firestore database is set up, remember to create the composite indexes for signedVAAs
gcloud firestore --project "<PROJECT>" indexes composite create --collection-group=signedVAAs --query-scope=COLLECTION --field-config=field-path=chainId,order=ascending --field-config=field-path=day,order=ascending
gcloud firestore --project "<PROJECT>" indexes composite create --collection-group=signedVAAs --query-scope=COLLECTION --field-config=field-path=chainEmitter,order=ascending --field-config=field-path=day,order=ascendingDisplays a visualization of the database.
npm start -w webscripts/debug-miss.ts traces stuck VAAs through the pipeline and reports the first stage each one is blocked at. It checks, in order:
- Is a signed VAA already published on Wormholescan? (if so, the miss is stale and downstream checks are informational)
- Has the source-chain transaction reached finality, per the watcher's last indexed block?
- If the chain is in the delegate set, have enough current delegate guardians signed?
- Is the message held in the governor by any guardians?
- Is the message pending in the global accountant?
- Have enough current canonical guardians observed the message to reach quorum?
Stages 2 and 5 only count signatures from guardians currently in the relevant set; rotated-out keys are ignored.
# Analyze every current miss past the standard miss threshold
npm run debug-miss
# Skip the threshold filter and look at all reported misses
npm run debug-miss -- --all
# Inspect one specific VAA
npm run debug-miss -- 30/0000000000000000000000008d2de8d2f73f1f4cab472ac9a881c9b123c79627/254903
# Also write a CSV report
npm run debug-miss -- --csv miss.csv./cloud_functions stores the TypeScript Google Cloud Functions.
To add a new function:
-
Create a new file under
./cloud_functions/src.CRON jobs are generally prefixed with
compute -
Register new function in
./cloud_functions/src/index.tsexport const { newFunction } = require('./newFunction'); functions.http('newFunction', newFunction);
To run it locally:
-
Make sure to authenticate your GCP account.
-
Update the
startscript in./cloud_functions/package.json{ "scripts": { "...": "...", "start": "npx functions-framework --target=newFunction [--signature-type=http]" } } -
To run the function locally, run in
./cloud_functions:npm run start