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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
use std::ops::Deref;

use ark_ff::PrimeField;
use ark_r1cs_std::prelude::*;
use ark_r1cs_std::uint8::UInt8;
use ark_relations::r1cs::SynthesisError;
use decaf377::r1cs::ElementVar;
use decaf377::Fq;
use decaf377::Fr;
use once_cell::sync::Lazy;
use penumbra_proto::penumbra::core::asset::v1 as pb;
use penumbra_proto::DomainType;

use crate::value::ValueVar;
use crate::Value;

impl Value {
    #[allow(non_snake_case)]
    pub fn commit(&self, blinding: Fr) -> Commitment {
        let G_v = self.asset_id.value_generator();
        let H = VALUE_BLINDING_GENERATOR.deref();

        let v = Fr::from(self.amount);
        let C = v * G_v + blinding * H;

        Commitment(C)
    }
}

impl ValueVar {
    pub fn commit(
        &self,
        value_blinding: Vec<UInt8<Fq>>,
    ) -> Result<BalanceCommitmentVar, SynthesisError> {
        let cs = self.amount().cs();
        let value_blinding_generator = ElementVar::new_constant(cs, *VALUE_BLINDING_GENERATOR)?;

        let asset_generator = self.asset_id.value_generator()?;
        let value_amount = self.amount();
        let commitment = asset_generator.scalar_mul_le(value_amount.to_bits_le()?.iter())?
            + value_blinding_generator.scalar_mul_le(value_blinding.to_bits_le()?.iter())?;

        Ok(BalanceCommitmentVar { inner: commitment })
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
pub struct Commitment(pub decaf377::Element);

impl Commitment {
    pub fn to_bytes(&self) -> [u8; 32] {
        (*self).into()
    }
}

pub static VALUE_BLINDING_GENERATOR: Lazy<decaf377::Element> = Lazy::new(|| {
    let s = Fq::from_le_bytes_mod_order(blake2b_simd::blake2b(b"decaf377-rdsa-binding").as_bytes());
    decaf377::Element::encode_to_curve(&s)
});

pub struct BalanceCommitmentVar {
    pub inner: ElementVar,
}

impl AllocVar<Commitment, Fq> for BalanceCommitmentVar {
    fn new_variable<T: std::borrow::Borrow<Commitment>>(
        cs: impl Into<ark_relations::r1cs::Namespace<Fq>>,
        f: impl FnOnce() -> Result<T, SynthesisError>,
        mode: ark_r1cs_std::prelude::AllocationMode,
    ) -> Result<Self, SynthesisError> {
        let ns = cs.into();
        let cs = ns.cs();
        let inner: Commitment = *f()?.borrow();
        match mode {
            AllocationMode::Constant => unimplemented!(),
            AllocationMode::Input => {
                let element_var: ElementVar = AllocVar::new_input(cs, || Ok(inner.0))?;
                Ok(Self { inner: element_var })
            }
            AllocationMode::Witness => unimplemented!(),
        }
    }
}

impl R1CSVar<Fq> for BalanceCommitmentVar {
    type Value = Commitment;

    fn cs(&self) -> ark_relations::r1cs::ConstraintSystemRef<Fq> {
        self.inner.cs()
    }

    fn value(&self) -> Result<Self::Value, SynthesisError> {
        let inner = self.inner.value()?;
        Ok(Commitment(inner))
    }
}

impl std::ops::Add<BalanceCommitmentVar> for BalanceCommitmentVar {
    type Output = Self;
    fn add(self, rhs: Self) -> Self::Output {
        Self {
            inner: self.inner + rhs.inner,
        }
    }
}

impl std::ops::Sub<BalanceCommitmentVar> for BalanceCommitmentVar {
    type Output = Self;
    fn sub(self, rhs: Self) -> Self::Output {
        Self {
            inner: self.inner - rhs.inner,
        }
    }
}

impl EqGadget<Fq> for BalanceCommitmentVar {
    fn is_eq(&self, other: &Self) -> Result<Boolean<Fq>, SynthesisError> {
        self.inner.is_eq(&other.inner)
    }
}

#[derive(thiserror::Error, Debug)]
pub enum Error {
    #[error("Invalid valid commitment")]
    InvalidBalanceCommitment,
}

impl std::ops::Add<Commitment> for Commitment {
    type Output = Commitment;
    fn add(self, rhs: Commitment) -> Self::Output {
        Commitment(self.0 + rhs.0)
    }
}

impl std::ops::Sub<Commitment> for Commitment {
    type Output = Commitment;
    fn sub(self, rhs: Commitment) -> Self::Output {
        Commitment(self.0 - rhs.0)
    }
}

impl std::ops::Neg for Commitment {
    type Output = Commitment;
    fn neg(self) -> Self::Output {
        Commitment(-self.0)
    }
}

impl From<Commitment> for [u8; 32] {
    fn from(commitment: Commitment) -> [u8; 32] {
        commitment.0.vartime_compress().0
    }
}

impl TryFrom<[u8; 32]> for Commitment {
    type Error = Error;

    fn try_from(bytes: [u8; 32]) -> Result<Commitment, Self::Error> {
        let inner = decaf377::Encoding(bytes)
            .vartime_decompress()
            .map_err(|_| Error::InvalidBalanceCommitment)?;

        Ok(Commitment(inner))
    }
}

impl TryFrom<&[u8]> for Commitment {
    type Error = Error;

    fn try_from(slice: &[u8]) -> Result<Commitment, Self::Error> {
        let bytes = slice[..]
            .try_into()
            .map_err(|_| Error::InvalidBalanceCommitment)?;

        let inner = decaf377::Encoding(bytes)
            .vartime_decompress()
            .map_err(|_| Error::InvalidBalanceCommitment)?;

        Ok(Commitment(inner))
    }
}

impl DomainType for Commitment {
    type Proto = pb::BalanceCommitment;
}

impl From<Commitment> for pb::BalanceCommitment {
    fn from(cv: Commitment) -> Self {
        Self {
            inner: cv.to_bytes().to_vec(),
        }
    }
}

impl TryFrom<pb::BalanceCommitment> for Commitment {
    type Error = anyhow::Error;
    fn try_from(value: pb::BalanceCommitment) -> Result<Self, Self::Error> {
        value.inner.as_slice().try_into().map_err(Into::into)
    }
}