Skip to content

Commit c6f8c21

Browse files
feat: probe Aptos providers by ledger chain id (#4069)
1 parent 52cae8c commit c6f8c21

7 files changed

Lines changed: 435 additions & 58 deletions

File tree

crates/foreign-chain-health-check/src/probe.rs

Lines changed: 86 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::collections::BTreeMap;
55
use std::time::Duration;
66

77
use foreign_chain_inspector::abstract_chain::inspector::Abstract;
8+
use foreign_chain_inspector::aptos::inspector::AptosInspector;
89
use foreign_chain_inspector::arbitrum::inspector::Arbitrum;
910
use foreign_chain_inspector::base::inspector::Base;
1011
use foreign_chain_inspector::bitcoin::inspector::BitcoinInspector;
@@ -16,11 +17,12 @@ use foreign_chain_inspector::starknet::inspector::StarknetInspector;
1617
use foreign_chain_inspector::{
1718
FanOut, ForeignChainInspectionError, NetworkFingerprint, ProviderFailure,
1819
};
20+
use foreign_chain_rpc_interfaces::aptos::ReqwestAptosClient;
1921
use mpc_node_config::{ForeignChainConfig, ForeignChainProviderConfig, ForeignChainsConfig};
2022
use near_mpc_bounded_collections::NonEmptyVec;
2123
use near_mpc_contract_interface::types::{ForeignChain, ProviderId};
2224

23-
use crate::prepare_jsonrpc;
25+
use crate::{prepare_aptos, prepare_jsonrpc};
2426

2527
/// One provider's verdict. Anything other than [`ProviderStatus::Healthy`] is unhealthy.
2628
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -91,6 +93,8 @@ impl ProbeReport {
9193
/// Each provider is tried up to `max_retries` times, `timeout_sec` per try, and only for as long as
9294
/// the failures stay transient. This returns within the largest configured `timeout_sec *
9395
/// max_retries`, plus the [`foreign_chain_inspector::RETRY_BACKOFF`] between tries.
96+
///
97+
/// TODO(#4043): take the inspectors as a dependency instead
9498
pub async fn probe_all_providers(config: &ForeignChainsConfig) -> ProbeReport {
9599
let probe_attempts = config
96100
.iter_chains()
@@ -114,8 +118,20 @@ pub async fn probe_all_providers(config: &ForeignChainsConfig) -> ProbeReport {
114118
})
115119
.await
116120
}
117-
// TODO(#4003): probe Aptos and Sui. Ethereum, Solana and Ton have no inspector, so
118-
// there is nothing to probe them with.
121+
ForeignChain::Aptos => {
122+
let timeout = Duration::from_secs(chain_config.timeout_sec.get());
123+
probe_chain(chain, chain_config, move |provider| {
124+
let (url, auth_header) = prepare_aptos(provider)?;
125+
Ok(AptosInspector::new(ReqwestAptosClient::new(
126+
url,
127+
auth_header,
128+
timeout,
129+
)))
130+
})
131+
.await
132+
}
133+
// TODO(#4003): probe Sui. Ethereum, Solana and Ton have no inspector, so there is
134+
// nothing to probe them with.
119135
_ => rows_of(chain, chain_config, ProviderStatus::ProbeNotImplemented),
120136
}
121137
});
@@ -244,6 +260,11 @@ mod tests {
244260
const CLOSED_PORT_URL: &str = "http://127.0.0.1:9";
245261
/// For a chain with no probe: the value is never read, only whether it is set at all.
246262
const ANY_FINGERPRINT: &str = "any-fingerprint";
263+
/// Aptos providers reports its chain id as a bare JSON number (`uint8`). The configured fingerprint is
264+
/// the same number as text.
265+
const APTOS_MAINNET: u64 = 1;
266+
const APTOS_TESTNET: u64 = 2;
267+
const PROVIDER_NAME: &str = "publicnode";
247268
/// Bitcoin's genesis block hash, which is what tells its networks apart.
248269
const BITCOIN_MAINNET: &str =
249270
"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f";
@@ -885,6 +906,68 @@ mod tests {
885906
);
886907
}
887908

909+
async fn mock_ledger_info<'a>(
910+
server: &'a httpmock::MockServer,
911+
chain_id: u64,
912+
) -> httpmock::Mock<'a> {
913+
let body = serde_json::json!({"chain_id": chain_id, "ledger_version": "1"});
914+
server
915+
.mock_async(|when, then| {
916+
when.method(httpmock::Method::GET);
917+
then.status(200).json_body(body);
918+
})
919+
.await
920+
}
921+
922+
#[tokio::test]
923+
async fn probe_all_providers__should_report_aptos_on_its_expected_chain_id_as_healthy() {
924+
// Given
925+
let server = httpmock::MockServer::start_async().await;
926+
mock_ledger_info(&server, APTOS_MAINNET).await;
927+
let config = ForeignChainsConfig {
928+
aptos: Some(chain_config(
929+
Some(&APTOS_MAINNET.to_string()),
930+
one_provider(PROVIDER_NAME, &server.base_url()),
931+
)),
932+
..Default::default()
933+
};
934+
935+
// When
936+
let report = probe_all_providers(&config).await;
937+
938+
// Then
939+
assert_eq!(
940+
must_status_of(&report, ForeignChain::Aptos, PROVIDER_NAME),
941+
ProviderStatus::Healthy
942+
);
943+
}
944+
945+
#[tokio::test]
946+
async fn probe_all_providers__should_report_aptos_on_another_network_as_wrong_network() {
947+
// Given
948+
let server = httpmock::MockServer::start_async().await;
949+
mock_ledger_info(&server, APTOS_TESTNET).await;
950+
let config = ForeignChainsConfig {
951+
aptos: Some(chain_config(
952+
Some(&APTOS_MAINNET.to_string()),
953+
one_provider(PROVIDER_NAME, &server.base_url()),
954+
)),
955+
..Default::default()
956+
};
957+
958+
// When
959+
let report = probe_all_providers(&config).await;
960+
961+
// Then
962+
assert_eq!(
963+
must_status_of(&report, ForeignChain::Aptos, PROVIDER_NAME),
964+
ProviderStatus::WrongNetwork {
965+
expected: NetworkFingerprint::new(APTOS_MAINNET.to_string()),
966+
observed: NetworkFingerprint::new(APTOS_TESTNET.to_string()),
967+
}
968+
);
969+
}
970+
888971
#[tokio::test]
889972
async fn probe_all_providers__should_retry_a_provider_that_refused_with_a_rate_limit_code() {
890973
// Given

0 commit comments

Comments
 (0)