Skip to main content

calimero_primitives/
application.rs

1#[cfg(test)]
2#[path = "tests/application.rs"]
3mod tests;
4
5use core::fmt::{self, Display, Formatter};
6use core::ops::Deref;
7use core::str::FromStr;
8#[cfg(feature = "borsh")]
9use std::io;
10
11#[cfg(feature = "borsh")]
12use borsh::{BorshDeserialize, BorshSerialize};
13use serde::de::{Error as SerdeError, Visitor};
14use serde::{Deserialize, Deserializer, Serialize, Serializer};
15use thiserror::Error as ThisError;
16use url::{ParseError, Url};
17
18use crate::blobs::BlobId;
19use crate::hash::{Hash, HashError};
20
21#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Ord, PartialOrd)]
22#[cfg_attr(
23    feature = "borsh",
24    derive(borsh::BorshDeserialize, borsh::BorshSerialize)
25)]
26// todo! define macros that construct newtypes
27// todo! wrapping Hash<N> with this interface
28pub struct ApplicationId(Hash);
29
30impl From<[u8; 32]> for ApplicationId {
31    fn from(id: [u8; 32]) -> Self {
32        Self(id.into())
33    }
34}
35
36impl AsRef<[u8; 32]> for ApplicationId {
37    fn as_ref(&self) -> &[u8; 32] {
38        &self.0
39    }
40}
41
42impl Deref for ApplicationId {
43    type Target = [u8; 32];
44
45    fn deref(&self) -> &Self::Target {
46        &self.0
47    }
48}
49
50impl ApplicationId {
51    #[must_use]
52    pub fn as_str(&self) -> &str {
53        self.0.as_str()
54    }
55}
56
57impl Display for ApplicationId {
58    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
59        f.pad(self.as_str())
60    }
61}
62
63impl From<ApplicationId> for String {
64    fn from(id: ApplicationId) -> Self {
65        id.as_str().to_owned()
66    }
67}
68
69impl From<&ApplicationId> for String {
70    fn from(id: &ApplicationId) -> Self {
71        id.as_str().to_owned()
72    }
73}
74
75#[derive(Clone, Copy, Debug, ThisError)]
76#[error(transparent)]
77pub struct InvalidApplicationId(HashError);
78
79impl FromStr for ApplicationId {
80    type Err = InvalidApplicationId;
81
82    fn from_str(s: &str) -> Result<Self, Self::Err> {
83        Ok(Self(s.parse().map_err(InvalidApplicationId)?))
84    }
85}
86
87/// Signer identifier derived from the Ed25519 public key that signs the MPK bundle.
88/// Establishes cryptographic update authority. Must be non-empty.
89/// In v0, encoded as did:key: `did:key:z{base58btc(0xed01 || public_key)}`.
90
91#[derive(Clone, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
92pub struct SignerId(Box<str>);
93
94impl SignerId {
95    /// Creates a new `SignerId` from a string.
96    ///
97    /// # Errors
98    ///
99    /// Returns [`InvalidSignerId::Empty`] if the string is empty.
100    pub fn new(s: impl Into<Box<str>>) -> Result<Self, InvalidSignerId> {
101        let s = s.into();
102        if s.is_empty() {
103            return Err(InvalidSignerId::Empty);
104        }
105        Ok(Self(s))
106    }
107
108    /// Returns the signerId as a string slice.
109    #[must_use]
110    pub fn as_str(&self) -> &str {
111        &self.0
112    }
113}
114
115impl Deref for SignerId {
116    type Target = str;
117
118    fn deref(&self) -> &Self::Target {
119        &self.0
120    }
121}
122
123impl AsRef<str> for SignerId {
124    fn as_ref(&self) -> &str {
125        &self.0
126    }
127}
128
129impl Display for SignerId {
130    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
131        f.pad(&self.0)
132    }
133}
134
135impl From<SignerId> for String {
136    fn from(id: SignerId) -> Self {
137        id.0.into_string()
138    }
139}
140
141impl From<&SignerId> for String {
142    fn from(id: &SignerId) -> Self {
143        id.0.to_string()
144    }
145}
146
147/// Error type for invalid signer identifiers.
148#[derive(Clone, Copy, Debug, ThisError)]
149#[non_exhaustive]
150pub enum InvalidSignerId {
151    /// The signerId string is empty.
152    #[error("signerId cannot be empty")]
153    Empty,
154}
155
156impl FromStr for SignerId {
157    type Err = InvalidSignerId;
158
159    fn from_str(s: &str) -> Result<Self, Self::Err> {
160        Self::new(s)
161    }
162}
163
164impl Serialize for SignerId {
165    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
166        serializer.serialize_str(&self.0)
167    }
168}
169
170impl<'de> Deserialize<'de> for SignerId {
171    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
172        struct SignerIdVisitor;
173
174        impl Visitor<'_> for SignerIdVisitor {
175            type Value = SignerId;
176
177            fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
178                formatter.write_str("a non-empty signer identifier string")
179            }
180
181            fn visit_str<E: SerdeError>(self, v: &str) -> Result<Self::Value, E> {
182                SignerId::new(v).map_err(E::custom)
183            }
184        }
185
186        deserializer.deserialize_str(SignerIdVisitor)
187    }
188}
189
190#[cfg(feature = "borsh")]
191impl BorshSerialize for SignerId {
192    fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
193        // Serialize as length-prefixed bytes
194        let bytes = self.0.as_bytes();
195        let len = bytes.len() as u32;
196        BorshSerialize::serialize(&len, writer)?;
197        writer.write_all(bytes)
198    }
199}
200
201#[cfg(feature = "borsh")]
202impl BorshDeserialize for SignerId {
203    fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
204        let len = u32::deserialize_reader(reader)? as usize;
205        let mut bytes = vec![0u8; len];
206        reader.read_exact(&mut bytes)?;
207
208        let s =
209            String::from_utf8(bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
210        SignerId::new(s).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
211    }
212}
213
214/// Stable application identity: (appId, signerId). An app is uniquely
215/// identified by its AppKey. appId is the manifest `package` (human-friendly label, not
216/// a security boundary); signerId is the cryptographic update authority.
217/// Display format is `{app_id}:{signer_id}`, used as keys in Desired State Documents.
218#[derive(Clone, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
219pub struct AppKey {
220    /// The application identifier (package name from manifest).
221    app_id: Box<str>,
222    /// The signer identifier (did:key format).
223    signer_id: SignerId,
224}
225
226impl AppKey {
227    /// Creates a new `AppKey` from an app ID and signer ID.
228    ///
229    /// # Errors
230    ///
231    /// Returns [`InvalidAppKey::EmptyAppId`] if the app_id is empty.
232    /// Returns [`InvalidAppKey::ColonInAppId`] if the app_id contains a colon.
233    pub fn new(app_id: impl Into<Box<str>>, signer_id: SignerId) -> Result<Self, InvalidAppKey> {
234        let app_id = app_id.into();
235        if app_id.is_empty() {
236            return Err(InvalidAppKey::EmptyAppId);
237        }
238        if app_id.contains(':') {
239            return Err(InvalidAppKey::ColonInAppId);
240        }
241        Ok(Self { app_id, signer_id })
242    }
243
244    /// Returns the application identifier.
245    #[must_use]
246    pub fn app_id(&self) -> &str {
247        &self.app_id
248    }
249
250    /// Returns the signer identifier.
251    #[must_use]
252    pub fn signer_id(&self) -> &SignerId {
253        &self.signer_id
254    }
255}
256
257impl Display for AppKey {
258    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
259        write!(f, "{}:{}", self.app_id, self.signer_id)
260    }
261}
262
263impl From<AppKey> for String {
264    fn from(key: AppKey) -> Self {
265        key.to_string()
266    }
267}
268
269impl From<&AppKey> for String {
270    fn from(key: &AppKey) -> Self {
271        key.to_string()
272    }
273}
274
275/// Error type for invalid AppKey.
276#[derive(Clone, Debug, ThisError)]
277#[non_exhaustive]
278pub enum InvalidAppKey {
279    /// The appId is empty.
280    #[error("appId cannot be empty")]
281    EmptyAppId,
282
283    /// The appId contains a colon, which is not allowed (used as separator in serialized format).
284    #[error("appId cannot contain ':' (colon is used as separator in serialized format)")]
285    ColonInAppId,
286
287    /// The signerId is invalid.
288    #[error("invalid signerId: {0}")]
289    InvalidSignerId(#[from] InvalidSignerId),
290
291    /// The AppKey string format is invalid (missing separator).
292    #[error("invalid AppKey format: expected 'appId:signerId', got '{0}'")]
293    InvalidFormat(String),
294}
295
296impl FromStr for AppKey {
297    type Err = InvalidAppKey;
298
299    fn from_str(s: &str) -> Result<Self, Self::Err> {
300        // Find the first colon separator
301        // Note: signerId (did:key:...) contains colons, so we split on the first colon only
302        let separator_pos = s
303            .find(':')
304            .ok_or_else(|| InvalidAppKey::InvalidFormat(s.to_owned()))?;
305
306        let app_id = &s[..separator_pos];
307        let signer_id_str = &s[separator_pos + 1..];
308
309        let signer_id = SignerId::new(signer_id_str)?;
310        Self::new(app_id, signer_id)
311    }
312}
313
314impl Serialize for AppKey {
315    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
316        serializer.serialize_str(&self.to_string())
317    }
318}
319
320impl<'de> Deserialize<'de> for AppKey {
321    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
322        struct AppKeyVisitor;
323
324        impl Visitor<'_> for AppKeyVisitor {
325            type Value = AppKey;
326
327            fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
328                formatter.write_str("a string in format 'appId:signerId'")
329            }
330
331            fn visit_str<E: SerdeError>(self, v: &str) -> Result<Self::Value, E> {
332                AppKey::from_str(v).map_err(E::custom)
333            }
334        }
335
336        deserializer.deserialize_str(AppKeyVisitor)
337    }
338}
339
340#[cfg(feature = "borsh")]
341impl BorshSerialize for AppKey {
342    fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
343        // Serialize app_id as length-prefixed bytes
344        let app_id_bytes = self.app_id.as_bytes();
345        let app_id_len = app_id_bytes.len() as u32;
346        BorshSerialize::serialize(&app_id_len, writer)?;
347        writer.write_all(app_id_bytes)?;
348
349        // Serialize signer_id
350        BorshSerialize::serialize(&self.signer_id, writer)
351    }
352}
353
354#[cfg(feature = "borsh")]
355impl BorshDeserialize for AppKey {
356    fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
357        // Deserialize app_id
358        let app_id_len = u32::deserialize_reader(reader)? as usize;
359        let mut app_id_bytes = vec![0u8; app_id_len];
360        reader.read_exact(&mut app_id_bytes)?;
361        let app_id = String::from_utf8(app_id_bytes)
362            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?
363            .into_boxed_str();
364
365        // Deserialize signer_id
366        let signer_id = SignerId::deserialize_reader(reader)?;
367
368        AppKey::new(app_id, signer_id).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
369    }
370}
371
372#[derive(Clone, Debug, Deserialize, Serialize)]
373pub struct ApplicationSource(Url);
374
375impl FromStr for ApplicationSource {
376    type Err = ParseError;
377
378    fn from_str(s: &str) -> Result<Self, Self::Err> {
379        s.parse().map(Self)
380    }
381}
382
383impl From<Url> for ApplicationSource {
384    fn from(value: Url) -> Self {
385        Self(value)
386    }
387}
388
389impl From<ApplicationSource> for Url {
390    fn from(value: ApplicationSource) -> Self {
391        value.0
392    }
393}
394
395impl Display for ApplicationSource {
396    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
397        Display::fmt(&self.0, f)
398    }
399}
400
401#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
402#[cfg_attr(
403    feature = "borsh",
404    derive(borsh::BorshDeserialize, borsh::BorshSerialize)
405)]
406pub struct ApplicationBlob {
407    pub bytecode: BlobId,
408    pub compiled: BlobId,
409}
410
411#[derive(Clone, Debug, Deserialize, Serialize)]
412#[non_exhaustive]
413pub struct Application {
414    pub id: ApplicationId,
415    pub blob: ApplicationBlob,
416    pub size: u64,
417    pub source: ApplicationSource,
418    pub metadata: Vec<u8>,
419}
420
421impl Application {
422    #[must_use]
423    pub const fn new(
424        id: ApplicationId,
425        blob: ApplicationBlob,
426        size: u64,
427        source: ApplicationSource,
428        metadata: Vec<u8>,
429    ) -> Self {
430        Self {
431            id,
432            blob,
433            size,
434            source,
435            metadata,
436        }
437    }
438}