mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-07-16 02:40:04 +00:00
81 lines
2.8 KiB
Rust
81 lines
2.8 KiB
Rust
use common::HashType;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
/// Why an L2 block from the channel could not be applied.
|
|
///
|
|
/// Persisted in `RocksDB` (as part of [`crate::StallReason`]), so every variant
|
|
/// must be `Clone + Serialize + Deserialize`.
|
|
#[derive(Debug, Clone, Serialize, Deserialize, thiserror::Error)]
|
|
pub enum BlockIngestError {
|
|
#[error("Failed to deserialize L2 block: {0}")]
|
|
/// Here we store the error string that is derived from [`borsh::from_slice`]'s [`Err`].
|
|
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("Block has no transactions")]
|
|
EmptyBlock,
|
|
#[error("Last transaction must be the public clock invocation for the block timestamp")]
|
|
InvalidClockTransaction,
|
|
#[error("Genesis block must contain only public transactions")]
|
|
NonPublicGenesisTransaction,
|
|
#[error("State transition failed at transaction {tx_index}: {reason}")]
|
|
StateTransition {
|
|
/// Index of the failing transaction within the block body.
|
|
tx_index: u64,
|
|
/// Reason string from `lee::Error` to `anyhow::Error` to `{:#}`.
|
|
///
|
|
/// This is required because `lee::Error` is not `Clone + Serialize + Deserialize`, so we
|
|
/// cannot store it directly.
|
|
reason: String,
|
|
},
|
|
}
|
|
|
|
impl BlockIngestError {
|
|
/// Whether the failure may be transient rather than a property of the block.
|
|
///
|
|
/// FIXME: `StateTransition` is too coarse — its `reason` string mixes genuine
|
|
/// state-transition rejections with infra failures (risc0 executor teardown,
|
|
/// storage errors). Once it carries a structured cause, narrow this so only
|
|
/// infra failures retry.
|
|
#[must_use]
|
|
pub const fn is_retryable(&self) -> bool {
|
|
matches!(self, Self::StateTransition { .. })
|
|
}
|
|
}
|
|
|
|
#[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
|
|
}
|
|
));
|
|
}
|
|
}
|