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