diff --git a/src/indexer.rs b/src/indexer.rs index 8fff1ca8c..3fa08c795 100644 --- a/src/indexer.rs +++ b/src/indexer.rs @@ -10,6 +10,10 @@ use metashrew::{ }; use protorune::Protorune; use protorune_support::network::{set_network, NetworkParams}; +use crate::tables::BLOCK_TRACES_CACHE; +use crate::tables::BLOCK_TRACES; +use std::sync::Arc; +use metashrew_support::index_pointer::KeyValuePointer; #[cfg(all( not(feature = "mainnet"), @@ -68,6 +72,9 @@ pub fn configure_network() { } pub fn index_block(block: &Block, height: u32) -> Result<()> { + // Reset the BlockTrace cache at the beginning of each block + BLOCK_TRACES_CACHE.write().unwrap().clear(); + configure_network(); let really_is_genesis = is_genesis(height.into()); if really_is_genesis { @@ -75,5 +82,32 @@ pub fn index_block(block: &Block, height: u32) -> Result<()> { } FuelTank::initialize(&block); Protorune::index_block::(block.clone(), height.into())?; + + // Save the complete BlockTrace to persistent storage + save_trace_block(height.into())?; + + Ok(()) +} + +// Function to save the complete BlockTrace to persistent storage +fn save_trace_block(height: u64) -> Result<()> { + // Get the cached BlockTrace + let cached_bytes = { + let cache = BLOCK_TRACES_CACHE.read().unwrap(); + if let Some(bytes) = cache.get(&height) { + bytes.clone() + } else { + // If no events were processed for this block, create an empty BlockTrace + use alkanes_support::proto::alkanes::AlkanesBlockTraceEvent; + use protobuf::Message; + + let empty_trace = AlkanesBlockTraceEvent::new(); + empty_trace.write_to_bytes()? + } + }; + + // Store in the persistent BLOCK_TRACES table + BLOCK_TRACES.select_value::(height).set(Arc::new(cached_bytes)); + Ok(()) } diff --git a/src/tables.rs b/src/tables.rs index 36962a84f..14e97b9b3 100644 --- a/src/tables.rs +++ b/src/tables.rs @@ -1,8 +1,20 @@ use metashrew::index_pointer::IndexPointer; use metashrew_support::index_pointer::KeyValuePointer; use once_cell::sync::Lazy; +use std::collections::HashMap; +use std::sync::RwLock; +use protobuf::Message; +use alkanes_support::proto; pub static TRACES: Lazy = Lazy::new(|| IndexPointer::from_keyword("/trace/")); pub static TRACES_BY_HEIGHT: Lazy = Lazy::new(|| IndexPointer::from_keyword("/trace/")); + +// New table for storing complete BlockTrace structures by height +pub static BLOCK_TRACES: Lazy = + Lazy::new(|| IndexPointer::from_keyword("/traces/byblock/")); + +// Cache for storing complete BlockTrace structures by height +pub static BLOCK_TRACES_CACHE: Lazy>>> = + Lazy::new(|| RwLock::new(HashMap::new())); diff --git a/src/trace.rs b/src/trace.rs index 6d547df6f..560f551c0 100644 --- a/src/trace.rs +++ b/src/trace.rs @@ -1,25 +1,70 @@ -use crate::tables::{TRACES, TRACES_BY_HEIGHT}; +use crate::tables::{TRACES, TRACES_BY_HEIGHT, BLOCK_TRACES_CACHE}; use alkanes_support::proto; use alkanes_support::trace::Trace; use anyhow::Result; use bitcoin::OutPoint; +use bitcoin::hashes::Hash; use metashrew_support::index_pointer::KeyValuePointer; use metashrew_support::utils::consensus_encode; -use protobuf::Message; +use protobuf::{Message, MessageField}; use std::sync::Arc; #[allow(unused_imports)] use { metashrew::{println, stdio::stdout}, std::fmt::Write, }; +use protorune::tables::RUNES; pub fn save_trace(outpoint: &OutPoint, height: u64, trace: Trace) -> Result<()> { let buffer: Vec = consensus_encode::(outpoint)?; - TRACES.select(&buffer).set(Arc::>::new( - >::into(trace).write_to_bytes()?, - )); + + // Convert trace to AlkanesTrace protobuf and save to TRACES + let alkanes_trace = >::into(trace); + let trace_bytes = alkanes_trace.write_to_bytes()?; + TRACES.select(&buffer).set(Arc::>::new(trace_bytes.clone())); + + // Add to TRACES_BY_HEIGHT TRACES_BY_HEIGHT .select_value(height) - .append(Arc::new(buffer)); + .append(Arc::new(buffer.clone())); + + // Update or create BlockTrace in cache + update_block_trace_cache(outpoint, height, alkanes_trace)?; + + Ok(()) +} + +// Helper function to update the block trace cache +fn update_block_trace_cache(outpoint: &OutPoint, height: u64, trace: proto::alkanes::AlkanesTrace) -> Result<()> { + let txid = outpoint.txid.as_byte_array().to_vec(); + let txindex: u32 = RUNES.TXID_TO_TXINDEX.select(&txid).get_value(); + + // Create block event for this trace + let block_event = proto::alkanes::AlkanesBlockEvent { + txindex: txindex as u64, + outpoint: MessageField::some(proto::alkanes::Outpoint { + txid: outpoint.txid.as_byte_array().to_vec(), + vout: outpoint.vout, + ..Default::default() + }), + traces: MessageField::some(trace), + ..Default::default() + }; + + // Get or create block trace + let mut cache = BLOCK_TRACES_CACHE.write().unwrap(); + let mut block_trace = if let Some(cached_bytes) = cache.get(&height) { + proto::alkanes::AlkanesBlockTraceEvent::parse_from_bytes(cached_bytes)? + } else { + proto::alkanes::AlkanesBlockTraceEvent::new() + }; + + // Add the event to the block trace + block_trace.events.push(block_event); + + // Serialize and store updated BlockTrace + let serialized = block_trace.write_to_bytes()?; + cache.insert(height, serialized); + Ok(()) } diff --git a/src/view.rs b/src/view.rs index b22205a44..05f57b63d 100644 --- a/src/view.rs +++ b/src/view.rs @@ -1,6 +1,6 @@ use crate::message::AlkaneMessageContext; use crate::network::set_view_mode; -use crate::tables::{TRACES, TRACES_BY_HEIGHT}; +use crate::tables::{BLOCK_TRACES, BLOCK_TRACES_CACHE, TRACES, TRACES_BY_HEIGHT}; use crate::utils::{ alkane_inventory_pointer, balance_pointer, credit_balances, debit_balances, pipe_storagemap_to, }; @@ -294,8 +294,29 @@ pub fn alkane_inventory(req: &AlkaneInventoryRequest) -> Result Result> { + use crate::tables::{BLOCK_TRACES, BLOCK_TRACES_CACHE}; + + // First check if we have the data in the persistent storage + let height_u64 = height as u64; + + // Try to get data from the persistent BLOCK_TRACES table + let pointer = BLOCK_TRACES.select_value::(height_u64); + // We'll use the get() method which returns Arc> and check if it's empty or not + let stored_data = pointer.get(); + if !stored_data.as_ref().is_empty() { + return Ok(stored_data.as_ref().clone()); + } + + // Then check if we have a cached version (fallback) + if let Some(cached_bytes) = BLOCK_TRACES_CACHE.read().unwrap().get(&height_u64) { + return Ok(cached_bytes.clone()); + } + + // If not found in storage or cache, rebuild it (fallback - this shouldn't happen if indexer is working correctly) + println!("Warning: BlockTrace for height {} not found in storage or cache, rebuilding...", height); + let mut block_events: Vec = vec![]; - for outpoint in TRACES_BY_HEIGHT.select_value(height as u64).get_list() { + for outpoint in TRACES_BY_HEIGHT.select_value(height_u64).get_list() { let op = outpoint.clone().to_vec(); let outpoint_decoded = consensus_decode::(&mut Cursor::new(op))?; let txid = outpoint_decoded.txid.as_byte_array().to_vec(); @@ -320,7 +341,16 @@ pub fn traceblock(height: u32) -> Result> { ..Default::default() }; - result.write_to_bytes().map_err(|e| anyhow!("{:?}", e)) + // Serialize the result + let serialized = result.write_to_bytes().map_err(|e| anyhow!("{:?}", e))?; + + // Store in cache for future use + BLOCK_TRACES_CACHE.write().unwrap().insert(height_u64, serialized.clone()); + + // Also store in persistent storage + BLOCK_TRACES.select_value::(height_u64).set(Arc::new(serialized.clone())); + + Ok(serialized) } pub fn trace(outpoint: &OutPoint) -> Result> {