Skip to content

Watcher implementation & Stellar on Tilt#38

Open
svlachakis wants to merge 7 commits into
mainfrom
watcher-implementation
Open

Watcher implementation & Stellar on Tilt#38
svlachakis wants to merge 7 commits into
mainfrom
watcher-implementation

Conversation

@svlachakis
Copy link
Copy Markdown

@svlachakis svlachakis commented Nov 7, 2025

Stellar Watcher

Core Functionality

The Stellar watcher monitors the Wormhole Core Bridge contract on Stellar for cross-chain message publications

Event Polling

  • Continuously polls the Soroban RPC getEvents endpoint at configurable intervals (default 700ms)
  • Detects new message_published events from the Wormhole contract

Event Parsing

  • Decodes base64-encoded XDR event data from Soroban contract events
  • Extracts message fields: nonce, sequence, emitter_address, payload, consistency_level
  • Converts Stellar-specific formats to Wormhole's standard MessagePublication format

Message Publishing

Publishes parsed messages to the guardian's message processing pipeline with:

  • Timestamp: current time
  • Emitter chain: Stellar
  • Fields: all extracted message fields
Ledger Tracking
  • Maintains state of the last processed ledger
  • Avoids duplicate processing
  • Handles restarts gracefully

Reobservation Support

Reobservation Request Handling

  • Listens for reobservation requests from the guardian network
  • Queries events by specific transaction hash
  • Re-parses and republishes messages with IsReobservation = true flag
  • Enables message recovery and debugging workflows

Future Work

Improvements

  • Timestamp changed from time.Now() → ledgerClosedAt / createdAt
  • IsReobservation flow upgraded from linear event search to getTransaction
  • Reobserver interface now implemented (returns w from Create)
  • Readiness (readiness.SetReady) wired up
  • P2P heartbeat (SetNetworkStats) wired up

Tests

  • Integration tests for reobservation requests
  • End-to-end tests with local Stellar network - should be new future task

Metrics (Maybe?)

  • Message observation counters (observed / confirmed / orphaned)
  • Reobservation request metrics (success / failure / latency) - should be new future task
  • Connection error tracking
  • Current ledger height gauges
  • RPC call latency histograms - should be new future task

Guardian Set Management (Maybe?) - should be new future task

  • Read guardian sets from Soroban contract storage
  • Requires implementing simulateTransaction or getLedgerEntries RPC calls or another approach
  • Periodic polling to detect guardian set updates
  • Publish guardian set changes to the guardian network

Tilt Infra - should be new future task

  • Someone needs to look on Tilt infra an make it even better, that's just a POC.

Example Result / Signed VAA

2025-11-07T15:10:43.230Z	INFO	guardian-0.root.61_watch	stellar message published	{"component": "stellar_watcher", "rpc": "[http://stellar.default.svc.cluster.local:8000/soroban/rpc",](http://stellar.default.svc.cluster.local:8000/soroban/rpc%22,) "contract": "CBWQUIB4R65Z2DGC263FQ7BBI7TGIGOLFTYMLE6QPWBD5QDOUVJY3AKR", "chain": "stellar", "ledger": 2438, "tx": "2e5c06ece7fc8191d2028ed6dd06d53506956f1ca7fabfb0e952ec258b33c456", "seq": 1, "consistency": 1}
2025-11-07T15:10:43.251Z	INFO	guardian-0.root	signed VAA with quorum	{"message_id": "61/fda6fcf519faef33c6cce224fe5eb856da4212588c6202eaa32a6db8facdceac/1", "digest": "1e08121ec71dc2e3ce18945b7ffdd71a0fe9773216308230d5e337c61df2222e"}

@svlachakis svlachakis changed the title Watcher implementation & stellar on tilt Watcher implementation & Stellar on Tilt Nov 7, 2025
  1. Timestamp from ledger — pollOnce now parses ledgerClosedAt from each event (ISO 8601 field from Stellar RPC). handleReobservationRequest calls getTransaction and
  uses its createdAt unix timestamp. All guardians will produce identical VAAs.
  2. Reobservation routine unmanaged — Replaced bare go w.runReobservationHandler(...) with common.RunWithScissors(ctx, errC, "stellar_reobservation", ...). The Run
  loop selects on errC and returns if the handler dies.
  3. TxID missing — mp.TxID is now set from hex-decoded txHash in both pollOnce and handleReobservationRequest.
  4. Reobservation uses getTransaction — Rewrote handleReobservationRequest to first call getTransaction(txHash) (gets ledger + timestamp), then
  getEvents(startLedger=ledger) filtered by txHash. Handles NOT_FOUND/FAILED status explicitly.
  5. Prometheus metrics — Added stellarConnectionErrors, stellarMessagesObserved, stellarMessagesConfirmed, currentStellarLedger counters/gauges.
  6. Reobserver interface — Create() now returns w instead of nil. Added Reobserve(ctx, chainID, txID, customEndpoint) method that creates a fresh HTTP client for the
  custom endpoint.
  7. Readiness — readiness.SetReady(w.readinessSync) called on every successful getEvents response in pollOnce.
  8. P2P heartbeat — p2p.DefaultRegistry.SetNetworkStats called on startup (with contract address) and updated each poll with the latestLedger height from the
  getEvents response.
  9. Zero emitter address — parseMessageFromXDR now returns nil with a Warn log if emitterAddress is empty.
  10. Pagination — pollOnce now loops using the event id as cursor when exactly maxPerPoll events are returned, ensuring no events are missed.
  11. Redundant RPC call — pollOnce no longer calls getLatestLedger separately; uses latestLedger from the getEvents response directly.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant