freenet_stdlib/client_api/
client_events.rs

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