From 24fe903e877b4faada74273fd1d3177941971584 Mon Sep 17 00:00:00 2001 From: Mathieu Dutour Sikiric Date: Wed, 22 Apr 2026 14:39:17 +0200 Subject: [PATCH 1/3] Some update.to the execution. --- linera-chain/src/block_tracker.rs | 146 ++++++++++++- linera-execution/src/execution_state_actor.rs | 192 +++++++++++++++- linera-execution/src/lib.rs | 2 +- linera-execution/src/runtime.rs | 206 +++++++++++++++++- 4 files changed, 530 insertions(+), 16 deletions(-) diff --git a/linera-chain/src/block_tracker.rs b/linera-chain/src/block_tracker.rs index af2b0e901330..3a7a055778bf 100644 --- a/linera-chain/src/block_tracker.rs +++ b/linera-chain/src/block_tracker.rs @@ -4,6 +4,7 @@ use std::collections::{BTreeMap, BTreeSet}; use custom_debug_derive::Debug; +use futures::channel::mpsc; #[cfg(with_metrics)] use linera_base::prometheus_util::MeasureLatency; use linera_base::{ @@ -12,11 +13,14 @@ use linera_base::{ identifiers::{AccountOwner, BlobId, ChainId, StreamId}, }; use linera_execution::{ - execution_state_actor::ExecutionStateActor, ExecutionRuntimeContext, ExecutionStateView, - Message, MessageContext, MessageKind, OperationContext, OutgoingMessage, ResourceController, - ResourceTracker, SystemExecutionStateView, TransactionOutcome, TransactionTracker, + execution_state_actor::{ + ExecutionRequest, ExecutionStateActor, RuntimeChannels, RuntimeCommand, + }, + ExecutionRuntimeContext, ExecutionStateView, Message, MessageContext, MessageKind, + OperationContext, OutgoingMessage, ResourceController, ResourceTracker, + SystemExecutionStateView, TransactionOutcome, TransactionTracker, }; -use linera_views::context::Context; +use linera_views::{context::Context, views::View}; use tracing::instrument; #[cfg(with_metrics)] @@ -121,6 +125,57 @@ impl<'resources, 'blobs> BlockExecutionTracker<'resources, 'blobs> { self.resource_controller_mut() .track_block_size_of(&incoming_bundle) .with_execution_context(chain_execution_context)?; + + // On native, spawn a bundle-level contract runtime thread so that contract + // instances persist across messages within an incoming bundle. + #[cfg(not(web))] + let (mut runtime_channels, contract_runtime_task) = { + let (command_tx, command_rx) = + std::sync::mpsc::channel::(); + let (execution_state_sender, execution_state_receiver) = + futures::channel::mpsc::unbounded(); + + let allow_application_logs = chain + .context() + .extra() + .execution_runtime_config() + .allow_application_logs; + + let runtime_resource_controller = ResourceController::new( + self.resource_controller.policy().clone(), + ResourceTracker::default(), + Amount::ZERO, + ); + let rt_chain_id = self.chain_id; + let rt_height = self.block_height; + let rt_timestamp = self.timestamp; + let task = chain + .context() + .extra() + .thread_pool() + .run_send((), move |()| async move { + let runtime = + linera_execution::runtime::ContractSyncRuntime::new_for_bundle( + execution_state_sender, + rt_chain_id, + rt_height, + round, + rt_timestamp, + runtime_resource_controller, + allow_application_logs, + ); + runtime.run_bundle_loop(&command_rx) + }) + .await; + + (Some((command_tx, execution_state_receiver)), task) + }; + #[cfg(web)] + let mut runtime_channels: Option<( + std::sync::mpsc::Sender, + mpsc::UnboundedReceiver, + )> = None; + for posted_message in incoming_bundle.messages() { Box::pin(self.execute_message_in_block( chain, @@ -128,9 +183,69 @@ impl<'resources, 'blobs> BlockExecutionTracker<'resources, 'blobs> { incoming_bundle, round, &mut txn_tracker, + &mut runtime_channels, )) .await?; } + + // Finalize the bundle-level runtime: signal it to finalize all loaded + // contract instances and shut down. + #[cfg(not(web))] + if let Some((command_tx, mut execution_state_receiver)) = runtime_channels { + use futures::StreamExt as _; + + let finalize_context = linera_execution::FinalizeContext { + authenticated_owner: self.authenticated_owner, + chain_id: self.chain_id, + height: self.block_height, + round, + }; + let resource_tracker = self.resource_controller.tracker; + command_tx + .send(RuntimeCommand::FinalizeAll { + context: finalize_context, + tracker: Box::new(resource_tracker), + }) + .map_err(|_| { + ChainError::InternalError( + "Runtime thread stopped unexpectedly".to_string(), + ) + })?; + + // Handle remaining state requests (write_batch from finalize) until + // channel closes. + let mut actor = ExecutionStateActor::new( + chain, + &mut txn_tracker, + self.resource_controller, + ); + while let Some(request) = execution_state_receiver.next().await { + if let ExecutionRequest::ActionComplete { .. } = request { + continue; + } + actor + .handle_request(request) + .await + .map_err(|error| { + ChainError::InternalError(error.to_string()) + })?; + } + + // Wait for the runtime thread to finish and use its final tracker. + let runtime_resource_controller = contract_runtime_task + .await + .map_err(|error| { + ChainError::InternalError(format!( + "Runtime thread failed: {error}" + )) + })? + .map_err(|error| { + ChainError::InternalError(format!( + "Runtime finalization failed: {error}" + )) + })?; + self.resource_controller.tracker = runtime_resource_controller.tracker; + } } Transaction::ExecuteOperation(operation) => { self.resource_controller_mut() @@ -195,6 +310,10 @@ impl<'resources, 'blobs> BlockExecutionTracker<'resources, 'blobs> { incoming_bundle: &IncomingBundle, round: Option, txn_tracker: &mut TransactionTracker, + runtime_channels: &mut Option<( + std::sync::mpsc::Sender, + mpsc::UnboundedReceiver, + )>, ) -> Result<(), ChainError> where C: Context + Clone + 'static, @@ -220,8 +339,23 @@ impl<'resources, 'blobs> BlockExecutionTracker<'resources, 'blobs> { // Once a chain is closed, accepting incoming messages is not allowed. ensure!(!chain.system.closed.get(), ChainError::ClosedChain); - let mut actor = - ExecutionStateActor::new(chain, txn_tracker, self.resource_controller); + let mut actor = match runtime_channels.as_mut() { + Some((command_tx, execution_state_receiver)) => { + let channels = RuntimeChannels { + command_tx, + execution_state_receiver, + }; + ExecutionStateActor::with_runtime( + chain, + txn_tracker, + self.resource_controller, + channels, + ) + } + None => { + ExecutionStateActor::new(chain, txn_tracker, self.resource_controller) + } + }; Box::pin(actor.execute_message( context, posted_message.message.clone(), diff --git a/linera-execution/src/execution_state_actor.rs b/linera-execution/src/execution_state_actor.rs index fd1a91e90ca4..fc4a33da2761 100644 --- a/linera-execution/src/execution_state_actor.rs +++ b/linera-execution/src/execution_state_actor.rs @@ -35,16 +35,57 @@ use crate::{ system::{CreateApplicationResult, OpenChainConfig}, util::{OracleResponseExt as _, RespondExt as _}, ApplicationDescription, ApplicationId, ExecutionError, ExecutionRuntimeContext, - ExecutionStateView, JsVec, Message, MessageContext, MessageKind, ModuleId, Operation, - OperationContext, OutgoingMessage, ProcessStreamsContext, QueryContext, QueryOutcome, - ResourceController, SystemMessage, TransactionTracker, UserContractCode, UserServiceCode, + ExecutionStateView, FinalizeContext, JsVec, Message, MessageContext, MessageKind, ModuleId, + Operation, OperationContext, OutgoingMessage, ProcessStreamsContext, QueryContext, QueryOutcome, + ResourceController, ResourceTracker, SystemMessage, TransactionTracker, UserContractCode, + UserServiceCode, }; +/// Commands sent from the async side to the contract runtime thread. +pub enum RuntimeCommand { + /// Execute a user action (operation, message, instantiate, or stream processing). + Execute(Box), + /// Finalize all loaded contract instances and shut down the runtime thread. + FinalizeAll { + context: FinalizeContext, + /// Snapshot of the main resource controller's tracker, used as the starting point + /// for tracker accumulation during finalize calls. + tracker: Box, + }, + /// Drop all loaded contract instances without finalizing (for checkpoint rollback). + DropAllInstances, +} + +/// Payload for `RuntimeCommand::Execute`. +pub struct ExecuteCommand { + pub application_id: ApplicationId, + pub action: UserAction, + pub refund_grant_to: Option, + pub codes: Vec<(UserContractCode, ApplicationDescription)>, + /// Balance available to the runtime controller for this action (with grant applied). + pub initial_balance: Amount, + /// Whether this action should skip balance deductions. + pub is_free: bool, + /// Snapshot of the main controller's tracker; the runtime resumes from this state. + pub tracker: ResourceTracker, +} + /// Actor for handling requests to the execution state. pub struct ExecutionStateActor<'a, C> { state: &'a mut ExecutionStateView, txn_tracker: &'a mut TransactionTracker, resource_controller: &'a mut ResourceController>, + /// Channels to the bundle-level contract runtime thread. + /// When `Some`, actions are sent to a shared runtime instead of creating a new one. + runtime_channels: Option>, +} + +/// Channels for communicating with the bundle-level contract runtime thread. +pub struct RuntimeChannels<'a> { + /// Sender to send commands (Execute, FinalizeAll, etc.) to the runtime thread. + pub command_tx: &'a std::sync::mpsc::Sender, + /// Receiver for state requests and action completions from the runtime thread. + pub execution_state_receiver: &'a mut mpsc::UnboundedReceiver, } #[cfg(with_metrics)] @@ -82,7 +123,10 @@ where C: Context + Clone + 'static, C::Extra: ExecutionRuntimeContext, { - /// Creates a new execution state actor. + /// Creates a new execution state actor without a shared runtime. + /// + /// Used for operations, service queries and other cases that don't need the + /// bundle-level runtime. pub fn new( state: &'a mut ExecutionStateView, txn_tracker: &'a mut TransactionTracker, @@ -92,6 +136,22 @@ where state, txn_tracker, resource_controller, + runtime_channels: None, + } + } + + /// Creates a new execution state actor connected to a bundle-level runtime. + pub fn with_runtime( + state: &'a mut ExecutionStateView, + txn_tracker: &'a mut TransactionTracker, + resource_controller: &'a mut ResourceController>, + runtime_channels: RuntimeChannels<'a>, + ) -> Self { + Self { + state, + txn_tracker, + resource_controller, + runtime_channels: Some(runtime_channels), } } @@ -151,7 +211,7 @@ where skip_all, fields(request_type = %request.as_ref()) )] - pub(crate) async fn handle_request( + pub async fn handle_request( &mut self, request: ExecutionRequest, ) -> Result<(), ExecutionError> { @@ -829,6 +889,10 @@ where callback.respond(allow); } + ActionComplete { .. } => { + panic!("ActionComplete should be handled by the request loop, not handle_request"); + } + #[cfg(web)] Log { message, level } => match level { tracing::log::Level::Trace | tracing::log::Level::Debug => { @@ -906,8 +970,113 @@ where refund_grant_to: Option, grant: Option<&mut Amount>, ) -> Result<(), ExecutionError> { - self.run_user_action_with_runtime(application_id, action, refund_grant_to, grant) - .await + if self.runtime_channels.is_some() { + self.send_action_to_runtime(application_id, action, refund_grant_to, grant) + .await + } else { + self.run_user_action_with_runtime(application_id, action, refund_grant_to, grant) + .await + } + } + + /// Sends a user action to the bundle-level shared runtime thread. + /// + /// Loads contract code and dependencies, sends the action via the command channel, + /// then handles state requests until `ActionComplete` is received. + #[instrument(skip_all, fields(application_id = %application_id))] + async fn send_action_to_runtime( + &mut self, + application_id: ApplicationId, + action: UserAction, + refund_grant_to: Option, + grant: Option<&mut Amount>, + ) -> Result<(), ExecutionError> { + // Load contract code and dependencies. + let (codes, descriptions): (Vec<_>, Vec<_>) = + self.contract_and_dependencies(application_id).await?; + let codes: Vec<_> = codes.into_iter().zip(descriptions).collect(); + + // Compute the initial balance available to this action. + let mut cloned_grant = grant.as_ref().map(|x| **x); + let initial_balance = self + .resource_controller + .with_state_and_grant(&mut self.state.system, cloned_grant.as_mut()) + .await? + .balance()?; + + // Messages and stream-processing to free apps skip balance deductions. + let is_free = matches!( + &action, + UserAction::Message(..) | UserAction::ProcessStreams(..) + ) && self + .resource_controller + .policy() + .is_free_app(&application_id); + self.resource_controller.is_free = is_free; + + // Snapshot the tracker so the runtime resumes from the main controller's state. + let tracker = self.resource_controller.tracker; + + // Take the channels out temporarily to avoid borrow conflicts with handle_request. + let channels = self + .runtime_channels + .take() + .expect("runtime_channels should be set"); + + // Send the execute command to the runtime thread. + channels + .command_tx + .send(RuntimeCommand::Execute(Box::new(ExecuteCommand { + application_id, + action, + refund_grant_to, + codes, + initial_balance, + is_free, + tracker, + }))) + .map_err(|_| ExecutionError::MissingRuntimeResponse)?; + + // Handle state requests until the action completes. + let (result, final_balance, final_tracker) = loop { + let Some(request) = channels.execution_state_receiver.next().await else { + self.runtime_channels = Some(channels); + return Err(ExecutionError::MissingRuntimeResponse); + }; + if let ExecutionRequest::ActionComplete { + result, + final_balance, + tracker, + } = request + { + break (result, final_balance, tracker); + } + match self.handle_request(request).await { + Ok(()) => {} + Err(error) => { + self.runtime_channels = Some(channels); + return Err(error); + } + } + }; + + // Put the channels back. + self.runtime_channels = Some(channels); + + self.resource_controller.is_free = false; + let result = result?; + self.txn_tracker.add_operation_result(result); + + // Copy the runtime's tracker back so later actions see accumulated fuel. + self.resource_controller.tracker = final_tracker; + + // Merge the balance delta back into the main controller. + self.resource_controller + .with_state_and_grant(&mut self.state.system, grant) + .await? + .merge_balance(initial_balance, final_balance)?; + + Ok(()) } // TODO(#5034): unify with `contract_and_dependencies` @@ -1557,4 +1726,13 @@ pub enum ExecutionRequest { message: String, level: tracing::log::Level, }, + + /// Signals that a user action has completed on the runtime thread. + ActionComplete { + result: Result>, ExecutionError>, + /// Balance held by the runtime controller after executing the action. + final_balance: Amount, + /// Tracker accumulated by the runtime controller after executing the action. + tracker: ResourceTracker, + }, } diff --git a/linera-execution/src/lib.rs b/linera-execution/src/lib.rs index 6a0054b5b6a1..33f28cc42537 100644 --- a/linera-execution/src/lib.rs +++ b/linera-execution/src/lib.rs @@ -12,7 +12,7 @@ pub mod execution_state_actor; mod graphql; mod policy; mod resources; -mod runtime; +pub mod runtime; pub mod system; #[cfg(with_testing)] pub mod test_utils; diff --git a/linera-execution/src/runtime.rs b/linera-execution/src/runtime.rs index 3c44d705e2e5..7c165360919d 100644 --- a/linera-execution/src/runtime.rs +++ b/linera-execution/src/runtime.rs @@ -28,8 +28,10 @@ use tracing::instrument; use crate::{ execution::UserAction, - execution_state_actor::{ExecutionRequest, ExecutionStateSender}, - resources::ResourceController, + execution_state_actor::{ + ExecuteCommand, ExecutionRequest, ExecutionStateSender, RuntimeCommand, + }, + resources::{ResourceController, ResourceTracker}, system::CreateApplicationResult, util::{ReceiverExt, UnboundedSenderExt}, ApplicationDescription, ApplicationId, BaseRuntime, ContractRuntime, DataBlobHash, @@ -1104,6 +1106,136 @@ impl ContractSyncRuntime { Ok((result, runtime.resource_controller)) } + + /// Creates a new `ContractSyncRuntime` for bundle-level execution. + /// + /// Unlike `new`, this does not take a specific action — the runtime will receive + /// actions via `run_bundle_loop`. + pub fn new_for_bundle( + execution_state_sender: ExecutionStateSender, + chain_id: ChainId, + height: BlockHeight, + round: Option, + timestamp: Timestamp, + resource_controller: ResourceController, + allow_application_logs: bool, + ) -> Self { + SyncRuntime(Some(ContractSyncRuntimeHandle::from( + SyncRuntimeInternal::new( + chain_id, + height, + round, + None, // executing_message: set per action + execution_state_sender, + None, // deadline + None, // refund_grant_to: set per action + resource_controller, + timestamp, + allow_application_logs, + ), + ))) + } + + /// Runs a bundle-level loop, receiving commands from the async side. + /// + /// The runtime thread stays alive for the entire incoming bundle, processing + /// messages one at a time. Contract instances are kept loaded across messages + /// so that cross-application state is visible without flushing to storage. + /// + /// Returns the `ResourceController` after all instances have been finalized. + pub fn run_bundle_loop( + self, + command_rx: &std::sync::mpsc::Receiver, + ) -> Result { + let handle = self.0.as_ref().expect("Runtime should be initialized"); + + loop { + let command = command_rx + .recv() + .map_err(|_| ExecutionError::MissingRuntimeResponse)?; + + match command { + RuntimeCommand::Execute(cmd) => { + let ExecuteCommand { + application_id, + action, + refund_grant_to, + codes, + initial_balance, + is_free, + tracker, + } = *cmd; + // Preload any new contract codes. + for (code, description) in codes { + self.preload_contract(ApplicationId::from(&description), code, description); + } + + // Sync the main controller's balance, is_free flag, and tracker into + // the runtime controller before executing. + { + let mut inner = handle.inner(); + inner.resource_controller.account = initial_balance; + inner.resource_controller.is_free = is_free; + inner.resource_controller.tracker = tracker; + } + + let result = handle.execute_action(application_id, &action, refund_grant_to); + + // Read back the runtime controller's final state so the async side can + // merge the balance delta and copy the tracker back. + let (final_balance, final_tracker) = { + let inner = handle.inner(); + ( + inner.resource_controller.account, + inner.resource_controller.tracker, + ) + }; + + // Signal completion through the state request channel. + let sender = handle.inner().execution_state_sender.clone(); + let _ = sender.unbounded_send(ExecutionRequest::ActionComplete { + result, + final_balance, + tracker: final_tracker, + }); + } + + RuntimeCommand::FinalizeAll { context, tracker } => { + // Sync the main controller's tracker so finalize accumulates against + // the budget. + { + let mut inner = handle.inner(); + inner.resource_controller.tracker = *tracker; + // Finalize runs cleanup code; skip balance deductions since there + // is no action-specific payer at this point. + inner.resource_controller.is_free = true; + inner.resource_controller.account = Amount::MAX; + } + handle.finalize(context)?; + break; + } + + RuntimeCommand::DropAllInstances => { + handle.drop_all_instances(); + + // Signal completion through the state request channel. + let sender = handle.inner().execution_state_sender.clone(); + let _ = sender.unbounded_send(ExecutionRequest::ActionComplete { + result: Ok(None), + final_balance: Amount::ZERO, + tracker: ResourceTracker::default(), + }); + } + } + } + + // Consume self and return the resource controller. + let runtime = self + .into_inner() + .expect("Runtime clones should have been freed by now"); + + Ok(runtime.resource_controller) + } } impl ContractSyncRuntimeHandle { @@ -1146,6 +1278,76 @@ impl ContractSyncRuntimeHandle { Ok(result) } + /// Executes a single user action without finalizing. + /// + /// Updates the runtime's `executing_message` and `refund_grant_to` context + /// based on the action, then executes the contract code. + #[instrument(skip_all, fields(application_id = %application_id))] + fn execute_action( + &self, + application_id: ApplicationId, + action: &UserAction, + refund_grant_to: Option, + ) -> Result>, ExecutionError> { + // Update per-action context on the runtime. + { + let mut runtime = self.inner(); + runtime.executing_message = if let UserAction::Message(context, _) = action { + Some(context.into()) + } else { + None + }; + runtime.refund_grant_to = refund_grant_to; + } + + let signer = action.signer(); + // We need to clone the action data out since execute takes FnOnce. + let closure = match action { + UserAction::Instantiate(_context, argument) => { + let argument = argument.clone(); + Box::new(move |code: &mut UserContractInstance| { + code.instantiate(argument).map(|()| None) + }) + as Box< + dyn FnOnce( + &mut UserContractInstance, + ) + -> Result>, ExecutionError>, + > + } + UserAction::Operation(_context, operation) => { + let operation = operation.clone(); + Box::new(move |code: &mut UserContractInstance| { + code.execute_operation(operation).map(Option::Some) + }) + } + UserAction::Message(_context, message) => { + let message = message.clone(); + Box::new(move |code: &mut UserContractInstance| { + code.execute_message(message).map(|()| None) + }) + } + UserAction::ProcessStreams(_context, updates) => { + let updates = updates.clone(); + Box::new(move |code: &mut UserContractInstance| { + code.process_streams(updates).map(|()| None) + }) + } + }; + + self.execute(application_id, signer, closure) + } + + /// Drops all loaded contract instances without finalizing. + /// + /// Used during checkpoint rollback to discard stale in-memory state. + fn drop_all_instances(&self) { + let mut runtime = self.inner(); + runtime.loaded_applications.clear(); + runtime.applications_to_finalize.clear(); + runtime.is_finalizing = false; + } + /// Notifies all loaded applications that execution is finalizing. #[instrument(skip_all)] fn finalize(&self, context: FinalizeContext) -> Result<(), ExecutionError> { From 90184506a6164c24730d727416d21ca6ff482bbe Mon Sep 17 00:00:00 2001 From: Mathieu Dutour Sikiric Date: Wed, 22 Apr 2026 16:27:52 +0200 Subject: [PATCH 2/3] Reformat. --- linera-chain/src/block_tracker.rs | 22 +++++-------------- linera-execution/src/execution_state_actor.rs | 6 ++--- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/linera-chain/src/block_tracker.rs b/linera-chain/src/block_tracker.rs index 3a7a055778bf..74237a58b682 100644 --- a/linera-chain/src/block_tracker.rs +++ b/linera-chain/src/block_tracker.rs @@ -130,8 +130,7 @@ impl<'resources, 'blobs> BlockExecutionTracker<'resources, 'blobs> { // instances persist across messages within an incoming bundle. #[cfg(not(web))] let (mut runtime_channels, contract_runtime_task) = { - let (command_tx, command_rx) = - std::sync::mpsc::channel::(); + let (command_tx, command_rx) = std::sync::mpsc::channel::(); let (execution_state_sender, execution_state_receiver) = futures::channel::mpsc::unbounded(); @@ -214,11 +213,8 @@ impl<'resources, 'blobs> BlockExecutionTracker<'resources, 'blobs> { // Handle remaining state requests (write_batch from finalize) until // channel closes. - let mut actor = ExecutionStateActor::new( - chain, - &mut txn_tracker, - self.resource_controller, - ); + let mut actor = + ExecutionStateActor::new(chain, &mut txn_tracker, self.resource_controller); while let Some(request) = execution_state_receiver.next().await { if let ExecutionRequest::ActionComplete { .. } = request { continue; @@ -226,18 +222,14 @@ impl<'resources, 'blobs> BlockExecutionTracker<'resources, 'blobs> { actor .handle_request(request) .await - .map_err(|error| { - ChainError::InternalError(error.to_string()) - })?; + .map_err(|error| ChainError::InternalError(error.to_string()))?; } // Wait for the runtime thread to finish and use its final tracker. let runtime_resource_controller = contract_runtime_task .await .map_err(|error| { - ChainError::InternalError(format!( - "Runtime thread failed: {error}" - )) + ChainError::InternalError(format!("Runtime thread failed: {error}")) })? .map_err(|error| { ChainError::InternalError(format!( @@ -352,9 +344,7 @@ impl<'resources, 'blobs> BlockExecutionTracker<'resources, 'blobs> { channels, ) } - None => { - ExecutionStateActor::new(chain, txn_tracker, self.resource_controller) - } + None => ExecutionStateActor::new(chain, txn_tracker, self.resource_controller), }; Box::pin(actor.execute_message( context, diff --git a/linera-execution/src/execution_state_actor.rs b/linera-execution/src/execution_state_actor.rs index fc4a33da2761..b8aaf00072b7 100644 --- a/linera-execution/src/execution_state_actor.rs +++ b/linera-execution/src/execution_state_actor.rs @@ -36,9 +36,9 @@ use crate::{ util::{OracleResponseExt as _, RespondExt as _}, ApplicationDescription, ApplicationId, ExecutionError, ExecutionRuntimeContext, ExecutionStateView, FinalizeContext, JsVec, Message, MessageContext, MessageKind, ModuleId, - Operation, OperationContext, OutgoingMessage, ProcessStreamsContext, QueryContext, QueryOutcome, - ResourceController, ResourceTracker, SystemMessage, TransactionTracker, UserContractCode, - UserServiceCode, + Operation, OperationContext, OutgoingMessage, ProcessStreamsContext, QueryContext, + QueryOutcome, ResourceController, ResourceTracker, SystemMessage, TransactionTracker, + UserContractCode, UserServiceCode, }; /// Commands sent from the async side to the contract runtime thread. From eab60fdb707cfbd0c6621c4bd973109cbd9a3404 Mon Sep 17 00:00:00 2001 From: Mathieu Dutour Sikiric Date: Wed, 22 Apr 2026 20:17:21 +0200 Subject: [PATCH 3/3] Address the warning from clippy. --- linera-chain/src/block_tracker.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/linera-chain/src/block_tracker.rs b/linera-chain/src/block_tracker.rs index 74237a58b682..a77bbad007e5 100644 --- a/linera-chain/src/block_tracker.rs +++ b/linera-chain/src/block_tracker.rs @@ -20,7 +20,9 @@ use linera_execution::{ OperationContext, OutgoingMessage, ResourceController, ResourceTracker, SystemExecutionStateView, TransactionOutcome, TransactionTracker, }; -use linera_views::{context::Context, views::View}; +use linera_views::context::Context; +#[cfg(not(web))] +use linera_views::views::View; use tracing::instrument; #[cfg(with_metrics)]