linera_execution/wasm/
runtime_api.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::{any::Any, collections::HashMap, marker::PhantomData};
5
6use linera_base::{
7    data_types::{
8        Amount, ApplicationPermissions, BlockHeight, Bytecode, SendMessageRequest, Timestamp,
9    },
10    http,
11    identifiers::{Account, AccountOwner, ApplicationId, ChainId, StreamName},
12    ownership::{ChainOwnership, ChangeApplicationPermissionsError, CloseChainError},
13    vm::VmRuntime,
14};
15use linera_views::batch::{Batch, WriteOperation};
16use linera_witty::{wit_export, Instance, RuntimeError};
17use tracing::log;
18
19use super::WasmExecutionError;
20use crate::{BaseRuntime, ContractRuntime, DataBlobHash, ExecutionError, ModuleId, ServiceRuntime};
21
22/// Common host data used as the `UserData` of the system API implementations.
23pub struct RuntimeApiData<Runtime> {
24    runtime: Runtime,
25    active_promises: HashMap<u32, Box<dyn Any + Send + Sync>>,
26    promise_counter: u32,
27}
28
29impl<Runtime> RuntimeApiData<Runtime> {
30    /// Creates a new [`RuntimeApiData`] using the provided `runtime` to execute the system APIs.
31    pub fn new(runtime: Runtime) -> Self {
32        RuntimeApiData {
33            runtime,
34            active_promises: HashMap::new(),
35            promise_counter: 0,
36        }
37    }
38
39    /// Returns a mutable reference the system API `Runtime`.
40    pub fn runtime_mut(&mut self) -> &mut Runtime {
41        &mut self.runtime
42    }
43
44    /// Registers a `promise` internally, returning an ID that is unique for the lifetime of this
45    /// [`RuntimeApiData`].
46    fn register_promise<Promise>(&mut self, promise: Promise) -> u32
47    where
48        Promise: Send + Sync + 'static,
49    {
50        let id = self.promise_counter;
51
52        self.active_promises.insert(id, Box::new(promise));
53        self.promise_counter += 1;
54
55        id
56    }
57
58    /// Returns a `Promise` registered to the provided `promise_id`.
59    fn take_promise<Promise>(&mut self, promise_id: u32) -> Result<Promise, RuntimeError>
60    where
61        Promise: Send + Sync + 'static,
62    {
63        let type_erased_promise = self
64            .active_promises
65            .remove(&promise_id)
66            .ok_or_else(|| RuntimeError::Custom(WasmExecutionError::UnknownPromise.into()))?;
67
68        type_erased_promise
69            .downcast()
70            .map(|boxed_promise| *boxed_promise)
71            .map_err(|_| RuntimeError::Custom(WasmExecutionError::IncorrectPromise.into()))
72    }
73}
74
75/// An implementation of the runtime API used to access the common behaviour and the view storage for both contracts and
76/// services.
77#[derive(Default)]
78pub struct BaseRuntimeApi<Caller>(PhantomData<Caller>);
79
80#[wit_export(package = "linera:app")]
81impl<Caller, Runtime> BaseRuntimeApi<Caller>
82where
83    Caller: Instance<UserData = RuntimeApiData<Runtime>>,
84    Runtime: BaseRuntime + 'static,
85{
86    /// Returns the ID of the current chain.
87    fn get_chain_id(caller: &mut Caller) -> Result<ChainId, RuntimeError> {
88        caller
89            .user_data_mut()
90            .runtime
91            .chain_id()
92            .map_err(|error| RuntimeError::Custom(error.into()))
93    }
94
95    /// Returns the height of the current block that is executing.
96    fn get_block_height(caller: &mut Caller) -> Result<BlockHeight, RuntimeError> {
97        caller
98            .user_data_mut()
99            .runtime
100            .block_height()
101            .map_err(|error| RuntimeError::Custom(error.into()))
102    }
103
104    /// Returns the ID of the current application.
105    fn get_application_id(caller: &mut Caller) -> Result<ApplicationId, RuntimeError> {
106        caller
107            .user_data_mut()
108            .runtime
109            .application_id()
110            .map_err(|error| RuntimeError::Custom(error.into()))
111    }
112
113    /// Returns the chain ID of the current application creator.
114    fn get_application_creator_chain_id(caller: &mut Caller) -> Result<ChainId, RuntimeError> {
115        caller
116            .user_data_mut()
117            .runtime
118            .application_creator_chain_id()
119            .map_err(|error| RuntimeError::Custom(error.into()))
120    }
121
122    /// Returns the application parameters provided when the application was created.
123    fn application_parameters(caller: &mut Caller) -> Result<Vec<u8>, RuntimeError> {
124        caller
125            .user_data_mut()
126            .runtime
127            .application_parameters()
128            .map_err(|error| RuntimeError::Custom(error.into()))
129    }
130
131    /// Retrieves the owner configuration for the current chain.
132    fn get_chain_ownership(caller: &mut Caller) -> Result<ChainOwnership, RuntimeError> {
133        caller
134            .user_data_mut()
135            .runtime
136            .chain_ownership()
137            .map_err(|error| RuntimeError::Custom(error.into()))
138    }
139
140    /// Retrieves the current system time, i.e. the timestamp of the block in which this is called.
141    fn read_system_timestamp(caller: &mut Caller) -> Result<Timestamp, RuntimeError> {
142        caller
143            .user_data_mut()
144            .runtime
145            .read_system_timestamp()
146            .map_err(|error| RuntimeError::Custom(error.into()))
147    }
148
149    /// Returns the current chain balance.
150    fn read_chain_balance(caller: &mut Caller) -> Result<Amount, RuntimeError> {
151        caller
152            .user_data_mut()
153            .runtime
154            .read_chain_balance()
155            .map_err(|error| RuntimeError::Custom(error.into()))
156    }
157
158    /// Returns the balance of one of the accounts on this chain.
159    fn read_owner_balance(
160        caller: &mut Caller,
161        owner: AccountOwner,
162    ) -> Result<Amount, RuntimeError> {
163        caller
164            .user_data_mut()
165            .runtime
166            .read_owner_balance(owner)
167            .map_err(|error| RuntimeError::Custom(error.into()))
168    }
169
170    /// Returns the balances of all accounts on the chain.
171    fn read_owner_balances(
172        caller: &mut Caller,
173    ) -> Result<Vec<(AccountOwner, Amount)>, RuntimeError> {
174        caller
175            .user_data_mut()
176            .runtime
177            .read_owner_balances()
178            .map_err(|error| RuntimeError::Custom(error.into()))
179    }
180
181    /// Returns the owners of accounts on this chain.
182    fn read_balance_owners(caller: &mut Caller) -> Result<Vec<AccountOwner>, RuntimeError> {
183        caller
184            .user_data_mut()
185            .runtime
186            .read_balance_owners()
187            .map_err(|error| RuntimeError::Custom(error.into()))
188    }
189
190    /// Makes an HTTP request to the given URL and returns the response body.
191    fn perform_http_request(
192        caller: &mut Caller,
193        request: http::Request,
194    ) -> Result<http::Response, RuntimeError> {
195        caller
196            .user_data_mut()
197            .runtime
198            .perform_http_request(request)
199            .map_err(|error| RuntimeError::Custom(error.into()))
200    }
201
202    /// Rejects the transaction if the current time at block validation is `>= timestamp`. Note
203    /// that block validation happens at or after the block timestamp, but isn't necessarily the
204    /// same.
205    fn assert_before(caller: &mut Caller, timestamp: Timestamp) -> Result<(), RuntimeError> {
206        caller
207            .user_data_mut()
208            .runtime
209            .assert_before(timestamp)
210            .map_err(|error| RuntimeError::Custom(error.into()))
211    }
212
213    /// Reads a data blob from storage.
214    fn read_data_blob(caller: &mut Caller, hash: DataBlobHash) -> Result<Vec<u8>, RuntimeError> {
215        caller
216            .user_data_mut()
217            .runtime
218            .read_data_blob(hash)
219            .map_err(|error| RuntimeError::Custom(error.into()))
220    }
221
222    /// Asserts the existence of a data blob with the given hash.
223    fn assert_data_blob_exists(
224        caller: &mut Caller,
225        hash: DataBlobHash,
226    ) -> Result<(), RuntimeError> {
227        caller
228            .user_data_mut()
229            .runtime
230            .assert_data_blob_exists(hash)
231            .map_err(|error| RuntimeError::Custom(error.into()))
232    }
233
234    /// Logs a `message` with the provided information `level`.
235    fn log(caller: &mut Caller, message: String, level: log::Level) -> Result<(), RuntimeError> {
236        let allowed = caller
237            .user_data_mut()
238            .runtime
239            .allow_application_logs()
240            .map_err(|error| RuntimeError::Custom(error.into()))?;
241
242        if !allowed {
243            return Ok(());
244        }
245
246        #[cfg(web)]
247        {
248            // Send log through the execution channel to the main thread
249            caller
250                .user_data_mut()
251                .runtime
252                .send_log(message.clone(), level);
253        }
254
255        // Also use tracing for native builds (and as a fallback on web)
256        match level {
257            log::Level::Trace => tracing::trace!("{message}"),
258            log::Level::Debug => tracing::debug!("{message}"),
259            log::Level::Info => tracing::info!("{message}"),
260            log::Level::Warn => tracing::warn!("{message}"),
261            log::Level::Error => tracing::error!("{message}"),
262        }
263        Ok(())
264    }
265
266    /// Creates a new promise to check if the `key` is in storage.
267    fn contains_key_new(caller: &mut Caller, key: Vec<u8>) -> Result<u32, RuntimeError> {
268        let mut data = caller.user_data_mut();
269        let promise = data
270            .runtime
271            .contains_key_new(key)
272            .map_err(|error| RuntimeError::Custom(error.into()))?;
273
274        Ok(data.register_promise(promise))
275    }
276
277    /// Waits for the promise to check if the `key` is in storage.
278    fn contains_key_wait(caller: &mut Caller, promise_id: u32) -> Result<bool, RuntimeError> {
279        let mut data = caller.user_data_mut();
280        let promise = data.take_promise(promise_id)?;
281
282        data.runtime
283            .contains_key_wait(&promise)
284            .map_err(|error| RuntimeError::Custom(error.into()))
285    }
286
287    /// Creates a new promise to check if the `keys` are in storage.
288    fn contains_keys_new(caller: &mut Caller, keys: Vec<Vec<u8>>) -> Result<u32, RuntimeError> {
289        let mut data = caller.user_data_mut();
290        let promise = data
291            .runtime
292            .contains_keys_new(keys)
293            .map_err(|error| RuntimeError::Custom(error.into()))?;
294
295        Ok(data.register_promise(promise))
296    }
297
298    /// Waits for the promise to check if the `keys` are in storage.
299    fn contains_keys_wait(caller: &mut Caller, promise_id: u32) -> Result<Vec<bool>, RuntimeError> {
300        let mut data = caller.user_data_mut();
301        let promise = data.take_promise(promise_id)?;
302
303        data.runtime
304            .contains_keys_wait(&promise)
305            .map_err(|error| RuntimeError::Custom(error.into()))
306    }
307
308    /// Creates a new promise to read multiple entries from storage.
309    fn read_multi_values_bytes_new(
310        caller: &mut Caller,
311        keys: Vec<Vec<u8>>,
312    ) -> Result<u32, RuntimeError> {
313        let mut data = caller.user_data_mut();
314        let promise = data
315            .runtime
316            .read_multi_values_bytes_new(keys)
317            .map_err(|error| RuntimeError::Custom(error.into()))?;
318
319        Ok(data.register_promise(promise))
320    }
321
322    /// Waits for the promise to read multiple entries from storage.
323    fn read_multi_values_bytes_wait(
324        caller: &mut Caller,
325        promise_id: u32,
326    ) -> Result<Vec<Option<Vec<u8>>>, RuntimeError> {
327        let mut data = caller.user_data_mut();
328        let promise = data.take_promise(promise_id)?;
329
330        data.runtime
331            .read_multi_values_bytes_wait(&promise)
332            .map_err(|error| RuntimeError::Custom(error.into()))
333    }
334
335    /// Creates a new promise to read a single entry from storage.
336    fn read_value_bytes_new(caller: &mut Caller, key: Vec<u8>) -> Result<u32, RuntimeError> {
337        let mut data = caller.user_data_mut();
338        let promise = data
339            .runtime
340            .read_value_bytes_new(key)
341            .map_err(|error| RuntimeError::Custom(error.into()))?;
342
343        Ok(data.register_promise(promise))
344    }
345
346    /// Waits for the promise to read a single entry from storage.
347    fn read_value_bytes_wait(
348        caller: &mut Caller,
349        promise_id: u32,
350    ) -> Result<Option<Vec<u8>>, RuntimeError> {
351        let mut data = caller.user_data_mut();
352        let promise = data.take_promise(promise_id)?;
353
354        data.runtime
355            .read_value_bytes_wait(&promise)
356            .map_err(|error| RuntimeError::Custom(error.into()))
357    }
358
359    /// Creates a new promise to search for keys that start with the `key_prefix`.
360    fn find_keys_new(caller: &mut Caller, key_prefix: Vec<u8>) -> Result<u32, RuntimeError> {
361        let mut data = caller.user_data_mut();
362        let promise = data
363            .runtime
364            .find_keys_by_prefix_new(key_prefix)
365            .map_err(|error| RuntimeError::Custom(error.into()))?;
366
367        Ok(data.register_promise(promise))
368    }
369
370    /// Waits for the promise to search for keys that start with the `key_prefix`.
371    fn find_keys_wait(caller: &mut Caller, promise_id: u32) -> Result<Vec<Vec<u8>>, RuntimeError> {
372        let mut data = caller.user_data_mut();
373        let promise = data.take_promise(promise_id)?;
374
375        data.runtime
376            .find_keys_by_prefix_wait(&promise)
377            .map_err(|error| RuntimeError::Custom(error.into()))
378    }
379
380    /// Creates a new promise to search for entries whose keys that start with the `key_prefix`.
381    fn find_key_values_new(caller: &mut Caller, key_prefix: Vec<u8>) -> Result<u32, RuntimeError> {
382        let mut data = caller.user_data_mut();
383        let promise = data
384            .runtime
385            .find_key_values_by_prefix_new(key_prefix)
386            .map_err(|error| RuntimeError::Custom(error.into()))?;
387
388        Ok(data.register_promise(promise))
389    }
390
391    /// Waits for the promise to search for entries whose keys that start with the `key_prefix`.
392    #[expect(clippy::type_complexity)]
393    fn find_key_values_wait(
394        caller: &mut Caller,
395        promise_id: u32,
396    ) -> Result<Vec<(Vec<u8>, Vec<u8>)>, RuntimeError> {
397        let mut data = caller.user_data_mut();
398        let promise = data.take_promise(promise_id)?;
399
400        data.runtime
401            .find_key_values_by_prefix_wait(&promise)
402            .map_err(|error| RuntimeError::Custom(error.into()))
403    }
404}
405
406/// An implementation of the system API made available to contracts.
407#[derive(Default)]
408pub struct ContractRuntimeApi<Caller>(PhantomData<Caller>);
409
410#[wit_export(package = "linera:app")]
411impl<Caller, Runtime> ContractRuntimeApi<Caller>
412where
413    Caller: Instance<UserData = RuntimeApiData<Runtime>>,
414    Runtime: ContractRuntime + 'static,
415{
416    /// Returns the authenticated owner for this execution, if there is one.
417    fn authenticated_owner(caller: &mut Caller) -> Result<Option<AccountOwner>, RuntimeError> {
418        caller
419            .user_data_mut()
420            .runtime
421            .authenticated_owner()
422            .map_err(|error| RuntimeError::Custom(error.into()))
423    }
424
425    /// Returns `Some(true)` if the incoming message was rejected from the original destination and
426    /// is now bouncing back, `Some(false)` if the message is being currently being delivered to
427    /// its original destination, or [`None`] if not executing an incoming message.
428    fn message_is_bouncing(caller: &mut Caller) -> Result<Option<bool>, RuntimeError> {
429        caller
430            .user_data_mut()
431            .runtime
432            .message_is_bouncing()
433            .map_err(|error| RuntimeError::Custom(error.into()))
434    }
435
436    /// Returns the chain ID where the current message originated from, or [`None`] if not executing
437    /// an incoming message.
438    fn message_origin_chain_id(caller: &mut Caller) -> Result<Option<ChainId>, RuntimeError> {
439        caller
440            .user_data_mut()
441            .runtime
442            .message_origin_chain_id()
443            .map_err(|error| RuntimeError::Custom(error.into()))
444    }
445
446    /// Returns the authenticated caller ID, if the caller configured it and if the current context.
447    fn authenticated_caller_id(caller: &mut Caller) -> Result<Option<ApplicationId>, RuntimeError> {
448        caller
449            .user_data_mut()
450            .runtime
451            .authenticated_caller_id()
452            .map_err(|error| RuntimeError::Custom(error.into()))
453    }
454
455    /// Schedules a message to be sent to this application on another chain.
456    fn send_message(
457        caller: &mut Caller,
458        message: SendMessageRequest<Vec<u8>>,
459    ) -> Result<(), RuntimeError> {
460        caller
461            .user_data_mut()
462            .runtime
463            .send_message(message)
464            .map_err(|error| RuntimeError::Custom(error.into()))
465    }
466
467    /// Transfers an `amount` of native tokens from `source` owner account (or the current chain's
468    /// balance) to `destination`.
469    fn transfer(
470        caller: &mut Caller,
471        source: AccountOwner,
472        destination: Account,
473        amount: Amount,
474    ) -> Result<(), RuntimeError> {
475        caller
476            .user_data_mut()
477            .runtime
478            .transfer(source, destination, amount)
479            .map_err(|error| RuntimeError::Custom(error.into()))
480    }
481
482    /// Claims an `amount` of native tokens from a `source` account to a `destination` account.
483    fn claim(
484        caller: &mut Caller,
485        source: Account,
486        destination: Account,
487        amount: Amount,
488    ) -> Result<(), RuntimeError> {
489        caller
490            .user_data_mut()
491            .runtime
492            .claim(source, destination, amount)
493            .map_err(|error| RuntimeError::Custom(error.into()))
494    }
495
496    /// Opens a new chain, configuring it with the provided `chain_ownership`,
497    /// `application_permissions` and initial `balance` (debited from the current chain).
498    fn open_chain(
499        caller: &mut Caller,
500        chain_ownership: ChainOwnership,
501        application_permissions: ApplicationPermissions,
502        balance: Amount,
503    ) -> Result<ChainId, RuntimeError> {
504        caller
505            .user_data_mut()
506            .runtime
507            .open_chain(chain_ownership, application_permissions, balance)
508            .map_err(|error| RuntimeError::Custom(error.into()))
509    }
510
511    /// Closes the current chain. Returns an error if the application doesn't have
512    /// permission to do so.
513    fn close_chain(caller: &mut Caller) -> Result<Result<(), CloseChainError>, RuntimeError> {
514        match caller.user_data_mut().runtime.close_chain() {
515            Ok(()) => Ok(Ok(())),
516            Err(ExecutionError::UnauthorizedApplication(_)) => {
517                Ok(Err(CloseChainError::NotPermitted))
518            }
519            Err(error) => Err(RuntimeError::Custom(error.into())),
520        }
521    }
522
523    /// Changes the application permissions for the current chain. Returns an error if the
524    /// application doesn't have permission to do so.
525    fn change_application_permissions(
526        caller: &mut Caller,
527        application_permissions: ApplicationPermissions,
528    ) -> Result<Result<(), ChangeApplicationPermissionsError>, RuntimeError> {
529        match caller
530            .user_data_mut()
531            .runtime
532            .change_application_permissions(application_permissions)
533        {
534            Ok(()) => Ok(Ok(())),
535            Err(ExecutionError::UnauthorizedApplication(_)) => {
536                Ok(Err(ChangeApplicationPermissionsError::NotPermitted))
537            }
538            Err(error) => Err(RuntimeError::Custom(error.into())),
539        }
540    }
541
542    /// Creates a new application on the chain, based on the supplied bytecode and
543    /// parameters.
544    fn create_application(
545        caller: &mut Caller,
546        module_id: ModuleId,
547        parameters: Vec<u8>,
548        argument: Vec<u8>,
549        required_application_ids: Vec<ApplicationId>,
550    ) -> Result<ApplicationId, RuntimeError> {
551        caller
552            .user_data_mut()
553            .runtime
554            .create_application(module_id, parameters, argument, required_application_ids)
555            .map_err(|error| RuntimeError::Custom(error.into()))
556    }
557
558    /// Creates a new data blob and returns its hash.
559    fn create_data_blob(caller: &mut Caller, bytes: Vec<u8>) -> Result<DataBlobHash, RuntimeError> {
560        caller
561            .user_data_mut()
562            .runtime
563            .create_data_blob(bytes)
564            .map_err(|error| RuntimeError::Custom(error.into()))
565    }
566
567    /// Publishes a module with contract and service bytecode and returns the module ID.
568    fn publish_module(
569        caller: &mut Caller,
570        contract: Bytecode,
571        service: Bytecode,
572        vm_runtime: VmRuntime,
573    ) -> Result<ModuleId, RuntimeError> {
574        caller
575            .user_data_mut()
576            .runtime
577            .publish_module(contract, service, vm_runtime)
578            .map_err(|error| RuntimeError::Custom(error.into()))
579    }
580
581    /// Calls another application.
582    fn try_call_application(
583        caller: &mut Caller,
584        authenticated: bool,
585        callee_id: ApplicationId,
586        argument: Vec<u8>,
587    ) -> Result<Vec<u8>, RuntimeError> {
588        caller
589            .user_data_mut()
590            .runtime
591            .try_call_application(authenticated, callee_id, argument)
592            .map_err(|error| RuntimeError::Custom(error.into()))
593    }
594
595    /// Adds a new item to an event stream. Returns the new event's index in the stream.
596    fn emit(caller: &mut Caller, name: StreamName, value: Vec<u8>) -> Result<u32, RuntimeError> {
597        caller
598            .user_data_mut()
599            .runtime
600            .emit(name, value)
601            .map_err(|error| RuntimeError::Custom(error.into()))
602    }
603
604    /// Reads an event from a stream. Returns the event's value.
605    ///
606    /// Returns an error if the event doesn't exist.
607    fn read_event(
608        caller: &mut Caller,
609        chain_id: ChainId,
610        name: StreamName,
611        index: u32,
612    ) -> Result<Vec<u8>, RuntimeError> {
613        caller
614            .user_data_mut()
615            .runtime
616            .read_event(chain_id, name, index)
617            .map_err(|error| RuntimeError::Custom(error.into()))
618    }
619
620    /// Subscribes this application to an event stream.
621    fn subscribe_to_events(
622        caller: &mut Caller,
623        chain_id: ChainId,
624        application_id: ApplicationId,
625        name: StreamName,
626    ) -> Result<(), RuntimeError> {
627        caller
628            .user_data_mut()
629            .runtime
630            .subscribe_to_events(chain_id, application_id, name)
631            .map_err(|error| RuntimeError::Custom(error.into()))
632    }
633
634    /// Unsubscribes this application from an event stream.
635    fn unsubscribe_from_events(
636        caller: &mut Caller,
637        chain_id: ChainId,
638        application_id: ApplicationId,
639        name: StreamName,
640    ) -> Result<(), RuntimeError> {
641        caller
642            .user_data_mut()
643            .runtime
644            .unsubscribe_from_events(chain_id, application_id, name)
645            .map_err(|error| RuntimeError::Custom(error.into()))
646    }
647
648    /// Queries a service and returns the response.
649    fn query_service(
650        caller: &mut Caller,
651        application_id: ApplicationId,
652        query: Vec<u8>,
653    ) -> Result<Vec<u8>, RuntimeError> {
654        caller
655            .user_data_mut()
656            .runtime
657            .query_service(application_id, query)
658            .map_err(|error| RuntimeError::Custom(error.into()))
659    }
660
661    /// Consume some fuel.
662    ///
663    /// This is intended for the metering instrumentation, but if the user wants to donate
664    /// some extra fuel, more power to them!
665    fn consume_fuel(caller: &mut Caller, fuel: u64) -> Result<(), RuntimeError> {
666        caller
667            .user_data_mut()
668            .runtime_mut()
669            .consume_fuel(fuel, VmRuntime::Wasm)
670            .map_err(|e| RuntimeError::Custom(e.into()))
671    }
672
673    /// Returns the multi-leader round in which this block was validated.
674    fn validation_round(caller: &mut Caller) -> Result<Option<u32>, RuntimeError> {
675        caller
676            .user_data_mut()
677            .runtime_mut()
678            .validation_round()
679            .map_err(|error| RuntimeError::Custom(error.into()))
680    }
681
682    /// Writes a batch of `operations` to storage.
683    fn write_batch(
684        caller: &mut Caller,
685        operations: Vec<WriteOperation>,
686    ) -> Result<(), RuntimeError> {
687        caller
688            .user_data_mut()
689            .runtime_mut()
690            .write_batch(Batch { operations })
691            .map_err(|error| RuntimeError::Custom(error.into()))
692    }
693
694    /// Returns true if the corresponding contract uses a zero amount of storage.
695    fn has_empty_storage(
696        caller: &mut Caller,
697        application: ApplicationId,
698    ) -> Result<bool, RuntimeError> {
699        caller
700            .user_data_mut()
701            .runtime_mut()
702            .has_empty_storage(application)
703            .map_err(|error| RuntimeError::Custom(error.into()))
704    }
705}
706
707/// An implementation of the system API made available to services.
708#[derive(Default)]
709pub struct ServiceRuntimeApi<Caller>(PhantomData<Caller>);
710
711#[wit_export(package = "linera:app")]
712impl<Caller, Runtime> ServiceRuntimeApi<Caller>
713where
714    Caller: Instance<UserData = RuntimeApiData<Runtime>>,
715    Runtime: ServiceRuntime + 'static,
716{
717    /// Schedules an operation to be included in the block being built by this query.
718    fn schedule_operation(caller: &mut Caller, operation: Vec<u8>) -> Result<(), RuntimeError> {
719        caller
720            .user_data_mut()
721            .runtime
722            .schedule_operation(operation)
723            .map_err(|error| RuntimeError::Custom(error.into()))
724    }
725
726    /// Queries another application.
727    fn try_query_application(
728        caller: &mut Caller,
729        application: ApplicationId,
730        argument: Vec<u8>,
731    ) -> Result<Vec<u8>, RuntimeError> {
732        caller
733            .user_data_mut()
734            .runtime
735            .try_query_application(application, argument)
736            .map_err(|error| RuntimeError::Custom(error.into()))
737    }
738
739    /// Checks if the service has exceeded its execution time limit.
740    ///
741    /// This is called by the metering instrumentation, but the fuel consumed argument is
742    /// ignored.
743    fn check_execution_time(caller: &mut Caller, _fuel_consumed: u64) -> Result<(), RuntimeError> {
744        caller
745            .user_data_mut()
746            .runtime_mut()
747            .check_execution_time()
748            .map_err(|error| RuntimeError::Custom(error.into()))
749    }
750}