1use std::{
7 collections::{BTreeMap, BTreeSet},
8 sync::Arc,
9};
10
11use custom_debug_derive::Debug;
12use futures::{channel::mpsc, StreamExt as _};
13#[cfg(with_metrics)]
14use linera_base::prometheus_util::MeasureLatency as _;
15use linera_base::{
16 data_types::{
17 Amount, ApplicationPermissions, ArithmeticError, BlobContent, BlockHeight, OracleResponse,
18 StreamUpdate, Timestamp,
19 },
20 ensure, hex_debug, hex_vec_debug, http,
21 identifiers::{
22 Account, AccountOwner, BlobId, BlobType, ChainId, EventId, GenericApplicationId,
23 OwnerSpender, StreamId,
24 },
25 ownership::ChainOwnership,
26 time::Instant,
27};
28use linera_views::{batch::Batch, context::Context, views::View};
29use oneshot::Sender;
30use reqwest::{header::HeaderMap, Client, Url};
31use tracing::{info_span, instrument, Instrument as _};
32
33use crate::{
34 execution::UserAction,
35 runtime::ContractSyncRuntime,
36 system::{CreateApplicationResult, OpenChainConfig},
37 util::{OracleResponseExt as _, RespondExt as _},
38 ApplicationDescription, ApplicationId, ExecutionError, ExecutionRuntimeContext,
39 ExecutionStateView, JsVec, Message, MessageContext, MessageKind, ModuleId, Operation,
40 OperationContext, OutgoingMessage, ProcessStreamsContext, QueryContext, QueryOutcome,
41 ResourceController, SystemMessage, SystemOperation, TransactionTracker, UserContractCode,
42 UserServiceCode,
43};
44
45pub struct ExecutionStateActor<'a, C> {
47 state: &'a mut ExecutionStateView<C>,
48 txn_tracker: &'a mut TransactionTracker,
49 resource_controller: &'a mut ResourceController<Option<AccountOwner>>,
50}
51
52#[cfg(with_metrics)]
53pub(crate) mod metrics {
54 use linera_base::prometheus_util::{exponential_bucket_latencies, register_histogram_vec};
55 use prometheus::HistogramVec;
56
57 linera_base::declare_metrics! {
58 pub static LOAD_CONTRACT_LATENCY: HistogramVec =
60 register_histogram_vec(
61 "load_contract_latency",
62 "Load contract latency",
63 &[],
64 exponential_bucket_latencies(250.0),
65 );
66
67 pub static LOAD_SERVICE_LATENCY: HistogramVec =
69 register_histogram_vec(
70 "load_service_latency",
71 "Load service latency",
72 &[],
73 exponential_bucket_latencies(250.0),
74 );
75 }
76}
77
78pub(crate) type ExecutionStateSender = mpsc::UnboundedSender<ExecutionRequest>;
79
80impl<'a, C> ExecutionStateActor<'a, C>
81where
82 C: Context + Clone + 'static,
83 C::Extra: ExecutionRuntimeContext,
84{
85 pub fn new(
87 state: &'a mut ExecutionStateView<C>,
88 txn_tracker: &'a mut TransactionTracker,
89 resource_controller: &'a mut ResourceController<Option<AccountOwner>>,
90 ) -> Self {
91 Self {
92 state,
93 txn_tracker,
94 resource_controller,
95 }
96 }
97
98 #[instrument(skip_all, fields(application_id = %id))]
99 pub(crate) async fn load_contract(
100 &mut self,
101 id: ApplicationId,
102 ) -> Result<(UserContractCode, ApplicationDescription), ExecutionError> {
103 #[cfg(with_metrics)]
104 let _latency = metrics::LOAD_CONTRACT_LATENCY.measure_latency();
105 let blob_id = id.description_blob_id();
106 let description = match self.txn_tracker.get_blob_content(&blob_id) {
107 Some(blob) => bcs::from_bytes(blob.bytes())?,
108 None => {
109 self.state
110 .system
111 .describe_application(id, self.txn_tracker)
112 .await?
113 }
114 };
115 let code = self
116 .state
117 .context()
118 .extra()
119 .get_user_contract(&description, self.txn_tracker)
120 .await?;
121 Ok((code, description))
122 }
123
124 pub(crate) async fn load_service(
125 &mut self,
126 id: ApplicationId,
127 ) -> Result<(UserServiceCode, ApplicationDescription), ExecutionError> {
128 #[cfg(with_metrics)]
129 let _latency = metrics::LOAD_SERVICE_LATENCY.measure_latency();
130 let blob_id = id.description_blob_id();
131 let description = match self.txn_tracker.get_blob_content(&blob_id) {
132 Some(blob) => bcs::from_bytes(blob.bytes())?,
133 None => {
134 self.state
135 .system
136 .describe_application(id, self.txn_tracker)
137 .await?
138 }
139 };
140 let code = self
141 .state
142 .context()
143 .extra()
144 .get_user_service(&description, self.txn_tracker)
145 .await?;
146 Ok((code, description))
147 }
148
149 #[instrument(
151 skip_all,
152 fields(request_type = %request.as_ref())
153 )]
154 pub(crate) async fn handle_request(
155 &mut self,
156 request: ExecutionRequest,
157 ) -> Result<(), ExecutionError> {
158 use ExecutionRequest::*;
159 match request {
160 #[cfg(not(web))]
161 LoadContract { id, callback } => {
162 let (code, description) = self.load_contract(id).await?;
163 callback.respond((code, description))
164 }
165 #[cfg(not(web))]
166 LoadService { id, callback } => {
167 let (code, description) = self.load_service(id).await?;
168 callback.respond((code, description))
169 }
170
171 ChainBalance { callback } => {
172 let balance = *self.state.system.balance.get();
173 callback.respond(balance);
174 }
175
176 OwnerBalance { owner, callback } => {
177 let balance = self
178 .state
179 .system
180 .balances
181 .get(&owner)
182 .await?
183 .unwrap_or_default();
184 callback.respond(balance);
185 }
186
187 OwnerBalances { callback } => {
188 callback.respond(self.state.system.balances.index_values().await?);
189 }
190
191 BalanceOwners { callback } => {
192 let owners = self.state.system.balances.indices().await?;
193 callback.respond(owners);
194 }
195
196 Allowance {
197 owner,
198 spender,
199 callback,
200 } => {
201 let owner_spender = OwnerSpender::new(owner, spender);
202 let allowance = self
203 .state
204 .system
205 .allowances
206 .get(&owner_spender)
207 .await?
208 .unwrap_or_default();
209 callback.respond(allowance);
210 }
211
212 Allowances { callback } => {
213 let entries: Vec<_> = self
214 .state
215 .system
216 .allowances
217 .index_values()
218 .await?
219 .into_iter()
220 .map(|(os, amount)| (os.owner, os.spender, amount))
221 .collect();
222 callback.respond(entries);
223 }
224
225 Transfer {
226 source,
227 destination,
228 amount,
229 signer,
230 application_id,
231 callback,
232 } => {
233 let maybe_message = self
234 .state
235 .system
236 .transfer(signer, Some(application_id), source, destination, amount)
237 .await?;
238 self.txn_tracker.add_outgoing_messages(maybe_message);
239 callback.respond(());
240 }
241
242 Claim {
243 source,
244 destination,
245 amount,
246 signer,
247 application_id,
248 callback,
249 } => {
250 let maybe_message = self
251 .state
252 .system
253 .claim(
254 signer,
255 Some(application_id),
256 source.owner,
257 source.chain_id,
258 destination,
259 amount,
260 )
261 .await?;
262 self.txn_tracker.add_outgoing_messages(maybe_message);
263 callback.respond(());
264 }
265
266 Approve {
267 owner,
268 spender,
269 amount,
270 signer,
271 application_id,
272 callback,
273 } => {
274 self.state
275 .system
276 .approve(signer, Some(application_id), owner, spender, amount)
277 .await?;
278 callback.respond(());
279 }
280
281 TransferFrom {
282 owner,
283 spender,
284 destination,
285 amount,
286 signer,
287 application_id,
288 callback,
289 } => {
290 let maybe_message = self
291 .state
292 .system
293 .transfer_from(
294 signer,
295 Some(application_id),
296 owner,
297 spender,
298 destination,
299 amount,
300 )
301 .await?;
302 self.txn_tracker.add_outgoing_messages(maybe_message);
303 callback.respond(());
304 }
305
306 SystemTimestamp { callback } => {
307 let timestamp = self.state.system.progress.get().timestamp;
308 callback.respond(timestamp);
309 }
310
311 ChainOwnership { callback } => {
312 let ownership = self.state.system.ownership.get().await?.clone();
313 callback.respond(ownership);
314 }
315
316 ApplicationPermissions { callback } => {
317 let permissions = self
318 .state
319 .system
320 .application_permissions
321 .get()
322 .await?
323 .clone();
324 callback.respond(permissions);
325 }
326
327 ReadApplicationDescription {
328 application_id,
329 callback,
330 } => {
331 let blob_id = application_id.description_blob_id();
332 let description = match self.txn_tracker.get_blob_content(&blob_id) {
333 Some(blob) => bcs::from_bytes(blob.bytes())?,
334 None => {
335 let blob_content = self.state.system.read_blob_content(blob_id).await?;
336 self.state
337 .system
338 .blob_used(self.txn_tracker, blob_id)
339 .await?;
340 bcs::from_bytes(blob_content.bytes())?
341 }
342 };
343 callback.respond(description);
344 }
345
346 ContainsKey { id, key, callback } => {
347 let view = self.state.users.try_load_entry(&id).await?;
348 let result = match view {
349 Some(view) => view.contains_key(&key).await?,
350 None => false,
351 };
352 callback.respond(result);
353 }
354
355 ContainsKeys { id, keys, callback } => {
356 let view = self.state.users.try_load_entry(&id).await?;
357 let result = match view {
358 Some(view) => view.contains_keys(&keys).await?,
359 None => vec![false; keys.len()],
360 };
361 callback.respond(result);
362 }
363
364 ReadMultiValuesBytes { id, keys, callback } => {
365 let view = self.state.users.try_load_entry(&id).await?;
366 let values = match view {
367 Some(view) => view.multi_get(&keys).await?,
368 None => vec![None; keys.len()],
369 };
370 callback.respond(values);
371 }
372
373 ReadValueBytes { id, key, callback } => {
374 let view = self.state.users.try_load_entry(&id).await?;
375 let result = match view {
376 Some(view) => view.get(&key).await?,
377 None => None,
378 };
379 callback.respond(result);
380 }
381
382 FindKeysByPrefix {
383 id,
384 key_prefix,
385 callback,
386 } => {
387 let view = self.state.users.try_load_entry(&id).await?;
388 let result = match view {
389 Some(view) => view.find_keys_by_prefix(&key_prefix).await?,
390 None => Vec::new(),
391 };
392 callback.respond(result);
393 }
394
395 FindKeyValuesByPrefix {
396 id,
397 key_prefix,
398 callback,
399 } => {
400 let view = self.state.users.try_load_entry(&id).await?;
401 let result = match view {
402 Some(view) => view.find_key_values_by_prefix(&key_prefix).await?,
403 None => Vec::new(),
404 };
405 callback.respond(result);
406 }
407
408 WriteBatch {
409 id,
410 batch,
411 callback,
412 } => {
413 let mut view = self.state.users.try_load_entry_mut(&id).await?;
414 view.write_batch(batch)?;
415 callback.respond(());
416 }
417
418 OpenChain {
419 config,
420 parent_id,
421 block_height,
422 timestamp,
423 callback,
424 } => {
425 let chain_id = self
426 .state
427 .system
428 .open_chain(
429 *config,
430 parent_id,
431 block_height,
432 timestamp,
433 self.txn_tracker,
434 )
435 .await?;
436 callback.respond(chain_id);
437 }
438
439 CloseChain {
440 application_id,
441 callback,
442 } => {
443 let app_permissions = self.state.system.application_permissions.get().await?;
444 if !app_permissions.can_manage_chain(&application_id) {
445 callback.respond(Err(ExecutionError::UnauthorizedApplication(application_id)));
446 } else {
447 self.state.system.close_chain();
448 callback.respond(Ok(()));
449 }
450 }
451
452 ChangeOwnership {
453 application_id,
454 ownership,
455 callback,
456 } => {
457 let app_permissions = self.state.system.application_permissions.get().await?;
458 if !app_permissions.can_manage_chain(&application_id) {
459 callback.respond(Err(ExecutionError::UnauthorizedApplication(application_id)));
460 } else {
461 self.state.system.ownership.set(ownership);
462 callback.respond(Ok(()));
463 }
464 }
465
466 ChangeApplicationPermissions {
467 application_id,
468 application_permissions,
469 callback,
470 } => {
471 let app_permissions = self.state.system.application_permissions.get().await?;
472 if !app_permissions.can_manage_chain(&application_id) {
473 callback.respond(Err(ExecutionError::UnauthorizedApplication(application_id)));
474 } else {
475 self.state
476 .system
477 .application_permissions
478 .set(application_permissions);
479 callback.respond(Ok(()));
480 }
481 }
482
483 PeekApplicationIndex { callback } => {
484 let index = self.txn_tracker.peek_application_index();
485 callback.respond(index)
486 }
487
488 CreateApplication {
489 chain_id,
490 block_height,
491 module_id,
492 parameters,
493 required_application_ids,
494 callback,
495 } => {
496 let create_application_result = self
497 .state
498 .system
499 .create_application(
500 chain_id,
501 block_height,
502 module_id,
503 parameters,
504 required_application_ids,
505 self.txn_tracker,
506 )
507 .await?;
508 callback.respond(create_application_result);
509 }
510
511 PerformHttpRequest {
512 request,
513 http_responses_are_oracle_responses,
514 callback,
515 } => {
516 let system = &mut self.state.system;
517 let response = self
518 .txn_tracker
519 .oracle(|| async {
520 let headers = request
521 .headers
522 .into_iter()
523 .map(|http::Header { name, value }| {
524 Ok((name.parse()?, value.try_into()?))
525 })
526 .collect::<Result<HeaderMap, ExecutionError>>()?;
527
528 let url = Url::parse(&request.url)?;
529 let host = url
530 .host_str()
531 .ok_or_else(|| ExecutionError::UnauthorizedHttpRequest(url.clone()))?;
532
533 let (_epoch, committee) = system
534 .current_committee()
535 .await?
536 .ok_or_else(|| ExecutionError::UnauthorizedHttpRequest(url.clone()))?;
537 let allowed_hosts = &committee.policy().http_request_allow_list;
538
539 ensure!(
540 allowed_hosts.contains(host),
541 ExecutionError::UnauthorizedHttpRequest(url)
542 );
543
544 let request = Client::new()
545 .request(request.method.into(), url)
546 .body(request.body)
547 .headers(headers);
548 #[cfg(not(web))]
549 let request = request.timeout(linera_base::time::Duration::from_millis(
550 committee.policy().http_request_timeout_ms,
551 ));
552
553 let response = request.send().await?;
554
555 let mut response_size_limit =
556 committee.policy().maximum_http_response_bytes;
557
558 if http_responses_are_oracle_responses {
559 response_size_limit = response_size_limit
560 .min(committee.policy().maximum_oracle_response_bytes);
561 }
562 Ok(OracleResponse::Http(
563 Self::receive_http_response(response, response_size_limit).await?,
564 ))
565 })
566 .await?
567 .to_http_response()?;
568 callback.respond(response);
569 }
570
571 ReadBlobContent { blob_id, callback } => {
572 let content = if let Some(content) = self.txn_tracker.get_blob_content(&blob_id) {
573 content.clone()
574 } else {
575 let content = self.state.system.read_blob_content(blob_id).await?;
576 if blob_id.blob_type == BlobType::Data {
577 self.resource_controller
578 .with_state(&mut self.state.system)
579 .await?
580 .track_blob_read(content.bytes().len() as u64)?;
581 }
582 self.state
583 .system
584 .blob_used(self.txn_tracker, blob_id)
585 .await?;
586 content
587 };
588 callback.respond(content)
589 }
590
591 AssertBlobExists { blob_id, callback } => {
592 self.state.system.assert_blob_exists(blob_id).await?;
593 if blob_id.blob_type == BlobType::Data {
595 self.resource_controller
596 .with_state(&mut self.state.system)
597 .await?
598 .track_blob_read(0)?;
599 }
600 let is_new = self
601 .state
602 .system
603 .blob_used(self.txn_tracker, blob_id)
604 .await?;
605 if is_new {
606 self.txn_tracker
607 .replay_oracle_response(OracleResponse::Blob(blob_id))?;
608 }
609 callback.respond(());
610 }
611
612 Emit {
613 stream_id,
614 value,
615 callback,
616 } => {
617 let count = self
618 .state
619 .system
620 .stream_event_counts
621 .get_mut_or_default(&stream_id)
622 .await?;
623 let index = *count;
624 *count = count.checked_add(1).ok_or(ArithmeticError::Overflow)?;
625 self.resource_controller
626 .with_state(&mut self.state.system)
627 .await?
628 .track_event_published(&value)?;
629 self.txn_tracker.add_event(stream_id, index, value);
630 callback.respond(index)
631 }
632
633 ReadEvent { event_id, callback } => {
634 let context = self.state.context();
635 let extra = context.extra();
636 let event = self
637 .txn_tracker
638 .oracle(|| async {
639 let event = extra
640 .get_event(event_id.clone())
641 .await?
642 .ok_or(ExecutionError::EventsNotFound(vec![event_id.clone()]))?;
643 Ok(OracleResponse::Event(
644 event_id.clone(),
645 Arc::unwrap_or_clone(event),
646 ))
647 })
648 .await?
649 .to_event(&event_id)?;
650 self.resource_controller
651 .with_state(&mut self.state.system)
652 .await?
653 .track_event_read(event.len() as u64)?;
654 callback.respond(event);
655 }
656
657 SubscribeToEvents {
658 chain_id,
659 stream_id,
660 subscriber_app_id,
661 callback,
662 } => {
663 let subscriptions = self
664 .state
665 .system
666 .event_subscriptions
667 .get_mut_or_default(&(chain_id, stream_id.clone()))
668 .await?;
669 let next_index = match subscriptions.applications.entry(subscriber_app_id) {
670 std::collections::btree_map::Entry::Vacant(entry) => {
671 entry.insert(0);
672 subscriptions.min_next_index = 0;
673 0
674 }
675 std::collections::btree_map::Entry::Occupied(entry) => *entry.get(),
676 };
677 self.txn_tracker.add_stream_to_process(
680 subscriber_app_id,
681 chain_id,
682 stream_id,
683 0,
684 0,
685 next_index,
686 );
687 callback.respond(());
688 }
689
690 UnsubscribeFromEvents {
691 chain_id,
692 stream_id,
693 subscriber_app_id,
694 callback,
695 } => {
696 let key = (chain_id, stream_id.clone());
697 let subscriptions = self
698 .state
699 .system
700 .event_subscriptions
701 .get_mut_or_default(&key)
702 .await?;
703 subscriptions.applications.remove(&subscriber_app_id);
704 if subscriptions.applications.is_empty() {
705 self.state.system.event_subscriptions.remove(&key)?;
706 } else {
707 subscriptions.recalculate_min();
708 }
709 if let crate::GenericApplicationId::User(app_id) = stream_id.application_id {
710 self.txn_tracker
711 .remove_stream_to_process(app_id, chain_id, stream_id);
712 }
713 callback.respond(());
714 }
715
716 GetApplicationPermissions { callback } => {
717 let app_permissions = self.state.system.application_permissions.get().await?;
718 callback.respond(app_permissions.clone());
719 }
720
721 QueryServiceOracle {
722 deadline,
723 application_id,
724 next_block_height,
725 query,
726 callback,
727 } => {
728 let state = &mut self.state;
729 let local_time = self.txn_tracker.local_time();
730 let created_blobs = self.txn_tracker.created_blobs().clone();
731 let bytes = self
732 .txn_tracker
733 .oracle(|| async {
734 let context = QueryContext {
735 chain_id: state.context().extra().chain_id(),
736 next_block_height,
737 local_time,
738 };
739 let QueryOutcome {
740 response,
741 operations,
742 } = Box::pin(state.query_user_application_with_deadline(
743 application_id,
744 context,
745 query,
746 deadline,
747 created_blobs,
748 ))
749 .await?;
750 ensure!(
751 operations.is_empty(),
752 ExecutionError::ServiceOracleQueryOperations(operations)
753 );
754 Ok(OracleResponse::Service(response))
755 })
756 .await?
757 .to_service_response()?;
758 callback.respond(bytes);
759 }
760
761 AddOutgoingMessage { message, callback } => {
762 self.txn_tracker.add_outgoing_message(message);
763 callback.respond(());
764 }
765
766 SetLocalTime {
767 local_time,
768 callback,
769 } => {
770 self.txn_tracker.set_local_time(local_time);
771 callback.respond(());
772 }
773
774 AssertBefore {
775 timestamp,
776 callback,
777 } => {
778 let result = if !self
779 .txn_tracker
780 .replay_oracle_response(OracleResponse::Assert)?
781 {
782 let local_time = self.txn_tracker.local_time();
784 if local_time >= timestamp {
785 Err(ExecutionError::AssertBefore {
786 timestamp,
787 local_time,
788 })
789 } else {
790 Ok(())
791 }
792 } else {
793 Ok(())
794 };
795 callback.respond(result);
796 }
797
798 AddCreatedBlob { blob, callback } => {
799 if self.resource_controller.is_free {
800 self.txn_tracker.mark_blob_free(blob.id());
801 }
802 self.txn_tracker.add_created_blob(blob);
803 callback.respond(());
804 }
805
806 ValidationRound { round, callback } => {
807 let validation_round = self
808 .txn_tracker
809 .oracle(|| async { Ok(OracleResponse::Round(round)) })
810 .await?
811 .to_round()?;
812 callback.respond(validation_round);
813 }
814
815 HasEmptyStorage {
816 application,
817 callback,
818 } => {
819 let view = self.state.users.try_load_entry(&application).await?;
820 let result = match view {
821 Some(view) => view.iterative_count().await? == 0,
822 None => true,
823 };
824 callback.respond(result);
825 }
826
827 AllowApplicationLogs { callback } => {
828 let allow = self
829 .state
830 .context()
831 .extra()
832 .execution_runtime_config()
833 .allow_application_logs;
834 callback.respond(allow);
835 }
836
837 #[cfg(web)]
838 Log { message, level } => match level {
839 tracing::log::Level::Trace | tracing::log::Level::Debug => {
840 tracing::debug!(target: "user_application_log", message = %message);
841 }
842 tracing::log::Level::Info => {
843 tracing::info!(target: "user_application_log", message = %message);
844 }
845 tracing::log::Level::Warn => {
846 tracing::warn!(target: "user_application_log", message = %message);
847 }
848 tracing::log::Level::Error => {
849 tracing::error!(target: "user_application_log", message = %message);
850 }
851 },
852 }
853
854 Ok(())
855 }
856
857 #[instrument(skip_all)]
860 async fn process_subscriptions(
861 &mut self,
862 context: ProcessStreamsContext,
863 ) -> Result<(), ExecutionError> {
864 let mut processed = BTreeSet::new();
867 loop {
868 let to_process = self
869 .txn_tracker
870 .take_streams_to_process()
871 .into_iter()
872 .filter_map(|(app_id, updates)| {
873 let updates = updates
874 .into_iter()
875 .filter_map(|update| {
876 if !processed.insert((
877 app_id,
878 update.chain_id,
879 update.stream_id.clone(),
880 )) {
881 return None;
882 }
883 Some(update)
884 })
885 .collect::<Vec<_>>();
886 if updates.is_empty() {
887 return None;
888 }
889 Some((app_id, updates))
890 })
891 .collect::<BTreeMap<_, _>>();
892 if to_process.is_empty() {
893 return Ok(());
894 }
895 for (app_id, updates) in to_process {
896 self.run_user_action(
897 app_id,
898 UserAction::ProcessStreams(context, updates),
899 None,
900 None,
901 )
902 .await?;
903 }
904 }
905 }
906
907 async fn summarize_events_at_checkpoint(
919 &mut self,
920 context: OperationContext,
921 ) -> Result<(), ExecutionError> {
922 let mut updates_by_app = BTreeMap::<ApplicationId, Vec<StreamUpdate>>::new();
923 for stream_id in self.state.previous_event_blocks.indices().await? {
924 let GenericApplicationId::User(application_id) = stream_id.application_id else {
925 continue;
926 };
927 let next_index = self
928 .state
929 .system
930 .stream_event_counts
931 .get(&stream_id)
932 .await?
933 .unwrap_or(0);
934 updates_by_app
938 .entry(application_id)
939 .or_default()
940 .push(StreamUpdate {
941 chain_id: context.chain_id,
942 stream_id,
943 previous_index: 0,
944 first_index: next_index,
945 next_index,
946 });
947 }
948
949 self.state.previous_event_blocks.clear();
952
953 let process_context = ProcessStreamsContext::from(context);
954 for (application_id, updates) in updates_by_app {
955 self.run_user_action(
956 application_id,
957 UserAction::SummarizeEvents(process_context, updates),
958 None,
959 None,
960 )
961 .await?;
962 }
963 Ok(())
964 }
965
966 pub(crate) async fn run_user_action(
967 &mut self,
968 application_id: ApplicationId,
969 action: UserAction,
970 refund_grant_to: Option<Account>,
971 grant: Option<&mut Amount>,
972 ) -> Result<(), ExecutionError> {
973 self.run_user_action_with_runtime(application_id, action, refund_grant_to, grant)
974 .await
975 }
976
977 pub(crate) async fn service_and_dependencies(
979 &mut self,
980 application: ApplicationId,
981 ) -> Result<(Vec<UserServiceCode>, Vec<ApplicationDescription>), ExecutionError> {
982 let mut stack = vec![application];
985 let mut codes = vec![];
986 let mut descriptions = vec![];
987
988 while let Some(id) = stack.pop() {
989 let (code, description) = self.load_service(id).await?;
990 stack.extend(description.required_application_ids.iter().rev().copied());
991 codes.push(code);
992 descriptions.push(description);
993 }
994
995 codes.reverse();
996 descriptions.reverse();
997
998 Ok((codes, descriptions))
999 }
1000
1001 #[instrument(skip_all, fields(application_id = %application))]
1003 async fn contract_and_dependencies(
1004 &mut self,
1005 application: ApplicationId,
1006 ) -> Result<(Vec<UserContractCode>, Vec<ApplicationDescription>), ExecutionError> {
1007 let mut stack = vec![application];
1010 let mut codes = vec![];
1011 let mut descriptions = vec![];
1012
1013 while let Some(id) = stack.pop() {
1014 let (code, description) = self.load_contract(id).await?;
1015 stack.extend(description.required_application_ids.iter().rev().copied());
1016 codes.push(code);
1017 descriptions.push(description);
1018 }
1019
1020 codes.reverse();
1021 descriptions.reverse();
1022
1023 Ok((codes, descriptions))
1024 }
1025
1026 #[instrument(skip_all, fields(application_id = %application_id))]
1027 async fn run_user_action_with_runtime(
1028 &mut self,
1029 application_id: ApplicationId,
1030 action: UserAction,
1031 refund_grant_to: Option<Account>,
1032 grant: Option<&mut Amount>,
1033 ) -> Result<(), ExecutionError> {
1034 let chain_id = self.state.context().extra().chain_id();
1035 let mut cloned_grant = grant.as_ref().map(|x| **x);
1036 let initial_balance = self
1037 .resource_controller
1038 .with_state_and_grant(&mut self.state.system, cloned_grant.as_mut())
1039 .await?
1040 .balance()?;
1041 let mut controller = ResourceController::new(
1042 self.resource_controller.policy().clone(),
1043 self.resource_controller.tracker,
1044 initial_balance,
1045 );
1046 let is_free = matches!(
1047 &action,
1048 UserAction::Message(..)
1049 | UserAction::ProcessStreams(..)
1050 | UserAction::SummarizeEvents(..)
1051 ) && self
1052 .resource_controller
1053 .policy()
1054 .is_free_app(&application_id);
1055 controller.is_free = is_free;
1056 self.resource_controller.is_free = is_free;
1057 let (execution_state_sender, mut execution_state_receiver) =
1058 futures::channel::mpsc::unbounded();
1059
1060 let (codes, descriptions): (Vec<_>, Vec<_>) =
1061 self.contract_and_dependencies(application_id).await?;
1062
1063 let allow_application_logs = self
1064 .state
1065 .context()
1066 .extra()
1067 .execution_runtime_config()
1068 .allow_application_logs;
1069
1070 let contract_runtime_task = self
1071 .state
1072 .context()
1073 .extra()
1074 .thread_pool()
1075 .run_send(JsVec(codes), move |codes| async move {
1076 let runtime = ContractSyncRuntime::new(
1077 execution_state_sender,
1078 chain_id,
1079 refund_grant_to,
1080 controller,
1081 &action,
1082 allow_application_logs,
1083 );
1084
1085 for (code, description) in codes.0.into_iter().zip(descriptions) {
1086 runtime.preload_contract(ApplicationId::from(&description), code, description);
1087 }
1088
1089 runtime.run_action(application_id, chain_id, action)
1090 })
1091 .await;
1092
1093 async {
1094 while let Some(request) = execution_state_receiver.next().await {
1095 self.handle_request(request).await?;
1096 }
1097 Ok::<(), ExecutionError>(())
1098 }
1099 .instrument(info_span!("handle_runtime_requests"))
1100 .await?;
1101
1102 let (result, controller) = contract_runtime_task.await??;
1103
1104 self.resource_controller.is_free = false;
1105
1106 self.txn_tracker.add_operation_result(result);
1107
1108 self.resource_controller
1109 .with_state_and_grant(&mut self.state.system, grant)
1110 .await?
1111 .merge_balance(initial_balance, controller.balance()?)?;
1112 self.resource_controller.tracker = controller.tracker;
1113
1114 Ok(())
1115 }
1116
1117 #[instrument(skip_all, fields(
1118 chain_id = %context.chain_id,
1119 block_height = %context.height,
1120 operation_type = %operation.as_ref(),
1121 ))]
1122 pub async fn execute_operation(
1124 &mut self,
1125 context: OperationContext,
1126 operation: Operation,
1127 ) -> Result<(), ExecutionError> {
1128 assert_eq!(context.chain_id, self.state.context().extra().chain_id());
1129 match operation {
1130 Operation::System(op) => match *op {
1131 SystemOperation::Checkpoint => {
1132 let prepared = self.txn_tracker.take_prepared_checkpoint().ok_or(
1133 ExecutionError::CheckpointPreconditionFailed(
1134 "Checkpoint operation reached the actor without prepared inputs; \
1135 the chain-level pre-block hook must run prepare_checkpoint first",
1136 ),
1137 )?;
1138 self.state
1139 .apply_checkpoint(prepared, self.txn_tracker)
1140 .await?;
1141 self.summarize_events_at_checkpoint(context).await?;
1142 }
1143 op => {
1144 let new_application = self
1145 .state
1146 .system
1147 .execute_operation(context, op, self.txn_tracker, self.resource_controller)
1148 .await?;
1149 if let Some((application_id, argument)) = new_application {
1150 let user_action = UserAction::Instantiate(context, argument);
1151 self.run_user_action(
1152 application_id,
1153 user_action,
1154 context.refund_grant_to(),
1155 None,
1156 )
1157 .await?;
1158 }
1159 }
1160 },
1161 Operation::User {
1162 application_id,
1163 bytes,
1164 } => {
1165 self.run_user_action(
1166 application_id,
1167 UserAction::Operation(context, bytes),
1168 context.refund_grant_to(),
1169 None,
1170 )
1171 .await?;
1172 }
1173 }
1174 self.process_subscriptions(context.into()).await?;
1175 Ok(())
1176 }
1177
1178 #[instrument(skip_all, fields(
1179 chain_id = %context.chain_id,
1180 block_height = %context.height,
1181 origin = %context.origin,
1182 is_bouncing = %context.is_bouncing,
1183 message_type = %message.as_ref(),
1184 ))]
1185 pub async fn execute_message(
1187 &mut self,
1188 context: MessageContext,
1189 message: Message,
1190 grant: Option<&mut Amount>,
1191 ) -> Result<(), ExecutionError> {
1192 assert_eq!(context.chain_id, self.state.context().extra().chain_id());
1193 match message {
1194 Message::System(message) => {
1195 let outcome = self.state.system.execute_message(context, message).await?;
1196 self.txn_tracker.add_outgoing_messages(outcome);
1197 }
1198 Message::User {
1199 application_id,
1200 bytes,
1201 } => {
1202 self.run_user_action(
1203 application_id,
1204 UserAction::Message(context, bytes),
1205 context.refund_grant_to,
1206 grant,
1207 )
1208 .await?;
1209 }
1210 }
1211 self.process_subscriptions(context.into()).await?;
1212 Ok(())
1213 }
1214
1215 pub fn bounce_message(
1217 &mut self,
1218 context: MessageContext,
1219 grant: Amount,
1220 message: Message,
1221 ) -> Result<(), ExecutionError> {
1222 assert_eq!(context.chain_id, self.state.context().extra().chain_id());
1223 self.txn_tracker.add_outgoing_message(OutgoingMessage {
1224 destination: context.origin,
1225 authenticated_owner: context.authenticated_owner,
1226 refund_grant_to: context.refund_grant_to.filter(|_| !grant.is_zero()),
1227 grant,
1228 kind: MessageKind::Bouncing,
1229 message,
1230 });
1231 Ok(())
1232 }
1233
1234 pub fn send_refund(
1236 &mut self,
1237 context: MessageContext,
1238 amount: Amount,
1239 ) -> Result<(), ExecutionError> {
1240 assert_eq!(context.chain_id, self.state.context().extra().chain_id());
1241 if amount.is_zero() {
1242 return Ok(());
1243 }
1244 let Some(account) = context.refund_grant_to else {
1245 return Err(ExecutionError::InternalError(
1246 "Messages with grants should have a non-empty `refund_grant_to`",
1247 ));
1248 };
1249 let message = SystemMessage::Credit {
1250 amount,
1251 source: context.authenticated_owner.unwrap_or(AccountOwner::CHAIN),
1252 target: account.owner,
1253 };
1254 self.txn_tracker.add_outgoing_message(
1255 OutgoingMessage::new(account.chain_id, message).with_kind(MessageKind::Tracked),
1256 );
1257 Ok(())
1258 }
1259
1260 async fn receive_http_response(
1264 response: reqwest::Response,
1265 size_limit: u64,
1266 ) -> Result<http::Response, ExecutionError> {
1267 let status = response.status().as_u16();
1268 let maybe_content_length = response.content_length();
1269
1270 let headers = response
1271 .headers()
1272 .iter()
1273 .map(|(name, value)| http::Header::new(name.to_string(), value.as_bytes()))
1274 .collect::<Vec<_>>();
1275
1276 let total_header_size = headers
1277 .iter()
1278 .map(|header| (header.name.len() + header.value.len()) as u64)
1279 .sum();
1280
1281 let mut remaining_bytes = size_limit.checked_sub(total_header_size).ok_or(
1282 ExecutionError::HttpResponseSizeLimitExceeded {
1283 limit: size_limit,
1284 size: total_header_size,
1285 },
1286 )?;
1287
1288 if let Some(content_length) = maybe_content_length {
1289 if content_length > remaining_bytes {
1290 return Err(ExecutionError::HttpResponseSizeLimitExceeded {
1291 limit: size_limit,
1292 size: content_length + total_header_size,
1293 });
1294 }
1295 }
1296
1297 let mut body = Vec::with_capacity(
1298 usize::try_from(maybe_content_length.unwrap_or(0)).unwrap_or(usize::MAX),
1299 );
1300 let mut body_stream = response.bytes_stream();
1301
1302 while let Some(bytes) = body_stream.next().await.transpose()? {
1303 remaining_bytes = remaining_bytes.checked_sub(bytes.len() as u64).ok_or(
1304 ExecutionError::HttpResponseSizeLimitExceeded {
1305 limit: size_limit,
1306 size: bytes.len() as u64 + (size_limit - remaining_bytes),
1307 },
1308 )?;
1309
1310 body.extend(&bytes);
1311 }
1312
1313 Ok(http::Response {
1314 status,
1315 headers,
1316 body,
1317 })
1318 }
1319}
1320
1321#[derive(Debug, strum::AsRefStr)]
1323#[allow(missing_docs)]
1324pub enum ExecutionRequest {
1325 #[cfg(not(web))]
1326 LoadContract {
1327 id: ApplicationId,
1328 #[debug(skip)]
1329 callback: Sender<(UserContractCode, ApplicationDescription)>,
1330 },
1331
1332 #[cfg(not(web))]
1333 LoadService {
1334 id: ApplicationId,
1335 #[debug(skip)]
1336 callback: Sender<(UserServiceCode, ApplicationDescription)>,
1337 },
1338
1339 ChainBalance {
1340 #[debug(skip)]
1341 callback: Sender<Amount>,
1342 },
1343
1344 OwnerBalance {
1345 owner: AccountOwner,
1346 #[debug(skip)]
1347 callback: Sender<Amount>,
1348 },
1349
1350 OwnerBalances {
1351 #[debug(skip)]
1352 callback: Sender<Vec<(AccountOwner, Amount)>>,
1353 },
1354
1355 BalanceOwners {
1356 #[debug(skip)]
1357 callback: Sender<Vec<AccountOwner>>,
1358 },
1359
1360 Allowance {
1361 owner: AccountOwner,
1362 spender: AccountOwner,
1363 #[debug(skip)]
1364 callback: Sender<Amount>,
1365 },
1366
1367 Allowances {
1368 #[debug(skip)]
1369 callback: Sender<Vec<(AccountOwner, AccountOwner, Amount)>>,
1370 },
1371
1372 Transfer {
1373 source: AccountOwner,
1374 destination: Account,
1375 amount: Amount,
1376 #[debug(skip_if = Option::is_none)]
1377 signer: Option<AccountOwner>,
1378 application_id: ApplicationId,
1379 #[debug(skip)]
1380 callback: Sender<()>,
1381 },
1382
1383 Claim {
1384 source: Account,
1385 destination: Account,
1386 amount: Amount,
1387 #[debug(skip_if = Option::is_none)]
1388 signer: Option<AccountOwner>,
1389 application_id: ApplicationId,
1390 #[debug(skip)]
1391 callback: Sender<()>,
1392 },
1393
1394 Approve {
1395 owner: AccountOwner,
1396 spender: AccountOwner,
1397 amount: Amount,
1398 #[debug(skip_if = Option::is_none)]
1399 signer: Option<AccountOwner>,
1400 application_id: ApplicationId,
1401 #[debug(skip)]
1402 callback: Sender<()>,
1403 },
1404
1405 TransferFrom {
1406 owner: AccountOwner,
1407 spender: AccountOwner,
1408 destination: Account,
1409 amount: Amount,
1410 #[debug(skip_if = Option::is_none)]
1411 signer: Option<AccountOwner>,
1412 application_id: ApplicationId,
1413 #[debug(skip)]
1414 callback: Sender<()>,
1415 },
1416
1417 SystemTimestamp {
1418 #[debug(skip)]
1419 callback: Sender<Timestamp>,
1420 },
1421
1422 ChainOwnership {
1423 #[debug(skip)]
1424 callback: Sender<ChainOwnership>,
1425 },
1426
1427 ApplicationPermissions {
1428 #[debug(skip)]
1429 callback: Sender<ApplicationPermissions>,
1430 },
1431
1432 ReadApplicationDescription {
1433 application_id: ApplicationId,
1434 #[debug(skip)]
1435 callback: Sender<ApplicationDescription>,
1436 },
1437
1438 ReadValueBytes {
1439 id: ApplicationId,
1440 #[debug(with = hex_debug)]
1441 key: Vec<u8>,
1442 #[debug(skip)]
1443 callback: Sender<Option<Vec<u8>>>,
1444 },
1445
1446 ContainsKey {
1447 id: ApplicationId,
1448 key: Vec<u8>,
1449 #[debug(skip)]
1450 callback: Sender<bool>,
1451 },
1452
1453 ContainsKeys {
1454 id: ApplicationId,
1455 #[debug(with = hex_vec_debug)]
1456 keys: Vec<Vec<u8>>,
1457 callback: Sender<Vec<bool>>,
1458 },
1459
1460 ReadMultiValuesBytes {
1461 id: ApplicationId,
1462 #[debug(with = hex_vec_debug)]
1463 keys: Vec<Vec<u8>>,
1464 #[debug(skip)]
1465 callback: Sender<Vec<Option<Vec<u8>>>>,
1466 },
1467
1468 FindKeysByPrefix {
1469 id: ApplicationId,
1470 #[debug(with = hex_debug)]
1471 key_prefix: Vec<u8>,
1472 #[debug(skip)]
1473 callback: Sender<Vec<Vec<u8>>>,
1474 },
1475
1476 FindKeyValuesByPrefix {
1477 id: ApplicationId,
1478 #[debug(with = hex_debug)]
1479 key_prefix: Vec<u8>,
1480 #[debug(skip)]
1481 callback: Sender<Vec<(Vec<u8>, Vec<u8>)>>,
1482 },
1483
1484 WriteBatch {
1485 id: ApplicationId,
1486 batch: Batch,
1487 #[debug(skip)]
1488 callback: Sender<()>,
1489 },
1490
1491 OpenChain {
1492 config: Box<OpenChainConfig>,
1493 parent_id: ChainId,
1494 block_height: BlockHeight,
1495 timestamp: Timestamp,
1496 #[debug(skip)]
1497 callback: Sender<ChainId>,
1498 },
1499
1500 CloseChain {
1501 application_id: ApplicationId,
1502 #[debug(skip)]
1503 callback: Sender<Result<(), ExecutionError>>,
1504 },
1505
1506 ChangeOwnership {
1507 application_id: ApplicationId,
1508 ownership: ChainOwnership,
1509 #[debug(skip)]
1510 callback: Sender<Result<(), ExecutionError>>,
1511 },
1512
1513 ChangeApplicationPermissions {
1514 application_id: ApplicationId,
1515 application_permissions: ApplicationPermissions,
1516 #[debug(skip)]
1517 callback: Sender<Result<(), ExecutionError>>,
1518 },
1519
1520 PeekApplicationIndex {
1521 #[debug(skip)]
1522 callback: Sender<u32>,
1523 },
1524
1525 CreateApplication {
1526 chain_id: ChainId,
1527 block_height: BlockHeight,
1528 module_id: ModuleId,
1529 parameters: Vec<u8>,
1530 required_application_ids: Vec<ApplicationId>,
1531 #[debug(skip)]
1532 callback: Sender<CreateApplicationResult>,
1533 },
1534
1535 PerformHttpRequest {
1536 request: http::Request,
1537 http_responses_are_oracle_responses: bool,
1538 #[debug(skip)]
1539 callback: Sender<http::Response>,
1540 },
1541
1542 ReadBlobContent {
1543 blob_id: BlobId,
1544 #[debug(skip)]
1545 callback: Sender<BlobContent>,
1546 },
1547
1548 AssertBlobExists {
1549 blob_id: BlobId,
1550 #[debug(skip)]
1551 callback: Sender<()>,
1552 },
1553
1554 Emit {
1555 stream_id: StreamId,
1556 #[debug(with = hex_debug)]
1557 value: Vec<u8>,
1558 #[debug(skip)]
1559 callback: Sender<u32>,
1560 },
1561
1562 ReadEvent {
1563 event_id: EventId,
1564 callback: oneshot::Sender<Vec<u8>>,
1565 },
1566
1567 SubscribeToEvents {
1568 chain_id: ChainId,
1569 stream_id: StreamId,
1570 subscriber_app_id: ApplicationId,
1571 #[debug(skip)]
1572 callback: Sender<()>,
1573 },
1574
1575 UnsubscribeFromEvents {
1576 chain_id: ChainId,
1577 stream_id: StreamId,
1578 subscriber_app_id: ApplicationId,
1579 #[debug(skip)]
1580 callback: Sender<()>,
1581 },
1582
1583 GetApplicationPermissions {
1584 #[debug(skip)]
1585 callback: Sender<ApplicationPermissions>,
1586 },
1587
1588 QueryServiceOracle {
1589 deadline: Option<Instant>,
1590 application_id: ApplicationId,
1591 next_block_height: BlockHeight,
1592 query: Vec<u8>,
1593 #[debug(skip)]
1594 callback: Sender<Vec<u8>>,
1595 },
1596
1597 AddOutgoingMessage {
1598 message: crate::OutgoingMessage,
1599 #[debug(skip)]
1600 callback: Sender<()>,
1601 },
1602
1603 SetLocalTime {
1604 local_time: Timestamp,
1605 #[debug(skip)]
1606 callback: Sender<()>,
1607 },
1608
1609 AssertBefore {
1610 timestamp: Timestamp,
1611 #[debug(skip)]
1612 callback: Sender<Result<(), ExecutionError>>,
1613 },
1614
1615 AddCreatedBlob {
1616 blob: crate::Blob,
1617 #[debug(skip)]
1618 callback: Sender<()>,
1619 },
1620
1621 ValidationRound {
1622 round: Option<u32>,
1623 #[debug(skip)]
1624 callback: Sender<Option<u32>>,
1625 },
1626
1627 HasEmptyStorage {
1628 application: ApplicationId,
1629 #[debug(skip)]
1630 callback: Sender<bool>,
1631 },
1632
1633 AllowApplicationLogs {
1634 #[debug(skip)]
1635 callback: Sender<bool>,
1636 },
1637
1638 #[cfg(web)]
1640 Log {
1641 message: String,
1642 level: tracing::log::Level,
1643 },
1644}