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