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