Skip to content

feat(sentinel): add bridge chain monitor for on-chain event surveillance#646

Merged
Lchangliang merged 5 commits intomainfrom
worktree-feat+bridge-monitor
Apr 9, 2026
Merged

feat(sentinel): add bridge chain monitor for on-chain event surveillance#646
Lchangliang merged 5 commits intomainfrom
worktree-feat+bridge-monitor

Conversation

@ByteYue
Copy link
Copy Markdown
Contributor

@ByteYue ByteYue commented Apr 8, 2026

Summary

Add a new chain_monitor module to Sentinel that monitors bridge contract events via Ethereum JSON-RPC (eth_getLogs polling). Implements 5 security monitoring rules:

  1. Large Withdrawal: Alert when TokensLocked exceeds configurable threshold
  2. Vault Balance: Monitor ERC20 vault balance for abnormal drops
  3. Bridge Timeout: Track cross-chain nonce correlation (ETH→Gravity), alert if not confirmed within timeout
  4. Owner Activity: Alert on privileged function calls (EmergencyWithdraw, ERC20Recovered, FeeConfigUpdated, etc.)
  5. Timelock/Ownership: Monitor Ownable2Step ownership transfers and governance bypass detection

Architecture

bin/sentinel/src/chain_monitor/
  mod.rs              — spawn_all() entry, launches monitors as independent tokio tasks
  config.rs           — TOML config structs for [chain_monitor] section
  abi.rs              — alloy sol! macro for all bridge contract event ABIs
  provider.rs         — alloy HTTP provider factory (connect_http)
  checkpoint.rs       — JSON checkpoint persistence (block cursors + pending nonces)
  large_withdrawal.rs — Rule 1: TokensLocked amount > threshold + EmergencyWithdraw
  vault_balance.rs    — Rule 2: ERC20 balanceOf polling with drop detection
  bridge_timeout.rs   — Rule 3: cross-chain nonce tracking with tokio::select! loop
  owner_activity.rs   — Rule 4: privileged event scanning on GBridgeSender + GravityPortal
  timelock.rs         — Rule 5: OwnershipTransferStarted/Transferred + governance bypass

Key Design Decisions

Decision Choice Rationale
Ethereum library alloy v1.0.37 Consistent with gravity_cli, ethers-rs is in maintenance mode
Polling strategy eth_getLogs Idempotent, checkpoint-friendly; WebSocket is fragile in prod
Module granularity One file per rule Each rule has distinct polling strategy, clear separation
State persistence JSON checkpoint file Simple, atomic write (tmp+rename), survives restarts
Integration pattern Independent tokio tasks Same as existing Probe module, no changes to main event loop

Minimal Integration Points

  • config.rs: Added chain_monitor: Option<ChainMonitorConfig> (fully backward-compatible)
  • main.rs: Added mod chain_monitor + 4-line spawn block after probe startup
  • Cargo.toml: Added alloy dependencies, upgraded reqwest 0.11→0.12

How Has This Been Tested

End-to-End Test with Mock RPC Server

Tested using a lightweight Python mock JSON-RPC server that simulates both Ethereum and Gravity chains, returning pre-crafted event logs at specific block numbers. No real contracts or EVM needed.

Test scenario:

Block Chain Event Expected Alert
5 Ethereum TokensLocked (100 tokens, nonce=1) Large withdrawal P0
6 Ethereum TokensLocked (100 tokens, nonce=2) Large withdrawal P0
8 Gravity NativeMinted (nonce=1 only) — (nonce 1 confirmed)
10 Ethereum EmergencyWithdraw Owner activity P0
15 Ethereum OwnershipTransferred Timelock P0
nonce=2 never minted Bridge timeout P0 (after 15s)

Sentinel stdout output:

Loading config from sentinel_bridge_test.toml
Starting chain monitors...
Starting chain monitor: large withdrawal (threshold: 500000000000000000 wei)
Starting chain monitor: bridge timeout (15s threshold)
Starting chain monitor: owner activity
Starting chain monitor: timelock/ownership
Sentinel started...
Bridge timeout: Tracking nonce 1 (block 5, timestamp ...)
Bridge timeout: Tracking nonce 2 (block 6, timestamp ...)
Bridge timeout: Nonce 1 confirmed on Gravity

Webhook alerts received (Feishu):

