calimero_node_primitives/client/
application.rs1use std::io;
2use std::sync::Arc;
3
4use calimero_primitives::application::{
5 Application, ApplicationBlob, ApplicationId, ApplicationSource,
6};
7use calimero_primitives::blobs::BlobId;
8use calimero_primitives::hash::Hash;
9use calimero_store::{key, types};
10use camino::Utf8PathBuf;
11use eyre::bail;
12use futures_util::TryStreamExt;
13use reqwest::Url;
14use tokio::fs::File;
15use tokio_util::compat::TokioAsyncReadCompatExt;
16
17use super::NodeClient;
18
19impl NodeClient {
20 pub fn get_application(
21 &self,
22 application_id: &ApplicationId,
23 ) -> eyre::Result<Option<Application>> {
24 let handle = self.datastore.handle();
25
26 let key = key::ApplicationMeta::new(*application_id);
27
28 let Some(application) = handle.get(&key)? else {
29 return Ok(None);
30 };
31
32 let application = Application::new(
33 *application_id,
34 ApplicationBlob {
35 bytecode: application.bytecode.blob_id(),
36 compiled: application.compiled.blob_id(),
37 },
38 application.size,
39 application.source.parse()?,
40 application.metadata.into_vec(),
41 );
42
43 Ok(Some(application))
44 }
45
46 pub async fn get_application_bytes(
47 &self,
48 application_id: &ApplicationId,
49 ) -> eyre::Result<Option<Arc<[u8]>>> {
50 let handle = self.datastore.handle();
51
52 let key = key::ApplicationMeta::new(*application_id);
53
54 let Some(application) = handle.get(&key)? else {
55 return Ok(None);
56 };
57
58 let Some(bytes) = self
59 .get_blob_bytes(&application.bytecode.blob_id(), None)
60 .await?
61 else {
62 bail!("fatal: application points to dangling blob");
63 };
64
65 Ok(Some(bytes))
66 }
67
68 pub fn has_application(&self, application_id: &ApplicationId) -> eyre::Result<bool> {
69 let handle = self.datastore.handle();
70
71 let key = key::ApplicationMeta::new(*application_id);
72
73 if let Some(application) = handle.get(&key)? {
74 return self.has_blob(&application.bytecode.blob_id());
75 }
76
77 Ok(false)
78 }
79
80 pub fn install_application(
81 &self,
82 blob_id: &BlobId,
83 size: u64,
84 source: &ApplicationSource,
85 metadata: Vec<u8>,
86 ) -> eyre::Result<ApplicationId> {
87 let application = types::ApplicationMeta::new(
88 key::BlobMeta::new(*blob_id),
89 size,
90 source.to_string().into_boxed_str(),
91 metadata.into_boxed_slice(),
92 key::BlobMeta::new(BlobId::from([0; 32])),
93 );
94
95 let application_id = {
96 let components = (
97 application.bytecode,
98 application.size,
99 &application.source,
100 &application.metadata,
101 );
102
103 ApplicationId::from(*Hash::hash_borsh(&components)?)
104 };
105
106 let mut handle = self.datastore.handle();
107
108 let key = key::ApplicationMeta::new(application_id);
109
110 handle.put(&key, &application)?;
111
112 Ok(application_id)
113 }
114
115 pub async fn install_application_from_path(
116 &self,
117 path: Utf8PathBuf,
118 metadata: Vec<u8>,
119 ) -> eyre::Result<ApplicationId> {
120 let path = path.canonicalize_utf8()?;
121
122 let file = File::open(&path).await?;
123
124 let expected_size = file.metadata().await?.len();
125
126 let (blob_id, size) = self
127 .add_blob(file.compat(), Some(expected_size), None)
128 .await?;
129
130 let Ok(uri) = Url::from_file_path(path) else {
131 bail!("non-absolute path")
132 };
133
134 self.install_application(&blob_id, size, &uri.as_str().parse()?, metadata)
135 }
136
137 pub async fn install_application_from_url(
138 &self,
139 url: Url,
140 metadata: Vec<u8>,
141 expected_hash: Option<&Hash>,
142 ) -> eyre::Result<ApplicationId> {
143 let uri = url.as_str().parse()?;
144
145 let response = reqwest::Client::new().get(url).send().await?;
146
147 let expected_size = response.content_length();
148
149 let (blob_id, size) = self
150 .add_blob(
151 response
152 .bytes_stream()
153 .map_err(io::Error::other)
154 .into_async_read(),
155 expected_size,
156 expected_hash,
157 )
158 .await?;
159
160 self.install_application(&blob_id, size, &uri, metadata)
161 }
162
163 pub fn uninstall_application(&self, application_id: &ApplicationId) -> eyre::Result<()> {
164 let mut handle = self.datastore.handle();
165
166 let key = key::ApplicationMeta::new(*application_id);
167
168 handle.delete(&key)?;
169
170 Ok(())
171 }
172
173 pub fn list_applications(&self) -> eyre::Result<Vec<Application>> {
174 let handle = self.datastore.handle();
175
176 let mut iter = handle.iter::<key::ApplicationMeta>()?;
177
178 let mut applications = vec![];
179
180 for (id, app) in iter.entries() {
181 let (id, app) = (id?, app?);
182 applications.push(Application::new(
183 id.application_id(),
184 ApplicationBlob {
185 bytecode: app.bytecode.blob_id(),
186 compiled: app.compiled.blob_id(),
187 },
188 app.size,
189 app.source.parse()?,
190 app.metadata.to_vec(),
191 ));
192 }
193
194 Ok(applications)
195 }
196
197 pub fn update_compiled_app(
198 &self,
199 application_id: &ApplicationId,
200 compiled_blob_id: &BlobId,
201 ) -> eyre::Result<()> {
202 let mut handle = self.datastore.handle();
203
204 let key = key::ApplicationMeta::new(*application_id);
205
206 let Some(mut application) = handle.get(&key)? else {
207 bail!("application not found");
208 };
209
210 application.compiled = key::BlobMeta::new(*compiled_blob_id);
211
212 handle.put(&key, &application)?;
213
214 Ok(())
215 }
216}