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::contract_interface::{RelatedContracts, UpdateData};
23use crate::prelude::{ContractInstanceId, WrappedState};
24use crate::versioning::ContractContainer;
25use crate::{code_hash::CodeHash, prelude::Parameters};
26
27const DELEGATE_HASH_LENGTH: usize = 32;
28
29#[derive(Clone, Debug, Serialize, Deserialize)]
30pub struct Delegate<'a> {
31    #[serde(borrow)]
32    parameters: Parameters<'a>,
33    #[serde(borrow)]
34    pub data: DelegateCode<'a>,
35    key: DelegateKey,
36}
37
38impl Delegate<'_> {
39    pub fn key(&self) -> &DelegateKey {
40        &self.key
41    }
42
43    pub fn code(&self) -> &DelegateCode<'_> {
44        &self.data
45    }
46
47    pub fn code_hash(&self) -> &CodeHash {
48        &self.data.code_hash
49    }
50
51    pub fn params(&self) -> &Parameters<'_> {
52        &self.parameters
53    }
54
55    pub fn into_owned(self) -> Delegate<'static> {
56        Delegate {
57            parameters: self.parameters.into_owned(),
58            data: self.data.into_owned(),
59            key: self.key,
60        }
61    }
62
63    pub fn size(&self) -> usize {
64        self.parameters.size() + self.data.size()
65    }
66
67    pub(crate) fn deserialize_delegate<'de, D>(deser: D) -> Result<Delegate<'static>, D::Error>
68    where
69        D: Deserializer<'de>,
70    {
71        let data: Delegate<'de> = Deserialize::deserialize(deser)?;
72        Ok(data.into_owned())
73    }
74}
75
76impl PartialEq for Delegate<'_> {
77    fn eq(&self, other: &Self) -> bool {
78        self.key == other.key
79    }
80}
81
82impl Eq for Delegate<'_> {}
83
84impl<'a> From<(&DelegateCode<'a>, &Parameters<'a>)> for Delegate<'a> {
85    fn from((data, parameters): (&DelegateCode<'a>, &Parameters<'a>)) -> Self {
86        Self {
87            key: DelegateKey::from_params_and_code(parameters, data),
88            parameters: parameters.clone(),
89            data: data.clone(),
90        }
91    }
92}
93
94/// Executable delegate
95#[derive(Debug, Serialize, Deserialize, Clone)]
96#[serde_as]
97pub struct DelegateCode<'a> {
98    #[serde_as(as = "serde_with::Bytes")]
99    #[serde(borrow)]
100    pub(crate) data: Cow<'a, [u8]>,
101    // todo: skip serializing and instead compute it
102    pub(crate) code_hash: CodeHash,
103}
104
105impl DelegateCode<'static> {
106    /// Loads the contract raw wasm module, without any version.
107    pub fn load_raw(path: &Path) -> Result<Self, std::io::Error> {
108        let contract_data = Self::load_bytes(path)?;
109        Ok(DelegateCode::from(contract_data))
110    }
111
112    pub(crate) fn load_bytes(path: &Path) -> Result<Vec<u8>, std::io::Error> {
113        let mut contract_file = File::open(path)?;
114        let mut contract_data = if let Ok(md) = contract_file.metadata() {
115            Vec::with_capacity(md.len() as usize)
116        } else {
117            Vec::new()
118        };
119        contract_file.read_to_end(&mut contract_data)?;
120        Ok(contract_data)
121    }
122}
123
124impl DelegateCode<'_> {
125    /// Delegate code hash.
126    pub fn hash(&self) -> &CodeHash {
127        &self.code_hash
128    }
129
130    /// Returns the `Base58` string representation of the delegate key.
131    pub fn hash_str(&self) -> String {
132        Self::encode_hash(&self.code_hash.0)
133    }
134
135    /// Reference to delegate code.
136    pub fn data(&self) -> &[u8] {
137        &self.data
138    }
139
140    /// Returns the `Base58` string representation of a hash.
141    pub fn encode_hash(hash: &[u8; DELEGATE_HASH_LENGTH]) -> String {
142        bs58::encode(hash)
143            .with_alphabet(bs58::Alphabet::BITCOIN)
144            .into_string()
145    }
146
147    pub fn into_owned(self) -> DelegateCode<'static> {
148        DelegateCode {
149            code_hash: self.code_hash,
150            data: Cow::from(self.data.into_owned()),
151        }
152    }
153
154    pub fn size(&self) -> usize {
155        self.data.len()
156    }
157}
158
159impl PartialEq for DelegateCode<'_> {
160    fn eq(&self, other: &Self) -> bool {
161        self.code_hash == other.code_hash
162    }
163}
164
165impl Eq for DelegateCode<'_> {}
166
167impl AsRef<[u8]> for DelegateCode<'_> {
168    fn as_ref(&self) -> &[u8] {
169        self.data.borrow()
170    }
171}
172
173impl From<Vec<u8>> for DelegateCode<'static> {
174    fn from(data: Vec<u8>) -> Self {
175        let key = CodeHash::from_code(data.as_slice());
176        DelegateCode {
177            data: Cow::from(data),
178            code_hash: key,
179        }
180    }
181}
182
183impl<'a> From<&'a [u8]> for DelegateCode<'a> {
184    fn from(code: &'a [u8]) -> Self {
185        let key = CodeHash::from_code(code);
186        DelegateCode {
187            data: Cow::from(code),
188            code_hash: key,
189        }
190    }
191}
192
193#[serde_as]
194#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
195pub struct DelegateKey {
196    #[serde_as(as = "[_; DELEGATE_HASH_LENGTH]")]
197    key: [u8; DELEGATE_HASH_LENGTH],
198    code_hash: CodeHash,
199}
200
201impl From<DelegateKey> for SecretsId {
202    fn from(key: DelegateKey) -> SecretsId {
203        SecretsId {
204            hash: key.key,
205            key: vec![],
206        }
207    }
208}
209
210impl DelegateKey {
211    pub const fn new(key: [u8; DELEGATE_HASH_LENGTH], code_hash: CodeHash) -> Self {
212        Self { key, code_hash }
213    }
214
215    fn from_params_and_code<'a>(
216        params: impl Borrow<Parameters<'a>>,
217        wasm_code: impl Borrow<DelegateCode<'a>>,
218    ) -> Self {
219        let code = wasm_code.borrow();
220        let key = generate_id(params.borrow(), code);
221        Self {
222            key,
223            code_hash: *code.hash(),
224        }
225    }
226
227    pub fn encode(&self) -> String {
228        bs58::encode(self.key)
229            .with_alphabet(bs58::Alphabet::BITCOIN)
230            .into_string()
231    }
232
233    pub fn code_hash(&self) -> &CodeHash {
234        &self.code_hash
235    }
236
237    pub fn bytes(&self) -> &[u8] {
238        self.key.as_ref()
239    }
240
241    pub fn from_params(
242        code_hash: impl Into<String>,
243        parameters: &Parameters,
244    ) -> Result<Self, bs58::decode::Error> {
245        let mut code_key = [0; DELEGATE_HASH_LENGTH];
246        bs58::decode(code_hash.into())
247            .with_alphabet(bs58::Alphabet::BITCOIN)
248            .onto(&mut code_key)?;
249        let mut hasher = Blake3::new();
250        hasher.update(code_key.as_slice());
251        hasher.update(parameters.as_ref());
252        let full_key_arr = hasher.finalize();
253
254        debug_assert_eq!(full_key_arr[..].len(), DELEGATE_HASH_LENGTH);
255        let mut key = [0; DELEGATE_HASH_LENGTH];
256        key.copy_from_slice(&full_key_arr);
257
258        Ok(Self {
259            key,
260            code_hash: CodeHash(code_key),
261        })
262    }
263}
264
265impl Deref for DelegateKey {
266    type Target = [u8; DELEGATE_HASH_LENGTH];
267
268    fn deref(&self) -> &Self::Target {
269        &self.key
270    }
271}
272
273impl Display for DelegateKey {
274    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
275        write!(f, "{}", self.encode())
276    }
277}
278
279impl<'a> TryFromFbs<&FbsDelegateKey<'a>> for DelegateKey {
280    fn try_decode_fbs(key: &FbsDelegateKey<'a>) -> Result<Self, WsApiError> {
281        let mut key_bytes = [0; DELEGATE_HASH_LENGTH];
282        key_bytes.copy_from_slice(key.key().bytes());
283        let code_hash = CodeHash::try_from(key.code_hash().bytes())
284            .map_err(|e| WsApiError::deserialization(e.to_string()))?;
285        Ok(DelegateKey {
286            key: key_bytes,
287            code_hash,
288        })
289    }
290}
291
292/// Type of errors during interaction with a delegate.
293///
294/// Marked `#[non_exhaustive]` so future error variants can be added without a
295/// source-level break. Downstream `match` sites must include a wildcard arm.
296#[non_exhaustive]
297#[derive(Debug, thiserror::Error, Serialize, Deserialize)]
298pub enum DelegateError {
299    #[error("de/serialization error: {0}")]
300    Deser(String),
301    #[error("{0}")]
302    Other(String),
303}
304
305fn generate_id<'a>(
306    parameters: &Parameters<'a>,
307    code_data: &DelegateCode<'a>,
308) -> [u8; DELEGATE_HASH_LENGTH] {
309    let contract_hash = code_data.hash();
310
311    let mut hasher = Blake3::new();
312    hasher.update(contract_hash.0.as_slice());
313    hasher.update(parameters.as_ref());
314    let full_key_arr = hasher.finalize();
315
316    debug_assert_eq!(full_key_arr[..].len(), DELEGATE_HASH_LENGTH);
317    let mut key = [0; DELEGATE_HASH_LENGTH];
318    key.copy_from_slice(&full_key_arr);
319    key
320}
321
322#[serde_as]
323#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
324pub struct SecretsId {
325    #[serde_as(as = "serde_with::Bytes")]
326    key: Vec<u8>,
327    #[serde_as(as = "[_; 32]")]
328    hash: [u8; 32],
329}
330
331impl SecretsId {
332    pub fn new(key: Vec<u8>) -> Self {
333        let mut hasher = Blake3::new();
334        hasher.update(&key);
335        let hashed = hasher.finalize();
336        let mut hash = [0; 32];
337        hash.copy_from_slice(&hashed);
338        Self { key, hash }
339    }
340
341    pub fn encode(&self) -> String {
342        bs58::encode(self.hash)
343            .with_alphabet(bs58::Alphabet::BITCOIN)
344            .into_string()
345    }
346
347    pub fn hash(&self) -> &[u8; 32] {
348        &self.hash
349    }
350    pub fn key(&self) -> &[u8] {
351        self.key.as_slice()
352    }
353}
354
355impl Display for SecretsId {
356    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
357        write!(f, "{}", self.encode())
358    }
359}
360
361impl<'a> TryFromFbs<&FbsSecretsId<'a>> for SecretsId {
362    fn try_decode_fbs(key: &FbsSecretsId<'a>) -> Result<Self, WsApiError> {
363        let mut key_hash = [0; 32];
364        key_hash.copy_from_slice(key.hash().bytes().iter().as_ref());
365        Ok(SecretsId {
366            key: key.key().bytes().to_vec(),
367            hash: key_hash,
368        })
369    }
370}
371
372/// Identifies where an inbound application message originated from.
373///
374/// When a web app sends a message to a delegate through the WebSocket API with
375/// an authentication token, the runtime resolves the token to the originating
376/// contract and wraps it in `MessageOrigin::WebApp`. When one delegate sends a
377/// message to another via [`OutboundDelegateMsg::SendDelegateMessage`], the
378/// runtime attests the caller's identity in `MessageOrigin::Delegate`.
379/// Delegates receive this as the `origin` parameter of
380/// [`DelegateInterface::process`].
381///
382/// This enum is `#[non_exhaustive]`: downstream code matching on it must
383/// include a wildcard arm so future variants can be added without a
384/// source-level breaking change.
385#[non_exhaustive]
386#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
387pub enum MessageOrigin {
388    /// The message was sent by a web application backed by the given contract.
389    WebApp(ContractInstanceId),
390    /// The message was sent by another delegate via
391    /// [`OutboundDelegateMsg::SendDelegateMessage`]. The carried key is the
392    /// runtime-attested identity of the calling delegate; the receiver can
393    /// trust it to make authorization decisions.
394    ///
395    /// Note: an inter-delegate message **replaces** rather than composes with
396    /// any inherited `WebApp` origin the calling delegate may itself hold.
397    /// The receiver sees only `Delegate(caller_key)` for the duration of the
398    /// call, and does not gain contract access on behalf of any web app the
399    /// caller was acting for. Authorization should be made on the calling
400    /// delegate's identity alone.
401    Delegate(DelegateKey),
402}
403
404/// A Delegate is a webassembly code designed to act as an agent for the user on
405/// Freenet. Delegates can:
406///
407///  * Store private data on behalf of the user
408///  * Create, read, and modify contracts
409///  * Create other delegates
410///  * Send and receive messages from other delegates and user interfaces
411///  * Ask the user questions and receive answers
412///
413/// Example use cases:
414///
415///  * A delegate stores a private key for the user, other components can ask
416///    the delegate to sign messages, it will ask the user for permission
417///  * A delegate monitors an inbox contract and downloads new messages when
418///    they arrive
419///
420/// # Example
421///
422/// ```ignore
423/// use freenet_stdlib::prelude::*;
424///
425/// struct MyDelegate;
426///
427/// #[delegate]
428/// impl DelegateInterface for MyDelegate {
429///     fn process(
430///         ctx: &mut DelegateCtx,
431///         _params: Parameters<'static>,
432///         _origin: Option<MessageOrigin>,
433///         message: InboundDelegateMsg,
434///     ) -> Result<Vec<OutboundDelegateMsg>, DelegateError> {
435///         // Access secrets synchronously - no round-trip needed!
436///         if let Some(key) = ctx.get_secret(b"private_key") {
437///             // use key...
438///         }
439///         ctx.set_secret(b"new_key", b"value");
440///
441///         // Read/write context for temporary state within a batch
442///         ctx.write(b"some state");
443///
444///         Ok(vec![])
445///     }
446/// }
447/// ```
448pub trait DelegateInterface {
449    /// Process inbound message, producing zero or more outbound messages in response.
450    ///
451    /// # Arguments
452    /// - `ctx`: Mutable handle to the delegate's execution environment. Provides:
453    ///   - **Context** (temporary): `read()`, `write()`, `len()`, `clear()` - state within a batch
454    ///   - **Secrets** (persistent): `get_secret()`, `set_secret()`, `has_secret()`, `remove_secret()`
455    /// - `parameters`: The delegate's initialization parameters.
456    /// - `origin`: An optional [`MessageOrigin`] identifying where the message came from.
457    ///   For messages sent by web applications, this is `MessageOrigin::WebApp(contract_id)`.
458    /// - `message`: The inbound message to process.
459    fn process(
460        ctx: &mut crate::delegate_host::DelegateCtx,
461        parameters: Parameters<'static>,
462        origin: Option<MessageOrigin>,
463        message: InboundDelegateMsg,
464    ) -> Result<Vec<OutboundDelegateMsg>, DelegateError>;
465}
466
467#[serde_as]
468#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
469pub struct DelegateContext(#[serde_as(as = "serde_with::Bytes")] Vec<u8>);
470
471impl DelegateContext {
472    pub const MAX_SIZE: usize = 4096 * 10 * 10;
473
474    pub fn new(bytes: Vec<u8>) -> Self {
475        assert!(bytes.len() < Self::MAX_SIZE);
476        Self(bytes)
477    }
478
479    pub fn append(&mut self, bytes: &mut Vec<u8>) {
480        assert!(self.0.len() + bytes.len() < Self::MAX_SIZE);
481        self.0.append(bytes)
482    }
483
484    pub fn replace(&mut self, bytes: Vec<u8>) {
485        assert!(bytes.len() < Self::MAX_SIZE);
486        let _ = std::mem::replace(&mut self.0, bytes);
487    }
488}
489
490impl AsRef<[u8]> for DelegateContext {
491    fn as_ref(&self) -> &[u8] {
492        &self.0
493    }
494}
495
496/// Messages delivered **into** a delegate's `process()` function.
497///
498/// This is the inbound counterpart of [`OutboundDelegateMsg`] and sits on the
499/// host↔delegate wire boundary. Marked `#[non_exhaustive]` so future variants
500/// can be added without a source-level break; downstream `match` sites must
501/// include a wildcard arm. This matches the pre-existing `#[non_exhaustive]`
502/// on `OutboundDelegateMsg`.
503///
504/// Wire format: bincode with variant index 0..=N in declaration order. The
505/// `inbound_delegate_msg_wire_format_is_stable` test pins the bytes for
506/// `ApplicationMessage(..)` so that refactors cannot silently shift the tag.
507#[non_exhaustive]
508#[derive(Serialize, Deserialize, Debug, Clone)]
509pub enum InboundDelegateMsg<'a> {
510    ApplicationMessage(ApplicationMessage),
511    UserResponse(#[serde(borrow)] UserInputResponse<'a>),
512    GetContractResponse(GetContractResponse),
513    PutContractResponse(PutContractResponse),
514    UpdateContractResponse(UpdateContractResponse),
515    SubscribeContractResponse(SubscribeContractResponse),
516    ContractNotification(ContractNotification),
517    DelegateMessage(DelegateMessage),
518}
519
520impl InboundDelegateMsg<'_> {
521    pub fn into_owned(self) -> InboundDelegateMsg<'static> {
522        match self {
523            InboundDelegateMsg::ApplicationMessage(r) => InboundDelegateMsg::ApplicationMessage(r),
524            InboundDelegateMsg::UserResponse(r) => InboundDelegateMsg::UserResponse(r.into_owned()),
525            InboundDelegateMsg::GetContractResponse(r) => {
526                InboundDelegateMsg::GetContractResponse(r)
527            }
528            InboundDelegateMsg::PutContractResponse(r) => {
529                InboundDelegateMsg::PutContractResponse(r)
530            }
531            InboundDelegateMsg::UpdateContractResponse(r) => {
532                InboundDelegateMsg::UpdateContractResponse(r)
533            }
534            InboundDelegateMsg::SubscribeContractResponse(r) => {
535                InboundDelegateMsg::SubscribeContractResponse(r)
536            }
537            InboundDelegateMsg::ContractNotification(r) => {
538                InboundDelegateMsg::ContractNotification(r)
539            }
540            InboundDelegateMsg::DelegateMessage(r) => InboundDelegateMsg::DelegateMessage(r),
541        }
542    }
543
544    pub fn get_context(&self) -> Option<&DelegateContext> {
545        match self {
546            InboundDelegateMsg::ApplicationMessage(ApplicationMessage { context, .. }) => {
547                Some(context)
548            }
549            InboundDelegateMsg::GetContractResponse(GetContractResponse { context, .. }) => {
550                Some(context)
551            }
552            InboundDelegateMsg::PutContractResponse(PutContractResponse { context, .. }) => {
553                Some(context)
554            }
555            InboundDelegateMsg::UpdateContractResponse(UpdateContractResponse {
556                context, ..
557            }) => Some(context),
558            InboundDelegateMsg::SubscribeContractResponse(SubscribeContractResponse {
559                context,
560                ..
561            }) => Some(context),
562            InboundDelegateMsg::ContractNotification(ContractNotification { context, .. }) => {
563                Some(context)
564            }
565            InboundDelegateMsg::DelegateMessage(DelegateMessage { context, .. }) => Some(context),
566            _ => None,
567        }
568    }
569
570    pub fn get_mut_context(&mut self) -> Option<&mut DelegateContext> {
571        match self {
572            InboundDelegateMsg::ApplicationMessage(ApplicationMessage { context, .. }) => {
573                Some(context)
574            }
575            InboundDelegateMsg::GetContractResponse(GetContractResponse { context, .. }) => {
576                Some(context)
577            }
578            InboundDelegateMsg::PutContractResponse(PutContractResponse { context, .. }) => {
579                Some(context)
580            }
581            InboundDelegateMsg::UpdateContractResponse(UpdateContractResponse {
582                context, ..
583            }) => Some(context),
584            InboundDelegateMsg::SubscribeContractResponse(SubscribeContractResponse {
585                context,
586                ..
587            }) => Some(context),
588            InboundDelegateMsg::ContractNotification(ContractNotification { context, .. }) => {
589                Some(context)
590            }
591            InboundDelegateMsg::DelegateMessage(DelegateMessage { context, .. }) => Some(context),
592            _ => None,
593        }
594    }
595}
596
597impl From<ApplicationMessage> for InboundDelegateMsg<'_> {
598    fn from(value: ApplicationMessage) -> Self {
599        Self::ApplicationMessage(value)
600    }
601}
602
603impl<'a> TryFromFbs<&FbsInboundDelegateMsg<'a>> for InboundDelegateMsg<'a> {
604    fn try_decode_fbs(msg: &FbsInboundDelegateMsg<'a>) -> Result<Self, WsApiError> {
605        match msg.inbound_type() {
606            InboundDelegateMsgType::common_ApplicationMessage => {
607                let app_msg = msg.inbound_as_common_application_message().unwrap();
608                let app_msg = ApplicationMessage {
609                    payload: app_msg.payload().bytes().to_vec(),
610                    context: DelegateContext::new(app_msg.context().bytes().to_vec()),
611                    processed: app_msg.processed(),
612                };
613                Ok(InboundDelegateMsg::ApplicationMessage(app_msg))
614            }
615            InboundDelegateMsgType::UserInputResponse => {
616                let user_response = msg.inbound_as_user_input_response().unwrap();
617                let user_response = UserInputResponse {
618                    request_id: user_response.request_id(),
619                    response: ClientResponse::new(user_response.response().data().bytes().to_vec()),
620                    context: DelegateContext::new(
621                        user_response.delegate_context().bytes().to_vec(),
622                    ),
623                };
624                Ok(InboundDelegateMsg::UserResponse(user_response))
625            }
626            _ => unreachable!("invalid inbound delegate message type"),
627        }
628    }
629}
630
631#[non_exhaustive]
632#[derive(Serialize, Deserialize, Debug, Clone)]
633pub struct ApplicationMessage {
634    pub payload: Vec<u8>,
635    pub context: DelegateContext,
636    pub processed: bool,
637}
638
639impl ApplicationMessage {
640    pub fn new(payload: Vec<u8>) -> Self {
641        Self {
642            payload,
643            context: DelegateContext::default(),
644            processed: false,
645        }
646    }
647
648    pub fn with_context(mut self, context: DelegateContext) -> Self {
649        self.context = context;
650        self
651    }
652
653    pub fn processed(mut self, p: bool) -> Self {
654        self.processed = p;
655        self
656    }
657}
658
659#[derive(Serialize, Deserialize, Debug, Clone)]
660pub struct UserInputResponse<'a> {
661    pub request_id: u32,
662    #[serde(borrow)]
663    pub response: ClientResponse<'a>,
664    pub context: DelegateContext,
665}
666
667impl UserInputResponse<'_> {
668    pub fn into_owned(self) -> UserInputResponse<'static> {
669        UserInputResponse {
670            request_id: self.request_id,
671            response: self.response.into_owned(),
672            context: self.context,
673        }
674    }
675}
676
677#[derive(Serialize, Deserialize, Debug, Clone)]
678pub enum OutboundDelegateMsg {
679    // for the apps
680    ApplicationMessage(ApplicationMessage),
681    RequestUserInput(
682        #[serde(deserialize_with = "OutboundDelegateMsg::deser_user_input_req")]
683        UserInputRequest<'static>,
684    ),
685    // todo: remove when context can be accessed from the delegate environment and we pass it as reference
686    ContextUpdated(DelegateContext),
687    GetContractRequest(GetContractRequest),
688    PutContractRequest(PutContractRequest),
689    UpdateContractRequest(UpdateContractRequest),
690    SubscribeContractRequest(SubscribeContractRequest),
691    SendDelegateMessage(DelegateMessage),
692}
693
694impl From<ApplicationMessage> for OutboundDelegateMsg {
695    fn from(req: ApplicationMessage) -> Self {
696        Self::ApplicationMessage(req)
697    }
698}
699
700impl From<GetContractRequest> for OutboundDelegateMsg {
701    fn from(req: GetContractRequest) -> Self {
702        Self::GetContractRequest(req)
703    }
704}
705
706impl From<PutContractRequest> for OutboundDelegateMsg {
707    fn from(req: PutContractRequest) -> Self {
708        Self::PutContractRequest(req)
709    }
710}
711
712impl From<UpdateContractRequest> for OutboundDelegateMsg {
713    fn from(req: UpdateContractRequest) -> Self {
714        Self::UpdateContractRequest(req)
715    }
716}
717
718impl From<SubscribeContractRequest> for OutboundDelegateMsg {
719    fn from(req: SubscribeContractRequest) -> Self {
720        Self::SubscribeContractRequest(req)
721    }
722}
723
724impl From<DelegateMessage> for OutboundDelegateMsg {
725    fn from(msg: DelegateMessage) -> Self {
726        Self::SendDelegateMessage(msg)
727    }
728}
729
730impl OutboundDelegateMsg {
731    fn deser_user_input_req<'de, D>(deser: D) -> Result<UserInputRequest<'static>, D::Error>
732    where
733        D: serde::Deserializer<'de>,
734    {
735        let value = <UserInputRequest<'de> as Deserialize>::deserialize(deser)?;
736        Ok(value.into_owned())
737    }
738
739    pub fn processed(&self) -> bool {
740        match self {
741            OutboundDelegateMsg::ApplicationMessage(msg) => msg.processed,
742            OutboundDelegateMsg::GetContractRequest(msg) => msg.processed,
743            OutboundDelegateMsg::PutContractRequest(msg) => msg.processed,
744            OutboundDelegateMsg::UpdateContractRequest(msg) => msg.processed,
745            OutboundDelegateMsg::SubscribeContractRequest(msg) => msg.processed,
746            OutboundDelegateMsg::SendDelegateMessage(msg) => msg.processed,
747            OutboundDelegateMsg::RequestUserInput(_) => true,
748            OutboundDelegateMsg::ContextUpdated(_) => true,
749        }
750    }
751
752    pub fn get_context(&self) -> Option<&DelegateContext> {
753        match self {
754            OutboundDelegateMsg::ApplicationMessage(ApplicationMessage { context, .. }) => {
755                Some(context)
756            }
757            OutboundDelegateMsg::GetContractRequest(GetContractRequest { context, .. }) => {
758                Some(context)
759            }
760            OutboundDelegateMsg::PutContractRequest(PutContractRequest { context, .. }) => {
761                Some(context)
762            }
763            OutboundDelegateMsg::UpdateContractRequest(UpdateContractRequest {
764                context, ..
765            }) => Some(context),
766            OutboundDelegateMsg::SubscribeContractRequest(SubscribeContractRequest {
767                context,
768                ..
769            }) => Some(context),
770            OutboundDelegateMsg::SendDelegateMessage(DelegateMessage { context, .. }) => {
771                Some(context)
772            }
773            _ => None,
774        }
775    }
776
777    pub fn get_mut_context(&mut self) -> Option<&mut DelegateContext> {
778        match self {
779            OutboundDelegateMsg::ApplicationMessage(ApplicationMessage { context, .. }) => {
780                Some(context)
781            }
782            OutboundDelegateMsg::GetContractRequest(GetContractRequest { context, .. }) => {
783                Some(context)
784            }
785            OutboundDelegateMsg::PutContractRequest(PutContractRequest { context, .. }) => {
786                Some(context)
787            }
788            OutboundDelegateMsg::UpdateContractRequest(UpdateContractRequest {
789                context, ..
790            }) => Some(context),
791            OutboundDelegateMsg::SubscribeContractRequest(SubscribeContractRequest {
792                context,
793                ..
794            }) => Some(context),
795            OutboundDelegateMsg::SendDelegateMessage(DelegateMessage { context, .. }) => {
796                Some(context)
797            }
798            _ => None,
799        }
800    }
801}
802
803/// Request to get contract state from within a delegate.
804#[derive(Serialize, Deserialize, Debug, Clone)]
805pub struct GetContractRequest {
806    pub contract_id: ContractInstanceId,
807    pub context: DelegateContext,
808    pub processed: bool,
809}
810
811impl GetContractRequest {
812    pub fn new(contract_id: ContractInstanceId) -> Self {
813        Self {
814            contract_id,
815            context: Default::default(),
816            processed: false,
817        }
818    }
819}
820
821/// Response containing contract state for a delegate.
822#[derive(Serialize, Deserialize, Debug, Clone)]
823pub struct GetContractResponse {
824    pub contract_id: ContractInstanceId,
825    /// The contract state, or None if the contract was not found locally.
826    pub state: Option<WrappedState>,
827    pub context: DelegateContext,
828}
829
830/// Request to store a new contract from within a delegate.
831#[derive(Serialize, Deserialize, Debug, Clone)]
832pub struct PutContractRequest {
833    /// The contract code and parameters.
834    pub contract: ContractContainer,
835    /// The initial state for the contract.
836    pub state: WrappedState,
837    /// Related contracts that this contract depends on.
838    #[serde(deserialize_with = "RelatedContracts::deser_related_contracts")]
839    pub related_contracts: RelatedContracts<'static>,
840    /// Context for the delegate.
841    pub context: DelegateContext,
842    /// Whether this request has been processed.
843    pub processed: bool,
844}
845
846impl PutContractRequest {
847    pub fn new(
848        contract: ContractContainer,
849        state: WrappedState,
850        related_contracts: RelatedContracts<'static>,
851    ) -> Self {
852        Self {
853            contract,
854            state,
855            related_contracts,
856            context: Default::default(),
857            processed: false,
858        }
859    }
860}
861
862/// Response after attempting to store a contract from a delegate.
863#[derive(Serialize, Deserialize, Debug, Clone)]
864pub struct PutContractResponse {
865    /// The ID of the contract that was (attempted to be) stored.
866    pub contract_id: ContractInstanceId,
867    /// Success (Ok) or error message (Err).
868    pub result: Result<(), String>,
869    /// Context for the delegate.
870    pub context: DelegateContext,
871}
872
873/// Request to update an existing contract's state from within a delegate.
874#[derive(Serialize, Deserialize, Debug, Clone)]
875pub struct UpdateContractRequest {
876    /// The contract to update.
877    pub contract_id: ContractInstanceId,
878    /// The update to apply (full state or delta).
879    #[serde(deserialize_with = "UpdateContractRequest::deser_update_data")]
880    pub update: UpdateData<'static>,
881    /// Context for the delegate.
882    pub context: DelegateContext,
883    /// Whether this request has been processed.
884    pub processed: bool,
885}
886
887impl UpdateContractRequest {
888    pub fn new(contract_id: ContractInstanceId, update: UpdateData<'static>) -> Self {
889        Self {
890            contract_id,
891            update,
892            context: Default::default(),
893            processed: false,
894        }
895    }
896
897    fn deser_update_data<'de, D>(deser: D) -> Result<UpdateData<'static>, D::Error>
898    where
899        D: Deserializer<'de>,
900    {
901        let value = <UpdateData<'de> as Deserialize>::deserialize(deser)?;
902        Ok(value.into_owned())
903    }
904}
905
906/// Response after attempting to update a contract from a delegate.
907#[derive(Serialize, Deserialize, Debug, Clone)]
908pub struct UpdateContractResponse {
909    /// The contract that was updated.
910    pub contract_id: ContractInstanceId,
911    /// Success (Ok) or error message (Err).
912    pub result: Result<(), String>,
913    /// Context for the delegate.
914    pub context: DelegateContext,
915}
916
917/// Request to subscribe to a contract's state changes from within a delegate.
918#[derive(Serialize, Deserialize, Debug, Clone)]
919pub struct SubscribeContractRequest {
920    /// The contract to subscribe to.
921    pub contract_id: ContractInstanceId,
922    /// Context for the delegate.
923    pub context: DelegateContext,
924    /// Whether this request has been processed.
925    pub processed: bool,
926}
927
928impl SubscribeContractRequest {
929    pub fn new(contract_id: ContractInstanceId) -> Self {
930        Self {
931            contract_id,
932            context: Default::default(),
933            processed: false,
934        }
935    }
936}
937
938/// Response after attempting to subscribe to a contract from a delegate.
939#[derive(Serialize, Deserialize, Debug, Clone)]
940pub struct SubscribeContractResponse {
941    /// The contract subscribed to.
942    pub contract_id: ContractInstanceId,
943    /// Success (Ok) or error message (Err).
944    pub result: Result<(), String>,
945    /// Context for the delegate.
946    pub context: DelegateContext,
947}
948
949/// A message sent from one delegate to another.
950///
951/// Delegates can communicate with each other by emitting
952/// `OutboundDelegateMsg::SendDelegateMessage` with a `DelegateMessage` targeting
953/// another delegate. The runtime delivers it as `InboundDelegateMsg::DelegateMessage`
954/// to the target delegate's `process()` function.
955///
956/// The `sender` field is overwritten by the runtime with the actual sender's key
957/// (sender attestation), so delegates cannot spoof their identity.
958#[derive(Serialize, Deserialize, Debug, Clone)]
959pub struct DelegateMessage {
960    /// The delegate to deliver this message to.
961    pub target: DelegateKey,
962    /// The delegate that sent this message (overwritten by runtime for attestation).
963    pub sender: DelegateKey,
964    /// Arbitrary message payload.
965    pub payload: Vec<u8>,
966    /// Delegate context, carried through the processing pipeline.
967    pub context: DelegateContext,
968    /// Runtime protocol flag indicating whether this message has been delivered.
969    pub processed: bool,
970}
971
972impl DelegateMessage {
973    pub fn new(target: DelegateKey, sender: DelegateKey, payload: Vec<u8>) -> Self {
974        Self {
975            target,
976            sender,
977            payload,
978            context: DelegateContext::default(),
979            processed: false,
980        }
981    }
982}
983
984/// Notification delivered to a delegate when a subscribed contract's state changes.
985#[derive(Serialize, Deserialize, Debug, Clone)]
986pub struct ContractNotification {
987    /// The contract whose state changed.
988    pub contract_id: ContractInstanceId,
989    /// The new state of the contract.
990    pub new_state: WrappedState,
991    /// Context for the delegate.
992    pub context: DelegateContext,
993}
994
995#[serde_as]
996#[derive(Serialize, Deserialize, Debug, Clone)]
997pub struct NotificationMessage<'a>(
998    #[serde_as(as = "serde_with::Bytes")]
999    #[serde(borrow)]
1000    Cow<'a, [u8]>,
1001);
1002
1003impl TryFrom<&serde_json::Value> for NotificationMessage<'static> {
1004    type Error = ();
1005
1006    fn try_from(json: &serde_json::Value) -> Result<NotificationMessage<'static>, ()> {
1007        // todo: validate format when we have a better idea of what we want here
1008        let bytes = serde_json::to_vec(json).unwrap();
1009        Ok(Self(Cow::Owned(bytes)))
1010    }
1011}
1012
1013impl NotificationMessage<'_> {
1014    pub fn into_owned(self) -> NotificationMessage<'static> {
1015        NotificationMessage(self.0.into_owned().into())
1016    }
1017    pub fn bytes(&self) -> &[u8] {
1018        self.0.as_ref()
1019    }
1020}
1021
1022#[serde_as]
1023#[derive(Serialize, Deserialize, Debug, Clone)]
1024pub struct ClientResponse<'a>(
1025    #[serde_as(as = "serde_with::Bytes")]
1026    #[serde(borrow)]
1027    Cow<'a, [u8]>,
1028);
1029
1030impl Deref for ClientResponse<'_> {
1031    type Target = [u8];
1032
1033    fn deref(&self) -> &Self::Target {
1034        &self.0
1035    }
1036}
1037
1038impl ClientResponse<'_> {
1039    pub fn new(response: Vec<u8>) -> Self {
1040        Self(response.into())
1041    }
1042    pub fn into_owned(self) -> ClientResponse<'static> {
1043        ClientResponse(self.0.into_owned().into())
1044    }
1045    pub fn bytes(&self) -> &[u8] {
1046        self.0.as_ref()
1047    }
1048}
1049
1050#[derive(Serialize, Deserialize, Debug, Clone)]
1051pub struct UserInputRequest<'a> {
1052    pub request_id: u32,
1053    #[serde(borrow)]
1054    /// An interpretable message by the notification system.
1055    pub message: NotificationMessage<'a>,
1056    /// If a response is required from the user they can be chosen from this list.
1057    pub responses: Vec<ClientResponse<'a>>,
1058}
1059
1060impl UserInputRequest<'_> {
1061    pub fn into_owned(self) -> UserInputRequest<'static> {
1062        UserInputRequest {
1063            request_id: self.request_id,
1064            message: self.message.into_owned(),
1065            responses: self.responses.into_iter().map(|r| r.into_owned()).collect(),
1066        }
1067    }
1068}
1069
1070#[doc(hidden)]
1071pub(crate) mod wasm_interface {
1072    //! Contains all the types to interface between the host environment and
1073    //! the wasm module execution.
1074    use super::*;
1075    use crate::memory::WasmLinearMem;
1076
1077    #[repr(C)]
1078    #[derive(Debug, Clone, Copy)]
1079    pub struct DelegateInterfaceResult {
1080        ptr: i64,
1081        size: u32,
1082    }
1083
1084    impl DelegateInterfaceResult {
1085        pub unsafe fn from_raw(ptr: i64, mem: &WasmLinearMem) -> Self {
1086            let result = Box::leak(Box::from_raw(crate::memory::buf::compute_ptr(
1087                ptr as *mut Self,
1088                mem,
1089            )));
1090            #[cfg(feature = "trace")]
1091            {
1092                tracing::trace!(
1093                    "got FFI result @ {ptr} ({:p}) -> {result:?}",
1094                    ptr as *mut Self
1095                );
1096            }
1097            *result
1098        }
1099
1100        #[cfg(feature = "contract")]
1101        pub fn into_raw(self) -> i64 {
1102            #[cfg(feature = "trace")]
1103            {
1104                tracing::trace!("returning FFI -> {self:?}");
1105            }
1106            let ptr = Box::into_raw(Box::new(self));
1107            #[cfg(feature = "trace")]
1108            {
1109                tracing::trace!("FFI result ptr: {ptr:p} ({}i64)", ptr as i64);
1110            }
1111            ptr as _
1112        }
1113
1114        pub unsafe fn unwrap(
1115            self,
1116            mem: WasmLinearMem,
1117        ) -> Result<Vec<OutboundDelegateMsg>, DelegateError> {
1118            let ptr = crate::memory::buf::compute_ptr(self.ptr as *mut u8, &mem);
1119            let serialized = std::slice::from_raw_parts(ptr as *const u8, self.size as _);
1120            let value: Result<Vec<OutboundDelegateMsg>, DelegateError> =
1121                bincode::deserialize(serialized)
1122                    .map_err(|e| DelegateError::Other(format!("{e}")))?;
1123            #[cfg(feature = "trace")]
1124            {
1125                tracing::trace!(
1126                    "got result through FFI; addr: {:p} ({}i64, mapped: {ptr:p})
1127                     serialized: {serialized:?}
1128                     value: {value:?}",
1129                    self.ptr as *mut u8,
1130                    self.ptr
1131                );
1132            }
1133            value
1134        }
1135    }
1136
1137    impl From<Result<Vec<OutboundDelegateMsg>, DelegateError>> for DelegateInterfaceResult {
1138        fn from(value: Result<Vec<OutboundDelegateMsg>, DelegateError>) -> Self {
1139            let serialized = bincode::serialize(&value).unwrap();
1140            let size = serialized.len() as _;
1141            let ptr = serialized.as_ptr();
1142            #[cfg(feature = "trace")]
1143            {
1144                tracing::trace!(
1145                    "sending result through FFI; addr: {ptr:p} ({}),\n  serialized: {serialized:?}\n  value: {value:?}",
1146                    ptr as i64
1147                );
1148            }
1149            std::mem::forget(serialized);
1150            Self {
1151                ptr: ptr as i64,
1152                size,
1153            }
1154        }
1155    }
1156}
1157
1158#[cfg(test)]
1159mod message_origin_tests {
1160    use super::*;
1161
1162    /// Wire-format pin: bincode encoding of `MessageOrigin::WebApp(..)` must
1163    /// stay byte-identical across stdlib releases. Deployed delegate WASM
1164    /// compiled against an older stdlib will receive these bytes from a
1165    /// host running the new stdlib and must continue to deserialize them.
1166    /// If this test ever fails, it is a wire-format break and is NOT
1167    /// publishable as a non-major bump.
1168    #[test]
1169    fn webapp_origin_wire_format_is_stable() {
1170        let id = ContractInstanceId::new([0xABu8; 32]);
1171        let origin = MessageOrigin::WebApp(id);
1172        let encoded = bincode::serialize(&origin).unwrap();
1173
1174        // Variant tag 0 (4-byte LE u32 in default bincode config) followed by
1175        // the 32 raw bytes of the ContractInstanceId.
1176        let mut expected = vec![0u8, 0, 0, 0];
1177        expected.extend_from_slice(&[0xABu8; 32]);
1178        assert_eq!(encoded, expected);
1179    }
1180
1181    /// Wire-format pin for the `Delegate` variant. Locks the full byte
1182    /// layout (variant tag + serde repr of `DelegateKey`) so that any future
1183    /// change to either `DelegateKey`'s serde or the workspace bincode
1184    /// config is caught loudly. If `DelegateKey`'s on-the-wire encoding
1185    /// changes, deployed delegates compiled against a previous stdlib will
1186    /// silently fail to deserialize inter-delegate origins — which is
1187    /// exactly the failure mode this test exists to prevent.
1188    #[test]
1189    fn delegate_origin_wire_format_is_stable() {
1190        let key = DelegateKey::new([0x11u8; 32], crate::code_hash::CodeHash::new([0x22u8; 32]));
1191        let origin = MessageOrigin::Delegate(key);
1192        let encoded = bincode::serialize(&origin).unwrap();
1193
1194        // Variant tag 1 (4-byte LE u32 in default bincode config), followed
1195        // by the 32-byte `key` field, followed by the 32-byte `code_hash`
1196        // field of `DelegateKey`.
1197        let mut expected = vec![1u8, 0, 0, 0];
1198        expected.extend_from_slice(&[0x11u8; 32]);
1199        expected.extend_from_slice(&[0x22u8; 32]);
1200        assert_eq!(encoded, expected);
1201
1202        // And it must still round-trip.
1203        let decoded: MessageOrigin = bincode::deserialize(&encoded).unwrap();
1204        assert!(matches!(decoded, MessageOrigin::Delegate(_)));
1205    }
1206
1207    /// Wire-format pin for the first variant of [`InboundDelegateMsg`]. Pins
1208    /// the tag so that reordering the enum cannot silently shift existing
1209    /// deployed delegate WASM off the correct variant. Only tag+payload
1210    /// prefix is asserted (not the full ApplicationMessage byte layout),
1211    /// since ApplicationMessage's internal fields have their own stability
1212    /// expectations handled at a different layer. What matters here is that
1213    /// variant 0 stays `ApplicationMessage` on the wire.
1214    #[test]
1215    fn inbound_delegate_msg_wire_format_is_stable() {
1216        let msg = InboundDelegateMsg::ApplicationMessage(ApplicationMessage::new(vec![0xCC]));
1217        let encoded = bincode::serialize(&msg).unwrap();
1218        assert_eq!(
1219            encoded[..4],
1220            [0, 0, 0, 0],
1221            "ApplicationMessage must stay at variant tag 0 on the wire; \
1222             reordering InboundDelegateMsg variants is a wire-format break"
1223        );
1224        // And it must still round-trip into the same variant.
1225        let decoded: InboundDelegateMsg<'_> = bincode::deserialize(&encoded).unwrap();
1226        assert!(matches!(decoded, InboundDelegateMsg::ApplicationMessage(_)));
1227    }
1228}