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}
133
134impl Display for ClientError {
135 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 write!(f, "client error: {}", self.kind)
137 }
138}
139
140impl std::error::Error for ClientError {}
141
142#[derive(Debug, thiserror::Error, Serialize, Deserialize, Clone)]
143#[non_exhaustive]
144pub enum RequestError {
145 #[error(transparent)]
146 ContractError(#[from] ContractError),
147 #[error(transparent)]
148 DelegateError(#[from] DelegateError),
149 #[error("client disconnect")]
150 Disconnect,
151 #[error("operation timed out")]
152 Timeout,
153}
154
155#[derive(Debug, thiserror::Error, Serialize, Deserialize, Clone)]
157#[non_exhaustive]
158pub enum DelegateError {
159 #[error("error while registering delegate {0}")]
160 RegisterError(DelegateKey),
161 #[error("execution error, cause {0}")]
162 ExecutionError(Cow<'static, str>),
163 #[error("missing delegate {0}")]
164 Missing(DelegateKey),
165 #[error("missing secret `{secret}` for delegate {key}")]
166 MissingSecret { key: DelegateKey, secret: SecretsId },
167 #[error("forbidden access to secret: {0}")]
168 ForbiddenSecretAccess(SecretsId),
169}
170
171#[derive(Debug, thiserror::Error, Serialize, Deserialize, Clone)]
173#[non_exhaustive]
174pub enum ContractError {
175 #[error("failed to get contract {key}, reason: {cause}")]
176 Get {
177 key: ContractKey,
178 cause: Cow<'static, str>,
179 },
180 #[error("put error for contract {key}, reason: {cause}")]
181 Put {
182 key: ContractKey,
183 cause: Cow<'static, str>,
184 },
185 #[error("update error for contract {key}, reason: {cause}")]
186 Update {
187 key: ContractKey,
188 cause: Cow<'static, str>,
189 },
190 #[error("failed to subscribe for contract {key}, reason: {cause}")]
191 Subscribe {
192 key: ContractKey,
193 cause: Cow<'static, str>,
194 },
195 #[error("dependency contract stack overflow : {key}")]
197 ContractStackOverflow {
198 key: crate::contract_interface::ContractInstanceId,
199 },
200 #[error("missing related contract: {key}")]
201 MissingRelated {
202 key: crate::contract_interface::ContractInstanceId,
203 },
204 #[error("missing contract: {key}")]
205 MissingContract {
206 key: crate::contract_interface::ContractInstanceId,
207 },
208}
209
210impl ContractError {
211 const EXECUTION_ERROR: &'static str = "execution error";
212 const INVALID_PUT: &'static str = "invalid put";
213
214 pub fn update_exec_error(key: ContractKey, additional_info: impl std::fmt::Display) -> Self {
215 Self::Update {
216 key,
217 cause: format!(
218 "{exec_err}: {additional_info}",
219 exec_err = Self::EXECUTION_ERROR
220 )
221 .into(),
222 }
223 }
224
225 pub fn invalid_put(key: ContractKey) -> Self {
226 Self::Put {
227 key,
228 cause: Self::INVALID_PUT.into(),
229 }
230 }
231
232 pub fn invalid_update(key: ContractKey) -> Self {
233 Self::Update {
234 key,
235 cause: Self::INVALID_PUT.into(),
236 }
237 }
238}
239
240#[derive(Serialize, Deserialize, Debug, Clone)]
242#[non_exhaustive]
243pub enum ClientRequest<'a> {
245 DelegateOp(#[serde(borrow)] DelegateRequest<'a>),
246 ContractOp(#[serde(borrow)] ContractRequest<'a>),
247 Disconnect {
248 cause: Option<Cow<'static, str>>,
249 },
250 Authenticate {
251 token: String,
252 },
253 NodeQueries(NodeQuery),
254 Close,
256}
257
258#[derive(Serialize, Deserialize, Debug, Clone)]
259pub struct ConnectedPeers {}
260
261#[derive(Serialize, Deserialize, Debug, Clone)]
262pub struct NodeDiagnostics {
263 pub contract_key: Option<ContractKey>,
265}
266
267impl ClientRequest<'_> {
268 pub fn into_owned(self) -> ClientRequest<'static> {
269 match self {
270 ClientRequest::ContractOp(op) => {
271 let owned = match op {
272 ContractRequest::Put {
273 contract,
274 state,
275 related_contracts,
276 subscribe,
277 blocking_subscribe,
278 } => {
279 let related_contracts = related_contracts.into_owned();
280 ContractRequest::Put {
281 contract,
282 state,
283 related_contracts,
284 subscribe,
285 blocking_subscribe,
286 }
287 }
288 ContractRequest::Update { key, data } => {
289 let data = data.into_owned();
290 ContractRequest::Update { key, data }
291 }
292 ContractRequest::Get {
293 key,
294 return_contract_code,
295 subscribe,
296 blocking_subscribe,
297 } => ContractRequest::Get {
298 key,
299 return_contract_code,
300 subscribe,
301 blocking_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 Put {
372 contract: ContractContainer,
373 state: WrappedState,
375 #[serde(borrow)]
377 related_contracts: RelatedContracts<'a>,
378 subscribe: bool,
380 #[serde(default)]
383 blocking_subscribe: bool,
384 },
385 Update {
387 key: ContractKey,
388 #[serde(borrow)]
389 data: UpdateData<'a>,
390 },
391 Get {
393 key: ContractInstanceId,
396 return_contract_code: bool,
398 subscribe: bool,
400 #[serde(default)]
403 blocking_subscribe: bool,
404 },
405 Subscribe {
408 key: ContractInstanceId,
410 summary: Option<StateSummary<'a>>,
411 },
412}
413
414impl ContractRequest<'_> {
415 pub fn into_owned(self) -> ContractRequest<'static> {
416 match self {
417 Self::Put {
418 contract,
419 state,
420 related_contracts,
421 subscribe,
422 blocking_subscribe,
423 } => ContractRequest::Put {
424 contract,
425 state,
426 related_contracts: related_contracts.into_owned(),
427 subscribe,
428 blocking_subscribe,
429 },
430 Self::Update { key, data } => ContractRequest::Update {
431 key,
432 data: data.into_owned(),
433 },
434 Self::Get {
435 key,
436 return_contract_code: fetch_contract,
437 subscribe,
438 blocking_subscribe,
439 } => ContractRequest::Get {
440 key,
441 return_contract_code: fetch_contract,
442 subscribe,
443 blocking_subscribe,
444 },
445 Self::Subscribe { key, summary } => ContractRequest::Subscribe {
446 key,
447 summary: summary.map(StateSummary::into_owned),
448 },
449 }
450 }
451}
452
453impl<'a> From<ContractRequest<'a>> for ClientRequest<'a> {
454 fn from(op: ContractRequest<'a>) -> Self {
455 ClientRequest::ContractOp(op)
456 }
457}
458
459impl<'a> TryFromFbs<&FbsContractRequest<'a>> for ContractRequest<'a> {
461 fn try_decode_fbs(request: &FbsContractRequest<'a>) -> Result<Self, WsApiError> {
462 let req = {
463 match request.contract_request_type() {
464 ContractRequestType::Get => {
465 let get = request.contract_request_as_get().unwrap();
466 let fbs_key = get.key();
469 let key_bytes: [u8; 32] = fbs_key.instance().data().bytes().try_into().unwrap();
470 let key = ContractInstanceId::new(key_bytes);
471 let fetch_contract = get.fetch_contract();
472 let subscribe = get.subscribe();
473 let blocking_subscribe = get.blocking_subscribe();
474 ContractRequest::Get {
475 key,
476 return_contract_code: fetch_contract,
477 subscribe,
478 blocking_subscribe,
479 }
480 }
481 ContractRequestType::Put => {
482 let put = request.contract_request_as_put().unwrap();
483 let contract = ContractContainer::try_decode_fbs(&put.container())?;
484 let state = WrappedState::new(put.wrapped_state().bytes().to_vec());
485 let related_contracts =
486 RelatedContracts::try_decode_fbs(&put.related_contracts())?.into_owned();
487 let subscribe = put.subscribe();
488 let blocking_subscribe = put.blocking_subscribe();
489 ContractRequest::Put {
490 contract,
491 state,
492 related_contracts,
493 subscribe,
494 blocking_subscribe,
495 }
496 }
497 ContractRequestType::Update => {
498 let update = request.contract_request_as_update().unwrap();
499 let key = ContractKey::try_decode_fbs(&update.key())?;
500 let data = UpdateData::try_decode_fbs(&update.data())?.into_owned();
501 ContractRequest::Update { key, data }
502 }
503 ContractRequestType::Subscribe => {
504 let subscribe = request.contract_request_as_subscribe().unwrap();
505 let fbs_key = subscribe.key();
507 let key_bytes: [u8; 32] = fbs_key.instance().data().bytes().try_into().unwrap();
508 let key = ContractInstanceId::new(key_bytes);
509 let summary = subscribe
510 .summary()
511 .map(|summary_data| StateSummary::from(summary_data.bytes()));
512 ContractRequest::Subscribe { key, summary }
513 }
514 _ => unreachable!(),
515 }
516 };
517
518 Ok(req)
519 }
520}
521
522impl<'a> From<DelegateRequest<'a>> for ClientRequest<'a> {
523 fn from(op: DelegateRequest<'a>) -> Self {
524 ClientRequest::DelegateOp(op)
525 }
526}
527
528#[derive(Serialize, Deserialize, Debug, Clone)]
529#[non_exhaustive]
530pub enum DelegateRequest<'a> {
531 ApplicationMessages {
532 key: DelegateKey,
533 #[serde(deserialize_with = "Parameters::deser_params")]
534 params: Parameters<'a>,
535 #[serde(borrow)]
536 inbound: Vec<InboundDelegateMsg<'a>>,
537 },
538 RegisterDelegate {
539 delegate: DelegateContainer,
540 cipher: [u8; 32],
541 nonce: [u8; 24],
542 },
543 UnregisterDelegate(DelegateKey),
544}
545
546impl DelegateRequest<'_> {
547 pub const DEFAULT_CIPHER: [u8; 32] = [
548 0, 24, 22, 150, 112, 207, 24, 65, 182, 161, 169, 227, 66, 182, 237, 215, 206, 164, 58, 161,
549 64, 108, 157, 195, 0, 0, 0, 0, 0, 0, 0, 0,
550 ];
551
552 pub const DEFAULT_NONCE: [u8; 24] = [
553 57, 18, 79, 116, 63, 134, 93, 39, 208, 161, 156, 229, 222, 247, 111, 79, 210, 126, 127, 55,
554 224, 150, 139, 80,
555 ];
556
557 pub fn into_owned(self) -> DelegateRequest<'static> {
558 match self {
559 DelegateRequest::ApplicationMessages {
560 key,
561 inbound,
562 params,
563 } => DelegateRequest::ApplicationMessages {
564 key,
565 params: params.into_owned(),
566 inbound: inbound.into_iter().map(|e| e.into_owned()).collect(),
567 },
568 DelegateRequest::RegisterDelegate {
569 delegate,
570 cipher,
571 nonce,
572 } => DelegateRequest::RegisterDelegate {
573 delegate,
574 cipher,
575 nonce,
576 },
577 DelegateRequest::UnregisterDelegate(key) => DelegateRequest::UnregisterDelegate(key),
578 }
579 }
580
581 pub fn key(&self) -> &DelegateKey {
582 match self {
583 DelegateRequest::ApplicationMessages { 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) => match op {
618 DelegateRequest::ApplicationMessages { key, inbound, .. } => {
619 write!(
620 f,
621 "DelegateRequest::ApplicationMessages for `{key}` with {} messages",
622 inbound.len()
623 )
624 }
625 DelegateRequest::RegisterDelegate { delegate, .. } => {
626 write!(
627 f,
628 "DelegateRequest::RegisterDelegate for delegate.key()=`{}`",
629 delegate.key()
630 )
631 }
632 DelegateRequest::UnregisterDelegate(key) => {
633 write!(f, "DelegateRequest::UnregisterDelegate for key `{key}`")
634 }
635 },
636 ClientRequest::Disconnect { .. } => write!(f, "client disconnected"),
637 ClientRequest::Authenticate { .. } => write!(f, "authenticate"),
638 ClientRequest::NodeQueries(query) => write!(f, "node queries: {:?}", query),
639 ClientRequest::Close => write!(f, "close"),
640 }
641 }
642}
643
644impl<'a> TryFromFbs<&FbsDelegateRequest<'a>> for DelegateRequest<'a> {
646 fn try_decode_fbs(request: &FbsDelegateRequest<'a>) -> Result<Self, WsApiError> {
647 let req = {
648 match request.delegate_request_type() {
649 DelegateRequestType::ApplicationMessages => {
650 let app_msg = request.delegate_request_as_application_messages().unwrap();
651 let key = DelegateKey::try_decode_fbs(&app_msg.key())?;
652 let params = Parameters::from(app_msg.params().bytes());
653 let inbound = app_msg
654 .inbound()
655 .iter()
656 .map(|msg| InboundDelegateMsg::try_decode_fbs(&msg))
657 .collect::<Result<Vec<_>, _>>()?;
658 DelegateRequest::ApplicationMessages {
659 key,
660 params,
661 inbound,
662 }
663 }
664 DelegateRequestType::RegisterDelegate => {
665 let register = request.delegate_request_as_register_delegate().unwrap();
666 let delegate = DelegateContainer::try_decode_fbs(®ister.delegate())?;
667 let cipher =
668 <[u8; 32]>::try_from(register.cipher().bytes().to_vec().as_slice())
669 .unwrap();
670 let nonce =
671 <[u8; 24]>::try_from(register.nonce().bytes().to_vec().as_slice()).unwrap();
672 DelegateRequest::RegisterDelegate {
673 delegate,
674 cipher,
675 nonce,
676 }
677 }
678 DelegateRequestType::UnregisterDelegate => {
679 let unregister = request.delegate_request_as_unregister_delegate().unwrap();
680 let key = DelegateKey::try_decode_fbs(&unregister.key())?;
681 DelegateRequest::UnregisterDelegate(key)
682 }
683 _ => unreachable!(),
684 }
685 };
686
687 Ok(req)
688 }
689}
690
691#[derive(Serialize, Deserialize, Debug, Clone)]
693#[non_exhaustive]
694pub enum HostResponse<T = WrappedState> {
695 ContractResponse(#[serde(bound(deserialize = "T: DeserializeOwned"))] ContractResponse<T>),
696 DelegateResponse {
697 key: DelegateKey,
698 values: Vec<OutboundDelegateMsg>,
699 },
700 QueryResponse(QueryResponse),
701 Ok,
703}
704
705type Peer = String;
706
707#[derive(Serialize, Deserialize, Debug, Clone)]
708pub enum QueryResponse {
709 ConnectedPeers { peers: Vec<(Peer, SocketAddr)> },
710 NetworkDebug(NetworkDebugInfo),
711 NodeDiagnostics(NodeDiagnosticsResponse),
712 ProximityCache(ProximityCacheInfo),
713}
714
715#[derive(Serialize, Deserialize, Debug, Clone)]
716pub struct NetworkDebugInfo {
717 pub subscriptions: Vec<SubscriptionInfo>,
718 pub connected_peers: Vec<(String, SocketAddr)>,
719}
720
721#[derive(Serialize, Deserialize, Debug, Clone)]
722pub struct NodeDiagnosticsResponse {
723 pub node_info: Option<NodeInfo>,
725
726 pub network_info: Option<NetworkInfo>,
728
729 pub subscriptions: Vec<SubscriptionInfo>,
731
732 pub contract_states: std::collections::HashMap<ContractKey, ContractState>,
734
735 pub system_metrics: Option<SystemMetrics>,
737
738 pub connected_peers_detailed: Vec<ConnectedPeerInfo>,
740}
741
742#[derive(Serialize, Deserialize, Debug, Clone)]
743pub struct NodeInfo {
744 pub peer_id: String,
745 pub is_gateway: bool,
746 pub location: Option<String>,
747 pub listening_address: Option<String>,
748 pub uptime_seconds: u64,
749}
750
751#[derive(Serialize, Deserialize, Debug, Clone)]
752pub struct NetworkInfo {
753 pub connected_peers: Vec<(String, String)>, pub active_connections: usize,
755}
756
757#[derive(Serialize, Deserialize, Debug, Clone)]
758pub struct ContractState {
759 pub subscribers: u32,
761 pub subscriber_peer_ids: Vec<String>,
763}
764
765#[derive(Serialize, Deserialize, Debug, Clone)]
766pub struct SystemMetrics {
767 pub active_connections: u32,
768 pub seeding_contracts: u32,
769}
770
771#[derive(Serialize, Deserialize, Debug, Clone)]
772pub struct SubscriptionInfo {
773 pub contract_key: ContractInstanceId,
774 pub client_id: usize,
775}
776
777#[derive(Serialize, Deserialize, Debug, Clone)]
779pub struct ConnectedPeerInfo {
780 pub peer_id: String,
781 pub address: String,
782}
783
784#[derive(Serialize, Deserialize, Debug, Clone)]
785pub enum NodeQuery {
786 ConnectedPeers,
787 SubscriptionInfo,
788 NodeDiagnostics {
789 config: NodeDiagnosticsConfig,
791 },
792 ProximityCacheInfo,
794}
795
796#[derive(Serialize, Deserialize, Debug, Clone)]
798pub struct ProximityCacheInfo {
799 pub my_cache: Vec<ContractCacheEntry>,
801 pub neighbor_caches: Vec<NeighborCacheInfo>,
803 pub stats: ProximityStats,
805}
806
807#[derive(Serialize, Deserialize, Debug, Clone)]
808pub struct ContractCacheEntry {
809 pub contract_key: String,
811 pub cache_hash: u32,
813 pub cached_since: u64,
815}
816
817#[derive(Serialize, Deserialize, Debug, Clone)]
818pub struct NeighborCacheInfo {
819 pub peer_id: String,
821 pub known_contracts: Vec<u32>,
823 pub last_update: u64,
825 pub update_count: u64,
827}
828
829#[derive(Serialize, Deserialize, Debug, Clone)]
830pub struct ProximityStats {
831 pub cache_announces_sent: u64,
833 pub cache_announces_received: u64,
835 pub updates_via_proximity: u64,
837 pub updates_via_subscription: u64,
839 pub false_positive_forwards: u64,
841 pub avg_neighbor_cache_size: f32,
843}
844
845#[derive(Serialize, Deserialize, Debug, Clone)]
846pub struct NodeDiagnosticsConfig {
847 pub include_node_info: bool,
849
850 pub include_network_info: bool,
852
853 pub include_subscriptions: bool,
855
856 pub contract_keys: Vec<ContractKey>,
858
859 pub include_system_metrics: bool,
861
862 pub include_detailed_peer_info: bool,
864
865 pub include_subscriber_peer_ids: bool,
867}
868
869impl NodeDiagnosticsConfig {
870 pub fn for_update_propagation_debugging(contract_key: ContractKey) -> Self {
872 Self {
873 include_node_info: true,
874 include_network_info: true,
875 include_subscriptions: true,
876 contract_keys: vec![contract_key],
877 include_system_metrics: true,
878 include_detailed_peer_info: true,
879 include_subscriber_peer_ids: true,
880 }
881 }
882
883 pub fn basic_status() -> Self {
885 Self {
886 include_node_info: true,
887 include_network_info: true,
888 include_subscriptions: false,
889 contract_keys: vec![],
890 include_system_metrics: false,
891 include_detailed_peer_info: false,
892 include_subscriber_peer_ids: false,
893 }
894 }
895
896 pub fn full() -> Self {
898 Self {
899 include_node_info: true,
900 include_network_info: true,
901 include_subscriptions: true,
902 contract_keys: vec![], include_system_metrics: true,
904 include_detailed_peer_info: true,
905 include_subscriber_peer_ids: true,
906 }
907 }
908}
909
910impl HostResponse {
911 pub fn unwrap_put(self) -> ContractKey {
912 if let Self::ContractResponse(ContractResponse::PutResponse { key }) = self {
913 key
914 } else {
915 panic!("called `HostResponse::unwrap_put()` on other than `PutResponse` value")
916 }
917 }
918
919 pub fn unwrap_get(self) -> (WrappedState, Option<ContractContainer>) {
920 if let Self::ContractResponse(ContractResponse::GetResponse {
921 contract, state, ..
922 }) = self
923 {
924 (state, contract)
925 } else {
926 panic!("called `HostResponse::unwrap_put()` on other than `PutResponse` value")
927 }
928 }
929
930 pub fn into_fbs_bytes(self) -> Result<Vec<u8>, Box<ClientError>> {
931 let mut builder = flatbuffers::FlatBufferBuilder::new();
932 match self {
933 HostResponse::ContractResponse(res) => match res {
934 ContractResponse::PutResponse { key } => {
935 let instance_data = builder.create_vector(key.as_bytes());
936 let instance_offset = FbsContractInstanceId::create(
937 &mut builder,
938 &ContractInstanceIdArgs {
939 data: Some(instance_data),
940 },
941 );
942
943 let code = Some(builder.create_vector(&key.code_hash().0));
944 let key_offset = FbsContractKey::create(
945 &mut builder,
946 &ContractKeyArgs {
947 instance: Some(instance_offset),
948 code,
949 },
950 );
951
952 let put_offset = FbsPutResponse::create(
953 &mut builder,
954 &PutResponseArgs {
955 key: Some(key_offset),
956 },
957 );
958
959 let contract_response_offset = FbsContractResponse::create(
960 &mut builder,
961 &ContractResponseArgs {
962 contract_response: Some(put_offset.as_union_value()),
963 contract_response_type: ContractResponseType::PutResponse,
964 },
965 );
966
967 let response_offset = FbsHostResponse::create(
968 &mut builder,
969 &HostResponseArgs {
970 response: Some(contract_response_offset.as_union_value()),
971 response_type: HostResponseType::ContractResponse,
972 },
973 );
974
975 finish_host_response_buffer(&mut builder, response_offset);
976 Ok(builder.finished_data().to_vec())
977 }
978 ContractResponse::UpdateResponse { key, summary } => {
979 let instance_data = builder.create_vector(key.as_bytes());
980 let instance_offset = FbsContractInstanceId::create(
981 &mut builder,
982 &ContractInstanceIdArgs {
983 data: Some(instance_data),
984 },
985 );
986
987 let code = Some(builder.create_vector(&key.code_hash().0));
988
989 let key_offset = FbsContractKey::create(
990 &mut builder,
991 &ContractKeyArgs {
992 instance: Some(instance_offset),
993 code,
994 },
995 );
996
997 let summary_data = builder.create_vector(&summary.into_bytes());
998
999 let update_response_offset = FbsUpdateResponse::create(
1000 &mut builder,
1001 &UpdateResponseArgs {
1002 key: Some(key_offset),
1003 summary: Some(summary_data),
1004 },
1005 );
1006
1007 let contract_response_offset = FbsContractResponse::create(
1008 &mut builder,
1009 &ContractResponseArgs {
1010 contract_response: Some(update_response_offset.as_union_value()),
1011 contract_response_type: ContractResponseType::UpdateResponse,
1012 },
1013 );
1014
1015 let response_offset = FbsHostResponse::create(
1016 &mut builder,
1017 &HostResponseArgs {
1018 response: Some(contract_response_offset.as_union_value()),
1019 response_type: HostResponseType::ContractResponse,
1020 },
1021 );
1022
1023 finish_host_response_buffer(&mut builder, response_offset);
1024 Ok(builder.finished_data().to_vec())
1025 }
1026 ContractResponse::GetResponse {
1027 key,
1028 contract: contract_container,
1029 state,
1030 } => {
1031 let instance_data = builder.create_vector(key.as_bytes());
1032 let instance_offset = FbsContractInstanceId::create(
1033 &mut builder,
1034 &ContractInstanceIdArgs {
1035 data: Some(instance_data),
1036 },
1037 );
1038
1039 let code = Some(builder.create_vector(&key.code_hash().0));
1040 let key_offset = FbsContractKey::create(
1041 &mut builder,
1042 &ContractKeyArgs {
1043 instance: Some(instance_offset),
1044 code,
1045 },
1046 );
1047
1048 let container_offset = if let Some(contract) = contract_container {
1049 let data = builder.create_vector(contract.key().as_bytes());
1050
1051 let instance_offset = FbsContractInstanceId::create(
1052 &mut builder,
1053 &ContractInstanceIdArgs { data: Some(data) },
1054 );
1055
1056 let code = Some(builder.create_vector(&contract.key().code_hash().0));
1057 let contract_key_offset = FbsContractKey::create(
1058 &mut builder,
1059 &ContractKeyArgs {
1060 instance: Some(instance_offset),
1061 code,
1062 },
1063 );
1064
1065 let contract_data =
1066 builder.create_vector(contract.clone().unwrap_v1().data.data());
1067 let contract_code_hash =
1068 builder.create_vector(&contract.clone().unwrap_v1().data.hash().0);
1069
1070 let contract_code_offset = ContractCode::create(
1071 &mut builder,
1072 &ContractCodeArgs {
1073 data: Some(contract_data),
1074 code_hash: Some(contract_code_hash),
1075 },
1076 );
1077
1078 let contract_params =
1079 builder.create_vector(&contract.clone().params().into_bytes());
1080
1081 let contract_offset = match contract {
1082 Wasm(V1(..)) => WasmContractV1::create(
1083 &mut builder,
1084 &WasmContractV1Args {
1085 key: Some(contract_key_offset),
1086 data: Some(contract_code_offset),
1087 parameters: Some(contract_params),
1088 },
1089 ),
1090 };
1091
1092 Some(FbsContractContainer::create(
1093 &mut builder,
1094 &ContractContainerArgs {
1095 contract_type: ContractType::WasmContractV1,
1096 contract: Some(contract_offset.as_union_value()),
1097 },
1098 ))
1099 } else {
1100 None
1101 };
1102
1103 let state_data = builder.create_vector(&state);
1104
1105 let get_offset = FbsGetResponse::create(
1106 &mut builder,
1107 &GetResponseArgs {
1108 key: Some(key_offset),
1109 contract: container_offset,
1110 state: Some(state_data),
1111 },
1112 );
1113
1114 let contract_response_offset = FbsContractResponse::create(
1115 &mut builder,
1116 &ContractResponseArgs {
1117 contract_response_type: ContractResponseType::GetResponse,
1118 contract_response: Some(get_offset.as_union_value()),
1119 },
1120 );
1121
1122 let response_offset = FbsHostResponse::create(
1123 &mut builder,
1124 &HostResponseArgs {
1125 response: Some(contract_response_offset.as_union_value()),
1126 response_type: HostResponseType::ContractResponse,
1127 },
1128 );
1129
1130 finish_host_response_buffer(&mut builder, response_offset);
1131 Ok(builder.finished_data().to_vec())
1132 }
1133 ContractResponse::UpdateNotification { key, update } => {
1134 let instance_data = builder.create_vector(key.as_bytes());
1135 let instance_offset = FbsContractInstanceId::create(
1136 &mut builder,
1137 &ContractInstanceIdArgs {
1138 data: Some(instance_data),
1139 },
1140 );
1141
1142 let code = Some(builder.create_vector(&key.code_hash().0));
1143 let key_offset = FbsContractKey::create(
1144 &mut builder,
1145 &ContractKeyArgs {
1146 instance: Some(instance_offset),
1147 code,
1148 },
1149 );
1150
1151 let update_data = match update {
1152 State(state) => {
1153 let state_data = builder.create_vector(&state.into_bytes());
1154 let state_update_offset = StateUpdate::create(
1155 &mut builder,
1156 &StateUpdateArgs {
1157 state: Some(state_data),
1158 },
1159 );
1160 FbsUpdateData::create(
1161 &mut builder,
1162 &UpdateDataArgs {
1163 update_data_type: UpdateDataType::StateUpdate,
1164 update_data: Some(state_update_offset.as_union_value()),
1165 },
1166 )
1167 }
1168 Delta(delta) => {
1169 let delta_data = builder.create_vector(&delta.into_bytes());
1170 let update_offset = DeltaUpdate::create(
1171 &mut builder,
1172 &DeltaUpdateArgs {
1173 delta: Some(delta_data),
1174 },
1175 );
1176 FbsUpdateData::create(
1177 &mut builder,
1178 &UpdateDataArgs {
1179 update_data_type: UpdateDataType::DeltaUpdate,
1180 update_data: Some(update_offset.as_union_value()),
1181 },
1182 )
1183 }
1184 StateAndDelta { state, delta } => {
1185 let state_data = builder.create_vector(&state.into_bytes());
1186 let delta_data = builder.create_vector(&delta.into_bytes());
1187
1188 let update_offset = StateAndDeltaUpdate::create(
1189 &mut builder,
1190 &StateAndDeltaUpdateArgs {
1191 state: Some(state_data),
1192 delta: Some(delta_data),
1193 },
1194 );
1195
1196 FbsUpdateData::create(
1197 &mut builder,
1198 &UpdateDataArgs {
1199 update_data_type: UpdateDataType::StateAndDeltaUpdate,
1200 update_data: Some(update_offset.as_union_value()),
1201 },
1202 )
1203 }
1204 RelatedState { related_to, state } => {
1205 let state_data = builder.create_vector(&state.into_bytes());
1206 let instance_data =
1207 builder.create_vector(related_to.encode().as_bytes());
1208
1209 let instance_offset = FbsContractInstanceId::create(
1210 &mut builder,
1211 &ContractInstanceIdArgs {
1212 data: Some(instance_data),
1213 },
1214 );
1215
1216 let update_offset = RelatedStateUpdate::create(
1217 &mut builder,
1218 &RelatedStateUpdateArgs {
1219 related_to: Some(instance_offset),
1220 state: Some(state_data),
1221 },
1222 );
1223
1224 FbsUpdateData::create(
1225 &mut builder,
1226 &UpdateDataArgs {
1227 update_data_type: UpdateDataType::RelatedStateUpdate,
1228 update_data: Some(update_offset.as_union_value()),
1229 },
1230 )
1231 }
1232 RelatedDelta { related_to, delta } => {
1233 let instance_data =
1234 builder.create_vector(related_to.encode().as_bytes());
1235 let delta_data = builder.create_vector(&delta.into_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 = RelatedDeltaUpdate::create(
1245 &mut builder,
1246 &RelatedDeltaUpdateArgs {
1247 related_to: Some(instance_offset),
1248 delta: Some(delta_data),
1249 },
1250 );
1251
1252 FbsUpdateData::create(
1253 &mut builder,
1254 &UpdateDataArgs {
1255 update_data_type: UpdateDataType::RelatedDeltaUpdate,
1256 update_data: Some(update_offset.as_union_value()),
1257 },
1258 )
1259 }
1260 RelatedStateAndDelta {
1261 related_to,
1262 state,
1263 delta,
1264 } => {
1265 let instance_data =
1266 builder.create_vector(related_to.encode().as_bytes());
1267 let state_data = builder.create_vector(&state.into_bytes());
1268 let delta_data = builder.create_vector(&delta.into_bytes());
1269
1270 let instance_offset = FbsContractInstanceId::create(
1271 &mut builder,
1272 &ContractInstanceIdArgs {
1273 data: Some(instance_data),
1274 },
1275 );
1276
1277 let update_offset = RelatedStateAndDeltaUpdate::create(
1278 &mut builder,
1279 &RelatedStateAndDeltaUpdateArgs {
1280 related_to: Some(instance_offset),
1281 state: Some(state_data),
1282 delta: Some(delta_data),
1283 },
1284 );
1285
1286 FbsUpdateData::create(
1287 &mut builder,
1288 &UpdateDataArgs {
1289 update_data_type: UpdateDataType::RelatedStateAndDeltaUpdate,
1290 update_data: Some(update_offset.as_union_value()),
1291 },
1292 )
1293 }
1294 };
1295
1296 let update_notification_offset = FbsUpdateNotification::create(
1297 &mut builder,
1298 &UpdateNotificationArgs {
1299 key: Some(key_offset),
1300 update: Some(update_data),
1301 },
1302 );
1303
1304 let put_response_offset = FbsContractResponse::create(
1305 &mut builder,
1306 &ContractResponseArgs {
1307 contract_response_type: ContractResponseType::UpdateNotification,
1308 contract_response: Some(update_notification_offset.as_union_value()),
1309 },
1310 );
1311
1312 let host_response_offset = FbsHostResponse::create(
1313 &mut builder,
1314 &HostResponseArgs {
1315 response_type: HostResponseType::ContractResponse,
1316 response: Some(put_response_offset.as_union_value()),
1317 },
1318 );
1319
1320 finish_host_response_buffer(&mut builder, host_response_offset);
1321 Ok(builder.finished_data().to_vec())
1322 }
1323 ContractResponse::SubscribeResponse { .. } => todo!(),
1324 ContractResponse::NotFound { instance_id } => {
1325 let instance_data = builder.create_vector(instance_id.as_bytes());
1326 let instance_offset = FbsContractInstanceId::create(
1327 &mut builder,
1328 &ContractInstanceIdArgs {
1329 data: Some(instance_data),
1330 },
1331 );
1332
1333 let not_found_offset = FbsNotFound::create(
1334 &mut builder,
1335 &NotFoundArgs {
1336 instance_id: Some(instance_offset),
1337 },
1338 );
1339
1340 let contract_response_offset = FbsContractResponse::create(
1341 &mut builder,
1342 &ContractResponseArgs {
1343 contract_response_type: ContractResponseType::NotFound,
1344 contract_response: Some(not_found_offset.as_union_value()),
1345 },
1346 );
1347
1348 let response_offset = FbsHostResponse::create(
1349 &mut builder,
1350 &HostResponseArgs {
1351 response: Some(contract_response_offset.as_union_value()),
1352 response_type: HostResponseType::ContractResponse,
1353 },
1354 );
1355
1356 finish_host_response_buffer(&mut builder, response_offset);
1357 Ok(builder.finished_data().to_vec())
1358 }
1359 },
1360 HostResponse::DelegateResponse { key, values } => {
1361 let key_data = builder.create_vector(key.bytes());
1362 let code_hash_data = builder.create_vector(&key.code_hash().0);
1363 let key_offset = FbsDelegateKey::create(
1364 &mut builder,
1365 &DelegateKeyArgs {
1366 key: Some(key_data),
1367 code_hash: Some(code_hash_data),
1368 },
1369 );
1370 let mut messages: Vec<WIPOffset<FbsOutboundDelegateMsg>> = Vec::new();
1371 values.iter().for_each(|msg| match msg {
1372 OutboundDelegateMsg::ApplicationMessage(app) => {
1373 let instance_data = builder.create_vector(key.bytes());
1374 let instance_offset = FbsContractInstanceId::create(
1375 &mut builder,
1376 &ContractInstanceIdArgs {
1377 data: Some(instance_data),
1378 },
1379 );
1380 let payload_data = builder.create_vector(&app.payload);
1381 let delegate_context_data = builder.create_vector(app.context.as_ref());
1382 let app_offset = FbsApplicationMessage::create(
1383 &mut builder,
1384 &ApplicationMessageArgs {
1385 app: Some(instance_offset),
1386 payload: Some(payload_data),
1387 context: Some(delegate_context_data),
1388 processed: app.processed,
1389 },
1390 );
1391 let msg = FbsOutboundDelegateMsg::create(
1392 &mut builder,
1393 &OutboundDelegateMsgArgs {
1394 inbound_type: OutboundDelegateMsgType::common_ApplicationMessage,
1395 inbound: Some(app_offset.as_union_value()),
1396 },
1397 );
1398 messages.push(msg);
1399 }
1400 OutboundDelegateMsg::RequestUserInput(input) => {
1401 let message_data = builder.create_vector(input.message.bytes());
1402 let mut responses: Vec<WIPOffset<FbsClientResponse>> = Vec::new();
1403 input.responses.iter().for_each(|resp| {
1404 let response_data = builder.create_vector(resp.bytes());
1405 let response = FbsClientResponse::create(
1406 &mut builder,
1407 &ClientResponseArgs {
1408 data: Some(response_data),
1409 },
1410 );
1411 responses.push(response)
1412 });
1413 let responses_offset = builder.create_vector(&responses);
1414 let input_offset = FbsRequestUserInput::create(
1415 &mut builder,
1416 &RequestUserInputArgs {
1417 request_id: input.request_id,
1418 message: Some(message_data),
1419 responses: Some(responses_offset),
1420 },
1421 );
1422 let msg = FbsOutboundDelegateMsg::create(
1423 &mut builder,
1424 &OutboundDelegateMsgArgs {
1425 inbound_type: OutboundDelegateMsgType::RequestUserInput,
1426 inbound: Some(input_offset.as_union_value()),
1427 },
1428 );
1429 messages.push(msg);
1430 }
1431 OutboundDelegateMsg::ContextUpdated(context) => {
1432 let context_data = builder.create_vector(context.as_ref());
1433 let context_offset = FbsContextUpdated::create(
1434 &mut builder,
1435 &ContextUpdatedArgs {
1436 context: Some(context_data),
1437 },
1438 );
1439 let msg = FbsOutboundDelegateMsg::create(
1440 &mut builder,
1441 &OutboundDelegateMsgArgs {
1442 inbound_type: OutboundDelegateMsgType::ContextUpdated,
1443 inbound: Some(context_offset.as_union_value()),
1444 },
1445 );
1446 messages.push(msg);
1447 }
1448 OutboundDelegateMsg::GetContractRequest(_) => {
1449 tracing::error!(
1452 "GetContractRequest reached client serialization - this is a bug"
1453 );
1454 }
1455 OutboundDelegateMsg::PutContractRequest(_) => {
1456 tracing::error!(
1459 "PutContractRequest reached client serialization - this is a bug"
1460 );
1461 }
1462 OutboundDelegateMsg::UpdateContractRequest(_) => {
1463 tracing::error!(
1464 "UpdateContractRequest reached client serialization - this is a bug"
1465 );
1466 }
1467 OutboundDelegateMsg::SubscribeContractRequest(_) => {
1468 tracing::error!(
1469 "SubscribeContractRequest reached client serialization - this is a bug"
1470 );
1471 }
1472 });
1473 let messages_offset = builder.create_vector(&messages);
1474 let delegate_response_offset = FbsDelegateResponse::create(
1475 &mut builder,
1476 &DelegateResponseArgs {
1477 key: Some(key_offset),
1478 values: Some(messages_offset),
1479 },
1480 );
1481 let host_response_offset = FbsHostResponse::create(
1482 &mut builder,
1483 &HostResponseArgs {
1484 response_type: HostResponseType::DelegateResponse,
1485 response: Some(delegate_response_offset.as_union_value()),
1486 },
1487 );
1488 finish_host_response_buffer(&mut builder, host_response_offset);
1489 Ok(builder.finished_data().to_vec())
1490 }
1491 HostResponse::Ok => {
1492 let ok_offset = FbsOk::create(&mut builder, &OkArgs { msg: None });
1493 let host_response_offset = FbsHostResponse::create(
1494 &mut builder,
1495 &HostResponseArgs {
1496 response_type: HostResponseType::Ok,
1497 response: Some(ok_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::QueryResponse(_) => unimplemented!(),
1504 }
1505 }
1506}
1507
1508impl Display for HostResponse {
1509 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1510 match self {
1511 HostResponse::ContractResponse(res) => match res {
1512 ContractResponse::PutResponse { key } => {
1513 f.write_fmt(format_args!("put response for `{key}`"))
1514 }
1515 ContractResponse::UpdateResponse { key, .. } => {
1516 f.write_fmt(format_args!("update response for `{key}`"))
1517 }
1518 ContractResponse::GetResponse { key, .. } => {
1519 f.write_fmt(format_args!("get response for `{key}`"))
1520 }
1521 ContractResponse::UpdateNotification { key, .. } => {
1522 f.write_fmt(format_args!("update notification for `{key}`"))
1523 }
1524 ContractResponse::SubscribeResponse { key, .. } => {
1525 f.write_fmt(format_args!("subscribe response for `{key}`"))
1526 }
1527 ContractResponse::NotFound { instance_id } => {
1528 f.write_fmt(format_args!("not found for `{instance_id}`"))
1529 }
1530 },
1531 HostResponse::DelegateResponse { .. } => write!(f, "delegate responses"),
1532 HostResponse::Ok => write!(f, "ok response"),
1533 HostResponse::QueryResponse(_) => write!(f, "query response"),
1534 }
1535 }
1536}
1537
1538#[derive(Clone, Serialize, Deserialize, Debug)]
1539#[non_exhaustive]
1540pub enum ContractResponse<T = WrappedState> {
1541 GetResponse {
1542 key: ContractKey,
1543 contract: Option<ContractContainer>,
1544 #[serde(bound(deserialize = "T: DeserializeOwned"))]
1545 state: T,
1546 },
1547 PutResponse {
1548 key: ContractKey,
1549 },
1550 UpdateNotification {
1552 key: ContractKey,
1553 #[serde(deserialize_with = "UpdateData::deser_update_data")]
1554 update: UpdateData<'static>,
1555 },
1556 UpdateResponse {
1558 key: ContractKey,
1559 #[serde(deserialize_with = "StateSummary::deser_state_summary")]
1560 summary: StateSummary<'static>,
1561 },
1562 SubscribeResponse {
1563 key: ContractKey,
1564 subscribed: bool,
1565 },
1566 NotFound {
1570 instance_id: ContractInstanceId,
1572 },
1573}
1574
1575impl<T> From<ContractResponse<T>> for HostResponse<T> {
1576 fn from(value: ContractResponse<T>) -> HostResponse<T> {
1577 HostResponse::ContractResponse(value)
1578 }
1579}
1580
1581#[cfg(test)]
1582mod client_request_test {
1583 use crate::client_api::{ContractRequest, TryFromFbs};
1584 use crate::contract_interface::UpdateData;
1585 use crate::generated::client_request::root_as_client_request;
1586
1587 const EXPECTED_ENCODED_CONTRACT_ID: &str = "6kVs66bKaQAC6ohr8b43SvJ95r36tc2hnG7HezmaJHF9";
1588
1589 #[test]
1590 fn test_build_contract_put_op_from_fbs() -> Result<(), Box<dyn std::error::Error>> {
1591 let put_req_op = vec![
1592 4, 0, 0, 0, 244, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 1, 8, 0, 12, 0, 11, 0, 4, 0, 8,
1593 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 1, 198, 255, 255, 255, 12, 0, 0, 0, 20, 0, 0, 0, 36, 0,
1594 0, 0, 170, 255, 255, 255, 4, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8,
1595 8, 0, 10, 0, 9, 0, 4, 0, 8, 0, 0, 0, 16, 0, 0, 0, 0, 1, 10, 0, 16, 0, 12, 0, 8, 0, 4,
1596 0, 10, 0, 0, 0, 12, 0, 0, 0, 76, 0, 0, 0, 92, 0, 0, 0, 176, 255, 255, 255, 8, 0, 0, 0,
1597 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 8, 0, 4, 0, 6, 0, 0, 0, 4, 0, 0, 0, 32, 0, 0, 0,
1598 85, 111, 11, 171, 40, 85, 240, 177, 207, 81, 106, 157, 173, 90, 234, 2, 250, 253, 75,
1599 210, 62, 7, 6, 34, 75, 26, 229, 230, 107, 167, 17, 108, 8, 0, 0, 0, 1, 2, 3, 4, 5, 6,
1600 7, 8, 8, 0, 12, 0, 8, 0, 4, 0, 8, 0, 0, 0, 8, 0, 0, 0, 16, 0, 0, 0, 8, 0, 0, 0, 1, 2,
1601 3, 4, 5, 6, 7, 8, 8, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8,
1602 ];
1603 let request = if let Ok(client_request) = root_as_client_request(&put_req_op) {
1604 let contract_request = client_request.client_request_as_contract_request().unwrap();
1605 ContractRequest::try_decode_fbs(&contract_request)?
1606 } else {
1607 panic!("failed to decode client request")
1608 };
1609
1610 match request {
1611 ContractRequest::Put {
1612 contract,
1613 state,
1614 related_contracts: _,
1615 subscribe,
1616 blocking_subscribe,
1617 } => {
1618 assert_eq!(
1619 contract.to_string(),
1620 "WasmContainer([api=0.0.1](D8fdVLbRyMLw5mZtPRpWMFcrXGN2z8Nq8UGcLGPFBg2W))"
1621 );
1622 assert_eq!(contract.unwrap_v1().data.data(), &[1, 2, 3, 4, 5, 6, 7, 8]);
1623 assert_eq!(state.to_vec(), &[1, 2, 3, 4, 5, 6, 7, 8]);
1624 assert!(!subscribe);
1625 assert!(!blocking_subscribe);
1626 }
1627 _ => panic!("wrong contract request type"),
1628 }
1629
1630 Ok(())
1631 }
1632
1633 #[test]
1634 fn test_build_contract_get_op_from_fbs() -> Result<(), Box<dyn std::error::Error>> {
1635 let get_req_op = vec![
1636 4, 0, 0, 0, 244, 255, 255, 255, 16, 0, 0, 0, 0, 0, 0, 1, 8, 0, 12, 0, 11, 0, 4, 0, 8,
1637 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 3, 222, 255, 255, 255, 12, 0, 0, 0, 8, 0, 12, 0, 8, 0, 4,
1638 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,
1639 4, 0, 0, 0, 32, 0, 0, 0, 85, 111, 11, 171, 40, 85, 240, 177, 207, 81, 106, 157, 173,
1640 90, 234, 2, 250, 253, 75, 210, 62, 7, 6, 34, 75, 26, 229, 230, 107, 167, 17, 108,
1641 ];
1642 let request = if let Ok(client_request) = root_as_client_request(&get_req_op) {
1643 let contract_request = client_request.client_request_as_contract_request().unwrap();
1644 ContractRequest::try_decode_fbs(&contract_request)?
1645 } else {
1646 panic!("failed to decode client request")
1647 };
1648
1649 match request {
1650 ContractRequest::Get {
1651 key,
1652 return_contract_code: fetch_contract,
1653 subscribe,
1654 blocking_subscribe,
1655 } => {
1656 assert_eq!(key.encode(), EXPECTED_ENCODED_CONTRACT_ID);
1657 assert!(!fetch_contract);
1658 assert!(!subscribe);
1659 assert!(!blocking_subscribe);
1660 }
1661 _ => panic!("wrong contract request type"),
1662 }
1663
1664 Ok(())
1665 }
1666
1667 #[test]
1668 fn test_build_contract_update_op_from_fbs() -> Result<(), Box<dyn std::error::Error>> {
1669 let update_op = vec![
1670 4, 0, 0, 0, 220, 255, 255, 255, 8, 0, 0, 0, 0, 0, 0, 1, 232, 255, 255, 255, 8, 0, 0, 0,
1671 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,
1672 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,
1673 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,
1674 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,
1675 85, 240, 177, 207, 81, 106, 157, 173, 90, 234, 2, 250, 253, 75, 210, 62, 7, 6, 34, 75,
1676 26, 229, 230, 107, 167, 17, 108,
1677 ];
1678 let request = if let Ok(client_request) = root_as_client_request(&update_op) {
1679 let contract_request = client_request.client_request_as_contract_request().unwrap();
1680 ContractRequest::try_decode_fbs(&contract_request)?
1681 } else {
1682 panic!("failed to decode client request")
1683 };
1684
1685 match request {
1686 ContractRequest::Update { key, data } => {
1687 assert_eq!(
1688 key.encoded_contract_id(),
1689 "6kVs66bKaQAC6ohr8b43SvJ95r36tc2hnG7HezmaJHF9"
1690 );
1691 match data {
1692 UpdateData::Delta(delta) => {
1693 assert_eq!(delta.to_vec(), &[1, 2, 3, 4, 5, 6, 7, 8])
1694 }
1695 _ => panic!("wrong update data type"),
1696 }
1697 }
1698 _ => panic!("wrong contract request type"),
1699 }
1700
1701 Ok(())
1702 }
1703}