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