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