Skip to content

feat: probe Sui providers by genesis checkpoint digest - #4070

Merged
haiyuechen-nearone merged 12 commits into
mainfrom
4003-probe-sui-genesis-digest
Aug 24, 2026
Merged

feat: probe Sui providers by genesis checkpoint digest#4070
haiyuechen-nearone merged 12 commits into
mainfrom
4003-probe-sui-genesis-digest

Conversation

@haiyuechen-nearone

@haiyuechen-nearone haiyuechen-nearone commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Closes #4093.

Notes for review

  • Nothing is normalized. The Sui genesis checkpoint digest is Base58, which is case sensitive and carries no prefix or padding.

  • NotFound reads off the response type, through the HasAbsenceMeaning traits feat: probe Aptos providers by ledger chain id #4069 added.

  • DeadlineExceeded is mapped to Timeout instead of RpcRequestFailed by this PR. Both variants are transient, so retries and the signing path fan out are unchanged.

  • Separate mock servers in tests. The mock HTTP server used by previous tests cannot serve gRPC requests.

  • Review feedback widened the diff beyond Sui. Every chain's network fingerprint constant is now defined once in its inspector module and referenced from golden values, probe tests, and manual tests.

@haiyuechen-nearone haiyuechen-nearone changed the title feat(probe): probe Sui for its genesis checkpoint digest feat: probe Sui for its genesis checkpoint digest Aug 5, 2026
@haiyuechen-nearone haiyuechen-nearone changed the title feat: probe Sui for its genesis checkpoint digest feat(probe): identify Sui by its genesis checkpoint digest Aug 7, 2026
@haiyuechen-nearone
haiyuechen-nearone force-pushed the 4003-probe-sui-genesis-digest branch from 8141a21 to 052f530 Compare August 7, 2026 14:35
@haiyuechen-nearone
haiyuechen-nearone force-pushed the 4003-probe-sui-genesis-digest branch from 052f530 to fa6f569 Compare August 7, 2026 19:46
@haiyuechen-nearone haiyuechen-nearone changed the title feat(probe): identify Sui by its genesis checkpoint digest feat: probe Sui providers by genesis checkpoint digest Aug 11, 2026
@haiyuechen-nearone
haiyuechen-nearone marked this pull request as ready for review August 11, 2026 11:13
@haiyuechen-nearone
haiyuechen-nearone force-pushed the 4003-probe-sui-genesis-digest branch from 5aa7e47 to 5ae8dd8 Compare August 11, 2026 11:13
@claude

claude Bot commented Aug 11, 2026

Copy link
Copy Markdown

Pull request overview

Wires Sui into the network-fingerprint probe by implementing NetworkFingerprintInspector for SuiInspector, reading the base58 genesis-checkpoint digest out of GetServiceInfo.chain_id. The old classify_status free function is replaced by the ClassifyRpcOutcome blanket impl over Result<T, tonic::Status> introduced in #4069, so NotFound now resolves through the response type's HasAbsenceMeaning (transaction -> TransactionNotFound, service info -> RpcRequestRejected) rather than being unconditionally a missing transaction. DeadlineExceeded is split out to Timeout, matching what the Aptos classifier already does.

The probe path is still library-only (probe_all_providers has no production caller yet, see crates/foreign-chain-health-check/src/lib.rs:43), and Timeout/RpcRequestFailed are indistinguishable to FanOut::extract (both are is_transient()), so the signing path is unaffected - as the PR body claims.

