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