freenet_stdlib/client_api/
client_events.rs

1use flatbuffers::WIPOffset;
2use std::borrow::Cow;
3use std::fmt::Display;
4use std::net::SocketAddr;
5
6use serde::{de::DeserializeOwned, Deserialize, Serialize};
7
8use crate::client_api::TryFromFbs;
9use crate::generated::client_request::{
10    root_as_client_request, ClientRequestType, ContractRequest as FbsContractRequest,
11    ContractRequestType, DelegateRequest as FbsDelegateRequest, DelegateRequestType,
12};
13
14use crate::delegate_interface::DelegateContext;
15use crate::generated::common::{
16    ApplicationMessage as FbsApplicationMessage, ApplicationMessageArgs, ContractCode,
17    ContractCodeArgs, ContractContainer as FbsContractContainer, ContractContainerArgs,
18    ContractInstanceId as FbsContractInstanceId, ContractInstanceIdArgs,
19    ContractKey as FbsContractKey, ContractKeyArgs,
20    ContractType, DeltaUpdate, DeltaUpdateArgs, GetSecretRequest as FbsGetSecretRequest,
21    GetSecretRequestArgs, GetSecretResponse as FbsGetSecretResponse, GetSecretResponseArgs,
22    RelatedDeltaUpdate, RelatedDeltaUpdateArgs, RelatedStateAndDeltaUpdate,
23    RelatedStateAndDeltaUpdateArgs, RelatedStateUpdate, RelatedStateUpdateArgs,
24    SecretsId as FbsSecretsId, SecretsIdArgs, StateAndDeltaUpdate, StateAndDeltaUpdateArgs,
25    StateUpdate, StateUpdateArgs, UpdateData as FbsUpdateData, UpdateDataArgs, UpdateDataType,
26    WasmContractV1, WasmContractV1Args,
27};
28use crate::generated::host_response::{
29    finish_host_response_buffer, ClientResponse as FbsClientResponse, ClientResponseArgs,
30    ContextUpdated as FbsContextUpdated, ContextUpdatedArgs,
31    ContractResponse as FbsContractResponse, ContractResponseArgs, ContractResponseType,
32    DelegateKey as FbsDelegateKey, DelegateKeyArgs, DelegateResponse as FbsDelegateResponse,
33    DelegateResponseArgs, GetResponse as FbsGetResponse, GetResponseArgs,
34    HostResponse as FbsHostResponse, HostResponseArgs, HostResponseType, Ok as FbsOk, OkArgs,
35    OutboundDelegateMsg as FbsOutboundDelegateMsg, OutboundDelegateMsgArgs,
36    OutboundDelegateMsgType, PutResponse as FbsPutResponse, PutResponseArgs,
37    RequestUserInput as FbsRequestUserInput, RequestUserInputArgs,
38    SetSecretRequest as FbsSetSecretRequest, SetSecretRequestArgs,
39    UpdateNotification as FbsUpdateNotification, UpdateNotificationArgs,
40    UpdateResponse as FbsUpdateResponse, UpdateResponseArgs,
41};
42use crate::prelude::ContractContainer::Wasm;
43use crate::prelude::ContractWasmAPIVersion::V1;
44use crate::prelude::UpdateData::{
45    Delta, RelatedDelta, RelatedState, RelatedStateAndDelta, State, StateAndDelta,
46};
47use crate::{
48    delegate_interface::{DelegateKey, InboundDelegateMsg, OutboundDelegateMsg},
49    prelude::{
50        ContractInstanceId, ContractKey, DelegateContainer, GetSecretRequest, Parameters,
51        RelatedContracts, SecretsId, StateSummary, UpdateData, WrappedState,
52    },
53    versioning::ContractContainer,
54};
55
56use super::WsApiError;
57
58#[derive(Debug, Serialize, Deserialize, Clone)]
59pub struct ClientError {
60    kind: Box<ErrorKind>,
61}
62
63impl ClientError {
64    pub fn into_fbs_bytes(self) -> Result<Vec<u8>, Box<ClientError>> {
65        use crate::generated::host_response::{Error, ErrorArgs};
66        let mut builder = flatbuffers::FlatBufferBuilder::new();
67        let msg_offset = builder.create_string(&self.to_string());
68        let err_offset = Error::create(
69            &mut builder,
70            &ErrorArgs {
71                msg: Some(msg_offset),
72            },
73        );
74        let host_response_offset = FbsHostResponse::create(
75            &mut builder,
76            &HostResponseArgs {
77                response_type: HostResponseType::Ok,
78                response: Some(err_offset.as_union_value()),
79            },
80        );
81        finish_host_response_buffer(&mut builder, host_response_offset);
82        Ok(builder.finished_data().to_vec())
83    }
84
85    pub fn kind(&self) -> &ErrorKind {
86        &self.kind
87    }
88}
89
90impl From<ErrorKind> for ClientError {
91    fn from(kind: ErrorKind) -> Self {
92        ClientError {
93            kind: Box::new(kind),
94        }
95    }
96}
97
98impl<T: Into<Cow<'static, str>>> From<T> for ClientError {
99    fn from(cause: T) -> Self {
100        ClientError {
101            kind: Box::new(ErrorKind::Unhandled {
102                cause: cause.into(),
103            }),
104        }
105    }
106}
107
108#[derive(thiserror::Error, Debug, Serialize, Deserialize, Clone)]
109#[non_exhaustive]
110pub enum ErrorKind {
111    #[error("comm channel between client/host closed")]
112    ChannelClosed,
113    #[error("error while deserializing: {cause}")]
114    DeserializationError { cause: Cow<'static, str> },
115    #[error("client disconnected")]
116    Disconnect,
117    #[error("failed while trying to unpack state for {0}")]
118    IncorrectState(ContractKey),
119    #[error("node not available")]
120    NodeUnavailable,
121    #[error("lost the connection with the protocol handling connections")]
122    TransportProtocolDisconnect,
123    #[error("unhandled error: {cause}")]
124    Unhandled { cause: Cow<'static, str> },
125    #[error("unknown client id: {0}")]
126    UnknownClient(usize),
127    #[error(transparent)]
128    RequestError(#[from] RequestError),
129    #[error("error while executing operation in the network: {cause}")]
130    OperationError { cause: Cow<'static, str> },
131    // TODO: identify requests by some id so we can inform clients which one failed exactly
132    #[error("operation timed out")]
133    FailedOperation,
134    #[error("peer should shutdown")]
135    Shutdown,
136}
137
138impl Display for ClientError {
139    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140        write!(f, "client error: {}", self.kind)
141    }
142}
143
144impl std::error::Error for ClientError {}
145
146#[derive(Debug, thiserror::Error, Serialize, Deserialize, Clone)]
147#[non_exhaustive]
148pub enum RequestError {
149    #[error(transparent)]
150    ContractError(#[from] ContractError),
151    #[error(transparent)]
152    DelegateError(#[from] DelegateError),
153    #[error("client disconnect")]
154    Disconnect,
155    #[error("operation timed out")]
156    Timeout,
157}
158
159/// Errors that may happen while interacting with delegates.
160#[derive(Debug, thiserror::Error, Serialize, Deserialize, Clone)]
161#[non_exhaustive]
162pub enum DelegateError {
163    #[error("error while registering delegate {0}")]
164    RegisterError(DelegateKey),
165    #[error("execution error, cause {0}")]
166    ExecutionError(Cow<'static, str>),
167    #[error("missing delegate {0}")]
168    Missing(DelegateKey),
169    #[error("missing secret `{secret}` for delegate {key}")]
170    MissingSecret { key: DelegateKey, secret: SecretsId },
171    #[error("forbidden access to secret: {0}")]
172    ForbiddenSecretAccess(SecretsId),
173}
174
175/// Errors that may happen while interacting with contracts.
176#[derive(Debug, thiserror::Error, Serialize, Deserialize, Clone)]
177#[non_exhaustive]
178pub enum ContractError {
179    #[error("failed to get contract {key}, reason: {cause}")]
180    Get {
181        key: ContractKey,
182        cause: Cow<'static, str>,
183    },
184    #[error("put error for contract {key}, reason: {cause}")]
185    Put {
186        key: ContractKey,
187        cause: Cow<'static, str>,
188    },
189    #[error("update error for contract {key}, reason: {cause}")]
190    Update {
191        key: ContractKey,
192        cause: Cow<'static, str>,
193    },
194    #[error("failed to subscribe for contract {key}, reason: {cause}")]
195    Subscribe {
196        key: ContractKey,
197        cause: Cow<'static, str>,
198    },
199    // todo: actually build a stack of the involved keys
200    #[error("dependency contract stack overflow : {key}")]
201    ContractStackOverflow {
202        key: crate::contract_interface::ContractInstanceId,
203    },
204    #[error("missing related contract: {key}")]
205    MissingRelated {
206        key: crate::contract_interface::ContractInstanceId,
207    },
208    #[error("missing contract: {key}")]
209    MissingContract {
210        key: crate::contract_interface::ContractInstanceId,
211    },
212}
213
214impl ContractError {
215    const EXECUTION_ERROR: &'static str = "execution error";
216    const INVALID_PUT: &'static str = "invalid put";
217
218    pub fn update_exec_error(key: ContractKey, additional_info: impl std::fmt::Display) -> Self {
219        Self::Update {
220            key,
221            cause: format!(
222                "{exec_err}: {additional_info}",
223                exec_err = Self::EXECUTION_ERROR
224            )
225            .into(),
226        }
227    }
228
229    pub fn invalid_put(key: ContractKey) -> Self {
230        Self::Put {
231            key,
232            cause: Self::INVALID_PUT.into(),
233        }
234    }
235
236    pub fn invalid_update(key: ContractKey) -> Self {
237        Self::Update {
238            key,
239            cause: Self::INVALID_PUT.into(),
240        }
241    }
242}
243
244/// A request from a client application to the host.
245#[derive(Serialize, Deserialize, Debug, Clone)]
246#[non_exhaustive]
247// #[cfg_attr(test, derive(arbitrary::Arbitrary))]
248pub enum ClientRequest<'a> {
249    DelegateOp(#[serde(borrow)] DelegateRequest<'a>),
250    ContractOp(#[serde(borrow)] ContractRequest<'a>),
251    Disconnect {
252        cause: Option<Cow<'static, str>>,
253    },
254    Authenticate {
255        token: String,
256    },
257    NodeQueries(NodeQuery),
258    /// Gracefully disconnect from the host.
259    Close,
260}
261
262#[derive(Serialize, Deserialize, Debug, Clone)]
263pub struct ConnectedPeers {}
264
265#[derive(Serialize, Deserialize, Debug, Clone)]
266pub struct NodeDiagnostics {
267    /// Optional contract key to filter diagnostics for specific contract
268    pub contract_key: Option<ContractKey>,
269}
270
271impl ClientRequest<'_> {
272    pub fn into_owned(self) -> ClientRequest<'static> {
273        match self {
274            ClientRequest::ContractOp(op) => {
275                let owned = match op {
276                    ContractRequest::Put {
277                        contract,
278                        state,
279                        related_contracts,
280                        subscribe,
281                    } => {
282                        let related_contracts = related_contracts.into_owned();
283                        ContractRequest::Put {
284                            contract,
285                            state,
286                            related_contracts,
287                            subscribe,
288                        }
289                    }
290                    ContractRequest::Update { key, data } => {
291                        let data = data.into_owned();
292                        ContractRequest::Update { key, data }
293                    }
294                    ContractRequest::Get {
295                        key,
296                        return_contract_code,
297                        subscribe,
298                    } => ContractRequest::Get {
299                        key,
300                        return_contract_code,
301                        subscribe,
302                    },
303                    ContractRequest::Subscribe { key, summary } => ContractRequest::Subscribe {
304                        key,
305                        summary: summary.map(StateSummary::into_owned),
306                    },
307                };
308                owned.into()
309            }
310            ClientRequest::DelegateOp(op) => {
311                let op = op.into_owned();
312                ClientRequest::DelegateOp(op)
313            }
314            ClientRequest::Disconnect { cause } => ClientRequest::Disconnect { cause },
315            ClientRequest::Authenticate { token } => ClientRequest::Authenticate { token },
316            ClientRequest::NodeQueries(query) => ClientRequest::NodeQueries(query),
317            ClientRequest::Close => ClientRequest::Close,
318        }
319    }
320
321    pub fn is_disconnect(&self) -> bool {
322        matches!(self, Self::Disconnect { .. })
323    }
324
325    pub fn try_decode_fbs(msg: &[u8]) -> Result<ClientRequest<'_>, WsApiError> {
326        let req = {
327            match root_as_client_request(msg) {
328                Ok(client_request) => match client_request.client_request_type() {
329                    ClientRequestType::ContractRequest => {
330                        let contract_request =
331                            client_request.client_request_as_contract_request().unwrap();
332                        ContractRequest::try_decode_fbs(&contract_request)?.into()
333                    }
334                    ClientRequestType::DelegateRequest => {
335                        let delegate_request =
336                            client_request.client_request_as_delegate_request().unwrap();
337                        DelegateRequest::try_decode_fbs(&delegate_request)?.into()
338                    }
339                    ClientRequestType::Disconnect => {
340                        let delegate_request =
341                            client_request.client_request_as_disconnect().unwrap();
342                        let cause = delegate_request
343                            .cause()
344                            .map(|cause_msg| cause_msg.to_string().into());
345                        ClientRequest::Disconnect { cause }
346                    }
347                    ClientRequestType::Authenticate => {
348                        let auth_req = client_request.client_request_as_authenticate().unwrap();
349                        let token = auth_req.token();
350                        ClientRequest::Authenticate {
351                            token: token.to_owned(),
352                        }
353                    }
354                    _ => unreachable!(),
355                },
356                Err(e) => {
357                    let cause = format!("{e}");
358                    return Err(WsApiError::deserialization(cause));
359                }
360            }
361        };
362
363        Ok(req)
364    }
365}
366
367#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
368#[non_exhaustive]
369pub enum ContractRequest<'a> {
370    /// Insert a new value in a contract corresponding with the provided key.
371    Put {
372        contract: ContractContainer,
373        /// Value to upsert in the contract.
374        state: WrappedState,
375        /// Related contracts.
376        #[serde(borrow)]
377        related_contracts: RelatedContracts<'a>,
378        /// If this flag is set then subscribe to updates for this contract.
379        subscribe: bool,
380    },
381    /// Update an existing contract corresponding with the provided key.
382    Update {
383        key: ContractKey,
384        #[serde(borrow)]
385        data: UpdateData<'a>,
386    },
387    /// Fetch the current state from a contract corresponding to the provided key.
388    Get {
389        /// Instance ID of the contract (the hash of code + params).
390        /// Only the instance ID is needed since the client doesn't have the code hash yet.
391        key: ContractInstanceId,
392        /// If this flag is set then fetch also the contract itself.
393        return_contract_code: bool,
394        /// If this flag is set then subscribe to updates for this contract.
395        subscribe: bool,
396    },
397    /// Subscribe to the changes in a given contract. Implicitly starts a get operation
398    /// if the contract is not present yet.
399    Subscribe {
400        /// Instance ID of the contract.
401        key: ContractInstanceId,
402        summary: Option<StateSummary<'a>>,
403    },
404}
405
406impl ContractRequest<'_> {
407    pub fn into_owned(self) -> ContractRequest<'static> {
408        match self {
409            Self::Put {
410                contract,
411                state,
412                related_contracts,
413                subscribe,
414            } => ContractRequest::Put {
415                contract,
416                state,
417                related_contracts: related_contracts.into_owned(),
418                subscribe,
419            },
420            Self::Update { key, data } => ContractRequest::Update {
421                key,
422                data: data.into_owned(),
423            },
424            Self::Get {
425                key,
426                return_contract_code: fetch_contract,
427                subscribe,
428            } => ContractRequest::Get {
429                key,
430                return_contract_code: fetch_contract,
431                subscribe,
432            },
433            Self::Subscribe { key, summary } => ContractRequest::Subscribe {
434                key,
435                summary: summary.map(StateSummary::into_owned),
436            },
437        }
438    }
439}
440
441impl<'a> From<ContractRequest<'a>> for ClientRequest<'a> {
442    fn from(op: ContractRequest<'a>) -> Self {
443        ClientRequest::ContractOp(op)
444    }
445}
446
447/// Deserializes a `ContractRequest` from a Flatbuffers message.
448impl<'a> TryFromFbs<&FbsContractRequest<'a>> for ContractRequest<'a> {
449    fn try_decode_fbs(request: &FbsContractRequest<'a>) -> Result<Self, WsApiError> {
450        let req = {
451            match request.contract_request_type() {
452                ContractRequestType::Get => {
453                    let get = request.contract_request_as_get().unwrap();
454                    // Extract just the instance ID - GET only needs the instance ID,
455                    // not the full key (which may not be complete on the client side)
456                    let fbs_key = get.key();
457                    let key_bytes: [u8; 32] = fbs_key.instance().data().bytes().try_into().unwrap();
458                    let key = ContractInstanceId::new(key_bytes);
459                    let fetch_contract = get.fetch_contract();
460                    let subscribe = get.subscribe();
461                    ContractRequest::Get {
462                        key,
463                        return_contract_code: fetch_contract,
464                        subscribe,
465                    }
466                }
467                ContractRequestType::Put => {
468                    let put = request.contract_request_as_put().unwrap();
469                    let contract = ContractContainer::try_decode_fbs(&put.container())?;
470                    let state = WrappedState::new(put.wrapped_state().bytes().to_vec());
471                    let related_contracts =
472                        RelatedContracts::try_decode_fbs(&put.related_contracts())?.into_owned();
473                    let subscribe = put.subscribe();
474                    ContractRequest::Put {
475                        contract,
476                        state,
477                        related_contracts,
478                        subscribe,
479                    }
480                }
481                ContractRequestType::Update => {
482                    let update = request.contract_request_as_update().unwrap();
483                    let key = ContractKey::try_decode_fbs(&update.key())?;
484                    let data = UpdateData::try_decode_fbs(&update.data())?.into_owned();
485                    ContractRequest::Update { key, data }
486                }
487                ContractRequestType::Subscribe => {
488                    let subscribe = request.contract_request_as_subscribe().unwrap();
489                    // Extract just the instance ID for Subscribe
490                    let fbs_key = subscribe.key();
491                    let key_bytes: [u8; 32] = fbs_key.instance().data().bytes().try_into().unwrap();
492                    let key = ContractInstanceId::new(key_bytes);
493                    let summary = subscribe
494                        .summary()
495                        .map(|summary_data| StateSummary::from(summary_data.bytes()));
496                    ContractRequest::Subscribe { key, summary }
497                }
498                _ => unreachable!(),
499            }
500        };
501
502        Ok(req)
503    }
504}
505
506impl<'a> From<DelegateRequest<'a>> for ClientRequest<'a> {
507    fn from(op: DelegateRequest<'a>) -> Self {
508        ClientRequest::DelegateOp(op)
509    }
510}
511
512#[derive(Serialize, Deserialize, Debug, Clone)]
513#[non_exhaustive]
514pub enum DelegateRequest<'a> {
515    ApplicationMessages {
516        key: DelegateKey,
517        #[serde(deserialize_with = "Parameters::deser_params")]
518        params: Parameters<'a>,
519        #[serde(borrow)]
520        inbound: Vec<InboundDelegateMsg<'a>>,
521    },
522    GetSecretRequest {
523        key: DelegateKey,
524        #[serde(borrow)]
525        params: Parameters<'a>,
526        get_request: GetSecretRequest,
527    },
528    RegisterDelegate {
529        delegate: DelegateContainer,
530        cipher: [u8; 32],
531        nonce: [u8; 24],
532    },
533    UnregisterDelegate(DelegateKey),
534}
535
536impl DelegateRequest<'_> {
537    pub const DEFAULT_CIPHER: [u8; 32] = [
538        0, 24, 22, 150, 112, 207, 24, 65, 182, 161, 169, 227, 66, 182, 237, 215, 206, 164, 58, 161,
539        64, 108, 157, 195, 0, 0, 0, 0, 0, 0, 0, 0,
540    ];
541
542    pub const DEFAULT_NONCE: [u8; 24] = [
543        57, 18, 79, 116, 63, 134, 93, 39, 208, 161, 156, 229, 222, 247, 111, 79, 210, 126, 127, 55,
544        224, 150, 139, 80,
545    ];
546
547    pub fn into_owned(self) -> DelegateRequest<'static> {
548        match self {
549            DelegateRequest::ApplicationMessages {
550                key,
551                inbound,
552                params,
553            } => DelegateRequest::ApplicationMessages {
554                key,
555                params: params.into_owned(),
556                inbound: inbound.into_iter().map(|e| e.into_owned()).collect(),
557            },
558            DelegateRequest::GetSecretRequest {
559                key,
560                get_request,
561                params,
562            } => DelegateRequest::GetSecretRequest {
563                key,
564                get_request,
565                params: params.into_owned(),
566            },
567            DelegateRequest::RegisterDelegate {
568                delegate,
569                cipher,
570                nonce,
571            } => DelegateRequest::RegisterDelegate {
572                delegate,
573                cipher,
574                nonce,
575            },
576            DelegateRequest::UnregisterDelegate(key) => DelegateRequest::UnregisterDelegate(key),
577        }
578    }
579
580    pub fn key(&self) -> &DelegateKey {
581        match self {
582            DelegateRequest::ApplicationMessages { key, .. } => key,
583            DelegateRequest::GetSecretRequest { key, .. } => key,
584            DelegateRequest::RegisterDelegate { delegate, .. } => delegate.key(),
585            DelegateRequest::UnregisterDelegate(key) => key,
586        }
587    }
588}
589
590impl Display for ClientRequest<'_> {
591    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
592        match self {
593            ClientRequest::ContractOp(op) => match op {
594                ContractRequest::Put {
595                    contract, state, ..
596                } => {
597                    write!(
598                        f,
599                        "ContractRequest::Put for contract `{contract}` with state {state}"
600                    )
601                }
602                ContractRequest::Update { key, .. } => write!(f, "update request for {key}"),
603                ContractRequest::Get {
604                    key,
605                    return_contract_code: contract,
606                    ..
607                } => {
608                    write!(
609                        f,
610                        "ContractRequest::Get for key `{key}` (fetch full contract: {contract})"
611                    )
612                }
613                ContractRequest::Subscribe { key, .. } => {
614                    write!(f, "ContractRequest::Subscribe for `{key}`")
615                }
616            },
617            ClientRequest::DelegateOp(op) => {
618                match op {
619                    DelegateRequest::ApplicationMessages { key, inbound, .. } => {
620                        write!(
621                            f,
622                            "DelegateRequest::ApplicationMessages for `{key}` with {} messages",
623                            inbound.len()
624                        )
625                    }
626                    DelegateRequest::GetSecretRequest {
627                        get_request: GetSecretRequest { key: secret_id, .. },
628                        key,
629                        ..
630                    } => {
631                        write!(f, "DelegateRequest::GetSecretRequest secret_id `{secret_id}` for key `{key}`")
632                    }
633                    DelegateRequest::RegisterDelegate { delegate, .. } => {
634                        write!(
635                            f,
636                            "DelegateRequest::RegisterDelegate for delegate.key()=`{}`",
637                            delegate.key()
638                        )
639                    }
640                    DelegateRequest::UnregisterDelegate(key) => {
641                        write!(f, "DelegateRequest::UnregisterDelegate for key `{key}`")
642                    }
643                }
644            }
645            ClientRequest::Disconnect { .. } => write!(f, "client disconnected"),
646            ClientRequest::Authenticate { .. } => write!(f, "authenticate"),
647            ClientRequest::NodeQueries(query) => write!(f, "node queries: {:?}", query),
648            ClientRequest::Close => write!(f, "close"),
649        }
650    }
651}
652
653/// Deserializes a `DelegateRequest` from a Flatbuffers message.
654impl<'a> TryFromFbs<&FbsDelegateRequest<'a>> for DelegateRequest<'a> {
655    fn try_decode_fbs(request: &FbsDelegateRequest<'a>) -> Result<Self, WsApiError> {
656        let req = {
657            match request.delegate_request_type() {
658                DelegateRequestType::ApplicationMessages => {
659                    let app_msg = request.delegate_request_as_application_messages().unwrap();
660                    let key = DelegateKey::try_decode_fbs(&app_msg.key())?;
661                    let params = Parameters::from(app_msg.params().bytes());
662                    let inbound = app_msg
663                        .inbound()
664                        .iter()
665                        .map(|msg| InboundDelegateMsg::try_decode_fbs(&msg))
666                        .collect::<Result<Vec<_>, _>>()?;
667                    DelegateRequest::ApplicationMessages {
668                        key,
669                        params,
670                        inbound,
671                    }
672                }
673                DelegateRequestType::GetSecretRequestType => {
674                    let get_secret = request
675                        .delegate_request_as_get_secret_request_type()
676                        .unwrap();
677                    let key = DelegateKey::try_decode_fbs(&get_secret.key())?;
678                    let params = Parameters::from(get_secret.params().bytes().to_vec());
679                    let get_request = GetSecretRequest {
680                        key: SecretsId::try_decode_fbs(&get_secret.get_request().key())?,
681                        context: DelegateContext::new(
682                            get_secret.get_request().delegate_context().bytes().to_vec(),
683                        ),
684                        processed: get_secret.get_request().processed(),
685                    };
686                    DelegateRequest::GetSecretRequest {
687                        key,
688                        params,
689                        get_request,
690                    }
691                }
692                DelegateRequestType::RegisterDelegate => {
693                    let register = request.delegate_request_as_register_delegate().unwrap();
694                    let delegate = DelegateContainer::try_decode_fbs(&register.delegate())?;
695                    let cipher =
696                        <[u8; 32]>::try_from(register.cipher().bytes().to_vec().as_slice())
697                            .unwrap();
698                    let nonce =
699                        <[u8; 24]>::try_from(register.nonce().bytes().to_vec().as_slice()).unwrap();
700                    DelegateRequest::RegisterDelegate {
701                        delegate,
702                        cipher,
703                        nonce,
704                    }
705                }
706                DelegateRequestType::UnregisterDelegate => {
707                    let unregister = request.delegate_request_as_unregister_delegate().unwrap();
708                    let key = DelegateKey::try_decode_fbs(&unregister.key())?;
709                    DelegateRequest::UnregisterDelegate(key)
710                }
711                _ => unreachable!(),
712            }
713        };
714
715        Ok(req)
716    }
717}
718
719/// A response to a previous [`ClientRequest`]
720#[derive(Serialize, Deserialize, Debug, Clone)]
721#[non_exhaustive]
722pub enum HostResponse<T = WrappedState> {
723    ContractResponse(#[serde(bound(deserialize = "T: DeserializeOwned"))] ContractResponse<T>),
724    DelegateResponse {
725        key: DelegateKey,
726        values: Vec<OutboundDelegateMsg>,
727    },
728    QueryResponse(QueryResponse),
729    /// A requested action which doesn't require an answer was performed successfully.
730    Ok,
731}
732
733type Peer = String;
734
735#[derive(Serialize, Deserialize, Debug, Clone)]
736pub enum QueryResponse {
737    ConnectedPeers { peers: Vec<(Peer, SocketAddr)> },
738    NetworkDebug(NetworkDebugInfo),
739    NodeDiagnostics(NodeDiagnosticsResponse),
740    ProximityCache(ProximityCacheInfo),
741}
742
743#[derive(Serialize, Deserialize, Debug, Clone)]
744pub struct NetworkDebugInfo {
745    pub subscriptions: Vec<SubscriptionInfo>,
746    pub connected_peers: Vec<(String, SocketAddr)>,
747}
748
749#[derive(Serialize, Deserialize, Debug, Clone)]
750pub struct NodeDiagnosticsResponse {
751    /// Node information
752    pub node_info: Option<NodeInfo>,
753
754    /// Network connectivity information
755    pub network_info: Option<NetworkInfo>,
756
757    /// Contract subscription information
758    pub subscriptions: Vec<SubscriptionInfo>,
759
760    /// Contract states for specific contracts
761    pub contract_states: std::collections::HashMap<ContractKey, ContractState>,
762
763    /// System metrics
764    pub system_metrics: Option<SystemMetrics>,
765
766    /// Information about connected peers with detailed data
767    pub connected_peers_detailed: Vec<ConnectedPeerInfo>,
768}
769
770#[derive(Serialize, Deserialize, Debug, Clone)]
771pub struct NodeInfo {
772    pub peer_id: String,
773    pub is_gateway: bool,
774    pub location: Option<String>,
775    pub listening_address: Option<String>,
776    pub uptime_seconds: u64,
777}
778
779#[derive(Serialize, Deserialize, Debug, Clone)]
780pub struct NetworkInfo {
781    pub connected_peers: Vec<(String, String)>, // (peer_id, address)
782    pub active_connections: usize,
783}
784
785#[derive(Serialize, Deserialize, Debug, Clone)]
786pub struct ContractState {
787    /// Number of nodes subscribed to this contract
788    pub subscribers: u32,
789    /// Peer IDs of nodes that are subscribed to this contract
790    pub subscriber_peer_ids: Vec<String>,
791}
792
793#[derive(Serialize, Deserialize, Debug, Clone)]
794pub struct SystemMetrics {
795    pub active_connections: u32,
796    pub seeding_contracts: u32,
797}
798
799#[derive(Serialize, Deserialize, Debug, Clone)]
800pub struct SubscriptionInfo {
801    pub contract_key: ContractKey,
802    pub client_id: usize,
803}
804
805/// Basic information about a connected peer
806#[derive(Serialize, Deserialize, Debug, Clone)]
807pub struct ConnectedPeerInfo {
808    pub peer_id: String,
809    pub address: String,
810}
811
812#[derive(Serialize, Deserialize, Debug, Clone)]
813pub enum NodeQuery {
814    ConnectedPeers,
815    SubscriptionInfo,
816    NodeDiagnostics {
817        /// Diagnostic configuration specifying what information to collect
818        config: NodeDiagnosticsConfig,
819    },
820    /// Phase 3: Query proximity cache information for update propagation
821    ProximityCacheInfo,
822}
823
824/// Phase 3: Proximity cache information for update propagation
825#[derive(Serialize, Deserialize, Debug, Clone)]
826pub struct ProximityCacheInfo {
827    /// Contracts this node is currently caching
828    pub my_cache: Vec<ContractCacheEntry>,
829    /// What we know about neighbor caches
830    pub neighbor_caches: Vec<NeighborCacheInfo>,
831    /// Proximity propagation statistics
832    pub stats: ProximityStats,
833}
834
835#[derive(Serialize, Deserialize, Debug, Clone)]
836pub struct ContractCacheEntry {
837    /// Full contract key as string
838    pub contract_key: String,
839    /// 32-bit hash for proximity matching
840    pub cache_hash: u32,
841    /// When this contract was cached (Unix timestamp)
842    pub cached_since: u64,
843}
844
845#[derive(Serialize, Deserialize, Debug, Clone)]
846pub struct NeighborCacheInfo {
847    /// Peer identifier
848    pub peer_id: String,
849    /// Contract hashes this neighbor is known to cache
850    pub known_contracts: Vec<u32>,
851    /// Last update received from this neighbor (Unix timestamp)
852    pub last_update: u64,
853    /// Number of updates received from this neighbor
854    pub update_count: u64,
855}
856
857#[derive(Serialize, Deserialize, Debug, Clone)]
858pub struct ProximityStats {
859    /// Number of cache announcements sent
860    pub cache_announces_sent: u64,
861    /// Number of cache announcements received
862    pub cache_announces_received: u64,
863    /// Updates forwarded via proximity (not subscription)
864    pub updates_via_proximity: u64,
865    /// Updates forwarded via subscription
866    pub updates_via_subscription: u64,
867    /// False positives due to hash collisions
868    pub false_positive_forwards: u64,
869    /// Average number of contracts per neighbor
870    pub avg_neighbor_cache_size: f32,
871}
872
873#[derive(Serialize, Deserialize, Debug, Clone)]
874pub struct NodeDiagnosticsConfig {
875    /// Include basic node information (ID, location, uptime, etc.)
876    pub include_node_info: bool,
877
878    /// Include network connectivity information
879    pub include_network_info: bool,
880
881    /// Include contract subscription information
882    pub include_subscriptions: bool,
883
884    /// Include contract states for specific contracts (empty = all contracts)
885    pub contract_keys: Vec<ContractKey>,
886
887    /// Include memory and performance metrics
888    pub include_system_metrics: bool,
889
890    /// Include detailed information about connected peers (vs basic peer list)
891    pub include_detailed_peer_info: bool,
892
893    /// Include peer IDs of subscribers in contract state information
894    pub include_subscriber_peer_ids: bool,
895}
896
897impl NodeDiagnosticsConfig {
898    /// Create a comprehensive diagnostic config for debugging update propagation issues
899    pub fn for_update_propagation_debugging(contract_key: ContractKey) -> Self {
900        Self {
901            include_node_info: true,
902            include_network_info: true,
903            include_subscriptions: true,
904            contract_keys: vec![contract_key],
905            include_system_metrics: true,
906            include_detailed_peer_info: true,
907            include_subscriber_peer_ids: true,
908        }
909    }
910
911    /// Create a lightweight diagnostic config for basic node status
912    pub fn basic_status() -> Self {
913        Self {
914            include_node_info: true,
915            include_network_info: true,
916            include_subscriptions: false,
917            contract_keys: vec![],
918            include_system_metrics: false,
919            include_detailed_peer_info: false,
920            include_subscriber_peer_ids: false,
921        }
922    }
923
924    /// Create a full diagnostic config (all information)
925    pub fn full() -> Self {
926        Self {
927            include_node_info: true,
928            include_network_info: true,
929            include_subscriptions: true,
930            contract_keys: vec![], // empty = all contracts
931            include_system_metrics: true,
932            include_detailed_peer_info: true,
933            include_subscriber_peer_ids: true,
934        }
935    }
936}
937
938impl HostResponse {
939    pub fn unwrap_put(self) -> ContractKey {
940        if let Self::ContractResponse(ContractResponse::PutResponse { key }) = self {
941            key
942        } else {
943            panic!("called `HostResponse::unwrap_put()` on other than `PutResponse` value")
944        }
945    }
946
947    pub fn unwrap_get(self) -> (WrappedState, Option<ContractContainer>) {
948        if let Self::ContractResponse(ContractResponse::GetResponse {
949            contract, state, ..
950        }) = self
951        {
952            (state, contract)
953        } else {
954            panic!("called `HostResponse::unwrap_put()` on other than `PutResponse` value")
955        }
956    }
957
958    pub fn into_fbs_bytes(self) -> Result<Vec<u8>, Box<ClientError>> {
959        let mut builder = flatbuffers::FlatBufferBuilder::new();
960        match self {
961            HostResponse::ContractResponse(res) => match res {
962                ContractResponse::PutResponse { key } => {
963                    let instance_data = builder.create_vector(key.as_bytes());
964                    let instance_offset = FbsContractInstanceId::create(
965                        &mut builder,
966                        &ContractInstanceIdArgs {
967                            data: Some(instance_data),
968                        },
969                    );
970
971                    let code = Some(builder.create_vector(&key.code_hash().0));
972                    let key_offset = FbsContractKey::create(
973                        &mut builder,
974                        &ContractKeyArgs {
975                            instance: Some(instance_offset),
976                            code,
977                        },
978                    );
979
980                    let put_offset = FbsPutResponse::create(
981                        &mut builder,
982                        &PutResponseArgs {
983                            key: Some(key_offset),
984                        },
985                    );
986
987                    let contract_response_offset = FbsContractResponse::create(
988                        &mut builder,
989                        &ContractResponseArgs {
990                            contract_response: Some(put_offset.as_union_value()),
991                            contract_response_type: ContractResponseType::PutResponse,
992                        },
993                    );
994
995                    let response_offset = FbsHostResponse::create(
996                        &mut builder,
997                        &HostResponseArgs {
998                            response: Some(contract_response_offset.as_union_value()),
999                            response_type: HostResponseType::ContractResponse,
1000                        },
1001                    );
1002
1003                    finish_host_response_buffer(&mut builder, response_offset);
1004                    Ok(builder.finished_data().to_vec())
1005                }
1006                ContractResponse::UpdateResponse { key, summary } => {
1007                    let instance_data = builder.create_vector(key.as_bytes());
1008                    let instance_offset = FbsContractInstanceId::create(
1009                        &mut builder,
1010                        &ContractInstanceIdArgs {
1011                            data: Some(instance_data),
1012                        },
1013                    );
1014
1015                    let code = Some(builder.create_vector(&key.code_hash().0));
1016
1017                    let key_offset = FbsContractKey::create(
1018                        &mut builder,
1019                        &ContractKeyArgs {
1020                            instance: Some(instance_offset),
1021                            code,
1022                        },
1023                    );
1024
1025                    let summary_data = builder.create_vector(&summary.into_bytes());
1026
1027                    let update_response_offset = FbsUpdateResponse::create(
1028                        &mut builder,
1029                        &UpdateResponseArgs {
1030                            key: Some(key_offset),
1031                            summary: Some(summary_data),
1032                        },
1033                    );
1034
1035                    let contract_response_offset = FbsContractResponse::create(
1036                        &mut builder,
1037                        &ContractResponseArgs {
1038                            contract_response: Some(update_response_offset.as_union_value()),
1039                            contract_response_type: ContractResponseType::UpdateResponse,
1040                        },
1041                    );
1042
1043                    let response_offset = FbsHostResponse::create(
1044                        &mut builder,
1045                        &HostResponseArgs {
1046                            response: Some(contract_response_offset.as_union_value()),
1047                            response_type: HostResponseType::ContractResponse,
1048                        },
1049                    );
1050
1051                    finish_host_response_buffer(&mut builder, response_offset);
1052                    Ok(builder.finished_data().to_vec())
1053                }
1054                ContractResponse::GetResponse {
1055                    key,
1056                    contract: contract_container,
1057                    state,
1058                } => {
1059                    let instance_data = builder.create_vector(key.as_bytes());
1060                    let instance_offset = FbsContractInstanceId::create(
1061                        &mut builder,
1062                        &ContractInstanceIdArgs {
1063                            data: Some(instance_data),
1064                        },
1065                    );
1066
1067                    let code = Some(builder.create_vector(&key.code_hash().0));
1068                    let key_offset = FbsContractKey::create(
1069                        &mut builder,
1070                        &ContractKeyArgs {
1071                            instance: Some(instance_offset),
1072                            code,
1073                        },
1074                    );
1075
1076                    let container_offset = if let Some(contract) = contract_container {
1077                        let data = builder.create_vector(contract.key().as_bytes());
1078
1079                        let instance_offset = FbsContractInstanceId::create(
1080                            &mut builder,
1081                            &ContractInstanceIdArgs { data: Some(data) },
1082                        );
1083
1084                        let code = Some(builder.create_vector(&contract.key().code_hash().0));
1085                        let contract_key_offset = FbsContractKey::create(
1086                            &mut builder,
1087                            &ContractKeyArgs {
1088                                instance: Some(instance_offset),
1089                                code,
1090                            },
1091                        );
1092
1093                        let contract_data =
1094                            builder.create_vector(contract.clone().unwrap_v1().data.data());
1095                        let contract_code_hash =
1096                            builder.create_vector(&contract.clone().unwrap_v1().data.hash().0);
1097
1098                        let contract_code_offset = ContractCode::create(
1099                            &mut builder,
1100                            &ContractCodeArgs {
1101                                data: Some(contract_data),
1102                                code_hash: Some(contract_code_hash),
1103                            },
1104                        );
1105
1106                        let contract_params =
1107                            builder.create_vector(&contract.clone().params().into_bytes());
1108
1109                        let contract_offset = match contract {
1110                            Wasm(V1(..)) => WasmContractV1::create(
1111                                &mut builder,
1112                                &WasmContractV1Args {
1113                                    key: Some(contract_key_offset),
1114                                    data: Some(contract_code_offset),
1115                                    parameters: Some(contract_params),
1116                                },
1117                            ),
1118                        };
1119
1120                        Some(FbsContractContainer::create(
1121                            &mut builder,
1122                            &ContractContainerArgs {
1123                                contract_type: ContractType::WasmContractV1,
1124                                contract: Some(contract_offset.as_union_value()),
1125                            },
1126                        ))
1127                    } else {
1128                        None
1129                    };
1130
1131                    let state_data = builder.create_vector(&state);
1132
1133                    let get_offset = FbsGetResponse::create(
1134                        &mut builder,
1135                        &GetResponseArgs {
1136                            key: Some(key_offset),
1137                            contract: container_offset,
1138                            state: Some(state_data),
1139                        },
1140                    );
1141
1142                    let contract_response_offset = FbsContractResponse::create(
1143                        &mut builder,
1144                        &ContractResponseArgs {
1145                            contract_response_type: ContractResponseType::GetResponse,
1146                            contract_response: Some(get_offset.as_union_value()),
1147                        },
1148                    );
1149
1150                    let response_offset = FbsHostResponse::create(
1151                        &mut builder,
1152                        &HostResponseArgs {
1153                            response: Some(contract_response_offset.as_union_value()),
1154                            response_type: HostResponseType::ContractResponse,
1155                        },
1156                    );
1157
1158                    finish_host_response_buffer(&mut builder, response_offset);
1159                    Ok(builder.finished_data().to_vec())
1160                }
1161                ContractResponse::UpdateNotification { key, update } => {
1162                    let instance_data = builder.create_vector(key.as_bytes());
1163                    let instance_offset = FbsContractInstanceId::create(
1164                        &mut builder,
1165                        &ContractInstanceIdArgs {
1166                            data: Some(instance_data),
1167                        },
1168                    );
1169
1170                    let code = Some(builder.create_vector(&key.code_hash().0));
1171                    let key_offset = FbsContractKey::create(
1172                        &mut builder,
1173                        &ContractKeyArgs {
1174                            instance: Some(instance_offset),
1175                            code,
1176                        },
1177                    );
1178
1179                    let update_data = match update {
1180                        State(state) => {
1181                            let state_data = builder.create_vector(&state.into_bytes());
1182                            let state_update_offset = StateUpdate::create(
1183                                &mut builder,
1184                                &StateUpdateArgs {
1185                                    state: Some(state_data),
1186                                },
1187                            );
1188                            FbsUpdateData::create(
1189                                &mut builder,
1190                                &UpdateDataArgs {
1191                                    update_data_type: UpdateDataType::StateUpdate,
1192                                    update_data: Some(state_update_offset.as_union_value()),
1193                                },
1194                            )
1195                        }
1196                        Delta(delta) => {
1197                            let delta_data = builder.create_vector(&delta.into_bytes());
1198                            let update_offset = DeltaUpdate::create(
1199                                &mut builder,
1200                                &DeltaUpdateArgs {
1201                                    delta: Some(delta_data),
1202                                },
1203                            );
1204                            FbsUpdateData::create(
1205                                &mut builder,
1206                                &UpdateDataArgs {
1207                                    update_data_type: UpdateDataType::DeltaUpdate,
1208                                    update_data: Some(update_offset.as_union_value()),
1209                                },
1210                            )
1211                        }
1212                        StateAndDelta { state, delta } => {
1213                            let state_data = builder.create_vector(&state.into_bytes());
1214                            let delta_data = builder.create_vector(&delta.into_bytes());
1215
1216                            let update_offset = StateAndDeltaUpdate::create(
1217                                &mut builder,
1218                                &StateAndDeltaUpdateArgs {
1219                                    state: Some(state_data),
1220                                    delta: Some(delta_data),
1221                                },
1222                            );
1223
1224                            FbsUpdateData::create(
1225                                &mut builder,
1226                                &UpdateDataArgs {
1227                                    update_data_type: UpdateDataType::StateAndDeltaUpdate,
1228                                    update_data: Some(update_offset.as_union_value()),
1229                                },
1230                            )
1231                        }
1232                        RelatedState { related_to, state } => {
1233                            let state_data = builder.create_vector(&state.into_bytes());
1234                            let instance_data =
1235                                builder.create_vector(related_to.encode().as_bytes());
1236
1237                            let instance_offset = FbsContractInstanceId::create(
1238                                &mut builder,
1239                                &ContractInstanceIdArgs {
1240                                    data: Some(instance_data),
1241                                },
1242                            );
1243
1244                            let update_offset = RelatedStateUpdate::create(
1245                                &mut builder,
1246                                &RelatedStateUpdateArgs {
1247                                    related_to: Some(instance_offset),
1248                                    state: Some(state_data),
1249                                },
1250                            );
1251
1252                            FbsUpdateData::create(
1253                                &mut builder,
1254                                &UpdateDataArgs {
1255                                    update_data_type: UpdateDataType::RelatedStateUpdate,
1256                                    update_data: Some(update_offset.as_union_value()),
1257                                },
1258                            )
1259                        }
1260                        RelatedDelta { related_to, delta } => {
1261                            let instance_data =
1262                                builder.create_vector(related_to.encode().as_bytes());
1263                            let delta_data = builder.create_vector(&delta.into_bytes());
1264
1265                            let instance_offset = FbsContractInstanceId::create(
1266                                &mut builder,
1267                                &ContractInstanceIdArgs {
1268                                    data: Some(instance_data),
1269                                },
1270                            );
1271
1272                            let update_offset = RelatedDeltaUpdate::create(
1273                                &mut builder,
1274                                &RelatedDeltaUpdateArgs {
1275                                    related_to: Some(instance_offset),
1276                                    delta: Some(delta_data),
1277                                },
1278                            );
1279
1280                            FbsUpdateData::create(
1281                                &mut builder,
1282                                &UpdateDataArgs {
1283                                    update_data_type: UpdateDataType::RelatedDeltaUpdate,
1284                                    update_data: Some(update_offset.as_union_value()),
1285                                },
1286                            )
1287                        }
1288                        RelatedStateAndDelta {
1289                            related_to,
1290                            state,
1291                            delta,
1292                        } => {
1293                            let instance_data =
1294                                builder.create_vector(related_to.encode().as_bytes());
1295                            let state_data = builder.create_vector(&state.into_bytes());
1296                            let delta_data = builder.create_vector(&delta.into_bytes());
1297
1298                            let instance_offset = FbsContractInstanceId::create(
1299                                &mut builder,
1300                                &ContractInstanceIdArgs {
1301                                    data: Some(instance_data),
1302                                },
1303                            );
1304
1305                            let update_offset = RelatedStateAndDeltaUpdate::create(
1306                                &mut builder,
1307                                &RelatedStateAndDeltaUpdateArgs {
1308                                    related_to: Some(instance_offset),
1309                                    state: Some(state_data),
1310                                    delta: Some(delta_data),
1311                                },
1312                            );
1313
1314                            FbsUpdateData::create(
1315                                &mut builder,
1316                                &UpdateDataArgs {
1317                                    update_data_type: UpdateDataType::RelatedStateAndDeltaUpdate,
1318                                    update_data: Some(update_offset.as_union_value()),
1319                                },
1320                            )
1321                        }
1322                    };
1323
1324                    let update_notification_offset = FbsUpdateNotification::create(
1325                        &mut builder,
1326                        &UpdateNotificationArgs {
1327                            key: Some(key_offset),
1328                            update: Some(update_data),
1329                        },
1330                    );
1331
1332                    let put_response_offset = FbsContractResponse::create(
1333                        &mut builder,
1334                        &ContractResponseArgs {
1335                            contract_response_type: ContractResponseType::UpdateNotification,
1336                            contract_response: Some(update_notification_offset.as_union_value()),
1337                        },
1338                    );
1339
1340                    let host_response_offset = FbsHostResponse::create(
1341                        &mut builder,
1342                        &HostResponseArgs {
1343                            response_type: HostResponseType::ContractResponse,
1344                            response: Some(put_response_offset.as_union_value()),
1345                        },
1346                    );
1347
1348                    finish_host_response_buffer(&mut builder, host_response_offset);
1349                    Ok(builder.finished_data().to_vec())
1350                }
1351                ContractResponse::SubscribeResponse { .. } => todo!(),
1352            },
1353            HostResponse::DelegateResponse { key, values } => {
1354                let key_data = builder.create_vector(key.bytes());
1355                let code_hash_data = builder.create_vector(&key.code_hash().0);
1356                let key_offset = FbsDelegateKey::create(
1357                    &mut builder,
1358                    &DelegateKeyArgs {
1359                        key: Some(key_data),
1360                        code_hash: Some(code_hash_data),
1361                    },
1362                );
1363                let mut messages: Vec<WIPOffset<FbsOutboundDelegateMsg>> = Vec::new();
1364                values.iter().for_each(|msg| match msg {
1365                    OutboundDelegateMsg::ApplicationMessage(app) => {
1366                        let instance_data = builder.create_vector(key.bytes());
1367                        let instance_offset = FbsContractInstanceId::create(
1368                            &mut builder,
1369                            &ContractInstanceIdArgs {
1370                                data: Some(instance_data),
1371                            },
1372                        );
1373                        let payload_data = builder.create_vector(&app.payload);
1374                        let delegate_context_data = builder.create_vector(app.context.as_ref());
1375                        let app_offset = FbsApplicationMessage::create(
1376                            &mut builder,
1377                            &ApplicationMessageArgs {
1378                                app: Some(instance_offset),
1379                                payload: Some(payload_data),
1380                                context: Some(delegate_context_data),
1381                                processed: app.processed,
1382                            },
1383                        );
1384                        let msg = FbsOutboundDelegateMsg::create(
1385                            &mut builder,
1386                            &OutboundDelegateMsgArgs {
1387                                inbound_type: OutboundDelegateMsgType::common_ApplicationMessage,
1388                                inbound: Some(app_offset.as_union_value()),
1389                            },
1390                        );
1391                        messages.push(msg);
1392                    }
1393                    OutboundDelegateMsg::RequestUserInput(input) => {
1394                        let message_data = builder.create_vector(input.message.bytes());
1395                        let mut responses: Vec<WIPOffset<FbsClientResponse>> = Vec::new();
1396                        input.responses.iter().for_each(|resp| {
1397                            let response_data = builder.create_vector(resp.bytes());
1398                            let response = FbsClientResponse::create(
1399                                &mut builder,
1400                                &ClientResponseArgs {
1401                                    data: Some(response_data),
1402                                },
1403                            );
1404                            responses.push(response)
1405                        });
1406                        let responses_offset = builder.create_vector(&responses);
1407                        let input_offset = FbsRequestUserInput::create(
1408                            &mut builder,
1409                            &RequestUserInputArgs {
1410                                request_id: input.request_id,
1411                                message: Some(message_data),
1412                                responses: Some(responses_offset),
1413                            },
1414                        );
1415                        let msg = FbsOutboundDelegateMsg::create(
1416                            &mut builder,
1417                            &OutboundDelegateMsgArgs {
1418                                inbound_type: OutboundDelegateMsgType::RequestUserInput,
1419                                inbound: Some(input_offset.as_union_value()),
1420                            },
1421                        );
1422                        messages.push(msg);
1423                    }
1424                    OutboundDelegateMsg::ContextUpdated(context) => {
1425                        let context_data = builder.create_vector(context.as_ref());
1426                        let context_offset = FbsContextUpdated::create(
1427                            &mut builder,
1428                            &ContextUpdatedArgs {
1429                                context: Some(context_data),
1430                            },
1431                        );
1432                        let msg = FbsOutboundDelegateMsg::create(
1433                            &mut builder,
1434                            &OutboundDelegateMsgArgs {
1435                                inbound_type: OutboundDelegateMsgType::ContextUpdated,
1436                                inbound: Some(context_offset.as_union_value()),
1437                            },
1438                        );
1439                        messages.push(msg);
1440                    }
1441                    OutboundDelegateMsg::GetSecretRequest(request) => {
1442                        let secret_key_data = builder.create_vector(request.key.key());
1443                        let secret_hash_data = builder.create_vector(request.key.hash());
1444                        let secret_id_offset = FbsSecretsId::create(
1445                            &mut builder,
1446                            &SecretsIdArgs {
1447                                key: Some(secret_key_data),
1448                                hash: Some(secret_hash_data),
1449                            },
1450                        );
1451
1452                        let delegate_context_data = builder.create_vector(request.context.as_ref());
1453                        let request_offset = FbsGetSecretRequest::create(
1454                            &mut builder,
1455                            &GetSecretRequestArgs {
1456                                key: Some(secret_id_offset),
1457                                delegate_context: Some(delegate_context_data),
1458                                processed: request.processed,
1459                            },
1460                        );
1461                        let msg = FbsOutboundDelegateMsg::create(
1462                            &mut builder,
1463                            &OutboundDelegateMsgArgs {
1464                                inbound_type: OutboundDelegateMsgType::common_GetSecretRequest,
1465                                inbound: Some(request_offset.as_union_value()),
1466                            },
1467                        );
1468                        messages.push(msg);
1469                    }
1470                    OutboundDelegateMsg::SetSecretRequest(request) => {
1471                        let secret_key_data = builder.create_vector(request.key.key());
1472                        let secret_hash_data = builder.create_vector(request.key.hash());
1473                        let secret_id_offset = FbsSecretsId::create(
1474                            &mut builder,
1475                            &SecretsIdArgs {
1476                                key: Some(secret_key_data),
1477                                hash: Some(secret_hash_data),
1478                            },
1479                        );
1480
1481                        let value_data = request
1482                            .value
1483                            .clone()
1484                            .map(|value| builder.create_vector(value.as_slice()));
1485                        let request_offset = FbsSetSecretRequest::create(
1486                            &mut builder,
1487                            &SetSecretRequestArgs {
1488                                key: Some(secret_id_offset),
1489                                value: value_data,
1490                            },
1491                        );
1492                        let msg = FbsOutboundDelegateMsg::create(
1493                            &mut builder,
1494                            &OutboundDelegateMsgArgs {
1495                                inbound_type: OutboundDelegateMsgType::SetSecretRequest,
1496                                inbound: Some(request_offset.as_union_value()),
1497                            },
1498                        );
1499                        messages.push(msg);
1500                    }
1501                    OutboundDelegateMsg::GetSecretResponse(response) => {
1502                        let secret_key_data = builder.create_vector(response.key.key());
1503                        let secret_hash_data = builder.create_vector(response.key.hash());
1504                        let secret_id_offset = FbsSecretsId::create(
1505                            &mut builder,
1506                            &SecretsIdArgs {
1507                                key: Some(secret_key_data),
1508                                hash: Some(secret_hash_data),
1509                            },
1510                        );
1511
1512                        let value_data = response
1513                            .value
1514                            .clone()
1515                            .map(|value| builder.create_vector(value.as_slice()));
1516
1517                        let delegate_context_data =
1518                            builder.create_vector(response.context.as_ref());
1519                        let response_offset = FbsGetSecretResponse::create(
1520                            &mut builder,
1521                            &GetSecretResponseArgs {
1522                                key: Some(secret_id_offset),
1523                                value: value_data,
1524                                delegate_context: Some(delegate_context_data),
1525                            },
1526                        );
1527                        let msg = FbsOutboundDelegateMsg::create(
1528                            &mut builder,
1529                            &OutboundDelegateMsgArgs {
1530                                inbound_type: OutboundDelegateMsgType::common_GetSecretResponse,
1531                                inbound: Some(response_offset.as_union_value()),
1532                            },
1533                        );
1534                        messages.push(msg);
1535                    }
1536                });
1537                let messages_offset = builder.create_vector(&messages);
1538                let delegate_response_offset = FbsDelegateResponse::create(
1539                    &mut builder,
1540                    &DelegateResponseArgs {
1541                        key: Some(key_offset),
1542                        values: Some(messages_offset),
1543                    },
1544                );
1545                let host_response_offset = FbsHostResponse::create(
1546                    &mut builder,
1547                    &HostResponseArgs {
1548                        response_type: HostResponseType::DelegateResponse,
1549                        response: Some(delegate_response_offset.as_union_value()),
1550                    },
1551                );
1552                finish_host_response_buffer(&mut builder, host_response_offset);
1553                Ok(builder.finished_data().to_vec())
1554            }
1555            HostResponse::Ok => {
1556                let ok_offset = FbsOk::create(&mut builder, &OkArgs { msg: None });
1557                let host_response_offset = FbsHostResponse::create(
1558                    &mut builder,
1559                    &HostResponseArgs {
1560                        response_type: HostResponseType::Ok,
1561                        response: Some(ok_offset.as_union_value()),
1562                    },
1563                );
1564                finish_host_response_buffer(&mut builder, host_response_offset);
1565                Ok(builder.finished_data().to_vec())
1566            }
1567            HostResponse::QueryResponse(_) => unimplemented!(),
1568        }
1569    }
1570}
1571
1572impl Display for HostResponse {
1573    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1574        match self {
1575            HostResponse::ContractResponse(res) => match res {
1576                ContractResponse::PutResponse { key } => {
1577                    f.write_fmt(format_args!("put response for `{key}`"))
1578                }
1579                ContractResponse::UpdateResponse { key, .. } => {
1580                    f.write_fmt(format_args!("update response for `{key}`"))
1581                }
1582                ContractResponse::GetResponse { key, .. } => {
1583                    f.write_fmt(format_args!("get response for `{key}`"))
1584                }
1585                ContractResponse::UpdateNotification { key, .. } => {
1586                    f.write_fmt(format_args!("update notification for `{key}`"))
1587                }
1588                ContractResponse::SubscribeResponse { key, .. } => {
1589                    f.write_fmt(format_args!("subscribe response for `{key}`"))
1590                }
1591            },
1592            HostResponse::DelegateResponse { .. } => write!(f, "delegate responses"),
1593            HostResponse::Ok => write!(f, "ok response"),
1594            HostResponse::QueryResponse(_) => write!(f, "query response"),
1595        }
1596    }
1597}
1598
1599#[derive(Clone, Serialize, Deserialize, Debug)]
1600#[non_exhaustive]
1601pub enum ContractResponse<T = WrappedState> {
1602    GetResponse {
1603        key: ContractKey,
1604        contract: Option<ContractContainer>,
1605        #[serde(bound(deserialize = "T: DeserializeOwned"))]
1606        state: T,
1607    },
1608    PutResponse {
1609        key: ContractKey,
1610    },
1611    /// Message sent when there is an update to a subscribed contract.
1612    UpdateNotification {
1613        key: ContractKey,
1614        #[serde(deserialize_with = "UpdateData::deser_update_data")]
1615        update: UpdateData<'static>,
1616    },
1617    /// Successful update
1618    UpdateResponse {
1619        key: ContractKey,
1620        #[serde(deserialize_with = "StateSummary::deser_state_summary")]
1621        summary: StateSummary<'static>,
1622    },
1623    SubscribeResponse {
1624        key: ContractKey,
1625        subscribed: bool,
1626    },
1627}
1628
1629impl<T> From<ContractResponse<T>> for HostResponse<T> {
1630    fn from(value: ContractResponse<T>) -> HostResponse<T> {
1631        HostResponse::ContractResponse(value)
1632    }
1633}
1634
1635#[cfg(test)]
1636mod client_request_test {
1637    use crate::client_api::{ContractRequest, TryFromFbs};
1638    use crate::contract_interface::UpdateData;
1639    use crate::generated::client_request::root_as_client_request;
1640
1641    const EXPECTED_ENCODED_CONTRACT_ID: &str = "6kVs66bKaQAC6ohr8b43SvJ95r36tc2hnG7HezmaJHF9";
1642
1643    #[test]
1644    fn test_build_contract_put_op_from_fbs() -> Result<(), Box<dyn std::error::Error>> {
1645        let put_req_op = vec![
1646            4, 0, 0, 0, 244, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 1, 8, 0, 12, 0, 11, 0, 4, 0, 8,
1647            0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 1, 198, 255, 255, 255, 12, 0, 0, 0, 20, 0, 0, 0, 36, 0,
1648            0, 0, 170, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8,
1649            8, 0, 10, 0, 9, 0, 4, 0, 8, 0, 0, 0, 16, 0, 0, 0, 0, 1, 10, 0, 16, 0, 12, 0, 8, 0, 4,
1650            0, 10, 0, 0, 0, 12, 0, 0, 0, 76, 0, 0, 0, 92, 0, 0, 0, 176, 255, 255, 255, 8, 0, 0, 0,
1651            16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 8, 0, 4, 0, 6, 0, 0, 0, 4, 0, 0, 0, 32, 0, 0, 0,
1652            85, 111, 11, 171, 40, 85, 240, 177, 207, 81, 106, 157, 173, 90, 234, 2, 250, 253, 75,
1653            210, 62, 7, 6, 34, 75, 26, 229, 230, 107, 167, 17, 108, 8, 0, 0, 0, 1, 2, 3, 4, 5, 6,
1654            7, 8, 8, 0, 12, 0, 8, 0, 4, 0, 8, 0, 0, 0, 8, 0, 0, 0, 16, 0, 0, 0, 8, 0, 0, 0, 1, 2,
1655            3, 4, 5, 6, 7, 8, 8, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8,
1656        ];
1657        let request = if let Ok(client_request) = root_as_client_request(&put_req_op) {
1658            let contract_request = client_request.client_request_as_contract_request().unwrap();
1659            ContractRequest::try_decode_fbs(&contract_request)?
1660        } else {
1661            panic!("failed to decode client request")
1662        };
1663
1664        match request {
1665            ContractRequest::Put {
1666                contract,
1667                state,
1668                related_contracts: _,
1669                subscribe,
1670            } => {
1671                assert_eq!(
1672                    contract.to_string(),
1673                    "WasmContainer([api=0.0.1](D8fdVLbRyMLw5mZtPRpWMFcrXGN2z8Nq8UGcLGPFBg2W))"
1674                );
1675                assert_eq!(contract.unwrap_v1().data.data(), &[1, 2, 3, 4, 5, 6, 7, 8]);
1676                assert_eq!(state.to_vec(), &[1, 2, 3, 4, 5, 6, 7, 8]);
1677                assert!(!subscribe);
1678            }
1679            _ => panic!("wrong contract request type"),
1680        }
1681
1682        Ok(())
1683    }
1684
1685    #[test]
1686    fn test_build_contract_get_op_from_fbs() -> Result<(), Box<dyn std::error::Error>> {
1687        let get_req_op = vec![
1688            4, 0, 0, 0, 244, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 1, 8, 0, 12, 0, 11, 0, 4, 0, 8,
1689            0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 3, 222, 255, 255, 255, 12, 0, 0, 0, 8, 0, 12, 0, 8, 0, 4,
1690            0, 8, 0, 0, 0, 8, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 8, 0, 4, 0, 6, 0, 0, 0,
1691            4, 0, 0, 0, 32, 0, 0, 0, 85, 111, 11, 171, 40, 85, 240, 177, 207, 81, 106, 157, 173,
1692            90, 234, 2, 250, 253, 75, 210, 62, 7, 6, 34, 75, 26, 229, 230, 107, 167, 17, 108,
1693        ];
1694        let request = if let Ok(client_request) = root_as_client_request(&get_req_op) {
1695            let contract_request = client_request.client_request_as_contract_request().unwrap();
1696            ContractRequest::try_decode_fbs(&contract_request)?
1697        } else {
1698            panic!("failed to decode client request")
1699        };
1700
1701        match request {
1702            ContractRequest::Get {
1703                key,
1704                return_contract_code: fetch_contract,
1705                subscribe,
1706            } => {
1707                assert_eq!(key.encode(), EXPECTED_ENCODED_CONTRACT_ID);
1708                assert!(!fetch_contract);
1709                assert!(!subscribe);
1710            }
1711            _ => panic!("wrong contract request type"),
1712        }
1713
1714        Ok(())
1715    }
1716
1717    #[test]
1718    fn test_build_contract_update_op_from_fbs() -> Result<(), Box<dyn std::error::Error>> {
1719        let update_op = vec![
1720            4, 0, 0, 0, 220, 255, 255, 255, 8, 0, 0, 0, 0, 0, 0, 1, 232, 255, 255, 255, 8, 0, 0, 0,
1721            0, 0, 0, 2, 204, 255, 255, 255, 16, 0, 0, 0, 52, 0, 0, 0, 8, 0, 12, 0, 11, 0, 4, 0, 8,
1722            0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 2, 210, 255, 255, 255, 4, 0, 0, 0, 8, 0, 0, 0, 1, 2, 3,
1723            4, 5, 6, 7, 8, 8, 0, 12, 0, 8, 0, 4, 0, 8, 0, 0, 0, 8, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0,
1724            0, 0, 0, 6, 0, 8, 0, 4, 0, 6, 0, 0, 0, 4, 0, 0, 0, 32, 0, 0, 0, 85, 111, 11, 171, 40,
1725            85, 240, 177, 207, 81, 106, 157, 173, 90, 234, 2, 250, 253, 75, 210, 62, 7, 6, 34, 75,
1726            26, 229, 230, 107, 167, 17, 108,
1727        ];
1728        let request = if let Ok(client_request) = root_as_client_request(&update_op) {
1729            let contract_request = client_request.client_request_as_contract_request().unwrap();
1730            ContractRequest::try_decode_fbs(&contract_request)?
1731        } else {
1732            panic!("failed to decode client request")
1733        };
1734
1735        match request {
1736            ContractRequest::Update { key, data } => {
1737                assert_eq!(
1738                    key.encoded_contract_id(),
1739                    "6kVs66bKaQAC6ohr8b43SvJ95r36tc2hnG7HezmaJHF9"
1740                );
1741                match data {
1742                    UpdateData::Delta(delta) => {
1743                        assert_eq!(delta.to_vec(), &[1, 2, 3, 4, 5, 6, 7, 8])
1744                    }
1745                    _ => panic!("wrong update data type"),
1746                }
1747            }
1748            _ => panic!("wrong contract request type"),
1749        }
1750
1751        Ok(())
1752    }
1753}