Changes:

  • SuiInspector gains network_fingerprint (identity canonical_fingerprint, since base58 has one spelling) and a ForeignChain::Sui arm in probe_all_providers; the TODO(#4003) is retired.
  • classify_status -> ClassifyRpcOutcome for Result<T, Status>, with HasAbsenceMeaning for GetTransactionResponse / GetServiceInfoResponse, and DeadlineExceeded remapped to Timeout.
  • Test scaffolding: a real tonic gRPC fake (FakeSuiLedger/FakeSuiServer) in the health-check crate, since httpmock cannot serve gRPC; MockSuiClient now arms both RPCs; a live #[ignore]d fingerprint check mirroring the Aptos one.
  • Docs: Sui added to the probe table, prose rewritten around "every chain with an inspector is probed".

Reviewed changes

Per-file summary
File Description
crates/foreign-chain-inspector/src/sui/inspector.rs NetworkFingerprintInspector impl; classify_status replaced by ClassifyRpcOutcome; DeadlineExceeded -> Timeout; unit tests renamed/extended
crates/foreign-chain-health-check/src/probe.rs ForeignChain::Sui probe arm, timeout_of reuse, TODO(#4003) removed, tonic-based fake ledger + 3 probe tests
crates/foreign-chain-inspector/tests/sui_inspector.rs MockSuiClient arms get_service_info; 3 network_fingerprint tests
crates/foreign-chain-inspector/tests/sui_rpc_manual.rs #[ignore]d live fingerprint check against the mainnet archive
crates/foreign-chain-health-check/Cargo.toml, Cargo.lock tonic dev-dependency with router/server for the fake gRPC server
docs/foreign-chain-transactions.md Sui row in the probe table; prose on which chains are probed

Findings

Blocking (must fix before merge):

  • docs/foreign-chain-transactions.md:551 and :722-723 - both new sentences name ton as a chain that "has no inspector, so [it] ignore[s] expected_network_fingerprint" / "report[s] ProbeNotImplemented whether the field is set or not". ton has no slot in the node's config at all (crates/node-config/src/foreign_chains.rs:17-43) and is never yielded by all_configured_chains (:164-182), so it can never be configured, probed, or produce a report row - the ProbeNotImplemented claim is false for it, and it contradicts the unchanged sentence at :713 ("solana and ethereum are configurable but absent from the table"). Same inaccuracy in the code comment at crates/foreign-chain-health-check/src/probe.rs:140. Either drop ton from all three spots, or state that it is a contract-side chain with no node config.

Non-blocking (nits, follow-ups, suggestions):

  • crates/foreign-chain-health-check/src/probe.rs:141 - with Sui landed, the only variants reaching _ => ProbeNotImplemented are Solana and Ethereum, yet the docs now promise "every chain with an inspector is probed". The wildcard silently breaks that promise for the next chain added to ForeignChain. Spelling the arm out (ForeignChain::Solana | ForeignChain::Ethereum | ForeignChain::Ton =>) turns that into a compile error instead of an unprobed row on the dashboard.
  • crates/foreign-chain-inspector/src/sui/inspector.rs:137 - the impl is keyed on tonic::Status, not on anything Sui-specific, but lives in the Sui module. A future gRPC-based chain silently inherits Sui's mapping and, by coherence, cannot override it. Consider moving it beside the trait in lib.rs so its reach is visible at the definition site.
  • crates/foreign-chain-inspector/src/sui/inspector.rs:148-151 - worth confirming (I could not check tonic's source from this checkout): tonic derives a Status from transport-level std::io::Errors, and if that conversion maps ErrorKind::NotFound to Code::NotFound, a purely local transport failure would be classified as TransactionNotFound - a non-transient chain verdict in FanOut::extract, not an availability blip. Pre-existing rather than introduced here, but this PR is the one refining exactly this axis, so it seems the right place to confirm the assumption that Code::NotFound only ever comes from the server.
  • crates/foreign-chain-inspector/tests/sui_inspector.rs:30 and :43 - the un-armed RPC now answers Status::unimplemented(...), which the code under test classifies as RpcRequestRejected. A test that reaches the wrong RPC therefore fails (or passes) on a plausible-looking assertion instead of panicking loudly; get_checkpoint at :59 still uses unimplemented!(), so the mock now mixes both conventions. Option<Result<...>> plus .expect("test did not arm get_service_info") would keep the loud failure.
  • crates/foreign-chain-inspector/tests/sui_inspector.rs:20 - "answering the one call a test arms, and refusing the other" does not describe status(), which arms both with the same Status.

⚠️ Issues found

@haiyuechen-nearone
haiyuechen-nearone force-pushed the 4003-probe-sui-genesis-digest branch from f2f2d16 to 90a2d58 Compare August 11, 2026 16:23
@haiyuechen-nearone haiyuechen-nearone self-assigned this Aug 11, 2026
@haiyuechen-nearone

Copy link
Copy Markdown
Contributor Author

@claude review

@claude

claude Bot commented Aug 11, 2026

Copy link
Copy Markdown

Pull request overview

Third step of the #4003 probe stack: SuiInspector gains NetworkFingerprintInspector, reading the base58 genesis-checkpoint digest out of GetServiceInfo.chain_id, and probe_all_providers gains a ForeignChain::Sui arm. The old classify_status free function is replaced by the ClassifyRpcOutcome blanket impl over Result<T, tonic::Status> introduced in #4069, so NotFound now resolves through the response type's HasAbsenceMeaning (transaction → TransactionNotFound, service info → RpcRequestRejected) rather than being unconditionally a missing transaction. DeadlineExceeded is split out to Timeout.

The blocking documentation inaccuracy from the previous round is fixed: the prose and the code comment now name only solana and ethereum, which matches both all_configured_chains (crates/node-config/src/foreign_chains.rs:164-182) and the set of inspector modules that actually exist. MockSuiClient now panics on an unarmed RPC instead of answering a plausible-looking Status::unimplemented, and its doc comment describes what it does.

probe_all_providers still has no production caller (crates/foreign-chain-health-check/src/lib.rs:43), and Timeout/RpcRequestFailed are indistinguishable to FanOut::extract (both is_transient()), so the signing path is unaffected — as the PR body claims.

Changes:

  • NetworkFingerprintInspector for SuiInspector, with an identity canonical_fingerprint (base58 has one spelling), plus the ForeignChain::Sui arm in probe_all_providers; TODO(#4003) retired.
  • classify_statusimpl<T: HasAbsenceMeaning> ClassifyRpcOutcome for Result<T, Status>, with HasAbsenceMeaning for GetTransactionResponse / GetServiceInfoResponse, and DeadlineExceeded remapped to Timeout.
  • Test scaffolding: a real tonic gRPC fake (FakeSuiLedger/FakeSuiServer) in the health-check crate, since httpmock cannot serve gRPC; six probe_all_providers verdict tests; MockSuiClient arms each RPC independently; a live #[ignore]d fingerprint check mirroring the Aptos one.
  • Docs: Sui row in the probe table, prose rewritten around "every chain with an inspector is probed".

Reviewed changes

Per-file summary
File Description
crates/foreign-chain-inspector/src/sui/inspector.rs NetworkFingerprintInspector impl; classify_statusClassifyRpcOutcome; HasAbsenceMeaning for the two response types; DeadlineExceededTimeout; unit tests renamed/extended
crates/foreign-chain-health-check/src/probe.rs ForeignChain::Sui probe arm, timeout_of reuse, TODO(#4003) removed, tonic-based fake ledger + 6 probe tests
crates/foreign-chain-inspector/tests/sui_inspector.rs MockSuiClient arms each RPC separately and panics when a test reaches an unarmed one; 3 network_fingerprint tests
crates/foreign-chain-inspector/tests/sui_rpc_manual.rs #[ignore]d live fingerprint check against the mainnet archive
crates/foreign-chain-health-check/Cargo.toml, Cargo.lock tonic dev-dependency with router/server for the fake gRPC server
docs/foreign-chain-transactions.md Sui row in the probe table; prose on which chains are probed

Findings

Non-blocking (nits, follow-ups, suggestions):

  • crates/foreign-chain-inspector/src/sui/inspector.rs:157 / crates/foreign-chain-health-check/src/probe.rs:142 — the case this arm exists to serve (a provider that stalls) is decided by a race between three deadlines set to the same value. prepare_sui(provider, timeout) gives the client a per-request gRPC deadline (request.set_timeout, crates/foreign-chain-rpc-interfaces/src/sui.rs:95-99) equal to timeout_of(chain_config), which is the same duration FanOut::network_fingerprints passes to its own tokio::time::timeout (crates/foreign-chain-inspector/src/lib.rs:253). So a stall can surface as: the server honouring grpc-timeoutDEADLINE_EXCEEDEDTimedOut; tonic's own client-side timeout → TimedOut only if tonic reports it as DEADLINE_EXCEEDED; or the probe's tokio::time::timeoutTimeoutTimedOut. I could not check tonic's source from this checkout, but its GrpcTimeout layer has historically produced Status::cancelled("Timeout expired"), which lands in the RpcRequestFailed arm → ProviderStatus::Unreachable — exactly the verdict the change sets out to remove, and what the comment on :156 promises it will not be. Worth confirming; if it is Cancelled, the cheapest fix is to let the probe own the deadline (pass a generous or no per-request timeout to prepare_sui from the probe arm) rather than to also map Cancelled, which legitimately means "caller cancelled" in other contexts.
    • Related coverage gap: probe_all_providers__should_report_a_slow_sui_provider_as_timed_out pins the mapping — the fake answers DEADLINE_EXCEEDED immediately — not the scenario the name describes, and the stalled-call test was dropped in 63c9fc6. Starknet still has the end-to-end version (probe_all_providers__should_report_a_provider_that_does_not_answer_in_time, probe.rs:599); Sui now has nothing equivalent.
  • crates/foreign-chain-health-check/src/probe.rs:995FakeSuiLedger implements only get_service_info, so the remaining LedgerService RPCs necessarily fall through to tonic's generated default stubs and answer Status::unimplemented. Through classified() that is RpcRequestRejectedProviderStatus::RequestRejected: a plausible-looking verdict, which is the same soft failure f3ae667 just removed from MockSuiClient. If the probe ever calls a second RPC, these tests assert a wrong status instead of failing loudly. Overriding the RPCs the fake should never receive with unreachable!() keeps the two fakes consistent.
  • Unchanged from the previous round, restated only so they are not lost: the _ => wildcard at probe.rs:147 (now that the docs promise "every chain with an inspector is probed", spelling out ForeignChain::Solana | ForeignChain::Ethereum turns the next chain added to ForeignChain into a compile error rather than a silently unprobed dashboard row); the ClassifyRpcOutcome for Result<T, Status> impl living in the Sui module while being keyed only on tonic::Status; and confirming that Code::NotFound can only originate server-side, never from a local transport error.

✅ Approved

@haiyuechen-nearone
haiyuechen-nearone force-pushed the 4003-probe-sui-genesis-digest branch from f3ae667 to 39b7ec2 Compare August 13, 2026 19:45
@haiyuechen-nearone
haiyuechen-nearone force-pushed the 4003-probe-sui-genesis-digest branch from 3a4ecd9 to 888d013 Compare August 14, 2026 07:23
@haiyuechen-nearone
haiyuechen-nearone force-pushed the 4003-probe-sui-genesis-digest branch from 888d013 to 1258f4e Compare August 17, 2026 12:47
Base automatically changed from 4003-probe-aptos-chain-id to main August 20, 2026 11:18
`GetServiceInfo` reports the digest as base58, which is the form it is
published and configured in, so nothing is normalized. Completes the probe for
every chain that has an inspector.
Serve a fake `LedgerService` over gRPC so the probe tests cover Sui end to
end: on its genesis digest, on another network, unreachable, and stalled. The
mock HTTP server the other chains use cannot answer a gRPC call.

Read the Sui `NotFound` meaning off the response type through
`ClassifyRpcOutcome`, as Aptos already does, rather than overriding it at the
`network_fingerprint` call site, so a further call site cannot silently
inherit `TransactionNotFound`. Derive the attempt deadline once through
`timeout_of`, and move the client deadline test beside `prepare_sui`.
Every caller bounds the call itself: `network_fingerprints` arms a deadline
before tonic stamps the client's, and the signing flow wraps `extract` in
`FOREIGN_CHAIN_INSPECTION_TIMEOUT`. The client's `DeadlineExceeded` never
surfaces, so the test pinned a tonic detail no caller observes.

A provider that answers `DEADLINE_EXCEEDED` itself does reach the mapping, and
`classified__should_name_a_deadline_as_a_timeout` covers that without a server.
The fake ledger loses the delay it only needed in order to stall.
The fake ledger now serves whatever answer a test arms, so one server reaches
every verdict: on chain, on another network, service info without a chain id,
a refused service, and a deadline. Each case enters through
`probe_all_providers`, as the other chains' cases do, which also pins the
dispatch arm rather than leaving that to the closed port case alone.
A mock that answered every RPC with a plausible status let a test that
called the wrong one fail on its assertion instead of on the wrong call.
Also drops Ton from the chains said to have no inspector: it has no
config section, so it never reaches the probe.
Move the NotFound rationale down to the arm it explains, and drop the comments that restate the code or the fixture they sit on.
@haiyuechen-nearone
haiyuechen-nearone force-pushed the 4003-probe-sui-genesis-digest branch from 1258f4e to 1f40b8c Compare August 21, 2026 08:27
@haiyuechen-nearone
haiyuechen-nearone requested review from anodar, gilcu3 and pbeza and removed request for anodar August 21, 2026 12:07
@gilcu3

gilcu3 commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

@claude review

@claude

claude Bot commented Aug 24, 2026

Copy link
Copy Markdown

Pull request overview

Third step of the #4003 probe stack, and the one that completes it: SuiInspector gains NetworkFingerprintInspector, reading the base58 genesis-checkpoint digest out of GetServiceInfo.chain_id, and probe_all_providers gains a ForeignChain::Sui arm. The old classify_status free function becomes the ClassifyRpcOutcome blanket impl over Result<T, tonic::Status> introduced in #4069, so NotFound now resolves through the response type's HasAbsenceMeaning (transaction → TransactionNotFound, service info → RpcRequestRejected) instead of being unconditionally a missing transaction, and DeadlineExceeded splits out to Timeout.

I re-checked the claims in the PR body against the tree. GetServiceInfoResponse had no prior inspector call site, so the NotFound split changes no existing behaviour; Timeout and RpcRequestFailed are both is_transient(), so FanOut::extract cannot tell them apart and the signing path is unchanged — only provider_failure(), which nothing but the probe reads, distinguishes them. probe_all_providers still has no production caller. The docs fix from the earlier round holds up: solana and ethereum are the only chains left without an inspector that can reach the probe, matching all_configured_chains (crates/node-config/src/foreign_chains.rs:168-188), which has no ton slot at all.

Changes:

  • NetworkFingerprintInspector for SuiInspector with an identity canonical_fingerprint (base58 has one spelling), plus the ForeignChain::Sui dispatch arm; TODO(#4003) retired and timeout_of reused for the Aptos arm and probe_chain.
  • classify_statusimpl<T: HasAbsenceMeaning> ClassifyRpcOutcome for Result<T, Status>, with HasAbsenceMeaning for GetTransactionResponse / GetServiceInfoResponse, and DeadlineExceededTimeout.
  • Test scaffolding: a real tonic gRPC fake (FakeSuiLedger / FakeSuiServer), since httpmock cannot serve gRPC; six probe_all_providers verdict tests; MockSuiClient arms each RPC independently and panics on an unarmed one; an #[ignore]d live fingerprint check mirroring the Aptos one.
  • Docs: Sui row in the probe table, prose rewritten around "every chain with an inspector is probed".

Reviewed changes

Per-file summary
File Description
crates/foreign-chain-inspector/src/sui/inspector.rs NetworkFingerprintInspector impl; classify_statusClassifyRpcOutcome; HasAbsenceMeaning for the two response types; DeadlineExceededTimeout; unit tests renamed/extended
crates/foreign-chain-health-check/src/probe.rs ForeignChain::Sui probe arm, timeout_of reuse, TODO(#4003) removed, tonic-based fake ledger + 6 probe tests
crates/foreign-chain-inspector/tests/sui_inspector.rs MockSuiClient arms each RPC separately; 3 network_fingerprint tests
crates/foreign-chain-inspector/tests/sui_rpc_manual.rs #[ignore]d live fingerprint check against the mainnet archive, mirroring aptos_rpc_manual.rs:64-84
crates/foreign-chain-health-check/Cargo.toml, Cargo.lock tonic dev-dependency with router/server; the workspace is on resolver = "3", so those features do not unify into the node's normal build
docs/foreign-chain-transactions.md Sui row in the probe table; prose on which chains are probed

Findings

Non-blocking (nits, follow-ups, suggestions):

  • crates/foreign-chain-inspector/src/sui/inspector.rs:124-161 — the rationale went out with classify_status and was not re-homed. The Aptos twin, which this impl is otherwise a mirror of, keeps all three of its why comments (aptos/inspector.rs:108 /// Every Aptos node serves the ledger info at its REST API base., :124 on the 404 arm, :136 // Retrying cannot change a deterministic 4xx.); the Sui impl now has none. What a reader cannot reconstruct from the code is why Cancelled / Aborted / Unknown are transient while Unimplemented / InvalidArgument / PermissionDenied are substantive refusals that must not be retried past — precisely the distinction the fan-out acts on. 1f40b8cb says the NotFound rationale moved "down to the arm it explains", but d9b06867 removed it again, so nothing survives. One line above the Code::NotFound arm and one above Code::Unavailable | … would restore it without reintroducing paraphrase.
  • crates/foreign-chain-health-check/src/probe.rs:1031-1042 (sui_answering) — the .expect("the fake Sui ledger should keep serving …") runs inside a detached tokio::spawn, and the JoinHandle is only ever abort()ed in Drop, never awaited. If serve_with_incoming returns an error, the panic is captured in the handle and dropped: the test then fails on Unreachable vs. the expected verdict instead of on the message that explains why. Diagnostics only, but the message currently cannot reach anyone.
  • Carried over from the previous rounds and still open, listed so they are not lost rather than re-argued: the _ => wildcard at probe.rs:145 (spelling out ForeignChain::Solana | ForeignChain::Ethereum | ForeignChain::Ton would make the docs' "every chain with an inspector is probed" a compile-time guarantee); ClassifyRpcOutcome for Result<T, Status> living in the Sui module while keyed only on tonic::Status, so a future gRPC chain inherits it and cannot override it; FakeSuiLedger leaving the other LedgerService RPCs on tonic's default Unimplemented stubs, which classified() turns into a plausible-looking RequestRejected; and probe_all_providers__should_report_a_slow_sui_provider_as_timed_out (probe.rs:1128) naming a scenario it does not exercise — the fake answers DEADLINE_EXCEEDED immediately, so the test pins the mapping, not slowness. On that last one, c47f3003's reasoning checks out for the probe path: network_fingerprints arms its tokio::time::timeout (foreign-chain-inspector/src/lib.rs:255) before tonic stamps the client deadline in request_with_timeout (foreign-chain-rpc-interfaces/src/sui.rs:95-99), both from the same timeout_sec, so the outer one expires first; and in the signing path either deadline yields a transient error, so the verdict is the same. A rename would still make the test say what it pins.

✅ Approved

gilcu3
gilcu3 previously approved these changes Aug 24, 2026

@gilcu3 gilcu3 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

Comment thread crates/foreign-chain-health-check/Cargo.toml Outdated
Comment thread crates/foreign-chain-inspector/tests/sui_inspector.rs Outdated
Comment thread crates/foreign-chain-health-check/Cargo.toml Outdated
Comment thread crates/foreign-chain-inspector/src/sui/inspector.rs
Comment thread crates/foreign-chain-inspector/src/sui/inspector.rs
pbeza
pbeza previously approved these changes Aug 24, 2026
Comment thread crates/foreign-chain-health-check/src/probe.rs
Comment thread crates/foreign-chain-health-check/src/probe.rs Outdated
Comment thread crates/foreign-chain-health-check/src/probe.rs Outdated
Comment on lines +1048 to +1162
#[tokio::test]
async fn probe_all_providers__should_report_sui_on_its_genesis_digest_as_healthy() {
// Given
let server = sui_on_chain(SUI_MAINNET).await;
let config = sui_only(chain_config(
Some(SUI_MAINNET),
one_provider(PROVIDER_NAME, &server.url),
));

// When
let report = probe_all_providers(&config).await;

// Then
assert_eq!(
must_status_of(&report, ForeignChain::Sui, PROVIDER_NAME),
ProviderStatus::Healthy
);
}

#[tokio::test]
async fn probe_all_providers__should_report_sui_on_another_network_as_wrong_network() {
// Given
let server = sui_on_chain(SUI_TESTNET).await;
let config = sui_only(chain_config(
Some(SUI_MAINNET),
one_provider(PROVIDER_NAME, &server.url),
));

// When
let report = probe_all_providers(&config).await;

// Then
assert_eq!(
must_status_of(&report, ForeignChain::Sui, PROVIDER_NAME),
ProviderStatus::WrongNetwork {
expected: NetworkFingerprint::new(SUI_MAINNET),
observed: NetworkFingerprint::new(SUI_TESTNET),
}
);
}

#[tokio::test]
async fn probe_all_providers__should_report_sui_service_info_without_a_chain_id_as_malformed() {
// Given
let server = sui_answering(Ok(GetServiceInfoResponse::default())).await;
let config = sui_only(chain_config(
Some(SUI_MAINNET),
one_provider(PROVIDER_NAME, &server.url),
));

// When
let report = probe_all_providers(&config).await;

// Then
assert_eq!(
must_status_of(&report, ForeignChain::Sui, PROVIDER_NAME),
ProviderStatus::MalformedResponse
);
}

#[tokio::test]
async fn probe_all_providers__should_report_a_sui_provider_not_serving_the_api_as_rejected() {
// Given
let server = sui_answering(Err(Status::not_found("no such service"))).await;
let config = sui_only(chain_config(
Some(SUI_MAINNET),
one_provider(PROVIDER_NAME, &server.url),
));

// When
let report = probe_all_providers(&config).await;

// Then
assert_eq!(
must_status_of(&report, ForeignChain::Sui, PROVIDER_NAME),
ProviderStatus::RequestRejected
);
}

#[tokio::test]
async fn probe_all_providers__should_report_a_slow_sui_provider_as_timed_out() {
// Given
let server = sui_answering(Err(Status::deadline_exceeded("too slow"))).await;
let config = sui_only(chain_config(
Some(SUI_MAINNET),
one_provider(PROVIDER_NAME, &server.url),
));

// When
let report = probe_all_providers(&config).await;

// Then
assert_eq!(
must_status_of(&report, ForeignChain::Sui, PROVIDER_NAME),
ProviderStatus::TimedOut
);
}

#[tokio::test]
async fn probe_all_providers__should_report_an_unreachable_sui_provider() {
// Given
let config = sui_only(chain_config(
Some(SUI_MAINNET),
one_provider(PROVIDER_NAME, CLOSED_PORT_URL),
));

// When
let report = probe_all_providers(&config).await;

// Then
assert_eq!(
must_status_of(&report, ForeignChain::Sui, PROVIDER_NAME),
ProviderStatus::Unreachable
);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of these tests look very similar. Perhaps there’s an idiomatic way to shorten this with rstest? (Maybe not, which is fine too.)

…dict tests

Define each chain's fingerprint constant once in its inspector module,
collapse the five Sui verdict probes into one rstest table, declare the
tonic codegen feature we relied on through unification, and clarify the
Sui fingerprint normalization comment.
@haiyuechen-nearone
haiyuechen-nearone dismissed stale reviews from pbeza and gilcu3 via e0330c1 August 24, 2026 17:38

@gilcu3 gilcu3 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, that was a big dedup!

@pbeza pbeza left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love the dedup!

@haiyuechen-nearone
haiyuechen-nearone added this pull request to the merge queue Aug 24, 2026
Merged via the queue into main with commit da519e7 Aug 24, 2026
21 checks passed
@haiyuechen-nearone
haiyuechen-nearone deleted the 4003-probe-sui-genesis-digest branch August 24, 2026 20:46
haiyuechen-nearone added a commit that referenced this pull request Aug 25, 2026
The branch was rebased onto latest main, dropping the commits that landed
separately as #4070. This merge keeps the rebased tree and records the
superseded history so the branch fast-forwards on the remote.
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.

Probe Sui for its genesis checkpoint digest

3 participants