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            request_sent_certificate_hashes_in_range: None,
617            create_network_actions: chain_info_query.create_network_actions.unwrap_or(true),
618        })
619    }
620}
621
622impl TryFrom<ChainInfoQuery> for api::ChainInfoQuery {
623    type Error = GrpcProtoConversionError;
624
625    fn try_from(chain_info_query: ChainInfoQuery) -> Result<Self, Self::Error> {
626        let request_sent_certificate_hashes_by_heights =
627            bincode::serialize(&chain_info_query.request_sent_certificate_hashes_by_heights)?;
628        let request_owner_balance = Some(chain_info_query.request_owner_balance.try_into()?);
629        let request_leader_timeout = chain_info_query
630            .request_leader_timeout
631            .map(|height_and_round| bincode::serialize(&height_and_round))
632            .transpose()?;
633
634        Ok(Self {
635            chain_id: Some(chain_info_query.chain_id.into()),
636            request_committees: chain_info_query.request_committees,
637            request_owner_balance,
638            request_pending_message_bundles: chain_info_query.request_pending_message_bundles,
639            test_next_block_height: chain_info_query.test_next_block_height.map(Into::into),
640            request_sent_certificate_hashes_by_heights: Some(
641                request_sent_certificate_hashes_by_heights,
642            ),
643            request_received_log_excluding_first_n: chain_info_query
644                .request_received_log_excluding_first_n,
645            request_manager_values: chain_info_query.request_manager_values,
646            request_leader_timeout,
647            request_fallback: chain_info_query.request_fallback,
648            create_network_actions: Some(chain_info_query.create_network_actions),
649        })
650    }
651}
652
653impl From<ChainId> for api::ChainId {
654    fn from(chain_id: ChainId) -> Self {
655        Self {
656            bytes: chain_id.0.as_bytes().to_vec(),
657        }
658    }
659}
660
661impl TryFrom<api::ChainId> for ChainId {
662    type Error = GrpcProtoConversionError;
663
664    fn try_from(chain_id: api::ChainId) -> Result<Self, Self::Error> {
665        Ok(ChainId::try_from(chain_id.bytes.as_slice())?)
666    }
667}
668
669impl From<AccountPublicKey> for api::AccountPublicKey {
670    fn from(public_key: AccountPublicKey) -> Self {
671        Self {
672            bytes: public_key.as_bytes(),
673        }
674    }
675}
676
677impl From<ValidatorPublicKey> for api::ValidatorPublicKey {
678    fn from(public_key: ValidatorPublicKey) -> Self {
679        Self {
680            bytes: public_key.as_bytes().to_vec(),
681        }
682    }
683}
684
685impl TryFrom<api::ValidatorPublicKey> for ValidatorPublicKey {
686    type Error = GrpcProtoConversionError;
687
688    fn try_from(public_key: api::ValidatorPublicKey) -> Result<Self, Self::Error> {
689        Ok(Self::from_bytes(public_key.bytes.as_slice())?)
690    }
691}
692
693impl TryFrom<api::AccountPublicKey> for AccountPublicKey {
694    type Error = GrpcProtoConversionError;
695
696    fn try_from(public_key: api::AccountPublicKey) -> Result<Self, Self::Error> {
697        Ok(Self::from_slice(public_key.bytes.as_slice())?)
698    }
699}
700
701impl From<AccountSignature> for api::AccountSignature {
702    fn from(signature: AccountSignature) -> Self {
703        Self {
704            bytes: signature.to_bytes(),
705        }
706    }
707}
708
709impl From<ValidatorSignature> for api::ValidatorSignature {
710    fn from(signature: ValidatorSignature) -> Self {
711        Self {
712            bytes: signature.as_bytes().to_vec(),
713        }
714    }
715}
716
717impl TryFrom<api::ValidatorSignature> for ValidatorSignature {
718    type Error = GrpcProtoConversionError;
719
720    fn try_from(signature: api::ValidatorSignature) -> Result<Self, Self::Error> {
721        Self::from_slice(signature.bytes.as_slice()).map_err(GrpcProtoConversionError::CryptoError)
722    }
723}
724
725impl TryFrom<api::AccountSignature> for AccountSignature {
726    type Error = GrpcProtoConversionError;
727
728    fn try_from(signature: api::AccountSignature) -> Result<Self, Self::Error> {
729        Ok(Self::from_slice(signature.bytes.as_slice())?)
730    }
731}
732
733impl TryFrom<ChainInfoResponse> for api::ChainInfoResponse {
734    type Error = GrpcProtoConversionError;
735
736    fn try_from(chain_info_response: ChainInfoResponse) -> Result<Self, Self::Error> {
737        Ok(Self {
738            chain_info: bincode::serialize(&chain_info_response.info)?,
739            signature: chain_info_response.signature.map(Into::into),
740        })
741    }
742}
743
744impl TryFrom<api::ChainInfoResponse> for ChainInfoResponse {
745    type Error = GrpcProtoConversionError;
746
747    fn try_from(chain_info_response: api::ChainInfoResponse) -> Result<Self, Self::Error> {
748        let signature = chain_info_response
749            .signature
750            .map(TryInto::try_into)
751            .transpose()?;
752        let info = bincode::deserialize(chain_info_response.chain_info.as_slice())?;
753        Ok(Self { info, signature })
754    }
755}
756
757impl TryFrom<(ChainId, BlobId)> for api::PendingBlobRequest {
758    type Error = GrpcProtoConversionError;
759
760    fn try_from((chain_id, blob_id): (ChainId, BlobId)) -> Result<Self, Self::Error> {
761        Ok(Self {
762            chain_id: Some(chain_id.into()),
763            blob_id: Some(blob_id.try_into()?),
764        })
765    }
766}
767
768impl TryFrom<api::PendingBlobRequest> for (ChainId, BlobId) {
769    type Error = GrpcProtoConversionError;
770
771    fn try_from(request: PendingBlobRequest) -> Result<Self, Self::Error> {
772        Ok((
773            try_proto_convert(request.chain_id)?,
774            try_proto_convert(request.blob_id)?,
775        ))
776    }
777}
778
779impl TryFrom<(ChainId, BlobContent)> for api::HandlePendingBlobRequest {
780    type Error = GrpcProtoConversionError;
781
782    fn try_from((chain_id, blob_content): (ChainId, BlobContent)) -> Result<Self, Self::Error> {
783        Ok(Self {
784            chain_id: Some(chain_id.into()),
785            blob: Some(blob_content.try_into()?),
786        })
787    }
788}
789
790impl TryFrom<api::HandlePendingBlobRequest> for (ChainId, BlobContent) {
791    type Error = GrpcProtoConversionError;
792
793    fn try_from(request: api::HandlePendingBlobRequest) -> Result<Self, Self::Error> {
794        Ok((
795            try_proto_convert(request.chain_id)?,
796            try_proto_convert(request.blob)?,
797        ))
798    }
799}
800
801impl TryFrom<BlobContent> for api::PendingBlobResult {
802    type Error = GrpcProtoConversionError;
803
804    fn try_from(blob: BlobContent) -> Result<Self, Self::Error> {
805        Ok(Self {
806            inner: Some(api::pending_blob_result::Inner::Blob(blob.try_into()?)),
807        })
808    }
809}
810
811impl TryFrom<NodeError> for api::PendingBlobResult {
812    type Error = GrpcProtoConversionError;
813
814    fn try_from(node_error: NodeError) -> Result<Self, Self::Error> {
815        let error = bincode::serialize(&node_error)?;
816        Ok(api::PendingBlobResult {
817            inner: Some(api::pending_blob_result::Inner::Error(error)),
818        })
819    }
820}
821
822impl From<BlockHeight> for api::BlockHeight {
823    fn from(block_height: BlockHeight) -> Self {
824        Self {
825            height: block_height.0,
826        }
827    }
828}
829
830impl From<api::BlockHeight> for BlockHeight {
831    fn from(block_height: api::BlockHeight) -> Self {
832        Self(block_height.height)
833    }
834}
835
836impl TryFrom<AccountOwner> for api::AccountOwner {
837    type Error = GrpcProtoConversionError;
838
839    fn try_from(account_owner: AccountOwner) -> Result<Self, Self::Error> {
840        Ok(Self {
841            bytes: bincode::serialize(&account_owner)?,
842        })
843    }
844}
845
846impl TryFrom<api::AccountOwner> for AccountOwner {
847    type Error = GrpcProtoConversionError;
848
849    fn try_from(account_owner: api::AccountOwner) -> Result<Self, Self::Error> {
850        Ok(bincode::deserialize(&account_owner.bytes)?)
851    }
852}
853
854impl TryFrom<api::BlobId> for BlobId {
855    type Error = GrpcProtoConversionError;
856
857    fn try_from(blob_id: api::BlobId) -> Result<Self, Self::Error> {
858        Ok(bincode::deserialize(blob_id.bytes.as_slice())?)
859    }
860}
861
862impl TryFrom<api::BlobIds> for Vec<BlobId> {
863    type Error = GrpcProtoConversionError;
864
865    fn try_from(blob_ids: api::BlobIds) -> Result<Self, Self::Error> {
866        Ok(blob_ids
867            .bytes
868            .into_iter()
869            .map(|x| bincode::deserialize(x.as_slice()))
870            .collect::<Result<_, _>>()?)
871    }
872}
873
874impl TryFrom<BlobId> for api::BlobId {
875    type Error = GrpcProtoConversionError;
876
877    fn try_from(blob_id: BlobId) -> Result<Self, Self::Error> {
878        Ok(Self {
879            bytes: bincode::serialize(&blob_id)?,
880        })
881    }
882}
883
884impl TryFrom<Vec<BlobId>> for api::BlobIds {
885    type Error = GrpcProtoConversionError;
886
887    fn try_from(blob_ids: Vec<BlobId>) -> Result<Self, Self::Error> {
888        let bytes = blob_ids
889            .into_iter()
890            .map(|blob_id| bincode::serialize(&blob_id))
891            .collect::<Result<_, _>>()?;
892        Ok(Self { bytes })
893    }
894}
895
896impl TryFrom<api::CryptoHash> for CryptoHash {
897    type Error = GrpcProtoConversionError;
898
899    fn try_from(hash: api::CryptoHash) -> Result<Self, Self::Error> {
900        Ok(CryptoHash::try_from(hash.bytes.as_slice())?)
901    }
902}
903
904impl TryFrom<BlobContent> for api::BlobContent {
905    type Error = GrpcProtoConversionError;
906
907    fn try_from(blob: BlobContent) -> Result<Self, Self::Error> {
908        Ok(Self {
909            bytes: bincode::serialize(&blob)?,
910        })
911    }
912}
913
914impl TryFrom<api::BlobContent> for BlobContent {
915    type Error = GrpcProtoConversionError;
916
917    fn try_from(blob: api::BlobContent) -> Result<Self, Self::Error> {
918        Ok(bincode::deserialize(blob.bytes.as_slice())?)
919    }
920}
921
922impl From<CryptoHash> for api::CryptoHash {
923    fn from(hash: CryptoHash) -> Self {
924        Self {
925            bytes: hash.as_bytes().to_vec(),
926        }
927    }
928}
929
930impl From<Vec<CryptoHash>> for api::CertificatesBatchRequest {
931    fn from(certs: Vec<CryptoHash>) -> Self {
932        Self {
933            hashes: certs.into_iter().map(Into::into).collect(),
934        }
935    }
936}
937
938impl TryFrom<Certificate> for api::Certificate {
939    type Error = GrpcProtoConversionError;
940
941    fn try_from(certificate: Certificate) -> Result<Self, Self::Error> {
942        let round = bincode::serialize(&certificate.round())?;
943        let signatures = bincode::serialize(certificate.signatures())?;
944
945        let (kind, value) = match certificate {
946            Certificate::Confirmed(confirmed) => (
947                api::CertificateKind::Confirmed,
948                bincode::serialize(confirmed.value())?,
949            ),
950            Certificate::Validated(validated) => (
951                api::CertificateKind::Validated,
952                bincode::serialize(validated.value())?,
953            ),
954            Certificate::Timeout(timeout) => (
955                api::CertificateKind::Timeout,
956                bincode::serialize(timeout.value())?,
957            ),
958        };
959
960        Ok(Self {
961            value,
962            round,
963            signatures,
964            kind: kind as i32,
965        })
966    }
967}
968
969impl TryFrom<api::Certificate> for Certificate {
970    type Error = GrpcProtoConversionError;
971
972    fn try_from(certificate: api::Certificate) -> Result<Self, Self::Error> {
973        let round = bincode::deserialize(&certificate.round)?;
974        let signatures = bincode::deserialize(&certificate.signatures)?;
975
976        let value = if certificate.kind == api::CertificateKind::Confirmed as i32 {
977            let value: ConfirmedBlock = bincode::deserialize(&certificate.value)?;
978            Certificate::Confirmed(ConfirmedBlockCertificate::new(value, round, signatures))
979        } else if certificate.kind == api::CertificateKind::Validated as i32 {
980            let value: ValidatedBlock = bincode::deserialize(&certificate.value)?;
981            Certificate::Validated(ValidatedBlockCertificate::new(value, round, signatures))
982        } else if certificate.kind == api::CertificateKind::Timeout as i32 {
983            let value: Timeout = bincode::deserialize(&certificate.value)?;
984            Certificate::Timeout(TimeoutCertificate::new(value, round, signatures))
985        } else {
986            return Err(GrpcProtoConversionError::InvalidCertificateType);
987        };
988
989        Ok(value)
990    }
991}
992
993impl TryFrom<Vec<Certificate>> for api::CertificatesBatchResponse {
994    type Error = GrpcProtoConversionError;
995
996    fn try_from(certs: Vec<Certificate>) -> Result<Self, Self::Error> {
997        Ok(Self {
998            certificates: certs
999                .into_iter()
1000                .map(TryInto::try_into)
1001                .collect::<Result<_, _>>()?,
1002        })
1003    }
1004}
1005
1006impl TryFrom<api::CertificatesBatchResponse> for Vec<Certificate> {
1007    type Error = GrpcProtoConversionError;
1008
1009    fn try_from(response: api::CertificatesBatchResponse) -> Result<Self, Self::Error> {
1010        response
1011            .certificates
1012            .into_iter()
1013            .map(Certificate::try_from)
1014            .collect()
1015    }
1016}
1017
1018impl From<CertificatesByHeightRequest> for api::DownloadCertificatesByHeightsRequest {
1019    fn from(request: CertificatesByHeightRequest) -> Self {
1020        Self {
1021            chain_id: Some(request.chain_id.into()),
1022            heights: request.heights.into_iter().map(Into::into).collect(),
1023        }
1024    }
1025}
1026
1027impl TryFrom<api::DownloadCertificatesByHeightsRequest> for CertificatesByHeightRequest {
1028    type Error = GrpcProtoConversionError;
1029
1030    fn try_from(request: api::DownloadCertificatesByHeightsRequest) -> Result<Self, Self::Error> {
1031        Ok(Self {
1032            chain_id: try_proto_convert(request.chain_id)?,
1033            heights: request.heights.into_iter().map(Into::into).collect(),
1034        })
1035    }
1036}
1037
1038#[cfg(test)]
1039pub mod tests {
1040    use std::{borrow::Cow, fmt::Debug};
1041
1042    use linera_base::{
1043        crypto::{AccountSecretKey, BcsSignable, CryptoHash, Secp256k1SecretKey, ValidatorKeypair},
1044        data_types::{Amount, Blob, Epoch, Round, Timestamp},
1045    };
1046    use linera_chain::{
1047        data_types::{BlockExecutionOutcome, OriginalProposal, ProposedBlock},
1048        test::make_first_block,
1049        types::CertificateKind,
1050    };
1051    use linera_core::data_types::ChainInfo;
1052    use serde::{Deserialize, Serialize};
1053
1054    use super::*;
1055
1056    #[derive(Debug, Serialize, Deserialize)]
1057    struct Foo(String);
1058
1059    impl BcsSignable<'_> for Foo {}
1060
1061    fn dummy_chain_id(index: u32) -> ChainId {
1062        ChainId(CryptoHash::test_hash(format!("chain{}", index)))
1063    }
1064
1065    fn get_block() -> ProposedBlock {
1066        make_first_block(dummy_chain_id(0))
1067    }
1068
1069    /// A convenience function for testing. It converts a type into its
1070    /// RPC equivalent and back - asserting that the two are equal.
1071    fn round_trip_check<T, M>(value: T)
1072    where
1073        T: TryFrom<M> + Clone + Debug + Eq,
1074        M: TryFrom<T>,
1075        T::Error: Debug,
1076        M::Error: Debug,
1077    {
1078        let message = M::try_from(value.clone()).unwrap();
1079        assert_eq!(value, message.try_into().unwrap());
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            request_sent_certificate_hashes_in_range: None,
1177            create_network_actions: true,
1178        };
1179        round_trip_check::<_, api::ChainInfoQuery>(chain_info_query_some);
1180    }
1181
1182    #[test]
1183    pub fn test_pending_blob_request() {
1184        let chain_id = dummy_chain_id(2);
1185        let blob_id = Blob::new(BlobContent::new_data(*b"foo")).id();
1186        let pending_blob_request = (chain_id, blob_id);
1187        round_trip_check::<_, api::PendingBlobRequest>(pending_blob_request);
1188    }
1189
1190    #[test]
1191    pub fn test_pending_blob_result() {
1192        let blob = BlobContent::new_data(*b"foo");
1193        round_trip_check::<_, api::PendingBlobResult>(blob);
1194    }
1195
1196    #[test]
1197    pub fn test_handle_pending_blob_request() {
1198        let chain_id = dummy_chain_id(2);
1199        let blob_content = BlobContent::new_data(*b"foo");
1200        let pending_blob_request = (chain_id, blob_content);
1201        round_trip_check::<_, api::HandlePendingBlobRequest>(pending_blob_request);
1202    }
1203
1204    #[test]
1205    pub fn test_lite_certificate() {
1206        let key_pair = ValidatorKeypair::generate();
1207        let certificate = LiteCertificate {
1208            value: LiteValue {
1209                value_hash: CryptoHash::new(&Foo("value".into())),
1210                chain_id: dummy_chain_id(0),
1211                kind: CertificateKind::Validated,
1212            },
1213            round: Round::MultiLeader(2),
1214            signatures: Cow::Owned(vec![(
1215                key_pair.public_key,
1216                ValidatorSignature::new(&Foo("test".into()), &key_pair.secret_key),
1217            )]),
1218        };
1219        let request = HandleLiteCertRequest {
1220            certificate,
1221            wait_for_outgoing_messages: true,
1222        };
1223
1224        round_trip_check::<_, api::LiteCertificate>(request);
1225    }
1226
1227    #[test]
1228    pub fn test_certificate() {
1229        let key_pair = ValidatorKeypair::generate();
1230        let certificate = ValidatedBlockCertificate::new(
1231            ValidatedBlock::new(
1232                BlockExecutionOutcome {
1233                    state_hash: CryptoHash::new(&Foo("test".into())),
1234                    ..BlockExecutionOutcome::default()
1235                }
1236                .with(get_block()),
1237            ),
1238            Round::MultiLeader(3),
1239            vec![(
1240                key_pair.public_key,
1241                ValidatorSignature::new(&Foo("test".into()), &key_pair.secret_key),
1242            )],
1243        );
1244        let request = HandleValidatedCertificateRequest { certificate };
1245
1246        round_trip_check::<_, api::HandleValidatedCertificateRequest>(request);
1247    }
1248
1249    #[test]
1250    pub fn test_cross_chain_request() {
1251        let cross_chain_request_update_recipient = CrossChainRequest::UpdateRecipient {
1252            sender: dummy_chain_id(0),
1253            recipient: dummy_chain_id(0),
1254            bundles: vec![],
1255        };
1256        round_trip_check::<_, api::CrossChainRequest>(cross_chain_request_update_recipient);
1257
1258        let cross_chain_request_confirm_updated_recipient =
1259            CrossChainRequest::ConfirmUpdatedRecipient {
1260                sender: dummy_chain_id(0),
1261                recipient: dummy_chain_id(0),
1262                latest_height: BlockHeight(1),
1263            };
1264        round_trip_check::<_, api::CrossChainRequest>(
1265            cross_chain_request_confirm_updated_recipient,
1266        );
1267    }
1268
1269    #[test]
1270    pub fn test_block_proposal() {
1271        let key_pair = ValidatorKeypair::generate();
1272        let outcome = BlockExecutionOutcome {
1273            state_hash: CryptoHash::new(&Foo("validated".into())),
1274            ..BlockExecutionOutcome::default()
1275        };
1276        let certificate = ValidatedBlockCertificate::new(
1277            ValidatedBlock::new(outcome.clone().with(get_block())),
1278            Round::SingleLeader(2),
1279            vec![(
1280                key_pair.public_key,
1281                ValidatorSignature::new(&Foo("signed".into()), &key_pair.secret_key),
1282            )],
1283        )
1284        .lite_certificate()
1285        .cloned();
1286        let key_pair = AccountSecretKey::Secp256k1(Secp256k1SecretKey::generate());
1287        let block_proposal = BlockProposal {
1288            content: ProposalContent {
1289                block: get_block(),
1290                round: Round::SingleLeader(4),
1291                outcome: Some(outcome),
1292            },
1293            signature: key_pair.sign(&Foo("test".into())),
1294            original_proposal: Some(OriginalProposal::Regular { certificate }),
1295        };
1296
1297        round_trip_check::<_, api::BlockProposal>(block_proposal);
1298    }
1299
1300    #[test]
1301    pub fn test_notification() {
1302        let notification = Notification {
1303            chain_id: dummy_chain_id(0),
1304            reason: linera_core::worker::Reason::NewBlock {
1305                height: BlockHeight(0),
1306                hash: CryptoHash::new(&Foo("".into())),
1307            },
1308        };
1309        let message = api::Notification::try_from(notification.clone()).unwrap();
1310        assert_eq!(
1311            Some(notification),
1312            Option::<Notification>::try_from(message).unwrap()
1313        );
1314
1315        let ack = api::Notification::default();
1316        assert_eq!(None, Option::<Notification>::try_from(ack).unwrap());
1317    }
1318}