Skip to content

Commit dda46bb

Browse files
committed
refactor: improved code quality
1 parent a020121 commit dda46bb

4 files changed

Lines changed: 95 additions & 46 deletions

File tree

crates/foreign-chain-config-tester/src/checks.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use foreign_chain_inspector::{
2222
},
2323
sui::{
2424
SuiTransactionDigest,
25-
inspector::{SuiFinality, SuiInspector},
25+
inspector::{SuiExtractor, SuiFinality, SuiInspector},
2626
},
2727
};
2828
use foreign_chain_rpc_interfaces::aptos::ReqwestAptosClient;
@@ -151,16 +151,21 @@ pub async fn check_sui(client: impl SuiRpcClient, expected_chain_id: &str) -> an
151151
let tx = golden::base58_32(digest)?;
152152

153153
let inspector = SuiInspector::new(client);
154+
// Probe the first event so the extraction pipeline (BCS pass-through, address parsing,
155+
// type-tag normalization, contents-name cross-check) is exercised whenever the probe
156+
// transaction emits events. A transaction with no events (`LogIndexOutOfBounds`) or a
157+
// failed one still proves the provider serves canonical checkpointed data.
154158
match inspector
155159
.extract(
156160
SuiTransactionDigest::from(tx),
157161
SuiFinality::Checkpointed,
158-
vec![],
162+
vec![SuiExtractor::Event { event_index: 0 }],
159163
)
160164
.await
161165
{
162-
// A failed transaction still proves the provider serves canonical checkpointed data.
163-
Ok(_) | Err(ForeignChainInspectionError::TransactionFailed) => Ok(()),
166+
Ok(_)
167+
| Err(ForeignChainInspectionError::TransactionFailed)
168+
| Err(ForeignChainInspectionError::LogIndexOutOfBounds) => Ok(()),
164169
Err(e) => Err(e).context("failed to inspect a transaction from the latest checkpoint"),
165170
}
166171
}

