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)]
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)]
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)]
726pub enum QueryResponse {
727    ConnectedPeers { peers: Vec<(Peer, SocketAddr)> },
728    NetworkDebug(NetworkDebugInfo),
729    NodeDiagnostics(NodeDiagnosticsResponse),
730}
731
732#[derive(Serialize, Deserialize, Debug, Clone)]
733pub struct NetworkDebugInfo {
734    pub subscriptions: Vec<SubscriptionInfo>,
735    pub connected_peers: Vec<(String, SocketAddr)>,
736}
737
738#[derive(Serialize, Deserialize, Debug, Clone)]
739pub struct NodeDiagnosticsResponse {
740    /// Node information
741    pub node_info: Option<NodeInfo>,
742
743    /// Network connectivity information
744    pub network_info: Option<NetworkInfo>,
745
746    /// Contract subscription information
747    pub subscriptions: Vec<SubscriptionInfo>,
748
749    /// Contract states for specific contracts
750    pub contract_states: std::collections::HashMap<ContractKey, ContractState>,
751
752    /// System metrics
753    pub system_metrics: Option<SystemMetrics>,
754
755    /// Information about connected peers with detailed data
756    pub connected_peers_detailed: Vec<ConnectedPeerInfo>,
757}
758
759#[derive(Serialize, Deserialize, Debug, Clone)]
760pub struct NodeInfo {
761    pub peer_id: String,
762    pub is_gateway: bool,
763    pub location: Option<String>,
764    pub listening_address: Option<String>,
765    pub uptime_seconds: u64,
766}
767
768#[derive(Serialize, Deserialize, Debug, Clone)]
769pub struct NetworkInfo {
770    pub connected_peers: Vec<(String, String)>, // (peer_id, address)
771    pub active_connections: usize,
772}
773
774#[derive(Serialize, Deserialize, Debug, Clone)]
775pub struct ContractState {
776    /// Number of nodes subscribed to this contract
777    pub subscribers: u32,
778    /// Peer IDs of nodes that are subscribed to this contract
779    pub subscriber_peer_ids: Vec<String>,
780}
781
782#[derive(Serialize, Deserialize, Debug, Clone)]
783pub struct SystemMetrics {
784    pub active_connections: u32,
785    pub seeding_contracts: u32,
786}
787
788#[derive(Serialize, Deserialize, Debug, Clone)]
789pub struct SubscriptionInfo {
790    pub contract_key: ContractKey,
791    pub client_id: usize,
792}
793
794/// Basic information about a connected peer
795#[derive(Serialize, Deserialize, Debug, Clone)]
796pub struct ConnectedPeerInfo {
797    pub peer_id: String,
798    pub address: String,
799}
800
801#[derive(Serialize, Deserialize, Debug, Clone)]
802pub enum NodeQuery {
803    ConnectedPeers,
804    SubscriptionInfo,
805    NodeDiagnostics {
806        /// Diagnostic configuration specifying what information to collect
807        config: NodeDiagnosticsConfig,
808    },
809}
810
811#[derive(Serialize, Deserialize, Debug, Clone)]
812pub struct NodeDiagnosticsConfig {
813    /// Include basic node information (ID, location, uptime, etc.)
814    pub include_node_info: bool,
815
816    /// Include network connectivity information
817    pub include_network_info: bool,
818
819    /// Include contract subscription information
820    pub include_subscriptions: bool,
821
822    /// Include contract states for specific contracts (empty = all contracts)
823    pub contract_keys: Vec<ContractKey>,
824
825    /// Include memory and performance metrics
826    pub include_system_metrics: bool,
827
828    /// Include detailed information about connected peers (vs basic peer list)
829    pub include_detailed_peer_info: bool,
830
831    /// Include peer IDs of subscribers in contract state information
832    pub include_subscriber_peer_ids: bool,
833}
834
835impl NodeDiagnosticsConfig {
836    /// Create a comprehensive diagnostic config for debugging update propagation issues
837    pub fn for_update_propagation_debugging(contract_key: ContractKey) -> Self {
838        Self {
839            include_node_info: true,
840            include_network_info: true,
841            include_subscriptions: true,
842            contract_keys: vec![contract_key],
843            include_system_metrics: true,
844            include_detailed_peer_info: true,
845            include_subscriber_peer_ids: true,
846        }
847    }
848
849    /// Create a lightweight diagnostic config for basic node status
850    pub fn basic_status() -> Self {
851        Self {
852            include_node_info: true,
853            include_network_info: true,
854            include_subscriptions: false,
855            contract_keys: vec![],
856            include_system_metrics: false,
857            include_detailed_peer_info: false,
858            include_subscriber_peer_ids: false,
859        }
860    }
861
862    /// Create a full diagnostic config (all information)
863    pub fn full() -> Self {
864        Self {
865            include_node_info: true,
866            include_network_info: true,
867            include_subscriptions: true,
868            contract_keys: vec![], // empty = all contracts
869            include_system_metrics: true,
870            include_detailed_peer_info: true,
871            include_subscriber_peer_ids: true,
872        }
873    }
874}
875
876impl HostResponse {
877    pub fn unwrap_put(self) -> ContractKey {
878        if let Self::ContractResponse(ContractResponse::PutResponse { key }) = self {
879            key
880        } else {
881            panic!("called `HostResponse::unwrap_put()` on other than `PutResponse` value")
882        }
883    }
884
885    pub fn unwrap_get(self) -> (WrappedState, Option<ContractContainer>) {
886        if let Self::ContractResponse(ContractResponse::GetResponse {
887            contract, state, ..
888        }) = self
889        {
890            (state, contract)
891        } else {
892            panic!("called `HostResponse::unwrap_put()` on other than `PutResponse` value")
893        }
894    }
895
896    pub fn into_fbs_bytes(self) -> Result<Vec<u8>, Box<ClientError>> {
897        let mut builder = flatbuffers::FlatBufferBuilder::new();
898        match self {
899            HostResponse::ContractResponse(res) => match res {
900                ContractResponse::PutResponse { key } => {
901                    let instance_data = builder.create_vector(key.as_bytes());
902                    let instance_offset = ContractInstanceId::create(
903                        &mut builder,
904                        &ContractInstanceIdArgs {
905                            data: Some(instance_data),
906                        },
907                    );
908
909                    let code = key
910                        .code_hash()
911                        .map(|code| builder.create_vector(code.0.as_ref()));
912                    let key_offset = FbsContractKey::create(
913                        &mut builder,
914                        &ContractKeyArgs {
915                            instance: Some(instance_offset),
916                            code,
917                        },
918                    );
919
920                    let put_offset = FbsPutResponse::create(
921                        &mut builder,
922                        &PutResponseArgs {
923                            key: Some(key_offset),
924                        },
925                    );
926
927                    let contract_response_offset = FbsContractResponse::create(
928                        &mut builder,
929                        &ContractResponseArgs {
930                            contract_response: Some(put_offset.as_union_value()),
931                            contract_response_type: ContractResponseType::PutResponse,
932                        },
933                    );
934
935                    let response_offset = FbsHostResponse::create(
936                        &mut builder,
937                        &HostResponseArgs {
938                            response: Some(contract_response_offset.as_union_value()),
939                            response_type: HostResponseType::ContractResponse,
940                        },
941                    );
942
943                    finish_host_response_buffer(&mut builder, response_offset);
944                    Ok(builder.finished_data().to_vec())
945                }
946                ContractResponse::UpdateResponse { key, summary } => {
947                    let instance_data = builder.create_vector(key.as_bytes());
948                    let instance_offset = ContractInstanceId::create(
949                        &mut builder,
950                        &ContractInstanceIdArgs {
951                            data: Some(instance_data),
952                        },
953                    );
954
955                    let code = key
956                        .code_hash()
957                        .map(|code| builder.create_vector(code.0.as_ref()));
958
959                    let key_offset = FbsContractKey::create(
960                        &mut builder,
961                        &ContractKeyArgs {
962                            instance: Some(instance_offset),
963                            code,
964                        },
965                    );
966
967                    let summary_data = builder.create_vector(&summary.into_bytes());
968
969                    let update_response_offset = FbsUpdateResponse::create(
970                        &mut builder,
971                        &UpdateResponseArgs {
972                            key: Some(key_offset),
973                            summary: Some(summary_data),
974                        },
975                    );
976
977                    let contract_response_offset = FbsContractResponse::create(
978                        &mut builder,
979                        &ContractResponseArgs {
980                            contract_response: Some(update_response_offset.as_union_value()),
981                            contract_response_type: ContractResponseType::UpdateResponse,
982                        },
983                    );
984
985                    let response_offset = FbsHostResponse::create(
986                        &mut builder,
987                        &HostResponseArgs {
988                            response: Some(contract_response_offset.as_union_value()),
989                            response_type: HostResponseType::ContractResponse,
990                        },
991                    );
992
993                    finish_host_response_buffer(&mut builder, response_offset);
994                    Ok(builder.finished_data().to_vec())
995                }
996                ContractResponse::GetResponse {
997                    key,
998                    contract: contract_container,
999                    state,
1000                } => {
1001                    let instance_data = builder.create_vector(key.as_bytes());
1002                    let instance_offset = ContractInstanceId::create(
1003                        &mut builder,
1004                        &ContractInstanceIdArgs {
1005                            data: Some(instance_data),
1006                        },
1007                    );
1008
1009                    let code = key.code_hash().map(|code| builder.create_vector(&code.0));
1010                    let key_offset = FbsContractKey::create(
1011                        &mut builder,
1012                        &ContractKeyArgs {
1013                            instance: Some(instance_offset),
1014                            code,
1015                        },
1016                    );
1017
1018                    let container_offset = if let Some(contract) = contract_container {
1019                        let data = builder.create_vector(contract.key().as_bytes());
1020
1021                        let instance_offset = ContractInstanceId::create(
1022                            &mut builder,
1023                            &ContractInstanceIdArgs { data: Some(data) },
1024                        );
1025
1026                        let code = contract
1027                            .key()
1028                            .code_hash()
1029                            .map(|code| builder.create_vector(&code.0));
1030                        let contract_key_offset = FbsContractKey::create(
1031                            &mut builder,
1032                            &ContractKeyArgs {
1033                                instance: Some(instance_offset),
1034                                code,
1035                            },
1036                        );
1037
1038                        let contract_data =
1039                            builder.create_vector(contract.clone().unwrap_v1().data.data());
1040                        let contract_code_hash =
1041                            builder.create_vector(&contract.clone().unwrap_v1().data.hash().0);
1042
1043                        let contract_code_offset = ContractCode::create(
1044                            &mut builder,
1045                            &ContractCodeArgs {
1046                                data: Some(contract_data),
1047                                code_hash: Some(contract_code_hash),
1048                            },
1049                        );
1050
1051                        let contract_params =
1052                            builder.create_vector(&contract.clone().params().into_bytes());
1053
1054                        let contract_offset = match contract {
1055                            Wasm(V1(..)) => WasmContractV1::create(
1056                                &mut builder,
1057                                &WasmContractV1Args {
1058                                    key: Some(contract_key_offset),
1059                                    data: Some(contract_code_offset),
1060                                    parameters: Some(contract_params),
1061                                },
1062                            ),
1063                        };
1064
1065                        Some(FbsContractContainer::create(
1066                            &mut builder,
1067                            &ContractContainerArgs {
1068                                contract_type: ContractType::WasmContractV1,
1069                                contract: Some(contract_offset.as_union_value()),
1070                            },
1071                        ))
1072                    } else {
1073                        None
1074                    };
1075
1076                    let state_data = builder.create_vector(&state);
1077
1078                    let get_offset = FbsGetResponse::create(
1079                        &mut builder,
1080                        &GetResponseArgs {
1081                            key: Some(key_offset),
1082                            contract: container_offset,
1083                            state: Some(state_data),
1084                        },
1085                    );
1086
1087                    let contract_response_offset = FbsContractResponse::create(
1088                        &mut builder,
1089                        &ContractResponseArgs {
1090                            contract_response_type: ContractResponseType::GetResponse,
1091                            contract_response: Some(get_offset.as_union_value()),
1092                        },
1093                    );
1094
1095                    let response_offset = FbsHostResponse::create(
1096                        &mut builder,
1097                        &HostResponseArgs {
1098                            response: Some(contract_response_offset.as_union_value()),
1099                            response_type: HostResponseType::ContractResponse,
1100                        },
1101                    );
1102
1103                    finish_host_response_buffer(&mut builder, response_offset);
1104                    Ok(builder.finished_data().to_vec())
1105                }
1106                ContractResponse::UpdateNotification { key, update } => {
1107                    let instance_data = builder.create_vector(key.as_bytes());
1108                    let instance_offset = ContractInstanceId::create(
1109                        &mut builder,
1110                        &ContractInstanceIdArgs {
1111                            data: Some(instance_data),
1112                        },
1113                    );
1114
1115                    let code = key
1116                        .code_hash()
1117                        .map(|code| builder.create_vector(code.0.as_ref()));
1118                    let key_offset = FbsContractKey::create(
1119                        &mut builder,
1120                        &ContractKeyArgs {
1121                            instance: Some(instance_offset),
1122                            code,
1123                        },
1124                    );
1125
1126                    let update_data = match update {
1127                        State(state) => {
1128                            let state_data = builder.create_vector(&state.into_bytes());
1129                            let state_update_offset = StateUpdate::create(
1130                                &mut builder,
1131                                &StateUpdateArgs {
1132                                    state: Some(state_data),
1133                                },
1134                            );
1135                            FbsUpdateData::create(
1136                                &mut builder,
1137                                &UpdateDataArgs {
1138                                    update_data_type: UpdateDataType::StateUpdate,
1139                                    update_data: Some(state_update_offset.as_union_value()),
1140                                },
1141                            )
1142                        }
1143                        Delta(delta) => {
1144                            let delta_data = builder.create_vector(&delta.into_bytes());
1145                            let update_offset = DeltaUpdate::create(
1146                                &mut builder,
1147                                &DeltaUpdateArgs {
1148                                    delta: Some(delta_data),
1149                                },
1150                            );
1151                            FbsUpdateData::create(
1152                                &mut builder,
1153                                &UpdateDataArgs {
1154                                    update_data_type: UpdateDataType::DeltaUpdate,
1155                                    update_data: Some(update_offset.as_union_value()),
1156                                },
1157                            )
1158                        }
1159                        StateAndDelta { state, delta } => {
1160                            let state_data = builder.create_vector(&state.into_bytes());
1161                            let delta_data = builder.create_vector(&delta.into_bytes());
1162
1163                            let update_offset = StateAndDeltaUpdate::create(
1164                                &mut builder,
1165                                &StateAndDeltaUpdateArgs {
1166                                    state: Some(state_data),
1167                                    delta: Some(delta_data),
1168                                },
1169                            );
1170
1171                            FbsUpdateData::create(
1172                                &mut builder,
1173                                &UpdateDataArgs {
1174                                    update_data_type: UpdateDataType::StateAndDeltaUpdate,
1175                                    update_data: Some(update_offset.as_union_value()),
1176                                },
1177                            )
1178                        }
1179                        RelatedState { related_to, state } => {
1180                            let state_data = builder.create_vector(&state.into_bytes());
1181                            let instance_data =
1182                                builder.create_vector(related_to.encode().as_bytes());
1183
1184                            let instance_offset = ContractInstanceId::create(
1185                                &mut builder,
1186                                &ContractInstanceIdArgs {
1187                                    data: Some(instance_data),
1188                                },
1189                            );
1190
1191                            let update_offset = RelatedStateUpdate::create(
1192                                &mut builder,
1193                                &RelatedStateUpdateArgs {
1194                                    related_to: Some(instance_offset),
1195                                    state: Some(state_data),
1196                                },
1197                            );
1198
1199                            FbsUpdateData::create(
1200                                &mut builder,
1201                                &UpdateDataArgs {
1202                                    update_data_type: UpdateDataType::RelatedStateUpdate,
1203                                    update_data: Some(update_offset.as_union_value()),
1204                                },
1205                            )
1206                        }
1207                        RelatedDelta { related_to, delta } => {
1208                            let instance_data =
1209                                builder.create_vector(related_to.encode().as_bytes());
1210                            let delta_data = builder.create_vector(&delta.into_bytes());
1211
1212                            let instance_offset = ContractInstanceId::create(
1213                                &mut builder,
1214                                &ContractInstanceIdArgs {
1215                                    data: Some(instance_data),
1216                                },
1217                            );
1218
1219                            let update_offset = RelatedDeltaUpdate::create(
1220                                &mut builder,
1221                                &RelatedDeltaUpdateArgs {
1222                                    related_to: Some(instance_offset),
1223                                    delta: Some(delta_data),
1224                                },
1225                            );
1226
1227                            FbsUpdateData::create(
1228                                &mut builder,
1229                                &UpdateDataArgs {
1230                                    update_data_type: UpdateDataType::RelatedDeltaUpdate,
1231                                    update_data: Some(update_offset.as_union_value()),
1232                                },
1233                            )
1234                        }
1235                        RelatedStateAndDelta {
1236                            related_to,
1237                            state,
1238                            delta,
1239                        } => {
1240                            let instance_data =
1241                                builder.create_vector(related_to.encode().as_bytes());
1242                            let state_data = builder.create_vector(&state.into_bytes());
1243                            let delta_data = builder.create_vector(&delta.into_bytes());
1244
1245                            let instance_offset = ContractInstanceId::create(
1246                                &mut builder,
1247                                &ContractInstanceIdArgs {
1248                                    data: Some(instance_data),
1249                                },
1250                            );
1251
1252                            let update_offset = RelatedStateAndDeltaUpdate::create(
1253                                &mut builder,
1254                                &RelatedStateAndDeltaUpdateArgs {
1255                                    related_to: Some(instance_offset),
1256                                    state: Some(state_data),
1257                                    delta: Some(delta_data),
1258                                },
1259                            );
1260
1261                            FbsUpdateData::create(
1262                                &mut builder,
1263                                &UpdateDataArgs {
1264                                    update_data_type: UpdateDataType::RelatedStateAndDeltaUpdate,
1265                                    update_data: Some(update_offset.as_union_value()),
1266                                },
1267                            )
1268                        }
1269                    };
1270
1271                    let update_notification_offset = FbsUpdateNotification::create(
1272                        &mut builder,
1273                        &UpdateNotificationArgs {
1274                            key: Some(key_offset),
1275                            update: Some(update_data),
1276                        },
1277                    );
1278
1279                    let put_response_offset = FbsContractResponse::create(
1280                        &mut builder,
1281                        &ContractResponseArgs {
1282                            contract_response_type: ContractResponseType::UpdateNotification,
1283                            contract_response: Some(update_notification_offset.as_union_value()),
1284                        },
1285                    );
1286
1287                    let host_response_offset = FbsHostResponse::create(
1288                        &mut builder,
1289                        &HostResponseArgs {
1290                            response_type: HostResponseType::ContractResponse,
1291                            response: Some(put_response_offset.as_union_value()),
1292                        },
1293                    );
1294
1295                    finish_host_response_buffer(&mut builder, host_response_offset);
1296                    Ok(builder.finished_data().to_vec())
1297                }
1298                ContractResponse::SubscribeResponse { .. } => todo!(),
1299            },
1300            HostResponse::DelegateResponse { key, values } => {
1301                let key_data = builder.create_vector(key.bytes());
1302                let code_hash_data = builder.create_vector(&key.code_hash().0);
1303                let key_offset = FbsDelegateKey::create(
1304                    &mut builder,
1305                    &DelegateKeyArgs {
1306                        key: Some(key_data),
1307                        code_hash: Some(code_hash_data),
1308                    },
1309                );
1310                let mut messages: Vec<WIPOffset<FbsOutboundDelegateMsg>> = Vec::new();
1311                values.iter().for_each(|msg| match msg {
1312                    OutboundDelegateMsg::ApplicationMessage(app) => {
1313                        let instance_data = builder.create_vector(key.bytes());
1314                        let instance_offset = ContractInstanceId::create(
1315                            &mut builder,
1316                            &ContractInstanceIdArgs {
1317                                data: Some(instance_data),
1318                            },
1319                        );
1320                        let payload_data = builder.create_vector(&app.payload);
1321                        let delegate_context_data = builder.create_vector(app.context.as_ref());
1322                        let app_offset = FbsApplicationMessage::create(
1323                            &mut builder,
1324                            &ApplicationMessageArgs {
1325                                app: Some(instance_offset),
1326                                payload: Some(payload_data),
1327                                context: Some(delegate_context_data),
1328                                processed: app.processed,
1329                            },
1330                        );
1331                        let msg = FbsOutboundDelegateMsg::create(
1332                            &mut builder,
1333                            &OutboundDelegateMsgArgs {
1334                                inbound_type: OutboundDelegateMsgType::common_ApplicationMessage,
1335                                inbound: Some(app_offset.as_union_value()),
1336                            },
1337                        );
1338                        messages.push(msg);
1339                    }
1340                    OutboundDelegateMsg::RequestUserInput(input) => {
1341                        let message_data = builder.create_vector(input.message.bytes());
1342                        let mut responses: Vec<WIPOffset<FbsClientResponse>> = Vec::new();
1343                        input.responses.iter().for_each(|resp| {
1344                            let response_data = builder.create_vector(resp.bytes());
1345                            let response = FbsClientResponse::create(
1346                                &mut builder,
1347                                &ClientResponseArgs {
1348                                    data: Some(response_data),
1349                                },
1350                            );
1351                            responses.push(response)
1352                        });
1353                        let responses_offset = builder.create_vector(&responses);
1354                        let input_offset = FbsRequestUserInput::create(
1355                            &mut builder,
1356                            &RequestUserInputArgs {
1357                                request_id: input.request_id,
1358                                message: Some(message_data),
1359                                responses: Some(responses_offset),
1360                            },
1361                        );
1362                        let msg = FbsOutboundDelegateMsg::create(
1363                            &mut builder,
1364                            &OutboundDelegateMsgArgs {
1365                                inbound_type: OutboundDelegateMsgType::RequestUserInput,
1366                                inbound: Some(input_offset.as_union_value()),
1367                            },
1368                        );
1369                        messages.push(msg);
1370                    }
1371                    OutboundDelegateMsg::ContextUpdated(context) => {
1372                        let context_data = builder.create_vector(context.as_ref());
1373                        let context_offset = FbsContextUpdated::create(
1374                            &mut builder,
1375                            &ContextUpdatedArgs {
1376                                context: Some(context_data),
1377                            },
1378                        );
1379                        let msg = FbsOutboundDelegateMsg::create(
1380                            &mut builder,
1381                            &OutboundDelegateMsgArgs {
1382                                inbound_type: OutboundDelegateMsgType::ContextUpdated,
1383                                inbound: Some(context_offset.as_union_value()),
1384                            },
1385                        );
1386                        messages.push(msg);
1387                    }
1388                    OutboundDelegateMsg::GetSecretRequest(request) => {
1389                        let secret_key_data = builder.create_vector(request.key.key());
1390                        let secret_hash_data = builder.create_vector(request.key.hash());
1391                        let secret_id_offset = FbsSecretsId::create(
1392                            &mut builder,
1393                            &SecretsIdArgs {
1394                                key: Some(secret_key_data),
1395                                hash: Some(secret_hash_data),
1396                            },
1397                        );
1398
1399                        let delegate_context_data = builder.create_vector(request.context.as_ref());
1400                        let request_offset = FbsGetSecretRequest::create(
1401                            &mut builder,
1402                            &GetSecretRequestArgs {
1403                                key: Some(secret_id_offset),
1404                                delegate_context: Some(delegate_context_data),
1405                                processed: request.processed,
1406                            },
1407                        );
1408                        let msg = FbsOutboundDelegateMsg::create(
1409                            &mut builder,
1410                            &OutboundDelegateMsgArgs {
1411                                inbound_type: OutboundDelegateMsgType::common_GetSecretRequest,
1412                                inbound: Some(request_offset.as_union_value()),
1413                            },
1414                        );
1415                        messages.push(msg);
1416                    }
1417                    OutboundDelegateMsg::SetSecretRequest(request) => {
1418                        let secret_key_data = builder.create_vector(request.key.key());
1419                        let secret_hash_data = builder.create_vector(request.key.hash());
1420                        let secret_id_offset = FbsSecretsId::create(
1421                            &mut builder,
1422                            &SecretsIdArgs {
1423                                key: Some(secret_key_data),
1424                                hash: Some(secret_hash_data),
1425                            },
1426                        );
1427
1428                        let value_data = request
1429                            .value
1430                            .clone()
1431                            .map(|value| builder.create_vector(value.as_slice()));
1432                        let request_offset = FbsSetSecretRequest::create(
1433                            &mut builder,
1434                            &SetSecretRequestArgs {
1435                                key: Some(secret_id_offset),
1436                                value: value_data,
1437                            },
1438                        );
1439                        let msg = FbsOutboundDelegateMsg::create(
1440                            &mut builder,
1441                            &OutboundDelegateMsgArgs {
1442                                inbound_type: OutboundDelegateMsgType::SetSecretRequest,
1443                                inbound: Some(request_offset.as_union_value()),
1444                            },
1445                        );
1446                        messages.push(msg);
1447                    }
1448                    OutboundDelegateMsg::GetSecretResponse(response) => {
1449                        let secret_key_data = builder.create_vector(response.key.key());
1450                        let secret_hash_data = builder.create_vector(response.key.hash());
1451                        let secret_id_offset = FbsSecretsId::create(
1452                            &mut builder,
1453                            &SecretsIdArgs {
1454                                key: Some(secret_key_data),
1455                                hash: Some(secret_hash_data),
1456                            },
1457                        );
1458
1459                        let value_data = response
1460                            .value
1461                            .clone()
1462                            .map(|value| builder.create_vector(value.as_slice()));
1463
1464                        let delegate_context_data =
1465                            builder.create_vector(response.context.as_ref());
1466                        let response_offset = FbsGetSecretResponse::create(
1467                            &mut builder,
1468                            &GetSecretResponseArgs {
1469                                key: Some(secret_id_offset),
1470                                value: value_data,
1471                                delegate_context: Some(delegate_context_data),
1472                            },
1473                        );
1474                        let msg = FbsOutboundDelegateMsg::create(
1475                            &mut builder,
1476                            &OutboundDelegateMsgArgs {
1477                                inbound_type: OutboundDelegateMsgType::common_GetSecretResponse,
1478                                inbound: Some(response_offset.as_union_value()),
1479                            },
1480                        );
1481                        messages.push(msg);
1482                    }
1483                });
1484                let messages_offset = builder.create_vector(&messages);
1485                let delegate_response_offset = FbsDelegateResponse::create(
1486                    &mut builder,
1487                    &DelegateResponseArgs {
1488                        key: Some(key_offset),
1489                        values: Some(messages_offset),
1490                    },
1491                );
1492                let host_response_offset = FbsHostResponse::create(
1493                    &mut builder,
1494                    &HostResponseArgs {
1495                        response_type: HostResponseType::DelegateResponse,
1496                        response: Some(delegate_response_offset.as_union_value()),
1497                    },
1498                );
1499                finish_host_response_buffer(&mut builder, host_response_offset);
1500                Ok(builder.finished_data().to_vec())
1501            }
1502            HostResponse::Ok => {
1503                let ok_offset = FbsOk::create(&mut builder, &OkArgs { msg: None });
1504                let host_response_offset = FbsHostResponse::create(
1505                    &mut builder,
1506                    &HostResponseArgs {
1507                        response_type: HostResponseType::Ok,
1508                        response: Some(ok_offset.as_union_value()),
1509                    },
1510                );
1511                finish_host_response_buffer(&mut builder, host_response_offset);
1512                Ok(builder.finished_data().to_vec())
1513            }
1514            HostResponse::QueryResponse(_) => unimplemented!(),
1515        }
1516    }
1517}
1518
1519impl Display for HostResponse {
1520    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1521        match self {
1522            HostResponse::ContractResponse(res) => match res {
1523                ContractResponse::PutResponse { key } => {
1524                    f.write_fmt(format_args!("put response for `{key}`"))
1525                }
1526                ContractResponse::UpdateResponse { key, .. } => {
1527                    f.write_fmt(format_args!("update response for `{key}`"))
1528                }
1529                ContractResponse::GetResponse { key, .. } => {
1530                    f.write_fmt(format_args!("get response for `{key}`"))
1531                }
1532                ContractResponse::UpdateNotification { key, .. } => {
1533                    f.write_fmt(format_args!("update notification for `{key}`"))
1534                }
1535                ContractResponse::SubscribeResponse { key, .. } => {
1536                    f.write_fmt(format_args!("subscribe response for `{key}`"))
1537                }
1538            },
1539            HostResponse::DelegateResponse { .. } => write!(f, "delegate responses"),
1540            HostResponse::Ok => write!(f, "ok response"),
1541            HostResponse::QueryResponse(_) => write!(f, "query response"),
1542        }
1543    }
1544}
1545
1546#[derive(Clone, Serialize, Deserialize, Debug)]
1547#[non_exhaustive]
1548pub enum ContractResponse<T = WrappedState> {
1549    GetResponse {
1550        key: ContractKey,
1551        contract: Option<ContractContainer>,
1552        #[serde(bound(deserialize = "T: DeserializeOwned"))]
1553        state: T,
1554    },
1555    PutResponse {
1556        key: ContractKey,
1557    },
1558    /// Message sent when there is an update to a subscribed contract.
1559    UpdateNotification {
1560        key: ContractKey,
1561        #[serde(deserialize_with = "UpdateData::deser_update_data")]
1562        update: UpdateData<'static>,
1563    },
1564    /// Successful update
1565    UpdateResponse {
1566        key: ContractKey,
1567        #[serde(deserialize_with = "StateSummary::deser_state_summary")]
1568        summary: StateSummary<'static>,
1569    },
1570    SubscribeResponse {
1571        key: ContractKey,
1572        subscribed: bool,
1573    },
1574}
1575
1576impl<T> From<ContractResponse<T>> for HostResponse<T> {
1577    fn from(value: ContractResponse<T>) -> HostResponse<T> {
1578        HostResponse::ContractResponse(value)
1579    }
1580}
1581
1582#[cfg(test)]
1583mod client_request_test {
1584    use crate::client_api::{ContractRequest, TryFromFbs};
1585    use crate::contract_interface::UpdateData;
1586    use crate::generated::client_request::root_as_client_request;
1587
1588    const EXPECTED_ENCODED_CONTRACT_ID: &str = "6kVs66bKaQAC6ohr8b43SvJ95r36tc2hnG7HezmaJHF9";
1589
1590    #[test]
1591    fn test_build_contract_put_op_from_fbs() -> Result<(), Box<dyn std::error::Error>> {
1592        let put_req_op = vec![
1593            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,
1594            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,
1595            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,
1596            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,
1597            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,
1598            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,
1599            85, 111, 11, 171, 40, 85, 240, 177, 207, 81, 106, 157, 173, 90, 234, 2, 250, 253, 75,
1600            210, 62, 7, 6, 34, 75, 26, 229, 230, 107, 167, 17, 108, 8, 0, 0, 0, 1, 2, 3, 4, 5, 6,
1601            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,
1602            3, 4, 5, 6, 7, 8, 8, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8,
1603        ];
1604        let request = if let Ok(client_request) = root_as_client_request(&put_req_op) {
1605            let contract_request = client_request.client_request_as_contract_request().unwrap();
1606            ContractRequest::try_decode_fbs(&contract_request)?
1607        } else {
1608            panic!("failed to decode client request")
1609        };
1610
1611        match request {
1612            ContractRequest::Put {
1613                contract,
1614                state,
1615                related_contracts: _,
1616                subscribe,
1617            } => {
1618                assert_eq!(
1619                    contract.to_string(),
1620                    "WasmContainer([api=0.0.1](D8fdVLbRyMLw5mZtPRpWMFcrXGN2z8Nq8UGcLGPFBg2W))"
1621                );
1622                assert_eq!(contract.unwrap_v1().data.data(), &[1, 2, 3, 4, 5, 6, 7, 8]);
1623                assert_eq!(state.to_vec(), &[1, 2, 3, 4, 5, 6, 7, 8]);
1624                assert!(!subscribe);
1625            }
1626            _ => panic!("wrong contract request type"),
1627        }
1628
1629        Ok(())
1630    }
1631
1632    #[test]
1633    fn test_build_contract_get_op_from_fbs() -> Result<(), Box<dyn std::error::Error>> {
1634        let get_req_op = vec![
1635            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,
1636            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,
1637            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,
1638            4, 0, 0, 0, 32, 0, 0, 0, 85, 111, 11, 171, 40, 85, 240, 177, 207, 81, 106, 157, 173,
1639            90, 234, 2, 250, 253, 75, 210, 62, 7, 6, 34, 75, 26, 229, 230, 107, 167, 17, 108,
1640        ];
1641        let request = if let Ok(client_request) = root_as_client_request(&get_req_op) {
1642            let contract_request = client_request.client_request_as_contract_request().unwrap();
1643            ContractRequest::try_decode_fbs(&contract_request)?
1644        } else {
1645            panic!("failed to decode client request")
1646        };
1647
1648        match request {
1649            ContractRequest::Get {
1650                key,
1651                return_contract_code: fetch_contract,
1652                subscribe,
1653            } => {
1654                assert_eq!(key.encoded_contract_id(), EXPECTED_ENCODED_CONTRACT_ID);
1655                assert!(!fetch_contract);
1656                assert!(!subscribe);
1657            }
1658            _ => panic!("wrong contract request type"),
1659        }
1660
1661        Ok(())
1662    }
1663
1664    #[test]
1665    fn test_build_contract_update_op_from_fbs() -> Result<(), Box<dyn std::error::Error>> {
1666        let update_op = vec![
1667            4, 0, 0, 0, 220, 255, 255, 255, 8, 0, 0, 0, 0, 0, 0, 1, 232, 255, 255, 255, 8, 0, 0, 0,
1668            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,
1669            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,
1670            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,
1671            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,
1672            85, 240, 177, 207, 81, 106, 157, 173, 90, 234, 2, 250, 253, 75, 210, 62, 7, 6, 34, 75,
1673            26, 229, 230, 107, 167, 17, 108,
1674        ];
1675        let request = if let Ok(client_request) = root_as_client_request(&update_op) {
1676            let contract_request = client_request.client_request_as_contract_request().unwrap();
1677            ContractRequest::try_decode_fbs(&contract_request)?
1678        } else {
1679            panic!("failed to decode client request")
1680        };
1681
1682        match request {
1683            ContractRequest::Update { key, data } => {
1684                assert_eq!(
1685                    key.encoded_contract_id(),
1686                    "6kVs66bKaQAC6ohr8b43SvJ95r36tc2hnG7HezmaJHF9"
1687                );
1688                match data {
1689                    UpdateData::Delta(delta) => {
1690                        assert_eq!(delta.to_vec(), &[1, 2, 3, 4, 5, 6, 7, 8])
1691                    }
1692                    _ => panic!("wrong update data type"),
1693                }
1694            }
1695            _ => panic!("wrong contract request type"),
1696        }
1697
1698        Ok(())
1699    }
1700}