From 28ab34619cbf3b6255316b2b38c100bc635491f8 Mon Sep 17 00:00:00 2001 From: incrypto32 Date: Thu, 29 Jan 2026 18:21:23 +0400 Subject: [PATCH] ethereum: Handle mixed block formats in recent_blocks_cache The recent_blocks_cache can contain blocks in two formats: - Full format: {"block": {...}, "transaction_receipts": [...]} - Light format: just block fields (no wrapper) This happens because different code paths populate the cache with different formats (upsert_block uses full, blocks_from_store uses light). Changes: - load_blocks/parent_ptr: Extract block data from full format if needed using `value.get("block").unwrap_or(&value)` - ancestor_block: Explicit check for light format with clear logging, falls back to Firehose/RPC since receipts are needed for triggers - Improved log messages to show block number and hash on failures --- chain/ethereum/src/chain.rs | 68 +++++++++++++++----------- chain/ethereum/src/ethereum_adapter.rs | 10 ++-- 2 files changed, 46 insertions(+), 32 deletions(-) diff --git a/chain/ethereum/src/chain.rs b/chain/ethereum/src/chain.rs index 964f5ca4df8..85103cc5498 100644 --- a/chain/ethereum/src/chain.rs +++ b/chain/ethereum/src/chain.rs @@ -1075,32 +1075,42 @@ impl TriggersAdapterTrait for TriggersAdapter { .ancestor_block(ptr.clone(), offset, root.clone()) .await?; - // First check if we have the ancestor in cache and can deserialize it + // First check if we have the ancestor in cache and can deserialize it. + // recent_blocks_cache can have full format {"block": {...}, "transaction_receipts": [...]} + // or light format (just block fields). We need full format with receipts for + // ancestor_block since it's used for trigger processing. let block_ptr = match cached { Some((json, ptr)) => { - // Try to deserialize the cached block - match json::from_value::(json.clone()) { - Ok(block) => { - // Successfully cached and deserialized - return Ok(Some(BlockFinality::NonFinal(EthereumBlockWithCalls { - ethereum_block: block, - calls: None, - }))); - } - Err(e) => { - // Cache hit but deserialization failed - warn!( - self.logger, - "Failed to deserialize cached ancestor block #{} {} (offset {} from #{}): {}. \ - This may indicate stale cache data from a previous version. \ - Falling back to Firehose/RPC.", - ptr.number, - ptr.hash_hex(), - offset, - ptr_for_log.number, - e - ); - ptr + if json.get("block").is_none() { + warn!( + self.logger, + "Cached ancestor block #{} {} has light format without receipts. \ + Falling back to Firehose/RPC.", + ptr.number, + ptr.hash_hex(), + ); + ptr + } else { + match json::from_value::(json.clone()) { + Ok(block) => { + return Ok(Some(BlockFinality::NonFinal(EthereumBlockWithCalls { + ethereum_block: block, + calls: None, + }))); + } + Err(e) => { + warn!( + self.logger, + "Failed to deserialize cached ancestor block #{} {} (offset {} from #{}): {}. \ + Falling back to Firehose/RPC.", + ptr.number, + ptr.hash_hex(), + offset, + ptr_for_log.number, + e + ); + ptr + } } } } @@ -1161,15 +1171,17 @@ impl TriggersAdapterTrait for TriggersAdapter { // First try to get the block from the store if let Ok(blocks) = chain_store.blocks(vec![block.hash.clone()]).await { if let Some(cached_json) = blocks.first() { - match json::from_value::(cached_json.clone()) { - Ok(block) => { - return Ok(block.parent_ptr()); + // recent_blocks_cache can contain full format {"block": {...}, "transaction_receipts": [...]} + // or light format (just block fields). Extract block data for deserialization. + let inner = cached_json.get("block").unwrap_or(cached_json); + match json::from_value::(inner.clone()) { + Ok(light_block) => { + return Ok(light_block.parent_ptr()); } Err(e) => { warn!( self.logger, "Failed to deserialize cached block #{} {}: {}. \ - This may indicate stale cache data from a previous version. \ Falling back to Firehose.", block.number, block.hash_hex(), diff --git a/chain/ethereum/src/ethereum_adapter.rs b/chain/ethereum/src/ethereum_adapter.rs index 500251121e8..64affbeec0b 100644 --- a/chain/ethereum/src/ethereum_adapter.rs +++ b/chain/ethereum/src/ethereum_adapter.rs @@ -1614,14 +1614,16 @@ impl EthereumAdapterTrait for EthereumAdapter { .unwrap_or_default() .into_iter() .filter_map(|value| { - json::from_value(value.clone()) + // recent_blocks_cache can contain full format {"block": {...}, "transaction_receipts": [...]} + // or light format (just block fields). Extract block data for deserialization. + let inner = value.get("block").unwrap_or(&value); + json::from_value(inner.clone()) .map_err(|e| { - let block_num = value.get("number").and_then(|n| n.as_u64()); - let block_hash = value.get("hash").and_then(|h| h.as_str()); + let block_num = inner.get("number").and_then(|n| n.as_str()); + let block_hash = inner.get("hash").and_then(|h| h.as_str()); warn!( &logger, "Failed to deserialize cached block #{:?} {:?}: {}. \ - This may indicate stale cache data from a previous version. \ Block will be re-fetched from RPC.", block_num, block_hash,