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