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