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().iter().as_ref());
283        Ok(DelegateKey {
284            key: key_bytes,
285            code_hash: CodeHash::from_code(key.code_hash().bytes()),
286        })
287    }
288}
289
290/// Type of errors during interaction with a delegate.
291#[derive(Debug, thiserror::Error, Serialize, Deserialize)]
292pub enum DelegateError {
293    #[error("de/serialization error: {0}")]
294    Deser(String),
295    #[error("{0}")]
296    Other(String),
297}
298
299fn generate_id<'a>(
300    parameters: &Parameters<'a>,
301    code_data: &DelegateCode<'a>,
302) -> [u8; DELEGATE_HASH_LENGTH] {
303    let contract_hash = code_data.hash();
304
305    let mut hasher = Blake3::new();
306    hasher.update(contract_hash.0.as_slice());
307    hasher.update(parameters.as_ref());
308    let full_key_arr = hasher.finalize();
309
310    debug_assert_eq!(full_key_arr[..].len(), DELEGATE_HASH_LENGTH);
311    let mut key = [0; DELEGATE_HASH_LENGTH];
312    key.copy_from_slice(&full_key_arr);
313    key
314}
315
316#[serde_as]
317#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
318pub struct SecretsId {
319    #[serde_as(as = "serde_with::Bytes")]
320    key: Vec<u8>,
321    #[serde_as(as = "[_; 32]")]
322    hash: [u8; 32],
323}
324
325impl SecretsId {
326    pub fn new(key: Vec<u8>) -> Self {
327        let mut hasher = Blake3::new();
328        hasher.update(&key);
329        let hashed = hasher.finalize();
330        let mut hash = [0; 32];
331        hash.copy_from_slice(&hashed);
332        Self { key, hash }
333    }
334
335    pub fn encode(&self) -> String {
336        bs58::encode(self.hash)
337            .with_alphabet(bs58::Alphabet::BITCOIN)
338            .into_string()
339    }
340
341    pub fn hash(&self) -> &[u8; 32] {
342        &self.hash
343    }
344    pub fn key(&self) -> &[u8] {
345        self.key.as_slice()
346    }
347}
348
349impl Display for SecretsId {
350    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
351        write!(f, "{}", self.encode())
352    }
353}
354
355impl<'a> TryFromFbs<&FbsSecretsId<'a>> for SecretsId {
356    fn try_decode_fbs(key: &FbsSecretsId<'a>) -> Result<Self, WsApiError> {
357        let mut key_hash = [0; 32];
358        key_hash.copy_from_slice(key.hash().bytes().iter().as_ref());
359        Ok(SecretsId {
360            key: key.key().bytes().to_vec(),
361            hash: key_hash,
362        })
363    }
364}
365
366/// Identifies where an inbound application message originated from.
367///
368/// When a web app sends a message to a delegate through the WebSocket API with
369/// an authentication token, the runtime resolves the token to the originating
370/// contract and wraps it in `MessageOrigin::WebApp`. When one delegate sends a
371/// message to another via [`OutboundDelegateMsg::SendDelegateMessage`], the
372/// runtime attests the caller's identity in `MessageOrigin::Delegate`.
373/// Delegates receive this as the `origin` parameter of
374/// [`DelegateInterface::process`].
375///
376/// This enum is `#[non_exhaustive]`: downstream code matching on it must
377/// include a wildcard arm so future variants can be added without a
378/// source-level breaking change.
379#[non_exhaustive]
380#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
381pub enum MessageOrigin {
382    /// The message was sent by a web application backed by the given contract.
383    WebApp(ContractInstanceId),
384    /// The message was sent by another delegate via
385    /// [`OutboundDelegateMsg::SendDelegateMessage`]. The carried key is the
386    /// runtime-attested identity of the calling delegate; the receiver can
387    /// trust it to make authorization decisions.
388    ///
389    /// Note: an inter-delegate message **replaces** rather than composes with
390    /// any inherited `WebApp` origin the calling delegate may itself hold.
391    /// The receiver sees only `Delegate(caller_key)` for the duration of the
392    /// call, and does not gain contract access on behalf of any web app the
393    /// caller was acting for. Authorization should be made on the calling
394    /// delegate's identity alone.
395    Delegate(DelegateKey),
396}
397
398/// A Delegate is a webassembly code designed to act as an agent for the user on
399/// Freenet. Delegates can:
400///
401///  * Store private data on behalf of the user
402///  * Create, read, and modify contracts
403///  * Create other delegates
404///  * Send and receive messages from other delegates and user interfaces
405///  * Ask the user questions and receive answers
406///
407/// Example use cases:
408///
409///  * A delegate stores a private key for the user, other components can ask
410///    the delegate to sign messages, it will ask the user for permission
411///  * A delegate monitors an inbox contract and downloads new messages when
412///    they arrive
413///
414/// # Example
415///
416/// ```ignore
417/// use freenet_stdlib::prelude::*;
418///
419/// struct MyDelegate;
420///
421/// #[delegate]
422/// impl DelegateInterface for MyDelegate {
423///     fn process(
424///         ctx: &mut DelegateCtx,
425///         _params: Parameters<'static>,
426///         _origin: Option<MessageOrigin>,
427///         message: InboundDelegateMsg,
428///     ) -> Result<Vec<OutboundDelegateMsg>, DelegateError> {
429///         // Access secrets synchronously - no round-trip needed!
430///         if let Some(key) = ctx.get_secret(b"private_key") {
431///             // use key...
432///         }
433///         ctx.set_secret(b"new_key", b"value");
434///
435///         // Read/write context for temporary state within a batch
436///         ctx.write(b"some state");
437///
438///         Ok(vec![])
439///     }
440/// }
441/// ```
442pub trait DelegateInterface {
443    /// Process inbound message, producing zero or more outbound messages in response.
444    ///
445    /// # Arguments
446    /// - `ctx`: Mutable handle to the delegate's execution environment. Provides:
447    ///   - **Context** (temporary): `read()`, `write()`, `len()`, `clear()` - state within a batch
448    ///   - **Secrets** (persistent): `get_secret()`, `set_secret()`, `has_secret()`, `remove_secret()`
449    /// - `parameters`: The delegate's initialization parameters.
450    /// - `origin`: An optional [`MessageOrigin`] identifying where the message came from.
451    ///   For messages sent by web applications, this is `MessageOrigin::WebApp(contract_id)`.
452    /// - `message`: The inbound message to process.
453    fn process(
454        ctx: &mut crate::delegate_host::DelegateCtx,
455        parameters: Parameters<'static>,
456        origin: Option<MessageOrigin>,
457        message: InboundDelegateMsg,
458    ) -> Result<Vec<OutboundDelegateMsg>, DelegateError>;
459}
460
461#[serde_as]
462#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
463pub struct DelegateContext(#[serde_as(as = "serde_with::Bytes")] Vec<u8>);
464
465impl DelegateContext {
466    pub const MAX_SIZE: usize = 4096 * 10 * 10;
467
468    pub fn new(bytes: Vec<u8>) -> Self {
469        assert!(bytes.len() < Self::MAX_SIZE);
470        Self(bytes)
471    }
472
473    pub fn append(&mut self, bytes: &mut Vec<u8>) {
474        assert!(self.0.len() + bytes.len() < Self::MAX_SIZE);
475        self.0.append(bytes)
476    }
477
478    pub fn replace(&mut self, bytes: Vec<u8>) {
479        assert!(bytes.len() < Self::MAX_SIZE);
480        let _ = std::mem::replace(&mut self.0, bytes);
481    }
482}
483
484impl AsRef<[u8]> for DelegateContext {
485    fn as_ref(&self) -> &[u8] {
486        &self.0
487    }
488}
489
490#[derive(Serialize, Deserialize, Debug, Clone)]
491pub enum InboundDelegateMsg<'a> {
492    ApplicationMessage(ApplicationMessage),
493    UserResponse(#[serde(borrow)] UserInputResponse<'a>),
494    GetContractResponse(GetContractResponse),
495    PutContractResponse(PutContractResponse),
496    UpdateContractResponse(UpdateContractResponse),
497    SubscribeContractResponse(SubscribeContractResponse),
498    ContractNotification(ContractNotification),
499    DelegateMessage(DelegateMessage),
500}
501
502impl InboundDelegateMsg<'_> {
503    pub fn into_owned(self) -> InboundDelegateMsg<'static> {
504        match self {
505            InboundDelegateMsg::ApplicationMessage(r) => InboundDelegateMsg::ApplicationMessage(r),
506            InboundDelegateMsg::UserResponse(r) => InboundDelegateMsg::UserResponse(r.into_owned()),
507            InboundDelegateMsg::GetContractResponse(r) => {
508                InboundDelegateMsg::GetContractResponse(r)
509            }
510            InboundDelegateMsg::PutContractResponse(r) => {
511                InboundDelegateMsg::PutContractResponse(r)
512            }
513            InboundDelegateMsg::UpdateContractResponse(r) => {
514                InboundDelegateMsg::UpdateContractResponse(r)
515            }
516            InboundDelegateMsg::SubscribeContractResponse(r) => {
517                InboundDelegateMsg::SubscribeContractResponse(r)
518            }
519            InboundDelegateMsg::ContractNotification(r) => {
520                InboundDelegateMsg::ContractNotification(r)
521            }
522            InboundDelegateMsg::DelegateMessage(r) => InboundDelegateMsg::DelegateMessage(r),
523        }
524    }
525
526    pub fn get_context(&self) -> Option<&DelegateContext> {
527        match self {
528            InboundDelegateMsg::ApplicationMessage(ApplicationMessage { context, .. }) => {
529                Some(context)
530            }
531            InboundDelegateMsg::GetContractResponse(GetContractResponse { context, .. }) => {
532                Some(context)
533            }
534            InboundDelegateMsg::PutContractResponse(PutContractResponse { context, .. }) => {
535                Some(context)
536            }
537            InboundDelegateMsg::UpdateContractResponse(UpdateContractResponse {
538                context, ..
539            }) => Some(context),
540            InboundDelegateMsg::SubscribeContractResponse(SubscribeContractResponse {
541                context,
542                ..
543            }) => Some(context),
544            InboundDelegateMsg::ContractNotification(ContractNotification { context, .. }) => {
545                Some(context)
546            }
547            InboundDelegateMsg::DelegateMessage(DelegateMessage { context, .. }) => Some(context),
548            _ => None,
549        }
550    }
551
552    pub fn get_mut_context(&mut self) -> Option<&mut DelegateContext> {
553        match self {
554            InboundDelegateMsg::ApplicationMessage(ApplicationMessage { context, .. }) => {
555                Some(context)
556            }
557            InboundDelegateMsg::GetContractResponse(GetContractResponse { context, .. }) => {
558                Some(context)
559            }
560            InboundDelegateMsg::PutContractResponse(PutContractResponse { context, .. }) => {
561                Some(context)
562            }
563            InboundDelegateMsg::UpdateContractResponse(UpdateContractResponse {
564                context, ..
565            }) => Some(context),
566            InboundDelegateMsg::SubscribeContractResponse(SubscribeContractResponse {
567                context,
568                ..
569            }) => Some(context),
570            InboundDelegateMsg::ContractNotification(ContractNotification { context, .. }) => {
571                Some(context)
572            }
573            InboundDelegateMsg::DelegateMessage(DelegateMessage { context, .. }) => Some(context),
574            _ => None,
575        }
576    }
577}
578
579impl From<ApplicationMessage> for InboundDelegateMsg<'_> {
580    fn from(value: ApplicationMessage) -> Self {
581        Self::ApplicationMessage(value)
582    }
583}
584
585impl<'a> TryFromFbs<&FbsInboundDelegateMsg<'a>> for InboundDelegateMsg<'a> {
586    fn try_decode_fbs(msg: &FbsInboundDelegateMsg<'a>) -> Result<Self, WsApiError> {
587        match msg.inbound_type() {
588            InboundDelegateMsgType::common_ApplicationMessage => {
589                let app_msg = msg.inbound_as_common_application_message().unwrap();
590                let app_msg = ApplicationMessage {
591                    payload: app_msg.payload().bytes().to_vec(),
592                    context: DelegateContext::new(app_msg.context().bytes().to_vec()),
593                    processed: app_msg.processed(),
594                };
595                Ok(InboundDelegateMsg::ApplicationMessage(app_msg))
596            }
597            InboundDelegateMsgType::UserInputResponse => {
598                let user_response = msg.inbound_as_user_input_response().unwrap();
599                let user_response = UserInputResponse {
600                    request_id: user_response.request_id(),
601                    response: ClientResponse::new(user_response.response().data().bytes().to_vec()),
602                    context: DelegateContext::new(
603                        user_response.delegate_context().bytes().to_vec(),
604                    ),
605                };
606                Ok(InboundDelegateMsg::UserResponse(user_response))
607            }
608            _ => unreachable!("invalid inbound delegate message type"),
609        }
610    }
611}
612
613#[non_exhaustive]
614#[derive(Serialize, Deserialize, Debug, Clone)]
615pub struct ApplicationMessage {
616    pub payload: Vec<u8>,
617    pub context: DelegateContext,
618    pub processed: bool,
619}
620
621impl ApplicationMessage {
622    pub fn new(payload: Vec<u8>) -> Self {
623        Self {
624            payload,
625            context: DelegateContext::default(),
626            processed: false,
627        }
628    }
629
630    pub fn with_context(mut self, context: DelegateContext) -> Self {
631        self.context = context;
632        self
633    }
634
635    pub fn processed(mut self, p: bool) -> Self {
636        self.processed = p;
637        self
638    }
639}
640
641#[derive(Serialize, Deserialize, Debug, Clone)]
642pub struct UserInputResponse<'a> {
643    pub request_id: u32,
644    #[serde(borrow)]
645    pub response: ClientResponse<'a>,
646    pub context: DelegateContext,
647}
648
649impl UserInputResponse<'_> {
650    pub fn into_owned(self) -> UserInputResponse<'static> {
651        UserInputResponse {
652            request_id: self.request_id,
653            response: self.response.into_owned(),
654            context: self.context,
655        }
656    }
657}
658
659#[derive(Serialize, Deserialize, Debug, Clone)]
660pub enum OutboundDelegateMsg {
661    // for the apps
662    ApplicationMessage(ApplicationMessage),
663    RequestUserInput(
664        #[serde(deserialize_with = "OutboundDelegateMsg::deser_user_input_req")]
665        UserInputRequest<'static>,
666    ),
667    // todo: remove when context can be accessed from the delegate environment and we pass it as reference
668    ContextUpdated(DelegateContext),
669    GetContractRequest(GetContractRequest),
670    PutContractRequest(PutContractRequest),
671    UpdateContractRequest(UpdateContractRequest),
672    SubscribeContractRequest(SubscribeContractRequest),
673    SendDelegateMessage(DelegateMessage),
674}
675
676impl From<ApplicationMessage> for OutboundDelegateMsg {
677    fn from(req: ApplicationMessage) -> Self {
678        Self::ApplicationMessage(req)
679    }
680}
681
682impl From<GetContractRequest> for OutboundDelegateMsg {
683    fn from(req: GetContractRequest) -> Self {
684        Self::GetContractRequest(req)
685    }
686}
687
688impl From<PutContractRequest> for OutboundDelegateMsg {
689    fn from(req: PutContractRequest) -> Self {
690        Self::PutContractRequest(req)
691    }
692}
693
694impl From<UpdateContractRequest> for OutboundDelegateMsg {
695    fn from(req: UpdateContractRequest) -> Self {
696        Self::UpdateContractRequest(req)
697    }
698}
699
700impl From<SubscribeContractRequest> for OutboundDelegateMsg {
701    fn from(req: SubscribeContractRequest) -> Self {
702        Self::SubscribeContractRequest(req)
703    }
704}
705
706impl From<DelegateMessage> for OutboundDelegateMsg {
707    fn from(msg: DelegateMessage) -> Self {
708        Self::SendDelegateMessage(msg)
709    }
710}
711
712impl OutboundDelegateMsg {
713    fn deser_user_input_req<'de, D>(deser: D) -> Result<UserInputRequest<'static>, D::Error>
714    where
715        D: serde::Deserializer<'de>,
716    {
717        let value = <UserInputRequest<'de> as Deserialize>::deserialize(deser)?;
718        Ok(value.into_owned())
719    }
720
721    pub fn processed(&self) -> bool {
722        match self {
723            OutboundDelegateMsg::ApplicationMessage(msg) => msg.processed,
724            OutboundDelegateMsg::GetContractRequest(msg) => msg.processed,
725            OutboundDelegateMsg::PutContractRequest(msg) => msg.processed,
726            OutboundDelegateMsg::UpdateContractRequest(msg) => msg.processed,
727            OutboundDelegateMsg::SubscribeContractRequest(msg) => msg.processed,
728            OutboundDelegateMsg::SendDelegateMessage(msg) => msg.processed,
729            OutboundDelegateMsg::RequestUserInput(_) => true,
730            OutboundDelegateMsg::ContextUpdated(_) => true,
731        }
732    }
733
734    pub fn get_context(&self) -> Option<&DelegateContext> {
735        match self {
736            OutboundDelegateMsg::ApplicationMessage(ApplicationMessage { context, .. }) => {
737                Some(context)
738            }
739            OutboundDelegateMsg::GetContractRequest(GetContractRequest { context, .. }) => {
740                Some(context)
741            }
742            OutboundDelegateMsg::PutContractRequest(PutContractRequest { context, .. }) => {
743                Some(context)
744            }
745            OutboundDelegateMsg::UpdateContractRequest(UpdateContractRequest {
746                context, ..
747            }) => Some(context),
748            OutboundDelegateMsg::SubscribeContractRequest(SubscribeContractRequest {
749                context,
750                ..
751            }) => Some(context),
752            OutboundDelegateMsg::SendDelegateMessage(DelegateMessage { context, .. }) => {
753                Some(context)
754            }
755            _ => None,
756        }
757    }
758
759    pub fn get_mut_context(&mut self) -> Option<&mut DelegateContext> {
760        match self {
761            OutboundDelegateMsg::ApplicationMessage(ApplicationMessage { context, .. }) => {
762                Some(context)
763            }
764            OutboundDelegateMsg::GetContractRequest(GetContractRequest { context, .. }) => {
765                Some(context)
766            }
767            OutboundDelegateMsg::PutContractRequest(PutContractRequest { context, .. }) => {
768                Some(context)
769            }
770            OutboundDelegateMsg::UpdateContractRequest(UpdateContractRequest {
771                context, ..
772            }) => Some(context),
773            OutboundDelegateMsg::SubscribeContractRequest(SubscribeContractRequest {
774                context,
775                ..
776            }) => Some(context),
777            OutboundDelegateMsg::SendDelegateMessage(DelegateMessage { context, .. }) => {
778                Some(context)
779            }
780            _ => None,
781        }
782    }
783}
784
785/// Request to get contract state from within a delegate.
786#[derive(Serialize, Deserialize, Debug, Clone)]
787pub struct GetContractRequest {
788    pub contract_id: ContractInstanceId,
789    pub context: DelegateContext,
790    pub processed: bool,
791}
792
793impl GetContractRequest {
794    pub fn new(contract_id: ContractInstanceId) -> Self {
795        Self {
796            contract_id,
797            context: Default::default(),
798            processed: false,
799        }
800    }
801}
802
803/// Response containing contract state for a delegate.
804#[derive(Serialize, Deserialize, Debug, Clone)]
805pub struct GetContractResponse {
806    pub contract_id: ContractInstanceId,
807    /// The contract state, or None if the contract was not found locally.
808    pub state: Option<WrappedState>,
809    pub context: DelegateContext,
810}
811
812/// Request to store a new contract from within a delegate.
813#[derive(Serialize, Deserialize, Debug, Clone)]
814pub struct PutContractRequest {
815    /// The contract code and parameters.
816    pub contract: ContractContainer,
817    /// The initial state for the contract.
818    pub state: WrappedState,
819    /// Related contracts that this contract depends on.
820    #[serde(deserialize_with = "RelatedContracts::deser_related_contracts")]
821    pub related_contracts: RelatedContracts<'static>,
822    /// Context for the delegate.
823    pub context: DelegateContext,
824    /// Whether this request has been processed.
825    pub processed: bool,
826}
827
828impl PutContractRequest {
829    pub fn new(
830        contract: ContractContainer,
831        state: WrappedState,
832        related_contracts: RelatedContracts<'static>,
833    ) -> Self {
834        Self {
835            contract,
836            state,
837            related_contracts,
838            context: Default::default(),
839            processed: false,
840        }
841    }
842}
843
844/// Response after attempting to store a contract from a delegate.
845#[derive(Serialize, Deserialize, Debug, Clone)]
846pub struct PutContractResponse {
847    /// The ID of the contract that was (attempted to be) stored.
848    pub contract_id: ContractInstanceId,
849    /// Success (Ok) or error message (Err).
850    pub result: Result<(), String>,
851    /// Context for the delegate.
852    pub context: DelegateContext,
853}
854
855/// Request to update an existing contract's state from within a delegate.
856#[derive(Serialize, Deserialize, Debug, Clone)]
857pub struct UpdateContractRequest {
858    /// The contract to update.
859    pub contract_id: ContractInstanceId,
860    /// The update to apply (full state or delta).
861    #[serde(deserialize_with = "UpdateContractRequest::deser_update_data")]
862    pub update: UpdateData<'static>,
863    /// Context for the delegate.
864    pub context: DelegateContext,
865    /// Whether this request has been processed.
866    pub processed: bool,
867}
868
869impl UpdateContractRequest {
870    pub fn new(contract_id: ContractInstanceId, update: UpdateData<'static>) -> Self {
871        Self {
872            contract_id,
873            update,
874            context: Default::default(),
875            processed: false,
876        }
877    }
878
879    fn deser_update_data<'de, D>(deser: D) -> Result<UpdateData<'static>, D::Error>
880    where
881        D: Deserializer<'de>,
882    {
883        let value = <UpdateData<'de> as Deserialize>::deserialize(deser)?;
884        Ok(value.into_owned())
885    }
886}
887
888/// Response after attempting to update a contract from a delegate.
889#[derive(Serialize, Deserialize, Debug, Clone)]
890pub struct UpdateContractResponse {
891    /// The contract that was updated.
892    pub contract_id: ContractInstanceId,
893    /// Success (Ok) or error message (Err).
894    pub result: Result<(), String>,
895    /// Context for the delegate.
896    pub context: DelegateContext,
897}
898
899/// Request to subscribe to a contract's state changes from within a delegate.
900#[derive(Serialize, Deserialize, Debug, Clone)]
901pub struct SubscribeContractRequest {
902    /// The contract to subscribe to.
903    pub contract_id: ContractInstanceId,
904    /// Context for the delegate.
905    pub context: DelegateContext,
906    /// Whether this request has been processed.
907    pub processed: bool,
908}
909
910impl SubscribeContractRequest {
911    pub fn new(contract_id: ContractInstanceId) -> Self {
912        Self {
913            contract_id,
914            context: Default::default(),
915            processed: false,
916        }
917    }
918}
919
920/// Response after attempting to subscribe to a contract from a delegate.
921#[derive(Serialize, Deserialize, Debug, Clone)]
922pub struct SubscribeContractResponse {
923    /// The contract subscribed to.
924    pub contract_id: ContractInstanceId,
925    /// Success (Ok) or error message (Err).
926    pub result: Result<(), String>,
927    /// Context for the delegate.
928    pub context: DelegateContext,
929}
930
931/// A message sent from one delegate to another.
932///
933/// Delegates can communicate with each other by emitting
934/// `OutboundDelegateMsg::SendDelegateMessage` with a `DelegateMessage` targeting
935/// another delegate. The runtime delivers it as `InboundDelegateMsg::DelegateMessage`
936/// to the target delegate's `process()` function.
937///
938/// The `sender` field is overwritten by the runtime with the actual sender's key
939/// (sender attestation), so delegates cannot spoof their identity.
940#[derive(Serialize, Deserialize, Debug, Clone)]
941pub struct DelegateMessage {
942    /// The delegate to deliver this message to.
943    pub target: DelegateKey,
944    /// The delegate that sent this message (overwritten by runtime for attestation).
945    pub sender: DelegateKey,
946    /// Arbitrary message payload.
947    pub payload: Vec<u8>,
948    /// Delegate context, carried through the processing pipeline.
949    pub context: DelegateContext,
950    /// Runtime protocol flag indicating whether this message has been delivered.
951    pub processed: bool,
952}
953
954impl DelegateMessage {
955    pub fn new(target: DelegateKey, sender: DelegateKey, payload: Vec<u8>) -> Self {
956        Self {
957            target,
958            sender,
959            payload,
960            context: DelegateContext::default(),
961            processed: false,
962        }
963    }
964}
965
966/// Notification delivered to a delegate when a subscribed contract's state changes.
967#[derive(Serialize, Deserialize, Debug, Clone)]
968pub struct ContractNotification {
969    /// The contract whose state changed.
970    pub contract_id: ContractInstanceId,
971    /// The new state of the contract.
972    pub new_state: WrappedState,
973    /// Context for the delegate.
974    pub context: DelegateContext,
975}
976
977#[serde_as]
978#[derive(Serialize, Deserialize, Debug, Clone)]
979pub struct NotificationMessage<'a>(
980    #[serde_as(as = "serde_with::Bytes")]
981    #[serde(borrow)]
982    Cow<'a, [u8]>,
983);
984
985impl TryFrom<&serde_json::Value> for NotificationMessage<'static> {
986    type Error = ();
987
988    fn try_from(json: &serde_json::Value) -> Result<NotificationMessage<'static>, ()> {
989        // todo: validate format when we have a better idea of what we want here
990        let bytes = serde_json::to_vec(json).unwrap();
991        Ok(Self(Cow::Owned(bytes)))
992    }
993}
994
995impl NotificationMessage<'_> {
996    pub fn into_owned(self) -> NotificationMessage<'static> {
997        NotificationMessage(self.0.into_owned().into())
998    }
999    pub fn bytes(&self) -> &[u8] {
1000        self.0.as_ref()
1001    }
1002}
1003
1004#[serde_as]
1005#[derive(Serialize, Deserialize, Debug, Clone)]
1006pub struct ClientResponse<'a>(
1007    #[serde_as(as = "serde_with::Bytes")]
1008    #[serde(borrow)]
1009    Cow<'a, [u8]>,
1010);
1011
1012impl Deref for ClientResponse<'_> {
1013    type Target = [u8];
1014
1015    fn deref(&self) -> &Self::Target {
1016        &self.0
1017    }
1018}
1019
1020impl ClientResponse<'_> {
1021    pub fn new(response: Vec<u8>) -> Self {
1022        Self(response.into())
1023    }
1024    pub fn into_owned(self) -> ClientResponse<'static> {
1025        ClientResponse(self.0.into_owned().into())
1026    }
1027    pub fn bytes(&self) -> &[u8] {
1028        self.0.as_ref()
1029    }
1030}
1031
1032#[derive(Serialize, Deserialize, Debug, Clone)]
1033pub struct UserInputRequest<'a> {
1034    pub request_id: u32,
1035    #[serde(borrow)]
1036    /// An interpretable message by the notification system.
1037    pub message: NotificationMessage<'a>,
1038    /// If a response is required from the user they can be chosen from this list.
1039    pub responses: Vec<ClientResponse<'a>>,
1040}
1041
1042impl UserInputRequest<'_> {
1043    pub fn into_owned(self) -> UserInputRequest<'static> {
1044        UserInputRequest {
1045            request_id: self.request_id,
1046            message: self.message.into_owned(),
1047            responses: self.responses.into_iter().map(|r| r.into_owned()).collect(),
1048        }
1049    }
1050}
1051
1052#[doc(hidden)]
1053pub(crate) mod wasm_interface {
1054    //! Contains all the types to interface between the host environment and
1055    //! the wasm module execution.
1056    use super::*;
1057    use crate::memory::WasmLinearMem;
1058
1059    #[repr(C)]
1060    #[derive(Debug, Clone, Copy)]
1061    pub struct DelegateInterfaceResult {
1062        ptr: i64,
1063        size: u32,
1064    }
1065
1066    impl DelegateInterfaceResult {
1067        pub unsafe fn from_raw(ptr: i64, mem: &WasmLinearMem) -> Self {
1068            let result = Box::leak(Box::from_raw(crate::memory::buf::compute_ptr(
1069                ptr as *mut Self,
1070                mem,
1071            )));
1072            #[cfg(feature = "trace")]
1073            {
1074                tracing::trace!(
1075                    "got FFI result @ {ptr} ({:p}) -> {result:?}",
1076                    ptr as *mut Self
1077                );
1078            }
1079            *result
1080        }
1081
1082        #[cfg(feature = "contract")]
1083        pub fn into_raw(self) -> i64 {
1084            #[cfg(feature = "trace")]
1085            {
1086                tracing::trace!("returning FFI -> {self:?}");
1087            }
1088            let ptr = Box::into_raw(Box::new(self));
1089            #[cfg(feature = "trace")]
1090            {
1091                tracing::trace!("FFI result ptr: {ptr:p} ({}i64)", ptr as i64);
1092            }
1093            ptr as _
1094        }
1095
1096        pub unsafe fn unwrap(
1097            self,
1098            mem: WasmLinearMem,
1099        ) -> Result<Vec<OutboundDelegateMsg>, DelegateError> {
1100            let ptr = crate::memory::buf::compute_ptr(self.ptr as *mut u8, &mem);
1101            let serialized = std::slice::from_raw_parts(ptr as *const u8, self.size as _);
1102            let value: Result<Vec<OutboundDelegateMsg>, DelegateError> =
1103                bincode::deserialize(serialized)
1104                    .map_err(|e| DelegateError::Other(format!("{e}")))?;
1105            #[cfg(feature = "trace")]
1106            {
1107                tracing::trace!(
1108                    "got result through FFI; addr: {:p} ({}i64, mapped: {ptr:p})
1109                     serialized: {serialized:?}
1110                     value: {value:?}",
1111                    self.ptr as *mut u8,
1112                    self.ptr
1113                );
1114            }
1115            value
1116        }
1117    }
1118
1119    impl From<Result<Vec<OutboundDelegateMsg>, DelegateError>> for DelegateInterfaceResult {
1120        fn from(value: Result<Vec<OutboundDelegateMsg>, DelegateError>) -> Self {
1121            let serialized = bincode::serialize(&value).unwrap();
1122            let size = serialized.len() as _;
1123            let ptr = serialized.as_ptr();
1124            #[cfg(feature = "trace")]
1125            {
1126                tracing::trace!(
1127                    "sending result through FFI; addr: {ptr:p} ({}),\n  serialized: {serialized:?}\n  value: {value:?}",
1128                    ptr as i64
1129                );
1130            }
1131            std::mem::forget(serialized);
1132            Self {
1133                ptr: ptr as i64,
1134                size,
1135            }
1136        }
1137    }
1138}
1139
1140#[cfg(test)]
1141mod message_origin_tests {
1142    use super::*;
1143
1144    /// Wire-format pin: bincode encoding of `MessageOrigin::WebApp(..)` must
1145    /// stay byte-identical across stdlib releases. Deployed delegate WASM
1146    /// compiled against an older stdlib will receive these bytes from a
1147    /// host running the new stdlib and must continue to deserialize them.
1148    /// If this test ever fails, it is a wire-format break and is NOT
1149    /// publishable as a non-major bump.
1150    #[test]
1151    fn webapp_origin_wire_format_is_stable() {
1152        let id = ContractInstanceId::new([0xABu8; 32]);
1153        let origin = MessageOrigin::WebApp(id);
1154        let encoded = bincode::serialize(&origin).unwrap();
1155
1156        // Variant tag 0 (4-byte LE u32 in default bincode config) followed by
1157        // the 32 raw bytes of the ContractInstanceId.
1158        let mut expected = vec![0u8, 0, 0, 0];
1159        expected.extend_from_slice(&[0xABu8; 32]);
1160        assert_eq!(encoded, expected);
1161    }
1162
1163    /// Wire-format pin for the `Delegate` variant. Locks the full byte
1164    /// layout (variant tag + serde repr of `DelegateKey`) so that any future
1165    /// change to either `DelegateKey`'s serde or the workspace bincode
1166    /// config is caught loudly. If `DelegateKey`'s on-the-wire encoding
1167    /// changes, deployed delegates compiled against a previous stdlib will
1168    /// silently fail to deserialize inter-delegate origins — which is
1169    /// exactly the failure mode this test exists to prevent.
1170    #[test]
1171    fn delegate_origin_wire_format_is_stable() {
1172        let key = DelegateKey::new([0x11u8; 32], crate::code_hash::CodeHash::new([0x22u8; 32]));
1173        let origin = MessageOrigin::Delegate(key);
1174        let encoded = bincode::serialize(&origin).unwrap();
1175
1176        // Variant tag 1 (4-byte LE u32 in default bincode config), followed
1177        // by the 32-byte `key` field, followed by the 32-byte `code_hash`
1178        // field of `DelegateKey`.
1179        let mut expected = vec![1u8, 0, 0, 0];
1180        expected.extend_from_slice(&[0x11u8; 32]);
1181        expected.extend_from_slice(&[0x22u8; 32]);
1182        assert_eq!(encoded, expected);
1183
1184        // And it must still round-trip.
1185        let decoded: MessageOrigin = bincode::deserialize(&encoded).unwrap();
1186        assert!(matches!(decoded, MessageOrigin::Delegate(_)));
1187    }
1188}