Skip to content

Commit 8cc6e02

Browse files
committed
confidential: move commitment types into their own modules
The code for value, nonce, asset, have diverged in various ways. By splitting them into multiple files I hope to make diffing them easier. Code move only; the next commits will make chonges to bring the three files closer together. Also I am quietly relicensing this stuff from CC0 to MIT+Apache. I am not doing this to be sneaky; I was just creating new files so it was an opportunity to use a new license header. See rust-bitcoin/rust-bitcoin#5849
1 parent 278ad33 commit 8cc6e02

4 files changed

Lines changed: 1044 additions & 996 deletions

File tree

src/confidential/asset.rs

Lines changed: 362 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
//! Confiential Assets
4+
5+
use core::{fmt, str};
6+
use std::io;
7+
8+
use secp256k1_zkp::{self, Generator, Secp256k1, Signing, Tweak, ZERO_TWEAK};
9+
use secp256k1_zkp::rand::Rng;
10+
#[cfg(feature = "serde")]
11+
use serde::{Deserialize, Deserializer, Serialize, Serializer};
12+
13+
use crate::encode::{self, Decodable, Encodable};
14+
use crate::issuance::AssetId;
15+
16+
/// A CT commitment to an asset
17+
#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
18+
pub enum Asset {
19+
/// No value
20+
#[default]
21+
Null,
22+
/// Asset entropy is explicitly encoded
23+
Explicit(AssetId),
24+
/// Asset is committed
25+
Confidential(Generator),
26+
}
27+
28+
impl Asset {
29+
/// Create asset commitment.
30+
pub fn new_confidential<C: Signing>(
31+
secp: &Secp256k1<C>,
32+
asset: AssetId,
33+
bf: AssetBlindingFactor,
34+
) -> Self {
35+
Asset::Confidential(Generator::new_blinded(
36+
secp,
37+
asset.into_tag(),
38+
bf.into_inner(),
39+
))
40+
}
41+
42+
/// Serialized length, in bytes
43+
pub fn encoded_length(&self) -> usize {
44+
match *self {
45+
Asset::Null => 1,
46+
Asset::Explicit(..) => 33,
47+
Asset::Confidential(..) => 33,
48+
}
49+
}
50+
51+
/// Create from commitment.
52+
pub fn from_commitment(bytes: &[u8]) -> Result<Self, encode::Error> {
53+
Ok(Asset::Confidential(Generator::from_slice(bytes)?))
54+
}
55+
56+
/// Check if the object is null.
57+
pub fn is_null(&self) -> bool {
58+
matches!(*self, Asset::Null)
59+
}
60+
61+
/// Check if the object is explicit.
62+
pub fn is_explicit(&self) -> bool {
63+
matches!(*self, Asset::Explicit(_))
64+
}
65+
66+
/// Check if the object is confidential.
67+
pub fn is_confidential(&self) -> bool {
68+
matches!(*self, Asset::Confidential(_))
69+
}
70+
71+
/// Returns the explicit inner value.
72+
/// Returns [None] if [`Asset::is_explicit`] returns false.
73+
pub fn explicit(&self) -> Option<AssetId> {
74+
match *self {
75+
Asset::Explicit(i) => Some(i),
76+
_ => None,
77+
}
78+
}
79+
80+
/// Returns the confidential commitment in case of a confidential value.
81+
/// Returns [None] if [`Asset::is_confidential`] returns false.
82+
pub fn commitment(&self) -> Option<Generator> {
83+
match *self {
84+
Asset::Confidential(i) => Some(i),
85+
_ => None,
86+
}
87+
}
88+
89+
/// Internally used function for getting the generator from asset
90+
/// Used in the amount verification check
91+
/// Returns [`None`] is the asset is [`Asset::Null`]
92+
/// Converts a explicit asset into a generator and returns the confidential
93+
/// generator as is.
94+
pub fn into_asset_gen<C: secp256k1_zkp::Signing> (
95+
self,
96+
secp: &Secp256k1<C>,
97+
) -> Option<Generator> {
98+
match self {
99+
// Only error is Null error which is dealt with later
100+
// when we have more context information about it.
101+
Asset::Null => None,
102+
Asset::Explicit(x) => {
103+
Some(Generator::new_unblinded(secp, x.into_tag()))
104+
}
105+
Asset::Confidential(gen) => Some(gen),
106+
}
107+
}
108+
}
109+
110+
impl From<Generator> for Asset {
111+
fn from(from: Generator) -> Self {
112+
Asset::Confidential(from)
113+
}
114+
}
115+
116+
impl fmt::Display for Asset {
117+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
118+
match *self {
119+
Asset::Null => f.write_str("null"),
120+
Asset::Explicit(n) => write!(f, "{}", n),
121+
Asset::Confidential(generator) => write!(f, "{:02x}", generator),
122+
}
123+
}
124+
}
125+
126+
impl Encodable for Asset {
127+
fn consensus_encode<S: io::Write>(&self, mut s: S) -> Result<usize, encode::Error> {
128+
match *self {
129+
Asset::Null => 0u8.consensus_encode(s),
130+
Asset::Explicit(n) => {
131+
1u8.consensus_encode(&mut s)?;
132+
Ok(1 + n.consensus_encode(&mut s)?)
133+
}
134+
Asset::Confidential(generator) => {
135+
s.write_all(&generator.serialize())?;
136+
Ok(33)
137+
}
138+
}
139+
}
140+
}
141+
142+
impl Decodable for Asset {
143+
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> {
144+
let prefix = u8::consensus_decode(&mut d)?;
145+
146+
match prefix {
147+
0 => Ok(Asset::Null),
148+
1 => {
149+
let explicit = Decodable::consensus_decode(&mut d)?;
150+
Ok(Asset::Explicit(explicit))
151+
}
152+
p if p == 0x0a || p == 0x0b => {
153+
let mut comm = [0u8; 33];
154+
comm[0] = p;
155+
d.read_exact(&mut comm[1..])?;
156+
Ok(Asset::Confidential(Generator::from_slice(&comm[..])?))
157+
}
158+
p => Err(encode::Error::InvalidConfidentialPrefix(p)),
159+
}
160+
}
161+
}
162+
163+
#[cfg(feature = "serde")]
164+
impl Serialize for Asset {
165+
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
166+
use serde::ser::SerializeSeq;
167+
168+
let seq_len = match *self {
169+
Asset::Null => 1,
170+
Asset::Explicit(_) | Asset::Confidential(_) => 2
171+
};
172+
let mut seq = s.serialize_seq(Some(seq_len))?;
173+
174+
match *self {
175+
Asset::Null => seq.serialize_element(&0u8)?,
176+
Asset::Explicit(n) => {
177+
seq.serialize_element(&1u8)?;
178+
seq.serialize_element(&n)?;
179+
}
180+
Asset::Confidential(commitment) => {
181+
seq.serialize_element(&2u8)?;
182+
seq.serialize_element(&commitment)?;
183+
}
184+
}
185+
seq.end()
186+
}
187+
}
188+
189+
#[cfg(feature = "serde")]
190+
impl<'de> Deserialize<'de> for Asset {
191+
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
192+
use serde::de::{Error, SeqAccess, Visitor};
193+
struct CommitVisitor;
194+
195+
impl<'de> Visitor<'de> for CommitVisitor {
196+
type Value = Asset;
197+
198+
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
199+
f.write_str("a committed value")
200+
}
201+
202+
fn visit_seq<A: SeqAccess<'de>>(self, mut access: A) -> Result<Asset, A::Error> {
203+
let prefix = access.next_element::<u8>()?;
204+
match prefix {
205+
Some(0) => Ok(Asset::Null),
206+
Some(1) => {
207+
match access.next_element()? {
208+
Some(x) => Ok(Asset::Explicit(x)),
209+
None => Err(A::Error::custom("missing explicit asset")),
210+
}
211+
}
212+
Some(2) => {
213+
match access.next_element()? {
214+
Some(x) => Ok(Asset::Confidential(x)),
215+
None => Err(A::Error::custom("missing generator")),
216+
}
217+
}
218+
_ => Err(A::Error::custom("wrong or missing prefix")),
219+
}
220+
}
221+
}
222+
223+
d.deserialize_seq(CommitVisitor)
224+
}
225+
}
226+
227+
228+
/// Blinding factor used for asset commitments.
229+
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
230+
pub struct AssetBlindingFactor(pub(crate) Tweak);
231+
232+
impl AssetBlindingFactor {
233+
/// Generate random asset blinding factor.
234+
pub fn new<R: Rng>(rng: &mut R) -> Self {
235+
AssetBlindingFactor(Tweak::new(rng))
236+
}
237+
238+
/// Parse a blinding factor from a 64-character hex string.
239+
#[deprecated(since = "0.27.0", note = "use s.parse() instead")]
240+
pub fn from_hex(s: &str) -> Result<Self, encode::Error> {
241+
s.parse()
242+
}
243+
244+
/// Create from bytes.
245+
pub fn from_byte_array(bytes: [u8; 32]) -> Result<Self, secp256k1_zkp::Error> {
246+
Ok(AssetBlindingFactor(Tweak::from_inner(bytes)?))
247+
}
248+
249+
/// Create from bytes.
250+
pub fn from_slice(bytes: &[u8]) -> Result<Self, secp256k1_zkp::Error> {
251+
Ok(AssetBlindingFactor(Tweak::from_slice(bytes)?))
252+
}
253+
254+
/// Returns the inner value.
255+
pub fn into_inner(self) -> Tweak {
256+
self.0
257+
}
258+
259+
/// Get a unblinded/zero `AssetBlinding` factor
260+
pub fn zero() -> Self {
261+
AssetBlindingFactor(ZERO_TWEAK)
262+
}
263+
}
264+
265+
impl core::borrow::Borrow<[u8]> for AssetBlindingFactor {
266+
fn borrow(&self) -> &[u8] { &self.0[..] }
267+
}
268+
269+
hex::impl_fmt_traits! {
270+
#[display_backward(true)]
271+
impl fmt_traits for AssetBlindingFactor {
272+
const LENGTH: usize = 32;
273+
}
274+
}
275+
276+
impl str::FromStr for AssetBlindingFactor {
277+
type Err = encode::Error;
278+
279+
fn from_str(s: &str) -> Result<Self, Self::Err> {
280+
let mut slice: [u8; 32] = hex::decode_to_array(s)?;
281+
slice.reverse();
282+
283+
let inner = Tweak::from_inner(slice)?;
284+
Ok(AssetBlindingFactor(inner))
285+
}
286+
}
287+
288+
#[cfg(feature = "serde")]
289+
impl Serialize for AssetBlindingFactor {
290+
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
291+
if s.is_human_readable() {
292+
s.collect_str(&self)
293+
} else {
294+
s.serialize_bytes(&self.0[..])
295+
}
296+
}
297+
}
298+
299+
#[cfg(feature = "serde")]
300+
impl<'de> Deserialize<'de> for AssetBlindingFactor {
301+
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<AssetBlindingFactor, D::Error> {
302+
if d.is_human_readable() {
303+
struct HexVisitor;
304+
305+
impl ::serde::de::Visitor<'_> for HexVisitor {
306+
type Value = AssetBlindingFactor;
307+
308+
fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
309+
formatter.write_str("an ASCII hex string")
310+
}
311+
312+
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
313+
where
314+
E: ::serde::de::Error,
315+
{
316+
if let Ok(hex) = ::std::str::from_utf8(v) {
317+
hex.parse().map_err(E::custom)
318+
} else {
319+
Err(E::invalid_value(::serde::de::Unexpected::Bytes(v), &self))
320+
}
321+
}
322+
323+
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
324+
where
325+
E: ::serde::de::Error,
326+
{
327+
v.parse().map_err(E::custom)
328+
}
329+
}
330+
331+
d.deserialize_str(HexVisitor)
332+
} else {
333+
struct BytesVisitor;
334+
335+
impl ::serde::de::Visitor<'_> for BytesVisitor {
336+
type Value = AssetBlindingFactor;
337+
338+
fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
339+
formatter.write_str("a bytestring")
340+
}
341+
342+
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
343+
where
344+
E: ::serde::de::Error,
345+
{
346+
use core::convert::TryFrom;
347+
348+
match <[u8; 32]>::try_from(v) {
349+
Ok(ret) => {
350+
let inner = Tweak::from_inner(ret).map_err(E::custom)?;
351+
Ok(AssetBlindingFactor(inner))
352+
}
353+
Err(_) => Err(E::invalid_length(v.len(), &stringify!($len))),
354+
}
355+
}
356+
}
357+
358+
d.deserialize_bytes(BytesVisitor)
359+
}
360+
}
361+
}
362+

0 commit comments

Comments
 (0)