use core::cmp::Ordering;
use core::fmt::Debug;
use core::num::ParseIntError;
use core::ops::Deref;
use core::str::FromStr;
use std::io;
use std::io::Write;
use amplify::hex::{Error, FromHex, ToHex};
use amplify::{hex, Array, Bytes32, Wrapper};
use bp::secp256k1::rand::thread_rng;
use commit_verify::{
CommitEncode, CommitVerify, CommitmentProtocol, Conceal, Digest, Sha256, UntaggedProtocol,
};
use secp256k1_zkp::rand::{Rng, RngCore};
use secp256k1_zkp::SECP256K1;
use strict_encoding::{
DecodeError, ReadTuple, StrictDecode, StrictDumb, StrictEncode, TypedRead, TypedWrite,
WriteTuple,
};
use super::{ConfidentialState, ExposedState};
use crate::{schema, StateCommitment, StateData, StateType, LIB_NAME_RGB};
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, From)]
#[display(inner)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB, tags = custom)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub enum FungibleState {
#[from]
#[strict_type(tag = 8)] Bits64(u64),
}
impl Default for FungibleState {
fn default() -> Self { FungibleState::Bits64(0) }
}
impl From<RevealedValue> for FungibleState {
fn from(revealed: RevealedValue) -> Self { revealed.value }
}
impl FromStr for FungibleState {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> { s.parse().map(FungibleState::Bits64) }
}
impl From<FungibleState> for u64 {
fn from(value: FungibleState) -> Self {
match value {
FungibleState::Bits64(val) => val,
}
}
}
impl FungibleState {
pub fn fungible_type(&self) -> schema::FungibleType {
match self {
FungibleState::Bits64(_) => schema::FungibleType::Unsigned64Bit,
}
}
pub fn as_u64(&self) -> u64 { (*self).into() }
}
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)]
#[display(Self::to_hex)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", from = "secp256k1_zkp::SecretKey")
)]
pub struct BlindingFactor(Bytes32);
impl Deref for BlindingFactor {
type Target = [u8; 32];
fn deref(&self) -> &Self::Target { self.0.as_inner() }
}
impl ToHex for BlindingFactor {
fn to_hex(&self) -> String { self.0.to_hex() }
}
impl FromHex for BlindingFactor {
fn from_hex(s: &str) -> Result<Self, Error> { Bytes32::from_hex(s).map(Self) }
fn from_byte_iter<I>(_: I) -> Result<Self, Error>
where I: Iterator<Item = Result<u8, Error>> + ExactSizeIterator + DoubleEndedIterator {
unreachable!()
}
}
impl FromStr for BlindingFactor {
type Err = hex::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> { Self::from_hex(s) }
}
impl From<secp256k1_zkp::SecretKey> for BlindingFactor {
fn from(key: secp256k1_zkp::SecretKey) -> Self { Self(Bytes32::from_inner(*key.as_ref())) }
}
impl From<BlindingFactor> for secp256k1_zkp::SecretKey {
fn from(bf: BlindingFactor) -> Self {
secp256k1_zkp::SecretKey::from_slice(bf.0.as_inner())
.expect("blinding factor is an invalid secret key")
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display, Error)]
#[display(doc_comments)]
pub struct FieldOrderOverflow;
impl TryFrom<[u8; 32]> for BlindingFactor {
type Error = FieldOrderOverflow;
fn try_from(array: [u8; 32]) -> Result<Self, Self::Error> {
secp256k1_zkp::SecretKey::from_slice(&array)
.map_err(|_| FieldOrderOverflow)
.map(Self::from)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB, rename = "RevealedFungible")]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))]
pub struct RevealedValue {
pub value: FungibleState,
pub blinding: BlindingFactor,
}
impl RevealedValue {
pub fn new<R: Rng + RngCore>(value: impl Into<FungibleState>, rng: &mut R) -> Self {
Self {
value: value.into(),
blinding: BlindingFactor::from(secp256k1_zkp::SecretKey::new(rng)),
}
}
pub fn with(value: impl Into<FungibleState>, blinding: impl Into<BlindingFactor>) -> Self {
Self {
value: value.into(),
blinding: blinding.into(),
}
}
}
impl ExposedState for RevealedValue {
type Confidential = ConcealedValue;
fn state_type(&self) -> StateType { StateType::Fungible }
fn state_data(&self) -> StateData { StateData::Fungible(*self) }
}
impl Conceal for RevealedValue {
type Concealed = ConcealedValue;
fn conceal(&self) -> Self::Concealed { ConcealedValue::commit(self) }
}
impl CommitEncode for RevealedValue {
fn commit_encode(&self, e: &mut impl Write) {
let commitment = PedersenCommitment::commit(self);
commitment.commit_encode(e);
}
}
impl PartialOrd for RevealedValue {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match self.value.partial_cmp(&other.value) {
None => None,
Some(Ordering::Equal) => self.blinding.0.partial_cmp(&other.blinding.0),
other => other,
}
}
}
impl Ord for RevealedValue {
fn cmp(&self, other: &Self) -> Ordering {
match self.value.cmp(&other.value) {
Ordering::Equal => self.blinding.0.cmp(&other.blinding.0),
other => other,
}
}
}
#[derive(Wrapper, Copy, Clone, Eq, PartialEq, Hash, Debug, From)]
#[wrapper(Deref, FromStr, Display, LowerHex)]
#[derive(StrictType)]
#[strict_type(lib = LIB_NAME_RGB)]
#[derive(CommitEncode)]
#[commit_encode(strategy = strict)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
pub struct PedersenCommitment(secp256k1_zkp::PedersenCommitment);
impl StrictDumb for PedersenCommitment {
fn strict_dumb() -> Self {
secp256k1_zkp::PedersenCommitment::from_slice(&[0x08; 33])
.expect("hardcoded pedersen commitment value")
.into()
}
}
impl StrictEncode for PedersenCommitment {
fn strict_encode<W: TypedWrite>(&self, writer: W) -> io::Result<W> {
writer.write_tuple::<Self>(|w| Ok(w.write_field(&self.0.serialize())?.complete()))
}
}
impl StrictDecode for PedersenCommitment {
fn strict_decode(reader: &mut impl TypedRead) -> Result<Self, DecodeError> {
reader.read_tuple(|r| {
let commitment = r.read_field::<[u8; 33]>()?;
secp256k1_zkp::PedersenCommitment::from_slice(&commitment)
.map_err(|_| {
DecodeError::DataIntegrityError(s!("invalid pedersen commitment data"))
})
.map(PedersenCommitment::from_inner)
})
}
}
impl CommitVerify<RevealedValue, UntaggedProtocol> for PedersenCommitment {
fn commit(revealed: &RevealedValue) -> Self {
use secp256k1_zkp::{Generator, Tag, Tweak};
let blinding = Tweak::from_inner(revealed.blinding.0.into_inner())
.expect("type guarantees of BlindingFactor are broken");
let FungibleState::Bits64(value) = revealed.value;
let one_key = secp256k1_zkp::SecretKey::from_slice(&secp256k1_zkp::constants::ONE)
.expect("secret key from a constant");
let g = secp256k1_zkp::PublicKey::from_secret_key(SECP256K1, &one_key);
let h: [u8; 32] = Sha256::digest(&g.serialize_uncompressed()).into();
let tag = Tag::from(h);
let generator = Generator::new_unblinded(SECP256K1, tag);
secp256k1_zkp::PedersenCommitment::new(SECP256K1, value, blinding, generator).into()
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", transparent)
)]
pub struct NoiseDumb(Array<u8, 512>);
impl Default for NoiseDumb {
fn default() -> Self {
let mut dumb = [0u8; 512];
thread_rng().fill(&mut dumb);
NoiseDumb(dumb.into())
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB, tags = custom)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub enum RangeProof {
#[strict_type(tag = 0xFF)]
Placeholder(NoiseDumb),
}
impl Default for RangeProof {
fn default() -> Self { RangeProof::Placeholder(default!()) }
}
pub struct PedersenProtocol;
impl CommitmentProtocol for PedersenProtocol {}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB, rename = "ConcealedFungible")]
#[derive(CommitEncode)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct ConcealedValue {
pub commitment: PedersenCommitment,
#[commit_encode(skip)]
pub range_proof: RangeProof,
}
impl ConfidentialState for ConcealedValue {
fn state_type(&self) -> StateType { StateType::Fungible }
fn state_commitment(&self) -> StateCommitment { StateCommitment::Fungible(*self) }
}
impl CommitVerify<RevealedValue, PedersenProtocol> for ConcealedValue {
#[allow(dead_code, unreachable_code, unused_variables)]
fn commit(revealed: &RevealedValue) -> Self {
panic!(
"Error: current version of RGB Core doesn't support production of bulletproofs; thus, \
fungible state must be never concealed"
);
let commitment = PedersenCommitment::commit(revealed);
let range_proof = RangeProof::default();
ConcealedValue {
commitment,
range_proof,
}
}
}
impl ConcealedValue {
pub fn verify(&self) -> bool {
match self.range_proof {
RangeProof::Placeholder(_) => false,
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Display, Error)]
#[display(doc_comments)]
pub enum RangeProofError {
InvalidBlinding(BlindingFactor),
BulletproofsAbsent,
}
impl ConcealedValue {
pub fn verify_range_proof(&self) -> Result<bool, RangeProofError> {
Err(RangeProofError::BulletproofsAbsent)
}
}
#[cfg(test)]
mod test {
use std::collections::HashSet;
use super::*;
#[test]
fn commitments_determinism() {
let value = RevealedValue::new(15, &mut thread_rng());
let generators = (0..10)
.into_iter()
.map(|_| {
let mut val = vec![];
value.commit_encode(&mut val);
val
})
.collect::<HashSet<_>>();
assert_eq!(generators.len(), 1);
}
}