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