linera_rpc/grpc/
conversions.rs

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