locutus_stdlib/
component_interface.rs

1use std::{
2    borrow::{Borrow, Cow},
3    fmt::Display,
4    path::Path,
5};
6
7use blake2::{Blake2s256, Digest};
8use serde::{Deserialize, Serialize};
9use serde_with::serde_as;
10
11use crate::prelude::ContractInstanceId;
12
13const APPLICATION_HASH_SIZE: usize = 32;
14const COMPONENT_HASH_LENGTH: usize = 32;
15
16/// Executable component
17#[derive(Debug, Serialize, Deserialize, Clone)]
18#[serde_as]
19pub struct Component<'a> {
20    #[serde_as(as = "serde_with::Bytes")]
21    #[serde(borrow)]
22    code: Cow<'a, [u8]>,
23    key: ComponentKey,
24}
25
26impl PartialEq for Component<'_> {
27    fn eq(&self, other: &Self) -> bool {
28        self.key == other.key
29    }
30}
31
32impl Eq for Component<'_> {}
33
34impl Component<'_> {
35    pub fn key(&self) -> &ComponentKey {
36        &self.key
37    }
38
39    pub fn into_owned(self) -> Component<'static> {
40        Component {
41            key: self.key,
42            code: Cow::from(self.code.into_owned()),
43        }
44    }
45}
46
47impl AsRef<[u8]> for Component<'_> {
48    fn as_ref(&self) -> &[u8] {
49        self.code.borrow()
50    }
51}
52
53impl TryFrom<&Path> for Component<'static> {
54    type Error = std::io::Error;
55    fn try_from(path: &Path) -> Result<Self, Self::Error> {
56        let code = Cow::from(std::fs::read(path)?);
57        let key = ComponentKey::new(code.borrow());
58        Ok(Self { code, key })
59    }
60}
61
62impl From<Vec<u8>> for Component<'static> {
63    fn from(data: Vec<u8>) -> Self {
64        let key = ComponentKey::new(data.as_slice());
65        Component {
66            code: Cow::from(data),
67            key,
68        }
69    }
70}
71
72/// Type of errors during interaction with a component.
73#[derive(Debug, thiserror::Error, Serialize, Deserialize)]
74pub enum ComponentError {
75    #[error("de/serialization error: {0}")]
76    Deser(String),
77    #[error("{0}")]
78    Other(String),
79}
80
81#[serde_as]
82#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
83pub struct SecretsId {
84    #[serde_as(as = "serde_with::Bytes")]
85    key: Vec<u8>,
86    #[serde_as(as = "[_; 32]")]
87    hash: [u8; 32],
88}
89
90impl SecretsId {
91    pub fn new(key: Vec<u8>) -> Self {
92        let mut hasher = Blake2s256::new();
93        hasher.update(&key);
94        let hashed = hasher.finalize();
95        let mut hash = [0; 32];
96        hash.copy_from_slice(&hashed);
97        Self { key, hash }
98    }
99
100    pub fn encode(&self) -> String {
101        bs58::encode(self.hash)
102            .with_alphabet(bs58::Alphabet::BITCOIN)
103            .into_string()
104    }
105
106    /// Returns the hash of the contract key only.
107    pub fn code_hash(&self) -> &[u8; 32] {
108        &self.hash
109    }
110}
111
112pub trait ComponentInterface {
113    /// Process inbound message, producing zero or more outbound messages in response
114    /// Note that all state for the component must be stored using the secret mechanism.
115    fn process(messages: InboundComponentMsg) -> Result<Vec<OutboundComponentMsg>, ComponentError>;
116}
117
118#[serde_as]
119#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
120pub struct ComponentKey(#[serde_as(as = "[_; COMPONENT_HASH_LENGTH]")] [u8; COMPONENT_HASH_LENGTH]);
121
122impl ComponentKey {
123    pub fn new(wasm_code: &[u8]) -> Self {
124        let mut hasher = Blake2s256::new();
125        hasher.update(wasm_code);
126        let hashed = hasher.finalize();
127        let mut component_key = [0; COMPONENT_HASH_LENGTH];
128        component_key.copy_from_slice(&hashed);
129        Self(component_key)
130    }
131
132    pub fn encode(&self) -> String {
133        bs58::encode(self.0)
134            .with_alphabet(bs58::Alphabet::BITCOIN)
135            .into_string()
136    }
137
138    /// Returns the hash of the contract key only.
139    pub fn code_hash(&self) -> &[u8; COMPONENT_HASH_LENGTH] {
140        &self.0
141    }
142}
143
144impl Display for ComponentKey {
145    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146        write!(f, "{}", self.encode())
147    }
148}
149
150#[derive(Debug, Clone, Default, Serialize, Deserialize)]
151pub struct ComponentContext(pub Vec<u8>);
152
153impl ComponentContext {
154    pub const MAX_SIZE: usize = 4096 * 10 * 10;
155
156    pub fn new(bytes: Vec<u8>) -> Self {
157        assert!(bytes.len() < Self::MAX_SIZE);
158        Self(bytes)
159    }
160
161    pub fn append(&mut self, bytes: &mut Vec<u8>) {
162        assert!(self.0.len() + bytes.len() < Self::MAX_SIZE);
163        self.0.append(bytes)
164    }
165
166    pub fn replace(&mut self, bytes: Vec<u8>) {
167        assert!(bytes.len() < Self::MAX_SIZE);
168        let _ = std::mem::replace(&mut self.0, bytes);
169    }
170}
171
172#[serde_as]
173#[derive(Serialize, Deserialize)]
174pub struct ContractHash(#[serde_as(as = "[_; APPLICATION_HASH_SIZE]")] [u8; APPLICATION_HASH_SIZE]);
175
176#[derive(Serialize, Deserialize, Debug, Clone)]
177pub enum InboundComponentMsg<'a> {
178    ApplicationMessage(ApplicationMessage),
179    GetSecretResponse(GetSecretResponse),
180    RandomBytes(Vec<u8>),
181    UserResponse(#[serde(borrow)] UserInputResponse<'a>),
182    // GetContractResponse {
183    //     contract_id: ContractInstanceId,
184    //     #[serde(borrow)]
185    //     update_data: UpdateData<'static>,
186    //     context: Context
187    // },
188}
189
190impl InboundComponentMsg<'_> {
191    pub fn into_owned(self) -> InboundComponentMsg<'static> {
192        match self {
193            InboundComponentMsg::ApplicationMessage(r) => {
194                InboundComponentMsg::ApplicationMessage(r)
195            }
196            InboundComponentMsg::GetSecretResponse(r) => InboundComponentMsg::GetSecretResponse(r),
197            InboundComponentMsg::RandomBytes(b) => InboundComponentMsg::RandomBytes(b),
198            InboundComponentMsg::UserResponse(r) => {
199                InboundComponentMsg::UserResponse(r.into_owned())
200            }
201        }
202    }
203
204    pub fn get_context(&self) -> Option<&ComponentContext> {
205        match self {
206            InboundComponentMsg::ApplicationMessage(ApplicationMessage { context, .. }) => {
207                Some(context)
208            }
209            InboundComponentMsg::GetSecretResponse(GetSecretResponse { context, .. }) => {
210                Some(context)
211            }
212            _ => None,
213        }
214    }
215
216    pub fn get_mut_context(&mut self) -> Option<&mut ComponentContext> {
217        match self {
218            InboundComponentMsg::ApplicationMessage(ApplicationMessage { context, .. }) => {
219                Some(context)
220            }
221            InboundComponentMsg::GetSecretResponse(GetSecretResponse { context, .. }) => {
222                Some(context)
223            }
224            _ => None,
225        }
226    }
227}
228
229#[derive(Serialize, Deserialize, Debug, Clone)]
230pub struct GetSecretResponse {
231    pub key: SecretsId,
232    pub value: Option<Vec<u8>>,
233    pub context: ComponentContext,
234}
235
236#[non_exhaustive]
237#[derive(Serialize, Deserialize, Debug, Clone)]
238pub struct ApplicationMessage {
239    pub app: ContractInstanceId,
240    pub payload: Vec<u8>,
241    pub context: ComponentContext,
242    pub processed: bool,
243}
244
245impl ApplicationMessage {
246    pub fn new(app: ContractInstanceId, payload: Vec<u8>, processed: bool) -> Self {
247        Self {
248            app,
249            payload,
250            context: ComponentContext::default(),
251            processed,
252        }
253    }
254
255    pub fn with_context(mut self, context: ComponentContext) -> Self {
256        self.context = context;
257        self
258    }
259}
260
261#[derive(Serialize, Deserialize, Debug, Clone)]
262pub struct UserInputResponse<'a> {
263    request_id: u32,
264    #[serde(borrow)]
265    response: ClientResponse<'a>,
266}
267
268impl UserInputResponse<'_> {
269    pub fn into_owned(self) -> UserInputResponse<'static> {
270        UserInputResponse {
271            request_id: self.request_id,
272            response: self.response.into_owned(),
273        }
274    }
275}
276
277#[derive(Serialize, Deserialize, Debug)]
278pub enum OutboundComponentMsg {
279    // from the apps
280    ApplicationMessage(ApplicationMessage),
281    RequestUserInput(#[serde(deserialize_with = "deser_func")] UserInputRequest<'static>),
282    // from the node
283    GetSecretRequest(GetSecretRequest),
284    SetSecretRequest(SetSecretRequest),
285    RandomBytesRequest(usize),
286    // GetContractRequest {
287    //     mode: RelatedMode,
288    //     contract_id: ContractInstanceId,
289    // },
290}
291
292impl From<GetSecretRequest> for OutboundComponentMsg {
293    fn from(req: GetSecretRequest) -> Self {
294        Self::GetSecretRequest(req)
295    }
296}
297
298impl From<ApplicationMessage> for OutboundComponentMsg {
299    fn from(req: ApplicationMessage) -> Self {
300        Self::ApplicationMessage(req)
301    }
302}
303
304impl OutboundComponentMsg {
305    pub fn processed(&self) -> bool {
306        match self {
307            OutboundComponentMsg::ApplicationMessage(msg) => msg.processed,
308            OutboundComponentMsg::GetSecretRequest(msg) => msg.processed,
309            OutboundComponentMsg::RandomBytesRequest(_) => false,
310            OutboundComponentMsg::SetSecretRequest(_) => false,
311            OutboundComponentMsg::RequestUserInput(_) => true,
312        }
313    }
314
315    pub fn get_context(&self) -> Option<&ComponentContext> {
316        match self {
317            OutboundComponentMsg::ApplicationMessage(ApplicationMessage { context, .. }) => {
318                Some(context)
319            }
320            OutboundComponentMsg::GetSecretRequest(GetSecretRequest { context, .. }) => {
321                Some(context)
322            }
323            _ => None,
324        }
325    }
326
327    pub fn get_mut_context(&mut self) -> Option<&mut ComponentContext> {
328        match self {
329            OutboundComponentMsg::ApplicationMessage(ApplicationMessage { context, .. }) => {
330                Some(context)
331            }
332            OutboundComponentMsg::GetSecretRequest(GetSecretRequest { context, .. }) => {
333                Some(context)
334            }
335            _ => None,
336        }
337    }
338}
339
340fn deser_func<'de, D>(deser: D) -> Result<UserInputRequest<'static>, D::Error>
341where
342    D: serde::Deserializer<'de>,
343{
344    let value = <UserInputRequest<'de> as Deserialize>::deserialize(deser)?;
345    Ok(value.into_owned())
346}
347
348#[derive(Serialize, Deserialize, Debug)]
349pub struct GetSecretRequest {
350    pub key: SecretsId,
351    pub context: ComponentContext,
352    pub processed: bool,
353}
354
355#[derive(Serialize, Deserialize, Debug)]
356pub struct SetSecretRequest {
357    pub key: SecretsId,
358    /// Sets or unsets (if none) a value associated with the key.
359    pub value: Option<Vec<u8>>,
360}
361
362#[serde_as]
363#[derive(Serialize, Deserialize, Debug, Clone)]
364pub struct NotificationMessage<'a>(
365    #[serde_as(as = "serde_with::Bytes")]
366    #[serde(borrow)]
367    Cow<'a, [u8]>,
368);
369
370impl NotificationMessage<'_> {
371    pub fn into_owned(self) -> NotificationMessage<'static> {
372        NotificationMessage(self.0.into_owned().into())
373    }
374}
375
376#[serde_as]
377#[derive(Serialize, Deserialize, Debug, Clone)]
378pub struct ClientResponse<'a>(
379    #[serde_as(as = "serde_with::Bytes")]
380    #[serde(borrow)]
381    Cow<'a, [u8]>,
382);
383
384impl ClientResponse<'_> {
385    pub fn into_owned(self) -> ClientResponse<'static> {
386        ClientResponse(self.0.into_owned().into())
387    }
388}
389
390#[derive(Serialize, Deserialize, Debug, Clone)]
391pub struct UserInputRequest<'a> {
392    request_id: u32,
393    #[serde(borrow)]
394    /// An interpretable message by the notification system.
395    message: NotificationMessage<'a>,
396    /// If a response is required from the user they can be chosen from this list.
397    responses: Vec<ClientResponse<'a>>,
398}
399
400impl UserInputRequest<'_> {
401    pub fn into_owned(self) -> UserInputRequest<'static> {
402        UserInputRequest {
403            request_id: self.request_id,
404            message: self.message.into_owned(),
405            responses: self.responses.into_iter().map(|r| r.into_owned()).collect(),
406        }
407    }
408}
409
410#[doc(hidden)]
411pub(crate) mod wasm_interface {
412    //! Contains all the types to interface between the host environment and
413    //! the wasm module execution.
414    use super::*;
415    use crate::WasmLinearMem;
416
417    #[repr(C)]
418    #[derive(Debug, Clone, Copy)]
419    pub struct ComponentInterfaceResult {
420        ptr: i64,
421        size: u32,
422    }
423
424    impl ComponentInterfaceResult {
425        pub unsafe fn from_raw(ptr: i64, mem: &WasmLinearMem) -> Self {
426            let result = Box::leak(Box::from_raw(crate::buf::compute_ptr(
427                ptr as *mut Self,
428                mem,
429            )));
430            #[cfg(feature = "trace")]
431            {
432                tracing::trace!(
433                    "got FFI result @ {ptr} ({:p}) -> {result:?}",
434                    ptr as *mut Self
435                );
436            }
437            *result
438        }
439
440        pub fn into_raw(self) -> i64 {
441            #[cfg(feature = "trace")]
442            {
443                tracing::trace!("returning FFI -> {self:?}");
444            }
445            let ptr = Box::into_raw(Box::new(self));
446            #[cfg(feature = "trace")]
447            {
448                tracing::trace!("FFI result ptr: {ptr:p} ({}i64)", ptr as i64);
449            }
450            ptr as _
451        }
452
453        pub unsafe fn unwrap(
454            self,
455            mem: WasmLinearMem,
456        ) -> Result<Vec<OutboundComponentMsg>, ComponentError> {
457            let ptr = crate::buf::compute_ptr(self.ptr as *mut u8, &mem);
458            let serialized = std::slice::from_raw_parts(ptr as *const u8, self.size as _);
459            let value: Result<Vec<OutboundComponentMsg>, ComponentError> =
460                bincode::deserialize(serialized)
461                    .map_err(|e| ComponentError::Other(format!("{e}")))?;
462            #[cfg(feature = "trace")]
463            {
464                tracing::trace!(
465                    "got result through FFI; addr: {:p} ({}i64, mapped: {ptr:p})
466                     serialized: {serialized:?}
467                     value: {value:?}",
468                    self.ptr as *mut u8,
469                    self.ptr
470                );
471            }
472            value
473        }
474    }
475
476    impl From<Result<Vec<OutboundComponentMsg>, ComponentError>> for ComponentInterfaceResult {
477        fn from(value: Result<Vec<OutboundComponentMsg>, ComponentError>) -> Self {
478            let serialized = bincode::serialize(&value).unwrap();
479            let size = serialized.len() as _;
480            let ptr = serialized.as_ptr();
481            #[cfg(feature = "trace")]
482            {
483                tracing::trace!(
484                "sending result through FFI; addr: {ptr:p} ({}),\n  serialized: {serialized:?}\n  value: {value:?}",
485                ptr as i64
486            );
487            }
488            std::mem::forget(serialized);
489            Self {
490                ptr: ptr as i64,
491                size,
492            }
493        }
494    }
495}