Skip to main content

freenet_stdlib/
delegate_interface.rs

1use std::{
2    borrow::{Borrow, Cow},
3    fmt::Display,
4    fs::File,
5    io::Read,
6    ops::Deref,
7    path::Path,
8};
9
10use blake3::{traits::digest::Digest, Hasher as Blake3};
11use serde::{Deserialize, Deserializer, Serialize};
12use serde_with::serde_as;
13
14use crate::generated::client_request::{
15    DelegateKey as FbsDelegateKey, InboundDelegateMsg as FbsInboundDelegateMsg,
16    InboundDelegateMsgType,
17};
18
19use crate::common_generated::common::SecretsId as FbsSecretsId;
20
21use crate::client_api::{TryFromFbs, WsApiError};
22use crate::prelude::{ContractInstanceId, WrappedState, CONTRACT_KEY_SIZE};
23use crate::{code_hash::CodeHash, prelude::Parameters};
24
25const DELEGATE_HASH_LENGTH: usize = 32;
26
27#[derive(Clone, Debug, Serialize, Deserialize)]
28pub struct Delegate<'a> {
29    #[serde(borrow)]
30    parameters: Parameters<'a>,
31    #[serde(borrow)]
32    pub data: DelegateCode<'a>,
33    key: DelegateKey,
34}
35
36impl Delegate<'_> {
37    pub fn key(&self) -> &DelegateKey {
38        &self.key
39    }
40
41    pub fn code(&self) -> &DelegateCode<'_> {
42        &self.data
43    }
44
45    pub fn code_hash(&self) -> &CodeHash {
46        &self.data.code_hash
47    }
48
49    pub fn params(&self) -> &Parameters<'_> {
50        &self.parameters
51    }
52
53    pub fn into_owned(self) -> Delegate<'static> {
54        Delegate {
55            parameters: self.parameters.into_owned(),
56            data: self.data.into_owned(),
57            key: self.key,
58        }
59    }
60
61    pub fn size(&self) -> usize {
62        self.parameters.size() + self.data.size()
63    }
64
65    pub(crate) fn deserialize_delegate<'de, D>(deser: D) -> Result<Delegate<'static>, D::Error>
66    where
67        D: Deserializer<'de>,
68    {
69        let data: Delegate<'de> = Deserialize::deserialize(deser)?;
70        Ok(data.into_owned())
71    }
72}
73
74impl PartialEq for Delegate<'_> {
75    fn eq(&self, other: &Self) -> bool {
76        self.key == other.key
77    }
78}
79
80impl Eq for Delegate<'_> {}
81
82impl<'a> From<(&DelegateCode<'a>, &Parameters<'a>)> for Delegate<'a> {
83    fn from((data, parameters): (&DelegateCode<'a>, &Parameters<'a>)) -> Self {
84        Self {
85            key: DelegateKey::from_params_and_code(parameters, data),
86            parameters: parameters.clone(),
87            data: data.clone(),
88        }
89    }
90}
91
92/// Executable delegate
93#[derive(Debug, Serialize, Deserialize, Clone)]
94#[serde_as]
95pub struct DelegateCode<'a> {
96    #[serde_as(as = "serde_with::Bytes")]
97    #[serde(borrow)]
98    pub(crate) data: Cow<'a, [u8]>,
99    // todo: skip serializing and instead compute it
100    pub(crate) code_hash: CodeHash,
101}
102
103impl DelegateCode<'static> {
104    /// Loads the contract raw wasm module, without any version.
105    pub fn load_raw(path: &Path) -> Result<Self, std::io::Error> {
106        let contract_data = Self::load_bytes(path)?;
107        Ok(DelegateCode::from(contract_data))
108    }
109
110    pub(crate) fn load_bytes(path: &Path) -> Result<Vec<u8>, std::io::Error> {
111        let mut contract_file = File::open(path)?;
112        let mut contract_data = if let Ok(md) = contract_file.metadata() {
113            Vec::with_capacity(md.len() as usize)
114        } else {
115            Vec::new()
116        };
117        contract_file.read_to_end(&mut contract_data)?;
118        Ok(contract_data)
119    }
120}
121
122impl DelegateCode<'_> {
123    /// Delegate code hash.
124    pub fn hash(&self) -> &CodeHash {
125        &self.code_hash
126    }
127
128    /// Returns the `Base58` string representation of the delegate key.
129    pub fn hash_str(&self) -> String {
130        Self::encode_hash(&self.code_hash.0)
131    }
132
133    /// Reference to delegate code.
134    pub fn data(&self) -> &[u8] {
135        &self.data
136    }
137
138    /// Returns the `Base58` string representation of a hash.
139    pub fn encode_hash(hash: &[u8; DELEGATE_HASH_LENGTH]) -> String {
140        bs58::encode(hash)
141            .with_alphabet(bs58::Alphabet::BITCOIN)
142            .into_string()
143    }
144
145    pub fn into_owned(self) -> DelegateCode<'static> {
146        DelegateCode {
147            code_hash: self.code_hash,
148            data: Cow::from(self.data.into_owned()),
149        }
150    }
151
152    pub fn size(&self) -> usize {
153        self.data.len()
154    }
155}
156
157impl PartialEq for DelegateCode<'_> {
158    fn eq(&self, other: &Self) -> bool {
159        self.code_hash == other.code_hash
160    }
161}
162
163impl Eq for DelegateCode<'_> {}
164
165impl AsRef<[u8]> for DelegateCode<'_> {
166    fn as_ref(&self) -> &[u8] {
167        self.data.borrow()
168    }
169}
170
171impl From<Vec<u8>> for DelegateCode<'static> {
172    fn from(data: Vec<u8>) -> Self {
173        let key = CodeHash::from_code(data.as_slice());
174        DelegateCode {
175            data: Cow::from(data),
176            code_hash: key,
177        }
178    }
179}
180
181impl<'a> From<&'a [u8]> for DelegateCode<'a> {
182    fn from(code: &'a [u8]) -> Self {
183        let key = CodeHash::from_code(code);
184        DelegateCode {
185            data: Cow::from(code),
186            code_hash: key,
187        }
188    }
189}
190
191#[serde_as]
192#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
193pub struct DelegateKey {
194    #[serde_as(as = "[_; DELEGATE_HASH_LENGTH]")]
195    key: [u8; DELEGATE_HASH_LENGTH],
196    code_hash: CodeHash,
197}
198
199impl From<DelegateKey> for SecretsId {
200    fn from(key: DelegateKey) -> SecretsId {
201        SecretsId {
202            hash: key.key,
203            key: vec![],
204        }
205    }
206}
207
208impl DelegateKey {
209    pub const fn new(key: [u8; DELEGATE_HASH_LENGTH], code_hash: CodeHash) -> Self {
210        Self { key, code_hash }
211    }
212
213    fn from_params_and_code<'a>(
214        params: impl Borrow<Parameters<'a>>,
215        wasm_code: impl Borrow<DelegateCode<'a>>,
216    ) -> Self {
217        let code = wasm_code.borrow();
218        let key = generate_id(params.borrow(), code);
219        Self {
220            key,
221            code_hash: *code.hash(),
222        }
223    }
224
225    pub fn encode(&self) -> String {
226        bs58::encode(self.key)
227            .with_alphabet(bs58::Alphabet::BITCOIN)
228            .into_string()
229    }
230
231    pub fn code_hash(&self) -> &CodeHash {
232        &self.code_hash
233    }
234
235    pub fn bytes(&self) -> &[u8] {
236        self.key.as_ref()
237    }
238
239    pub fn from_params(
240        code_hash: impl Into<String>,
241        parameters: &Parameters,
242    ) -> Result<Self, bs58::decode::Error> {
243        let mut code_key = [0; DELEGATE_HASH_LENGTH];
244        bs58::decode(code_hash.into())
245            .with_alphabet(bs58::Alphabet::BITCOIN)
246            .onto(&mut code_key)?;
247        let mut hasher = Blake3::new();
248        hasher.update(code_key.as_slice());
249        hasher.update(parameters.as_ref());
250        let full_key_arr = hasher.finalize();
251
252        debug_assert_eq!(full_key_arr[..].len(), DELEGATE_HASH_LENGTH);
253        let mut key = [0; DELEGATE_HASH_LENGTH];
254        key.copy_from_slice(&full_key_arr);
255
256        Ok(Self {
257            key,
258            code_hash: CodeHash(code_key),
259        })
260    }
261}
262
263impl Deref for DelegateKey {
264    type Target = [u8; DELEGATE_HASH_LENGTH];
265
266    fn deref(&self) -> &Self::Target {
267        &self.key
268    }
269}
270
271impl Display for DelegateKey {
272    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
273        write!(f, "{}", self.encode())
274    }
275}
276
277impl<'a> TryFromFbs<&FbsDelegateKey<'a>> for DelegateKey {
278    fn try_decode_fbs(key: &FbsDelegateKey<'a>) -> Result<Self, WsApiError> {
279        let mut key_bytes = [0; DELEGATE_HASH_LENGTH];
280        key_bytes.copy_from_slice(key.key().bytes().iter().as_ref());
281        Ok(DelegateKey {
282            key: key_bytes,
283            code_hash: CodeHash::from_code(key.code_hash().bytes()),
284        })
285    }
286}
287
288/// Type of errors during interaction with a delegate.
289#[derive(Debug, thiserror::Error, Serialize, Deserialize)]
290pub enum DelegateError {
291    #[error("de/serialization error: {0}")]
292    Deser(String),
293    #[error("{0}")]
294    Other(String),
295}
296
297fn generate_id<'a>(
298    parameters: &Parameters<'a>,
299    code_data: &DelegateCode<'a>,
300) -> [u8; DELEGATE_HASH_LENGTH] {
301    let contract_hash = code_data.hash();
302
303    let mut hasher = Blake3::new();
304    hasher.update(contract_hash.0.as_slice());
305    hasher.update(parameters.as_ref());
306    let full_key_arr = hasher.finalize();
307
308    debug_assert_eq!(full_key_arr[..].len(), DELEGATE_HASH_LENGTH);
309    let mut key = [0; DELEGATE_HASH_LENGTH];
310    key.copy_from_slice(&full_key_arr);
311    key
312}
313
314#[serde_as]
315#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
316pub struct SecretsId {
317    #[serde_as(as = "serde_with::Bytes")]
318    key: Vec<u8>,
319    #[serde_as(as = "[_; 32]")]
320    hash: [u8; 32],
321}
322
323impl SecretsId {
324    pub fn new(key: Vec<u8>) -> Self {
325        let mut hasher = Blake3::new();
326        hasher.update(&key);
327        let hashed = hasher.finalize();
328        let mut hash = [0; 32];
329        hash.copy_from_slice(&hashed);
330        Self { key, hash }
331    }
332
333    pub fn encode(&self) -> String {
334        bs58::encode(self.hash)
335            .with_alphabet(bs58::Alphabet::BITCOIN)
336            .into_string()
337    }
338
339    pub fn hash(&self) -> &[u8; 32] {
340        &self.hash
341    }
342    pub fn key(&self) -> &[u8] {
343        self.key.as_slice()
344    }
345}
346
347impl Display for SecretsId {
348    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
349        write!(f, "{}", self.encode())
350    }
351}
352
353impl<'a> TryFromFbs<&FbsSecretsId<'a>> for SecretsId {
354    fn try_decode_fbs(key: &FbsSecretsId<'a>) -> Result<Self, WsApiError> {
355        let mut key_hash = [0; 32];
356        key_hash.copy_from_slice(key.hash().bytes().iter().as_ref());
357        Ok(SecretsId {
358            key: key.key().bytes().to_vec(),
359            hash: key_hash,
360        })
361    }
362}
363
364/// A Delegate is a webassembly code designed to act as an agent for the user on
365/// Freenet. Delegates can:
366///
367///  * Store private data on behalf of the user
368///  * Create, read, and modify contracts
369///  * Create other delegates
370///  * Send and receive messages from other delegates and user interfaces
371///  * Ask the user questions and receive answers
372///
373/// Example use cases:
374///
375///  * A delegate stores a private key for the user, other components can ask
376///    the delegate to sign messages, it will ask the user for permission
377///  * A delegate monitors an inbox contract and downloads new messages when
378///    they arrive
379///
380/// # Example
381///
382/// ```ignore
383/// use freenet_stdlib::prelude::*;
384///
385/// struct MyDelegate;
386///
387/// #[delegate]
388/// impl DelegateInterface for MyDelegate {
389///     fn process(
390///         ctx: &mut DelegateCtx,
391///         _params: Parameters<'static>,
392///         _attested: Option<&'static [u8]>,
393///         message: InboundDelegateMsg,
394///     ) -> Result<Vec<OutboundDelegateMsg>, DelegateError> {
395///         // Access secrets synchronously - no round-trip needed!
396///         if let Some(key) = ctx.get_secret(b"private_key") {
397///             // use key...
398///         }
399///         ctx.set_secret(b"new_key", b"value");
400///
401///         // Read/write context for temporary state within a batch
402///         ctx.write(b"some state");
403///
404///         Ok(vec![])
405///     }
406/// }
407/// ```
408pub trait DelegateInterface {
409    /// Process inbound message, producing zero or more outbound messages in response.
410    ///
411    /// # Arguments
412    /// - `ctx`: Mutable handle to the delegate's execution environment. Provides:
413    ///   - **Context** (temporary): `read()`, `write()`, `len()`, `clear()` - state within a batch
414    ///   - **Secrets** (persistent): `get_secret()`, `set_secret()`, `has_secret()`, `remove_secret()`
415    /// - `parameters`: The delegate's initialization parameters.
416    /// - `attested`: An optional identifier for the client of this function. Usually
417    ///   will be a [`ContractInstanceId`].
418    /// - `message`: The inbound message to process.
419    fn process(
420        ctx: &mut crate::delegate_host::DelegateCtx,
421        parameters: Parameters<'static>,
422        attested: Option<&'static [u8]>,
423        message: InboundDelegateMsg,
424    ) -> Result<Vec<OutboundDelegateMsg>, DelegateError>;
425}
426
427#[serde_as]
428#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
429pub struct DelegateContext(#[serde_as(as = "serde_with::Bytes")] Vec<u8>);
430
431impl DelegateContext {
432    pub const MAX_SIZE: usize = 4096 * 10 * 10;
433
434    pub fn new(bytes: Vec<u8>) -> Self {
435        assert!(bytes.len() < Self::MAX_SIZE);
436        Self(bytes)
437    }
438
439    pub fn append(&mut self, bytes: &mut Vec<u8>) {
440        assert!(self.0.len() + bytes.len() < Self::MAX_SIZE);
441        self.0.append(bytes)
442    }
443
444    pub fn replace(&mut self, bytes: Vec<u8>) {
445        assert!(bytes.len() < Self::MAX_SIZE);
446        let _ = std::mem::replace(&mut self.0, bytes);
447    }
448}
449
450impl AsRef<[u8]> for DelegateContext {
451    fn as_ref(&self) -> &[u8] {
452        &self.0
453    }
454}
455
456#[derive(Serialize, Deserialize, Debug, Clone)]
457pub enum InboundDelegateMsg<'a> {
458    ApplicationMessage(ApplicationMessage),
459    UserResponse(#[serde(borrow)] UserInputResponse<'a>),
460    GetContractResponse(GetContractResponse),
461}
462
463impl InboundDelegateMsg<'_> {
464    pub fn into_owned(self) -> InboundDelegateMsg<'static> {
465        match self {
466            InboundDelegateMsg::ApplicationMessage(r) => InboundDelegateMsg::ApplicationMessage(r),
467            InboundDelegateMsg::UserResponse(r) => InboundDelegateMsg::UserResponse(r.into_owned()),
468            InboundDelegateMsg::GetContractResponse(r) => {
469                InboundDelegateMsg::GetContractResponse(r)
470            }
471        }
472    }
473
474    pub fn get_context(&self) -> Option<&DelegateContext> {
475        match self {
476            InboundDelegateMsg::ApplicationMessage(ApplicationMessage { context, .. }) => {
477                Some(context)
478            }
479            InboundDelegateMsg::GetContractResponse(GetContractResponse { context, .. }) => {
480                Some(context)
481            }
482            _ => None,
483        }
484    }
485
486    pub fn get_mut_context(&mut self) -> Option<&mut DelegateContext> {
487        match self {
488            InboundDelegateMsg::ApplicationMessage(ApplicationMessage { context, .. }) => {
489                Some(context)
490            }
491            InboundDelegateMsg::GetContractResponse(GetContractResponse { context, .. }) => {
492                Some(context)
493            }
494            _ => None,
495        }
496    }
497}
498
499impl From<ApplicationMessage> for InboundDelegateMsg<'_> {
500    fn from(value: ApplicationMessage) -> Self {
501        Self::ApplicationMessage(value)
502    }
503}
504
505impl<'a> TryFromFbs<&FbsInboundDelegateMsg<'a>> for InboundDelegateMsg<'a> {
506    fn try_decode_fbs(msg: &FbsInboundDelegateMsg<'a>) -> Result<Self, WsApiError> {
507        match msg.inbound_type() {
508            InboundDelegateMsgType::common_ApplicationMessage => {
509                let app_msg = msg.inbound_as_common_application_message().unwrap();
510                let mut instance_key_bytes = [0; CONTRACT_KEY_SIZE];
511                instance_key_bytes
512                    .copy_from_slice(app_msg.app().data().bytes().to_vec().as_slice());
513                let app_msg = ApplicationMessage {
514                    app: ContractInstanceId::new(instance_key_bytes),
515                    payload: app_msg.payload().bytes().to_vec(),
516                    context: DelegateContext::new(app_msg.context().bytes().to_vec()),
517                    processed: app_msg.processed(),
518                };
519                Ok(InboundDelegateMsg::ApplicationMessage(app_msg))
520            }
521            InboundDelegateMsgType::UserInputResponse => {
522                let user_response = msg.inbound_as_user_input_response().unwrap();
523                let user_response = UserInputResponse {
524                    request_id: user_response.request_id(),
525                    response: ClientResponse::new(user_response.response().data().bytes().to_vec()),
526                    context: DelegateContext::new(
527                        user_response.delegate_context().bytes().to_vec(),
528                    ),
529                };
530                Ok(InboundDelegateMsg::UserResponse(user_response))
531            }
532            _ => unreachable!("invalid inbound delegate message type"),
533        }
534    }
535}
536
537#[non_exhaustive]
538#[derive(Serialize, Deserialize, Debug, Clone)]
539pub struct ApplicationMessage {
540    pub app: ContractInstanceId,
541    pub payload: Vec<u8>,
542    pub context: DelegateContext,
543    pub processed: bool,
544}
545
546impl ApplicationMessage {
547    pub fn new(app: ContractInstanceId, payload: Vec<u8>) -> Self {
548        Self {
549            app,
550            payload,
551            context: DelegateContext::default(),
552            processed: false,
553        }
554    }
555
556    pub fn with_context(mut self, context: DelegateContext) -> Self {
557        self.context = context;
558        self
559    }
560
561    pub fn processed(mut self, p: bool) -> Self {
562        self.processed = p;
563        self
564    }
565}
566
567#[derive(Serialize, Deserialize, Debug, Clone)]
568pub struct UserInputResponse<'a> {
569    pub request_id: u32,
570    #[serde(borrow)]
571    pub response: ClientResponse<'a>,
572    pub context: DelegateContext,
573}
574
575impl UserInputResponse<'_> {
576    pub fn into_owned(self) -> UserInputResponse<'static> {
577        UserInputResponse {
578            request_id: self.request_id,
579            response: self.response.into_owned(),
580            context: self.context,
581        }
582    }
583}
584
585#[derive(Serialize, Deserialize, Debug, Clone)]
586pub enum OutboundDelegateMsg {
587    // for the apps
588    ApplicationMessage(ApplicationMessage),
589    RequestUserInput(
590        #[serde(deserialize_with = "OutboundDelegateMsg::deser_user_input_req")]
591        UserInputRequest<'static>,
592    ),
593    // todo: remove when context can be accessed from the delegate environment and we pass it as reference
594    ContextUpdated(DelegateContext),
595    GetContractRequest(GetContractRequest),
596}
597
598impl From<ApplicationMessage> for OutboundDelegateMsg {
599    fn from(req: ApplicationMessage) -> Self {
600        Self::ApplicationMessage(req)
601    }
602}
603
604impl From<GetContractRequest> for OutboundDelegateMsg {
605    fn from(req: GetContractRequest) -> Self {
606        Self::GetContractRequest(req)
607    }
608}
609
610impl OutboundDelegateMsg {
611    fn deser_user_input_req<'de, D>(deser: D) -> Result<UserInputRequest<'static>, D::Error>
612    where
613        D: serde::Deserializer<'de>,
614    {
615        let value = <UserInputRequest<'de> as Deserialize>::deserialize(deser)?;
616        Ok(value.into_owned())
617    }
618
619    pub fn processed(&self) -> bool {
620        match self {
621            OutboundDelegateMsg::ApplicationMessage(msg) => msg.processed,
622            OutboundDelegateMsg::GetContractRequest(msg) => msg.processed,
623            OutboundDelegateMsg::RequestUserInput(_) => true,
624            OutboundDelegateMsg::ContextUpdated(_) => true,
625        }
626    }
627
628    pub fn get_context(&self) -> Option<&DelegateContext> {
629        match self {
630            OutboundDelegateMsg::ApplicationMessage(ApplicationMessage { context, .. }) => {
631                Some(context)
632            }
633            OutboundDelegateMsg::GetContractRequest(GetContractRequest { context, .. }) => {
634                Some(context)
635            }
636            _ => None,
637        }
638    }
639
640    pub fn get_mut_context(&mut self) -> Option<&mut DelegateContext> {
641        match self {
642            OutboundDelegateMsg::ApplicationMessage(ApplicationMessage { context, .. }) => {
643                Some(context)
644            }
645            OutboundDelegateMsg::GetContractRequest(GetContractRequest { context, .. }) => {
646                Some(context)
647            }
648            _ => None,
649        }
650    }
651}
652
653/// Request to get contract state from within a delegate.
654#[derive(Serialize, Deserialize, Debug, Clone)]
655pub struct GetContractRequest {
656    pub contract_id: ContractInstanceId,
657    pub context: DelegateContext,
658    pub processed: bool,
659}
660
661impl GetContractRequest {
662    pub fn new(contract_id: ContractInstanceId) -> Self {
663        Self {
664            contract_id,
665            context: Default::default(),
666            processed: false,
667        }
668    }
669}
670
671/// Response containing contract state for a delegate.
672#[derive(Serialize, Deserialize, Debug, Clone)]
673pub struct GetContractResponse {
674    pub contract_id: ContractInstanceId,
675    /// The contract state, or None if the contract was not found locally.
676    pub state: Option<WrappedState>,
677    pub context: DelegateContext,
678}
679
680#[serde_as]
681#[derive(Serialize, Deserialize, Debug, Clone)]
682pub struct NotificationMessage<'a>(
683    #[serde_as(as = "serde_with::Bytes")]
684    #[serde(borrow)]
685    Cow<'a, [u8]>,
686);
687
688impl TryFrom<&serde_json::Value> for NotificationMessage<'static> {
689    type Error = ();
690
691    fn try_from(json: &serde_json::Value) -> Result<NotificationMessage<'static>, ()> {
692        // todo: validate format when we have a better idea of what we want here
693        let bytes = serde_json::to_vec(json).unwrap();
694        Ok(Self(Cow::Owned(bytes)))
695    }
696}
697
698impl NotificationMessage<'_> {
699    pub fn into_owned(self) -> NotificationMessage<'static> {
700        NotificationMessage(self.0.into_owned().into())
701    }
702    pub fn bytes(&self) -> &[u8] {
703        self.0.as_ref()
704    }
705}
706
707#[serde_as]
708#[derive(Serialize, Deserialize, Debug, Clone)]
709pub struct ClientResponse<'a>(
710    #[serde_as(as = "serde_with::Bytes")]
711    #[serde(borrow)]
712    Cow<'a, [u8]>,
713);
714
715impl Deref for ClientResponse<'_> {
716    type Target = [u8];
717
718    fn deref(&self) -> &Self::Target {
719        &self.0
720    }
721}
722
723impl ClientResponse<'_> {
724    pub fn new(response: Vec<u8>) -> Self {
725        Self(response.into())
726    }
727    pub fn into_owned(self) -> ClientResponse<'static> {
728        ClientResponse(self.0.into_owned().into())
729    }
730    pub fn bytes(&self) -> &[u8] {
731        self.0.as_ref()
732    }
733}
734
735#[derive(Serialize, Deserialize, Debug, Clone)]
736pub struct UserInputRequest<'a> {
737    pub request_id: u32,
738    #[serde(borrow)]
739    /// An interpretable message by the notification system.
740    pub message: NotificationMessage<'a>,
741    /// If a response is required from the user they can be chosen from this list.
742    pub responses: Vec<ClientResponse<'a>>,
743}
744
745impl UserInputRequest<'_> {
746    pub fn into_owned(self) -> UserInputRequest<'static> {
747        UserInputRequest {
748            request_id: self.request_id,
749            message: self.message.into_owned(),
750            responses: self.responses.into_iter().map(|r| r.into_owned()).collect(),
751        }
752    }
753}
754
755#[doc(hidden)]
756pub(crate) mod wasm_interface {
757    //! Contains all the types to interface between the host environment and
758    //! the wasm module execution.
759    use super::*;
760    use crate::memory::WasmLinearMem;
761
762    #[repr(C)]
763    #[derive(Debug, Clone, Copy)]
764    pub struct DelegateInterfaceResult {
765        ptr: i64,
766        size: u32,
767    }
768
769    impl DelegateInterfaceResult {
770        pub unsafe fn from_raw(ptr: i64, mem: &WasmLinearMem) -> Self {
771            let result = Box::leak(Box::from_raw(crate::memory::buf::compute_ptr(
772                ptr as *mut Self,
773                mem,
774            )));
775            #[cfg(feature = "trace")]
776            {
777                tracing::trace!(
778                    "got FFI result @ {ptr} ({:p}) -> {result:?}",
779                    ptr as *mut Self
780                );
781            }
782            *result
783        }
784
785        #[cfg(feature = "contract")]
786        pub fn into_raw(self) -> i64 {
787            #[cfg(feature = "trace")]
788            {
789                tracing::trace!("returning FFI -> {self:?}");
790            }
791            let ptr = Box::into_raw(Box::new(self));
792            #[cfg(feature = "trace")]
793            {
794                tracing::trace!("FFI result ptr: {ptr:p} ({}i64)", ptr as i64);
795            }
796            ptr as _
797        }
798
799        pub unsafe fn unwrap(
800            self,
801            mem: WasmLinearMem,
802        ) -> Result<Vec<OutboundDelegateMsg>, DelegateError> {
803            let ptr = crate::memory::buf::compute_ptr(self.ptr as *mut u8, &mem);
804            let serialized = std::slice::from_raw_parts(ptr as *const u8, self.size as _);
805            let value: Result<Vec<OutboundDelegateMsg>, DelegateError> =
806                bincode::deserialize(serialized)
807                    .map_err(|e| DelegateError::Other(format!("{e}")))?;
808            #[cfg(feature = "trace")]
809            {
810                tracing::trace!(
811                    "got result through FFI; addr: {:p} ({}i64, mapped: {ptr:p})
812                     serialized: {serialized:?}
813                     value: {value:?}",
814                    self.ptr as *mut u8,
815                    self.ptr
816                );
817            }
818            value
819        }
820    }
821
822    impl From<Result<Vec<OutboundDelegateMsg>, DelegateError>> for DelegateInterfaceResult {
823        fn from(value: Result<Vec<OutboundDelegateMsg>, DelegateError>) -> Self {
824            let serialized = bincode::serialize(&value).unwrap();
825            let size = serialized.len() as _;
826            let ptr = serialized.as_ptr();
827            #[cfg(feature = "trace")]
828            {
829                tracing::trace!(
830                    "sending result through FFI; addr: {ptr:p} ({}),\n  serialized: {serialized:?}\n  value: {value:?}",
831                    ptr as i64
832                );
833            }
834            std::mem::forget(serialized);
835            Self {
836                ptr: ptr as i64,
837                size,
838            }
839        }
840    }
841}