crates/foreign-chain-inspector/src/sui/inspector.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ impl SuiExtractor {
222222
.value
223223
.as_ref()
224224
.map(|value| value.to_vec())
225-
.ok_or_else(|| malformed_event_field("bcs contents"))?;
225+
.ok_or_else(|| malformed_event_field("bcs contents value"))?;
226226

227227
Ok(SuiExtractedValue::Event(SuiEvent {
228228
package_id,

crates/foreign-chain-rpc-interfaces/src/sui.rs

Lines changed: 66 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,52 @@ use std::time::Duration;
44
use http::{HeaderName, HeaderValue};
55
use sui_rpc::field::{FieldMask, FieldMaskUtil as _};
66
use sui_rpc::proto::sui::rpc::v2::{
7-
GetCheckpointRequest, GetCheckpointResponse, GetServiceInfoRequest, GetServiceInfoResponse,
8-
GetTransactionRequest, GetTransactionResponse, get_checkpoint_request::CheckpointId,
7+
Checkpoint, ExecutedTransaction, GetCheckpointRequest, GetCheckpointResponse,
8+
GetServiceInfoRequest, GetServiceInfoResponse, GetTransactionRequest, GetTransactionResponse,
9+
get_checkpoint_request::CheckpointId,
910
};
1011

1112
pub use sui_rpc::proto::sui::rpc::v2 as proto;
1213
pub use tonic::{Code, Status};
1314

14-
/// The exact fields the inspector verifies.
15-
const TRANSACTION_READ_MASK: &[&str] = &[
16-
"digest",
17-
"checkpoint",
18-
"effects.status",
19-
"events.events.package_id",
20-
"events.events.module",
21-
"events.events.sender",
22-
"events.events.event_type",
23-
"events.events.contents",
24-
];
25-
26-
/// Sections needed to pick a probe transaction from a checkpoint.
27-
const CHECKPOINT_READ_MASK: &[&str] = &["sequence_number", "transactions.digest"];
15+
fn transaction_read_mask() -> FieldMask {
16+
FieldMask::from_paths([
17+
ExecutedTransaction::path_builder().digest(),
18+
ExecutedTransaction::path_builder().checkpoint(),
19+
ExecutedTransaction::path_builder()
20+
.effects()
21+
.status()
22+
.finish(),
23+
ExecutedTransaction::path_builder()
24+
.events()
25+
.events()
26+
.package_id(),
27+
ExecutedTransaction::path_builder()
28+
.events()
29+
.events()
30+
.module(),
31+
ExecutedTransaction::path_builder()
32+
.events()
33+
.events()
34+
.sender(),
35+
ExecutedTransaction::path_builder()
36+
.events()
37+
.events()
38+
.event_type(),
39+
ExecutedTransaction::path_builder()
40+
.events()
41+
.events()
42+
.contents()
43+
.finish(),
44+
])
45+
}
46+
47+
fn checkpoint_read_mask() -> FieldMask {
48+
FieldMask::from_paths([
49+
Checkpoint::path_builder().sequence_number(),
50+
Checkpoint::path_builder().transactions().digest(),
51+
])
52+
}
2853

2954
/// Client interface for the Sui gRPC API (`sui.rpc.v2.LedgerService`).
3055
pub trait SuiRpcClient: Send + Sync {
@@ -87,7 +112,7 @@ impl SuiRpcClient for GrpcSuiClient {
87112
let request = self.request_with_timeout(
88113
GetTransactionRequest::default()
89114
.with_digest(digest)
90-
.with_read_mask(FieldMask::from_paths(TRANSACTION_READ_MASK.iter().copied())),
115+
.with_read_mask(transaction_read_mask()),
91116
);
92117
async move {
93118
let response = client.ledger_client().get_transaction(request).await?;
@@ -111,8 +136,7 @@ impl SuiRpcClient for GrpcSuiClient {
111136
sequence_number: u64,
112137
) -> impl Future<Output = Result<GetCheckpointResponse, Status>> + Send {
113138
let mut client = self.client.clone();
114-
let mut message = GetCheckpointRequest::default()
115-
.with_read_mask(FieldMask::from_paths(CHECKPOINT_READ_MASK.iter().copied()));
139+
let mut message = GetCheckpointRequest::default().with_read_mask(checkpoint_read_mask());
116140
message.checkpoint_id = Some(CheckpointId::SequenceNumber(sequence_number));
117141
let request = self.request_with_timeout(message);
118142
async move {
@@ -166,13 +190,30 @@ mod tests {
166190

167191
#[test]
168192
fn transaction_read_mask__should_request_only_verified_sections() {
169-
// Given / When
170-
let request = GetTransactionRequest::default()
171-
.with_digest("digest")
172-
.with_read_mask(FieldMask::from_paths(TRANSACTION_READ_MASK.iter().copied()));
193+
// Then — pins that the typed path builders produce exactly the wire paths the server
194+
// honors (the live manual test proves those paths return the expected fields), and that
195+
// the events' `json` rendering is not among them.
196+
assert_eq!(
197+
transaction_read_mask().paths,
198+
vec![
199+
"digest",
200+
"checkpoint",
201+
"effects.status",
202+
"events.events.package_id",
203+
"events.events.module",
204+
"events.events.sender",
205+
"events.events.event_type",
206+
"events.events.contents",
207+
]
208+
);
209+
}
173210

211+
#[test]
212+
fn checkpoint_read_mask__should_request_only_probe_sections() {
174213
// Then
175-
assert_eq!(request.digest.as_deref(), Some("digest"));
176-
assert_eq!(request.read_mask.unwrap().paths, TRANSACTION_READ_MASK);
214+
assert_eq!(
215+
checkpoint_read_mask().paths,
216+
vec!["sequence_number", "transactions.digest"]
217+
);
177218
}
178219
}

crates/node-config/src/foreign_chains.rs

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,18 @@ impl ForeignChainProviderConfig {
7777
self.auth.strip_placeholder(&self.rpc_url)
7878
}
7979

80-
fn validate_auth_config(&self) -> anyhow::Result<()> {
81-
auth::validate_auth_config(&self.auth, &self.rpc_url)
80+
fn validate_auth_config(&self, chain: dtos::ForeignChain) -> anyhow::Result<()> {
81+
auth::validate_auth_config(&self.auth, &self.rpc_url)?;
82+
83+
// Sui is reached over gRPC, which carries credentials in request metadata; a token
84+
// substituted into the URL path or query would never be sent.
85+
if chain == dtos::ForeignChain::Sui {
86+
anyhow::ensure!(
87+
matches!(self.auth, AuthConfig::None | AuthConfig::Header { .. }),
88+
"path or query auth is not supported: gRPC providers support only header auth",
89+
);
90+
}
91+
Ok(())
8292
}
8393
}
8494

@@ -134,18 +144,10 @@ impl ForeignChainsConfig {
134144
rpc_url
135145
);
136146

137-
// valid auth configuration
138-
provider.validate_auth_config()?;
139-
140-
// Sui providers are reached over gRPC, which carries credentials in request
141-
// metadata; a token substituted into the URL path or query would never be sent.
142-
if identifier == dtos::ForeignChain::Sui {
143-
anyhow::ensure!(
144-
matches!(provider.auth, AuthConfig::None | AuthConfig::Header { .. }),
145-
"sui provider `{}` uses path or query auth, but gRPC providers support only header auth",
146-
provider_name.as_str(),
147-
);
148-
}
147+
// valid auth configuration for the chain's transport
148+
provider.validate_auth_config(identifier).with_context(|| {
149+
format!("provider `{}` has invalid auth", provider_name.as_str())
150+
})?;
149151
}
150152
}
151153

@@ -938,8 +940,9 @@ foreign_chains:
938940
serde_yaml::from_str(yaml).expect("yaml fixture should be correct");
939941
let result = config.validate();
940942

941-
// Then
942-
let error = result.unwrap_err().to_string();
943+
// Then — the full error chain names the offending provider and the transport rule.
944+
let error = format!("{:#}", result.unwrap_err());
945+
assert!(error.contains("provider `keyinpath`"), "{error}");
943946
assert!(error.contains("support only header auth"), "{error}");
944947
}
945948
}

0 commit comments

Comments
 (0)