linera_execution/
applications.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::collections::{HashMap, HashSet};
5
6use linera_base::{data_types::UserApplicationDescription, identifiers::UserApplicationId};
7use linera_views::{
8    context::Context,
9    map_view::HashedMapView,
10    views::{ClonableView, HashableView},
11};
12#[cfg(with_testing)]
13use {
14    linera_views::context::{create_test_memory_context, MemoryContext},
15    linera_views::views::View,
16    std::collections::BTreeMap,
17};
18
19use crate::SystemExecutionError;
20
21#[cfg(test)]
22#[path = "unit_tests/applications_tests.rs"]
23mod applications_tests;
24
25#[derive(Debug, ClonableView, HashableView)]
26pub struct ApplicationRegistryView<C> {
27    /// The applications that are known by the chain.
28    pub known_applications: HashedMapView<C, UserApplicationId, UserApplicationDescription>,
29}
30
31#[cfg(with_testing)]
32#[derive(Default, Eq, PartialEq, Debug, Clone)]
33pub struct ApplicationRegistry {
34    pub known_applications: BTreeMap<UserApplicationId, UserApplicationDescription>,
35}
36
37impl<C> ApplicationRegistryView<C>
38where
39    C: Context + Clone + Send + Sync + 'static,
40{
41    #[cfg(with_testing)]
42    pub fn import(&mut self, registry: ApplicationRegistry) -> Result<(), SystemExecutionError> {
43        for (id, description) in registry.known_applications {
44            self.known_applications.insert(&id, description)?;
45        }
46        Ok(())
47    }
48
49    /// Registers an existing application.
50    ///
51    /// Keeps track of an existing application that the current chain is seeing for the first time.
52    pub async fn register_application(
53        &mut self,
54        application: UserApplicationDescription,
55    ) -> Result<UserApplicationId, SystemExecutionError> {
56        // Make sure that referenced applications ids have been registered.
57        for required_id in &application.required_application_ids {
58            self.describe_application(*required_id).await?;
59        }
60        let id = UserApplicationId::from(&application);
61        self.known_applications.insert(&id, application)?;
62        Ok(id)
63    }
64
65    /// Registers a newly created application.
66    pub async fn register_new_application(
67        &mut self,
68        application_id: UserApplicationId,
69        parameters: Vec<u8>,
70        required_application_ids: Vec<UserApplicationId>,
71    ) -> Result<(), SystemExecutionError> {
72        // Make sure that referenced applications ids have been registered.
73        for required_id in &required_application_ids {
74            self.describe_application(*required_id).await?;
75        }
76        // Create description and register it.
77        let UserApplicationId {
78            bytecode_id,
79            creation,
80        } = application_id;
81        let description = UserApplicationDescription {
82            bytecode_id,
83            parameters,
84            creation,
85            required_application_ids,
86        };
87        self.known_applications
88            .insert(&application_id, description)?;
89        Ok(())
90    }
91
92    /// Retrieves an application's description.
93    pub async fn describe_application(
94        &self,
95        id: UserApplicationId,
96    ) -> Result<UserApplicationDescription, SystemExecutionError> {
97        self.known_applications
98            .get(&id)
99            .await?
100            .ok_or_else(|| SystemExecutionError::UnknownApplicationId(Box::new(id)))
101    }
102
103    /// Retrieves the recursive dependencies of applications and apply a topological sort.
104    pub async fn find_dependencies(
105        &self,
106        mut stack: Vec<UserApplicationId>,
107        registered_apps: &HashMap<UserApplicationId, UserApplicationDescription>,
108    ) -> Result<Vec<UserApplicationId>, SystemExecutionError> {
109        // What we return at the end.
110        let mut result = Vec::new();
111        // The entries already inserted in `result`.
112        let mut sorted = HashSet::new();
113        // The entries for which dependencies have already been pushed once to the stack.
114        let mut seen = HashSet::new();
115
116        while let Some(id) = stack.pop() {
117            if sorted.contains(&id) {
118                continue;
119            }
120            if seen.contains(&id) {
121                // Second time we see this entry. It was last pushed just before its
122                // dependencies -- which are now fully sorted.
123                sorted.insert(id);
124                result.push(id);
125                continue;
126            }
127            // First time we see this entry:
128            // 1. Mark it so that its dependencies are no longer pushed to the stack.
129            seen.insert(id);
130            // 2. Schedule all the (yet unseen) dependencies, then this entry for a second visit.
131            stack.push(id);
132            let app = if let Some(app) = registered_apps.get(&id) {
133                app.clone()
134            } else {
135                self.describe_application(id).await?
136            };
137            for child in app.required_application_ids.iter().rev() {
138                if !seen.contains(child) {
139                    stack.push(*child);
140                }
141            }
142        }
143        Ok(result)
144    }
145
146    /// Retrieves applications' descriptions preceded by their recursive dependencies.
147    pub async fn describe_applications_with_dependencies(
148        &self,
149        ids: Vec<UserApplicationId>,
150        extra_registered_apps: &HashMap<UserApplicationId, UserApplicationDescription>,
151    ) -> Result<Vec<UserApplicationDescription>, SystemExecutionError> {
152        let ids_with_deps = self.find_dependencies(ids, extra_registered_apps).await?;
153        let mut result = Vec::new();
154        for id in ids_with_deps {
155            let description = if let Some(description) = extra_registered_apps.get(&id) {
156                description.clone()
157            } else {
158                self.describe_application(id).await?
159            };
160            result.push(description);
161        }
162        Ok(result)
163    }
164}
165
166#[cfg(with_testing)]
167impl ApplicationRegistryView<MemoryContext<()>>
168where
169    MemoryContext<()>: Context + Clone + Send + Sync + 'static,
170{
171    pub async fn new() -> Self {
172        let context = create_test_memory_context();
173        Self::load(context)
174            .await
175            .expect("Loading from memory should work")
176    }
177}