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