penumbra_sdk_shielded_pool/output/
action.rs

1use std::convert::{TryFrom, TryInto};
2
3use anyhow::{Context, Error};
4use penumbra_sdk_asset::balance;
5use penumbra_sdk_keys::symmetric::{OvkWrappedKey, WrappedMemoKey};
6use penumbra_sdk_proto::{
7    core::component::shielded_pool::v1 as pb, penumbra::core::component::shielded_pool::v1 as pbc,
8    DomainType,
9};
10use penumbra_sdk_txhash::{EffectHash, EffectingData};
11use serde::{Deserialize, Serialize};
12
13use crate::{NotePayload, OutputProof};
14
15#[derive(Clone, Debug)]
16pub struct Output {
17    pub body: Body,
18    pub proof: OutputProof,
19}
20
21#[derive(Clone, Debug, Deserialize, Serialize)]
22#[serde(try_from = "pb::OutputBody", into = "pb::OutputBody")]
23pub struct Body {
24    pub note_payload: NotePayload,
25    pub balance_commitment: balance::Commitment,
26    pub ovk_wrapped_key: OvkWrappedKey,
27    pub wrapped_memo_key: WrappedMemoKey,
28}
29
30impl EffectingData for Body {
31    fn effect_hash(&self) -> EffectHash {
32        EffectHash::from_proto_effecting_data(&self.to_proto())
33    }
34}
35
36impl EffectingData for Output {
37    fn effect_hash(&self) -> EffectHash {
38        // The effecting data is in the body of the output, so we can
39        // just use hash the proto-encoding of the body.
40        self.body.effect_hash()
41    }
42}
43
44impl DomainType for Output {
45    type Proto = pb::Output;
46}
47
48impl From<Output> for pb::Output {
49    fn from(output: Output) -> Self {
50        let proof: pbc::ZkOutputProof = output.proof.into();
51        pb::Output {
52            body: Some(output.body.into()),
53            proof: Some(proof),
54        }
55    }
56}
57
58impl TryFrom<pb::Output> for Output {
59    type Error = Error;
60
61    fn try_from(proto: pb::Output) -> anyhow::Result<Self, Self::Error> {
62        Ok(Output {
63            body: proto
64                .body
65                .ok_or_else(|| anyhow::anyhow!("missing output body"))?
66                .try_into()?,
67            proof: proto
68                .proof
69                .ok_or_else(|| anyhow::anyhow!("missing output proof"))?
70                .try_into()
71                .context("output proof malformed")?,
72        })
73    }
74}
75
76impl DomainType for Body {
77    type Proto = pb::OutputBody;
78}
79
80impl From<Body> for pb::OutputBody {
81    fn from(output: Body) -> Self {
82        pb::OutputBody {
83            note_payload: Some(output.note_payload.into()),
84            balance_commitment: Some(output.balance_commitment.into()),
85            wrapped_memo_key: output.wrapped_memo_key.0.to_vec(),
86            ovk_wrapped_key: output.ovk_wrapped_key.0.to_vec(),
87        }
88    }
89}
90
91impl TryFrom<pb::OutputBody> for Body {
92    type Error = Error;
93
94    fn try_from(proto: pb::OutputBody) -> anyhow::Result<Self, Self::Error> {
95        let note_payload = proto
96            .note_payload
97            .ok_or_else(|| anyhow::anyhow!("missing note payload"))?
98            .try_into()
99            .context("malformed note payload")?;
100
101        let wrapped_memo_key = proto.wrapped_memo_key[..]
102            .try_into()
103            .context("malformed wrapped memo key")?;
104
105        let ovk_wrapped_key: OvkWrappedKey = proto.ovk_wrapped_key[..]
106            .try_into()
107            .context("malformed ovk wrapped key")?;
108
109        let balance_commitment = proto
110            .balance_commitment
111            .ok_or_else(|| anyhow::anyhow!("missing balance commitment"))?
112            .try_into()
113            .context("malformed balance commitment")?;
114
115        Ok(Body {
116            note_payload,
117            wrapped_memo_key,
118            ovk_wrapped_key,
119            balance_commitment,
120        })
121    }
122}