Skip to content

Commit cbe7fb9

Browse files
ByteYueclaude
andauthored
fix(oracle): bump greth to b0ec42c, verified by new e2e cases (#725)
## Summary - Bump `gravity-reth` to `b0ec42c647fb397b1887a5d41bc76441f545c5fa`. The new rev carries an oracle / JWK nonce conversion fix that resolves a `No JWK entries to convert oracle nonce` error inside a single epoch (see "Regression reproduction" below). - Add two new bridge e2e cases that exercise the oracle pipeline under scenarios the existing `bridge` suite doesn't cover: - **`bridge_multi_round`** — 5 oracle update rounds within a single epoch (`epoch_interval = 4h`). Validates that repeated in-epoch oracle updates settle without JWK/state regressions. Asserts every `jwk txn epoch …` row from the test window shares the same epoch. - **`bridge_cross_epoch`** — bridge events spanning an epoch transition (`epoch_interval = 60s`). Validates that batches dispatched in consecutive epochs each settle correctly. Asserts the two `jwk txn epoch …` rows fall in strictly increasing epochs. - Supporting infrastructure (used by both new cases): - **`gravity_e2e/utils/mock_anvil.py`** — new `set_finalized()` helper + `mock_setFinalized` JSON-RPC, so tests can stage event visibility (preload all events, then advance `finalized` block in phases) without restarting Anvil. - **`gravity_e2e/utils/bridge_utils.py`** — `poll_all_native_minted` now performs incremental log scans inside the polling loop and short-circuits the moment every expected `NativeMinted` event is observed. `isProcessed(nonce)` reverts for unprocessed nonces in this setup, so relying on it alone forced the full timeout per round — multi-round wall-clock dropped from ~25 min (5 × 300 s) to ~1 min after this fix. ## Regression reproduction (old greth vs new greth) Re-pointed `bin/gravity_node/Cargo.toml` at the previous rev `364b851665cad891b9cc3cfbc0f958cb21f62bdd`, rebuilt `gravity_node`, and ran `bridge_multi_round` with the **same** test code as this PR (`--bridge-count 10 --rounds 5`). The bug surfaces deterministically: | | Old greth `364b851` | New greth `b0ec42c` (this PR) | |---|---|---| | `bridge_multi_round` suite | **FAILED** in 197 s | PASSED | | Round 1 (nonces 1–2) | ✅ settled in 6.1 s | ✅ settled in seconds | | Round 2 (nonces 3–4) | ❌ timed out at 180 s; later rounds never ran | ✅ | | Rounds 3–5 | not reached | ✅ all in epoch 2 | | `No JWK entries to convert oracle nonce` log lines | **≥18 occurrences**, repeating every ~10 s from the start of round 2 onward, all tagged `{"epoch":2}` | **0 occurrences** | | Error source | `aptos-jwk-consensus/src/jwk_manager/mod.rs:154` (reached via the old greth rev's pinned `gravity-aptos`) | — | Representative log line from the failing run (one of many, all identical apart from timestamp/thread): ``` 2026-05-20T06:24:58.057902Z [jwk-1] ERROR …/aptos-jwk-consensus/src/jwk_manager/mod.rs:154 JWKManager handling error: No JWK entries to convert oracle nonce {"epoch":2} ``` Round 1 succeeds because the JWK state is initialised cleanly at the start of the epoch; round 2's oracle nonce push then fails to find JWK entries to convert, and `jwk_observer`'s 10 s retry loop keeps logging the same error until the test timeout. New `b0ec42c` cleanly handles repeated in-epoch oracle pushes — both `bridge_multi_round` (5 rounds, same epoch) and `bridge_cross_epoch` (epoch 2 → 3 transition) pass with the error absent from the logs. After the reproduction, `Cargo.toml` was reverted to `b0ec42c` and the binary was rebuilt; no working-tree changes were left behind. ## Test plan All verified locally on `linux/x86_64`, against this branch's binaries (`RUSTC_WRAPPER=` to bypass a flaky sccache server): - [x] `cargo build -p gravity_node --profile quick-release` clean - [x] `cargo build -p gravity_cli --profile quick-release` clean - [x] `python3 gravity_e2e/runner.py bridge --force-init --bridge-count 10` → PASSED (1.09 s with the incremental-scan short-circuit) - [x] `python3 gravity_e2e/runner.py bridge_multi_round --force-init --bridge-count 10 --rounds 5` → PASSED — all 5 rounds in epoch `[2]` (verified by `jwk txn epoch …` row scan in `validator.log`; nonce cumulatively advanced `0 → 2 → 4 → 6 → 8 → 10`) - [x] `python3 gravity_e2e/runner.py bridge_cross_epoch --force-init --bridge-count 10 --first-batch-fraction 0.5` → PASSED — first batch `jwk txn` in epoch 2 (block 74), second batch in epoch 3 (block 273), 10/10 minted - [x] Targeted search of `validator.log` / `reth.log` / `debug.log` across both new cases for `No JWK entries to convert oracle nonce` / `JWK entries to convert` — no occurrences with the new greth rev - [x] Regression: reverting `greth` to `364b851` reproduces the bug in `bridge_multi_round` (round 2 hangs, ≥18 `No JWK entries to convert oracle nonce` log lines in epoch 2 — see table above) - [ ] CI: full `e2e-docker` workflow with the new suites picked up 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d2f0ca8 commit cbe7fb9

18 files changed

Lines changed: 2223 additions & 118 deletions

File tree

Cargo.lock

Lines changed: 112 additions & 112 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bin/gravity_node/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ serde = "^1.0.226"
3232
bincode = "1.3"
3333
time = "0.3.36"
3434
anyhow = "1.0.87"
35-
greth = { git = "https://github.com/Galxe/gravity-reth", rev = "364b851665cad891b9cc3cfbc0f958cb21f62bdd" }
35+
greth = { git = "https://github.com/Galxe/gravity-reth", rev = "41c0b7092ad578abcfb59b3aeb0ce9ec43f5fcf7" }
3636
reqwest = "0.12.9"
3737
alloy-primitives = { version = "=1.3.1", default-features = false, features = ["map-foldhash"] }
3838
alloy-eips = { version = "^1.0.37", default-features = false }
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Bridge E2E Test Suite
2+
3+
Tests the full bridge flow from an external EVM chain (Anvil) to the Gravity chain, verifying cross-chain token minting via oracle consensus.
4+
5+
## End-to-End Data Flow
6+
7+
```mermaid
8+
sequenceDiagram
9+
participant Test as test_bridge.py
10+
participant Anvil as Anvil (port 8546)
11+
participant Node as gravity_node (port 8545)
12+
13+
Note over Test: Setup Phase
14+
Test->>Anvil: Start Anvil + forge deploy contracts
15+
Test->>Node: Write relayer_config (Anvil URI mapping)
16+
17+
Note over Test: 10-min Loop
18+
loop Each iteration
19+
Test->>Node: Record recipient balance (before)
20+
Test->>Anvil: web3.py: mint → approve → bridgeToGravity()
21+
Anvil-->>Anvil: emit MessageSent
22+
Node->>Anvil: Relayer fetches event via RPC
23+
Node->>Node: Consensus → GBridgeReceiver → NativeMinted
24+
Test->>Node: poll_native_minted(nonce) ✓
25+
Test->>Node: Verify balance_after - balance_before == amount ✓
26+
end
27+
28+
Note over Test: Report stats (latency, success rate, nonce continuity)
29+
```
30+
31+
## Prerequisites
32+
33+
- **Anvil** (from foundry) installed and in PATH
34+
- **forge** installed and in PATH
35+
- **gravity_chain_core_contracts** repo available (default: `~/projects/gravity_chain_core_contracts`)
36+
- **gravity_node** binary built
37+
38+
## How to Run
39+
40+
### Via the runner script
41+
42+
```bash
43+
cd gravity-sdk/gravity_e2e
44+
./run_test.sh bridge
45+
```
46+
47+
### Directly with pytest
48+
49+
```bash
50+
# From gravity_e2e/ directory
51+
pytest cluster_test_cases/bridge/test_bridge.py \
52+
--cluster-config cluster_test_cases/bridge/cluster.toml \
53+
--contracts-dir ~/projects/gravity_chain_core_contracts \
54+
--bridge-duration 600 \
55+
-v -s
56+
```
57+
58+
### Custom duration (e.g., 1 minute for quick smoke test)
59+
60+
```bash
61+
pytest cluster_test_cases/bridge/test_bridge.py --bridge-duration 60 -v -s
62+
```
63+
64+
## Configuration
65+
66+
### `cluster.toml`
67+
68+
Single-node cluster with oracle + bridge config:
69+
70+
- `oracle_config.bridge_config.deploy = true` — deploys `GBridgeReceiver` at genesis
71+
- `oracle_config.bridge_config.trusted_bridge` — points to Anvil's `GBridgeSender` (deterministic address)
72+
- `oracle_config.tasks` — watches Anvil (chainId 31337) for `MessageSent` events on `GravityPortal`
73+
74+
### Relayer config
75+
76+
Dynamically written by the `anvil_bridge` fixture into each node's config dir:
77+
78+
```json
79+
{
80+
"uri_mappings": {
81+
"gravity://0/31337/events?contract=0xe7f1...&eventSignature=0x5646...&fromBlock=0": "http://localhost:8546"
82+
}
83+
}
84+
```
85+
86+
## Contract Addresses (Anvil Deterministic)
87+
88+
| Contract | Address | Function |
89+
|----------|---------|----------|
90+
| MockGToken | `0x5FbDB2315678afecb367f032d93F642f64180aa3` | ERC20 token on Anvil |
91+
| GravityPortal | `0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512` | Message relay on Anvil |
92+
| GBridgeSender | `0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0` | Bridge entry point on Anvil |
93+
| GBridgeReceiver | `0x595475934ed7d9faa7fca28341c2ce583904a44e` | Genesis-deployed on Gravity |
94+
95+
## File Structure
96+
97+
```
98+
gravity_e2e/
99+
├── gravity_e2e/utils/
100+
│ ├── anvil_manager.py # Anvil process lifecycle + forge deploy
101+
│ └── bridge_utils.py # BridgeHelper, poll_native_minted, BridgeStats
102+
└── cluster_test_cases/bridge/
103+
├── README.md # This file
104+
├── cluster.toml # Single-node cluster config
105+
├── conftest.py # anvil_bridge + bridge_helper fixtures
106+
└── test_bridge.py # 10-min continuous bridge loop test
107+
```
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Gravity Cluster Configuration - Bridge Cross-Epoch E2E Test Suite
2+
# Node deployment configuration only (genesis params in genesis.toml)
3+
4+
[cluster]
5+
name = "gravity-devnet-bridge-cross-epoch"
6+
base_dir = "/tmp/gravity-cluster-bridge-cross-epoch"
7+
8+
9+
[genesis_source]
10+
genesis_path = "./artifacts/genesis.json"
11+
waypoint_path = "./artifacts/waypoint.txt"
12+
13+
# Single genesis node
14+
[[nodes]]
15+
id = "node1"
16+
role = "genesis"
17+
source = { project_path = "../" }
18+
host = "127.0.0.1"
19+
validator_port = 6182
20+
vfn_port = 6192
21+
rpc_port = 8545
22+
metrics_port = 9003
23+
inspection_port = 10002
24+
https_port = 1024
25+
authrpc_port = 8575
26+
reth_p2p_port = 12026
27+
28+
[faucet_init]
29+
num_accounts = 0

0 commit comments

Comments
 (0)