-
Notifications
You must be signed in to change notification settings - Fork 25
/
commitment.rs
268 lines (236 loc) · 9.49 KB
/
commitment.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
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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
// Copyright 2019 The Tari Project
// SPDX-License-Identifier: BSD-3-Clause
//! A commitment is like a sealed envelope. You put some information inside the envelope, and then seal (commit) it.
//! You can't change what you've said, but also, no-one knows what you've said until you're ready to open (open) the
//! envelope and reveal its contents. Also it's a special envelope that can only be opened by a special opener that
//! you keep safe in your drawer.
use core::{
cmp::Ordering,
convert::TryFrom,
hash::{Hash, Hasher},
ops::{Add, Mul, Sub},
};
use tari_utilities::{ByteArray, ByteArrayError};
use crate::{
alloc::string::ToString,
errors::CommitmentError,
keys::{PublicKey, SecretKey},
};
/// There are also different types of commitments that vary in their security guarantees, but all of them are
/// represented by binary data; so [HomomorphicCommitment](trait.HomomorphicCommitment.html) implements
/// [ByteArray](trait.ByteArray.html).
///
/// The Homomorphic part means, more or less, that commitments follow some of the standard rules of
/// arithmetic. Adding two commitments is the same as committing to the sum of their parts:
/// $$ \begin{aligned}
/// C_1 &= v_1.H + k_1.G \\\\
/// C_2 &= v_2.H + k_2.G \\\\
/// \therefore C_1 + C_2 &= (v_1 + v_2)H + (k_1 + k_2)G
/// \end{aligned} $$
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct HomomorphicCommitment<P>(pub(crate) P);
#[cfg(feature = "borsh")]
impl<P: borsh::BorshDeserialize> borsh::BorshDeserialize for HomomorphicCommitment<P> {
fn deserialize_reader<R>(reader: &mut R) -> Result<Self, borsh::io::Error>
where R: borsh::io::Read {
Ok(Self(P::deserialize_reader(reader)?))
}
}
#[cfg(feature = "borsh")]
impl<P: borsh::BorshSerialize> borsh::BorshSerialize for HomomorphicCommitment<P> {
fn serialize<W: borsh::io::Write>(&self, writer: &mut W) -> borsh::io::Result<()> {
self.0.serialize(writer)
}
}
impl<P> HomomorphicCommitment<P>
where P: PublicKey
{
/// Get this commitment as a public key point
pub fn as_public_key(&self) -> &P {
&self.0
}
/// Converts a public key into a commitment
pub fn from_public_key(p: &P) -> HomomorphicCommitment<P> {
HomomorphicCommitment(p.clone())
}
}
impl<P> ByteArray for HomomorphicCommitment<P>
where P: PublicKey
{
fn from_canonical_bytes(bytes: &[u8]) -> Result<Self, ByteArrayError> {
let p = P::from_canonical_bytes(bytes)?;
Ok(Self(p))
}
fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
}
impl<P> PartialOrd for HomomorphicCommitment<P>
where P: PublicKey
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<P> Ord for HomomorphicCommitment<P>
where P: PublicKey
{
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}
/// Add two commitments together. Note! There is no check that the bases are equal.
impl<'b, P> Add for &'b HomomorphicCommitment<P>
where
P: PublicKey,
&'b P: Add<&'b P, Output = P>,
{
type Output = HomomorphicCommitment<P>;
fn add(self, rhs: &'b HomomorphicCommitment<P>) -> Self::Output {
HomomorphicCommitment(&self.0 + &rhs.0)
}
}
/// Add a public key to a commitment. Note! There is no check that the bases are equal.
impl<'b, P> Add<&'b P> for &'b HomomorphicCommitment<P>
where
P: PublicKey,
&'b P: Add<&'b P, Output = P>,
{
type Output = HomomorphicCommitment<P>;
fn add(self, rhs: &'b P) -> Self::Output {
HomomorphicCommitment(&self.0 + rhs)
}
}
/// Subtracts the left commitment from the right commitment. Note! There is no check that the bases are equal.
impl<'b, P> Sub for &'b HomomorphicCommitment<P>
where
P: PublicKey,
&'b P: Sub<&'b P, Output = P>,
{
type Output = HomomorphicCommitment<P>;
fn sub(self, rhs: &'b HomomorphicCommitment<P>) -> Self::Output {
HomomorphicCommitment(&self.0 - &rhs.0)
}
}
/// Multiply the commitment with a private key
impl<'a, 'b, P, K> Mul<&'b K> for &'a HomomorphicCommitment<P>
where
P: PublicKey<K = K>,
K: SecretKey,
&'b K: Mul<&'a P, Output = P>,
{
type Output = HomomorphicCommitment<P>;
fn mul(self, rhs: &'b K) -> HomomorphicCommitment<P> {
let p = rhs * &self.0;
HomomorphicCommitment::<P>::from_public_key(&p)
}
}
impl<P: PublicKey> Hash for HomomorphicCommitment<P> {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write(self.as_bytes())
}
}
impl<P: PublicKey> PartialEq for HomomorphicCommitment<P> {
fn eq(&self, other: &Self) -> bool {
self.as_public_key().eq(other.as_public_key())
}
}
impl<P: PublicKey> Eq for HomomorphicCommitment<P> {}
/// A trait for creating commitments
pub trait HomomorphicCommitmentFactory {
/// The type of public key that the underlying commitment will be based on
type P: PublicKey;
/// Create a new commitment with the blinding factor _k_ and value _v_ provided. The implementing type will provide
/// the base values
fn commit(&self, k: &<Self::P as PublicKey>::K, v: &<Self::P as PublicKey>::K) -> HomomorphicCommitment<Self::P>;
/// Return an identity point for addition using the specified base point. This is a commitment to zero with a zero
/// blinding factor on the base point
fn zero(&self) -> HomomorphicCommitment<Self::P>;
/// Test whether the given blinding factor _k_ and value _v_ open the given commitment
fn open(
&self,
k: &<Self::P as PublicKey>::K,
v: &<Self::P as PublicKey>::K,
commitment: &HomomorphicCommitment<Self::P>,
) -> bool;
/// Create a commitment from a blinding factor _k_ and an integer value
fn commit_value(&self, k: &<Self::P as PublicKey>::K, value: u64) -> HomomorphicCommitment<Self::P>;
/// Test whether the given private key and value open the given commitment
fn open_value(&self, k: &<Self::P as PublicKey>::K, v: u64, commitment: &HomomorphicCommitment<Self::P>) -> bool;
}
/// A trait for creating extended commitments that are based on a public key
pub trait ExtendedHomomorphicCommitmentFactory {
/// The type of public key that the underlying commitment will be based on
type P: PublicKey;
/// Create a new commitment with the blinding factor vector **k** and value _v_ provided. The implementing type will
/// provide the base values
fn commit_extended(
&self,
k_vec: &[<Self::P as PublicKey>::K],
v: &<Self::P as PublicKey>::K,
) -> Result<HomomorphicCommitment<Self::P>, CommitmentError>;
/// Return an identity point for addition using the specified base points. This is a commitment to zero with a zero
/// blinding factor vector on the base points
fn zero_extended(&self) -> HomomorphicCommitment<Self::P>;
/// Test whether the given blinding factor vector **k** and value _v_ open the given commitment
fn open_extended(
&self,
k_vec: &[<Self::P as PublicKey>::K],
v: &<Self::P as PublicKey>::K,
commitment: &HomomorphicCommitment<Self::P>,
) -> Result<bool, CommitmentError>;
/// Create a commitment from a blinding factor vector **k** and an integer value
fn commit_value_extended(
&self,
k_vec: &[<Self::P as PublicKey>::K],
value: u64,
) -> Result<HomomorphicCommitment<Self::P>, CommitmentError>;
/// Test whether the given private keys and value open the given commitment
fn open_value_extended(
&self,
k_vec: &[<Self::P as PublicKey>::K],
v: u64,
commitment: &HomomorphicCommitment<Self::P>,
) -> Result<bool, CommitmentError>;
}
/// The extension degree for extended Pedersen commitments. Currently this is limited to adding 5 base points to the
/// default Pedersen commitment, but in theory it could be arbitrarily long, although practically, very few if any
/// test cases will need to add more than 2 base points.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ExtensionDegree {
/// Default Pedersen commitment (`C = v.H + sum(k_i.G_i)|i=1`)
DefaultPedersen = 1,
/// Pedersen commitment extended with one degree (`C = v.H + sum(k_i.G_i)|i=1..2`)
AddOneBasePoint = 2,
/// Pedersen commitment extended with two degrees (`C = v.H + sum(k_i.G_i)|i=1..3`)
AddTwoBasePoints = 3,
/// Pedersen commitment extended with three degrees (`C = v.H + sum(k_i.G_i)|i=1..4`)
AddThreeBasePoints = 4,
/// Pedersen commitment extended with four degrees (`C = v.H + sum(k_i.G_i)|i=1..5`)
AddFourBasePoints = 5,
/// Pedersen commitment extended with five degrees (`C = v.H + sum(k_i.G_i)|i=1..6`)
AddFiveBasePoints = 6,
}
impl ExtensionDegree {
/// Helper function to convert a size into an extension degree
pub fn try_from_size(size: usize) -> Result<ExtensionDegree, CommitmentError> {
match size {
1 => Ok(ExtensionDegree::DefaultPedersen),
2 => Ok(ExtensionDegree::AddOneBasePoint),
3 => Ok(ExtensionDegree::AddTwoBasePoints),
4 => Ok(ExtensionDegree::AddThreeBasePoints),
5 => Ok(ExtensionDegree::AddFourBasePoints),
6 => Ok(ExtensionDegree::AddFiveBasePoints),
_ => Err(CommitmentError::CommitmentExtensionDegree {
reason: "Extension degree not valid".to_string(),
}),
}
}
}
impl TryFrom<usize> for ExtensionDegree {
type Error = CommitmentError;
fn try_from(value: usize) -> Result<Self, Self::Error> {
Self::try_from_size(value)
}
}