2026-01-27 09:46:31 +02:00
|
|
|
use anyhow::Result;
|
|
|
|
|
use bedrock_client::BedrockClient;
|
2026-02-03 11:36:07 +02:00
|
|
|
// ToDo: Remove after testnet
|
|
|
|
|
use common::PINATA_BASE58;
|
2026-02-04 14:57:38 +02:00
|
|
|
use common::{block::Block, sequencer_client::SequencerClient};
|
2026-01-21 14:50:29 +02:00
|
|
|
use futures::StreamExt;
|
|
|
|
|
use log::info;
|
2026-01-27 08:13:53 +02:00
|
|
|
use logos_blockchain_core::mantle::{
|
2026-01-12 15:51:24 +02:00
|
|
|
Op, SignedMantleTx,
|
|
|
|
|
ops::channel::{ChannelId, inscribe::InscriptionOp},
|
|
|
|
|
};
|
2026-01-09 15:10:38 +02:00
|
|
|
|
2026-02-03 11:36:07 +02:00
|
|
|
use crate::{block_store::IndexerStore, config::IndexerConfig};
|
2026-01-12 15:51:24 +02:00
|
|
|
|
2026-02-03 11:36:07 +02:00
|
|
|
pub mod block_store;
|
2026-01-12 15:51:24 +02:00
|
|
|
pub mod config;
|
2026-01-13 15:11:51 +02:00
|
|
|
pub mod state;
|
2026-01-12 15:51:24 +02:00
|
|
|
|
2026-01-30 21:37:27 +03:00
|
|
|
#[derive(Clone)]
|
2026-01-09 15:10:38 +02:00
|
|
|
pub struct IndexerCore {
|
2026-02-03 11:36:07 +02:00
|
|
|
pub bedrock_client: BedrockClient,
|
|
|
|
|
pub sequencer_client: SequencerClient,
|
|
|
|
|
pub config: IndexerConfig,
|
|
|
|
|
pub store: IndexerStore,
|
2026-01-09 15:10:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl IndexerCore {
|
2026-02-03 11:36:07 +02:00
|
|
|
pub async fn new(config: IndexerConfig) -> Result<Self> {
|
|
|
|
|
let sequencer_client = SequencerClient::new_with_auth(
|
|
|
|
|
config.sequencer_client_config.addr.clone(),
|
|
|
|
|
config.sequencer_client_config.auth.clone(),
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
let start_block = sequencer_client.get_genesis_block().await?;
|
|
|
|
|
|
|
|
|
|
let initial_commitments: Vec<nssa_core::Commitment> = config
|
|
|
|
|
.initial_commitments
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|init_comm_data| {
|
|
|
|
|
let npk = &init_comm_data.npk;
|
|
|
|
|
|
|
|
|
|
let mut acc = init_comm_data.account.clone();
|
|
|
|
|
|
|
|
|
|
acc.program_owner = nssa::program::Program::authenticated_transfer_program().id();
|
|
|
|
|
|
|
|
|
|
nssa_core::Commitment::new(npk, &acc)
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
let init_accs: Vec<(nssa::AccountId, u128)> = config
|
|
|
|
|
.initial_accounts
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|acc_data| (acc_data.account_id.parse().unwrap(), acc_data.balance))
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
let mut state = nssa::V02State::new_with_genesis_accounts(&init_accs, &initial_commitments);
|
|
|
|
|
|
|
|
|
|
// ToDo: Remove after testnet
|
|
|
|
|
state.add_pinata_program(PINATA_BASE58.parse().unwrap());
|
|
|
|
|
|
|
|
|
|
let home = config.home.clone();
|
|
|
|
|
|
2026-01-12 15:51:24 +02:00
|
|
|
Ok(Self {
|
2026-01-22 14:44:48 +02:00
|
|
|
bedrock_client: BedrockClient::new(
|
2026-01-27 09:46:31 +02:00
|
|
|
config.bedrock_client_config.auth.clone().map(Into::into),
|
|
|
|
|
config.bedrock_client_config.addr.clone(),
|
2026-01-22 14:44:48 +02:00
|
|
|
)?,
|
2026-02-03 11:36:07 +02:00
|
|
|
sequencer_client,
|
2026-01-12 15:51:24 +02:00
|
|
|
config,
|
2026-02-03 11:36:07 +02:00
|
|
|
// ToDo: Implement restarts
|
|
|
|
|
store: IndexerStore::open_db_with_genesis(&home, Some((start_block, state)))?,
|
2026-01-12 15:51:24 +02:00
|
|
|
})
|
2026-01-09 15:10:38 +02:00
|
|
|
}
|
2026-01-12 15:51:24 +02:00
|
|
|
|
2026-01-31 01:39:59 +03:00
|
|
|
pub async fn subscribe_parse_block_stream(&self) -> impl futures::Stream<Item = Result<Block>> {
|
2026-01-30 21:37:27 +03:00
|
|
|
async_stream::stream! {
|
2026-02-03 11:36:07 +02:00
|
|
|
loop {
|
|
|
|
|
let mut stream_pinned = Box::pin(self.bedrock_client.get_lib_stream().await?);
|
2026-01-30 21:37:27 +03:00
|
|
|
|
2026-02-03 11:36:07 +02:00
|
|
|
info!("Block stream joined");
|
2026-01-30 21:37:27 +03:00
|
|
|
|
2026-02-03 11:36:07 +02:00
|
|
|
while let Some(block_info) = stream_pinned.next().await {
|
|
|
|
|
let header_id = block_info.header_id;
|
2026-01-30 21:37:27 +03:00
|
|
|
|
2026-02-03 11:36:07 +02:00
|
|
|
info!("Observed L1 block at height {}", block_info.height);
|
2026-01-30 21:37:27 +03:00
|
|
|
|
2026-02-03 11:36:07 +02:00
|
|
|
if let Some(l1_block) = self
|
|
|
|
|
.bedrock_client
|
|
|
|
|
.get_block_by_id(header_id, &self.config.backoff)
|
|
|
|
|
.await?
|
|
|
|
|
{
|
|
|
|
|
info!("Extracted L1 block at height {}", block_info.height);
|
2026-01-30 21:37:27 +03:00
|
|
|
|
2026-02-03 11:36:07 +02:00
|
|
|
let l2_blocks_parsed = parse_blocks(
|
|
|
|
|
l1_block.into_transactions().into_iter(),
|
|
|
|
|
&self.config.channel_id,
|
|
|
|
|
).collect::<Vec<_>>();
|
2026-01-31 01:50:30 +03:00
|
|
|
|
2026-02-03 11:36:07 +02:00
|
|
|
info!("Parsed {} L2 blocks", l2_blocks_parsed.len());
|
2026-01-30 21:37:27 +03:00
|
|
|
|
2026-02-03 11:36:07 +02:00
|
|
|
for l2_block in l2_blocks_parsed {
|
|
|
|
|
self.store.put_block(l2_block.clone())?;
|
2026-01-15 15:44:48 +02:00
|
|
|
|
2026-02-03 11:36:07 +02:00
|
|
|
yield Ok(l2_block);
|
2026-01-16 16:15:21 +02:00
|
|
|
}
|
2026-01-12 15:51:24 +02:00
|
|
|
}
|
2026-02-03 11:36:07 +02:00
|
|
|
}
|
2026-01-12 15:51:24 +02:00
|
|
|
|
2026-02-03 11:36:07 +02:00
|
|
|
// Refetch stream after delay
|
|
|
|
|
tokio::time::sleep(std::time::Duration::from_millis(
|
|
|
|
|
self.config.resubscribe_interval_millis,
|
|
|
|
|
))
|
|
|
|
|
.await;
|
|
|
|
|
}
|
2026-01-16 16:15:21 +02:00
|
|
|
}
|
2026-01-12 15:51:24 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-21 14:50:29 +02:00
|
|
|
fn parse_blocks(
|
2026-01-12 15:51:24 +02:00
|
|
|
block_txs: impl Iterator<Item = SignedMantleTx>,
|
|
|
|
|
decoded_channel_id: &ChannelId,
|
2026-01-30 21:37:27 +03:00
|
|
|
) -> impl Iterator<Item = Block> {
|
2026-01-27 09:46:31 +02:00
|
|
|
block_txs.flat_map(|tx| {
|
|
|
|
|
tx.mantle_tx.ops.into_iter().filter_map(|op| match op {
|
|
|
|
|
Op::ChannelInscribe(InscriptionOp {
|
|
|
|
|
channel_id,
|
|
|
|
|
inscription,
|
|
|
|
|
..
|
|
|
|
|
}) if channel_id == *decoded_channel_id => {
|
2026-01-30 21:37:27 +03:00
|
|
|
borsh::from_slice::<Block>(&inscription).ok()
|
2026-01-27 09:46:31 +02:00
|
|
|
}
|
|
|
|
|
_ => None,
|
2026-01-12 15:51:24 +02:00
|
|
|
})
|
2026-01-27 09:46:31 +02:00
|
|
|
})
|
2026-02-04 14:57:38 +02:00
|
|
|
}
|