🚨 Log Sentinel Alert [P0] 🚨
File: BRIDGE_TIMEOUT
Error: Bridge transaction TIMEOUT!
  Nonce: 2
  Locked on Ethereum at: ... (17s ago)
  Not yet confirmed on Gravity after 15 seconds threshold
  Investigate relay pipeline!

🚨 Log Sentinel Alert [P0] 🚨
File: TIMELOCK
Error: CRITICAL OWNERSHIP CHANGE: Transfer completed!
  Contract: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
  Previous Owner: 0xf39F...2266
  New Owner: 0xDeaD...beef
  Ownership has been transferred - verify this was authorized!

🚨 Log Sentinel Alert [P0] 🚨
File: OWNER_ACTIVITY
Error: OWNER ACTIVITY: EmergencyWithdraw called!
  Recipient: 0x6954...765C
  Amount: 100000000000000000000 wei
  Contract: GBridgeSender (0x9fE4...fa6e0)

Verified behaviors:

  • ✅ Event decoding (alloy sol! macro + decode_log_data)
  • ✅ Cross-chain nonce correlation (nonce 1 tracked → confirmed → cleared)
  • ✅ Timeout detection (nonce 2 never confirmed → P0 alert after threshold)
  • ✅ Privileged operation detection (EmergencyWithdraw → P0)
  • ✅ Ownership change detection (OwnershipTransferred → P0)
  • ✅ Webhook delivery (Feishu)
  • ✅ Checkpoint persistence (block cursors saved to JSON, resume on restart)
  • ✅ Graceful handling when no events found (no crashes, no false positives)

Bridge E2E Cluster Integration

Also validated against the live bridge E2E cluster (python3 gravity_e2e/runner.py bridge):

  • Sentinel successfully connected to Gravity chain RPC (port 8545)
  • Scanned from block 0 to ~2996, made 107 network calls in 15 seconds
  • Checkpoint correctly persisted and resumed across runs
  • No false positives when scanning real chain data with no matching events

TOML Configuration Example

[chain_monitor]
ethereum_rpc_url = "https://eth-mainnet.example.com"
gravity_rpc_url = "http://localhost:8545"
gbridge_sender_address = "0x..."
gravity_portal_address = "0x..."
gbridge_receiver_address = "0x..."
native_oracle_address = "0x..."
owner_address = "0x..."
gtoken_address = "0x..."
poll_interval_seconds = 12
confirmation_blocks = 2
checkpoint_path = "chain_monitor_checkpoint.json"

[chain_monitor.large_withdrawal]
enabled = true
threshold_wei = "50000000000000000000"
priority = "p0"

[chain_monitor.bridge_timeout]
enabled = true
timeout_seconds = 1800
check_interval_seconds = 60
priority = "p0"

[chain_monitor.owner_activity]
enabled = true
priority = "p0"

[chain_monitor.timelock]
enabled = true
priority = "p0"
expected_governance_address = "0x..."

Type of Change

  • New feature

Which Components or Systems Does This Change Impact?

  • CLI Tools
  • Other (Sentinel monitoring daemon)

Checklist

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works

ByteYue and others added 4 commits April 9, 2026 14:33
Add a new chain_monitor module that monitors bridge contract events via
Ethereum JSON-RPC (eth_getLogs polling). Implements 5 security monitoring
rules:

1. Large Withdrawal: Alert when TokensLocked exceeds configurable threshold
2. Vault Balance: Monitor ERC20 vault balance for abnormal drops
3. Bridge Timeout: Track cross-chain nonce correlation (ETH→Gravity)
4. Owner Activity: Alert on privileged function calls (EmergencyWithdraw, etc.)
5. Timelock/Ownership: Monitor Ownable2Step ownership transfers and
   governance bypass detection

Key design decisions:
- Uses alloy v1.0.37 (consistent with gravity_cli)
- eth_getLogs polling over WebSocket for reliability
- JSON checkpoint persistence for block cursor recovery
- Independent tokio tasks per rule (same pattern as Probe)
- Fully backward-compatible: chain_monitor config section is optional

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…too_many_arguments

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ByteYue ByteYue force-pushed the worktree-feat+bridge-monitor branch from a295379 to 40160fe Compare April 9, 2026 06:33
@Lchangliang Lchangliang merged commit bf772b9 into main Apr 9, 2026
8 checks passed
@Lchangliang Lchangliang deleted the worktree-feat+bridge-monitor branch April 9, 2026 15:34
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.

3 participants