penumbra_sdk_dex/swap_claim/
action.rs

1use anyhow::Context;
2use penumbra_sdk_asset::Balance;
3use penumbra_sdk_fee::Fee;
4use penumbra_sdk_proof_params::GROTH16_PROOF_LENGTH_BYTES;
5use penumbra_sdk_proto::{penumbra::core::component::dex::v1 as pb, DomainType};
6use penumbra_sdk_sct::Nullifier;
7use penumbra_sdk_tct as tct;
8use penumbra_sdk_txhash::{EffectHash, EffectingData};
9use serde::{Deserialize, Serialize};
10
11use crate::BatchSwapOutputData;
12
13use super::proof::SwapClaimProof;
14
15#[derive(Debug, Clone)]
16pub struct SwapClaim {
17    pub proof: SwapClaimProof,
18    pub body: Body,
19    pub epoch_duration: u64,
20}
21
22impl SwapClaim {
23    /// Compute a commitment to the value contributed to a transaction by this swap claim.
24    /// Will add (f,fee_token) representing the pre-paid fee
25    pub fn balance(&self) -> Balance {
26        self.body.fee.value().into()
27    }
28}
29
30impl EffectingData for SwapClaim {
31    fn effect_hash(&self) -> EffectHash {
32        // The effecting data is in the body of the swap claim, so we can
33        // just use hash the proto-encoding of the body.
34        self.body.effect_hash()
35    }
36}
37
38impl DomainType for SwapClaim {
39    type Proto = pb::SwapClaim;
40}
41
42impl From<SwapClaim> for pb::SwapClaim {
43    fn from(sc: SwapClaim) -> Self {
44        pb::SwapClaim {
45            proof: Some(sc.proof.into()),
46            body: Some(sc.body.into()),
47            epoch_duration: sc.epoch_duration,
48        }
49    }
50}
51
52impl TryFrom<pb::SwapClaim> for SwapClaim {
53    type Error = anyhow::Error;
54    fn try_from(sc: pb::SwapClaim) -> Result<Self, Self::Error> {
55        let proof_bytes: [u8; GROTH16_PROOF_LENGTH_BYTES] = sc
56            .proof
57            .ok_or_else(|| anyhow::anyhow!("missing swap claim proof"))?
58            .inner
59            .as_slice()
60            .try_into()
61            .context("swap claim proof malformed")?;
62        Ok(Self {
63            body: sc
64                .body
65                .ok_or_else(|| anyhow::anyhow!("missing swap claim body"))?
66                .try_into()
67                .context("swap claim body malformed")?,
68            epoch_duration: sc.epoch_duration,
69            proof: SwapClaimProof(proof_bytes),
70        })
71    }
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
75#[serde(try_from = "pb::SwapClaimBody", into = "pb::SwapClaimBody")]
76pub struct Body {
77    pub nullifier: Nullifier,
78    pub fee: Fee,
79    pub output_1_commitment: tct::StateCommitment,
80    pub output_2_commitment: tct::StateCommitment,
81    pub output_data: BatchSwapOutputData,
82}
83
84impl EffectingData for Body {
85    fn effect_hash(&self) -> EffectHash {
86        EffectHash::from_proto_effecting_data(&self.to_proto())
87    }
88}
89
90impl DomainType for Body {
91    type Proto = pb::SwapClaimBody;
92}
93
94impl From<Body> for pb::SwapClaimBody {
95    fn from(s: Body) -> Self {
96        pb::SwapClaimBody {
97            nullifier: Some(s.nullifier.into()),
98            fee: Some(s.fee.into()),
99            output_1_commitment: Some(s.output_1_commitment.into()),
100            output_2_commitment: Some(s.output_2_commitment.into()),
101            output_data: Some(s.output_data.into()),
102        }
103    }
104}
105
106impl TryFrom<pb::SwapClaimBody> for Body {
107    type Error = anyhow::Error;
108    fn try_from(sc: pb::SwapClaimBody) -> Result<Self, Self::Error> {
109        Ok(Self {
110            nullifier: sc
111                .nullifier
112                .ok_or_else(|| anyhow::anyhow!("missing nullifier"))?
113                .try_into()?,
114            fee: sc
115                .fee
116                .ok_or_else(|| anyhow::anyhow!("missing fee"))?
117                .try_into()?,
118            output_1_commitment: sc
119                .output_1_commitment
120                .ok_or_else(|| anyhow::anyhow!("missing output_1"))?
121                .try_into()?,
122            output_2_commitment: sc
123                .output_2_commitment
124                .ok_or_else(|| anyhow::anyhow!("missing output_2"))?
125                .try_into()?,
126            output_data: sc
127                .output_data
128                .ok_or_else(|| anyhow::anyhow!("missing anchor"))?
129                .try_into()?,
130        })
131    }
132}