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 other => {
558 return Err(WsApiError::deserialization(format!(
559 "unknown ContractRequestType discriminant: {}",
560 other.0
561 )));
562 }
563 }
564 };
565
566 Ok(req)
567 }
568}
569
570impl<'a> From<DelegateRequest<'a>> for ClientRequest<'a> {
571 fn from(op: DelegateRequest<'a>) -> Self {
572 ClientRequest::DelegateOp(op)
573 }
574}
575
576#[derive(Serialize, Deserialize, Debug, Clone)]
577#[non_exhaustive]
578pub enum DelegateRequest<'a> {
579 ApplicationMessages {
580 key: DelegateKey,
581 #[serde(deserialize_with = "Parameters::deser_params")]
582 params: Parameters<'a>,
583 #[serde(borrow)]
584 inbound: Vec<InboundDelegateMsg<'a>>,
585 },
586 RegisterDelegate {
587 delegate: DelegateContainer,
588 cipher: [u8; 32],
589 nonce: [u8; 24],
590 },
591 UnregisterDelegate(DelegateKey),
592 RegisterDelegateWithPredecessors {
616 delegate: DelegateContainer,
617 cipher: [u8; 32],
618 nonce: [u8; 24],
619 predecessors: Vec<DelegateKey>,
620 },
621}
622
623impl DelegateRequest<'_> {
624 pub fn into_owned(self) -> DelegateRequest<'static> {
625 match self {
626 DelegateRequest::ApplicationMessages {
627 key,
628 inbound,
629 params,
630 } => DelegateRequest::ApplicationMessages {
631 key,
632 params: params.into_owned(),
633 inbound: inbound.into_iter().map(|e| e.into_owned()).collect(),
634 },
635 DelegateRequest::RegisterDelegate {
636 delegate,
637 cipher,
638 nonce,
639 } => DelegateRequest::RegisterDelegate {
640 delegate,
641 cipher,
642 nonce,
643 },
644 DelegateRequest::UnregisterDelegate(key) => DelegateRequest::UnregisterDelegate(key),
645 DelegateRequest::RegisterDelegateWithPredecessors {
646 delegate,
647 cipher,
648 nonce,
649 predecessors,
650 } => DelegateRequest::RegisterDelegateWithPredecessors {
651 delegate,
652 cipher,
653 nonce,
654 predecessors,
655 },
656 }
657 }
658
659 pub fn key(&self) -> &DelegateKey {
660 match self {
661 DelegateRequest::ApplicationMessages { key, .. } => key,
662 DelegateRequest::RegisterDelegate { delegate, .. } => delegate.key(),
663 DelegateRequest::UnregisterDelegate(key) => key,
664 DelegateRequest::RegisterDelegateWithPredecessors { delegate, .. } => delegate.key(),
665 }
666 }
667}
668
669impl Display for ClientRequest<'_> {
670 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
671 match self {
672 ClientRequest::ContractOp(op) => match op {
673 ContractRequest::Put {
674 contract, state, ..
675 } => {
676 write!(
677 f,
678 "ContractRequest::Put for contract `{contract}` with state {state}"
679 )
680 }
681 ContractRequest::Update { key, .. } => write!(f, "update request for {key}"),
682 ContractRequest::Get {
683 key,
684 return_contract_code: contract,
685 ..
686 } => {
687 write!(
688 f,
689 "ContractRequest::Get for key `{key}` (fetch full contract: {contract})"
690 )
691 }
692 ContractRequest::Subscribe { key, .. } => {
693 write!(f, "ContractRequest::Subscribe for `{key}`")
694 }
695 },
696 ClientRequest::DelegateOp(op) => match op {
697 DelegateRequest::ApplicationMessages { key, inbound, .. } => {
698 write!(
699 f,
700 "DelegateRequest::ApplicationMessages for `{key}` with {} messages",
701 inbound.len()
702 )
703 }
704 DelegateRequest::RegisterDelegate { delegate, .. } => {
705 write!(
706 f,
707 "DelegateRequest::RegisterDelegate for delegate.key()=`{}`",
708 delegate.key()
709 )
710 }
711 DelegateRequest::UnregisterDelegate(key) => {
712 write!(f, "DelegateRequest::UnregisterDelegate for key `{key}`")
713 }
714 DelegateRequest::RegisterDelegateWithPredecessors {
715 delegate,
716 predecessors,
717 ..
718 } => {
719 write!(
720 f,
721 "DelegateRequest::RegisterDelegateWithPredecessors for delegate.key()=`{}` with {} predecessor(s)",
722 delegate.key(),
723 predecessors.len()
724 )
725 }
726 },
727 ClientRequest::Disconnect { .. } => write!(f, "client disconnected"),
728 ClientRequest::Authenticate { .. } => write!(f, "authenticate"),
729 ClientRequest::NodeQueries(query) => write!(f, "node queries: {:?}", query),
730 ClientRequest::Close => write!(f, "close"),
731 ClientRequest::StreamChunk {
732 stream_id,
733 index,
734 total,
735 ..
736 } => write!(f, "stream chunk {index}/{total} (stream {stream_id})"),
737 }
738 }
739}
740
741impl<'a> TryFromFbs<&FbsDelegateRequest<'a>> for DelegateRequest<'a> {
743 fn try_decode_fbs(request: &FbsDelegateRequest<'a>) -> Result<Self, WsApiError> {
744 let req = {
745 match request.delegate_request_type() {
746 DelegateRequestType::ApplicationMessages => {
747 let app_msg = request.delegate_request_as_application_messages().unwrap();
748 let key = DelegateKey::try_decode_fbs(&app_msg.key())?;
749 let params = Parameters::from(app_msg.params().bytes());
750 let inbound = app_msg
751 .inbound()
752 .iter()
753 .map(|msg| InboundDelegateMsg::try_decode_fbs(&msg))
754 .collect::<Result<Vec<_>, _>>()?;
755 DelegateRequest::ApplicationMessages {
756 key,
757 params,
758 inbound,
759 }
760 }
761 DelegateRequestType::RegisterDelegate => {
762 let register = request.delegate_request_as_register_delegate().unwrap();
763 let delegate = DelegateContainer::try_decode_fbs(®ister.delegate())?;
764 let cipher =
765 <[u8; 32]>::try_from(register.cipher().bytes().to_vec().as_slice())
766 .unwrap();
767 let nonce =
768 <[u8; 24]>::try_from(register.nonce().bytes().to_vec().as_slice()).unwrap();
769 DelegateRequest::RegisterDelegate {
770 delegate,
771 cipher,
772 nonce,
773 }
774 }
775 DelegateRequestType::UnregisterDelegate => {
776 let unregister = request.delegate_request_as_unregister_delegate().unwrap();
777 let key = DelegateKey::try_decode_fbs(&unregister.key())?;
778 DelegateRequest::UnregisterDelegate(key)
779 }
780 other => {
787 return Err(WsApiError::deserialization(format!(
788 "unknown DelegateRequestType discriminant: {}",
789 other.0
790 )));
791 }
792 }
793 };
794
795 Ok(req)
796 }
797}
798
799#[derive(Serialize, Deserialize, Debug, Clone)]
801#[non_exhaustive]
802pub enum HostResponse<T = WrappedState> {
803 ContractResponse(#[serde(bound(deserialize = "T: DeserializeOwned"))] ContractResponse<T>),
804 DelegateResponse {
805 key: DelegateKey,
806 values: Vec<OutboundDelegateMsg>,
807 },
808 QueryResponse(QueryResponse),
809 Ok,
811 StreamChunk {
813 stream_id: u32,
814 index: u32,
815 total: u32,
816 data: Bytes,
817 },
818 StreamHeader {
822 stream_id: u32,
823 total_bytes: u64,
824 content: StreamContent,
825 },
826}
827
828#[derive(Debug, Serialize, Deserialize, Clone)]
830pub enum StreamContent {
831 GetResponse {
833 key: ContractKey,
834 includes_contract: bool,
835 },
836 Raw,
838}
839
840type Peer = String;
841
842#[derive(Serialize, Deserialize, Debug, Clone)]
843pub enum QueryResponse {
844 ConnectedPeers { peers: Vec<(Peer, SocketAddr)> },
845 NetworkDebug(NetworkDebugInfo),
846 NodeDiagnostics(NodeDiagnosticsResponse),
847 NeighborHosting(NeighborHostingInfo),
848}
849
850#[derive(Serialize, Deserialize, Debug, Clone)]
851pub struct NetworkDebugInfo {
852 pub subscriptions: Vec<SubscriptionInfo>,
853 pub connected_peers: Vec<(String, SocketAddr)>,
854}
855
856#[derive(Serialize, Deserialize, Debug, Clone)]
857pub struct NodeDiagnosticsResponse {
858 pub node_info: Option<NodeInfo>,
860
861 pub network_info: Option<NetworkInfo>,
863
864 pub subscriptions: Vec<SubscriptionInfo>,
866
867 pub contract_states: std::collections::HashMap<String, ContractState>,
878
879 pub system_metrics: Option<SystemMetrics>,
881
882 pub connected_peers_detailed: Vec<ConnectedPeerInfo>,
884}
885
886#[derive(Serialize, Deserialize, Debug, Clone)]
887pub struct NodeInfo {
888 pub peer_id: String,
889 pub is_gateway: bool,
890 pub location: Option<String>,
891 pub listening_address: Option<String>,
892 pub uptime_seconds: u64,
893}
894
895#[derive(Serialize, Deserialize, Debug, Clone)]
896pub struct NetworkInfo {
897 pub connected_peers: Vec<(String, String)>, pub active_connections: usize,
899}
900
901#[derive(Serialize, Deserialize, Debug, Clone)]
902pub struct ContractState {
903 pub subscribers: u32,
905 pub subscriber_peer_ids: Vec<String>,
907 #[serde(default)]
909 pub size_bytes: u64,
910}
911
912#[derive(Serialize, Deserialize, Debug, Clone)]
913pub struct SystemMetrics {
914 pub active_connections: u32,
915 pub hosting_contracts: u32,
916}
917
918#[derive(Serialize, Deserialize, Debug, Clone)]
919pub struct SubscriptionInfo {
920 pub contract_key: ContractInstanceId,
921 pub client_id: usize,
922}
923
924#[derive(Serialize, Deserialize, Debug, Clone)]
926pub struct ConnectedPeerInfo {
927 pub peer_id: String,
928 pub address: String,
929}
930
931#[derive(Serialize, Deserialize, Debug, Clone)]
932pub enum NodeQuery {
933 ConnectedPeers,
934 SubscriptionInfo,
935 NodeDiagnostics {
936 config: NodeDiagnosticsConfig,
938 },
939 NeighborHostingInfo,
941}
942
943#[derive(Serialize, Deserialize, Debug, Clone)]
945pub struct NeighborHostingInfo {
946 pub my_hosted: Vec<ContractHostingEntry>,
948 pub neighbor_hosting: Vec<NeighborHostingDetail>,
950 pub stats: HostingStats,
952}
953
954#[derive(Serialize, Deserialize, Debug, Clone)]
955pub struct ContractHostingEntry {
956 pub contract_key: String,
958 pub hosting_hash: u32,
960 pub hosted_since: u64,
962}
963
964#[derive(Serialize, Deserialize, Debug, Clone)]
965pub struct NeighborHostingDetail {
966 pub peer_id: String,
968 pub known_contracts: Vec<u32>,
970 pub last_update: u64,
972 pub update_count: u64,
974}
975
976#[derive(Serialize, Deserialize, Debug, Clone)]
977pub struct HostingStats {
978 pub hosting_announces_sent: u64,
980 pub hosting_announces_received: u64,
982 pub updates_via_proximity: u64,
984 pub updates_via_subscription: u64,
986 pub false_positive_forwards: u64,
988 pub avg_neighbor_hosting_size: f32,
990}
991
992#[derive(Serialize, Deserialize, Debug, Clone)]
993pub struct NodeDiagnosticsConfig {
994 pub include_node_info: bool,
996
997 pub include_network_info: bool,
999
1000 pub include_subscriptions: bool,
1002
1003 pub contract_keys: Vec<ContractKey>,
1005
1006 pub include_system_metrics: bool,
1008
1009 pub include_detailed_peer_info: bool,
1011
1012 pub include_subscriber_peer_ids: bool,
1014}
1015
1016impl NodeDiagnosticsConfig {
1017 pub fn for_update_propagation_debugging(contract_key: ContractKey) -> Self {
1019 Self {
1020 include_node_info: true,
1021 include_network_info: true,
1022 include_subscriptions: true,
1023 contract_keys: vec![contract_key],
1024 include_system_metrics: true,
1025 include_detailed_peer_info: true,
1026 include_subscriber_peer_ids: true,
1027 }
1028 }
1029
1030 pub fn basic_status() -> Self {
1032 Self {
1033 include_node_info: true,
1034 include_network_info: true,
1035 include_subscriptions: false,
1036 contract_keys: vec![],
1037 include_system_metrics: false,
1038 include_detailed_peer_info: false,
1039 include_subscriber_peer_ids: false,
1040 }
1041 }
1042
1043 pub fn full() -> Self {
1045 Self {
1046 include_node_info: true,
1047 include_network_info: true,
1048 include_subscriptions: true,
1049 contract_keys: vec![], include_system_metrics: true,
1051 include_detailed_peer_info: true,
1052 include_subscriber_peer_ids: true,
1053 }
1054 }
1055}
1056
1057impl HostResponse {
1058 pub fn unwrap_put(self) -> ContractKey {
1059 if let Self::ContractResponse(ContractResponse::PutResponse { key }) = self {
1060 key
1061 } else {
1062 panic!("called `HostResponse::unwrap_put()` on other than `PutResponse` value")
1063 }
1064 }
1065
1066 pub fn unwrap_get(self) -> (WrappedState, Option<ContractContainer>) {
1067 if let Self::ContractResponse(ContractResponse::GetResponse {
1068 contract, state, ..
1069 }) = self
1070 {
1071 (state, contract)
1072 } else {
1073 panic!("called `HostResponse::unwrap_put()` on other than `PutResponse` value")
1074 }
1075 }
1076
1077 pub fn into_fbs_bytes(self) -> Result<Vec<u8>, Box<ClientError>> {
1078 let mut builder = flatbuffers::FlatBufferBuilder::new();
1079 match self {
1080 HostResponse::ContractResponse(res) => match res {
1081 ContractResponse::PutResponse { key } => {
1082 let instance_data = builder.create_vector(key.as_bytes());
1083 let instance_offset = FbsContractInstanceId::create(
1084 &mut builder,
1085 &ContractInstanceIdArgs {
1086 data: Some(instance_data),
1087 },
1088 );
1089
1090 let code = Some(builder.create_vector(&key.code_hash().0));
1091 let key_offset = FbsContractKey::create(
1092 &mut builder,
1093 &ContractKeyArgs {
1094 instance: Some(instance_offset),
1095 code,
1096 },
1097 );
1098
1099 let put_offset = FbsPutResponse::create(
1100 &mut builder,
1101 &PutResponseArgs {
1102 key: Some(key_offset),
1103 },
1104 );
1105
1106 let contract_response_offset = FbsContractResponse::create(
1107 &mut builder,
1108 &ContractResponseArgs {
1109 contract_response: Some(put_offset.as_union_value()),
1110 contract_response_type: ContractResponseType::PutResponse,
1111 },
1112 );
1113
1114 let response_offset = FbsHostResponse::create(
1115 &mut builder,
1116 &HostResponseArgs {
1117 response: Some(contract_response_offset.as_union_value()),
1118 response_type: HostResponseType::ContractResponse,
1119 },
1120 );
1121
1122 finish_host_response_buffer(&mut builder, response_offset);
1123 Ok(builder.finished_data().to_vec())
1124 }
1125 ContractResponse::UpdateResponse { key, summary } => {
1126 let instance_data = builder.create_vector(key.as_bytes());
1127 let instance_offset = FbsContractInstanceId::create(
1128 &mut builder,
1129 &ContractInstanceIdArgs {
1130 data: Some(instance_data),
1131 },
1132 );
1133
1134 let code = Some(builder.create_vector(&key.code_hash().0));
1135
1136 let key_offset = FbsContractKey::create(
1137 &mut builder,
1138 &ContractKeyArgs {
1139 instance: Some(instance_offset),
1140 code,
1141 },
1142 );
1143
1144 let summary_data = builder.create_vector(&summary.into_bytes());
1145
1146 let update_response_offset = FbsUpdateResponse::create(
1147 &mut builder,
1148 &UpdateResponseArgs {
1149 key: Some(key_offset),
1150 summary: Some(summary_data),
1151 },
1152 );
1153
1154 let contract_response_offset = FbsContractResponse::create(
1155 &mut builder,
1156 &ContractResponseArgs {
1157 contract_response: Some(update_response_offset.as_union_value()),
1158 contract_response_type: ContractResponseType::UpdateResponse,
1159 },
1160 );
1161
1162 let response_offset = FbsHostResponse::create(
1163 &mut builder,
1164 &HostResponseArgs {
1165 response: Some(contract_response_offset.as_union_value()),
1166 response_type: HostResponseType::ContractResponse,
1167 },
1168 );
1169
1170 finish_host_response_buffer(&mut builder, response_offset);
1171 Ok(builder.finished_data().to_vec())
1172 }
1173 ContractResponse::GetResponse {
1174 key,
1175 contract: contract_container,
1176 state,
1177 } => {
1178 let instance_data = builder.create_vector(key.as_bytes());
1179 let instance_offset = FbsContractInstanceId::create(
1180 &mut builder,
1181 &ContractInstanceIdArgs {
1182 data: Some(instance_data),
1183 },
1184 );
1185
1186 let code = Some(builder.create_vector(&key.code_hash().0));
1187 let key_offset = FbsContractKey::create(
1188 &mut builder,
1189 &ContractKeyArgs {
1190 instance: Some(instance_offset),
1191 code,
1192 },
1193 );
1194
1195 let container_offset = if let Some(contract) = contract_container {
1196 let data = builder.create_vector(contract.key().as_bytes());
1197
1198 let instance_offset = FbsContractInstanceId::create(
1199 &mut builder,
1200 &ContractInstanceIdArgs { data: Some(data) },
1201 );
1202
1203 let code = Some(builder.create_vector(&contract.key().code_hash().0));
1204 let contract_key_offset = FbsContractKey::create(
1205 &mut builder,
1206 &ContractKeyArgs {
1207 instance: Some(instance_offset),
1208 code,
1209 },
1210 );
1211
1212 let contract_data =
1213 builder.create_vector(contract.clone().unwrap_v1().data.data());
1214 let contract_code_hash =
1215 builder.create_vector(&contract.clone().unwrap_v1().data.hash().0);
1216
1217 let contract_code_offset = ContractCode::create(
1218 &mut builder,
1219 &ContractCodeArgs {
1220 data: Some(contract_data),
1221 code_hash: Some(contract_code_hash),
1222 },
1223 );
1224
1225 let contract_params =
1226 builder.create_vector(&contract.clone().params().into_bytes());
1227
1228 let contract_offset = match contract {
1229 Wasm(V1(..)) => WasmContractV1::create(
1230 &mut builder,
1231 &WasmContractV1Args {
1232 key: Some(contract_key_offset),
1233 data: Some(contract_code_offset),
1234 parameters: Some(contract_params),
1235 },
1236 ),
1237 };
1238
1239 Some(FbsContractContainer::create(
1240 &mut builder,
1241 &ContractContainerArgs {
1242 contract_type: ContractType::WasmContractV1,
1243 contract: Some(contract_offset.as_union_value()),
1244 },
1245 ))
1246 } else {
1247 None
1248 };
1249
1250 let state_data = builder.create_vector(&state);
1251
1252 let get_offset = FbsGetResponse::create(
1253 &mut builder,
1254 &GetResponseArgs {
1255 key: Some(key_offset),
1256 contract: container_offset,
1257 state: Some(state_data),
1258 },
1259 );
1260
1261 let contract_response_offset = FbsContractResponse::create(
1262 &mut builder,
1263 &ContractResponseArgs {
1264 contract_response_type: ContractResponseType::GetResponse,
1265 contract_response: Some(get_offset.as_union_value()),
1266 },
1267 );
1268
1269 let response_offset = FbsHostResponse::create(
1270 &mut builder,
1271 &HostResponseArgs {
1272 response: Some(contract_response_offset.as_union_value()),
1273 response_type: HostResponseType::ContractResponse,
1274 },
1275 );
1276
1277 finish_host_response_buffer(&mut builder, response_offset);
1278 Ok(builder.finished_data().to_vec())
1279 }
1280 ContractResponse::UpdateNotification { key, update } => {
1281 let instance_data = builder.create_vector(key.as_bytes());
1282 let instance_offset = FbsContractInstanceId::create(
1283 &mut builder,
1284 &ContractInstanceIdArgs {
1285 data: Some(instance_data),
1286 },
1287 );
1288
1289 let code = Some(builder.create_vector(&key.code_hash().0));
1290 let key_offset = FbsContractKey::create(
1291 &mut builder,
1292 &ContractKeyArgs {
1293 instance: Some(instance_offset),
1294 code,
1295 },
1296 );
1297
1298 let update_data = match update {
1299 State(state) => {
1300 let state_data = builder.create_vector(&state.into_bytes());
1301 let state_update_offset = StateUpdate::create(
1302 &mut builder,
1303 &StateUpdateArgs {
1304 state: Some(state_data),
1305 },
1306 );
1307 FbsUpdateData::create(
1308 &mut builder,
1309 &UpdateDataArgs {
1310 update_data_type: UpdateDataType::StateUpdate,
1311 update_data: Some(state_update_offset.as_union_value()),
1312 },
1313 )
1314 }
1315 Delta(delta) => {
1316 let delta_data = builder.create_vector(&delta.into_bytes());
1317 let update_offset = DeltaUpdate::create(
1318 &mut builder,
1319 &DeltaUpdateArgs {
1320 delta: Some(delta_data),
1321 },
1322 );
1323 FbsUpdateData::create(
1324 &mut builder,
1325 &UpdateDataArgs {
1326 update_data_type: UpdateDataType::DeltaUpdate,
1327 update_data: Some(update_offset.as_union_value()),
1328 },
1329 )
1330 }
1331 StateAndDelta { state, delta } => {
1332 let state_data = builder.create_vector(&state.into_bytes());
1333 let delta_data = builder.create_vector(&delta.into_bytes());
1334
1335 let update_offset = StateAndDeltaUpdate::create(
1336 &mut builder,
1337 &StateAndDeltaUpdateArgs {
1338 state: Some(state_data),
1339 delta: Some(delta_data),
1340 },
1341 );
1342
1343 FbsUpdateData::create(
1344 &mut builder,
1345 &UpdateDataArgs {
1346 update_data_type: UpdateDataType::StateAndDeltaUpdate,
1347 update_data: Some(update_offset.as_union_value()),
1348 },
1349 )
1350 }
1351 RelatedState { related_to, state } => {
1352 let state_data = builder.create_vector(&state.into_bytes());
1353 let instance_data =
1354 builder.create_vector(related_to.encode().as_bytes());
1355
1356 let instance_offset = FbsContractInstanceId::create(
1357 &mut builder,
1358 &ContractInstanceIdArgs {
1359 data: Some(instance_data),
1360 },
1361 );
1362
1363 let update_offset = RelatedStateUpdate::create(
1364 &mut builder,
1365 &RelatedStateUpdateArgs {
1366 related_to: Some(instance_offset),
1367 state: Some(state_data),
1368 },
1369 );
1370
1371 FbsUpdateData::create(
1372 &mut builder,
1373 &UpdateDataArgs {
1374 update_data_type: UpdateDataType::RelatedStateUpdate,
1375 update_data: Some(update_offset.as_union_value()),
1376 },
1377 )
1378 }
1379 RelatedDelta { related_to, delta } => {
1380 let instance_data =
1381 builder.create_vector(related_to.encode().as_bytes());
1382 let delta_data = builder.create_vector(&delta.into_bytes());
1383
1384 let instance_offset = FbsContractInstanceId::create(
1385 &mut builder,
1386 &ContractInstanceIdArgs {
1387 data: Some(instance_data),
1388 },
1389 );
1390
1391 let update_offset = RelatedDeltaUpdate::create(
1392 &mut builder,
1393 &RelatedDeltaUpdateArgs {
1394 related_to: Some(instance_offset),
1395 delta: Some(delta_data),
1396 },
1397 );
1398
1399 FbsUpdateData::create(
1400 &mut builder,
1401 &UpdateDataArgs {
1402 update_data_type: UpdateDataType::RelatedDeltaUpdate,
1403 update_data: Some(update_offset.as_union_value()),
1404 },
1405 )
1406 }
1407 RelatedStateAndDelta {
1408 related_to,
1409 state,
1410 delta,
1411 } => {
1412 let instance_data =
1413 builder.create_vector(related_to.encode().as_bytes());
1414 let state_data = builder.create_vector(&state.into_bytes());
1415 let delta_data = builder.create_vector(&delta.into_bytes());
1416
1417 let instance_offset = FbsContractInstanceId::create(
1418 &mut builder,
1419 &ContractInstanceIdArgs {
1420 data: Some(instance_data),
1421 },
1422 );
1423
1424 let update_offset = RelatedStateAndDeltaUpdate::create(
1425 &mut builder,
1426 &RelatedStateAndDeltaUpdateArgs {
1427 related_to: Some(instance_offset),
1428 state: Some(state_data),
1429 delta: Some(delta_data),
1430 },
1431 );
1432
1433 FbsUpdateData::create(
1434 &mut builder,
1435 &UpdateDataArgs {
1436 update_data_type: UpdateDataType::RelatedStateAndDeltaUpdate,
1437 update_data: Some(update_offset.as_union_value()),
1438 },
1439 )
1440 }
1441 };
1442
1443 let update_notification_offset = FbsUpdateNotification::create(
1444 &mut builder,
1445 &UpdateNotificationArgs {
1446 key: Some(key_offset),
1447 update: Some(update_data),
1448 },
1449 );
1450
1451 let put_response_offset = FbsContractResponse::create(
1452 &mut builder,
1453 &ContractResponseArgs {
1454 contract_response_type: ContractResponseType::UpdateNotification,
1455 contract_response: Some(update_notification_offset.as_union_value()),
1456 },
1457 );
1458
1459 let host_response_offset = FbsHostResponse::create(
1460 &mut builder,
1461 &HostResponseArgs {
1462 response_type: HostResponseType::ContractResponse,
1463 response: Some(put_response_offset.as_union_value()),
1464 },
1465 );
1466
1467 finish_host_response_buffer(&mut builder, host_response_offset);
1468 Ok(builder.finished_data().to_vec())
1469 }
1470 ContractResponse::SubscribeResponse { key, .. } => {
1471 let instance_data = builder.create_vector(key.as_bytes());
1475 let instance_offset = FbsContractInstanceId::create(
1476 &mut builder,
1477 &ContractInstanceIdArgs {
1478 data: Some(instance_data),
1479 },
1480 );
1481 let code = Some(builder.create_vector(&key.code_hash().0));
1482 let key_offset = FbsContractKey::create(
1483 &mut builder,
1484 &ContractKeyArgs {
1485 instance: Some(instance_offset),
1486 code,
1487 },
1488 );
1489 let put_offset = FbsPutResponse::create(
1490 &mut builder,
1491 &PutResponseArgs {
1492 key: Some(key_offset),
1493 },
1494 );
1495 let contract_response_offset = FbsContractResponse::create(
1496 &mut builder,
1497 &ContractResponseArgs {
1498 contract_response_type: ContractResponseType::PutResponse,
1499 contract_response: Some(put_offset.as_union_value()),
1500 },
1501 );
1502 let host_response_offset = FbsHostResponse::create(
1503 &mut builder,
1504 &HostResponseArgs {
1505 response_type: HostResponseType::ContractResponse,
1506 response: Some(contract_response_offset.as_union_value()),
1507 },
1508 );
1509 finish_host_response_buffer(&mut builder, host_response_offset);
1510 Ok(builder.finished_data().to_vec())
1511 }
1512 ContractResponse::NotFound { instance_id } => {
1513 let instance_data = builder.create_vector(instance_id.as_bytes());
1514 let instance_offset = FbsContractInstanceId::create(
1515 &mut builder,
1516 &ContractInstanceIdArgs {
1517 data: Some(instance_data),
1518 },
1519 );
1520
1521 let not_found_offset = FbsNotFound::create(
1522 &mut builder,
1523 &NotFoundArgs {
1524 instance_id: Some(instance_offset),
1525 },
1526 );
1527
1528 let contract_response_offset = FbsContractResponse::create(
1529 &mut builder,
1530 &ContractResponseArgs {
1531 contract_response_type: ContractResponseType::NotFound,
1532 contract_response: Some(not_found_offset.as_union_value()),
1533 },
1534 );
1535
1536 let response_offset = FbsHostResponse::create(
1537 &mut builder,
1538 &HostResponseArgs {
1539 response: Some(contract_response_offset.as_union_value()),
1540 response_type: HostResponseType::ContractResponse,
1541 },
1542 );
1543
1544 finish_host_response_buffer(&mut builder, response_offset);
1545 Ok(builder.finished_data().to_vec())
1546 }
1547 },
1548 HostResponse::DelegateResponse { key, values } => {
1549 let key_data = builder.create_vector(key.bytes());
1550 let code_hash_data = builder.create_vector(&key.code_hash().0);
1551 let key_offset = FbsDelegateKey::create(
1552 &mut builder,
1553 &DelegateKeyArgs {
1554 key: Some(key_data),
1555 code_hash: Some(code_hash_data),
1556 },
1557 );
1558 let mut messages: Vec<WIPOffset<FbsOutboundDelegateMsg>> = Vec::new();
1559 values.iter().for_each(|msg| match msg {
1560 OutboundDelegateMsg::ApplicationMessage(app) => {
1561 let payload_data = builder.create_vector(&app.payload);
1562 let delegate_context_data = builder.create_vector(app.context.as_ref());
1563 let app_offset = FbsApplicationMessage::create(
1564 &mut builder,
1565 &ApplicationMessageArgs {
1566 payload: Some(payload_data),
1567 context: Some(delegate_context_data),
1568 processed: app.processed,
1569 },
1570 );
1571 let msg = FbsOutboundDelegateMsg::create(
1572 &mut builder,
1573 &OutboundDelegateMsgArgs {
1574 inbound_type: OutboundDelegateMsgType::common_ApplicationMessage,
1575 inbound: Some(app_offset.as_union_value()),
1576 },
1577 );
1578 messages.push(msg);
1579 }
1580 OutboundDelegateMsg::RequestUserInput(input) => {
1581 let message_data = builder.create_vector(input.message.bytes());
1582 let mut responses: Vec<WIPOffset<FbsClientResponse>> = Vec::new();
1583 input.responses.iter().for_each(|resp| {
1584 let response_data = builder.create_vector(resp.bytes());
1585 let response = FbsClientResponse::create(
1586 &mut builder,
1587 &ClientResponseArgs {
1588 data: Some(response_data),
1589 },
1590 );
1591 responses.push(response)
1592 });
1593 let responses_offset = builder.create_vector(&responses);
1594 let input_offset = FbsRequestUserInput::create(
1595 &mut builder,
1596 &RequestUserInputArgs {
1597 request_id: input.request_id,
1598 message: Some(message_data),
1599 responses: Some(responses_offset),
1600 },
1601 );
1602 let msg = FbsOutboundDelegateMsg::create(
1603 &mut builder,
1604 &OutboundDelegateMsgArgs {
1605 inbound_type: OutboundDelegateMsgType::RequestUserInput,
1606 inbound: Some(input_offset.as_union_value()),
1607 },
1608 );
1609 messages.push(msg);
1610 }
1611 OutboundDelegateMsg::ContextUpdated(context) => {
1612 let context_data = builder.create_vector(context.as_ref());
1613 let context_offset = FbsContextUpdated::create(
1614 &mut builder,
1615 &ContextUpdatedArgs {
1616 context: Some(context_data),
1617 },
1618 );
1619 let msg = FbsOutboundDelegateMsg::create(
1620 &mut builder,
1621 &OutboundDelegateMsgArgs {
1622 inbound_type: OutboundDelegateMsgType::ContextUpdated,
1623 inbound: Some(context_offset.as_union_value()),
1624 },
1625 );
1626 messages.push(msg);
1627 }
1628 OutboundDelegateMsg::GetContractRequest(_) => {
1629 tracing::error!(
1632 "GetContractRequest reached client serialization - this is a bug"
1633 );
1634 }
1635 OutboundDelegateMsg::PutContractRequest(_) => {
1636 tracing::error!(
1639 "PutContractRequest reached client serialization - this is a bug"
1640 );
1641 }
1642 OutboundDelegateMsg::UpdateContractRequest(_) => {
1643 tracing::error!(
1644 "UpdateContractRequest reached client serialization - this is a bug"
1645 );
1646 }
1647 OutboundDelegateMsg::SubscribeContractRequest(_) => {
1648 tracing::error!(
1649 "SubscribeContractRequest reached client serialization - this is a bug"
1650 );
1651 }
1652 OutboundDelegateMsg::SendDelegateMessage(_) => {
1653 tracing::error!(
1654 "SendDelegateMessage reached client serialization - this is a bug"
1655 );
1656 }
1657 });
1658 let messages_offset = builder.create_vector(&messages);
1659 let delegate_response_offset = FbsDelegateResponse::create(
1660 &mut builder,
1661 &DelegateResponseArgs {
1662 key: Some(key_offset),
1663 values: Some(messages_offset),
1664 },
1665 );
1666 let host_response_offset = FbsHostResponse::create(
1667 &mut builder,
1668 &HostResponseArgs {
1669 response_type: HostResponseType::DelegateResponse,
1670 response: Some(delegate_response_offset.as_union_value()),
1671 },
1672 );
1673 finish_host_response_buffer(&mut builder, host_response_offset);
1674 Ok(builder.finished_data().to_vec())
1675 }
1676 HostResponse::Ok => {
1677 let ok_offset = FbsOk::create(&mut builder, &OkArgs { msg: None });
1678 let host_response_offset = FbsHostResponse::create(
1679 &mut builder,
1680 &HostResponseArgs {
1681 response_type: HostResponseType::Ok,
1682 response: Some(ok_offset.as_union_value()),
1683 },
1684 );
1685 finish_host_response_buffer(&mut builder, host_response_offset);
1686 Ok(builder.finished_data().to_vec())
1687 }
1688 HostResponse::QueryResponse(_) => unimplemented!(),
1689 HostResponse::StreamChunk {
1690 stream_id,
1691 index,
1692 total,
1693 data,
1694 } => {
1695 let data_offset = builder.create_vector(&data);
1696 let chunk_offset = FbsHostStreamChunk::create(
1697 &mut builder,
1698 &FbsHostStreamChunkArgs {
1699 stream_id,
1700 index,
1701 total,
1702 data: Some(data_offset),
1703 },
1704 );
1705 let host_response_offset = FbsHostResponse::create(
1706 &mut builder,
1707 &HostResponseArgs {
1708 response_type: HostResponseType::StreamChunk,
1709 response: Some(chunk_offset.as_union_value()),
1710 },
1711 );
1712 finish_host_response_buffer(&mut builder, host_response_offset);
1713 Ok(builder.finished_data().to_vec())
1714 }
1715 HostResponse::StreamHeader { .. } => {
1716 Err(Box::new(ClientError::from(ErrorKind::Unhandled {
1720 cause: "StreamHeader is not supported over flatbuffers encoding".into(),
1721 })))
1722 }
1723 }
1724 }
1725}
1726
1727impl Display for HostResponse {
1728 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1729 match self {
1730 HostResponse::ContractResponse(res) => match res {
1731 ContractResponse::PutResponse { key } => {
1732 f.write_fmt(format_args!("put response for `{key}`"))
1733 }
1734 ContractResponse::UpdateResponse { key, .. } => {
1735 f.write_fmt(format_args!("update response for `{key}`"))
1736 }
1737 ContractResponse::GetResponse { key, .. } => {
1738 f.write_fmt(format_args!("get response for `{key}`"))
1739 }
1740 ContractResponse::UpdateNotification { key, .. } => {
1741 f.write_fmt(format_args!("update notification for `{key}`"))
1742 }
1743 ContractResponse::SubscribeResponse { key, .. } => {
1744 f.write_fmt(format_args!("subscribe response for `{key}`"))
1745 }
1746 ContractResponse::NotFound { instance_id } => {
1747 f.write_fmt(format_args!("not found for `{instance_id}`"))
1748 }
1749 },
1750 HostResponse::DelegateResponse { .. } => write!(f, "delegate responses"),
1751 HostResponse::Ok => write!(f, "ok response"),
1752 HostResponse::QueryResponse(_) => write!(f, "query response"),
1753 HostResponse::StreamChunk {
1754 stream_id,
1755 index,
1756 total,
1757 ..
1758 } => write!(f, "stream chunk {index}/{total} (stream {stream_id})"),
1759 HostResponse::StreamHeader {
1760 stream_id,
1761 total_bytes,
1762 ..
1763 } => write!(f, "stream header (stream {stream_id}, {total_bytes} bytes)"),
1764 }
1765 }
1766}
1767
1768#[derive(Clone, Serialize, Deserialize, Debug)]
1769#[non_exhaustive]
1770pub enum ContractResponse<T = WrappedState> {
1771 GetResponse {
1772 key: ContractKey,
1773 contract: Option<ContractContainer>,
1774 #[serde(bound(deserialize = "T: DeserializeOwned"))]
1775 state: T,
1776 },
1777 PutResponse {
1778 key: ContractKey,
1779 },
1780 UpdateNotification {
1782 key: ContractKey,
1783 #[serde(deserialize_with = "UpdateData::deser_update_data")]
1784 update: UpdateData<'static>,
1785 },
1786 UpdateResponse {
1788 key: ContractKey,
1789 #[serde(deserialize_with = "StateSummary::deser_state_summary")]
1790 summary: StateSummary<'static>,
1791 },
1792 SubscribeResponse {
1793 key: ContractKey,
1794 subscribed: bool,
1795 },
1796 NotFound {
1800 instance_id: ContractInstanceId,
1802 },
1803}
1804
1805impl<T> From<ContractResponse<T>> for HostResponse<T> {
1806 fn from(value: ContractResponse<T>) -> HostResponse<T> {
1807 HostResponse::ContractResponse(value)
1808 }
1809}
1810
1811#[cfg(test)]
1812mod node_diagnostics_response_tests {
1813 use super::{
1814 ConnectedPeerInfo, ContractState, NetworkInfo, NodeDiagnosticsResponse, NodeInfo,
1815 SubscriptionInfo, SystemMetrics,
1816 };
1817 use crate::contract_interface::ContractInstanceId;
1818 use std::collections::HashMap;
1819
1820 #[test]
1837 fn node_diagnostics_response_json_round_trips() {
1838 let mut contract_states = HashMap::new();
1839 contract_states.insert(
1840 "6kVs66bKaQAC6ohr8b43SvJ95r36tc2hnG7HezmaJHF9".to_string(),
1841 ContractState {
1842 subscribers: 3,
1843 subscriber_peer_ids: vec!["peer-a".to_string(), "peer-b".to_string()],
1844 size_bytes: 1024,
1845 },
1846 );
1847
1848 let response = NodeDiagnosticsResponse {
1849 node_info: Some(NodeInfo {
1850 peer_id: "peer-self".to_string(),
1851 is_gateway: true,
1852 location: Some("0.5".to_string()),
1853 listening_address: Some("0.0.0.0:31337".to_string()),
1854 uptime_seconds: 3600,
1855 }),
1856 network_info: Some(NetworkInfo {
1857 connected_peers: vec![("peer-x".to_string(), "10.0.0.1:31337".to_string())],
1858 active_connections: 1,
1859 }),
1860 subscriptions: vec![SubscriptionInfo {
1861 contract_key: ContractInstanceId::new([7u8; 32]),
1862 client_id: 42,
1863 }],
1864 contract_states,
1865 system_metrics: Some(SystemMetrics {
1866 active_connections: 1,
1867 hosting_contracts: 1,
1868 }),
1869 connected_peers_detailed: vec![ConnectedPeerInfo {
1870 peer_id: "peer-x".to_string(),
1871 address: "10.0.0.1:31337".to_string(),
1872 }],
1873 };
1874
1875 let json = serde_json::to_string(&response).expect("must serialize to JSON");
1876 let parsed: serde_json::Value = serde_json::from_str(&json).expect("output is valid JSON");
1877
1878 let obj = parsed.as_object().expect("top-level must be object");
1880 assert_eq!(obj.len(), 6, "expected six top-level fields, got {obj:?}");
1881 assert_eq!(parsed["node_info"]["peer_id"], "peer-self");
1882 assert_eq!(parsed["network_info"]["active_connections"], 1);
1883 assert_eq!(parsed["subscriptions"][0]["client_id"], 42);
1884 assert_eq!(parsed["system_metrics"]["hosting_contracts"], 1);
1885 assert_eq!(parsed["connected_peers_detailed"][0]["peer_id"], "peer-x");
1886
1887 let states = parsed["contract_states"]
1888 .as_object()
1889 .expect("contract_states must be a JSON object");
1890 assert_eq!(states.len(), 1);
1891 assert_eq!(
1892 states["6kVs66bKaQAC6ohr8b43SvJ95r36tc2hnG7HezmaJHF9"]["subscribers"],
1893 3
1894 );
1895
1896 let bytes = bincode::serialize(&response).expect("bincode must serialize");
1900 let decoded: NodeDiagnosticsResponse =
1901 bincode::deserialize(&bytes).expect("bincode must round-trip");
1902 assert_eq!(
1903 decoded.contract_states.len(),
1904 1,
1905 "bincode round-trip preserves contract_states entries"
1906 );
1907 }
1908}
1909
1910#[cfg(test)]
1911mod client_request_test {
1912 use crate::client_api::{ContractRequest, TryFromFbs};
1913 use crate::contract_interface::UpdateData;
1914 use crate::generated::client_request::root_as_client_request;
1915
1916 const EXPECTED_ENCODED_CONTRACT_ID: &str = "6kVs66bKaQAC6ohr8b43SvJ95r36tc2hnG7HezmaJHF9";
1917
1918 #[test]
1919 fn test_build_contract_put_op_from_fbs() -> Result<(), Box<dyn std::error::Error>> {
1920 let put_req_op = vec![
1921 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,
1922 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,
1923 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,
1924 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,
1925 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,
1926 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,
1927 85, 111, 11, 171, 40, 85, 240, 177, 207, 81, 106, 157, 173, 90, 234, 2, 250, 253, 75,
1928 210, 62, 7, 6, 34, 75, 26, 229, 230, 107, 167, 17, 108, 8, 0, 0, 0, 1, 2, 3, 4, 5, 6,
1929 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,
1930 3, 4, 5, 6, 7, 8, 8, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8,
1931 ];
1932 let request = if let Ok(client_request) = root_as_client_request(&put_req_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::Put {
1941 contract,
1942 state,
1943 related_contracts: _,
1944 subscribe,
1945 blocking_subscribe,
1946 } => {
1947 assert_eq!(
1948 contract.to_string(),
1949 "WasmContainer([api=0.0.1](D8fdVLbRyMLw5mZtPRpWMFcrXGN2z8Nq8UGcLGPFBg2W))"
1950 );
1951 assert_eq!(contract.unwrap_v1().data.data(), &[1, 2, 3, 4, 5, 6, 7, 8]);
1952 assert_eq!(state.to_vec(), &[1, 2, 3, 4, 5, 6, 7, 8]);
1953 assert!(!subscribe);
1954 assert!(!blocking_subscribe);
1955 }
1956 _ => panic!("wrong contract request type"),
1957 }
1958
1959 Ok(())
1960 }
1961
1962 #[test]
1963 fn test_build_contract_get_op_from_fbs() -> Result<(), Box<dyn std::error::Error>> {
1964 let get_req_op = vec![
1965 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,
1966 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,
1967 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,
1968 4, 0, 0, 0, 32, 0, 0, 0, 85, 111, 11, 171, 40, 85, 240, 177, 207, 81, 106, 157, 173,
1969 90, 234, 2, 250, 253, 75, 210, 62, 7, 6, 34, 75, 26, 229, 230, 107, 167, 17, 108,
1970 ];
1971 let request = if let Ok(client_request) = root_as_client_request(&get_req_op) {
1972 let contract_request = client_request.client_request_as_contract_request().unwrap();
1973 ContractRequest::try_decode_fbs(&contract_request)?
1974 } else {
1975 panic!("failed to decode client request")
1976 };
1977
1978 match request {
1979 ContractRequest::Get {
1980 key,
1981 return_contract_code: fetch_contract,
1982 subscribe,
1983 blocking_subscribe,
1984 } => {
1985 assert_eq!(key.encode(), EXPECTED_ENCODED_CONTRACT_ID);
1986 assert!(!fetch_contract);
1987 assert!(!subscribe);
1988 assert!(!blocking_subscribe);
1989 }
1990 _ => panic!("wrong contract request type"),
1991 }
1992
1993 Ok(())
1994 }
1995
1996 #[test]
1997 fn test_build_contract_update_op_from_fbs() -> Result<(), Box<dyn std::error::Error>> {
1998 let update_op = vec![
1999 4, 0, 0, 0, 220, 255, 255, 255, 8, 0, 0, 0, 0, 0, 0, 1, 232, 255, 255, 255, 8, 0, 0, 0,
2000 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,
2001 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,
2002 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,
2003 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,
2004 85, 240, 177, 207, 81, 106, 157, 173, 90, 234, 2, 250, 253, 75, 210, 62, 7, 6, 34, 75,
2005 26, 229, 230, 107, 167, 17, 108,
2006 ];
2007 let request = if let Ok(client_request) = root_as_client_request(&update_op) {
2008 let contract_request = client_request.client_request_as_contract_request().unwrap();
2009 ContractRequest::try_decode_fbs(&contract_request)?
2010 } else {
2011 panic!("failed to decode client request")
2012 };
2013
2014 match request {
2015 ContractRequest::Update { key, data } => {
2016 assert_eq!(
2017 key.encoded_contract_id(),
2018 "6kVs66bKaQAC6ohr8b43SvJ95r36tc2hnG7HezmaJHF9"
2019 );
2020 match data {
2021 UpdateData::Delta(delta) => {
2022 assert_eq!(delta.to_vec(), &[1, 2, 3, 4, 5, 6, 7, 8])
2023 }
2024 _ => panic!("wrong update data type"),
2025 }
2026 }
2027 _ => panic!("wrong contract request type"),
2028 }
2029
2030 Ok(())
2031 }
2032
2033 #[test]
2040 fn fbs_decode_rejects_unknown_contract_discriminant() {
2041 use crate::generated::client_request::{
2042 finish_client_request_buffer, ClientRequest as FbsClientRequest, ClientRequestArgs,
2043 ClientRequestType, ContractRequest as FbsContractRequest, ContractRequestArgs,
2044 ContractRequestType, DelegateKey as FbsDelegateKey, DelegateKeyArgs,
2045 UnregisterDelegate, UnregisterDelegateArgs,
2046 };
2047
2048 let mut b = flatbuffers::FlatBufferBuilder::new();
2049 let key = b.create_vector(&[0u8; 32]);
2052 let code_hash = b.create_vector(&[0u8; 32]);
2053 let dk = FbsDelegateKey::create(
2054 &mut b,
2055 &DelegateKeyArgs {
2056 key: Some(key),
2057 code_hash: Some(code_hash),
2058 },
2059 );
2060 let dummy = UnregisterDelegate::create(&mut b, &UnregisterDelegateArgs { key: Some(dk) });
2061 let contract = FbsContractRequest::create(
2063 &mut b,
2064 &ContractRequestArgs {
2065 contract_request_type: ContractRequestType(99),
2066 contract_request: Some(dummy.as_union_value()),
2067 },
2068 );
2069 let client = FbsClientRequest::create(
2070 &mut b,
2071 &ClientRequestArgs {
2072 client_request_type: ClientRequestType::ContractRequest,
2073 client_request: Some(contract.as_union_value()),
2074 },
2075 );
2076 finish_client_request_buffer(&mut b, client);
2077 let bytes = b.finished_data().to_vec();
2078
2079 let client =
2080 root_as_client_request(&bytes).expect("verifier accepts an unknown union discriminant");
2081 let fbs_contract = client
2082 .client_request_as_contract_request()
2083 .expect("client_request is a ContractRequest");
2084 assert!(
2085 ContractRequest::try_decode_fbs(&fbs_contract).is_err(),
2086 "an unknown ContractRequestType discriminant must be a clean \
2087 per-request error, never a panic that downs the connection handler"
2088 );
2089 }
2090}
2091
2092#[cfg(test)]
2108mod delegate_request_wire_format {
2109 use super::DelegateRequest;
2110 use crate::code_hash::CodeHash;
2111 use crate::prelude::{
2112 ApplicationMessage, Delegate, DelegateCode, DelegateContainer, DelegateKey,
2113 DelegateWasmAPIVersion, InboundDelegateMsg, Parameters,
2114 };
2115
2116 fn sample_container() -> DelegateContainer {
2117 let code = DelegateCode::from(vec![1u8, 2, 3, 4]);
2118 let params = Parameters::from(vec![9u8, 8, 7]);
2119 DelegateContainer::Wasm(DelegateWasmAPIVersion::V1(Delegate::from((&code, ¶ms))))
2120 }
2121
2122 fn sample_key(fill: u8) -> DelegateKey {
2123 DelegateKey::new([fill; 32], CodeHash::new([fill.wrapping_add(1); 32]))
2124 }
2125
2126 fn sample_app_messages() -> DelegateRequest<'static> {
2131 DelegateRequest::ApplicationMessages {
2132 key: DelegateKey::new([0x11; 32], CodeHash::new([0x22; 32])),
2133 params: Parameters::from(vec![0xDE, 0xAD, 0xBE, 0xEF]),
2134 inbound: vec![InboundDelegateMsg::ApplicationMessage(
2135 ApplicationMessage::new(vec![0x01, 0x02, 0x03]),
2136 )],
2137 }
2138 }
2139
2140 fn sample_register() -> DelegateRequest<'static> {
2141 DelegateRequest::RegisterDelegate {
2142 delegate: sample_container(),
2143 cipher: [0x55; 32],
2144 nonce: [0x66; 24],
2145 }
2146 }
2147
2148 fn sample_unregister() -> DelegateRequest<'static> {
2149 DelegateRequest::UnregisterDelegate(DelegateKey::new([0x11; 32], CodeHash::new([0x22; 32])))
2150 }
2151
2152 fn sample_register_with_predecessors() -> DelegateRequest<'static> {
2153 DelegateRequest::RegisterDelegateWithPredecessors {
2154 delegate: sample_container(),
2155 cipher: [0x33; 32],
2156 nonce: [0x44; 24],
2157 predecessors: vec![sample_key(0xA0), sample_key(0xB0), sample_key(0xC0)],
2158 }
2159 }
2160
2161 #[test]
2178 fn wire_format_is_frozen() {
2179 const APP_MESSAGES: &[u8] = &[
2181 0, 0, 0, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
2182 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 34, 34, 34, 34, 34, 34, 34, 34, 34,
2183 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
2184 34, 4, 0, 0, 0, 0, 0, 0, 0, 222, 173, 190, 239, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
2185 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2186 ];
2187 const REGISTER: &[u8] = &[
2188 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 9, 8, 7, 4, 0, 0, 0, 0, 0,
2189 0, 0, 1, 2, 3, 4, 99, 120, 29, 23, 20, 37, 163, 99, 18, 250, 5, 141, 135, 18, 213, 208,
2190 81, 53, 169, 145, 236, 32, 53, 28, 233, 214, 92, 219, 25, 160, 84, 50, 88, 111, 44, 39,
2191 24, 219, 97, 92, 222, 20, 205, 248, 149, 154, 214, 38, 193, 144, 31, 141, 32, 222, 49,
2192 197, 66, 237, 16, 98, 165, 72, 6, 11, 99, 120, 29, 23, 20, 37, 163, 99, 18, 250, 5,
2193 141, 135, 18, 213, 208, 81, 53, 169, 145, 236, 32, 53, 28, 233, 214, 92, 219, 25, 160,
2194 84, 50, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
2195 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 102, 102, 102, 102, 102, 102, 102, 102,
2196 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102,
2197 ];
2198 const UNREGISTER: &[u8] = &[
2199 2, 0, 0, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
2200 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 34, 34, 34, 34, 34, 34, 34, 34, 34,
2201 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
2202 34,
2203 ];
2204 const REGISTER_WITH_PREDECESSORS: &[u8] = &[
2206 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 9, 8, 7, 4, 0, 0, 0, 0, 0,
2207 0, 0, 1, 2, 3, 4, 99, 120, 29, 23, 20, 37, 163, 99, 18, 250, 5, 141, 135, 18, 213, 208,
2208 81, 53, 169, 145, 236, 32, 53, 28, 233, 214, 92, 219, 25, 160, 84, 50, 88, 111, 44, 39,
2209 24, 219, 97, 92, 222, 20, 205, 248, 149, 154, 214, 38, 193, 144, 31, 141, 32, 222, 49,
2210 197, 66, 237, 16, 98, 165, 72, 6, 11, 99, 120, 29, 23, 20, 37, 163, 99, 18, 250, 5,
2211 141, 135, 18, 213, 208, 81, 53, 169, 145, 236, 32, 53, 28, 233, 214, 92, 219, 25, 160,
2212 84, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
2213 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
2214 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 3, 0, 0, 0, 0, 0, 0, 0, 160,
2215 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160,
2216 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 161, 161, 161,
2217 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161,
2218 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 176, 176, 176, 176, 176,
2219 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176,
2220 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 177, 177, 177, 177, 177, 177, 177,
2221 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177,
2222 177, 177, 177, 177, 177, 177, 177, 177, 192, 192, 192, 192, 192, 192, 192, 192, 192,
2223 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
2224 192, 192, 192, 192, 192, 192, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193,
2225 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193,
2226 193, 193, 193, 193,
2227 ];
2228
2229 assert_eq!(
2231 bincode::serialize(&sample_app_messages()).unwrap(),
2232 APP_MESSAGES,
2233 "ApplicationMessages (tag 0) encoding changed"
2234 );
2235 assert_eq!(
2236 bincode::serialize(&sample_register()).unwrap(),
2237 REGISTER,
2238 "RegisterDelegate (tag 1) encoding changed"
2239 );
2240 assert_eq!(
2241 bincode::serialize(&sample_unregister()).unwrap(),
2242 UNREGISTER,
2243 "UnregisterDelegate (tag 2) encoding changed"
2244 );
2245 assert_eq!(
2246 bincode::serialize(&sample_register_with_predecessors()).unwrap(),
2247 REGISTER_WITH_PREDECESSORS,
2248 "RegisterDelegateWithPredecessors (tag 3) encoding changed"
2249 );
2250
2251 assert_eq!(APP_MESSAGES[..4], 0u32.to_le_bytes());
2254 assert_eq!(REGISTER[..4], 1u32.to_le_bytes());
2255 assert_eq!(UNREGISTER[..4], 2u32.to_le_bytes());
2256 assert_eq!(REGISTER_WITH_PREDECESSORS[..4], 3u32.to_le_bytes());
2257
2258 assert!(matches!(
2261 bincode::deserialize::<DelegateRequest>(APP_MESSAGES).unwrap(),
2262 DelegateRequest::ApplicationMessages { .. }
2263 ));
2264 assert!(matches!(
2265 bincode::deserialize::<DelegateRequest>(REGISTER).unwrap(),
2266 DelegateRequest::RegisterDelegate { .. }
2267 ));
2268 assert!(matches!(
2269 bincode::deserialize::<DelegateRequest>(UNREGISTER).unwrap(),
2270 DelegateRequest::UnregisterDelegate(_)
2271 ));
2272 assert!(matches!(
2273 bincode::deserialize::<DelegateRequest>(REGISTER_WITH_PREDECESSORS).unwrap(),
2274 DelegateRequest::RegisterDelegateWithPredecessors { .. }
2275 ));
2276 }
2277
2278 #[test]
2282 fn register_with_predecessors_tag_is_three() {
2283 let req = DelegateRequest::RegisterDelegateWithPredecessors {
2284 delegate: sample_container(),
2285 cipher: [0; 32],
2286 nonce: [0; 24],
2287 predecessors: vec![],
2288 };
2289 assert_eq!(
2290 bincode::serialize(&req).unwrap()[..4],
2291 3u32.to_le_bytes(),
2292 "RegisterDelegateWithPredecessors must be the 4th variant (tag 3)"
2293 );
2294 }
2295
2296 #[test]
2299 fn register_with_predecessors_round_trips() {
2300 let predecessors = vec![sample_key(0xA0), sample_key(0xB0), sample_key(0xC0)];
2301 let delegate = sample_container();
2302 let expected_key = delegate.key().clone();
2303
2304 let req = DelegateRequest::RegisterDelegateWithPredecessors {
2305 delegate,
2306 cipher: [0x33; 32],
2307 nonce: [0x44; 24],
2308 predecessors: predecessors.clone(),
2309 };
2310
2311 let bytes = bincode::serialize(&req).unwrap();
2312 let decoded: DelegateRequest = bincode::deserialize(&bytes).unwrap();
2313
2314 match decoded {
2315 DelegateRequest::RegisterDelegateWithPredecessors {
2316 delegate,
2317 cipher,
2318 nonce,
2319 predecessors: got,
2320 } => {
2321 assert_eq!(delegate.key(), &expected_key, "delegate preserved");
2322 assert_eq!(cipher, [0x33; 32], "cipher preserved");
2323 assert_eq!(nonce, [0x44; 24], "nonce preserved");
2324 assert_eq!(
2325 got, predecessors,
2326 "full predecessor list preserved in order"
2327 );
2328 }
2329 other => panic!("round-trip produced the wrong variant: {other:?}"),
2330 }
2331 }
2332
2333 #[test]
2336 fn key_returns_the_new_delegate() {
2337 let delegate = sample_container();
2338 let expected_key = delegate.key().clone();
2339 let req = DelegateRequest::RegisterDelegateWithPredecessors {
2340 delegate,
2341 cipher: [0; 32],
2342 nonce: [0; 24],
2343 predecessors: vec![sample_key(0xA0)],
2344 };
2345 assert_eq!(req.key(), &expected_key);
2346 }
2347
2348 #[test]
2357 fn fbs_decode_rejects_unknown_discriminant() {
2358 use crate::client_api::TryFromFbs;
2359 use crate::generated::client_request::{
2360 finish_client_request_buffer, root_as_client_request,
2361 ClientRequest as FbsClientRequest, ClientRequestArgs, ClientRequestType,
2362 DelegateKey as FbsDelegateKey, DelegateKeyArgs, DelegateRequest as FbsDelegateRequest,
2363 DelegateRequestArgs, DelegateRequestType, UnregisterDelegate, UnregisterDelegateArgs,
2364 };
2365
2366 let mut b = flatbuffers::FlatBufferBuilder::new();
2367 let key = b.create_vector(&[0u8; 32]);
2369 let code_hash = b.create_vector(&[0u8; 32]);
2370 let dk = FbsDelegateKey::create(
2371 &mut b,
2372 &DelegateKeyArgs {
2373 key: Some(key),
2374 code_hash: Some(code_hash),
2375 },
2376 );
2377 let unreg = UnregisterDelegate::create(&mut b, &UnregisterDelegateArgs { key: Some(dk) });
2378 let dreq = FbsDelegateRequest::create(
2381 &mut b,
2382 &DelegateRequestArgs {
2383 delegate_request_type: DelegateRequestType(99),
2384 delegate_request: Some(unreg.as_union_value()),
2385 },
2386 );
2387 let creq = FbsClientRequest::create(
2388 &mut b,
2389 &ClientRequestArgs {
2390 client_request_type: ClientRequestType::DelegateRequest,
2391 client_request: Some(dreq.as_union_value()),
2392 },
2393 );
2394 finish_client_request_buffer(&mut b, creq);
2395 let bytes = b.finished_data().to_vec();
2396
2397 let client =
2398 root_as_client_request(&bytes).expect("verifier accepts an unknown union discriminant");
2399 let fbs_delegate = client
2400 .client_request_as_delegate_request()
2401 .expect("client_request is a DelegateRequest");
2402 let decoded = DelegateRequest::try_decode_fbs(&fbs_delegate);
2403 assert!(
2404 decoded.is_err(),
2405 "an unknown DelegateRequestType discriminant must be a clean \
2406 per-request error, never a panic that downs the connection handler"
2407 );
2408 }
2409}