penumbra_dex/swap/
action.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
use anyhow::Context;
use ark_ff::Zero;
use decaf377::Fr;
use penumbra_asset::{balance, Balance, Value};
use penumbra_num::Amount;
use penumbra_proto::{
    core::component::dex::v1 as pbc, penumbra::core::component::dex::v1 as pb, DomainType,
};
use penumbra_txhash::{EffectHash, EffectingData};
use serde::{Deserialize, Serialize};

use crate::TradingPair;

use super::{proof::SwapProof, SwapPayload};

#[derive(Clone, Debug)]
pub struct Swap {
    pub proof: SwapProof,
    pub body: Body,
}

impl Swap {
    /// Temporary method until we resolve where `IsAction::balance_commitment` should live.
    pub fn balance_commitment_inner(&self) -> balance::Commitment {
        let input_1 = Value {
            amount: self.body.delta_1_i,
            asset_id: self.body.trading_pair.asset_1(),
        };
        let input_1 = -Balance::from(input_1);
        let commitment_input_1 = input_1.commit(Fr::zero());
        let input_2 = Value {
            amount: self.body.delta_2_i,
            asset_id: self.body.trading_pair.asset_2(),
        };
        let input_2 = -Balance::from(input_2);
        let commitment_input_2 = input_2.commit(Fr::zero());

        commitment_input_1 + commitment_input_2 + self.body.fee_commitment
    }
}

impl EffectingData for Swap {
    fn effect_hash(&self) -> EffectHash {
        // The effecting data is in the body of the swap, so we can
        // just use hash the proto-encoding of the body.
        self.body.effect_hash()
    }
}

impl DomainType for Swap {
    type Proto = pb::Swap;
}

impl From<Swap> for pb::Swap {
    fn from(s: Swap) -> Self {
        let proof: pbc::ZkSwapProof = s.proof.into();
        pb::Swap {
            proof: Some(proof),
            body: Some(s.body.into()),
        }
    }
}

impl TryFrom<pb::Swap> for Swap {
    type Error = anyhow::Error;
    fn try_from(s: pb::Swap) -> Result<Self, Self::Error> {
        Ok(Self {
            proof: s
                .proof
                .ok_or_else(|| anyhow::anyhow!("missing swap proof"))?
                .try_into()
                .context("swap proof malformed")?,
            body: s
                .body
                .ok_or_else(|| anyhow::anyhow!("missing swap body"))?
                .try_into()
                .context("swap body malformed")?,
        })
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(try_from = "pb::SwapBody", into = "pb::SwapBody")]
pub struct Body {
    pub trading_pair: TradingPair,
    pub delta_1_i: Amount,
    pub delta_2_i: Amount,
    pub fee_commitment: balance::Commitment,
    pub payload: SwapPayload,
}

impl EffectingData for Body {
    fn effect_hash(&self) -> EffectHash {
        EffectHash::from_proto_effecting_data(&self.to_proto())
    }
}

impl DomainType for Body {
    type Proto = pb::SwapBody;
}

impl From<Body> for pb::SwapBody {
    fn from(s: Body) -> Self {
        pb::SwapBody {
            trading_pair: Some(s.trading_pair.into()),
            delta_1_i: Some(s.delta_1_i.into()),
            delta_2_i: Some(s.delta_2_i.into()),
            fee_commitment: Some(s.fee_commitment.into()),
            payload: Some(s.payload.into()),
        }
    }
}

impl TryFrom<pb::SwapBody> for Body {
    type Error = anyhow::Error;
    fn try_from(s: pb::SwapBody) -> Result<Self, Self::Error> {
        Ok(Self {
            trading_pair: s
                .trading_pair
                .ok_or_else(|| anyhow::anyhow!("missing trading_pair"))?
                .try_into()?,

            delta_1_i: s
                .delta_1_i
                .ok_or_else(|| anyhow::anyhow!("missing delta_1"))?
                .try_into()?,
            delta_2_i: s
                .delta_2_i
                .ok_or_else(|| anyhow::anyhow!("missing delta_2"))?
                .try_into()?,

            fee_commitment: s
                .fee_commitment
                .ok_or_else(|| anyhow::anyhow!("missing fee_commitment"))?
                .try_into()?,
            payload: s
                .payload
                .ok_or_else(|| anyhow::anyhow!("missing payload"))?
                .try_into()?,
        })
    }
}