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