mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-02-27 16:53:13 +00:00
Merge pull request #354 from logos-blockchain/Pravdyvy/indexer-explorer-integration-fixes
Indexer<->Explorer integration fixes
This commit is contained in:
commit
18d9d80105
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2733,6 +2733,7 @@ dependencies = [
|
||||
"env_logger",
|
||||
"indexer_service_protocol",
|
||||
"indexer_service_rpc",
|
||||
"itertools 0.14.0",
|
||||
"jsonrpsee",
|
||||
"leptos",
|
||||
"leptos_axum",
|
||||
|
||||
@ -50,6 +50,9 @@ clap = { workspace = true, features = ["derive"], optional = true }
|
||||
url = { workspace = true, optional = true }
|
||||
env_logger = { workspace = true, optional = true }
|
||||
|
||||
# Mandatory server side dependencies
|
||||
itertools.workspace = true
|
||||
|
||||
[features]
|
||||
hydrate = ["leptos/hydrate"]
|
||||
ssr = [
|
||||
|
||||
@ -83,6 +83,17 @@ pub async fn get_block_by_id(block_id: BlockId) -> Result<Block, ServerFnError>
|
||||
.map_err(|e| ServerFnError::ServerError(format!("RPC error: {}", e)))
|
||||
}
|
||||
|
||||
/// Get latest block ID
|
||||
#[server]
|
||||
pub async fn get_latest_block_id() -> Result<BlockId, ServerFnError> {
|
||||
use indexer_service_rpc::RpcClient as _;
|
||||
let client = expect_context::<IndexerRpcClient>();
|
||||
client
|
||||
.get_last_finalized_block_id()
|
||||
.await
|
||||
.map_err(|e| ServerFnError::ServerError(format!("RPC error: {}", e)))
|
||||
}
|
||||
|
||||
/// Get block by hash
|
||||
#[server]
|
||||
pub async fn get_block_by_hash(block_hash: HashType) -> Result<Block, ServerFnError> {
|
||||
|
||||
@ -28,7 +28,7 @@ pub fn App() -> impl IntoView {
|
||||
|
||||
view! {
|
||||
<Stylesheet id="leptos" href="/explorer.css" />
|
||||
<Title text="LEE Blockchain Explorer" />
|
||||
<Title text="LEZ Block Explorer" />
|
||||
<Meta name="description" content="Explore the blockchain - view blocks, transactions, and accounts" />
|
||||
|
||||
<Router>
|
||||
@ -36,7 +36,7 @@ pub fn App() -> impl IntoView {
|
||||
<header class="app-header">
|
||||
<nav class="app-nav">
|
||||
<a href="/" class="nav-logo">
|
||||
"LEE Blockchain Explorer"
|
||||
"LEZ Block Explorer"
|
||||
</a>
|
||||
</nav>
|
||||
</header>
|
||||
@ -69,7 +69,7 @@ pub fn App() -> impl IntoView {
|
||||
</main>
|
||||
|
||||
<footer class="app-footer">
|
||||
<p>"LEE Blockchain Explorer © 2026"</p>
|
||||
<p>"LEZ Block Explorer © 2026"</p>
|
||||
</footer>
|
||||
</div>
|
||||
</Router>
|
||||
|
||||
@ -10,7 +10,7 @@ async fn main() {
|
||||
|
||||
env_logger::init();
|
||||
|
||||
/// LEE Blockchain Explorer Server CLI arguments.
|
||||
/// LEZ Block Explorer Server CLI arguments.
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = None)]
|
||||
struct Args {
|
||||
|
||||
@ -7,6 +7,8 @@ use crate::{
|
||||
components::{AccountPreview, BlockPreview, TransactionPreview},
|
||||
};
|
||||
|
||||
const RECENT_BLOCKS_LIMIT: u64 = 10;
|
||||
|
||||
/// Main page component
|
||||
#[component]
|
||||
pub fn MainPage() -> impl IntoView {
|
||||
@ -38,7 +40,21 @@ pub fn MainPage() -> impl IntoView {
|
||||
});
|
||||
|
||||
// Load recent blocks on mount
|
||||
let recent_blocks_resource = Resource::new(|| (), |_| async { api::get_blocks(0, 10).await });
|
||||
let recent_blocks_resource = Resource::new(
|
||||
|| (),
|
||||
|_| async {
|
||||
match api::get_latest_block_id().await {
|
||||
Ok(last_id) => {
|
||||
api::get_blocks(
|
||||
std::cmp::max(last_id.saturating_sub(RECENT_BLOCKS_LIMIT) as u32, 1),
|
||||
(RECENT_BLOCKS_LIMIT + 1) as u32,
|
||||
)
|
||||
.await
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Handle search - update URL parameter
|
||||
let on_search = move |ev: SubmitEvent| {
|
||||
@ -58,7 +74,7 @@ pub fn MainPage() -> impl IntoView {
|
||||
view! {
|
||||
<div class="main-page">
|
||||
<div class="page-header">
|
||||
<h1>"LEE Blockchain Explorer"</h1>
|
||||
<h1>"LEZ Block Explorer"</h1>
|
||||
</div>
|
||||
|
||||
<div class="search-section">
|
||||
|
||||
@ -4,6 +4,7 @@ use indexer_service_protocol::{
|
||||
HashType, PrivacyPreservingMessage, PrivacyPreservingTransaction, ProgramDeploymentMessage,
|
||||
ProgramDeploymentTransaction, PublicMessage, PublicTransaction, Transaction, WitnessSet,
|
||||
};
|
||||
use itertools::{EitherOrBoth, Itertools};
|
||||
use leptos::prelude::*;
|
||||
use leptos_router::{components::A, hooks::use_params_map};
|
||||
|
||||
@ -65,7 +66,8 @@ pub fn TransactionPage() -> impl IntoView {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{match tx {
|
||||
{
|
||||
match tx {
|
||||
Transaction::Public(ptx) => {
|
||||
let PublicTransaction {
|
||||
hash: _,
|
||||
@ -115,9 +117,11 @@ pub fn TransactionPage() -> impl IntoView {
|
||||
<div class="accounts-list">
|
||||
{account_ids
|
||||
.into_iter()
|
||||
.zip(nonces.into_iter())
|
||||
.map(|(account_id, nonce)| {
|
||||
let account_id_str = account_id.to_string();
|
||||
.zip_longest(nonces.into_iter())
|
||||
.map(|maybe_pair| {
|
||||
match maybe_pair {
|
||||
EitherOrBoth::Both(account_id, nonce) => {
|
||||
let account_id_str = account_id.to_string();
|
||||
view! {
|
||||
<div class="account-item">
|
||||
<A href=format!("/account/{}", account_id_str)>
|
||||
@ -128,6 +132,33 @@ pub fn TransactionPage() -> impl IntoView {
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
EitherOrBoth::Left(account_id) => {
|
||||
let account_id_str = account_id.to_string();
|
||||
view! {
|
||||
<div class="account-item">
|
||||
<A href=format!("/account/{}", account_id_str)>
|
||||
<span class="hash">{account_id_str}</span>
|
||||
</A>
|
||||
<span class="nonce">
|
||||
" (nonce: "{"Not affected by this transaction".to_string()}" )"
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
EitherOrBoth::Right(_) => {
|
||||
view! {
|
||||
<div class="account-item">
|
||||
<A href=format!("/account/{}", "Account not found")>
|
||||
<span class="hash">{"Account not found"}</span>
|
||||
</A>
|
||||
<span class="nonce">
|
||||
" (nonce: "{"Account not found".to_string()}" )"
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()}
|
||||
</div>
|
||||
@ -189,9 +220,11 @@ pub fn TransactionPage() -> impl IntoView {
|
||||
<div class="accounts-list">
|
||||
{public_account_ids
|
||||
.into_iter()
|
||||
.zip(nonces.into_iter())
|
||||
.map(|(account_id, nonce)| {
|
||||
let account_id_str = account_id.to_string();
|
||||
.zip_longest(nonces.into_iter())
|
||||
.map(|maybe_pair| {
|
||||
match maybe_pair {
|
||||
EitherOrBoth::Both(account_id, nonce) => {
|
||||
let account_id_str = account_id.to_string();
|
||||
view! {
|
||||
<div class="account-item">
|
||||
<A href=format!("/account/{}", account_id_str)>
|
||||
@ -202,6 +235,33 @@ pub fn TransactionPage() -> impl IntoView {
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
EitherOrBoth::Left(account_id) => {
|
||||
let account_id_str = account_id.to_string();
|
||||
view! {
|
||||
<div class="account-item">
|
||||
<A href=format!("/account/{}", account_id_str)>
|
||||
<span class="hash">{account_id_str}</span>
|
||||
</A>
|
||||
<span class="nonce">
|
||||
" (nonce: "{"Not affected by this transaction".to_string()}" )"
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
EitherOrBoth::Right(_) => {
|
||||
view! {
|
||||
<div class="account-item">
|
||||
<A href=format!("/account/{}", "Account not found")>
|
||||
<span class="hash">{"Account not found"}</span>
|
||||
</A>
|
||||
<span class="nonce">
|
||||
" (nonce: "{"Account not found".to_string()}" )"
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()}
|
||||
</div>
|
||||
|
||||
@ -2,7 +2,10 @@ use std::{path::Path, sync::Arc};
|
||||
|
||||
use anyhow::Result;
|
||||
use bedrock_client::HeaderId;
|
||||
use common::{block::Block, transaction::NSSATransaction};
|
||||
use common::{
|
||||
block::{BedrockStatus, Block},
|
||||
transaction::NSSATransaction,
|
||||
};
|
||||
use nssa::{Account, AccountId, V02State};
|
||||
use storage::indexer::RocksDBIO;
|
||||
|
||||
@ -100,7 +103,7 @@ impl IndexerStore {
|
||||
Ok(self.final_state()?.get_account_by_id(*account_id))
|
||||
}
|
||||
|
||||
pub fn put_block(&self, block: Block, l1_header: HeaderId) -> Result<()> {
|
||||
pub fn put_block(&self, mut block: Block, l1_header: HeaderId) -> Result<()> {
|
||||
let mut final_state = self.dbio.final_state()?;
|
||||
|
||||
for transaction in &block.body.transactions {
|
||||
@ -110,6 +113,11 @@ impl IndexerStore {
|
||||
.execute_check_on_state(&mut final_state)?;
|
||||
}
|
||||
|
||||
// ToDo: Currently we are fetching only finalized blocks
|
||||
// if it changes, the following lines need to be updated
|
||||
// to represent correct block finality
|
||||
block.bedrock_status = BedrockStatus::Finalized;
|
||||
|
||||
Ok(self.dbio.put_block(block, l1_header.into())?)
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,7 +15,6 @@ use crate::{block_store::IndexerStore, config::IndexerConfig};
|
||||
|
||||
pub mod block_store;
|
||||
pub mod config;
|
||||
pub mod state;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct IndexerCore {
|
||||
@ -123,6 +122,10 @@ impl IndexerCore {
|
||||
l2_blocks: l2_block_vec,
|
||||
l1_header,
|
||||
} in start_buff {
|
||||
let mut l2_blocks_parsed_ids: Vec<_> = l2_block_vec.iter().map(|block| block.header.block_id).collect();
|
||||
l2_blocks_parsed_ids.sort();
|
||||
info!("Parsed {} L2 blocks with ids {:?}", l2_block_vec.len(), l2_blocks_parsed_ids);
|
||||
|
||||
for l2_block in l2_block_vec {
|
||||
self.store.put_block(l2_block.clone(), l1_header)?;
|
||||
|
||||
@ -153,6 +156,10 @@ impl IndexerCore {
|
||||
l2_blocks: l2_block_vec,
|
||||
l1_header: header,
|
||||
} in buff {
|
||||
let mut l2_blocks_parsed_ids: Vec<_> = l2_block_vec.iter().map(|block| block.header.block_id).collect();
|
||||
l2_blocks_parsed_ids.sort();
|
||||
info!("Parsed {} L2 blocks with ids {:?}", l2_block_vec.len(), l2_blocks_parsed_ids);
|
||||
|
||||
for l2_block in l2_block_vec {
|
||||
self.store.put_block(l2_block.clone(), header)?;
|
||||
|
||||
@ -260,7 +267,7 @@ impl IndexerCore {
|
||||
|
||||
Ok(BackfillData {
|
||||
block_data: block_buffer,
|
||||
curr_fin_l1_lib_header: backfill_limit,
|
||||
curr_fin_l1_lib_header: curr_last_l1_lib_header,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IndexerState {
|
||||
// Only one field for now, for testing.
|
||||
pub latest_seen_block: Arc<RwLock<u64>>,
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"home": "./indexer/service",
|
||||
"consensus_info_polling_interval": "1s",
|
||||
"consensus_info_polling_interval": "60s",
|
||||
"bedrock_client_config": {
|
||||
"addr": "http://localhost:8080",
|
||||
"backoff": {
|
||||
|
||||
@ -5,7 +5,7 @@ edition = "2024"
|
||||
license = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
indexer_service_protocol = { workspace = true }
|
||||
indexer_service_protocol = { workspace = true, features = ["convert"] }
|
||||
|
||||
jsonrpsee = { workspace = true, features = ["macros"] }
|
||||
serde_json.workspace = true
|
||||
|
||||
@ -28,7 +28,7 @@ pub trait BlockSettlementClientTrait: Clone {
|
||||
/// Create and sign a transaction for inscribing data.
|
||||
fn create_inscribe_tx(&self, block: &Block) -> Result<(SignedMantleTx, MsgId)> {
|
||||
let inscription_data = borsh::to_vec(block)?;
|
||||
log::info!(
|
||||
log::debug!(
|
||||
"The size of the block {} is {} bytes",
|
||||
block.header.block_id,
|
||||
inscription_data.len()
|
||||
@ -104,7 +104,7 @@ impl BlockSettlementClientTrait for BlockSettlementClient {
|
||||
.await
|
||||
.context("Failed to post transaction to Bedrock")?;
|
||||
|
||||
log::info!("Posted block to Bedrock with parent id {parent_id:?} and msg id: {msg_id:?}");
|
||||
log::debug!("Posted block to Bedrock with parent id {parent_id:?} and msg id: {msg_id:?}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -182,8 +182,16 @@ async fn retry_pending_blocks(seq_core: &Arc<Mutex<SequencerCore>>) -> Result<()
|
||||
(pending_blocks, client)
|
||||
};
|
||||
|
||||
for block in pending_blocks.iter() {
|
||||
if !pending_blocks.is_empty() {
|
||||
info!(
|
||||
"Resubmitting blocks from {} to {}",
|
||||
pending_blocks.first().unwrap().header.block_id,
|
||||
pending_blocks.last().unwrap().header.block_id
|
||||
);
|
||||
}
|
||||
|
||||
for block in pending_blocks.iter() {
|
||||
debug!(
|
||||
"Resubmitting pending block with id {}",
|
||||
block.header.block_id
|
||||
);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user