1use std::collections::BTreeMap;
5
6use linera_base::{
7 crypto::{
8 AccountPublicKey, AccountSignature, CryptoError, CryptoHash, ValidatorPublicKey,
9 ValidatorSignature,
10 },
11 data_types::{BlobContent, BlockHeight, NetworkDescription},
12 ensure,
13 identifiers::{AccountOwner, BlobId, ChainId, StreamId},
14};
15use linera_chain::{
16 data_types::{BlockProposal, LiteValue, ProposalContent},
17 types::{
18 Certificate, CertificateKind, ConfirmedBlock, ConfirmedBlockCertificate, LiteCertificate,
19 Timeout, TimeoutCertificate, ValidatedBlock, ValidatedBlockCertificate,
20 },
21};
22use linera_core::{
23 data_types::{
24 CertificatesByHeightRequest, ChainInfoQuery, ChainInfoResponse, CrossChainRequest,
25 },
26 node::NodeError,
27 worker::Notification,
28};
29use thiserror::Error;
30use tonic::{Code, Status};
31
32use super::api::{self, PendingBlobRequest};
33use crate::{
34 HandleConfirmedCertificateRequest, HandleLiteCertRequest, HandleTimeoutCertificateRequest,
35 HandleValidatedCertificateRequest,
36};
37
38#[derive(Error, Debug)]
39pub enum GrpcProtoConversionError {
40 #[error(transparent)]
41 BincodeError(#[from] bincode::Error),
42 #[error("Conversion failed due to missing field")]
43 MissingField,
44 #[error("Signature error: {0}")]
45 SignatureError(ed25519_dalek::SignatureError),
46 #[error("Cryptographic error: {0}")]
47 CryptoError(#[from] CryptoError),
48 #[error("Inconsistent outer/inner chain IDs")]
49 InconsistentChainId,
50 #[error("Unrecognized certificate type")]
51 InvalidCertificateType,
52 #[error(
53 "Chain info queries asking for committees are reserved for local clients; \
54 validators serve committees via the event stream and the committee blob store"
55 )]
56 UnsupportedRequestCommittees,
57}
58
59impl From<ed25519_dalek::SignatureError> for GrpcProtoConversionError {
60 fn from(signature_error: ed25519_dalek::SignatureError) -> Self {
61 GrpcProtoConversionError::SignatureError(signature_error)
62 }
63}
64
65fn try_proto_convert<S, T>(t: Option<T>) -> Result<S, GrpcProtoConversionError>
67where
68 T: TryInto<S, Error = GrpcProtoConversionError>,
69{
70 t.ok_or(GrpcProtoConversionError::MissingField)?.try_into()
71}
72
73impl From<GrpcProtoConversionError> for Status {
74 fn from(error: GrpcProtoConversionError) -> Self {
75 Status::new(Code::InvalidArgument, error.to_string())
76 }
77}
78
79impl From<GrpcProtoConversionError> for NodeError {
80 fn from(error: GrpcProtoConversionError) -> Self {
81 NodeError::GrpcError {
82 error: error.to_string(),
83 }
84 }
85}
86
87impl From<linera_version::CrateVersion> for api::CrateVersion {
88 fn from(
89 linera_version::CrateVersion {
90 major,
91 minor,
92 patch,
93 }: linera_version::CrateVersion,
94 ) -> Self {
95 Self {
96 major,
97 minor,
98 patch,
99 }
100 }
101}
102
103impl From<api::CrateVersion> for linera_version::CrateVersion {
104 fn from(
105 api::CrateVersion {
106 major,
107 minor,
108 patch,
109 }: api::CrateVersion,
110 ) -> Self {
111 Self {
112 major,
113 minor,
114 patch,
115 }
116 }
117}
118
119impl From<linera_version::VersionInfo> for api::VersionInfo {
120 fn from(version_info: linera_version::VersionInfo) -> api::VersionInfo {
121 api::VersionInfo {
122 crate_version: Some(version_info.crate_version.value.into()),
123 git_commit: version_info.git_commit.into(),
124 git_dirty: version_info.git_dirty,
125 rpc_hash: version_info.rpc_hash.into(),
126 graphql_hash: version_info.graphql_hash.into(),
127 wit_hash: version_info.wit_hash.into(),
128 }
129 }
130}
131
132impl From<api::VersionInfo> for linera_version::VersionInfo {
133 fn from(version_info: api::VersionInfo) -> linera_version::VersionInfo {
134 linera_version::VersionInfo {
135 crate_version: linera_version::Pretty::new(
136 version_info
137 .crate_version
138 .unwrap_or(api::CrateVersion {
139 major: 0,
140 minor: 0,
141 patch: 0,
142 })
143 .into(),
144 ),
145 git_commit: version_info.git_commit.into(),
146 git_dirty: version_info.git_dirty,
147 rpc_hash: version_info.rpc_hash.into(),
148 graphql_hash: version_info.graphql_hash.into(),
149 wit_hash: version_info.wit_hash.into(),
150 }
151 }
152}
153
154impl From<NetworkDescription> for api::NetworkDescription {
155 fn from(
156 NetworkDescription {
157 name,
158 genesis_config_hash,
159 genesis_timestamp,
160 genesis_committee_blob_hash,
161 admin_chain_id,
162 }: NetworkDescription,
163 ) -> Self {
164 Self {
165 name,
166 genesis_config_hash: Some(genesis_config_hash.into()),
167 genesis_timestamp: genesis_timestamp.micros(),
168 admin_chain_id: Some(admin_chain_id.into()),
169 genesis_committee_blob_hash: Some(genesis_committee_blob_hash.into()),
170 }
171 }
172}
173
174impl TryFrom<api::NetworkDescription> for NetworkDescription {
175 type Error = GrpcProtoConversionError;
176
177 fn try_from(
178 api::NetworkDescription {
179 name,
180 genesis_config_hash,
181 genesis_timestamp,
182 genesis_committee_blob_hash,
183 admin_chain_id,
184 }: api::NetworkDescription,
185 ) -> Result<Self, Self::Error> {
186 Ok(Self {
187 name,
188 genesis_config_hash: try_proto_convert(genesis_config_hash)?,
189 genesis_timestamp: genesis_timestamp.into(),
190 admin_chain_id: try_proto_convert(admin_chain_id)?,
191 genesis_committee_blob_hash: try_proto_convert(genesis_committee_blob_hash)?,
192 })
193 }
194}
195
196impl TryFrom<Notification> for api::Notification {
197 type Error = GrpcProtoConversionError;
198
199 fn try_from(notification: Notification) -> Result<Self, Self::Error> {
200 Ok(Self {
201 chain_id: Some(notification.chain_id.into()),
202 reason: bincode::serialize(¬ification.reason)?,
203 })
204 }
205}
206
207impl TryFrom<api::Notification> for Option<Notification> {
208 type Error = GrpcProtoConversionError;
209
210 fn try_from(notification: api::Notification) -> Result<Self, Self::Error> {
211 if notification.chain_id.is_none() && notification.reason.is_empty() {
212 Ok(None)
213 } else {
214 Ok(Some(Notification {
215 chain_id: try_proto_convert(notification.chain_id)?,
216 reason: bincode::deserialize(¬ification.reason)?,
217 }))
218 }
219 }
220}
221
222impl TryFrom<ChainInfoResponse> for api::ChainInfoResult {
223 type Error = GrpcProtoConversionError;
224
225 fn try_from(chain_info_response: ChainInfoResponse) -> Result<Self, Self::Error> {
226 let response = chain_info_response.try_into()?;
227 Ok(api::ChainInfoResult {
228 inner: Some(api::chain_info_result::Inner::ChainInfoResponse(response)),
229 })
230 }
231}
232
233impl TryFrom<NodeError> for api::ChainInfoResult {
234 type Error = GrpcProtoConversionError;
235
236 fn try_from(node_error: NodeError) -> Result<Self, Self::Error> {
237 let error = bincode::serialize(&node_error)?;
238 Ok(api::ChainInfoResult {
239 inner: Some(api::chain_info_result::Inner::Error(error)),
240 })
241 }
242}
243
244impl TryFrom<BlockProposal> for api::BlockProposal {
245 type Error = GrpcProtoConversionError;
246
247 fn try_from(block_proposal: BlockProposal) -> Result<Self, Self::Error> {
248 Ok(Self {
249 chain_id: Some(block_proposal.content.block.chain_id.into()),
250 content: bincode::serialize(&block_proposal.content)?,
251 owner: Some(block_proposal.owner().try_into()?),
252 signature: Some(block_proposal.signature.into()),
253 original_proposal: block_proposal
254 .original_proposal
255 .map(|cert| bincode::serialize(&cert))
256 .transpose()?,
257 })
258 }
259}
260
261impl TryFrom<api::BlockProposal> for BlockProposal {
262 type Error = GrpcProtoConversionError;
263
264 fn try_from(block_proposal: api::BlockProposal) -> Result<Self, Self::Error> {
265 let content: ProposalContent = bincode::deserialize(&block_proposal.content)?;
266 ensure!(
267 Some(content.block.chain_id.into()) == block_proposal.chain_id,
268 GrpcProtoConversionError::InconsistentChainId
269 );
270 Ok(Self {
271 content,
272 signature: try_proto_convert(block_proposal.signature)?,
273 original_proposal: block_proposal
274 .original_proposal
275 .map(|bytes| bincode::deserialize(&bytes))
276 .transpose()?,
277 })
278 }
279}
280
281impl TryFrom<api::CrossChainRequest> for CrossChainRequest {
282 type Error = GrpcProtoConversionError;
283
284 fn try_from(cross_chain_request: api::CrossChainRequest) -> Result<Self, Self::Error> {
285 use api::cross_chain_request::Inner;
286
287 let ccr = match cross_chain_request
288 .inner
289 .ok_or(GrpcProtoConversionError::MissingField)?
290 {
291 Inner::UpdateRecipient(api::UpdateRecipient {
292 sender,
293 recipient,
294 bundles,
295 previous_height,
296 }) => CrossChainRequest::UpdateRecipient {
297 sender: try_proto_convert(sender)?,
298 recipient: try_proto_convert(recipient)?,
299 bundles: bincode::deserialize(&bundles)?,
300 previous_height: previous_height.map(Into::into),
301 },
302 Inner::ConfirmUpdatedRecipient(api::ConfirmUpdatedRecipient {
303 sender,
304 recipient,
305 latest_height,
306 }) => CrossChainRequest::ConfirmUpdatedRecipient {
307 sender: try_proto_convert(sender)?,
308 recipient: try_proto_convert(recipient)?,
309 latest_height: latest_height
310 .ok_or(GrpcProtoConversionError::MissingField)?
311 .into(),
312 },
313 Inner::RevertConfirm(api::RevertConfirm {
314 sender,
315 recipient,
316 retransmit_from,
317 }) => CrossChainRequest::RevertConfirm {
318 sender: try_proto_convert(sender)?,
319 recipient: try_proto_convert(recipient)?,
320 retransmit_from: retransmit_from
321 .ok_or(GrpcProtoConversionError::MissingField)?
322 .into(),
323 },
324 };
325 Ok(ccr)
326 }
327}
328
329impl TryFrom<CrossChainRequest> for api::CrossChainRequest {
330 type Error = GrpcProtoConversionError;
331
332 fn try_from(cross_chain_request: CrossChainRequest) -> Result<Self, Self::Error> {
333 use api::cross_chain_request::Inner;
334
335 let inner = match cross_chain_request {
336 CrossChainRequest::UpdateRecipient {
337 sender,
338 recipient,
339 bundles,
340 previous_height,
341 } => Inner::UpdateRecipient(api::UpdateRecipient {
342 sender: Some(sender.into()),
343 recipient: Some(recipient.into()),
344 bundles: bincode::serialize(&bundles)?,
345 previous_height: previous_height.map(Into::into),
346 }),
347 CrossChainRequest::ConfirmUpdatedRecipient {
348 sender,
349 recipient,
350 latest_height,
351 } => Inner::ConfirmUpdatedRecipient(api::ConfirmUpdatedRecipient {
352 sender: Some(sender.into()),
353 recipient: Some(recipient.into()),
354 latest_height: Some(latest_height.into()),
355 }),
356 CrossChainRequest::RevertConfirm {
357 sender,
358 recipient,
359 retransmit_from,
360 } => Inner::RevertConfirm(api::RevertConfirm {
361 sender: Some(sender.into()),
362 recipient: Some(recipient.into()),
363 retransmit_from: Some(retransmit_from.into()),
364 }),
365 };
366 Ok(Self { inner: Some(inner) })
367 }
368}
369
370impl TryFrom<api::LiteCertificate> for HandleLiteCertRequest<'_> {
371 type Error = GrpcProtoConversionError;
372
373 fn try_from(certificate: api::LiteCertificate) -> Result<Self, Self::Error> {
374 let kind = if certificate.kind == api::CertificateKind::Validated as i32 {
375 CertificateKind::Validated
376 } else if certificate.kind == api::CertificateKind::Confirmed as i32 {
377 CertificateKind::Confirmed
378 } else if certificate.kind == api::CertificateKind::Timeout as i32 {
379 CertificateKind::Timeout
380 } else {
381 return Err(GrpcProtoConversionError::InvalidCertificateType);
382 };
383
384 let value = LiteValue {
385 value_hash: CryptoHash::try_from(certificate.hash.as_slice())?,
386 chain_id: try_proto_convert(certificate.chain_id)?,
387 kind,
388 };
389 let signatures = bincode::deserialize(&certificate.signatures)?;
390 let round = bincode::deserialize(&certificate.round)?;
391 Ok(Self {
392 certificate: LiteCertificate::new(value, round, signatures),
393 wait_for_outgoing_messages: certificate.wait_for_outgoing_messages,
394 })
395 }
396}
397
398impl TryFrom<HandleLiteCertRequest<'_>> for api::LiteCertificate {
399 type Error = GrpcProtoConversionError;
400
401 fn try_from(request: HandleLiteCertRequest) -> Result<Self, Self::Error> {
402 Ok(Self {
403 hash: request.certificate.value.value_hash.as_bytes().to_vec(),
404 round: bincode::serialize(&request.certificate.round)?,
405 chain_id: Some(request.certificate.value.chain_id.into()),
406 signatures: bincode::serialize(&request.certificate.signatures)?,
407 wait_for_outgoing_messages: request.wait_for_outgoing_messages,
408 kind: request.certificate.value.kind as i32,
409 })
410 }
411}
412
413impl TryFrom<api::HandleTimeoutCertificateRequest> for HandleTimeoutCertificateRequest {
414 type Error = GrpcProtoConversionError;
415
416 fn try_from(cert_request: api::HandleTimeoutCertificateRequest) -> Result<Self, Self::Error> {
417 let certificate: TimeoutCertificate = cert_request
418 .certificate
419 .ok_or(GrpcProtoConversionError::MissingField)?
420 .try_into()?;
421
422 let req_chain_id: ChainId = cert_request
423 .chain_id
424 .ok_or(GrpcProtoConversionError::MissingField)?
425 .try_into()?;
426
427 ensure!(
428 certificate.inner().chain_id() == req_chain_id,
429 GrpcProtoConversionError::InconsistentChainId
430 );
431 Ok(HandleTimeoutCertificateRequest { certificate })
432 }
433}
434
435impl TryFrom<api::HandleValidatedCertificateRequest> for HandleValidatedCertificateRequest {
436 type Error = GrpcProtoConversionError;
437
438 fn try_from(cert_request: api::HandleValidatedCertificateRequest) -> Result<Self, Self::Error> {
439 let certificate: ValidatedBlockCertificate = cert_request
440 .certificate
441 .ok_or(GrpcProtoConversionError::MissingField)?
442 .try_into()?;
443
444 let req_chain_id: ChainId = cert_request
445 .chain_id
446 .ok_or(GrpcProtoConversionError::MissingField)?
447 .try_into()?;
448
449 ensure!(
450 certificate.inner().chain_id() == req_chain_id,
451 GrpcProtoConversionError::InconsistentChainId
452 );
453 Ok(HandleValidatedCertificateRequest { certificate })
454 }
455}
456
457impl TryFrom<api::HandleConfirmedCertificateRequest> for HandleConfirmedCertificateRequest {
458 type Error = GrpcProtoConversionError;
459
460 fn try_from(cert_request: api::HandleConfirmedCertificateRequest) -> Result<Self, Self::Error> {
461 let certificate: ConfirmedBlockCertificate = cert_request
462 .certificate
463 .ok_or(GrpcProtoConversionError::MissingField)?
464 .try_into()?;
465
466 let req_chain_id: ChainId = cert_request
467 .chain_id
468 .ok_or(GrpcProtoConversionError::MissingField)?
469 .try_into()?;
470
471 ensure!(
472 certificate.inner().chain_id() == req_chain_id,
473 GrpcProtoConversionError::InconsistentChainId
474 );
475 Ok(HandleConfirmedCertificateRequest {
476 certificate,
477 wait_for_outgoing_messages: cert_request.wait_for_outgoing_messages,
478 })
479 }
480}
481
482impl TryFrom<HandleConfirmedCertificateRequest> for api::HandleConfirmedCertificateRequest {
483 type Error = GrpcProtoConversionError;
484
485 fn try_from(request: HandleConfirmedCertificateRequest) -> Result<Self, Self::Error> {
486 Ok(Self {
487 chain_id: Some(request.certificate.inner().chain_id().into()),
488 certificate: Some(request.certificate.try_into()?),
489 wait_for_outgoing_messages: request.wait_for_outgoing_messages,
490 })
491 }
492}
493
494impl TryFrom<HandleValidatedCertificateRequest> for api::HandleValidatedCertificateRequest {
495 type Error = GrpcProtoConversionError;
496
497 fn try_from(request: HandleValidatedCertificateRequest) -> Result<Self, Self::Error> {
498 Ok(Self {
499 chain_id: Some(request.certificate.inner().chain_id().into()),
500 certificate: Some(request.certificate.try_into()?),
501 })
502 }
503}
504
505impl TryFrom<HandleTimeoutCertificateRequest> for api::HandleTimeoutCertificateRequest {
506 type Error = GrpcProtoConversionError;
507
508 fn try_from(request: HandleTimeoutCertificateRequest) -> Result<Self, Self::Error> {
509 Ok(Self {
510 chain_id: Some(request.certificate.inner().chain_id().into()),
511 certificate: Some(request.certificate.try_into()?),
512 })
513 }
514}
515
516impl TryFrom<api::Certificate> for TimeoutCertificate {
517 type Error = GrpcProtoConversionError;
518
519 fn try_from(certificate: api::Certificate) -> Result<Self, Self::Error> {
520 let round = bincode::deserialize(&certificate.round)?;
521 let signatures = bincode::deserialize(&certificate.signatures)?;
522 let cert_type = certificate.kind;
523
524 if cert_type == api::CertificateKind::Timeout as i32 {
525 let value: Timeout = bincode::deserialize(&certificate.value)?;
526 Ok(TimeoutCertificate::new(value, round, signatures))
527 } else {
528 Err(GrpcProtoConversionError::InvalidCertificateType)
529 }
530 }
531}
532
533impl TryFrom<api::Certificate> for ValidatedBlockCertificate {
534 type Error = GrpcProtoConversionError;
535
536 fn try_from(certificate: api::Certificate) -> Result<Self, Self::Error> {
537 let round = bincode::deserialize(&certificate.round)?;
538 let signatures = bincode::deserialize(&certificate.signatures)?;
539 let cert_type = certificate.kind;
540
541 if cert_type == api::CertificateKind::Validated as i32 {
542 let value: ValidatedBlock = bincode::deserialize(&certificate.value)?;
543 Ok(ValidatedBlockCertificate::new(value, round, signatures))
544 } else {
545 Err(GrpcProtoConversionError::InvalidCertificateType)
546 }
547 }
548}
549
550impl TryFrom<api::Certificate> for ConfirmedBlockCertificate {
551 type Error = GrpcProtoConversionError;
552
553 fn try_from(certificate: api::Certificate) -> Result<Self, Self::Error> {
554 let round = bincode::deserialize(&certificate.round)?;
555 let signatures = bincode::deserialize(&certificate.signatures)?;
556 let cert_type = certificate.kind;
557
558 if cert_type == api::CertificateKind::Confirmed as i32 {
559 let value: ConfirmedBlock = bincode::deserialize(&certificate.value)?;
560 Ok(ConfirmedBlockCertificate::new(value, round, signatures))
561 } else {
562 Err(GrpcProtoConversionError::InvalidCertificateType)
563 }
564 }
565}
566
567impl TryFrom<TimeoutCertificate> for api::Certificate {
568 type Error = GrpcProtoConversionError;
569
570 fn try_from(certificate: TimeoutCertificate) -> Result<Self, Self::Error> {
571 let round = bincode::serialize(&certificate.round)?;
572 let signatures = bincode::serialize(certificate.signatures())?;
573
574 let value = bincode::serialize(certificate.value())?;
575
576 Ok(Self {
577 value,
578 round,
579 signatures,
580 kind: api::CertificateKind::Timeout as i32,
581 })
582 }
583}
584
585impl TryFrom<ConfirmedBlockCertificate> for api::Certificate {
586 type Error = GrpcProtoConversionError;
587
588 fn try_from(certificate: ConfirmedBlockCertificate) -> Result<Self, Self::Error> {
589 let round = bincode::serialize(&certificate.round)?;
590 let signatures = bincode::serialize(certificate.signatures())?;
591
592 let value = bincode::serialize(certificate.value())?;
593
594 Ok(Self {
595 value,
596 round,
597 signatures,
598 kind: api::CertificateKind::Confirmed as i32,
599 })
600 }
601}
602
603impl TryFrom<ValidatedBlockCertificate> for api::Certificate {
604 type Error = GrpcProtoConversionError;
605
606 fn try_from(certificate: ValidatedBlockCertificate) -> Result<Self, Self::Error> {
607 let round = bincode::serialize(&certificate.round)?;
608 let signatures = bincode::serialize(certificate.signatures())?;
609
610 let value = bincode::serialize(certificate.value())?;
611
612 Ok(Self {
613 value,
614 round,
615 signatures,
616 kind: api::CertificateKind::Validated as i32,
617 })
618 }
619}
620
621impl TryFrom<api::ChainInfoQuery> for ChainInfoQuery {
622 type Error = GrpcProtoConversionError;
623
624 fn try_from(chain_info_query: api::ChainInfoQuery) -> Result<Self, Self::Error> {
625 ensure!(
630 !chain_info_query.request_committees,
631 GrpcProtoConversionError::UnsupportedRequestCommittees
632 );
633 let request_sent_certificate_hashes_by_heights = chain_info_query
634 .request_sent_certificate_hashes_by_heights
635 .map(|heights| bincode::deserialize(&heights))
636 .transpose()?
637 .unwrap_or_default();
638 let request_leader_timeout = chain_info_query
639 .request_leader_timeout
640 .map(|height_and_round| bincode::deserialize(&height_and_round))
641 .transpose()?;
642
643 Ok(Self {
644 request_committees: chain_info_query.request_committees,
645 request_owner_balance: try_proto_convert(chain_info_query.request_owner_balance)?,
646 request_pending_message_bundles: chain_info_query.request_pending_message_bundles,
647 chain_id: try_proto_convert(chain_info_query.chain_id)?,
648 request_received_log_excluding_first_n: chain_info_query
649 .request_received_log_excluding_first_n,
650 test_next_block_height: chain_info_query.test_next_block_height.map(Into::into),
651 request_manager_values: chain_info_query.request_manager_values,
652 request_leader_timeout,
653 request_fallback: chain_info_query.request_fallback,
654 request_sent_certificate_hashes_by_heights,
655 request_sent_certificate_hashes_in_range: None,
656 create_network_actions: chain_info_query.create_network_actions.unwrap_or(true),
657 })
658 }
659}
660
661impl TryFrom<ChainInfoQuery> for api::ChainInfoQuery {
662 type Error = GrpcProtoConversionError;
663
664 fn try_from(chain_info_query: ChainInfoQuery) -> Result<Self, Self::Error> {
665 let request_sent_certificate_hashes_by_heights =
666 bincode::serialize(&chain_info_query.request_sent_certificate_hashes_by_heights)?;
667 let request_owner_balance = Some(chain_info_query.request_owner_balance.try_into()?);
668 let request_leader_timeout = chain_info_query
669 .request_leader_timeout
670 .map(|height_and_round| bincode::serialize(&height_and_round))
671 .transpose()?;
672
673 Ok(Self {
674 chain_id: Some(chain_info_query.chain_id.into()),
675 request_committees: chain_info_query.request_committees,
676 request_owner_balance,
677 request_pending_message_bundles: chain_info_query.request_pending_message_bundles,
678 test_next_block_height: chain_info_query.test_next_block_height.map(Into::into),
679 request_sent_certificate_hashes_by_heights: Some(
680 request_sent_certificate_hashes_by_heights,
681 ),
682 request_received_log_excluding_first_n: chain_info_query
683 .request_received_log_excluding_first_n,
684 request_manager_values: chain_info_query.request_manager_values,
685 request_leader_timeout,
686 request_fallback: chain_info_query.request_fallback,
687 create_network_actions: Some(chain_info_query.create_network_actions),
688 })
689 }
690}
691
692impl From<ChainId> for api::ChainId {
693 fn from(chain_id: ChainId) -> Self {
694 Self {
695 bytes: chain_id.0.as_bytes().to_vec(),
696 }
697 }
698}
699
700impl TryFrom<api::ChainId> for ChainId {
701 type Error = GrpcProtoConversionError;
702
703 fn try_from(chain_id: api::ChainId) -> Result<Self, Self::Error> {
704 Ok(ChainId::try_from(chain_id.bytes.as_slice())?)
705 }
706}
707
708impl From<AccountPublicKey> for api::AccountPublicKey {
709 fn from(public_key: AccountPublicKey) -> Self {
710 Self {
711 bytes: public_key.as_bytes(),
712 }
713 }
714}
715
716impl From<ValidatorPublicKey> for api::ValidatorPublicKey {
717 fn from(public_key: ValidatorPublicKey) -> Self {
718 Self {
719 bytes: public_key.as_bytes().to_vec(),
720 }
721 }
722}
723
724impl TryFrom<api::ValidatorPublicKey> for ValidatorPublicKey {
725 type Error = GrpcProtoConversionError;
726
727 fn try_from(public_key: api::ValidatorPublicKey) -> Result<Self, Self::Error> {
728 Ok(Self::from_bytes(public_key.bytes.as_slice())?)
729 }
730}
731
732impl TryFrom<api::AccountPublicKey> for AccountPublicKey {
733 type Error = GrpcProtoConversionError;
734
735 fn try_from(public_key: api::AccountPublicKey) -> Result<Self, Self::Error> {
736 Ok(Self::from_slice(public_key.bytes.as_slice())?)
737 }
738}
739
740impl From<AccountSignature> for api::AccountSignature {
741 fn from(signature: AccountSignature) -> Self {
742 Self {
743 bytes: signature.to_bytes(),
744 }
745 }
746}
747
748impl From<ValidatorSignature> for api::ValidatorSignature {
749 fn from(signature: ValidatorSignature) -> Self {
750 Self {
751 bytes: signature.as_bytes().to_vec(),
752 }
753 }
754}
755
756impl TryFrom<api::ValidatorSignature> for ValidatorSignature {
757 type Error = GrpcProtoConversionError;
758
759 fn try_from(signature: api::ValidatorSignature) -> Result<Self, Self::Error> {
760 Self::from_slice(signature.bytes.as_slice()).map_err(GrpcProtoConversionError::CryptoError)
761 }
762}
763
764impl TryFrom<api::AccountSignature> for AccountSignature {
765 type Error = GrpcProtoConversionError;
766
767 fn try_from(signature: api::AccountSignature) -> Result<Self, Self::Error> {
768 Ok(Self::from_slice(signature.bytes.as_slice())?)
769 }
770}
771
772impl TryFrom<ChainInfoResponse> for api::ChainInfoResponse {
773 type Error = GrpcProtoConversionError;
774
775 fn try_from(chain_info_response: ChainInfoResponse) -> Result<Self, Self::Error> {
776 Ok(Self {
777 chain_info: bincode::serialize(&chain_info_response.info)?,
778 signature: chain_info_response.signature.map(Into::into),
779 })
780 }
781}
782
783impl TryFrom<api::ChainInfoResponse> for ChainInfoResponse {
784 type Error = GrpcProtoConversionError;
785
786 fn try_from(chain_info_response: api::ChainInfoResponse) -> Result<Self, Self::Error> {
787 let signature = chain_info_response
788 .signature
789 .map(TryInto::try_into)
790 .transpose()?;
791 let info = bincode::deserialize(chain_info_response.chain_info.as_slice())?;
792 Ok(Self { info, signature })
793 }
794}
795
796impl TryFrom<(ChainId, BlobId)> for api::PendingBlobRequest {
797 type Error = GrpcProtoConversionError;
798
799 fn try_from((chain_id, blob_id): (ChainId, BlobId)) -> Result<Self, Self::Error> {
800 Ok(Self {
801 chain_id: Some(chain_id.into()),
802 blob_id: Some(blob_id.try_into()?),
803 })
804 }
805}
806
807impl TryFrom<api::PendingBlobRequest> for (ChainId, BlobId) {
808 type Error = GrpcProtoConversionError;
809
810 fn try_from(request: PendingBlobRequest) -> Result<Self, Self::Error> {
811 Ok((
812 try_proto_convert(request.chain_id)?,
813 try_proto_convert(request.blob_id)?,
814 ))
815 }
816}
817
818impl TryFrom<(ChainId, BlobContent)> for api::HandlePendingBlobRequest {
819 type Error = GrpcProtoConversionError;
820
821 fn try_from((chain_id, blob_content): (ChainId, BlobContent)) -> Result<Self, Self::Error> {
822 Ok(Self {
823 chain_id: Some(chain_id.into()),
824 blob: Some(blob_content.try_into()?),
825 })
826 }
827}
828
829impl TryFrom<api::HandlePendingBlobRequest> for (ChainId, BlobContent) {
830 type Error = GrpcProtoConversionError;
831
832 fn try_from(request: api::HandlePendingBlobRequest) -> Result<Self, Self::Error> {
833 Ok((
834 try_proto_convert(request.chain_id)?,
835 try_proto_convert(request.blob)?,
836 ))
837 }
838}
839
840impl TryFrom<BlobContent> for api::PendingBlobResult {
841 type Error = GrpcProtoConversionError;
842
843 fn try_from(blob: BlobContent) -> Result<Self, Self::Error> {
844 Ok(Self {
845 inner: Some(api::pending_blob_result::Inner::Blob(blob.try_into()?)),
846 })
847 }
848}
849
850impl TryFrom<NodeError> for api::PendingBlobResult {
851 type Error = GrpcProtoConversionError;
852
853 fn try_from(node_error: NodeError) -> Result<Self, Self::Error> {
854 let error = bincode::serialize(&node_error)?;
855 Ok(api::PendingBlobResult {
856 inner: Some(api::pending_blob_result::Inner::Error(error)),
857 })
858 }
859}
860
861impl From<BlockHeight> for api::BlockHeight {
862 fn from(block_height: BlockHeight) -> Self {
863 Self {
864 height: block_height.0,
865 }
866 }
867}
868
869impl From<api::BlockHeight> for BlockHeight {
870 fn from(block_height: api::BlockHeight) -> Self {
871 Self(block_height.height)
872 }
873}
874
875impl TryFrom<AccountOwner> for api::AccountOwner {
876 type Error = GrpcProtoConversionError;
877
878 fn try_from(account_owner: AccountOwner) -> Result<Self, Self::Error> {
879 Ok(Self {
880 bytes: bincode::serialize(&account_owner)?,
881 })
882 }
883}
884
885impl TryFrom<api::AccountOwner> for AccountOwner {
886 type Error = GrpcProtoConversionError;
887
888 fn try_from(account_owner: api::AccountOwner) -> Result<Self, Self::Error> {
889 Ok(bincode::deserialize(&account_owner.bytes)?)
890 }
891}
892
893impl TryFrom<api::BlobId> for BlobId {
894 type Error = GrpcProtoConversionError;
895
896 fn try_from(blob_id: api::BlobId) -> Result<Self, Self::Error> {
897 Ok(bincode::deserialize(blob_id.bytes.as_slice())?)
898 }
899}
900
901impl TryFrom<api::BlobIds> for Vec<BlobId> {
902 type Error = GrpcProtoConversionError;
903
904 fn try_from(blob_ids: api::BlobIds) -> Result<Self, Self::Error> {
905 Ok(blob_ids
906 .bytes
907 .into_iter()
908 .map(|x| bincode::deserialize(x.as_slice()))
909 .collect::<Result<_, _>>()?)
910 }
911}
912
913impl TryFrom<BlobId> for api::BlobId {
914 type Error = GrpcProtoConversionError;
915
916 fn try_from(blob_id: BlobId) -> Result<Self, Self::Error> {
917 Ok(Self {
918 bytes: bincode::serialize(&blob_id)?,
919 })
920 }
921}
922
923impl TryFrom<Vec<BlobId>> for api::BlobIds {
924 type Error = GrpcProtoConversionError;
925
926 fn try_from(blob_ids: Vec<BlobId>) -> Result<Self, Self::Error> {
927 let bytes = blob_ids
928 .into_iter()
929 .map(|blob_id| bincode::serialize(&blob_id))
930 .collect::<Result<_, _>>()?;
931 Ok(Self { bytes })
932 }
933}
934
935impl TryFrom<api::CryptoHash> for CryptoHash {
936 type Error = GrpcProtoConversionError;
937
938 fn try_from(hash: api::CryptoHash) -> Result<Self, Self::Error> {
939 Ok(CryptoHash::try_from(hash.bytes.as_slice())?)
940 }
941}
942
943impl TryFrom<BlobContent> for api::BlobContent {
944 type Error = GrpcProtoConversionError;
945
946 fn try_from(blob: BlobContent) -> Result<Self, Self::Error> {
947 Ok(Self {
948 bytes: bincode::serialize(&blob)?,
949 })
950 }
951}
952
953impl TryFrom<api::BlobContent> for BlobContent {
954 type Error = GrpcProtoConversionError;
955
956 fn try_from(blob: api::BlobContent) -> Result<Self, Self::Error> {
957 Ok(bincode::deserialize(blob.bytes.as_slice())?)
958 }
959}
960
961impl From<CryptoHash> for api::CryptoHash {
962 fn from(hash: CryptoHash) -> Self {
963 Self {
964 bytes: hash.as_bytes().to_vec(),
965 }
966 }
967}
968
969impl From<Vec<CryptoHash>> for api::CertificatesBatchRequest {
970 fn from(certs: Vec<CryptoHash>) -> Self {
971 Self {
972 hashes: certs.into_iter().map(Into::into).collect(),
973 }
974 }
975}
976
977impl TryFrom<Certificate> for api::Certificate {
978 type Error = GrpcProtoConversionError;
979
980 fn try_from(certificate: Certificate) -> Result<Self, Self::Error> {
981 let round = bincode::serialize(&certificate.round())?;
982 let signatures = bincode::serialize(certificate.signatures())?;
983
984 let (kind, value) = match certificate {
985 Certificate::Confirmed(confirmed) => (
986 api::CertificateKind::Confirmed,
987 bincode::serialize(confirmed.value())?,
988 ),
989 Certificate::Validated(validated) => (
990 api::CertificateKind::Validated,
991 bincode::serialize(validated.value())?,
992 ),
993 Certificate::Timeout(timeout) => (
994 api::CertificateKind::Timeout,
995 bincode::serialize(timeout.value())?,
996 ),
997 };
998
999 Ok(Self {
1000 value,
1001 round,
1002 signatures,
1003 kind: kind as i32,
1004 })
1005 }
1006}
1007
1008impl TryFrom<api::Certificate> for Certificate {
1009 type Error = GrpcProtoConversionError;
1010
1011 fn try_from(certificate: api::Certificate) -> Result<Self, Self::Error> {
1012 let round = bincode::deserialize(&certificate.round)?;
1013 let signatures = bincode::deserialize(&certificate.signatures)?;
1014
1015 let value = if certificate.kind == api::CertificateKind::Confirmed as i32 {
1016 let value: ConfirmedBlock = bincode::deserialize(&certificate.value)?;
1017 Certificate::Confirmed(ConfirmedBlockCertificate::new(value, round, signatures))
1018 } else if certificate.kind == api::CertificateKind::Validated as i32 {
1019 let value: ValidatedBlock = bincode::deserialize(&certificate.value)?;
1020 Certificate::Validated(ValidatedBlockCertificate::new(value, round, signatures))
1021 } else if certificate.kind == api::CertificateKind::Timeout as i32 {
1022 let value: Timeout = bincode::deserialize(&certificate.value)?;
1023 Certificate::Timeout(TimeoutCertificate::new(value, round, signatures))
1024 } else {
1025 return Err(GrpcProtoConversionError::InvalidCertificateType);
1026 };
1027
1028 Ok(value)
1029 }
1030}
1031
1032impl TryFrom<Vec<Certificate>> for api::CertificatesBatchResponse {
1033 type Error = GrpcProtoConversionError;
1034
1035 fn try_from(certs: Vec<Certificate>) -> Result<Self, Self::Error> {
1036 Ok(Self {
1037 certificates: certs
1038 .into_iter()
1039 .map(TryInto::try_into)
1040 .collect::<Result<_, _>>()?,
1041 })
1042 }
1043}
1044
1045impl TryFrom<api::CertificatesBatchResponse> for Vec<Certificate> {
1046 type Error = GrpcProtoConversionError;
1047
1048 fn try_from(response: api::CertificatesBatchResponse) -> Result<Self, Self::Error> {
1049 response
1050 .certificates
1051 .into_iter()
1052 .map(Certificate::try_from)
1053 .collect()
1054 }
1055}
1056
1057impl From<CertificatesByHeightRequest> for api::DownloadCertificatesByHeightsRequest {
1058 fn from(request: CertificatesByHeightRequest) -> Self {
1059 Self {
1060 chain_id: Some(request.chain_id.into()),
1061 heights: request.heights.into_iter().map(Into::into).collect(),
1062 }
1063 }
1064}
1065
1066impl TryFrom<api::DownloadCertificatesByHeightsRequest> for CertificatesByHeightRequest {
1067 type Error = GrpcProtoConversionError;
1068
1069 fn try_from(request: api::DownloadCertificatesByHeightsRequest) -> Result<Self, Self::Error> {
1070 Ok(Self {
1071 chain_id: try_proto_convert(request.chain_id)?,
1072 heights: request.heights.into_iter().map(Into::into).collect(),
1073 })
1074 }
1075}
1076
1077impl TryFrom<StreamId> for api::StreamId {
1078 type Error = GrpcProtoConversionError;
1079
1080 fn try_from(stream_id: StreamId) -> Result<Self, Self::Error> {
1081 Ok(Self {
1082 bytes: bincode::serialize(&stream_id)?,
1083 })
1084 }
1085}
1086
1087impl TryFrom<api::StreamId> for StreamId {
1088 type Error = GrpcProtoConversionError;
1089
1090 fn try_from(stream_id: api::StreamId) -> Result<Self, Self::Error> {
1091 Ok(bincode::deserialize(&stream_id.bytes)?)
1092 }
1093}
1094
1095impl TryFrom<(ChainId, Vec<StreamId>)> for api::PreviousEventBlocksRequest {
1096 type Error = GrpcProtoConversionError;
1097
1098 fn try_from((chain_id, stream_ids): (ChainId, Vec<StreamId>)) -> Result<Self, Self::Error> {
1099 Ok(Self {
1100 chain_id: Some(chain_id.into()),
1101 stream_ids: stream_ids
1102 .into_iter()
1103 .map(TryInto::try_into)
1104 .collect::<Result<_, _>>()?,
1105 })
1106 }
1107}
1108
1109impl TryFrom<api::PreviousEventBlocksRequest> for (ChainId, Vec<StreamId>) {
1110 type Error = GrpcProtoConversionError;
1111
1112 fn try_from(request: api::PreviousEventBlocksRequest) -> Result<Self, Self::Error> {
1113 Ok((
1114 try_proto_convert(request.chain_id)?,
1115 request
1116 .stream_ids
1117 .into_iter()
1118 .map(TryInto::try_into)
1119 .collect::<Result<_, _>>()?,
1120 ))
1121 }
1122}
1123
1124impl TryFrom<BTreeMap<StreamId, (BlockHeight, CryptoHash)>> for api::PreviousEventBlocksResponse {
1125 type Error = GrpcProtoConversionError;
1126
1127 fn try_from(map: BTreeMap<StreamId, (BlockHeight, CryptoHash)>) -> Result<Self, Self::Error> {
1128 Ok(Self {
1129 previous_event_blocks: map
1130 .into_iter()
1131 .map(|(stream_id, (block_height, crypto_hash))| {
1132 Ok(api::PreviousEventBlock {
1133 stream_id: Some(stream_id.try_into()?),
1134 block_height: Some(block_height.into()),
1135 crypto_hash: Some(crypto_hash.into()),
1136 })
1137 })
1138 .collect::<Result<_, GrpcProtoConversionError>>()?,
1139 })
1140 }
1141}
1142
1143impl TryFrom<api::PreviousEventBlocksResponse> for BTreeMap<StreamId, (BlockHeight, CryptoHash)> {
1144 type Error = GrpcProtoConversionError;
1145
1146 fn try_from(response: api::PreviousEventBlocksResponse) -> Result<Self, Self::Error> {
1147 response
1148 .previous_event_blocks
1149 .into_iter()
1150 .map(|entry| {
1151 let stream_id: StreamId = entry
1152 .stream_id
1153 .ok_or(GrpcProtoConversionError::MissingField)?
1154 .try_into()?;
1155 let block_height: BlockHeight = entry
1156 .block_height
1157 .ok_or(GrpcProtoConversionError::MissingField)?
1158 .into();
1159 let crypto_hash: CryptoHash = entry
1160 .crypto_hash
1161 .ok_or(GrpcProtoConversionError::MissingField)?
1162 .try_into()?;
1163 Ok((stream_id, (block_height, crypto_hash)))
1164 })
1165 .collect()
1166 }
1167}
1168
1169#[cfg(test)]
1170pub mod tests {
1171 use std::{borrow::Cow, fmt::Debug};
1172
1173 use linera_base::{
1174 crypto::{AccountSecretKey, BcsSignable, CryptoHash, Secp256k1SecretKey, ValidatorKeypair},
1175 data_types::{Amount, Blob, Epoch, Round, Timestamp},
1176 };
1177 use linera_chain::{
1178 data_types::{BlockExecutionOutcome, OriginalProposal, ProposedBlock},
1179 test::make_first_block,
1180 types::CertificateKind,
1181 };
1182 use linera_core::data_types::ChainInfo;
1183 use serde::{Deserialize, Serialize};
1184
1185 use super::*;
1186
1187 #[derive(Debug, Serialize, Deserialize)]
1188 struct Foo(String);
1189
1190 impl BcsSignable<'_> for Foo {}
1191
1192 fn dummy_chain_id(index: u32) -> ChainId {
1193 ChainId(CryptoHash::test_hash(format!("chain{index}")))
1194 }
1195
1196 fn get_block() -> ProposedBlock {
1197 make_first_block(dummy_chain_id(0))
1198 }
1199
1200 fn round_trip_check<T, M>(value: &T)
1203 where
1204 T: TryFrom<M> + Clone + Debug + Eq,
1205 M: TryFrom<T>,
1206 T::Error: Debug,
1207 M::Error: Debug,
1208 {
1209 let message = M::try_from(value.clone()).unwrap();
1210 let round_trip_value: T = message.try_into().unwrap();
1211 assert_eq!(value, &round_trip_value);
1212 }
1213
1214 #[test]
1215 pub fn test_public_key() {
1216 let account_key = AccountSecretKey::generate().public();
1217 round_trip_check::<_, api::AccountPublicKey>(&account_key);
1218
1219 let validator_key = ValidatorKeypair::generate().public_key;
1220 round_trip_check::<_, api::ValidatorPublicKey>(&validator_key);
1221 }
1222
1223 #[test]
1224 pub fn test_signature() {
1225 let validator_key_pair = ValidatorKeypair::generate();
1226 let validator_signature =
1227 ValidatorSignature::new(&Foo("test".into()), &validator_key_pair.secret_key);
1228 round_trip_check::<_, api::ValidatorSignature>(&validator_signature);
1229
1230 let account_key_pair = AccountSecretKey::generate();
1231 let account_signature = account_key_pair.sign(&Foo("test".into()));
1232 round_trip_check::<_, api::AccountSignature>(&account_signature);
1233 }
1234
1235 #[test]
1236 pub fn test_owner() {
1237 let key_pair = AccountSecretKey::generate();
1238 let owner = AccountOwner::from(key_pair.public());
1239 round_trip_check::<_, api::AccountOwner>(&owner);
1240 }
1241
1242 #[test]
1243 pub fn test_block_height() {
1244 let block_height = BlockHeight::from(10);
1245 round_trip_check::<_, api::BlockHeight>(&block_height);
1246 }
1247
1248 #[test]
1249 pub fn test_chain_id() {
1250 let chain_id = dummy_chain_id(0);
1251 round_trip_check::<_, api::ChainId>(&chain_id);
1252 }
1253
1254 #[test]
1255 pub fn test_chain_info_response() {
1256 let chain_info = Box::new(ChainInfo {
1257 chain_id: dummy_chain_id(0),
1258 epoch: Epoch::ZERO,
1259 description: None,
1260 manager: Box::default(),
1261 chain_balance: Amount::ZERO,
1262 block_hash: None,
1263 timestamp: Timestamp::default(),
1264 next_block_height: BlockHeight::ZERO,
1265 state_hash: None,
1266 requested_committees: None,
1267 requested_owner_balance: None,
1268 requested_pending_message_bundles: vec![],
1269 requested_sent_certificate_hashes: vec![],
1270 count_received_log: 0,
1271 requested_received_log: vec![],
1272 });
1273
1274 let chain_info_response_none = ChainInfoResponse {
1275 info: chain_info.clone(),
1277 signature: None,
1278 };
1279 round_trip_check::<_, api::ChainInfoResponse>(&chain_info_response_none);
1280
1281 let chain_info_response_some = ChainInfoResponse {
1282 info: chain_info,
1284 signature: Some(ValidatorSignature::new(
1285 &Foo("test".into()),
1286 &ValidatorKeypair::generate().secret_key,
1287 )),
1288 };
1289 round_trip_check::<_, api::ChainInfoResponse>(&chain_info_response_some);
1290 }
1291
1292 #[test]
1293 pub fn test_chain_info_query() {
1294 let chain_info_query_none = ChainInfoQuery::new(dummy_chain_id(0));
1295 round_trip_check::<_, api::ChainInfoQuery>(&chain_info_query_none);
1296
1297 let chain_info_query_some = ChainInfoQuery {
1298 chain_id: dummy_chain_id(0),
1299 test_next_block_height: Some(BlockHeight::from(10)),
1300 request_committees: false,
1301 request_owner_balance: AccountOwner::CHAIN,
1302 request_pending_message_bundles: false,
1303 request_received_log_excluding_first_n: None,
1304 request_manager_values: false,
1305 request_leader_timeout: None,
1306 request_fallback: true,
1307 request_sent_certificate_hashes_by_heights: (3..8).map(BlockHeight::from).collect(),
1308 request_sent_certificate_hashes_in_range: None,
1309 create_network_actions: true,
1310 };
1311 round_trip_check::<_, api::ChainInfoQuery>(&chain_info_query_some);
1312 }
1313
1314 #[test]
1315 pub fn reject_chain_info_query_with_request_committees() {
1316 let proto = api::ChainInfoQuery {
1317 chain_id: Some(dummy_chain_id(0).into()),
1318 request_committees: true,
1319 request_owner_balance: Some(AccountOwner::CHAIN.try_into().unwrap()),
1320 ..api::ChainInfoQuery::default()
1321 };
1322 let error = ChainInfoQuery::try_from(proto).unwrap_err();
1323 assert!(matches!(
1324 error,
1325 GrpcProtoConversionError::UnsupportedRequestCommittees
1326 ));
1327 }
1328
1329 #[test]
1330 pub fn test_pending_blob_request() {
1331 let chain_id = dummy_chain_id(2);
1332 let blob_id = Blob::new(BlobContent::new_data(*b"foo")).id();
1333 let pending_blob_request = (chain_id, blob_id);
1334 round_trip_check::<_, api::PendingBlobRequest>(&pending_blob_request);
1335 }
1336
1337 #[test]
1338 pub fn test_pending_blob_result() {
1339 let blob = BlobContent::new_data(*b"foo");
1340 round_trip_check::<_, api::PendingBlobResult>(&blob);
1341 }
1342
1343 #[test]
1344 pub fn test_handle_pending_blob_request() {
1345 let chain_id = dummy_chain_id(2);
1346 let blob_content = BlobContent::new_data(*b"foo");
1347 let pending_blob_request = (chain_id, blob_content);
1348 round_trip_check::<_, api::HandlePendingBlobRequest>(&pending_blob_request);
1349 }
1350
1351 #[test]
1352 pub fn test_lite_certificate() {
1353 let key_pair = ValidatorKeypair::generate();
1354 let certificate = LiteCertificate {
1355 value: LiteValue {
1356 value_hash: CryptoHash::new(&Foo("value".into())),
1357 chain_id: dummy_chain_id(0),
1358 kind: CertificateKind::Validated,
1359 },
1360 round: Round::MultiLeader(2),
1361 signatures: Cow::Owned(vec![(
1362 key_pair.public_key,
1363 ValidatorSignature::new(&Foo("test".into()), &key_pair.secret_key),
1364 )]),
1365 };
1366 let request = HandleLiteCertRequest {
1367 certificate,
1368 wait_for_outgoing_messages: true,
1369 };
1370
1371 round_trip_check::<_, api::LiteCertificate>(&request);
1372 }
1373
1374 #[test]
1375 pub fn test_certificate() {
1376 let key_pair = ValidatorKeypair::generate();
1377 let certificate = ValidatedBlockCertificate::new(
1378 ValidatedBlock::new(
1379 BlockExecutionOutcome {
1380 state_hash: CryptoHash::new(&Foo("test".into())),
1381 ..BlockExecutionOutcome::default()
1382 }
1383 .with(get_block()),
1384 ),
1385 Round::MultiLeader(3),
1386 vec![(
1387 key_pair.public_key,
1388 ValidatorSignature::new(&Foo("test".into()), &key_pair.secret_key),
1389 )],
1390 );
1391 let request = HandleValidatedCertificateRequest { certificate };
1392
1393 round_trip_check::<_, api::HandleValidatedCertificateRequest>(&request);
1394 }
1395
1396 #[test]
1397 pub fn test_cross_chain_request() {
1398 let cross_chain_request_update_recipient = CrossChainRequest::UpdateRecipient {
1399 sender: dummy_chain_id(0),
1400 recipient: dummy_chain_id(0),
1401 bundles: vec![],
1402 previous_height: Some(BlockHeight::from(42)),
1403 };
1404 round_trip_check::<_, api::CrossChainRequest>(&cross_chain_request_update_recipient);
1405
1406 let cross_chain_request_confirm_updated_recipient =
1407 CrossChainRequest::ConfirmUpdatedRecipient {
1408 sender: dummy_chain_id(0),
1409 recipient: dummy_chain_id(0),
1410 latest_height: BlockHeight(1),
1411 };
1412 round_trip_check::<_, api::CrossChainRequest>(
1413 &cross_chain_request_confirm_updated_recipient,
1414 );
1415 }
1416
1417 #[test]
1418 pub fn test_block_proposal() {
1419 let key_pair = ValidatorKeypair::generate();
1420 let outcome = BlockExecutionOutcome {
1421 state_hash: CryptoHash::new(&Foo("validated".into())),
1422 ..BlockExecutionOutcome::default()
1423 };
1424 let certificate = ValidatedBlockCertificate::new(
1425 ValidatedBlock::new(outcome.clone().with(get_block())),
1426 Round::SingleLeader(2),
1427 vec![(
1428 key_pair.public_key,
1429 ValidatorSignature::new(&Foo("signed".into()), &key_pair.secret_key),
1430 )],
1431 )
1432 .lite_certificate()
1433 .cloned();
1434 let key_pair = AccountSecretKey::Secp256k1(Secp256k1SecretKey::generate());
1435 let block_proposal = BlockProposal {
1436 content: ProposalContent {
1437 block: get_block(),
1438 round: Round::SingleLeader(4),
1439 outcome: Some(outcome),
1440 },
1441 signature: key_pair.sign(&Foo("test".into())),
1442 original_proposal: Some(OriginalProposal::Regular { certificate }),
1443 };
1444
1445 round_trip_check::<_, api::BlockProposal>(&block_proposal);
1446 }
1447
1448 #[test]
1449 pub fn test_notification() {
1450 let notification = Notification {
1451 chain_id: dummy_chain_id(0),
1452 reason: linera_core::worker::Reason::NewBlock {
1453 height: BlockHeight(0),
1454 hash: CryptoHash::new(&Foo("".into())),
1455 event_streams: Default::default(),
1456 },
1457 };
1458 let message = api::Notification::try_from(notification.clone()).unwrap();
1459 assert_eq!(
1460 Some(notification),
1461 Option::<Notification>::try_from(message).unwrap()
1462 );
1463
1464 let ack = api::Notification::default();
1465 assert_eq!(None, Option::<Notification>::try_from(ack).unwrap());
1466 }
1467}