feat(indexer): add BlockIngestError enum, fix linting

This commit is contained in:
erhant 2026-06-26 14:00:44 +03:00
parent f0dec9dd72
commit 16b0011abd
6 changed files with 69 additions and 9 deletions

1
Cargo.lock generated
View File

@ -3865,6 +3865,7 @@ dependencies = [
"storage",
"tempfile",
"testnet_initial_state",
"thiserror 2.0.18",
"tokio",
"url",
]

View File

@ -178,7 +178,7 @@ mod tests {
}
.into_pending_block(&key);
let mut tampered = block.clone();
let mut tampered = block;
tampered.header.timestamp = 99; // header changed; stale hash no longer matches
assert_ne!(tampered.recompute_hash(), tampered.header.hash);
}

View File

@ -25,6 +25,7 @@ futures.workspace = true
url.workspace = true
logos-blockchain-core.workspace = true
serde_json.workspace = true
thiserror.workspace = true
async-stream.workspace = true
tokio.workspace = true

View File

@ -168,13 +168,11 @@ impl IndexerStore {
)
.context("Failed to execute genesis public transaction")?;
} else {
transaction
.clone()
.execute_on_state(
&mut state_guard,
block.header.block_id,
block.header.timestamp,
)?;
transaction.clone().execute_on_state(
&mut state_guard,
block.header.block_id,
block.header.timestamp,
)?;
}
}

View File

@ -0,0 +1,59 @@
use common::HashType;
use serde::{Deserialize, Serialize};
/// Why the indexer could not apply an L2 block from the channel. Stored inside a
/// [`crate::chain_breaker::ChainBreaker`] and surfaced on the status snapshot.
#[derive(Debug, Clone, Serialize, Deserialize, thiserror::Error)]
#[serde(rename_all = "camelCase")]
pub enum BlockIngestError {
#[error("failed to deserialize L2 block: {0}")]
Deserialize(String),
#[error("unexpected block id: expected {expected}, got {got}")]
UnexpectedBlockId { expected: u64, got: u64 },
#[error("broken chain link: expected prev {expected_prev}, got {got_prev}")]
BrokenChainLink {
expected_prev: HashType,
got_prev: HashType,
},
#[error("block hash mismatch: computed {computed}, header {header}")]
HashMismatch {
computed: HashType,
header: HashType,
},
#[error("state transition failed: {0}")]
StateTransition(String),
#[error("storage error: {0}")]
Storage(String),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serializes_and_round_trips_externally_tagged() {
let err = BlockIngestError::UnexpectedBlockId {
expected: 5,
got: 7,
};
let value = serde_json::to_value(&err).expect("serialize");
assert_eq!(
value,
serde_json::json!({ "unexpectedBlockId": { "expected": 5, "got": 7 } })
);
let back: BlockIngestError = serde_json::from_value(value).expect("deserialize");
assert!(matches!(
back,
BlockIngestError::UnexpectedBlockId {
expected: 5,
got: 7
}
));
}
#[test]
fn display_is_human_readable() {
let err = BlockIngestError::StateTransition("nonce too low".to_owned());
assert_eq!(err.to_string(), "state transition failed: nonce too low");
}
}

View File

@ -5,6 +5,7 @@ use arc_swap::ArcSwap;
use common::block::Block;
// ToDo: Remove after testnet
use futures::StreamExt as _;
pub use ingest_error::BlockIngestError;
use log::{error, info, warn};
use logos_blockchain_core::header::HeaderId;
use logos_blockchain_zone_sdk::{
@ -16,9 +17,9 @@ use crate::{
config::IndexerConfig,
status::{IndexerStatus, IndexerSyncStatus},
};
pub mod block_store;
pub mod config;
pub mod ingest_error;
pub mod status;
#[derive(Clone)]