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