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