darkfi_sdk/crypto/
blind.rs

1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2025 Dyne.org foundation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18
19use core::str::FromStr;
20
21#[cfg(feature = "async")]
22use darkfi_serial::{async_trait, AsyncDecodable, AsyncEncodable};
23use darkfi_serial::{Decodable, Encodable, SerialDecodable, SerialEncodable};
24
25use pasta_curves::{
26    group::ff::{Field, PrimeField},
27    pallas,
28};
29use rand_core::{CryptoRng, RngCore};
30
31use crate::error::ContractError;
32
33#[cfg(feature = "async")]
34pub trait EncDecode: Encodable + Decodable + AsyncEncodable + AsyncDecodable {}
35#[cfg(not(feature = "async"))]
36pub trait EncDecode: Encodable + Decodable {}
37
38impl EncDecode for pallas::Base {}
39impl EncDecode for pallas::Scalar {}
40
41/// Blinding factor used in bullas. Every bulla should contain one.
42#[derive(Debug, Copy, Clone, Eq, PartialEq, SerialEncodable, SerialDecodable)]
43pub struct Blind<F: Field + EncDecode>(pub F);
44
45impl<F: Field + EncDecode> Blind<F> {
46    pub const ZERO: Self = Self(F::ZERO);
47
48    pub fn random(rng: &mut (impl CryptoRng + RngCore)) -> Self {
49        Self(F::random(rng))
50    }
51
52    pub fn inner(&self) -> F {
53        self.0
54    }
55}
56
57impl<'a, F: Field + EncDecode> std::ops::Add<&'a Blind<F>> for &Blind<F> {
58    type Output = Blind<F>;
59
60    #[inline]
61    fn add(self, rhs: &'a Blind<F>) -> Blind<F> {
62        Blind(self.0.add(rhs.0))
63    }
64}
65
66impl<F: Field + EncDecode> std::ops::AddAssign for Blind<F> {
67    #[inline]
68    fn add_assign(&mut self, other: Self) {
69        self.0.add_assign(other.0)
70    }
71}
72
73pub type BaseBlind = Blind<pallas::Base>;
74pub type ScalarBlind = Blind<pallas::Scalar>;
75
76impl From<u64> for BaseBlind {
77    fn from(x: u64) -> Self {
78        Self(pallas::Base::from(x))
79    }
80}
81
82impl FromStr for BaseBlind {
83    type Err = ContractError;
84
85    /// Tries to create a `BaseBlind` object from a base58 encoded string.
86    fn from_str(enc: &str) -> Result<Self, Self::Err> {
87        let decoded = bs58::decode(enc).into_vec()?;
88        if decoded.len() != 32 {
89            return Err(Self::Err::IoError(
90                "Failed decoding BaseBlind from bytes, len is not 32".to_string(),
91            ))
92        }
93
94        match pallas::Base::from_repr(decoded.try_into().unwrap()).into() {
95            Some(k) => Ok(Self(k)),
96            None => Err(ContractError::IoError("Could not convert bytes to BaseBlind".to_string())),
97        }
98    }
99}
100
101impl core::fmt::Display for BaseBlind {
102    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
103        let disp: String = bs58::encode(self.0.to_repr()).into_string();
104        write!(f, "{}", disp)
105    }
106}
107
108impl From<u64> for ScalarBlind {
109    fn from(x: u64) -> Self {
110        Self(pallas::Scalar::from(x))
111    }
112}
113
114impl FromStr for ScalarBlind {
115    type Err = ContractError;
116
117    /// Tries to create a `ScalarBlind` object from a base58 encoded string.
118    fn from_str(enc: &str) -> Result<Self, Self::Err> {
119        let decoded = bs58::decode(enc).into_vec()?;
120        if decoded.len() != 32 {
121            return Err(Self::Err::IoError(
122                "Failed decoding ScalarBlind from bytes, len is not 32".to_string(),
123            ))
124        }
125
126        match pallas::Scalar::from_repr(decoded.try_into().unwrap()).into() {
127            Some(k) => Ok(Self(k)),
128            None => {
129                Err(ContractError::IoError("Could not convert bytes to ScalarBlind".to_string()))
130            }
131        }
132    }
133}
134
135impl core::fmt::Display for ScalarBlind {
136    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
137        let disp: String = bs58::encode(self.0.to_repr()).into_string();
138        write!(f, "{}", disp)
139    }
140}