diff --git a/host/src/server/api/admin.rs b/host/src/server/api/admin.rs index 15f761d14..64b6d2fcb 100644 --- a/host/src/server/api/admin.rs +++ b/host/src/server/api/admin.rs @@ -27,11 +27,11 @@ async fn set_ballot( Json(probs): Json>, ) -> HostResult<&'static str> { let ballot = Ballot::new(probs).map_err(|e| anyhow::anyhow!(e))?; - actor.set_ballot(ballot); + actor.set_ballot(ballot).await; Ok("Ballot set successfully") } async fn get_ballot(State(actor): State) -> Response { - let ballot = actor.get_ballot().probabilities().to_owned(); + let ballot = actor.get_ballot().await.probabilities().to_owned(); Json(ballot).into_response() } diff --git a/host/src/server/api/v2/proof/prune.rs b/host/src/server/api/v2/proof/prune.rs index a752a8f71..851b9396b 100644 --- a/host/src/server/api/v2/proof/prune.rs +++ b/host/src/server/api/v2/proof/prune.rs @@ -11,11 +11,15 @@ use utoipa::OpenApi; )] /// Prune all tasks. async fn prune_handler(State(actor): State) -> HostResult<()> { - let statuses = actor.pool_list_status().map_err(|e| anyhow::anyhow!(e))?; + let statuses = actor + .pool_list_status() + .await + .map_err(|e| anyhow::anyhow!(e))?; for (key, status) in statuses { tracing::info!("Pruning task: {key} with status: {status}"); let _ = actor .pool_remove_request(&key) + .await .map_err(|e| anyhow::anyhow!(e))?; } Ok(()) diff --git a/host/src/server/api/v2/proof/report.rs b/host/src/server/api/v2/proof/report.rs index 972204189..5a6a764b9 100644 --- a/host/src/server/api/v2/proof/report.rs +++ b/host/src/server/api/v2/proof/report.rs @@ -19,7 +19,10 @@ use utoipa::OpenApi; /// /// Retrieve a list of `{ chain_id, blockhash, prover_type, prover, status }` items. async fn report_handler(State(actor): State) -> HostResult> { - let statuses = actor.pool_list_status().map_err(|e| anyhow::anyhow!(e))?; + let statuses = actor + .pool_list_status() + .await + .map_err(|e| anyhow::anyhow!(e))?; // For compatibility with the old API, we need to convert the statuses to the old format. let to_task_status = |status: StatusWithContext| match status.into_status() { diff --git a/host/src/server/api/v3/proof/aggregate/prune.rs b/host/src/server/api/v3/proof/aggregate/prune.rs index 84f668c36..b5ff02eb7 100644 --- a/host/src/server/api/v3/proof/aggregate/prune.rs +++ b/host/src/server/api/v3/proof/aggregate/prune.rs @@ -11,11 +11,15 @@ use utoipa::OpenApi; )] /// Prune all aggregation tasks. async fn prune_handler(State(actor): State) -> HostResult<()> { - let statuses = actor.pool_list_status().map_err(|e| anyhow::anyhow!(e))?; + let statuses = actor + .pool_list_status() + .await + .map_err(|e| anyhow::anyhow!(e))?; for (key, status) in statuses { tracing::info!("Pruning task: {key} with status: {status}"); let _ = actor .pool_remove_request(&key) + .await .map_err(|e| anyhow::anyhow!(e))?; } Ok(()) diff --git a/host/src/server/handler.rs b/host/src/server/handler.rs index 6a5593594..deb620812 100644 --- a/host/src/server/handler.rs +++ b/host/src/server/handler.rs @@ -1,4 +1,4 @@ -use raiko_reqactor::{Action, Actor}; +use raiko_reqactor::Actor; use raiko_reqpool::{ AggregationRequestEntity, AggregationRequestKey, RequestEntity, RequestKey, SingleProofRequestKey, Status, @@ -13,18 +13,19 @@ pub async fn prove( request_key: RequestKey, request_entity: RequestEntity, ) -> Result { - let action = Action::Prove { - request_key, - request_entity, - start_time: chrono::Utc::now(), - }; - act(actor, action).await + if actor.is_paused() { + return Err("System is paused".to_string()); + } + + actor + .act(request_key.clone(), request_entity, chrono::Utc::now()) + .await + .map(|status| status.into_status()) } /// Cancel the request. -pub async fn cancel(actor: &Actor, request_key: RequestKey) -> Result { - let action = Action::Cancel { request_key }; - act(actor, action).await +pub async fn cancel(_actor: &Actor, _request_key: RequestKey) -> Result { + unimplemented!() } /// Prove the aggregation request and its sub-requests. @@ -95,43 +96,3 @@ pub async fn cancel_aggregation( } cancel(actor, request_key.into()).await } - -// === Helper functions === - -// Send the action to the Actor and return the response status -async fn act(actor: &Actor, action: Action) -> Result { - // Check if the system is paused - if actor.is_paused() { - return Err("System is paused".to_string()); - } - - // Return early if the request is already succeeded - if let Ok(Some(status)) = actor.pool_get_status(&action.request_key()) { - if matches!(status.status(), Status::Success { .. }) { - return Ok(status.into_status()); - } - } - - // Just logging the status of the request - let _ = actor - .pool_get_status(&action.request_key()) - .map(|status_opt| { - tracing::trace!( - "trace request in {request_key}: {status}", - request_key = action.request_key(), - status = status_opt - .map(|status| status.into_status().to_string()) - .unwrap_or("None".to_string()), - ) - }); - - // Send the action to the Actor and return the response status - actor.act(action.clone()).await.map(|status| { - tracing::trace!( - "trace request out {request_key}: {status}", - request_key = action.request_key(), - status = status.status() - ); - status.into_status() - }) -} diff --git a/host/src/server/utils.rs b/host/src/server/utils.rs index 83b7e9e2c..641377e22 100644 --- a/host/src/server/utils.rs +++ b/host/src/server/utils.rs @@ -87,7 +87,7 @@ pub async fn draw_for_zk_any_request( "Missing block number".to_string(), ))?; let (_, blockhash) = get_task_data(&network, block_number, actor.chain_specs()).await?; - Ok(actor.draw(&blockhash)) + Ok(actor.draw(&blockhash).await) } pub fn fulfill_sp1_params(req: &mut Value) { diff --git a/reqactor/src/actor.rs b/reqactor/src/actor.rs index 1292a1c1d..79e9fed47 100644 --- a/reqactor/src/actor.rs +++ b/reqactor/src/actor.rs @@ -2,7 +2,7 @@ use std::{ collections::HashMap, sync::{ atomic::{AtomicBool, Ordering}, - Arc, Mutex, + Arc, }, }; @@ -12,25 +12,25 @@ use raiko_lib::{ consts::{ChainSpec, SupportedChainSpecs}, proof_type::ProofType, }; -use raiko_reqpool::{Pool, RequestKey, StatusWithContext}; +use raiko_reqpool::{Pool, RequestEntity, RequestKey, Status, StatusWithContext}; use reth_primitives::BlockHash; -use tokio::sync::{mpsc::Sender, oneshot}; +use tokio::sync::{Mutex, Notify}; -use crate::Action; +use crate::queue::Queue; /// Actor is the main interface interacting with the backend and the pool. #[derive(Debug, Clone)] pub struct Actor { default_request_config: ProofRequestOpt, chain_specs: SupportedChainSpecs, - action_tx: Sender<(Action, oneshot::Sender>)>, - pause_tx: Sender<()>, is_paused: Arc, // TODO: Remove Mutex. currently, in order to pass `&mut Pool`, we need to use Arc>. pool: Arc>, // In order to support dynamic config via HTTP, we need to use Arc>. ballot: Arc>, + queue: Arc>, + notify: Arc, } impl Actor { @@ -39,17 +39,17 @@ impl Actor { ballot: Ballot, default_request_config: ProofRequestOpt, chain_specs: SupportedChainSpecs, - action_tx: Sender<(Action, oneshot::Sender>)>, - pause_tx: Sender<()>, + queue: Arc>, + notify: Arc, ) -> Self { Self { default_request_config, chain_specs, - action_tx, - pause_tx, is_paused: Arc::new(AtomicBool::new(false)), ballot: Arc::new(Mutex::new(ballot)), pool: Arc::new(Mutex::new(pool)), + queue, + notify, } } @@ -75,180 +75,98 @@ impl Actor { } /// Get the status of the request from the pool. - pub fn pool_get_status( + pub async fn pool_get_status( &self, request_key: &RequestKey, ) -> Result, String> { - self.pool.lock().unwrap().get_status(request_key) + self.pool.lock().await.get_status(request_key) } - pub fn pool_list_status(&self) -> Result, String> { - self.pool.lock().unwrap().list() + pub async fn pool_update_status( + &self, + request_key: RequestKey, + status: StatusWithContext, + ) -> Result<(), String> { + self.pool + .lock() + .await + .update_status(request_key, status) + .map(|_| ()) + } + + pub async fn pool_add_new( + &self, + request_key: RequestKey, + request_entity: RequestEntity, + status: StatusWithContext, + ) -> Result<(), String> { + self.pool + .lock() + .await + .add(request_key, request_entity, status) } - pub fn pool_remove_request(&self, request_key: &RequestKey) -> Result { - self.pool.lock().unwrap().remove(request_key) + pub async fn pool_list_status(&self) -> Result, String> { + self.pool.lock().await.list() + } + + pub async fn pool_remove_request(&self, request_key: &RequestKey) -> Result { + self.pool.lock().await.remove(request_key) } /// Send an action to the backend and wait for the response. - pub async fn act(&self, action: Action) -> Result { - let (resp_tx, resp_rx) = oneshot::channel(); - - // Send the action to the backend - let start_time = chrono::Utc::now(); - raiko_metrics::inc_actor_channel_in_count( - action.request_key(), - action.request_key().proof_type(), - ); - - self.action_tx - .send((action.clone(), resp_tx)) - .await - .map_err(|e| format!("failed to send action: {e}"))?; - - raiko_metrics::observe_actor_channel_in_duration( - action.request_key(), - action.request_key().proof_type(), - (chrono::Utc::now() - start_time) - .to_std() - .unwrap_or_default(), - ); - - // Wait for response of the action - resp_rx - .await - .map_err(|e| format!("failed to receive action response: {e}"))? + pub async fn act( + &self, + request_key: RequestKey, + request_entity: RequestEntity, + start_time: chrono::DateTime, + ) -> Result { + let pool_status_opt = self.pool_get_status(&request_key).await?; + + // Return successed status if the request is already succeeded + if matches!( + pool_status_opt.as_ref().map(|s| s.status()), + Some(Status::Success { .. }) + ) { + return Ok(pool_status_opt.unwrap()); + } + + // Mark the request as registered in the pool + let status = StatusWithContext::new(Status::Registered, start_time); + if pool_status_opt.is_none() { + self.pool_add_new(request_key.clone(), request_entity.clone(), status.clone()) + .await?; + } else { + self.pool_update_status(request_key.clone(), status.clone()) + .await?; + } + + // Push the request into the queue and notify to start the action + let mut queue = self.queue.lock().await; + if !queue.contains(&request_key) { + queue.add_pending(request_key, request_entity); + self.notify.notify_one(); + } + + return Ok(status); } - /// Set the pause flag and notify the task manager to pause, then wait for the task manager to - /// finish the pause process. - /// - /// Note that this function is blocking until the task manager finishes the pause process. pub async fn pause(&self) -> Result<(), String> { self.is_paused.store(true, Ordering::SeqCst); - self.pause_tx - .send(()) - .await - .map_err(|e| format!("failed to send pause signal: {e}"))?; Ok(()) } - pub fn get_ballot(&self) -> Ballot { - self.ballot.lock().unwrap().clone() + pub async fn get_ballot(&self) -> Ballot { + self.ballot.lock().await.clone() } - pub fn set_ballot(&self, new_ballot: Ballot) { - let mut ballot = self.ballot.lock().unwrap(); + pub async fn set_ballot(&self, new_ballot: Ballot) { + let mut ballot = self.ballot.lock().await; *ballot = new_ballot; } /// Draw proof types based on the block hash. - pub fn draw(&self, block_hash: &BlockHash) -> Option { - self.ballot.lock().unwrap().draw(block_hash) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use alloy_primitives::Address; - use raiko_lib::{ - consts::SupportedChainSpecs, - input::BlobProofType, - primitives::{ChainId, B256}, - proof_type::ProofType, - }; - use raiko_reqpool::{ - memory_pool, RequestEntity, RequestKey, SingleProofRequestEntity, SingleProofRequestKey, - StatusWithContext, - }; - use std::collections::HashMap; - use tokio::sync::mpsc; - - #[tokio::test] - async fn test_pause_sets_is_paused_flag() { - let (action_tx, _) = mpsc::channel(1); - let (pause_tx, _pause_rx) = mpsc::channel(1); - - let pool = memory_pool("test_pause_sets_is_paused_flag"); - let actor = Actor::new( - pool, - Ballot::default(), - ProofRequestOpt::default(), - SupportedChainSpecs::default(), - action_tx, - pause_tx, - ); - - assert!(!actor.is_paused(), "Actor should not be paused initially"); - - actor.pause().await.expect("Pause should succeed"); - assert!( - actor.is_paused(), - "Actor should be paused after calling pause()" - ); - } - - #[tokio::test] - async fn test_act_sends_action_and_returns_response() { - let (action_tx, mut action_rx) = mpsc::channel(1); - let (pause_tx, _) = mpsc::channel(1); - - let pool = memory_pool("test_act_sends_action_and_returns_response"); - let actor = Actor::new( - pool, - Ballot::default(), - ProofRequestOpt::default(), - SupportedChainSpecs::default(), - action_tx, - pause_tx, - ); - - // Create a test action - let request_key = RequestKey::SingleProof(SingleProofRequestKey::new( - ChainId::default(), - 1, - B256::default(), - ProofType::default(), - "test_prover".to_string(), - )); - let request_entity = RequestEntity::SingleProof(SingleProofRequestEntity::new( - 1, - 1, - "test_network".to_string(), - "test_l1_network".to_string(), - B256::default(), - Address::default(), - ProofType::default(), - BlobProofType::default(), - HashMap::new(), - )); - let test_action = Action::Prove { - request_key: request_key.clone(), - request_entity, - start_time: chrono::Utc::now(), - }; - - // Spawn a task to handle the action and send back a response - let status = StatusWithContext::new_registered(); - let status_clone = status.clone(); - let handle = tokio::spawn(async move { - let (action, resp_tx) = action_rx.recv().await.expect("Should receive action"); - // Verify we received the expected action - assert_eq!(action.request_key(), &request_key); - // Send back a mock response with Registered status - resp_tx - .send(Ok(status_clone)) - .expect("Should send response"); - }); - - // Send the action and wait for response - let result = actor.act(test_action).await; - - // Make sure we got back an Ok response - assert_eq!(result, Ok(status), "Should receive successful response"); - - // Wait for the handler to complete - handle.await.expect("Handler should complete"); + pub async fn draw(&self, block_hash: &BlockHash) -> Option { + self.ballot.lock().await.draw(block_hash) } } diff --git a/reqactor/src/backend.rs b/reqactor/src/backend.rs index ea3b24b50..134ccea06 100644 --- a/reqactor/src/backend.rs +++ b/reqactor/src/backend.rs @@ -1,5 +1,4 @@ use std::sync::Arc; -use std::time::Duration; use raiko_core::{ interfaces::{aggregate_proofs, ProofRequest}, @@ -17,462 +16,316 @@ use raiko_reqpool::{ SingleProofRequestEntity, Status, StatusWithContext, }; use reth_primitives::B256; -use tokio::sync::{ - mpsc::{self, Receiver, Sender}, - oneshot, Semaphore, -}; +use tokio::sync::{mpsc, oneshot, Mutex, Notify, Semaphore}; use tracing::{debug, trace}; -use crate::{Action, Pool}; +use crate::queue::Queue; +use crate::Pool; /// Backend runs in the background, and handles the actions from the actor. -#[derive(Clone)] +#[derive(Clone, Debug)] pub(crate) struct Backend { pool: Pool, chain_specs: SupportedChainSpecs, - internal_tx: Sender, - proving_semaphore: Arc, + queue: Arc>, + notifier: Arc, + semaphore: Arc, } -// TODO: load pool and notify internal channel impl Backend { - /// Run the backend in background. - /// - /// The returned channel sender is used to send actions to the actor, and the actor will - /// act on the actions and send responses back. - pub async fn serve_in_background( + pub fn new( pool: Pool, chain_specs: SupportedChainSpecs, - pause_rx: Receiver<()>, - action_rx: Receiver<(Action, oneshot::Sender>)>, max_proving_concurrency: usize, - ) { - let channel_size = 1024; - let (internal_tx, internal_rx) = mpsc::channel::(channel_size); - tokio::spawn(async move { - Backend { - pool, - chain_specs, - internal_tx, - proving_semaphore: Arc::new(Semaphore::new(max_proving_concurrency)), - } - .serve(action_rx, internal_rx, pause_rx) - .await; - }); + queue: Arc>, + notifier: Arc, + ) -> Self { + Self { + pool, + chain_specs, + queue, + notifier, + semaphore: Arc::new(Semaphore::new(max_proving_concurrency)), + } } - // There are three incoming channels: - // 1. action_rx: actions from the external Actor - // 2. internal_rx: internal signals from the backend itself - // 3. pause_rx: pause signal from the external Actor - async fn serve( - mut self, - mut action_rx: Receiver<(Action, oneshot::Sender>)>, - mut internal_rx: Receiver, - mut pause_rx: Receiver<()>, - ) { + pub async fn serve_in_background(self) { + let (done_tx, mut done_rx) = mpsc::channel(1000); + loop { - tokio::select! { - Some((action, resp_tx)) = action_rx.recv() => { - let request_key = action.request_key().clone(); - raiko_metrics::inc_actor_channel_out_count( - action.request_key(), - action.request_key().proof_type(), - ); - - let response = self.handle_external_action(action.clone()).await; - - // Signal the request key to the internal channel, to move on to the next step, whatever the result is - // - // NOTE: Why signal whatever the result is? It's for fault tolerance, to ensure the request will be - // handled even when something unexpected happens. - self.ensure_internal_signal(request_key).await; - - // When the client side is closed, the response channel is closed, and sending response to the - // channel will return an error. So we discard the result of sending response to the external actor. - let _discard = resp_tx.send(response.clone()); - } - Some(request_key) = internal_rx.recv() => { - self.handle_internal_signal(request_key.clone()).await; - } - Some(()) = pause_rx.recv() => { - tracing::info!("Actor Backend received pause-signal, halting"); - if let Err(err) = self.halt().await { - tracing::error!("Actor Backend failed to halt: {err:?}"); - } - } - else => { - // All channels are closed, exit the loop - tracing::info!("Actor Backend exited"); - break; - } + // Handle completed requests + while let Ok(request_key) = done_rx.try_recv() { + let mut queue = self.queue.lock().await; + queue.complete(request_key); } - } - } - - async fn handle_external_action( - &mut self, - action: Action, - ) -> Result { - match action { - Action::Prove { - request_key, - request_entity, - start_time, - } => match self.pool.get_status(&request_key) { - Ok(None) => { - tracing::debug!("Actor Backend received prove-action {request_key}, and it is not in pool, registering"); - self.register(request_key.clone(), request_entity, start_time) - .await - } - Ok(Some(status)) => match status.status() { - Status::Registered | Status::WorkInProgress | Status::Success { .. } => { - tracing::debug!("Actor Backend received prove-action {request_key}, but it is already {status}, skipping"); - Ok(status) - } - Status::Cancelled { .. } => { - tracing::warn!("Actor Backend received prove-action {request_key}, and it is cancelled, re-registering"); - self.register(request_key, request_entity, start_time).await - } - Status::Failed { .. } => { - tracing::warn!("Actor Backend received prove-action {request_key}, and it is failed, re-registering"); - self.register(request_key, request_entity, start_time).await - } - }, - Err(err) => { - tracing::error!( - "Actor Backend failed to get status of prove-action {request_key}: {err:?}" - ); - Err(err) - } - }, - Action::Cancel { request_key } => match self.pool.get_status(&request_key) { - Ok(None) => { - tracing::warn!("Actor Backend received cancel-action {request_key}, but it is not in pool, skipping"); - Err("request is not in pool".to_string()) - } - Ok(Some(status)) => match status.status() { - Status::Registered | Status::WorkInProgress => { - tracing::debug!("Actor Backend received cancel-action {request_key}, and it is {status}, cancelling"); - self.cancel(request_key, status).await - } - Status::Failed { .. } | Status::Cancelled { .. } | Status::Success { .. } => { - tracing::debug!("Actor Backend received cancel-action {request_key}, but it is already {status}, skipping"); - Ok(status) - } - }, - Err(err) => { - tracing::error!( - "Actor Backend failed to get status of cancel-action {request_key}: {err:?}" - ); - Err(err) + let (request_key, request_entity) = { + let mut queue = self.queue.lock().await; + if let Some((request_key, request_entity)) = queue.try_next() { + (request_key, request_entity) + } else { + drop(queue); + self.notifier.notified().await; + continue; } - }, - } - } - - // Check the request status and then move on to the next step accordingly. - async fn handle_internal_signal(&mut self, request_key: RequestKey) { - match self.pool.get(&request_key) { - Ok(Some((request_entity, status))) => match status.status() { - Status::Registered => match request_entity { + }; + + let request_key_ = request_key.clone(); + let mut pool_ = self.pool.clone(); + let chain_specs = self.chain_specs.clone(); + let semaphore_ = self.semaphore.clone(); + let (semaphore_acquired_tx, semaphore_acquired_rx) = oneshot::channel(); + let handle = tokio::spawn(async move { + let _permit = semaphore_.acquire().await.unwrap(); + let _ = semaphore_acquired_tx.send(()); + + let result = match request_entity { RequestEntity::SingleProof(entity) => { - tracing::debug!("Actor Backend received internal signal {request_key}, status: {status}, proving single proof"); - self.prove_single(request_key.clone(), entity).await; - self.ensure_internal_signal(request_key).await; + do_prove_single(&mut pool_, &chain_specs, request_key_.clone(), entity) + .await } RequestEntity::Aggregation(entity) => { - tracing::debug!("Actor Backend received internal signal {request_key}, status: {status}, proving aggregation proof"); - self.prove_aggregation(request_key.clone(), entity).await; - self.ensure_internal_signal(request_key).await; + do_prove_aggregation(&mut pool_, request_key_.clone(), entity).await } RequestEntity::BatchProof(entity) => { - tracing::debug!("Actor Backend received internal signal {request_key}, status: {status}, proving batch proof"); - self.prove_batch(request_key.clone(), entity).await; - self.ensure_internal_signal(request_key).await; + do_prove_batch(&mut pool_, &chain_specs, request_key_.clone(), entity).await } - }, - Status::WorkInProgress => { - // Wait for proving completion - tracing::debug!( - "Actor Backend checks a work-in-progress request {request_key}, elapsed: {elapsed:?}", - elapsed = chrono::Utc::now() - status.timestamp(), - ); - self.ensure_internal_signal_after(request_key, Duration::from_secs(3)) - .await; - } - Status::Success { .. } | Status::Cancelled { .. } | Status::Failed { .. } => { - tracing::debug!("Actor Backend received internal signal {request_key}, status: {status}, done"); - } - }, - Ok(None) => { - tracing::warn!( - "Actor Backend received internal signal {request_key}, but it is not in pool, skipping" - ); - } - Err(err) => { - // Fault tolerance: re-enqueue the internal signal after 3 seconds - tracing::warn!( - "Actor Backend failed to get status of internal signal {request_key}: {err:?}, performing fault tolerance and retrying later" + }; + let status = match result { + Ok(proof) => Status::Success { proof }, + Err(e) => Status::Failed { + error: e.to_string(), + }, + }; + let _ = pool_.update_status( + request_key_.clone(), + StatusWithContext::new(status, chrono::Utc::now()), ); - self.ensure_internal_signal_after(request_key, Duration::from_secs(3)) - .await; - } - } - } + }); - // Ensure signal the request key to the internal channel. - // - // Note that this function will retry sending the signal until success. - async fn ensure_internal_signal(&mut self, request_key: RequestKey) { - let mut ticker = tokio::time::interval(Duration::from_secs(3)); - let internal_tx = self.internal_tx.clone(); - tokio::spawn(async move { - loop { - ticker.tick().await; // first tick is immediate - if let Err(err) = internal_tx.send(request_key.clone()).await { - tracing::error!("Actor Backend failed to send internal signal {request_key}: {err:?}, retrying. It should not happen, please issue a bug report"); - } else { - break; - } - } - }); - } - - async fn ensure_internal_signal_after(&mut self, request_key: RequestKey, after: Duration) { - let mut timer = tokio::time::interval(after); - timer.tick().await; // first tick is immediate - timer.tick().await; - self.ensure_internal_signal(request_key).await - } + // Wait for the semaphore to be acquired + let _ = semaphore_acquired_rx.await; - // Register a new request to the pool and notify the actor. - async fn register( - &mut self, - request_key: RequestKey, - request_entity: RequestEntity, - start_time: chrono::DateTime, - ) -> Result { - // 1. Register to the pool - let status = StatusWithContext::new(Status::Registered, start_time); - if let Err(err) = self - .pool - .add_new(request_key.clone(), request_entity, status.clone()) - { - return Err(err); - } + let mut pool_ = self.pool.clone(); + let notifier_ = self.notifier.clone(); + let done_tx_ = done_tx.clone(); + tokio::spawn(async move { + let _ = done_tx_.send(request_key.clone()); + notifier_.notify_one(); - Ok(status) - } - - async fn cancel( - &mut self, - request_key: RequestKey, - old_status: StatusWithContext, - ) -> Result { - if old_status.status() != &Status::Registered - && old_status.status() != &Status::WorkInProgress - { - tracing::warn!("Actor Backend received cancel-action {request_key}, but it is not registered or work-in-progress, skipping"); - return Ok(old_status); - } - - // Case: old_status is registered: mark the request as cancelled in the pool and return directly - if old_status.status() == &Status::Registered { - let status = StatusWithContext::new_cancelled(); - self.pool.update_status(request_key, status.clone())?; - return Ok(status); - } - - // Case: old_status is work-in-progress: - // 1. Cancel the proving work by the cancel token // TODO: cancel token - // 2. Remove the proof id from the pool - // 3. Mark the request as cancelled in the pool - match &request_key { - RequestKey::SingleProof(key) => { - raiko_core::interfaces::cancel_proof( - key.proof_type().clone(), - ( - key.chain_id().clone(), - key.block_number().clone(), - key.block_hash().clone(), - *key.proof_type() as u8, - ), - Box::new(&mut self.pool), - ) - .await - .or_else(|e| { - if e.to_string().contains("No data for query") { - tracing::warn!("Actor Backend received cancel-action {request_key}, but it is already cancelled or not yet started, skipping"); - Ok(()) - } else { - tracing::error!( - "Actor Backend received cancel-action {request_key}, but failed to cancel proof: {e:?}" - ); - Err(format!("failed to cancel proof: {e:?}")) - } - })?; - - // 3. Mark the request as cancelled in the pool - let status = StatusWithContext::new_cancelled(); - self.pool.update_status(request_key, status.clone())?; - Ok(status) - } - RequestKey::Aggregation(..) => { - let status = StatusWithContext::new_cancelled(); - self.pool.update_status(request_key, status.clone())?; - Ok(status) - } - RequestKey::BatchProof(..) => { - let status = StatusWithContext::new_cancelled(); - self.pool.update_status(request_key, status.clone())?; - Ok(status) - } - } - } - - async fn prove_single( - &mut self, - request_key: RequestKey, - request_entity: SingleProofRequestEntity, - ) { - self.prove(request_key.clone(), |mut actor, request_key| async move { - do_prove_single( - &mut actor.pool, - &actor.chain_specs, - request_key, - request_entity, - ) - .await - }) - .await; - } - - async fn prove_aggregation( - &mut self, - request_key: RequestKey, - request_entity: AggregationRequestEntity, - ) { - self.prove(request_key.clone(), |mut actor, request_key| async move { - do_prove_aggregation(&mut actor.pool, request_key.clone(), request_entity).await - }) - .await; - } - - async fn prove_batch( - &mut self, - request_key: RequestKey, - request_entity: BatchProofRequestEntity, - ) { - self.prove(request_key.clone(), |mut actor, request_key| async move { - do_prove_batch( - &mut actor.pool, - &actor.chain_specs, - request_key.clone(), - request_entity, - ) - .await - }) - .await; - } - - /// Generic method to handle proving for different types of proofs - async fn prove(&mut self, request_key: RequestKey, prove_fn: F) - where - F: FnOnce(Backend, RequestKey) -> Fut + Send + 'static, - Fut: std::future::Future> + Send + 'static, - { - let request_key_ = request_key.clone(); - - // 1. Update the request status in pool to WorkInProgress - if let Err(err) = self - .pool - .update_status(request_key.clone(), Status::WorkInProgress.into()) - { - tracing::error!( - "Actor Backend failed to update status of prove-action {request_key}: {err:?}, status: {status}", - status = Status::WorkInProgress, - ); - return; - } - - // 2. Start the proving work in a separate thread - let mut actor = self.clone(); - let proving_semaphore = self.proving_semaphore.clone(); - let (semaphore_acquired_tx, semaphore_acquired_rx) = oneshot::channel(); - - let handle = tokio::spawn(async move { - // Acquire a permit from the semaphore before starting the proving work - let _permit = proving_semaphore - .acquire() - .await - .expect("semaphore should not be closed"); - semaphore_acquired_tx.send(()).unwrap(); - - // 2.1. Start the proving work - let proven_status = prove_fn(actor.clone(), request_key.clone()) - .await - .map(|proof| Status::Success { proof }) - .unwrap_or_else(|error| Status::Failed { error }); - - match &proven_status { - Status::Success { proof } => { - tracing::info!( - "Actor Backend successfully proved {request_key}, {:?}", - proof - ); - } - Status::Failed { error } => { - tracing::error!("Actor Backend failed to prove {request_key}: {error}"); - } - _ => {} - } - - // 2.2. Update the request status in pool to the resulted status - if let Err(err) = actor - .pool - .update_status(request_key.clone(), proven_status.clone().into()) - { - tracing::error!( - "Actor Backend failed to update status of prove-action {request_key}: {err:?}, status: {proven_status}" - ); - return; - } - // The permit is automatically dropped here, releasing the semaphore - }); - - // Only set up panic handler if we have a backup request key (for single proofs) - let mut pool_ = self.pool.clone(); - tokio::spawn(async move { - if let Err(e) = handle.await { - if e.is_panic() { - tracing::error!("Actor Backend panicked while proving: {e:?}"); + if let Err(e) = handle.await { + tracing::error!("Actor thread errored while proving {request_key}: {e:?}"); let status = Status::Failed { error: e.to_string(), }; - if let Err(err) = - pool_.update_status(request_key_.clone(), status.clone().into()) - { - tracing::error!( - "Actor Backend failed to update status of prove-action {request_key_}: {err:?}, status: {status}", - status = status, - ); - } - } else { - tracing::error!("Actor Backend failed to prove: {e:?}"); + let _ = pool_.update_status(request_key.clone(), status.clone().into()); } - } - }); - - // Wait for the semaphore to be acquired - semaphore_acquired_rx.await.unwrap(); + }); + } } - async fn halt(&mut self) -> Result<(), String> { - // TODO: implement halt for pause - Ok(()) - } + // async fn handle_external_action( + // &mut self, + // action: Action, + // ) -> Result { + // match action { + // Action::Prove { + // request_key, + // request_entity, + // start_time, + // } => match self.pool.get_status(&request_key) { + // Ok(None) => { + // tracing::debug!("Actor Backend received prove-action {request_key}, and it is not in pool, registering"); + // self.register(request_key.clone(), request_entity, start_time) + // .await + // } + // Ok(Some(status)) => match status.status() { + // Status::Registered | Status::WorkInProgress | Status::Success { .. } => { + // tracing::debug!("Actor Backend received prove-action {request_key}, but it is already {status}, skipping"); + // Ok(status) + // } + // Status::Cancelled { .. } => { + // tracing::warn!("Actor Backend received prove-action {request_key}, and it is cancelled, re-registering"); + // self.register(request_key, request_entity, start_time).await + // } + // Status::Failed { .. } => { + // tracing::warn!("Actor Backend received prove-action {request_key}, and it is failed, re-registering"); + // self.register(request_key, request_entity, start_time).await + // } + // }, + // Err(err) => { + // tracing::error!( + // "Actor Backend failed to get status of prove-action {request_key}: {err:?}" + // ); + // Err(err) + // } + // }, + // Action::Cancel { request_key } => match self.pool.get_status(&request_key) { + // Ok(None) => { + // tracing::warn!("Actor Backend received cancel-action {request_key}, but it is not in pool, skipping"); + // Err("request is not in pool".to_string()) + // } + // Ok(Some(status)) => match status.status() { + // Status::Registered | Status::WorkInProgress => { + // tracing::debug!("Actor Backend received cancel-action {request_key}, and it is {status}, cancelling"); + // self.cancel(request_key, status).await + // } + + // Status::Failed { .. } | Status::Cancelled { .. } | Status::Success { .. } => { + // tracing::debug!("Actor Backend received cancel-action {request_key}, but it is already {status}, skipping"); + // Ok(status) + // } + // }, + // Err(err) => { + // tracing::error!( + // "Actor Backend failed to get status of cancel-action {request_key}: {err:?}" + // ); + // Err(err) + // } + // }, + // } + // } + + // // Check the request status and then move on to the next step accordingly. + // async fn handle_internal_signal(&mut self, request_key: RequestKey) { + // match self.pool.get(&request_key) { + // Ok(Some((request_entity, status))) => match status.status() { + // Status::Registered => match request_entity { + // RequestEntity::SingleProof(entity) => { + // tracing::debug!("Actor Backend received internal signal {request_key}, status: {status}, proving single proof"); + // self.prove_single(request_key.clone(), entity).await; + // self.ensure_internal_signal(request_key).await; + // } + // RequestEntity::Aggregation(entity) => { + // tracing::debug!("Actor Backend received internal signal {request_key}, status: {status}, proving aggregation proof"); + // self.prove_aggregation(request_key.clone(), entity).await; + // self.ensure_internal_signal(request_key).await; + // } + // RequestEntity::BatchProof(entity) => { + // tracing::debug!("Actor Backend received internal signal {request_key}, status: {status}, proving batch proof"); + // self.prove_batch(request_key.clone(), entity).await; + // self.ensure_internal_signal(request_key).await; + // } + // }, + // Status::WorkInProgress => { + // // Wait for proving completion + // tracing::debug!( + // "Actor Backend checks a work-in-progress request {request_key}, elapsed: {elapsed:?}", + // elapsed = chrono::Utc::now() - status.timestamp(), + // ); + // self.ensure_internal_signal_after(request_key, Duration::from_secs(3)) + // .await; + // } + // Status::Success { .. } | Status::Cancelled { .. } | Status::Failed { .. } => { + // tracing::debug!("Actor Backend received internal signal {request_key}, status: {status}, done"); + // } + // }, + // Ok(None) => { + // tracing::warn!( + // "Actor Backend received internal signal {request_key}, but it is not in pool, skipping" + // ); + // } + // Err(err) => { + // // Fault tolerance: re-enqueue the internal signal after 3 seconds + // tracing::warn!( + // "Actor Backend failed to get status of internal signal {request_key}: {err:?}, performing fault tolerance and retrying later" + // ); + // self.ensure_internal_signal_after(request_key, Duration::from_secs(3)) + // .await; + // } + // } + // } + + // // Ensure signal the request key to the internal channel. + // // + // // Note that this function will retry sending the signal until success. + // async fn ensure_internal_signal(&mut self, request_key: RequestKey) { + // let mut ticker = tokio::time::interval(Duration::from_secs(3)); + // let internal_tx = self.internal_tx.clone(); + // tokio::spawn(async move { + // loop { + // ticker.tick().await; // first tick is immediate + // if let Err(err) = internal_tx.send(request_key.clone()).await { + // tracing::error!("Actor Backend failed to send internal signal {request_key}: {err:?}, retrying. It should not happen, please issue a bug report"); + // } else { + // break; + // } + // } + // }); + // } + + // async fn ensure_internal_signal_after(&mut self, request_key: RequestKey, after: Duration) { + // let mut timer = tokio::time::interval(after); + // timer.tick().await; // first tick is immediate + // timer.tick().await; + // self.ensure_internal_signal(request_key).await + // } + + // async fn cancel( + // &mut self, + // request_key: RequestKey, + // old_status: StatusWithContext, + // ) -> Result { + // if old_status.status() != &Status::Registered + // && old_status.status() != &Status::WorkInProgress + // { + // tracing::warn!("Actor Backend received cancel-action {request_key}, but it is not registered or work-in-progress, skipping"); + // return Ok(old_status); + // } + + // // Case: old_status is registered: mark the request as cancelled in the pool and return directly + // if old_status.status() == &Status::Registered { + // let status = StatusWithContext::new_cancelled(); + // self.pool.update_status(request_key, status.clone())?; + // return Ok(status); + // } + + // // Case: old_status is work-in-progress: + // // 1. Cancel the proving work by the cancel token // TODO: cancel token + // // 2. Remove the proof id from the pool + // // 3. Mark the request as cancelled in the pool + // match &request_key { + // RequestKey::SingleProof(key) => { + // raiko_core::interfaces::cancel_proof( + // key.proof_type().clone(), + // ( + // key.chain_id().clone(), + // key.block_number().clone(), + // key.block_hash().clone(), + // *key.proof_type() as u8, + // ), + // Box::new(&mut self.pool), + // ) + // .await + // .or_else(|e| { + // if e.to_string().contains("No data for query") { + // tracing::warn!("Actor Backend received cancel-action {request_key}, but it is already cancelled or not yet started, skipping"); + // Ok(()) + // } else { + // tracing::error!( + // "Actor Backend received cancel-action {request_key}, but failed to cancel proof: {e:?}" + // ); + // Err(format!("failed to cancel proof: {e:?}")) + // } + // })?; + + // // 3. Mark the request as cancelled in the pool + // let status = StatusWithContext::new_cancelled(); + // self.pool.update_status(request_key, status.clone())?; + // Ok(status) + // } + // RequestKey::Aggregation(..) => { + // let status = StatusWithContext::new_cancelled(); + // self.pool.update_status(request_key, status.clone())?; + // Ok(status) + // } + // RequestKey::BatchProof(..) => { + // let status = StatusWithContext::new_cancelled(); + // self.pool.update_status(request_key, status.clone())?; + // Ok(status) + // } + // } + // } } // TODO: cache input, reference to raiko_host::cache diff --git a/reqactor/src/lib.rs b/reqactor/src/lib.rs index 852ef02c9..412315186 100644 --- a/reqactor/src/lib.rs +++ b/reqactor/src/lib.rs @@ -1,13 +1,16 @@ mod action; mod actor; mod backend; +mod queue; +use std::sync::Arc; +use tokio::sync::{Mutex, Notify}; + +use backend::Backend; +use queue::Queue; use raiko_ballot::Ballot; use raiko_core::interfaces::ProofRequestOpt; use raiko_lib::consts::SupportedChainSpecs; -use tokio::sync::{mpsc, oneshot}; - -pub(crate) use backend::Backend; // re-export pub use action::Action; @@ -25,26 +28,25 @@ pub async fn start_actor( default_request_config: ProofRequestOpt, max_proving_concurrency: usize, ) -> Actor { - let channel_size = 1024; - let (action_tx, action_rx) = - mpsc::channel::<(Action, oneshot::Sender>)>(channel_size); - let (pause_tx, pause_rx) = mpsc::channel::<()>(1); - - Backend::serve_in_background( + let queue = Arc::new(Mutex::new(Queue::new())); + let notify = Arc::new(Notify::new()); + let actor = Actor::new( pool.clone(), - chain_specs.clone(), - pause_rx, - action_rx, - max_proving_concurrency, - ) - .await; - - Actor::new( - pool, ballot, default_request_config, chain_specs.clone(), - action_tx, - pause_tx, - ) + Arc::clone(&queue), + Arc::clone(¬ify), + ); + let backend = Backend::new( + pool, + chain_specs, + max_proving_concurrency, + Arc::clone(&queue), + Arc::clone(¬ify), + ); + let _ = tokio::spawn(async move { + backend.serve_in_background().await; + }); + actor } diff --git a/reqactor/src/queue.rs b/reqactor/src/queue.rs new file mode 100644 index 000000000..b3766afcb --- /dev/null +++ b/reqactor/src/queue.rs @@ -0,0 +1,60 @@ +use std::collections::{HashSet, VecDeque}; + +use raiko_reqpool::{RequestEntity, RequestKey}; + +/// Queue of requests to be processed +#[derive(Debug)] +pub struct Queue { + /// High priority pending requests + high_pending: VecDeque<(RequestKey, RequestEntity)>, + /// Low priority pending requests + low_pending: VecDeque<(RequestKey, RequestEntity)>, + /// Requests that are currently being worked on + working_in_progress: HashSet, + /// Requests that have been pushed to the queue or are in-flight + queued_keys: HashSet, +} + +impl Queue { + pub fn new() -> Self { + Self { + high_pending: VecDeque::new(), + low_pending: VecDeque::new(), + working_in_progress: HashSet::new(), + queued_keys: HashSet::new(), + } + } + + pub fn contains(&self, request_key: &RequestKey) -> bool { + self.queued_keys.contains(request_key) + } + + pub fn add_pending(&mut self, request_key: RequestKey, request_entity: RequestEntity) { + if self.queued_keys.insert(request_key.clone()) { + let is_high_priority = matches!(request_key, RequestKey::Aggregation(_)); + if is_high_priority { + self.high_pending.push_back((request_key, request_entity)); + } else { + self.low_pending.push_back((request_key, request_entity)); + } + } + } + + /// Attempts to move a request from either the high or low priority queue into the in-flight set + /// and starts processing it. High priority requests are processed first. + pub fn try_next(&mut self) -> Option<(RequestKey, RequestEntity)> { + // Try high priority queue first + let (request_key, request_entity) = self + .high_pending + .pop_front() + .or_else(|| self.low_pending.pop_front())?; + + self.working_in_progress.insert(request_key.clone()); + Some((request_key, request_entity)) + } + + pub fn complete(&mut self, request_key: RequestKey) { + self.working_in_progress.remove(&request_key); + self.queued_keys.remove(&request_key); + } +} diff --git a/script/ITaikoInbox.json b/script/ITaikoInbox.json new file mode 100644 index 000000000..3a406cc86 --- /dev/null +++ b/script/ITaikoInbox.json @@ -0,0 +1 @@ +{"abi":[{"type":"function","name":"bondBalanceOf","inputs":[{"name":"_user","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"bondToken","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"depositBond","inputs":[{"name":"_amount","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"payable"},{"type":"function","name":"getBatch","inputs":[{"name":"_batchId","type":"uint64","internalType":"uint64"}],"outputs":[{"name":"batch_","type":"tuple","internalType":"struct ITaikoInbox.Batch","components":[{"name":"metaHash","type":"bytes32","internalType":"bytes32"},{"name":"lastBlockId","type":"uint64","internalType":"uint64"},{"name":"reserved3","type":"uint96","internalType":"uint96"},{"name":"livenessBond","type":"uint96","internalType":"uint96"},{"name":"batchId","type":"uint64","internalType":"uint64"},{"name":"lastBlockTimestamp","type":"uint64","internalType":"uint64"},{"name":"anchorBlockId","type":"uint64","internalType":"uint64"},{"name":"nextTransitionId","type":"uint24","internalType":"uint24"},{"name":"reserved4","type":"uint8","internalType":"uint8"},{"name":"verifiedTransitionId","type":"uint24","internalType":"uint24"}]}],"stateMutability":"view"},{"type":"function","name":"getBatchVerifyingTransition","inputs":[{"name":"_batchId","type":"uint64","internalType":"uint64"}],"outputs":[{"name":"","type":"tuple","internalType":"struct ITaikoInbox.TransitionState","components":[{"name":"parentHash","type":"bytes32","internalType":"bytes32"},{"name":"blockHash","type":"bytes32","internalType":"bytes32"},{"name":"stateRoot","type":"bytes32","internalType":"bytes32"},{"name":"prover","type":"address","internalType":"address"},{"name":"inProvingWindow","type":"bool","internalType":"bool"},{"name":"createdAt","type":"uint48","internalType":"uint48"}]}],"stateMutability":"view"},{"type":"function","name":"getLastSyncedTransition","inputs":[],"outputs":[{"name":"batchId_","type":"uint64","internalType":"uint64"},{"name":"blockId_","type":"uint64","internalType":"uint64"},{"name":"ts_","type":"tuple","internalType":"struct ITaikoInbox.TransitionState","components":[{"name":"parentHash","type":"bytes32","internalType":"bytes32"},{"name":"blockHash","type":"bytes32","internalType":"bytes32"},{"name":"stateRoot","type":"bytes32","internalType":"bytes32"},{"name":"prover","type":"address","internalType":"address"},{"name":"inProvingWindow","type":"bool","internalType":"bool"},{"name":"createdAt","type":"uint48","internalType":"uint48"}]}],"stateMutability":"view"},{"type":"function","name":"getLastVerifiedTransition","inputs":[],"outputs":[{"name":"batchId_","type":"uint64","internalType":"uint64"},{"name":"blockId_","type":"uint64","internalType":"uint64"},{"name":"ts_","type":"tuple","internalType":"struct ITaikoInbox.TransitionState","components":[{"name":"parentHash","type":"bytes32","internalType":"bytes32"},{"name":"blockHash","type":"bytes32","internalType":"bytes32"},{"name":"stateRoot","type":"bytes32","internalType":"bytes32"},{"name":"prover","type":"address","internalType":"address"},{"name":"inProvingWindow","type":"bool","internalType":"bool"},{"name":"createdAt","type":"uint48","internalType":"uint48"}]}],"stateMutability":"view"},{"type":"function","name":"getStats1","inputs":[],"outputs":[{"name":"","type":"tuple","internalType":"struct ITaikoInbox.Stats1","components":[{"name":"genesisHeight","type":"uint64","internalType":"uint64"},{"name":"__reserved2","type":"uint64","internalType":"uint64"},{"name":"lastSyncedBatchId","type":"uint64","internalType":"uint64"},{"name":"lastSyncedAt","type":"uint64","internalType":"uint64"}]}],"stateMutability":"view"},{"type":"function","name":"getStats2","inputs":[],"outputs":[{"name":"","type":"tuple","internalType":"struct ITaikoInbox.Stats2","components":[{"name":"numBatches","type":"uint64","internalType":"uint64"},{"name":"lastVerifiedBatchId","type":"uint64","internalType":"uint64"},{"name":"paused","type":"bool","internalType":"bool"},{"name":"lastProposedIn","type":"uint56","internalType":"uint56"},{"name":"lastUnpausedAt","type":"uint64","internalType":"uint64"}]}],"stateMutability":"view"},{"type":"function","name":"getTransitionById","inputs":[{"name":"_batchId","type":"uint64","internalType":"uint64"},{"name":"_tid","type":"uint24","internalType":"uint24"}],"outputs":[{"name":"","type":"tuple","internalType":"struct ITaikoInbox.TransitionState","components":[{"name":"parentHash","type":"bytes32","internalType":"bytes32"},{"name":"blockHash","type":"bytes32","internalType":"bytes32"},{"name":"stateRoot","type":"bytes32","internalType":"bytes32"},{"name":"prover","type":"address","internalType":"address"},{"name":"inProvingWindow","type":"bool","internalType":"bool"},{"name":"createdAt","type":"uint48","internalType":"uint48"}]}],"stateMutability":"view"},{"type":"function","name":"getTransitionByParentHash","inputs":[{"name":"_batchId","type":"uint64","internalType":"uint64"},{"name":"_parentHash","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"","type":"tuple","internalType":"struct ITaikoInbox.TransitionState","components":[{"name":"parentHash","type":"bytes32","internalType":"bytes32"},{"name":"blockHash","type":"bytes32","internalType":"bytes32"},{"name":"stateRoot","type":"bytes32","internalType":"bytes32"},{"name":"prover","type":"address","internalType":"address"},{"name":"inProvingWindow","type":"bool","internalType":"bool"},{"name":"createdAt","type":"uint48","internalType":"uint48"}]}],"stateMutability":"view"},{"type":"function","name":"pacayaConfig","inputs":[],"outputs":[{"name":"","type":"tuple","internalType":"struct ITaikoInbox.Config","components":[{"name":"chainId","type":"uint64","internalType":"uint64"},{"name":"maxUnverifiedBatches","type":"uint64","internalType":"uint64"},{"name":"batchRingBufferSize","type":"uint64","internalType":"uint64"},{"name":"maxBatchesToVerify","type":"uint64","internalType":"uint64"},{"name":"blockMaxGasLimit","type":"uint32","internalType":"uint32"},{"name":"livenessBondBase","type":"uint96","internalType":"uint96"},{"name":"livenessBondPerBlock","type":"uint96","internalType":"uint96"},{"name":"stateRootSyncInternal","type":"uint8","internalType":"uint8"},{"name":"maxAnchorHeightOffset","type":"uint64","internalType":"uint64"},{"name":"baseFeeConfig","type":"tuple","internalType":"struct LibSharedData.BaseFeeConfig","components":[{"name":"adjustmentQuotient","type":"uint8","internalType":"uint8"},{"name":"sharingPctg","type":"uint8","internalType":"uint8"},{"name":"gasIssuancePerSecond","type":"uint32","internalType":"uint32"},{"name":"minGasExcess","type":"uint64","internalType":"uint64"},{"name":"maxGasIssuancePerBlock","type":"uint32","internalType":"uint32"}]},{"name":"provingWindow","type":"uint16","internalType":"uint16"},{"name":"cooldownWindow","type":"uint24","internalType":"uint24"},{"name":"maxSignalsToReceive","type":"uint8","internalType":"uint8"},{"name":"maxBlocksPerBatch","type":"uint16","internalType":"uint16"},{"name":"forkHeights","type":"tuple","internalType":"struct ITaikoInbox.ForkHeights","components":[{"name":"ontake","type":"uint64","internalType":"uint64"},{"name":"pacaya","type":"uint64","internalType":"uint64"},{"name":"shasta","type":"uint64","internalType":"uint64"},{"name":"unzen","type":"uint64","internalType":"uint64"}]}]}],"stateMutability":"view"},{"type":"function","name":"proposeBatch","inputs":[{"name":"_params","type":"bytes","internalType":"bytes"},{"name":"_txList","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"info_","type":"tuple","internalType":"struct ITaikoInbox.BatchInfo","components":[{"name":"txsHash","type":"bytes32","internalType":"bytes32"},{"name":"blocks","type":"tuple[]","internalType":"struct ITaikoInbox.BlockParams[]","components":[{"name":"numTransactions","type":"uint16","internalType":"uint16"},{"name":"timeShift","type":"uint8","internalType":"uint8"},{"name":"signalSlots","type":"bytes32[]","internalType":"bytes32[]"}]},{"name":"blobHashes","type":"bytes32[]","internalType":"bytes32[]"},{"name":"extraData","type":"bytes32","internalType":"bytes32"},{"name":"coinbase","type":"address","internalType":"address"},{"name":"proposedIn","type":"uint64","internalType":"uint64"},{"name":"blobCreatedIn","type":"uint64","internalType":"uint64"},{"name":"blobByteOffset","type":"uint32","internalType":"uint32"},{"name":"blobByteSize","type":"uint32","internalType":"uint32"},{"name":"gasLimit","type":"uint32","internalType":"uint32"},{"name":"lastBlockId","type":"uint64","internalType":"uint64"},{"name":"lastBlockTimestamp","type":"uint64","internalType":"uint64"},{"name":"anchorBlockId","type":"uint64","internalType":"uint64"},{"name":"anchorBlockHash","type":"bytes32","internalType":"bytes32"},{"name":"baseFeeConfig","type":"tuple","internalType":"struct LibSharedData.BaseFeeConfig","components":[{"name":"adjustmentQuotient","type":"uint8","internalType":"uint8"},{"name":"sharingPctg","type":"uint8","internalType":"uint8"},{"name":"gasIssuancePerSecond","type":"uint32","internalType":"uint32"},{"name":"minGasExcess","type":"uint64","internalType":"uint64"},{"name":"maxGasIssuancePerBlock","type":"uint32","internalType":"uint32"}]}]},{"name":"meta_","type":"tuple","internalType":"struct ITaikoInbox.BatchMetadata","components":[{"name":"infoHash","type":"bytes32","internalType":"bytes32"},{"name":"proposer","type":"address","internalType":"address"},{"name":"batchId","type":"uint64","internalType":"uint64"},{"name":"proposedAt","type":"uint64","internalType":"uint64"}]}],"stateMutability":"nonpayable"},{"type":"function","name":"proveBatches","inputs":[{"name":"_params","type":"bytes","internalType":"bytes"},{"name":"_proof","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"withdrawBond","inputs":[{"name":"_amount","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"event","name":"BatchProposed","inputs":[{"name":"info","type":"tuple","indexed":false,"internalType":"struct ITaikoInbox.BatchInfo","components":[{"name":"txsHash","type":"bytes32","internalType":"bytes32"},{"name":"blocks","type":"tuple[]","internalType":"struct ITaikoInbox.BlockParams[]","components":[{"name":"numTransactions","type":"uint16","internalType":"uint16"},{"name":"timeShift","type":"uint8","internalType":"uint8"},{"name":"signalSlots","type":"bytes32[]","internalType":"bytes32[]"}]},{"name":"blobHashes","type":"bytes32[]","internalType":"bytes32[]"},{"name":"extraData","type":"bytes32","internalType":"bytes32"},{"name":"coinbase","type":"address","internalType":"address"},{"name":"proposedIn","type":"uint64","internalType":"uint64"},{"name":"blobCreatedIn","type":"uint64","internalType":"uint64"},{"name":"blobByteOffset","type":"uint32","internalType":"uint32"},{"name":"blobByteSize","type":"uint32","internalType":"uint32"},{"name":"gasLimit","type":"uint32","internalType":"uint32"},{"name":"lastBlockId","type":"uint64","internalType":"uint64"},{"name":"lastBlockTimestamp","type":"uint64","internalType":"uint64"},{"name":"anchorBlockId","type":"uint64","internalType":"uint64"},{"name":"anchorBlockHash","type":"bytes32","internalType":"bytes32"},{"name":"baseFeeConfig","type":"tuple","internalType":"struct LibSharedData.BaseFeeConfig","components":[{"name":"adjustmentQuotient","type":"uint8","internalType":"uint8"},{"name":"sharingPctg","type":"uint8","internalType":"uint8"},{"name":"gasIssuancePerSecond","type":"uint32","internalType":"uint32"},{"name":"minGasExcess","type":"uint64","internalType":"uint64"},{"name":"maxGasIssuancePerBlock","type":"uint32","internalType":"uint32"}]}]},{"name":"meta","type":"tuple","indexed":false,"internalType":"struct ITaikoInbox.BatchMetadata","components":[{"name":"infoHash","type":"bytes32","internalType":"bytes32"},{"name":"proposer","type":"address","internalType":"address"},{"name":"batchId","type":"uint64","internalType":"uint64"},{"name":"proposedAt","type":"uint64","internalType":"uint64"}]},{"name":"txList","type":"bytes","indexed":false,"internalType":"bytes"}],"anonymous":false},{"type":"event","name":"BatchesProved","inputs":[{"name":"verifier","type":"address","indexed":false,"internalType":"address"},{"name":"batchIds","type":"uint64[]","indexed":false,"internalType":"uint64[]"},{"name":"transitions","type":"tuple[]","indexed":false,"internalType":"struct ITaikoInbox.Transition[]","components":[{"name":"parentHash","type":"bytes32","internalType":"bytes32"},{"name":"blockHash","type":"bytes32","internalType":"bytes32"},{"name":"stateRoot","type":"bytes32","internalType":"bytes32"}]}],"anonymous":false},{"type":"event","name":"BatchesVerified","inputs":[{"name":"batchId","type":"uint64","indexed":false,"internalType":"uint64"},{"name":"blockHash","type":"bytes32","indexed":false,"internalType":"bytes32"}],"anonymous":false},{"type":"event","name":"BondCredited","inputs":[{"name":"user","type":"address","indexed":true,"internalType":"address"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"BondDebited","inputs":[{"name":"user","type":"address","indexed":true,"internalType":"address"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"BondDeposited","inputs":[{"name":"user","type":"address","indexed":true,"internalType":"address"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"BondWithdrawn","inputs":[{"name":"user","type":"address","indexed":true,"internalType":"address"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"ConflictingProof","inputs":[{"name":"batchId","type":"uint64","indexed":false,"internalType":"uint64"},{"name":"oldTran","type":"tuple","indexed":false,"internalType":"struct ITaikoInbox.TransitionState","components":[{"name":"parentHash","type":"bytes32","internalType":"bytes32"},{"name":"blockHash","type":"bytes32","internalType":"bytes32"},{"name":"stateRoot","type":"bytes32","internalType":"bytes32"},{"name":"prover","type":"address","internalType":"address"},{"name":"inProvingWindow","type":"bool","internalType":"bool"},{"name":"createdAt","type":"uint48","internalType":"uint48"}]},{"name":"newTran","type":"tuple","indexed":false,"internalType":"struct ITaikoInbox.Transition","components":[{"name":"parentHash","type":"bytes32","internalType":"bytes32"},{"name":"blockHash","type":"bytes32","internalType":"bytes32"},{"name":"stateRoot","type":"bytes32","internalType":"bytes32"}]}],"anonymous":false},{"type":"event","name":"Stats1Updated","inputs":[{"name":"stats1","type":"tuple","indexed":false,"internalType":"struct ITaikoInbox.Stats1","components":[{"name":"genesisHeight","type":"uint64","internalType":"uint64"},{"name":"__reserved2","type":"uint64","internalType":"uint64"},{"name":"lastSyncedBatchId","type":"uint64","internalType":"uint64"},{"name":"lastSyncedAt","type":"uint64","internalType":"uint64"}]}],"anonymous":false},{"type":"event","name":"Stats2Updated","inputs":[{"name":"stats2","type":"tuple","indexed":false,"internalType":"struct ITaikoInbox.Stats2","components":[{"name":"numBatches","type":"uint64","internalType":"uint64"},{"name":"lastVerifiedBatchId","type":"uint64","internalType":"uint64"},{"name":"paused","type":"bool","internalType":"bool"},{"name":"lastProposedIn","type":"uint56","internalType":"uint56"},{"name":"lastUnpausedAt","type":"uint64","internalType":"uint64"}]}],"anonymous":false},{"type":"error","name":"AnchorBlockIdSmallerThanParent","inputs":[]},{"type":"error","name":"AnchorBlockIdTooLarge","inputs":[]},{"type":"error","name":"AnchorBlockIdTooSmall","inputs":[]},{"type":"error","name":"ArraySizesMismatch","inputs":[]},{"type":"error","name":"BatchNotFound","inputs":[]},{"type":"error","name":"BatchVerified","inputs":[]},{"type":"error","name":"BlobNotFound","inputs":[]},{"type":"error","name":"BlobNotSpecified","inputs":[]},{"type":"error","name":"BlockNotFound","inputs":[]},{"type":"error","name":"ContractPaused","inputs":[]},{"type":"error","name":"CustomProposerMissing","inputs":[]},{"type":"error","name":"CustomProposerNotAllowed","inputs":[]},{"type":"error","name":"EtherNotPaidAsBond","inputs":[]},{"type":"error","name":"ForkNotActivated","inputs":[]},{"type":"error","name":"InsufficientBond","inputs":[]},{"type":"error","name":"InvalidBlobCreatedIn","inputs":[]},{"type":"error","name":"InvalidBlobParams","inputs":[]},{"type":"error","name":"InvalidGenesisBlockHash","inputs":[]},{"type":"error","name":"InvalidParams","inputs":[]},{"type":"error","name":"InvalidTransitionBlockHash","inputs":[]},{"type":"error","name":"InvalidTransitionParentHash","inputs":[]},{"type":"error","name":"InvalidTransitionStateRoot","inputs":[]},{"type":"error","name":"MetaHashMismatch","inputs":[]},{"type":"error","name":"MsgValueNotZero","inputs":[]},{"type":"error","name":"NoBlocksToProve","inputs":[]},{"type":"error","name":"NotFirstProposal","inputs":[]},{"type":"error","name":"NotInboxWrapper","inputs":[]},{"type":"error","name":"ParentMetaHashMismatch","inputs":[]},{"type":"error","name":"SameTransition","inputs":[]},{"type":"error","name":"SignalNotSent","inputs":[]},{"type":"error","name":"TimestampSmallerThanParent","inputs":[]},{"type":"error","name":"TimestampTooLarge","inputs":[]},{"type":"error","name":"TimestampTooSmall","inputs":[]},{"type":"error","name":"TooManyBatches","inputs":[]},{"type":"error","name":"TooManyBlocks","inputs":[]},{"type":"error","name":"TooManySignals","inputs":[]},{"type":"error","name":"TransitionNotFound","inputs":[]},{"type":"error","name":"ZeroAnchorBlockHash","inputs":[]}],"bytecode":{"object":"0x","sourceMap":"","linkReferences":{}},"deployedBytecode":{"object":"0x","sourceMap":"","linkReferences":{}},"methodIdentifiers":{"bondBalanceOf(address)":"a9c2c835","bondToken()":"c28f4392","depositBond(uint256)":"4dcb05f9","getBatch(uint64)":"888775d9","getBatchVerifyingTransition(uint64)":"7e7501dc","getLastSyncedTransition()":"cee1136c","getLastVerifiedTransition()":"9c436473","getStats1()":"12ad809c","getStats2()":"26baca1c","getTransitionById(uint64,uint24)":"ff109f59","getTransitionByParentHash(uint64,bytes32)":"e8353dc0","pacayaConfig()":"b932bf2b","proposeBatch(bytes,bytes)":"47faad14","proveBatches(bytes,bytes)":"c9cc2843","withdrawBond(uint256)":"c3daab96"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.27+commit.40a35a09\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"AnchorBlockIdSmallerThanParent\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"AnchorBlockIdTooLarge\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"AnchorBlockIdTooSmall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ArraySizesMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BatchNotFound\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BatchVerified\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BlobNotFound\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BlobNotSpecified\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BlockNotFound\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ContractPaused\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CustomProposerMissing\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CustomProposerNotAllowed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EtherNotPaidAsBond\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ForkNotActivated\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientBond\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidBlobCreatedIn\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidBlobParams\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidGenesisBlockHash\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidParams\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTransitionBlockHash\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTransitionParentHash\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTransitionStateRoot\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MetaHashMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"MsgValueNotZero\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoBlocksToProve\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotFirstProposal\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotInboxWrapper\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ParentMetaHashMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SameTransition\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SignalNotSent\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TimestampSmallerThanParent\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TimestampTooLarge\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TimestampTooSmall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyBatches\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManyBlocks\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TooManySignals\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TransitionNotFound\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAnchorBlockHash\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"txsHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint16\",\"name\":\"numTransactions\",\"type\":\"uint16\"},{\"internalType\":\"uint8\",\"name\":\"timeShift\",\"type\":\"uint8\"},{\"internalType\":\"bytes32[]\",\"name\":\"signalSlots\",\"type\":\"bytes32[]\"}],\"internalType\":\"struct ITaikoInbox.BlockParams[]\",\"name\":\"blocks\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"blobHashes\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"extraData\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coinbase\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"proposedIn\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"blobCreatedIn\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"blobByteOffset\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"blobByteSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"lastBlockId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastBlockTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"anchorBlockId\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"anchorBlockHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"adjustmentQuotient\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"sharingPctg\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"gasIssuancePerSecond\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"minGasExcess\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"maxGasIssuancePerBlock\",\"type\":\"uint32\"}],\"internalType\":\"struct LibSharedData.BaseFeeConfig\",\"name\":\"baseFeeConfig\",\"type\":\"tuple\"}],\"indexed\":false,\"internalType\":\"struct ITaikoInbox.BatchInfo\",\"name\":\"info\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"infoHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"proposer\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"batchId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"proposedAt\",\"type\":\"uint64\"}],\"indexed\":false,\"internalType\":\"struct ITaikoInbox.BatchMetadata\",\"name\":\"meta\",\"type\":\"tuple\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"txList\",\"type\":\"bytes\"}],\"name\":\"BatchProposed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"verifier\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint64[]\",\"name\":\"batchIds\",\"type\":\"uint64[]\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"}],\"indexed\":false,\"internalType\":\"struct ITaikoInbox.Transition[]\",\"name\":\"transitions\",\"type\":\"tuple[]\"}],\"name\":\"BatchesProved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"batchId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"name\":\"BatchesVerified\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"BondCredited\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"BondDebited\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"BondDeposited\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"BondWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"batchId\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"inProvingWindow\",\"type\":\"bool\"},{\"internalType\":\"uint48\",\"name\":\"createdAt\",\"type\":\"uint48\"}],\"indexed\":false,\"internalType\":\"struct ITaikoInbox.TransitionState\",\"name\":\"oldTran\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"}],\"indexed\":false,\"internalType\":\"struct ITaikoInbox.Transition\",\"name\":\"newTran\",\"type\":\"tuple\"}],\"name\":\"ConflictingProof\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"genesisHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"__reserved2\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastSyncedBatchId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastSyncedAt\",\"type\":\"uint64\"}],\"indexed\":false,\"internalType\":\"struct ITaikoInbox.Stats1\",\"name\":\"stats1\",\"type\":\"tuple\"}],\"name\":\"Stats1Updated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"numBatches\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastVerifiedBatchId\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"paused\",\"type\":\"bool\"},{\"internalType\":\"uint56\",\"name\":\"lastProposedIn\",\"type\":\"uint56\"},{\"internalType\":\"uint64\",\"name\":\"lastUnpausedAt\",\"type\":\"uint64\"}],\"indexed\":false,\"internalType\":\"struct ITaikoInbox.Stats2\",\"name\":\"stats2\",\"type\":\"tuple\"}],\"name\":\"Stats2Updated\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_user\",\"type\":\"address\"}],\"name\":\"bondBalanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"bondToken\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"depositBond\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"_batchId\",\"type\":\"uint64\"}],\"name\":\"getBatch\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"metaHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"lastBlockId\",\"type\":\"uint64\"},{\"internalType\":\"uint96\",\"name\":\"reserved3\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"livenessBond\",\"type\":\"uint96\"},{\"internalType\":\"uint64\",\"name\":\"batchId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastBlockTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"anchorBlockId\",\"type\":\"uint64\"},{\"internalType\":\"uint24\",\"name\":\"nextTransitionId\",\"type\":\"uint24\"},{\"internalType\":\"uint8\",\"name\":\"reserved4\",\"type\":\"uint8\"},{\"internalType\":\"uint24\",\"name\":\"verifiedTransitionId\",\"type\":\"uint24\"}],\"internalType\":\"struct ITaikoInbox.Batch\",\"name\":\"batch_\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"_batchId\",\"type\":\"uint64\"}],\"name\":\"getBatchVerifyingTransition\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"inProvingWindow\",\"type\":\"bool\"},{\"internalType\":\"uint48\",\"name\":\"createdAt\",\"type\":\"uint48\"}],\"internalType\":\"struct ITaikoInbox.TransitionState\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLastSyncedTransition\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"batchId_\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"blockId_\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"inProvingWindow\",\"type\":\"bool\"},{\"internalType\":\"uint48\",\"name\":\"createdAt\",\"type\":\"uint48\"}],\"internalType\":\"struct ITaikoInbox.TransitionState\",\"name\":\"ts_\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLastVerifiedTransition\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"batchId_\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"blockId_\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"inProvingWindow\",\"type\":\"bool\"},{\"internalType\":\"uint48\",\"name\":\"createdAt\",\"type\":\"uint48\"}],\"internalType\":\"struct ITaikoInbox.TransitionState\",\"name\":\"ts_\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStats1\",\"outputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"genesisHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"__reserved2\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastSyncedBatchId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastSyncedAt\",\"type\":\"uint64\"}],\"internalType\":\"struct ITaikoInbox.Stats1\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStats2\",\"outputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"numBatches\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastVerifiedBatchId\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"paused\",\"type\":\"bool\"},{\"internalType\":\"uint56\",\"name\":\"lastProposedIn\",\"type\":\"uint56\"},{\"internalType\":\"uint64\",\"name\":\"lastUnpausedAt\",\"type\":\"uint64\"}],\"internalType\":\"struct ITaikoInbox.Stats2\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"_batchId\",\"type\":\"uint64\"},{\"internalType\":\"uint24\",\"name\":\"_tid\",\"type\":\"uint24\"}],\"name\":\"getTransitionById\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"inProvingWindow\",\"type\":\"bool\"},{\"internalType\":\"uint48\",\"name\":\"createdAt\",\"type\":\"uint48\"}],\"internalType\":\"struct ITaikoInbox.TransitionState\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"_batchId\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"_parentHash\",\"type\":\"bytes32\"}],\"name\":\"getTransitionByParentHash\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"inProvingWindow\",\"type\":\"bool\"},{\"internalType\":\"uint48\",\"name\":\"createdAt\",\"type\":\"uint48\"}],\"internalType\":\"struct ITaikoInbox.TransitionState\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pacayaConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"chainId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"maxUnverifiedBatches\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"batchRingBufferSize\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"maxBatchesToVerify\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"blockMaxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint96\",\"name\":\"livenessBondBase\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"livenessBondPerBlock\",\"type\":\"uint96\"},{\"internalType\":\"uint8\",\"name\":\"stateRootSyncInternal\",\"type\":\"uint8\"},{\"internalType\":\"uint64\",\"name\":\"maxAnchorHeightOffset\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"adjustmentQuotient\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"sharingPctg\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"gasIssuancePerSecond\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"minGasExcess\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"maxGasIssuancePerBlock\",\"type\":\"uint32\"}],\"internalType\":\"struct LibSharedData.BaseFeeConfig\",\"name\":\"baseFeeConfig\",\"type\":\"tuple\"},{\"internalType\":\"uint16\",\"name\":\"provingWindow\",\"type\":\"uint16\"},{\"internalType\":\"uint24\",\"name\":\"cooldownWindow\",\"type\":\"uint24\"},{\"internalType\":\"uint8\",\"name\":\"maxSignalsToReceive\",\"type\":\"uint8\"},{\"internalType\":\"uint16\",\"name\":\"maxBlocksPerBatch\",\"type\":\"uint16\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"ontake\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"pacaya\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"shasta\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"unzen\",\"type\":\"uint64\"}],\"internalType\":\"struct ITaikoInbox.ForkHeights\",\"name\":\"forkHeights\",\"type\":\"tuple\"}],\"internalType\":\"struct ITaikoInbox.Config\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_params\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"_txList\",\"type\":\"bytes\"}],\"name\":\"proposeBatch\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"txsHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint16\",\"name\":\"numTransactions\",\"type\":\"uint16\"},{\"internalType\":\"uint8\",\"name\":\"timeShift\",\"type\":\"uint8\"},{\"internalType\":\"bytes32[]\",\"name\":\"signalSlots\",\"type\":\"bytes32[]\"}],\"internalType\":\"struct ITaikoInbox.BlockParams[]\",\"name\":\"blocks\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"blobHashes\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"extraData\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coinbase\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"proposedIn\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"blobCreatedIn\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"blobByteOffset\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"blobByteSize\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"lastBlockId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastBlockTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"anchorBlockId\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"anchorBlockHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"adjustmentQuotient\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"sharingPctg\",\"type\":\"uint8\"},{\"internalType\":\"uint32\",\"name\":\"gasIssuancePerSecond\",\"type\":\"uint32\"},{\"internalType\":\"uint64\",\"name\":\"minGasExcess\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"maxGasIssuancePerBlock\",\"type\":\"uint32\"}],\"internalType\":\"struct LibSharedData.BaseFeeConfig\",\"name\":\"baseFeeConfig\",\"type\":\"tuple\"}],\"internalType\":\"struct ITaikoInbox.BatchInfo\",\"name\":\"info_\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"infoHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"proposer\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"batchId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"proposedAt\",\"type\":\"uint64\"}],\"internalType\":\"struct ITaikoInbox.BatchMetadata\",\"name\":\"meta_\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_params\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"_proof\",\"type\":\"bytes\"}],\"name\":\"proveBatches\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"withdrawBond\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"custom:security-contact\":\"security@taiko.xyz\",\"details\":\"Registered in the address resolver as \\\"taiko\\\".\",\"events\":{\"BatchProposed((bytes32,(uint16,uint8,bytes32[])[],bytes32[],bytes32,address,uint64,uint64,uint32,uint32,uint32,uint64,uint64,uint64,bytes32,(uint8,uint8,uint32,uint64,uint32)),(bytes32,address,uint64,uint64),bytes)\":{\"params\":{\"info\":\"The info of the proposed batch.\",\"meta\":\"The metadata of the proposed batch.\",\"txList\":\"The tx list in calldata.\"}},\"BatchesProved(address,uint64[],(bytes32,bytes32,bytes32)[])\":{\"params\":{\"transitions\":\"The transitions data.\",\"verifier\":\"The address of the verifier.\"}},\"BatchesVerified(uint64,bytes32)\":{\"params\":{\"batchId\":\"The ID of the verified batch.\",\"blockHash\":\"The hash of the verified batch.\"}},\"BondCredited(address,uint256)\":{\"params\":{\"amount\":\"The amount of tokens credited.\",\"user\":\"The address of the user whose bond balance is credited.\"}},\"BondDebited(address,uint256)\":{\"params\":{\"amount\":\"The amount of tokens debited.\",\"user\":\"The address of the user whose bond balance is debited.\"}},\"BondDeposited(address,uint256)\":{\"params\":{\"amount\":\"The amount of tokens deposited.\",\"user\":\"The address of the user who deposited the tokens.\"}},\"BondWithdrawn(address,uint256)\":{\"params\":{\"amount\":\"The amount of tokens withdrawn.\",\"user\":\"The address of the user who withdrew the tokens.\"}},\"ConflictingProof(uint64,(bytes32,bytes32,bytes32,address,bool,uint48),(bytes32,bytes32,bytes32))\":{\"params\":{\"batchId\":\"The batch ID.\",\"newTran\":\"The new transition.\",\"oldTran\":\"The old transition overwritten.\"}},\"Stats1Updated((uint64,uint64,uint64,uint64))\":{\"params\":{\"stats1\":\"The Stats1 data structure.\"}},\"Stats2Updated((uint64,uint64,bool,uint56,uint64))\":{\"params\":{\"stats2\":\"The Stats2 data structure.\"}}},\"kind\":\"dev\",\"methods\":{\"bondBalanceOf(address)\":{\"params\":{\"_user\":\"The address of the user.\"},\"returns\":{\"_0\":\"The TAIKO token balance of the user.\"}},\"bondToken()\":{\"returns\":{\"_0\":\"The Bond token address.\"}},\"depositBond(uint256)\":{\"params\":{\"_amount\":\"The amount of TAIKO tokens to deposit.\"}},\"getBatch(uint64)\":{\"params\":{\"_batchId\":\"The ID of the batch to retrieve.\"},\"returns\":{\"batch_\":\"The batch data.\"}},\"getBatchVerifyingTransition(uint64)\":{\"params\":{\"_batchId\":\"The batch ID.\"},\"returns\":{\"_0\":\"The transition used for verifying the batch.\"}},\"getLastSyncedTransition()\":{\"returns\":{\"batchId_\":\"The batch ID of the last synced transition.\",\"blockId_\":\"The block ID of the last synced block.\",\"ts_\":\"The last synced transition.\"}},\"getLastVerifiedTransition()\":{\"returns\":{\"batchId_\":\"The batch ID of the last verified transition.\",\"blockId_\":\"The block ID of the last verified block.\",\"ts_\":\"The last verified transition.\"}},\"getStats1()\":{\"returns\":{\"_0\":\"Stats1 structure containing the statistics.\"}},\"getStats2()\":{\"returns\":{\"_0\":\"Stats2 structure containing the statistics.\"}},\"getTransitionById(uint64,uint24)\":{\"params\":{\"_batchId\":\"The batch ID.\",\"_tid\":\"The transition ID.\"},\"returns\":{\"_0\":\"The specified transition state.\"}},\"getTransitionByParentHash(uint64,bytes32)\":{\"params\":{\"_batchId\":\"The batch ID.\",\"_parentHash\":\"The parent hash.\"},\"returns\":{\"_0\":\"The specified transition state.\"}},\"pacayaConfig()\":{\"returns\":{\"_0\":\"The current configuration.\"}},\"proposeBatch(bytes,bytes)\":{\"params\":{\"_params\":\"ABI-encoded parameters.\",\"_txList\":\"The transaction list in calldata. If the txList is empty, blob will be used for data availability.\"},\"returns\":{\"info_\":\"The info of the proposed batch.\",\"meta_\":\"The metadata of the proposed batch.\"}},\"proveBatches(bytes,bytes)\":{\"params\":{\"_params\":\"ABI-encoded parameter containing: - metas: Array of metadata for each batch being proved. - transitions: Array of batch transitions to be proved.\",\"_proof\":\"The aggregated cryptographic proof proving the batches transitions.\"}},\"withdrawBond(uint256)\":{\"params\":{\"_amount\":\"The amount of TAIKO tokens to withdraw.\"}}},\"title\":\"TaikoInbox\",\"version\":1},\"userdoc\":{\"events\":{\"BatchProposed((bytes32,(uint16,uint8,bytes32[])[],bytes32[],bytes32,address,uint64,uint64,uint32,uint32,uint32,uint64,uint64,uint64,bytes32,(uint8,uint8,uint32,uint64,uint32)),(bytes32,address,uint64,uint64),bytes)\":{\"notice\":\"Emitted when a batch is proposed.\"},\"BatchesProved(address,uint64[],(bytes32,bytes32,bytes32)[])\":{\"notice\":\"Emitted when multiple transitions are proved.\"},\"BatchesVerified(uint64,bytes32)\":{\"notice\":\"Emitted when a batch is verified.\"},\"BondCredited(address,uint256)\":{\"notice\":\"Emitted when a token is credited back to a user's bond balance.\"},\"BondDebited(address,uint256)\":{\"notice\":\"Emitted when a token is debited from a user's bond balance.\"},\"BondDeposited(address,uint256)\":{\"notice\":\"Emitted when tokens are deposited into a user's bond balance.\"},\"BondWithdrawn(address,uint256)\":{\"notice\":\"Emitted when tokens are withdrawn from a user's bond balance.\"},\"ConflictingProof(uint64,(bytes32,bytes32,bytes32,address,bool,uint48),(bytes32,bytes32,bytes32))\":{\"notice\":\"Emitted when a transition is overwritten by a conflicting one with the same parent hash but different block hash or state root.\"},\"Stats1Updated((uint64,uint64,uint64,uint64))\":{\"notice\":\"Emitted when a batch is synced.\"},\"Stats2Updated((uint64,uint64,bool,uint56,uint64))\":{\"notice\":\"Emitted when some state variable values changed.\"}},\"kind\":\"user\",\"methods\":{\"bondBalanceOf(address)\":{\"notice\":\"Returns the TAIKO token balance of a specific user.\"},\"bondToken()\":{\"notice\":\"Retrieves the Bond token address. If Ether is used as bond, this function returns address(0).\"},\"depositBond(uint256)\":{\"notice\":\"Deposits TAIKO tokens into the contract to be used as liveness bond.\"},\"getBatch(uint64)\":{\"notice\":\"Retrieves data about a specific batch.\"},\"getBatchVerifyingTransition(uint64)\":{\"notice\":\"Retrieves the transition used for verifying a batch.\"},\"getLastSyncedTransition()\":{\"notice\":\"Retrieves the transition used for the last synced batch.\"},\"getLastVerifiedTransition()\":{\"notice\":\"Retrieves the transition used for the last verified batch.\"},\"getStats1()\":{\"notice\":\"Retrieves the first set of protocol statistics.\"},\"getStats2()\":{\"notice\":\"Retrieves the second set of protocol statistics.\"},\"getTransitionById(uint64,uint24)\":{\"notice\":\"Retrieves a specific transition by batch ID and transition ID. This function may revert if the transition is not found.\"},\"getTransitionByParentHash(uint64,bytes32)\":{\"notice\":\"Retrieves a specific transition by batch ID and parent Hash. This function may revert if the transition is not found.\"},\"pacayaConfig()\":{\"notice\":\"Retrieves the current protocol configuration.\"},\"proposeBatch(bytes,bytes)\":{\"notice\":\"Proposes a batch of blocks.\"},\"proveBatches(bytes,bytes)\":{\"notice\":\"Proves state transitions for multiple batches with a single aggregated proof.\"},\"withdrawBond(uint256)\":{\"notice\":\"Withdraws a specified amount of TAIKO tokens from the contract.\"}},\"notice\":\"Acts as the inbox for the Taiko Alethia protocol, a simplified version of the original Taiko-Based Contestable Rollup (BCR). The tier-based proof system and contestation mechanisms have been removed. Key assumptions of this protocol: - Block proposals and proofs are asynchronous. Proofs are not available at proposal time, unlike Taiko Gwyneth, which assumes synchronous composability. - Proofs are presumed error-free and thoroughly validated, with proof type management delegated to IVerifier contracts.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/layer1/based/ITaikoInbox.sol\":\"ITaikoInbox\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@openzeppelin-upgrades/contracts/=node_modules/@openzeppelin/contracts-upgradeable/\",\":@openzeppelin/=node_modules/@openzeppelin/\",\":@optimism/=node_modules/optimism/\",\":@p256-verifier/contracts/=node_modules/p256-verifier/src/\",\":@risc0/contracts/=node_modules/risc0-ethereum/contracts/src/\",\":@solady/=node_modules/solady/\",\":@sp1-contracts/=node_modules/sp1-contracts/contracts/\",\":ds-test/=node_modules/ds-test/src/\",\":eigenlayer-contracts/=node_modules/eigenlayer-contracts/\",\":eigenlayer-middleware/=node_modules/eigenlayer-middleware/\",\":forge-std/=node_modules/forge-std/\",\":openzeppelin/=node_modules/@openzeppelin/\",\":optimism/=node_modules/optimism/\",\":p256-verifier/=node_modules/p256-verifier/\",\":risc0-ethereum/=node_modules/risc0-ethereum/\",\":solady/=node_modules/solady/\",\":sp1-contracts/=node_modules/sp1-contracts/\",\":src/=contracts/\"]},\"sources\":{\"contracts/layer1/based/ITaikoInbox.sol\":{\"keccak256\":\"0xc7ce38878c60bff1ae7979d5daa8bb7c6d2b2945551663150ecd8c3eb90dc430\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8e245de7383f8da0ceca0c237b9c2ea1f1fd35eb3d05c04553187e8c25099232\",\"dweb:/ipfs/QmeD7QjG23rdosWLCTTh2pGTfySttySKkvsMdaKPJ45WmW\"]},\"contracts/shared/based/LibSharedData.sol\":{\"keccak256\":\"0x68967df1d6c504e78f6e84752b855ed2fd0e1288cb2be765219b803c2df0a5a6\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8c447c0f3b50be9310b41156e9b0f500f838109e4083ca31eefdfbd80cf1f1ff\",\"dweb:/ipfs/QmdzRwJabMHwkfKa3d6bfN6CLxLfjPuau7nVQKW8uDf1T8\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.27+commit.40a35a09"},"language":"Solidity","output":{"abi":[{"inputs":[],"type":"error","name":"AnchorBlockIdSmallerThanParent"},{"inputs":[],"type":"error","name":"AnchorBlockIdTooLarge"},{"inputs":[],"type":"error","name":"AnchorBlockIdTooSmall"},{"inputs":[],"type":"error","name":"ArraySizesMismatch"},{"inputs":[],"type":"error","name":"BatchNotFound"},{"inputs":[],"type":"error","name":"BatchVerified"},{"inputs":[],"type":"error","name":"BlobNotFound"},{"inputs":[],"type":"error","name":"BlobNotSpecified"},{"inputs":[],"type":"error","name":"BlockNotFound"},{"inputs":[],"type":"error","name":"ContractPaused"},{"inputs":[],"type":"error","name":"CustomProposerMissing"},{"inputs":[],"type":"error","name":"CustomProposerNotAllowed"},{"inputs":[],"type":"error","name":"EtherNotPaidAsBond"},{"inputs":[],"type":"error","name":"ForkNotActivated"},{"inputs":[],"type":"error","name":"InsufficientBond"},{"inputs":[],"type":"error","name":"InvalidBlobCreatedIn"},{"inputs":[],"type":"error","name":"InvalidBlobParams"},{"inputs":[],"type":"error","name":"InvalidGenesisBlockHash"},{"inputs":[],"type":"error","name":"InvalidParams"},{"inputs":[],"type":"error","name":"InvalidTransitionBlockHash"},{"inputs":[],"type":"error","name":"InvalidTransitionParentHash"},{"inputs":[],"type":"error","name":"InvalidTransitionStateRoot"},{"inputs":[],"type":"error","name":"MetaHashMismatch"},{"inputs":[],"type":"error","name":"MsgValueNotZero"},{"inputs":[],"type":"error","name":"NoBlocksToProve"},{"inputs":[],"type":"error","name":"NotFirstProposal"},{"inputs":[],"type":"error","name":"NotInboxWrapper"},{"inputs":[],"type":"error","name":"ParentMetaHashMismatch"},{"inputs":[],"type":"error","name":"SameTransition"},{"inputs":[],"type":"error","name":"SignalNotSent"},{"inputs":[],"type":"error","name":"TimestampSmallerThanParent"},{"inputs":[],"type":"error","name":"TimestampTooLarge"},{"inputs":[],"type":"error","name":"TimestampTooSmall"},{"inputs":[],"type":"error","name":"TooManyBatches"},{"inputs":[],"type":"error","name":"TooManyBlocks"},{"inputs":[],"type":"error","name":"TooManySignals"},{"inputs":[],"type":"error","name":"TransitionNotFound"},{"inputs":[],"type":"error","name":"ZeroAnchorBlockHash"},{"inputs":[{"internalType":"struct ITaikoInbox.BatchInfo","name":"info","type":"tuple","components":[{"internalType":"bytes32","name":"txsHash","type":"bytes32"},{"internalType":"struct ITaikoInbox.BlockParams[]","name":"blocks","type":"tuple[]","components":[{"internalType":"uint16","name":"numTransactions","type":"uint16"},{"internalType":"uint8","name":"timeShift","type":"uint8"},{"internalType":"bytes32[]","name":"signalSlots","type":"bytes32[]"}]},{"internalType":"bytes32[]","name":"blobHashes","type":"bytes32[]"},{"internalType":"bytes32","name":"extraData","type":"bytes32"},{"internalType":"address","name":"coinbase","type":"address"},{"internalType":"uint64","name":"proposedIn","type":"uint64"},{"internalType":"uint64","name":"blobCreatedIn","type":"uint64"},{"internalType":"uint32","name":"blobByteOffset","type":"uint32"},{"internalType":"uint32","name":"blobByteSize","type":"uint32"},{"internalType":"uint32","name":"gasLimit","type":"uint32"},{"internalType":"uint64","name":"lastBlockId","type":"uint64"},{"internalType":"uint64","name":"lastBlockTimestamp","type":"uint64"},{"internalType":"uint64","name":"anchorBlockId","type":"uint64"},{"internalType":"bytes32","name":"anchorBlockHash","type":"bytes32"},{"internalType":"struct LibSharedData.BaseFeeConfig","name":"baseFeeConfig","type":"tuple","components":[{"internalType":"uint8","name":"adjustmentQuotient","type":"uint8"},{"internalType":"uint8","name":"sharingPctg","type":"uint8"},{"internalType":"uint32","name":"gasIssuancePerSecond","type":"uint32"},{"internalType":"uint64","name":"minGasExcess","type":"uint64"},{"internalType":"uint32","name":"maxGasIssuancePerBlock","type":"uint32"}]}],"indexed":false},{"internalType":"struct ITaikoInbox.BatchMetadata","name":"meta","type":"tuple","components":[{"internalType":"bytes32","name":"infoHash","type":"bytes32"},{"internalType":"address","name":"proposer","type":"address"},{"internalType":"uint64","name":"batchId","type":"uint64"},{"internalType":"uint64","name":"proposedAt","type":"uint64"}],"indexed":false},{"internalType":"bytes","name":"txList","type":"bytes","indexed":false}],"type":"event","name":"BatchProposed","anonymous":false},{"inputs":[{"internalType":"address","name":"verifier","type":"address","indexed":false},{"internalType":"uint64[]","name":"batchIds","type":"uint64[]","indexed":false},{"internalType":"struct ITaikoInbox.Transition[]","name":"transitions","type":"tuple[]","components":[{"internalType":"bytes32","name":"parentHash","type":"bytes32"},{"internalType":"bytes32","name":"blockHash","type":"bytes32"},{"internalType":"bytes32","name":"stateRoot","type":"bytes32"}],"indexed":false}],"type":"event","name":"BatchesProved","anonymous":false},{"inputs":[{"internalType":"uint64","name":"batchId","type":"uint64","indexed":false},{"internalType":"bytes32","name":"blockHash","type":"bytes32","indexed":false}],"type":"event","name":"BatchesVerified","anonymous":false},{"inputs":[{"internalType":"address","name":"user","type":"address","indexed":true},{"internalType":"uint256","name":"amount","type":"uint256","indexed":false}],"type":"event","name":"BondCredited","anonymous":false},{"inputs":[{"internalType":"address","name":"user","type":"address","indexed":true},{"internalType":"uint256","name":"amount","type":"uint256","indexed":false}],"type":"event","name":"BondDebited","anonymous":false},{"inputs":[{"internalType":"address","name":"user","type":"address","indexed":true},{"internalType":"uint256","name":"amount","type":"uint256","indexed":false}],"type":"event","name":"BondDeposited","anonymous":false},{"inputs":[{"internalType":"address","name":"user","type":"address","indexed":true},{"internalType":"uint256","name":"amount","type":"uint256","indexed":false}],"type":"event","name":"BondWithdrawn","anonymous":false},{"inputs":[{"internalType":"uint64","name":"batchId","type":"uint64","indexed":false},{"internalType":"struct ITaikoInbox.TransitionState","name":"oldTran","type":"tuple","components":[{"internalType":"bytes32","name":"parentHash","type":"bytes32"},{"internalType":"bytes32","name":"blockHash","type":"bytes32"},{"internalType":"bytes32","name":"stateRoot","type":"bytes32"},{"internalType":"address","name":"prover","type":"address"},{"internalType":"bool","name":"inProvingWindow","type":"bool"},{"internalType":"uint48","name":"createdAt","type":"uint48"}],"indexed":false},{"internalType":"struct ITaikoInbox.Transition","name":"newTran","type":"tuple","components":[{"internalType":"bytes32","name":"parentHash","type":"bytes32"},{"internalType":"bytes32","name":"blockHash","type":"bytes32"},{"internalType":"bytes32","name":"stateRoot","type":"bytes32"}],"indexed":false}],"type":"event","name":"ConflictingProof","anonymous":false},{"inputs":[{"internalType":"struct ITaikoInbox.Stats1","name":"stats1","type":"tuple","components":[{"internalType":"uint64","name":"genesisHeight","type":"uint64"},{"internalType":"uint64","name":"__reserved2","type":"uint64"},{"internalType":"uint64","name":"lastSyncedBatchId","type":"uint64"},{"internalType":"uint64","name":"lastSyncedAt","type":"uint64"}],"indexed":false}],"type":"event","name":"Stats1Updated","anonymous":false},{"inputs":[{"internalType":"struct ITaikoInbox.Stats2","name":"stats2","type":"tuple","components":[{"internalType":"uint64","name":"numBatches","type":"uint64"},{"internalType":"uint64","name":"lastVerifiedBatchId","type":"uint64"},{"internalType":"bool","name":"paused","type":"bool"},{"internalType":"uint56","name":"lastProposedIn","type":"uint56"},{"internalType":"uint64","name":"lastUnpausedAt","type":"uint64"}],"indexed":false}],"type":"event","name":"Stats2Updated","anonymous":false},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"stateMutability":"view","type":"function","name":"bondBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"bondToken","outputs":[{"internalType":"address","name":"","type":"address"}]},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"stateMutability":"payable","type":"function","name":"depositBond"},{"inputs":[{"internalType":"uint64","name":"_batchId","type":"uint64"}],"stateMutability":"view","type":"function","name":"getBatch","outputs":[{"internalType":"struct ITaikoInbox.Batch","name":"batch_","type":"tuple","components":[{"internalType":"bytes32","name":"metaHash","type":"bytes32"},{"internalType":"uint64","name":"lastBlockId","type":"uint64"},{"internalType":"uint96","name":"reserved3","type":"uint96"},{"internalType":"uint96","name":"livenessBond","type":"uint96"},{"internalType":"uint64","name":"batchId","type":"uint64"},{"internalType":"uint64","name":"lastBlockTimestamp","type":"uint64"},{"internalType":"uint64","name":"anchorBlockId","type":"uint64"},{"internalType":"uint24","name":"nextTransitionId","type":"uint24"},{"internalType":"uint8","name":"reserved4","type":"uint8"},{"internalType":"uint24","name":"verifiedTransitionId","type":"uint24"}]}]},{"inputs":[{"internalType":"uint64","name":"_batchId","type":"uint64"}],"stateMutability":"view","type":"function","name":"getBatchVerifyingTransition","outputs":[{"internalType":"struct ITaikoInbox.TransitionState","name":"","type":"tuple","components":[{"internalType":"bytes32","name":"parentHash","type":"bytes32"},{"internalType":"bytes32","name":"blockHash","type":"bytes32"},{"internalType":"bytes32","name":"stateRoot","type":"bytes32"},{"internalType":"address","name":"prover","type":"address"},{"internalType":"bool","name":"inProvingWindow","type":"bool"},{"internalType":"uint48","name":"createdAt","type":"uint48"}]}]},{"inputs":[],"stateMutability":"view","type":"function","name":"getLastSyncedTransition","outputs":[{"internalType":"uint64","name":"batchId_","type":"uint64"},{"internalType":"uint64","name":"blockId_","type":"uint64"},{"internalType":"struct ITaikoInbox.TransitionState","name":"ts_","type":"tuple","components":[{"internalType":"bytes32","name":"parentHash","type":"bytes32"},{"internalType":"bytes32","name":"blockHash","type":"bytes32"},{"internalType":"bytes32","name":"stateRoot","type":"bytes32"},{"internalType":"address","name":"prover","type":"address"},{"internalType":"bool","name":"inProvingWindow","type":"bool"},{"internalType":"uint48","name":"createdAt","type":"uint48"}]}]},{"inputs":[],"stateMutability":"view","type":"function","name":"getLastVerifiedTransition","outputs":[{"internalType":"uint64","name":"batchId_","type":"uint64"},{"internalType":"uint64","name":"blockId_","type":"uint64"},{"internalType":"struct ITaikoInbox.TransitionState","name":"ts_","type":"tuple","components":[{"internalType":"bytes32","name":"parentHash","type":"bytes32"},{"internalType":"bytes32","name":"blockHash","type":"bytes32"},{"internalType":"bytes32","name":"stateRoot","type":"bytes32"},{"internalType":"address","name":"prover","type":"address"},{"internalType":"bool","name":"inProvingWindow","type":"bool"},{"internalType":"uint48","name":"createdAt","type":"uint48"}]}]},{"inputs":[],"stateMutability":"view","type":"function","name":"getStats1","outputs":[{"internalType":"struct ITaikoInbox.Stats1","name":"","type":"tuple","components":[{"internalType":"uint64","name":"genesisHeight","type":"uint64"},{"internalType":"uint64","name":"__reserved2","type":"uint64"},{"internalType":"uint64","name":"lastSyncedBatchId","type":"uint64"},{"internalType":"uint64","name":"lastSyncedAt","type":"uint64"}]}]},{"inputs":[],"stateMutability":"view","type":"function","name":"getStats2","outputs":[{"internalType":"struct ITaikoInbox.Stats2","name":"","type":"tuple","components":[{"internalType":"uint64","name":"numBatches","type":"uint64"},{"internalType":"uint64","name":"lastVerifiedBatchId","type":"uint64"},{"internalType":"bool","name":"paused","type":"bool"},{"internalType":"uint56","name":"lastProposedIn","type":"uint56"},{"internalType":"uint64","name":"lastUnpausedAt","type":"uint64"}]}]},{"inputs":[{"internalType":"uint64","name":"_batchId","type":"uint64"},{"internalType":"uint24","name":"_tid","type":"uint24"}],"stateMutability":"view","type":"function","name":"getTransitionById","outputs":[{"internalType":"struct ITaikoInbox.TransitionState","name":"","type":"tuple","components":[{"internalType":"bytes32","name":"parentHash","type":"bytes32"},{"internalType":"bytes32","name":"blockHash","type":"bytes32"},{"internalType":"bytes32","name":"stateRoot","type":"bytes32"},{"internalType":"address","name":"prover","type":"address"},{"internalType":"bool","name":"inProvingWindow","type":"bool"},{"internalType":"uint48","name":"createdAt","type":"uint48"}]}]},{"inputs":[{"internalType":"uint64","name":"_batchId","type":"uint64"},{"internalType":"bytes32","name":"_parentHash","type":"bytes32"}],"stateMutability":"view","type":"function","name":"getTransitionByParentHash","outputs":[{"internalType":"struct ITaikoInbox.TransitionState","name":"","type":"tuple","components":[{"internalType":"bytes32","name":"parentHash","type":"bytes32"},{"internalType":"bytes32","name":"blockHash","type":"bytes32"},{"internalType":"bytes32","name":"stateRoot","type":"bytes32"},{"internalType":"address","name":"prover","type":"address"},{"internalType":"bool","name":"inProvingWindow","type":"bool"},{"internalType":"uint48","name":"createdAt","type":"uint48"}]}]},{"inputs":[],"stateMutability":"view","type":"function","name":"pacayaConfig","outputs":[{"internalType":"struct ITaikoInbox.Config","name":"","type":"tuple","components":[{"internalType":"uint64","name":"chainId","type":"uint64"},{"internalType":"uint64","name":"maxUnverifiedBatches","type":"uint64"},{"internalType":"uint64","name":"batchRingBufferSize","type":"uint64"},{"internalType":"uint64","name":"maxBatchesToVerify","type":"uint64"},{"internalType":"uint32","name":"blockMaxGasLimit","type":"uint32"},{"internalType":"uint96","name":"livenessBondBase","type":"uint96"},{"internalType":"uint96","name":"livenessBondPerBlock","type":"uint96"},{"internalType":"uint8","name":"stateRootSyncInternal","type":"uint8"},{"internalType":"uint64","name":"maxAnchorHeightOffset","type":"uint64"},{"internalType":"struct LibSharedData.BaseFeeConfig","name":"baseFeeConfig","type":"tuple","components":[{"internalType":"uint8","name":"adjustmentQuotient","type":"uint8"},{"internalType":"uint8","name":"sharingPctg","type":"uint8"},{"internalType":"uint32","name":"gasIssuancePerSecond","type":"uint32"},{"internalType":"uint64","name":"minGasExcess","type":"uint64"},{"internalType":"uint32","name":"maxGasIssuancePerBlock","type":"uint32"}]},{"internalType":"uint16","name":"provingWindow","type":"uint16"},{"internalType":"uint24","name":"cooldownWindow","type":"uint24"},{"internalType":"uint8","name":"maxSignalsToReceive","type":"uint8"},{"internalType":"uint16","name":"maxBlocksPerBatch","type":"uint16"},{"internalType":"struct ITaikoInbox.ForkHeights","name":"forkHeights","type":"tuple","components":[{"internalType":"uint64","name":"ontake","type":"uint64"},{"internalType":"uint64","name":"pacaya","type":"uint64"},{"internalType":"uint64","name":"shasta","type":"uint64"},{"internalType":"uint64","name":"unzen","type":"uint64"}]}]}]},{"inputs":[{"internalType":"bytes","name":"_params","type":"bytes"},{"internalType":"bytes","name":"_txList","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"proposeBatch","outputs":[{"internalType":"struct ITaikoInbox.BatchInfo","name":"info_","type":"tuple","components":[{"internalType":"bytes32","name":"txsHash","type":"bytes32"},{"internalType":"struct ITaikoInbox.BlockParams[]","name":"blocks","type":"tuple[]","components":[{"internalType":"uint16","name":"numTransactions","type":"uint16"},{"internalType":"uint8","name":"timeShift","type":"uint8"},{"internalType":"bytes32[]","name":"signalSlots","type":"bytes32[]"}]},{"internalType":"bytes32[]","name":"blobHashes","type":"bytes32[]"},{"internalType":"bytes32","name":"extraData","type":"bytes32"},{"internalType":"address","name":"coinbase","type":"address"},{"internalType":"uint64","name":"proposedIn","type":"uint64"},{"internalType":"uint64","name":"blobCreatedIn","type":"uint64"},{"internalType":"uint32","name":"blobByteOffset","type":"uint32"},{"internalType":"uint32","name":"blobByteSize","type":"uint32"},{"internalType":"uint32","name":"gasLimit","type":"uint32"},{"internalType":"uint64","name":"lastBlockId","type":"uint64"},{"internalType":"uint64","name":"lastBlockTimestamp","type":"uint64"},{"internalType":"uint64","name":"anchorBlockId","type":"uint64"},{"internalType":"bytes32","name":"anchorBlockHash","type":"bytes32"},{"internalType":"struct LibSharedData.BaseFeeConfig","name":"baseFeeConfig","type":"tuple","components":[{"internalType":"uint8","name":"adjustmentQuotient","type":"uint8"},{"internalType":"uint8","name":"sharingPctg","type":"uint8"},{"internalType":"uint32","name":"gasIssuancePerSecond","type":"uint32"},{"internalType":"uint64","name":"minGasExcess","type":"uint64"},{"internalType":"uint32","name":"maxGasIssuancePerBlock","type":"uint32"}]}]},{"internalType":"struct ITaikoInbox.BatchMetadata","name":"meta_","type":"tuple","components":[{"internalType":"bytes32","name":"infoHash","type":"bytes32"},{"internalType":"address","name":"proposer","type":"address"},{"internalType":"uint64","name":"batchId","type":"uint64"},{"internalType":"uint64","name":"proposedAt","type":"uint64"}]}]},{"inputs":[{"internalType":"bytes","name":"_params","type":"bytes"},{"internalType":"bytes","name":"_proof","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"proveBatches"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function","name":"withdrawBond"}],"devdoc":{"kind":"dev","methods":{"bondBalanceOf(address)":{"params":{"_user":"The address of the user."},"returns":{"_0":"The TAIKO token balance of the user."}},"bondToken()":{"returns":{"_0":"The Bond token address."}},"depositBond(uint256)":{"params":{"_amount":"The amount of TAIKO tokens to deposit."}},"getBatch(uint64)":{"params":{"_batchId":"The ID of the batch to retrieve."},"returns":{"batch_":"The batch data."}},"getBatchVerifyingTransition(uint64)":{"params":{"_batchId":"The batch ID."},"returns":{"_0":"The transition used for verifying the batch."}},"getLastSyncedTransition()":{"returns":{"batchId_":"The batch ID of the last synced transition.","blockId_":"The block ID of the last synced block.","ts_":"The last synced transition."}},"getLastVerifiedTransition()":{"returns":{"batchId_":"The batch ID of the last verified transition.","blockId_":"The block ID of the last verified block.","ts_":"The last verified transition."}},"getStats1()":{"returns":{"_0":"Stats1 structure containing the statistics."}},"getStats2()":{"returns":{"_0":"Stats2 structure containing the statistics."}},"getTransitionById(uint64,uint24)":{"params":{"_batchId":"The batch ID.","_tid":"The transition ID."},"returns":{"_0":"The specified transition state."}},"getTransitionByParentHash(uint64,bytes32)":{"params":{"_batchId":"The batch ID.","_parentHash":"The parent hash."},"returns":{"_0":"The specified transition state."}},"pacayaConfig()":{"returns":{"_0":"The current configuration."}},"proposeBatch(bytes,bytes)":{"params":{"_params":"ABI-encoded parameters.","_txList":"The transaction list in calldata. If the txList is empty, blob will be used for data availability."},"returns":{"info_":"The info of the proposed batch.","meta_":"The metadata of the proposed batch."}},"proveBatches(bytes,bytes)":{"params":{"_params":"ABI-encoded parameter containing: - metas: Array of metadata for each batch being proved. - transitions: Array of batch transitions to be proved.","_proof":"The aggregated cryptographic proof proving the batches transitions."}},"withdrawBond(uint256)":{"params":{"_amount":"The amount of TAIKO tokens to withdraw."}}},"version":1},"userdoc":{"kind":"user","methods":{"bondBalanceOf(address)":{"notice":"Returns the TAIKO token balance of a specific user."},"bondToken()":{"notice":"Retrieves the Bond token address. If Ether is used as bond, this function returns address(0)."},"depositBond(uint256)":{"notice":"Deposits TAIKO tokens into the contract to be used as liveness bond."},"getBatch(uint64)":{"notice":"Retrieves data about a specific batch."},"getBatchVerifyingTransition(uint64)":{"notice":"Retrieves the transition used for verifying a batch."},"getLastSyncedTransition()":{"notice":"Retrieves the transition used for the last synced batch."},"getLastVerifiedTransition()":{"notice":"Retrieves the transition used for the last verified batch."},"getStats1()":{"notice":"Retrieves the first set of protocol statistics."},"getStats2()":{"notice":"Retrieves the second set of protocol statistics."},"getTransitionById(uint64,uint24)":{"notice":"Retrieves a specific transition by batch ID and transition ID. This function may revert if the transition is not found."},"getTransitionByParentHash(uint64,bytes32)":{"notice":"Retrieves a specific transition by batch ID and parent Hash. This function may revert if the transition is not found."},"pacayaConfig()":{"notice":"Retrieves the current protocol configuration."},"proposeBatch(bytes,bytes)":{"notice":"Proposes a batch of blocks."},"proveBatches(bytes,bytes)":{"notice":"Proves state transitions for multiple batches with a single aggregated proof."},"withdrawBond(uint256)":{"notice":"Withdraws a specified amount of TAIKO tokens from the contract."}},"version":1}},"settings":{"remappings":["@openzeppelin-upgrades/contracts/=node_modules/@openzeppelin/contracts-upgradeable/","@openzeppelin/=node_modules/@openzeppelin/","@optimism/=node_modules/optimism/","@p256-verifier/contracts/=node_modules/p256-verifier/src/","@risc0/contracts/=node_modules/risc0-ethereum/contracts/src/","@solady/=node_modules/solady/","@sp1-contracts/=node_modules/sp1-contracts/contracts/","ds-test/=node_modules/ds-test/src/","eigenlayer-contracts/=node_modules/eigenlayer-contracts/","eigenlayer-middleware/=node_modules/eigenlayer-middleware/","forge-std/=node_modules/forge-std/","openzeppelin/=node_modules/@openzeppelin/","optimism/=node_modules/optimism/","p256-verifier/=node_modules/p256-verifier/","risc0-ethereum/=node_modules/risc0-ethereum/","solady/=node_modules/solady/","sp1-contracts/=node_modules/sp1-contracts/","src/=contracts/"],"optimizer":{"enabled":true,"runs":200},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"contracts/layer1/based/ITaikoInbox.sol":"ITaikoInbox"},"evmVersion":"cancun","libraries":{}},"sources":{"contracts/layer1/based/ITaikoInbox.sol":{"keccak256":"0xc7ce38878c60bff1ae7979d5daa8bb7c6d2b2945551663150ecd8c3eb90dc430","urls":["bzz-raw://8e245de7383f8da0ceca0c237b9c2ea1f1fd35eb3d05c04553187e8c25099232","dweb:/ipfs/QmeD7QjG23rdosWLCTTh2pGTfySttySKkvsMdaKPJ45WmW"],"license":"MIT"},"contracts/shared/based/LibSharedData.sol":{"keccak256":"0x68967df1d6c504e78f6e84752b855ed2fd0e1288cb2be765219b803c2df0a5a6","urls":["bzz-raw://8c447c0f3b50be9310b41156e9b0f500f838109e4083ca31eefdfbd80cf1f1ff","dweb:/ipfs/QmdzRwJabMHwkfKa3d6bfN6CLxLfjPuau7nVQKW8uDf1T8"],"license":"MIT"}},"version":1},"id":15} \ No newline at end of file diff --git a/script/prove-batch.sh b/script/prove-batch.sh index b1f197ed7..cd55442fd 100755 --- a/script/prove-batch.sh +++ b/script/prove-batch.sh @@ -8,108 +8,6 @@ proof="$2" batch_id="$3" # Use the fourth parameter(s) as the batch number as a range batch_proposal_height="$4" +aggregate="${5:-"false"}" - -# Check the chain name and set the corresponding RPC values -if [ "$chain" == "ethereum" ]; then - l1_network="ethereum" -elif [ "$chain" == "holesky" ]; then - l1_network="holesky" -elif [ "$chain" == "taiko_mainnet" ]; then - l1_network="ethereum" -elif [ "$chain" == "taiko_a7" ]; then - l1_network="holesky" -elif [ "$chain" == "taiko_dev" ]; then - l1_network="taiko_dev_l1" -else - echo "Using customized chain name $1. Please double check the RPCs." - l1_network="holesky" -fi - -if [ "$proof" == "native" ]; then - proofParam=' - "proof_type": "NATIVE", - "blob_proof_type": "proof_of_equivalence", - "native" : { - "json_guest_input": null - } - ' -elif [ "$proof" == "sp1" ]; then - proofParam=' - "proof_type": "sp1", - "blob_proof_type": "proof_of_equivalence", - "sp1": { - "recursion": "plonk", - "prover": "network", - "verify": true - } - ' -elif [ "$proof" == "sp1-aggregation" ]; then - proofParam=' - "proof_type": "sp1", - "blob_proof_type": "proof_of_equivalence", - "sp1": { - "recursion": "compressed", - "prover": "network", - "verify": false - } - ' -elif [ "$proof" == "sgx" ]; then - proofParam=' - "proof_type": "sgx", - "sgx" : { - "instance_id": 123, - "setup": false, - "bootstrap": false, - "prove": true, - "input_path": null - } -' -elif [ "$proof" == "risc0" ]; then - proofParam=' - "proof_type": "risc0", - "blob_proof_type": "proof_of_equivalence", - "risc0": { - "bonsai": false, - "snark": false, - "profile": true, - "execution_po2": 18 - } - ' -elif [ "$proof" == "risc0-bonsai" ]; then - proofParam=' - "proof_type": "risc0", - "blob_proof_type": "proof_of_equivalence", - "risc0": { - "bonsai": true, - "snark": true, - "profile": false, - "execution_po2": 20 - } - ' -else - echo "Invalid proof name. Please use 'native', 'risc0[-bonsai]', 'sp1', or 'sgx'." - exit 1 -fi - - -prover="0x70997970C51812dc3A010C7d01b50e0d17dc79C8" -graffiti="8008500000000000000000000000000000000000000000000000000000000000" - - -echo "- proving batch $batch_id @ $batch_proposal_height on $chain with $proof proof" -curl --location --request POST 'http://localhost:8080/v3/proof/batch' \ - --header 'Content-Type: application/json' \ - --header 'Authorization: Bearer' \ - --data-raw "{ - \"network\": \"$chain\", - \"l1_network\": \"$l1_network\", - \"batches\": [{\"batch_id\": $batch_id, \"l1_inclusion_block_number\": $batch_proposal_height}], - \"prover\": \"$prover\", - \"graffiti\": \"$graffiti\", - $proofParam - }" -set +x -echo "" - -sleep 1.0 +$(dirname "$0")/prove.sh batch "$chain" "$proof" "$batch_id":"$batch_proposal_height" "$aggregate" \ No newline at end of file diff --git a/script/prove.sh b/script/prove.sh new file mode 100755 index 000000000..977f2b23f --- /dev/null +++ b/script/prove.sh @@ -0,0 +1,126 @@ +#!/usr/bin/env bash + +prover=${prover:-"0x70997970C51812dc3A010C7d01b50e0d17dc79C8"} +graffiti=${graffiti:-"8008500000000000000000000000000000000000000000000000000000000000"} +raiko_endpoint=${raiko_endpoint:-"http://localhost:8080"} +raiko_api_key=${raiko_api_key:-"4cbd753fbcbc2639de804f8ce425016a50e0ecd53db00cb5397912e83f5e570e"} + +usage() { + echo "Usage:" + echo " prove.sh batch [:,...] [aggregate(default: false)]" + echo " prove.sh block [aggregate(default: false)]" + exit 1 +} + +# Check the chain name and set the corresponding RPC values +get_l1_network() { + local chain="$1" + local l1_network + + if [ "$chain" == "ethereum" ]; then + l1_network="ethereum" + elif [ "$chain" == "holesky" ]; then + l1_network="holesky" + elif [ "$chain" == "taiko_mainnet" ]; then + l1_network="ethereum" + elif [ "$chain" == "taiko_a7" ]; then + l1_network="holesky" + elif [ "$chain" == "taiko_dev" ]; then + l1_network="taiko_dev_l1" + else + echo "Invalid chain name. Please use 'ethereum', 'holesky', 'taiko_mainnet', 'taiko_a7', or 'taiko_dev'." + exit 1 + fi + + echo "$l1_network" +} + +prove_batch() { + local chain="${1?"chain is required"}" + local proof_type="${2?"proof_type is required"}" + local batch_inputs="${3?"batch inputs are required"}" + local aggregate="${4:-"false"}" + local l1_network="${5?"l1_network is required"}" + + # Convert comma-separated batch inputs into JSON array + local batches_json="[" + local first=true + IFS=',' read -ra BATCHES <<< "$batch_inputs" + for batch in "${BATCHES[@]}"; do + IFS=':' read -r batch_id batch_proposal_height <<< "$batch" + if [ "$first" = true ]; then + first=false + else + batches_json+="," + fi + batches_json+="{\"batch_id\": $batch_id, \"l1_inclusion_block_number\": $batch_proposal_height}" + done + batches_json+="]" + + set -x + curl --location --request POST "$raiko_endpoint/v3/proof/batch" \ + --header "Content-Type: application/json" \ + --header "Authorization: Bearer $raiko_api_key" \ + --data-raw "{ + \"network\": \"$chain\", + \"l1_network\": \"$l1_network\", + \"batches\": $batches_json, + \"prover\": \"$prover\", + \"graffiti\": \"$graffiti\", + \"aggregate\": $aggregate, + \"proof_type\": \"$proof_type\" + }" + set +x +} + +prove_block() { + local chain="${1?"chain is required"}" + local proof_type="${2?"proof_type is required"}" + local block_id="${3?"block_id is required"}" + local aggregate="${4:-"false"}" + local l1_network="${5?"l1_network is required"}" + + set -x + curl --location --request POST "$raiko_endpoint/v2/proof" \ + --header "Content-Type: application/json" \ + --header "Authorization: Bearer $raiko_api_key" \ + --data-raw "{ + \"network\": \"$chain\", + \"l1_network\": \"$l1_network\", + \"block_numbers\": [[$block_id, null], [$(($block_id+1)), null]], + \"block_number\": $block_id, + \"prover\": \"$prover\", + \"graffiti\": \"$graffiti\", + \"aggregate\": $aggregate, + \"proof_type\": \"$proof_type\" + }" + set +x +} + +main() { + mode="$1" + case "$mode" in + batch) + chain="$2" + proof_type="$3" + batch_inputs="$4" + aggregate="${5:-"false"}" + l1_network=$(get_l1_network "$chain") + prove_batch "$chain" "$proof_type" "$batch_inputs" "$aggregate" "$l1_network" + ;; + block) + chain="$2" + proof_type="$3" + block_id="$4" + aggregate="${5:-"false"}" + l1_network=$(get_l1_network "$chain") + prove_block "$chain" "$proof_type" "$block_id" "$aggregate" "$l1_network" + ;; + *) + usage + ;; + esac + echo "" +} + +main "$@" \ No newline at end of file diff --git a/script/requirements.txt b/script/requirements.txt new file mode 100644 index 000000000..7fe3bf5a0 --- /dev/null +++ b/script/requirements.txt @@ -0,0 +1,3 @@ +web3>=6.15.1 +cffi>=1.15.1 +pycryptodome>=3.19.1 \ No newline at end of file diff --git a/script/traverse_batch.py b/script/traverse_batch.py new file mode 100644 index 000000000..955ddd536 --- /dev/null +++ b/script/traverse_batch.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 + +# Usage: +# +# ``` +# pip3 install -r script/requirements.txt +# +# python3 ./script/traverse_batch.py --rpc https://l1rpc.internal.taiko.xyz --contract 0xbE71D121291517c85Ab4d3ac65d70F6b1FD57118 --from-block 12700 --to-block 81670 +# ``` + +import argparse +from web3 import Web3 +from typing import Dict, Any +import json +from datetime import datetime + +# Load ABI from JSON file +with open('script/ITaikoInbox.json', 'r') as f: + contract_json = json.load(f) + # Find the BatchProposed event in the ABI + BATCH_PROPOSED_ABI = next(item for item in contract_json['abi'] if item['type'] == 'event' and item['name'] == 'BatchProposed') + +def setup_web3(rpc_url: str) -> Web3: + """Setup Web3 connection.""" + w3 = Web3(Web3.HTTPProvider(rpc_url)) + if not w3.is_connected(): + raise ConnectionError("Failed to connect to the RPC endpoint") + return w3 + +def traverse_batches(w3: Web3, contract_address: str, block_from: int, block_to: int) -> None: + """Traverse blocks and find BatchProposed events.""" + contract = w3.eth.contract(address=contract_address, abi=[BATCH_PROPOSED_ABI]) + + # Process blocks in pages of 50 + page_size = 50 + current_from = block_from + + while current_from <= block_to: + current_to = min(current_from + page_size - 1, block_to) + + try: + # Get all events in the current page + logs = w3.eth.get_logs({ + 'address': contract_address, + # Hint: copy from explorer + 'topics': ["0x9eb7fc80523943f28950bbb71ed6d584effe3e1e02ca4ddc8c86e5ee1558c096"], + 'fromBlock': current_from, + 'toBlock': current_to + }) + + for log in logs: + try: + # Decode the event data + event = contract.events.BatchProposed().process_log(log) + batch_id = event['args']['meta']['batchId'] + l1_block_number = event['blockNumber'] + print(f'{batch_id}:{l1_block_number},', end='') + except Exception as e: + print(f"Error processing log: {str(e)}") + continue + + except Exception as e: + print(f"Error getting logs for blocks {current_from}-{current_to}: {str(e)}") + + # Move to next page + current_from = current_to + 1 + +def main(): + parser = argparse.ArgumentParser(description='Traverse chain and find BatchProposed events') + parser.add_argument('--rpc', required=True, help='RPC URL of the target chain') + parser.add_argument('--contract', required=True, help='Contract address of TaikoInbox') + parser.add_argument('--from-block', type=int, required=True, help='Starting block number') + parser.add_argument('--to-block', type=int, required=True, help='Ending block number') + + args = parser.parse_args() + + w3 = setup_web3(args.rpc) + traverse_batches(w3, args.contract, args.from_block, args.to_block) + +if __name__ == "__main__": + main()