penumbra_sdk_proto/
protobuf.rs

1use crate::Name;
2use std::convert::{From, TryFrom};
3
4#[cfg(feature = "tendermint")]
5mod tendermint_compat;
6
7/// A marker type that captures the relationships between a domain type (`Self`) and a protobuf type (`Self::Proto`).
8pub trait DomainType
9where
10    Self: Clone + Sized + TryFrom<Self::Proto>,
11    Self::Proto: prost::Name + prost::Message + Default + From<Self> + Send + Sync + 'static,
12    anyhow::Error: From<<Self as TryFrom<Self::Proto>>::Error>,
13{
14    type Proto;
15
16    /// Encode this domain type to a byte vector, via proto type `P`.
17    fn encode_to_vec(&self) -> Vec<u8> {
18        use prost::Message;
19        self.to_proto().encode_to_vec()
20    }
21
22    /// Convert this domain type to the associated proto type.
23    ///
24    /// This uses the `From` impl internally, so it works exactly
25    /// like `.into()`, but does not require type inference.
26    fn to_proto(&self) -> Self::Proto {
27        Self::Proto::from(self.clone())
28    }
29
30    /// Decode this domain type from a byte buffer, via proto type `P`.
31    fn decode<B: bytes::Buf>(buf: B) -> anyhow::Result<Self> {
32        <Self::Proto as prost::Message>::decode(buf)?
33            .try_into()
34            .map_err(Into::into)
35    }
36}
37
38// Implementations on foreign types.
39//
40// This should only be done here in cases where the domain type lives in a crate
41// that shouldn't depend on the Penumbra proto framework.
42
43use crate::penumbra::core::component::ibc::v1::IbcRelay;
44use crate::penumbra::crypto::decaf377_rdsa::v1::{
45    BindingSignature, SpendAuthSignature, SpendVerificationKey,
46};
47
48use decaf377_rdsa::{Binding, Signature, SpendAuth, VerificationKey};
49
50impl DomainType for Signature<SpendAuth> {
51    type Proto = SpendAuthSignature;
52}
53impl DomainType for Signature<Binding> {
54    type Proto = BindingSignature;
55}
56impl DomainType for VerificationKey<SpendAuth> {
57    type Proto = SpendVerificationKey;
58}
59
60impl From<Signature<SpendAuth>> for SpendAuthSignature {
61    fn from(sig: Signature<SpendAuth>) -> Self {
62        Self {
63            inner: sig.to_bytes().to_vec(),
64        }
65    }
66}
67
68impl From<Signature<Binding>> for BindingSignature {
69    fn from(sig: Signature<Binding>) -> Self {
70        Self {
71            inner: sig.to_bytes().to_vec(),
72        }
73    }
74}
75
76impl From<VerificationKey<SpendAuth>> for SpendVerificationKey {
77    fn from(key: VerificationKey<SpendAuth>) -> Self {
78        Self {
79            inner: key.to_bytes().to_vec(),
80        }
81    }
82}
83
84impl TryFrom<SpendAuthSignature> for Signature<SpendAuth> {
85    type Error = anyhow::Error;
86    fn try_from(value: SpendAuthSignature) -> Result<Self, Self::Error> {
87        Ok(value.inner.as_slice().try_into()?)
88    }
89}
90
91impl TryFrom<BindingSignature> for Signature<Binding> {
92    type Error = anyhow::Error;
93    fn try_from(value: BindingSignature) -> Result<Self, Self::Error> {
94        Ok(value.inner.as_slice().try_into()?)
95    }
96}
97
98impl TryFrom<SpendVerificationKey> for VerificationKey<SpendAuth> {
99    type Error = anyhow::Error;
100    fn try_from(value: SpendVerificationKey) -> Result<Self, Self::Error> {
101        Ok(value.inner.as_slice().try_into()?)
102    }
103}
104
105// Fuzzy Message Detection
106use crate::penumbra::crypto::decaf377_fmd::v1::Clue as ProtoClue;
107use decaf377_fmd::Clue;
108
109impl DomainType for Clue {
110    type Proto = ProtoClue;
111}
112
113impl From<Clue> for ProtoClue {
114    fn from(msg: Clue) -> Self {
115        ProtoClue { inner: msg.into() }
116    }
117}
118
119impl TryFrom<ProtoClue> for Clue {
120    type Error = anyhow::Error;
121
122    fn try_from(proto: ProtoClue) -> Result<Self, Self::Error> {
123        proto.inner[..]
124            .try_into()
125            .map_err(|_| anyhow::anyhow!("expected 68-byte clue"))
126    }
127}
128
129// Consensus key
130//
131// The tendermint-rs PublicKey type already has a tendermint-proto type;
132// this redefines its proto, because the encodings are consensus-critical
133// and we don't vendor all of the tendermint protos.
134use crate::penumbra::core::keys::v1::ConsensusKey;
135
136impl DomainType for tendermint::PublicKey {
137    type Proto = ConsensusKey;
138}
139
140impl From<tendermint::PublicKey> for crate::penumbra::core::keys::v1::ConsensusKey {
141    fn from(v: tendermint::PublicKey) -> Self {
142        Self {
143            inner: v.to_bytes(),
144        }
145    }
146}
147
148impl TryFrom<crate::core::keys::v1::ConsensusKey> for tendermint::PublicKey {
149    type Error = anyhow::Error;
150    fn try_from(value: crate::core::keys::v1::ConsensusKey) -> Result<Self, Self::Error> {
151        Self::from_raw_ed25519(value.inner.as_slice())
152            .ok_or_else(|| anyhow::anyhow!("invalid ed25519 key"))
153    }
154}
155
156// IBC-rs impls
157extern crate ibc_types;
158
159use ibc_proto::ibc::core::channel::v1::Channel as RawChannel;
160use ibc_proto::ibc::core::client::v1::Height as RawHeight;
161use ibc_proto::ibc::core::connection::v1::ClientPaths as RawClientPaths;
162use ibc_proto::ibc::core::connection::v1::ConnectionEnd as RawConnectionEnd;
163
164use ibc_types::core::channel::ChannelEnd;
165use ibc_types::core::client::Height;
166use ibc_types::core::connection::{ClientPaths, ConnectionEnd};
167use ibc_types::lightclients::tendermint::client_state::ClientState;
168use ibc_types::lightclients::tendermint::consensus_state::ConsensusState;
169
170impl DomainType for ClientPaths {
171    type Proto = RawClientPaths;
172}
173
174impl DomainType for ConnectionEnd {
175    type Proto = RawConnectionEnd;
176}
177
178impl DomainType for ChannelEnd {
179    type Proto = RawChannel;
180}
181impl DomainType for Height {
182    type Proto = RawHeight;
183}
184
185impl DomainType for ClientState {
186    type Proto = ibc_proto::google::protobuf::Any;
187}
188impl DomainType for ConsensusState {
189    type Proto = ibc_proto::google::protobuf::Any;
190}
191
192impl<T> From<T> for IbcRelay
193where
194    T: ibc_types::DomainType + Send + Sync + 'static,
195    <T as TryFrom<<T as ibc_types::DomainType>::Proto>>::Error: Send + Sync + std::error::Error,
196{
197    fn from(v: T) -> Self {
198        let value_bytes = v.encode_to_vec();
199        let any = pbjson_types::Any {
200            type_url: <T as ibc_types::DomainType>::Proto::type_url(),
201            value: value_bytes.into(),
202        };
203
204        Self {
205            raw_action: Some(any),
206        }
207    }
208}