fix(api): bridge mint scan reads its layout from the first height that exists
ci / gate (push) Successful in 4m48s
deploy / validate (push) Skipped
deploy / build (push) Successful in 8m58s
deploy / deploy-web (push) Successful in 11s
deploy / deploy-api (push) Successful in 22s

The backward pass sat on #218,897,633 — a height NEAR never produced — and
asked gargravarr for its shard layout on every turn: UNKNOWN_BLOCK, a quiet
sleep, the same question again. #31 fixed exactly this in the intents scan;
bridgescan was written without it. The layout now comes from the first
height in the batch that exists, any other failure is logged, and the
history job walks down past a skipped height rather than retrying it.

Refs #44

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015tyzzRVjH9AdwGtYwsH5VC
This commit is contained in:
2026-10-07 10:24:11 +03:00
co-authored by Claude Opus 5.5
parent 5f30000539
commit e183fdafde
+33 -3
View File
@@ -183,7 +183,28 @@ async fn mints(
tokio::time::sleep(TIP_WAIT).await;
continue;
};
let Ok(layout) = near.shard_layout(heights[0]).await else {
// From the first height in the batch that exists. NEAR skips heights,
// and asking at a skipped one is UNKNOWN_BLOCK forever: the backward
// pass sat on #218,897,633 that way, retrying the same never-produced
// block every turn without a word (blackbeard/observer#31, again).
let mut layout = None;
for &height in &heights {
match near.shard_layout(height).await {
Ok(l) => {
layout = Some(l);
break;
}
Err(e) if blackbeard_data::near::unknown(&e) => continue,
Err(e) => {
tracing::warn!(chain = %id, near_height = height, error = %e,
"bridge mint shard layout unreadable");
break;
}
}
}
let Some(layout) = layout else {
tracing::warn!(chain = %id, from = heights[0], to = heights[heights.len() - 1],
"no height in the bridge mint batch is readable; will retry");
tokio::time::sleep(RETRY).await;
continue;
};
@@ -357,11 +378,20 @@ async fn history(
tracing::info!(chain = %id, changes = changes.len(), "supply changes found before our nodes");
for (height, delta) in changes {
// A change can be recorded at a skipped height (the supply read falls
// back to the block before), so walk down past any that were never
// produced rather than asking about the same one forever.
let mut at = height;
let layout = loop {
tokio::time::sleep(ARCHIVE_SPACING).await;
match archive.shard_layout(height).await {
match archive.shard_layout(at).await {
Ok(l) => break l,
Err(_) => tokio::time::sleep(RETRY).await,
Err(e) if blackbeard_data::near::unknown(&e) && at + LOOKBACK > height => at -= 1,
Err(e) => {
tracing::debug!(chain = %id, near_height = at, error = %e,
"archive layout unreadable; retrying");
tokio::time::sleep(RETRY).await
}
}
};
let shard_of = |a: &str| core::shard_for(a, &layout.boundary_accounts, &layout.shard_ids);