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 fn into_owned(self) -> DelegateRequest<'static> {
585 match self {
586 DelegateRequest::ApplicationMessages {
587 key,
588 inbound,
589 params,
590 } => DelegateRequest::ApplicationMessages {
591 key,
592 params: params.into_owned(),
593 inbound: inbound.into_iter().map(|e| e.into_owned()).collect(),
594 },
595 DelegateRequest::RegisterDelegate {
596 delegate,
597 cipher,
598 nonce,
599 } => DelegateRequest::RegisterDelegate {
600 delegate,
601 cipher,
602 nonce,
603 },
604 DelegateRequest::UnregisterDelegate(key) => DelegateRequest::UnregisterDelegate(key),
605 }
606 }
607
608 pub fn key(&self) -> &DelegateKey {
609 match self {
610 DelegateRequest::ApplicationMessages { key, .. } => key,
611 DelegateRequest::RegisterDelegate { delegate, .. } => delegate.key(),
612 DelegateRequest::UnregisterDelegate(key) => key,
613 }
614 }
615}
616
617impl Display for ClientRequest<'_> {
618 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
619 match self {
620 ClientRequest::ContractOp(op) => match op {
621 ContractRequest::Put {
622 contract, state, ..
623 } => {
624 write!(
625 f,
626 "ContractRequest::Put for contract `{contract}` with state {state}"
627 )
628 }
629 ContractRequest::Update { key, .. } => write!(f, "update request for {key}"),
630 ContractRequest::Get {
631 key,
632 return_contract_code: contract,
633 ..
634 } => {
635 write!(
636 f,
637 "ContractRequest::Get for key `{key}` (fetch full contract: {contract})"
638 )
639 }
640 ContractRequest::Subscribe { key, .. } => {
641 write!(f, "ContractRequest::Subscribe for `{key}`")
642 }
643 },
644 ClientRequest::DelegateOp(op) => match op {
645 DelegateRequest::ApplicationMessages { key, inbound, .. } => {
646 write!(
647 f,
648 "DelegateRequest::ApplicationMessages for `{key}` with {} messages",
649 inbound.len()
650 )
651 }
652 DelegateRequest::RegisterDelegate { delegate, .. } => {
653 write!(
654 f,
655 "DelegateRequest::RegisterDelegate for delegate.key()=`{}`",
656 delegate.key()
657 )
658 }
659 DelegateRequest::UnregisterDelegate(key) => {
660 write!(f, "DelegateRequest::UnregisterDelegate for key `{key}`")
661 }
662 },
663 ClientRequest::Disconnect { .. } => write!(f, "client disconnected"),
664 ClientRequest::Authenticate { .. } => write!(f, "authenticate"),
665 ClientRequest::NodeQueries(query) => write!(f, "node queries: {:?}", query),
666 ClientRequest::Close => write!(f, "close"),
667 ClientRequest::StreamChunk {
668 stream_id,
669 index,
670 total,
671 ..
672 } => write!(f, "stream chunk {index}/{total} (stream {stream_id})"),
673 }
674 }
675}
676
677impl<'a> TryFromFbs<&FbsDelegateRequest<'a>> for DelegateRequest<'a> {
679 fn try_decode_fbs(request: &FbsDelegateRequest<'a>) -> Result<Self, WsApiError> {
680 let req = {
681 match request.delegate_request_type() {
682 DelegateRequestType::ApplicationMessages => {
683 let app_msg = request.delegate_request_as_application_messages().unwrap();
684 let key = DelegateKey::try_decode_fbs(&app_msg.key())?;
685 let params = Parameters::from(app_msg.params().bytes());
686 let inbound = app_msg
687 .inbound()
688 .iter()
689 .map(|msg| InboundDelegateMsg::try_decode_fbs(&msg))
690 .collect::<Result<Vec<_>, _>>()?;
691 DelegateRequest::ApplicationMessages {
692 key,
693 params,
694 inbound,
695 }
696 }
697 DelegateRequestType::RegisterDelegate => {
698 let register = request.delegate_request_as_register_delegate().unwrap();
699 let delegate = DelegateContainer::try_decode_fbs(®ister.delegate())?;
700 let cipher =
701 <[u8; 32]>::try_from(register.cipher().bytes().to_vec().as_slice())
702 .unwrap();
703 let nonce =
704 <[u8; 24]>::try_from(register.nonce().bytes().to_vec().as_slice()).unwrap();
705 DelegateRequest::RegisterDelegate {
706 delegate,
707 cipher,
708 nonce,
709 }
710 }
711 DelegateRequestType::UnregisterDelegate => {
712 let unregister = request.delegate_request_as_unregister_delegate().unwrap();
713 let key = DelegateKey::try_decode_fbs(&unregister.key())?;
714 DelegateRequest::UnregisterDelegate(key)
715 }
716 _ => unreachable!(),
717 }
718 };
719
720 Ok(req)
721 }
722}
723
724#[derive(Serialize, Deserialize, Debug, Clone)]
726#[non_exhaustive]
727pub enum HostResponse<T = WrappedState> {
728 ContractResponse(#[serde(bound(deserialize = "T: DeserializeOwned"))] ContractResponse<T>),
729 DelegateResponse {
730 key: DelegateKey,
731 values: Vec<OutboundDelegateMsg>,
732 },
733 QueryResponse(QueryResponse),
734 Ok,
736 StreamChunk {
738 stream_id: u32,
739 index: u32,
740 total: u32,
741 data: Bytes,
742 },
743 StreamHeader {
747 stream_id: u32,
748 total_bytes: u64,
749 content: StreamContent,
750 },
751}
752
753#[derive(Debug, Serialize, Deserialize, Clone)]
755pub enum StreamContent {
756 GetResponse {
758 key: ContractKey,
759 includes_contract: bool,
760 },
761 Raw,
763}
764
765type Peer = String;
766
767#[derive(Serialize, Deserialize, Debug, Clone)]
768pub enum QueryResponse {
769 ConnectedPeers { peers: Vec<(Peer, SocketAddr)> },
770 NetworkDebug(NetworkDebugInfo),
771 NodeDiagnostics(NodeDiagnosticsResponse),
772 NeighborHosting(NeighborHostingInfo),
773}
774
775#[derive(Serialize, Deserialize, Debug, Clone)]
776pub struct NetworkDebugInfo {
777 pub subscriptions: Vec<SubscriptionInfo>,
778 pub connected_peers: Vec<(String, SocketAddr)>,
779}
780
781#[derive(Serialize, Deserialize, Debug, Clone)]
782pub struct NodeDiagnosticsResponse {
783 pub node_info: Option<NodeInfo>,
785
786 pub network_info: Option<NetworkInfo>,
788
789 pub subscriptions: Vec<SubscriptionInfo>,
791
792 pub contract_states: std::collections::HashMap<String, ContractState>,
803
804 pub system_metrics: Option<SystemMetrics>,
806
807 pub connected_peers_detailed: Vec<ConnectedPeerInfo>,
809}
810
811#[derive(Serialize, Deserialize, Debug, Clone)]
812pub struct NodeInfo {
813 pub peer_id: String,
814 pub is_gateway: bool,
815 pub location: Option<String>,
816 pub listening_address: Option<String>,
817 pub uptime_seconds: u64,
818}
819
820#[derive(Serialize, Deserialize, Debug, Clone)]
821pub struct NetworkInfo {
822 pub connected_peers: Vec<(String, String)>, pub active_connections: usize,
824}
825
826#[derive(Serialize, Deserialize, Debug, Clone)]
827pub struct ContractState {
828 pub subscribers: u32,
830 pub subscriber_peer_ids: Vec<String>,
832 #[serde(default)]
834 pub size_bytes: u64,
835}
836
837#[derive(Serialize, Deserialize, Debug, Clone)]
838pub struct SystemMetrics {
839 pub active_connections: u32,
840 pub hosting_contracts: u32,
841}
842
843#[derive(Serialize, Deserialize, Debug, Clone)]
844pub struct SubscriptionInfo {
845 pub contract_key: ContractInstanceId,
846 pub client_id: usize,
847}
848
849#[derive(Serialize, Deserialize, Debug, Clone)]
851pub struct ConnectedPeerInfo {
852 pub peer_id: String,
853 pub address: String,
854}
855
856#[derive(Serialize, Deserialize, Debug, Clone)]
857pub enum NodeQuery {
858 ConnectedPeers,
859 SubscriptionInfo,
860 NodeDiagnostics {
861 config: NodeDiagnosticsConfig,
863 },
864 NeighborHostingInfo,
866}
867
868#[derive(Serialize, Deserialize, Debug, Clone)]
870pub struct NeighborHostingInfo {
871 pub my_hosted: Vec<ContractHostingEntry>,
873 pub neighbor_hosting: Vec<NeighborHostingDetail>,
875 pub stats: HostingStats,
877}
878
879#[derive(Serialize, Deserialize, Debug, Clone)]
880pub struct ContractHostingEntry {
881 pub contract_key: String,
883 pub hosting_hash: u32,
885 pub hosted_since: u64,
887}
888
889#[derive(Serialize, Deserialize, Debug, Clone)]
890pub struct NeighborHostingDetail {
891 pub peer_id: String,
893 pub known_contracts: Vec<u32>,
895 pub last_update: u64,
897 pub update_count: u64,
899}
900
901#[derive(Serialize, Deserialize, Debug, Clone)]
902pub struct HostingStats {
903 pub hosting_announces_sent: u64,
905 pub hosting_announces_received: u64,
907 pub updates_via_proximity: u64,
909 pub updates_via_subscription: u64,
911 pub false_positive_forwards: u64,
913 pub avg_neighbor_hosting_size: f32,
915}
916
917#[derive(Serialize, Deserialize, Debug, Clone)]
918pub struct NodeDiagnosticsConfig {
919 pub include_node_info: bool,
921
922 pub include_network_info: bool,
924
925 pub include_subscriptions: bool,
927
928 pub contract_keys: Vec<ContractKey>,
930
931 pub include_system_metrics: bool,
933
934 pub include_detailed_peer_info: bool,
936
937 pub include_subscriber_peer_ids: bool,
939}
940
941impl NodeDiagnosticsConfig {
942 pub fn for_update_propagation_debugging(contract_key: ContractKey) -> Self {
944 Self {
945 include_node_info: true,
946 include_network_info: true,
947 include_subscriptions: true,
948 contract_keys: vec![contract_key],
949 include_system_metrics: true,
950 include_detailed_peer_info: true,
951 include_subscriber_peer_ids: true,
952 }
953 }
954
955 pub fn basic_status() -> Self {
957 Self {
958 include_node_info: true,
959 include_network_info: true,
960 include_subscriptions: false,
961 contract_keys: vec![],
962 include_system_metrics: false,
963 include_detailed_peer_info: false,
964 include_subscriber_peer_ids: false,
965 }
966 }
967
968 pub fn full() -> Self {
970 Self {
971 include_node_info: true,
972 include_network_info: true,
973 include_subscriptions: true,
974 contract_keys: vec![], include_system_metrics: true,
976 include_detailed_peer_info: true,
977 include_subscriber_peer_ids: true,
978 }
979 }
980}
981
982impl HostResponse {
983 pub fn unwrap_put(self) -> ContractKey {
984 if let Self::ContractResponse(ContractResponse::PutResponse { key }) = self {
985 key
986 } else {
987 panic!("called `HostResponse::unwrap_put()` on other than `PutResponse` value")
988 }
989 }
990
991 pub fn unwrap_get(self) -> (WrappedState, Option<ContractContainer>) {
992 if let Self::ContractResponse(ContractResponse::GetResponse {
993 contract, state, ..
994 }) = self
995 {
996 (state, contract)
997 } else {
998 panic!("called `HostResponse::unwrap_put()` on other than `PutResponse` value")
999 }
1000 }
1001
1002 pub fn into_fbs_bytes(self) -> Result<Vec<u8>, Box<ClientError>> {
1003 let mut builder = flatbuffers::FlatBufferBuilder::new();
1004 match self {
1005 HostResponse::ContractResponse(res) => match res {
1006 ContractResponse::PutResponse { key } => {
1007 let instance_data = builder.create_vector(key.as_bytes());
1008 let instance_offset = FbsContractInstanceId::create(
1009 &mut builder,
1010 &ContractInstanceIdArgs {
1011 data: Some(instance_data),
1012 },
1013 );
1014
1015 let code = Some(builder.create_vector(&key.code_hash().0));
1016 let key_offset = FbsContractKey::create(
1017 &mut builder,
1018 &ContractKeyArgs {
1019 instance: Some(instance_offset),
1020 code,
1021 },
1022 );
1023
1024 let put_offset = FbsPutResponse::create(
1025 &mut builder,
1026 &PutResponseArgs {
1027 key: Some(key_offset),
1028 },
1029 );
1030
1031 let contract_response_offset = FbsContractResponse::create(
1032 &mut builder,
1033 &ContractResponseArgs {
1034 contract_response: Some(put_offset.as_union_value()),
1035 contract_response_type: ContractResponseType::PutResponse,
1036 },
1037 );
1038
1039 let response_offset = FbsHostResponse::create(
1040 &mut builder,
1041 &HostResponseArgs {
1042 response: Some(contract_response_offset.as_union_value()),
1043 response_type: HostResponseType::ContractResponse,
1044 },
1045 );
1046
1047 finish_host_response_buffer(&mut builder, response_offset);
1048 Ok(builder.finished_data().to_vec())
1049 }
1050 ContractResponse::UpdateResponse { key, summary } => {
1051 let instance_data = builder.create_vector(key.as_bytes());
1052 let instance_offset = FbsContractInstanceId::create(
1053 &mut builder,
1054 &ContractInstanceIdArgs {
1055 data: Some(instance_data),
1056 },
1057 );
1058
1059 let code = Some(builder.create_vector(&key.code_hash().0));
1060
1061 let key_offset = FbsContractKey::create(
1062 &mut builder,
1063 &ContractKeyArgs {
1064 instance: Some(instance_offset),
1065 code,
1066 },
1067 );
1068
1069 let summary_data = builder.create_vector(&summary.into_bytes());
1070
1071 let update_response_offset = FbsUpdateResponse::create(
1072 &mut builder,
1073 &UpdateResponseArgs {
1074 key: Some(key_offset),
1075 summary: Some(summary_data),
1076 },
1077 );
1078
1079 let contract_response_offset = FbsContractResponse::create(
1080 &mut builder,
1081 &ContractResponseArgs {
1082 contract_response: Some(update_response_offset.as_union_value()),
1083 contract_response_type: ContractResponseType::UpdateResponse,
1084 },
1085 );
1086
1087 let response_offset = FbsHostResponse::create(
1088 &mut builder,
1089 &HostResponseArgs {
1090 response: Some(contract_response_offset.as_union_value()),
1091 response_type: HostResponseType::ContractResponse,
1092 },
1093 );
1094
1095 finish_host_response_buffer(&mut builder, response_offset);
1096 Ok(builder.finished_data().to_vec())
1097 }
1098 ContractResponse::GetResponse {
1099 key,
1100 contract: contract_container,
1101 state,
1102 } => {
1103 let instance_data = builder.create_vector(key.as_bytes());
1104 let instance_offset = FbsContractInstanceId::create(
1105 &mut builder,
1106 &ContractInstanceIdArgs {
1107 data: Some(instance_data),
1108 },
1109 );
1110
1111 let code = Some(builder.create_vector(&key.code_hash().0));
1112 let key_offset = FbsContractKey::create(
1113 &mut builder,
1114 &ContractKeyArgs {
1115 instance: Some(instance_offset),
1116 code,
1117 },
1118 );
1119
1120 let container_offset = if let Some(contract) = contract_container {
1121 let data = builder.create_vector(contract.key().as_bytes());
1122
1123 let instance_offset = FbsContractInstanceId::create(
1124 &mut builder,
1125 &ContractInstanceIdArgs { data: Some(data) },
1126 );
1127
1128 let code = Some(builder.create_vector(&contract.key().code_hash().0));
1129 let contract_key_offset = FbsContractKey::create(
1130 &mut builder,
1131 &ContractKeyArgs {
1132 instance: Some(instance_offset),
1133 code,
1134 },
1135 );
1136
1137 let contract_data =
1138 builder.create_vector(contract.clone().unwrap_v1().data.data());
1139 let contract_code_hash =
1140 builder.create_vector(&contract.clone().unwrap_v1().data.hash().0);
1141
1142 let contract_code_offset = ContractCode::create(
1143 &mut builder,
1144 &ContractCodeArgs {
1145 data: Some(contract_data),
1146 code_hash: Some(contract_code_hash),
1147 },
1148 );
1149
1150 let contract_params =
1151 builder.create_vector(&contract.clone().params().into_bytes());
1152
1153 let contract_offset = match contract {
1154 Wasm(V1(..)) => WasmContractV1::create(
1155 &mut builder,
1156 &WasmContractV1Args {
1157 key: Some(contract_key_offset),
1158 data: Some(contract_code_offset),
1159 parameters: Some(contract_params),
1160 },
1161 ),
1162 };
1163
1164 Some(FbsContractContainer::create(
1165 &mut builder,
1166 &ContractContainerArgs {
1167 contract_type: ContractType::WasmContractV1,
1168 contract: Some(contract_offset.as_union_value()),
1169 },
1170 ))
1171 } else {
1172 None
1173 };
1174
1175 let state_data = builder.create_vector(&state);
1176
1177 let get_offset = FbsGetResponse::create(
1178 &mut builder,
1179 &GetResponseArgs {
1180 key: Some(key_offset),
1181 contract: container_offset,
1182 state: Some(state_data),
1183 },
1184 );
1185
1186 let contract_response_offset = FbsContractResponse::create(
1187 &mut builder,
1188 &ContractResponseArgs {
1189 contract_response_type: ContractResponseType::GetResponse,
1190 contract_response: Some(get_offset.as_union_value()),
1191 },
1192 );
1193
1194 let response_offset = FbsHostResponse::create(
1195 &mut builder,
1196 &HostResponseArgs {
1197 response: Some(contract_response_offset.as_union_value()),
1198 response_type: HostResponseType::ContractResponse,
1199 },
1200 );
1201
1202 finish_host_response_buffer(&mut builder, response_offset);
1203 Ok(builder.finished_data().to_vec())
1204 }
1205 ContractResponse::UpdateNotification { key, update } => {
1206 let instance_data = builder.create_vector(key.as_bytes());
1207 let instance_offset = FbsContractInstanceId::create(
1208 &mut builder,
1209 &ContractInstanceIdArgs {
1210 data: Some(instance_data),
1211 },
1212 );
1213
1214 let code = Some(builder.create_vector(&key.code_hash().0));
1215 let key_offset = FbsContractKey::create(
1216 &mut builder,
1217 &ContractKeyArgs {
1218 instance: Some(instance_offset),
1219 code,
1220 },
1221 );
1222
1223 let update_data = match update {
1224 State(state) => {
1225 let state_data = builder.create_vector(&state.into_bytes());
1226 let state_update_offset = StateUpdate::create(
1227 &mut builder,
1228 &StateUpdateArgs {
1229 state: Some(state_data),
1230 },
1231 );
1232 FbsUpdateData::create(
1233 &mut builder,
1234 &UpdateDataArgs {
1235 update_data_type: UpdateDataType::StateUpdate,
1236 update_data: Some(state_update_offset.as_union_value()),
1237 },
1238 )
1239 }
1240 Delta(delta) => {
1241 let delta_data = builder.create_vector(&delta.into_bytes());
1242 let update_offset = DeltaUpdate::create(
1243 &mut builder,
1244 &DeltaUpdateArgs {
1245 delta: Some(delta_data),
1246 },
1247 );
1248 FbsUpdateData::create(
1249 &mut builder,
1250 &UpdateDataArgs {
1251 update_data_type: UpdateDataType::DeltaUpdate,
1252 update_data: Some(update_offset.as_union_value()),
1253 },
1254 )
1255 }
1256 StateAndDelta { state, delta } => {
1257 let state_data = builder.create_vector(&state.into_bytes());
1258 let delta_data = builder.create_vector(&delta.into_bytes());
1259
1260 let update_offset = StateAndDeltaUpdate::create(
1261 &mut builder,
1262 &StateAndDeltaUpdateArgs {
1263 state: Some(state_data),
1264 delta: Some(delta_data),
1265 },
1266 );
1267
1268 FbsUpdateData::create(
1269 &mut builder,
1270 &UpdateDataArgs {
1271 update_data_type: UpdateDataType::StateAndDeltaUpdate,
1272 update_data: Some(update_offset.as_union_value()),
1273 },
1274 )
1275 }
1276 RelatedState { related_to, state } => {
1277 let state_data = builder.create_vector(&state.into_bytes());
1278 let instance_data =
1279 builder.create_vector(related_to.encode().as_bytes());
1280
1281 let instance_offset = FbsContractInstanceId::create(
1282 &mut builder,
1283 &ContractInstanceIdArgs {
1284 data: Some(instance_data),
1285 },
1286 );
1287
1288 let update_offset = RelatedStateUpdate::create(
1289 &mut builder,
1290 &RelatedStateUpdateArgs {
1291 related_to: Some(instance_offset),
1292 state: Some(state_data),
1293 },
1294 );
1295
1296 FbsUpdateData::create(
1297 &mut builder,
1298 &UpdateDataArgs {
1299 update_data_type: UpdateDataType::RelatedStateUpdate,
1300 update_data: Some(update_offset.as_union_value()),
1301 },
1302 )
1303 }
1304 RelatedDelta { related_to, delta } => {
1305 let instance_data =
1306 builder.create_vector(related_to.encode().as_bytes());
1307 let delta_data = builder.create_vector(&delta.into_bytes());
1308
1309 let instance_offset = FbsContractInstanceId::create(
1310 &mut builder,
1311 &ContractInstanceIdArgs {
1312 data: Some(instance_data),
1313 },
1314 );
1315
1316 let update_offset = RelatedDeltaUpdate::create(
1317 &mut builder,
1318 &RelatedDeltaUpdateArgs {
1319 related_to: Some(instance_offset),
1320 delta: Some(delta_data),
1321 },
1322 );
1323
1324 FbsUpdateData::create(
1325 &mut builder,
1326 &UpdateDataArgs {
1327 update_data_type: UpdateDataType::RelatedDeltaUpdate,
1328 update_data: Some(update_offset.as_union_value()),
1329 },
1330 )
1331 }
1332 RelatedStateAndDelta {
1333 related_to,
1334 state,
1335 delta,
1336 } => {
1337 let instance_data =
1338 builder.create_vector(related_to.encode().as_bytes());
1339 let state_data = builder.create_vector(&state.into_bytes());
1340 let delta_data = builder.create_vector(&delta.into_bytes());
1341
1342 let instance_offset = FbsContractInstanceId::create(
1343 &mut builder,
1344 &ContractInstanceIdArgs {
1345 data: Some(instance_data),
1346 },
1347 );
1348
1349 let update_offset = RelatedStateAndDeltaUpdate::create(
1350 &mut builder,
1351 &RelatedStateAndDeltaUpdateArgs {
1352 related_to: Some(instance_offset),
1353 state: Some(state_data),
1354 delta: Some(delta_data),
1355 },
1356 );
1357
1358 FbsUpdateData::create(
1359 &mut builder,
1360 &UpdateDataArgs {
1361 update_data_type: UpdateDataType::RelatedStateAndDeltaUpdate,
1362 update_data: Some(update_offset.as_union_value()),
1363 },
1364 )
1365 }
1366 };
1367
1368 let update_notification_offset = FbsUpdateNotification::create(
1369 &mut builder,
1370 &UpdateNotificationArgs {
1371 key: Some(key_offset),
1372 update: Some(update_data),
1373 },
1374 );
1375
1376 let put_response_offset = FbsContractResponse::create(
1377 &mut builder,
1378 &ContractResponseArgs {
1379 contract_response_type: ContractResponseType::UpdateNotification,
1380 contract_response: Some(update_notification_offset.as_union_value()),
1381 },
1382 );
1383
1384 let host_response_offset = FbsHostResponse::create(
1385 &mut builder,
1386 &HostResponseArgs {
1387 response_type: HostResponseType::ContractResponse,
1388 response: Some(put_response_offset.as_union_value()),
1389 },
1390 );
1391
1392 finish_host_response_buffer(&mut builder, host_response_offset);
1393 Ok(builder.finished_data().to_vec())
1394 }
1395 ContractResponse::SubscribeResponse { key, .. } => {
1396 let instance_data = builder.create_vector(key.as_bytes());
1400 let instance_offset = FbsContractInstanceId::create(
1401 &mut builder,
1402 &ContractInstanceIdArgs {
1403 data: Some(instance_data),
1404 },
1405 );
1406 let code = Some(builder.create_vector(&key.code_hash().0));
1407 let key_offset = FbsContractKey::create(
1408 &mut builder,
1409 &ContractKeyArgs {
1410 instance: Some(instance_offset),
1411 code,
1412 },
1413 );
1414 let put_offset = FbsPutResponse::create(
1415 &mut builder,
1416 &PutResponseArgs {
1417 key: Some(key_offset),
1418 },
1419 );
1420 let contract_response_offset = FbsContractResponse::create(
1421 &mut builder,
1422 &ContractResponseArgs {
1423 contract_response_type: ContractResponseType::PutResponse,
1424 contract_response: Some(put_offset.as_union_value()),
1425 },
1426 );
1427 let host_response_offset = FbsHostResponse::create(
1428 &mut builder,
1429 &HostResponseArgs {
1430 response_type: HostResponseType::ContractResponse,
1431 response: Some(contract_response_offset.as_union_value()),
1432 },
1433 );
1434 finish_host_response_buffer(&mut builder, host_response_offset);
1435 Ok(builder.finished_data().to_vec())
1436 }
1437 ContractResponse::NotFound { instance_id } => {
1438 let instance_data = builder.create_vector(instance_id.as_bytes());
1439 let instance_offset = FbsContractInstanceId::create(
1440 &mut builder,
1441 &ContractInstanceIdArgs {
1442 data: Some(instance_data),
1443 },
1444 );
1445
1446 let not_found_offset = FbsNotFound::create(
1447 &mut builder,
1448 &NotFoundArgs {
1449 instance_id: Some(instance_offset),
1450 },
1451 );
1452
1453 let contract_response_offset = FbsContractResponse::create(
1454 &mut builder,
1455 &ContractResponseArgs {
1456 contract_response_type: ContractResponseType::NotFound,
1457 contract_response: Some(not_found_offset.as_union_value()),
1458 },
1459 );
1460
1461 let response_offset = FbsHostResponse::create(
1462 &mut builder,
1463 &HostResponseArgs {
1464 response: Some(contract_response_offset.as_union_value()),
1465 response_type: HostResponseType::ContractResponse,
1466 },
1467 );
1468
1469 finish_host_response_buffer(&mut builder, response_offset);
1470 Ok(builder.finished_data().to_vec())
1471 }
1472 },
1473 HostResponse::DelegateResponse { key, values } => {
1474 let key_data = builder.create_vector(key.bytes());
1475 let code_hash_data = builder.create_vector(&key.code_hash().0);
1476 let key_offset = FbsDelegateKey::create(
1477 &mut builder,
1478 &DelegateKeyArgs {
1479 key: Some(key_data),
1480 code_hash: Some(code_hash_data),
1481 },
1482 );
1483 let mut messages: Vec<WIPOffset<FbsOutboundDelegateMsg>> = Vec::new();
1484 values.iter().for_each(|msg| match msg {
1485 OutboundDelegateMsg::ApplicationMessage(app) => {
1486 let payload_data = builder.create_vector(&app.payload);
1487 let delegate_context_data = builder.create_vector(app.context.as_ref());
1488 let app_offset = FbsApplicationMessage::create(
1489 &mut builder,
1490 &ApplicationMessageArgs {
1491 payload: Some(payload_data),
1492 context: Some(delegate_context_data),
1493 processed: app.processed,
1494 },
1495 );
1496 let msg = FbsOutboundDelegateMsg::create(
1497 &mut builder,
1498 &OutboundDelegateMsgArgs {
1499 inbound_type: OutboundDelegateMsgType::common_ApplicationMessage,
1500 inbound: Some(app_offset.as_union_value()),
1501 },
1502 );
1503 messages.push(msg);
1504 }
1505 OutboundDelegateMsg::RequestUserInput(input) => {
1506 let message_data = builder.create_vector(input.message.bytes());
1507 let mut responses: Vec<WIPOffset<FbsClientResponse>> = Vec::new();
1508 input.responses.iter().for_each(|resp| {
1509 let response_data = builder.create_vector(resp.bytes());
1510 let response = FbsClientResponse::create(
1511 &mut builder,
1512 &ClientResponseArgs {
1513 data: Some(response_data),
1514 },
1515 );
1516 responses.push(response)
1517 });
1518 let responses_offset = builder.create_vector(&responses);
1519 let input_offset = FbsRequestUserInput::create(
1520 &mut builder,
1521 &RequestUserInputArgs {
1522 request_id: input.request_id,
1523 message: Some(message_data),
1524 responses: Some(responses_offset),
1525 },
1526 );
1527 let msg = FbsOutboundDelegateMsg::create(
1528 &mut builder,
1529 &OutboundDelegateMsgArgs {
1530 inbound_type: OutboundDelegateMsgType::RequestUserInput,
1531 inbound: Some(input_offset.as_union_value()),
1532 },
1533 );
1534 messages.push(msg);
1535 }
1536 OutboundDelegateMsg::ContextUpdated(context) => {
1537 let context_data = builder.create_vector(context.as_ref());
1538 let context_offset = FbsContextUpdated::create(
1539 &mut builder,
1540 &ContextUpdatedArgs {
1541 context: Some(context_data),
1542 },
1543 );
1544 let msg = FbsOutboundDelegateMsg::create(
1545 &mut builder,
1546 &OutboundDelegateMsgArgs {
1547 inbound_type: OutboundDelegateMsgType::ContextUpdated,
1548 inbound: Some(context_offset.as_union_value()),
1549 },
1550 );
1551 messages.push(msg);
1552 }
1553 OutboundDelegateMsg::GetContractRequest(_) => {
1554 tracing::error!(
1557 "GetContractRequest reached client serialization - this is a bug"
1558 );
1559 }
1560 OutboundDelegateMsg::PutContractRequest(_) => {
1561 tracing::error!(
1564 "PutContractRequest reached client serialization - this is a bug"
1565 );
1566 }
1567 OutboundDelegateMsg::UpdateContractRequest(_) => {
1568 tracing::error!(
1569 "UpdateContractRequest reached client serialization - this is a bug"
1570 );
1571 }
1572 OutboundDelegateMsg::SubscribeContractRequest(_) => {
1573 tracing::error!(
1574 "SubscribeContractRequest reached client serialization - this is a bug"
1575 );
1576 }
1577 OutboundDelegateMsg::SendDelegateMessage(_) => {
1578 tracing::error!(
1579 "SendDelegateMessage reached client serialization - this is a bug"
1580 );
1581 }
1582 });
1583 let messages_offset = builder.create_vector(&messages);
1584 let delegate_response_offset = FbsDelegateResponse::create(
1585 &mut builder,
1586 &DelegateResponseArgs {
1587 key: Some(key_offset),
1588 values: Some(messages_offset),
1589 },
1590 );
1591 let host_response_offset = FbsHostResponse::create(
1592 &mut builder,
1593 &HostResponseArgs {
1594 response_type: HostResponseType::DelegateResponse,
1595 response: Some(delegate_response_offset.as_union_value()),
1596 },
1597 );
1598 finish_host_response_buffer(&mut builder, host_response_offset);
1599 Ok(builder.finished_data().to_vec())
1600 }
1601 HostResponse::Ok => {
1602 let ok_offset = FbsOk::create(&mut builder, &OkArgs { msg: None });
1603 let host_response_offset = FbsHostResponse::create(
1604 &mut builder,
1605 &HostResponseArgs {
1606 response_type: HostResponseType::Ok,
1607 response: Some(ok_offset.as_union_value()),
1608 },
1609 );
1610 finish_host_response_buffer(&mut builder, host_response_offset);
1611 Ok(builder.finished_data().to_vec())
1612 }
1613 HostResponse::QueryResponse(_) => unimplemented!(),
1614 HostResponse::StreamChunk {
1615 stream_id,
1616 index,
1617 total,
1618 data,
1619 } => {
1620 let data_offset = builder.create_vector(&data);
1621 let chunk_offset = FbsHostStreamChunk::create(
1622 &mut builder,
1623 &FbsHostStreamChunkArgs {
1624 stream_id,
1625 index,
1626 total,
1627 data: Some(data_offset),
1628 },
1629 );
1630 let host_response_offset = FbsHostResponse::create(
1631 &mut builder,
1632 &HostResponseArgs {
1633 response_type: HostResponseType::StreamChunk,
1634 response: Some(chunk_offset.as_union_value()),
1635 },
1636 );
1637 finish_host_response_buffer(&mut builder, host_response_offset);
1638 Ok(builder.finished_data().to_vec())
1639 }
1640 HostResponse::StreamHeader { .. } => {
1641 Err(Box::new(ClientError::from(ErrorKind::Unhandled {
1645 cause: "StreamHeader is not supported over flatbuffers encoding".into(),
1646 })))
1647 }
1648 }
1649 }
1650}
1651
1652impl Display for HostResponse {
1653 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1654 match self {
1655 HostResponse::ContractResponse(res) => match res {
1656 ContractResponse::PutResponse { key } => {
1657 f.write_fmt(format_args!("put response for `{key}`"))
1658 }
1659 ContractResponse::UpdateResponse { key, .. } => {
1660 f.write_fmt(format_args!("update response for `{key}`"))
1661 }
1662 ContractResponse::GetResponse { key, .. } => {
1663 f.write_fmt(format_args!("get response for `{key}`"))
1664 }
1665 ContractResponse::UpdateNotification { key, .. } => {
1666 f.write_fmt(format_args!("update notification for `{key}`"))
1667 }
1668 ContractResponse::SubscribeResponse { key, .. } => {
1669 f.write_fmt(format_args!("subscribe response for `{key}`"))
1670 }
1671 ContractResponse::NotFound { instance_id } => {
1672 f.write_fmt(format_args!("not found for `{instance_id}`"))
1673 }
1674 },
1675 HostResponse::DelegateResponse { .. } => write!(f, "delegate responses"),
1676 HostResponse::Ok => write!(f, "ok response"),
1677 HostResponse::QueryResponse(_) => write!(f, "query response"),
1678 HostResponse::StreamChunk {
1679 stream_id,
1680 index,
1681 total,
1682 ..
1683 } => write!(f, "stream chunk {index}/{total} (stream {stream_id})"),
1684 HostResponse::StreamHeader {
1685 stream_id,
1686 total_bytes,
1687 ..
1688 } => write!(f, "stream header (stream {stream_id}, {total_bytes} bytes)"),
1689 }
1690 }
1691}
1692
1693#[derive(Clone, Serialize, Deserialize, Debug)]
1694#[non_exhaustive]
1695pub enum ContractResponse<T = WrappedState> {
1696 GetResponse {
1697 key: ContractKey,
1698 contract: Option<ContractContainer>,
1699 #[serde(bound(deserialize = "T: DeserializeOwned"))]
1700 state: T,
1701 },
1702 PutResponse {
1703 key: ContractKey,
1704 },
1705 UpdateNotification {
1707 key: ContractKey,
1708 #[serde(deserialize_with = "UpdateData::deser_update_data")]
1709 update: UpdateData<'static>,
1710 },
1711 UpdateResponse {
1713 key: ContractKey,
1714 #[serde(deserialize_with = "StateSummary::deser_state_summary")]
1715 summary: StateSummary<'static>,
1716 },
1717 SubscribeResponse {
1718 key: ContractKey,
1719 subscribed: bool,
1720 },
1721 NotFound {
1725 instance_id: ContractInstanceId,
1727 },
1728}
1729
1730impl<T> From<ContractResponse<T>> for HostResponse<T> {
1731 fn from(value: ContractResponse<T>) -> HostResponse<T> {
1732 HostResponse::ContractResponse(value)
1733 }
1734}
1735
1736#[cfg(test)]
1737mod node_diagnostics_response_tests {
1738 use super::{
1739 ConnectedPeerInfo, ContractState, NetworkInfo, NodeDiagnosticsResponse, NodeInfo,
1740 SubscriptionInfo, SystemMetrics,
1741 };
1742 use crate::contract_interface::ContractInstanceId;
1743 use std::collections::HashMap;
1744
1745 #[test]
1762 fn node_diagnostics_response_json_round_trips() {
1763 let mut contract_states = HashMap::new();
1764 contract_states.insert(
1765 "6kVs66bKaQAC6ohr8b43SvJ95r36tc2hnG7HezmaJHF9".to_string(),
1766 ContractState {
1767 subscribers: 3,
1768 subscriber_peer_ids: vec!["peer-a".to_string(), "peer-b".to_string()],
1769 size_bytes: 1024,
1770 },
1771 );
1772
1773 let response = NodeDiagnosticsResponse {
1774 node_info: Some(NodeInfo {
1775 peer_id: "peer-self".to_string(),
1776 is_gateway: true,
1777 location: Some("0.5".to_string()),
1778 listening_address: Some("0.0.0.0:31337".to_string()),
1779 uptime_seconds: 3600,
1780 }),
1781 network_info: Some(NetworkInfo {
1782 connected_peers: vec![("peer-x".to_string(), "10.0.0.1:31337".to_string())],
1783 active_connections: 1,
1784 }),
1785 subscriptions: vec![SubscriptionInfo {
1786 contract_key: ContractInstanceId::new([7u8; 32]),
1787 client_id: 42,
1788 }],
1789 contract_states,
1790 system_metrics: Some(SystemMetrics {
1791 active_connections: 1,
1792 hosting_contracts: 1,
1793 }),
1794 connected_peers_detailed: vec![ConnectedPeerInfo {
1795 peer_id: "peer-x".to_string(),
1796 address: "10.0.0.1:31337".to_string(),
1797 }],
1798 };
1799
1800 let json = serde_json::to_string(&response).expect("must serialize to JSON");
1801 let parsed: serde_json::Value = serde_json::from_str(&json).expect("output is valid JSON");
1802
1803 let obj = parsed.as_object().expect("top-level must be object");
1805 assert_eq!(obj.len(), 6, "expected six top-level fields, got {obj:?}");
1806 assert_eq!(parsed["node_info"]["peer_id"], "peer-self");
1807 assert_eq!(parsed["network_info"]["active_connections"], 1);
1808 assert_eq!(parsed["subscriptions"][0]["client_id"], 42);
1809 assert_eq!(parsed["system_metrics"]["hosting_contracts"], 1);
1810 assert_eq!(parsed["connected_peers_detailed"][0]["peer_id"], "peer-x");
1811
1812 let states = parsed["contract_states"]
1813 .as_object()
1814 .expect("contract_states must be a JSON object");
1815 assert_eq!(states.len(), 1);
1816 assert_eq!(
1817 states["6kVs66bKaQAC6ohr8b43SvJ95r36tc2hnG7HezmaJHF9"]["subscribers"],
1818 3
1819 );
1820
1821 let bytes = bincode::serialize(&response).expect("bincode must serialize");
1825 let decoded: NodeDiagnosticsResponse =
1826 bincode::deserialize(&bytes).expect("bincode must round-trip");
1827 assert_eq!(
1828 decoded.contract_states.len(),
1829 1,
1830 "bincode round-trip preserves contract_states entries"
1831 );
1832 }
1833}
1834
1835#[cfg(test)]
1836mod client_request_test {
1837 use crate::client_api::{ContractRequest, TryFromFbs};
1838 use crate::contract_interface::UpdateData;
1839 use crate::generated::client_request::root_as_client_request;
1840
1841 const EXPECTED_ENCODED_CONTRACT_ID: &str = "6kVs66bKaQAC6ohr8b43SvJ95r36tc2hnG7HezmaJHF9";
1842
1843 #[test]
1844 fn test_build_contract_put_op_from_fbs() -> Result<(), Box<dyn std::error::Error>> {
1845 let put_req_op = vec![
1846 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,
1847 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,
1848 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,
1849 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,
1850 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,
1851 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,
1852 85, 111, 11, 171, 40, 85, 240, 177, 207, 81, 106, 157, 173, 90, 234, 2, 250, 253, 75,
1853 210, 62, 7, 6, 34, 75, 26, 229, 230, 107, 167, 17, 108, 8, 0, 0, 0, 1, 2, 3, 4, 5, 6,
1854 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,
1855 3, 4, 5, 6, 7, 8, 8, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8,
1856 ];
1857 let request = if let Ok(client_request) = root_as_client_request(&put_req_op) {
1858 let contract_request = client_request.client_request_as_contract_request().unwrap();
1859 ContractRequest::try_decode_fbs(&contract_request)?
1860 } else {
1861 panic!("failed to decode client request")
1862 };
1863
1864 match request {
1865 ContractRequest::Put {
1866 contract,
1867 state,
1868 related_contracts: _,
1869 subscribe,
1870 blocking_subscribe,
1871 } => {
1872 assert_eq!(
1873 contract.to_string(),
1874 "WasmContainer([api=0.0.1](D8fdVLbRyMLw5mZtPRpWMFcrXGN2z8Nq8UGcLGPFBg2W))"
1875 );
1876 assert_eq!(contract.unwrap_v1().data.data(), &[1, 2, 3, 4, 5, 6, 7, 8]);
1877 assert_eq!(state.to_vec(), &[1, 2, 3, 4, 5, 6, 7, 8]);
1878 assert!(!subscribe);
1879 assert!(!blocking_subscribe);
1880 }
1881 _ => panic!("wrong contract request type"),
1882 }
1883
1884 Ok(())
1885 }
1886
1887 #[test]
1888 fn test_build_contract_get_op_from_fbs() -> Result<(), Box<dyn std::error::Error>> {
1889 let get_req_op = vec![
1890 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,
1891 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,
1892 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,
1893 4, 0, 0, 0, 32, 0, 0, 0, 85, 111, 11, 171, 40, 85, 240, 177, 207, 81, 106, 157, 173,
1894 90, 234, 2, 250, 253, 75, 210, 62, 7, 6, 34, 75, 26, 229, 230, 107, 167, 17, 108,
1895 ];
1896 let request = if let Ok(client_request) = root_as_client_request(&get_req_op) {
1897 let contract_request = client_request.client_request_as_contract_request().unwrap();
1898 ContractRequest::try_decode_fbs(&contract_request)?
1899 } else {
1900 panic!("failed to decode client request")
1901 };
1902
1903 match request {
1904 ContractRequest::Get {
1905 key,
1906 return_contract_code: fetch_contract,
1907 subscribe,
1908 blocking_subscribe,
1909 } => {
1910 assert_eq!(key.encode(), EXPECTED_ENCODED_CONTRACT_ID);
1911 assert!(!fetch_contract);
1912 assert!(!subscribe);
1913 assert!(!blocking_subscribe);
1914 }
1915 _ => panic!("wrong contract request type"),
1916 }
1917
1918 Ok(())
1919 }
1920
1921 #[test]
1922 fn test_build_contract_update_op_from_fbs() -> Result<(), Box<dyn std::error::Error>> {
1923 let update_op = vec![
1924 4, 0, 0, 0, 220, 255, 255, 255, 8, 0, 0, 0, 0, 0, 0, 1, 232, 255, 255, 255, 8, 0, 0, 0,
1925 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,
1926 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,
1927 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,
1928 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,
1929 85, 240, 177, 207, 81, 106, 157, 173, 90, 234, 2, 250, 253, 75, 210, 62, 7, 6, 34, 75,
1930 26, 229, 230, 107, 167, 17, 108,
1931 ];
1932 let request = if let Ok(client_request) = root_as_client_request(&update_op) {
1933 let contract_request = client_request.client_request_as_contract_request().unwrap();
1934 ContractRequest::try_decode_fbs(&contract_request)?
1935 } else {
1936 panic!("failed to decode client request")
1937 };
1938
1939 match request {
1940 ContractRequest::Update { key, data } => {
1941 assert_eq!(
1942 key.encoded_contract_id(),
1943 "6kVs66bKaQAC6ohr8b43SvJ95r36tc2hnG7HezmaJHF9"
1944 );
1945 match data {
1946 UpdateData::Delta(delta) => {
1947 assert_eq!(delta.to_vec(), &[1, 2, 3, 4, 5, 6, 7, 8])
1948 }
1949 _ => panic!("wrong update data type"),
1950 }
1951 }
1952 _ => panic!("wrong contract request type"),
1953 }
1954
1955 Ok(())
1956 }
1957}