use std::{fmt, io};
use crate::consensus::encode::{self, serialize, Decodable, Encodable, VarInt};
use crate::cryptonote::hash;
use crate::cryptonote::onetime_key::KeyGenerator;
use crate::util::key::H;
use crate::{PublicKey, ViewPair};
#[cfg(feature = "serde")]
use serde_big_array::BigArray;
#[cfg(feature = "serde")]
use serde_crate::{Deserialize, Serialize};
use curve25519_dalek::constants::ED25519_BASEPOINT_POINT;
use curve25519_dalek::edwards::EdwardsPoint;
use curve25519_dalek::scalar::Scalar;
use sealed::sealed;
use thiserror::Error;
use std::convert::TryInto;
#[derive(Error, Debug, PartialEq)]
pub enum Error {
#[error("Unknown RingCt type")]
UnknownRctType,
}
#[derive(Clone, Copy, PartialEq, Hash, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
pub struct Key {
pub key: [u8; 32],
}
impl_hex_display!(Key, key);
impl_consensus_encoding!(Key, key);
impl Key {
fn new() -> Key {
Key { key: [0; 32] }
}
}
impl From<[u8; 32]> for Key {
fn from(key: [u8; 32]) -> Self {
Self { key }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
pub struct Key64 {
#[cfg_attr(feature = "serde", serde(with = "BigArray"))]
pub keys: [Key; 64],
}
impl Key64 {
fn new() -> Key64 {
Key64 {
keys: [Key::new(); 64],
}
}
}
impl From<[Key; 64]> for Key64 {
fn from(keys: [Key; 64]) -> Self {
Self { keys }
}
}
impl Decodable for Key64 {
fn consensus_decode<D: io::Read>(d: &mut D) -> Result<Key64, encode::Error> {
let mut key64 = Key64::new();
for i in 0..64 {
let key: Key = Decodable::consensus_decode(d)?;
key64.keys[i] = key;
}
Ok(key64)
}
}
#[sealed]
impl crate::consensus::encode::Encodable for Key64 {
fn consensus_encode<S: io::Write>(&self, s: &mut S) -> Result<usize, io::Error> {
let mut len = 0;
for i in 0..64 {
len += self.keys[i].consensus_encode(s)?;
}
Ok(len)
}
}
impl fmt::Display for Key64 {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
for key in self.keys.iter() {
writeln!(fmt, "{}", key)?;
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
pub struct CtKey {
pub mask: Key,
}
impl fmt::Display for CtKey {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
writeln!(fmt, "Mask: {}", self.mask)
}
}
impl_consensus_encoding!(CtKey, mask);
#[derive(Debug)]
#[allow(non_snake_case)]
pub struct MultisigKlrki {
pub K: Key,
pub L: Key,
pub R: Key,
pub ki: Key,
}
impl_consensus_encoding!(MultisigKlrki, K, L, R, ki);
#[derive(Debug)]
pub struct MultisigOut {
pub c: Vec<Key>,
}
impl_consensus_encoding!(MultisigOut, c);
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
pub enum EcdhInfo {
Standard {
mask: Key,
amount: Key,
},
Bulletproof {
amount: hash::Hash8,
},
}
impl EcdhInfo {
pub fn open_commitment(
&self,
view_pair: &ViewPair,
tx_pubkey: &PublicKey,
index: usize,
candidate_commitment: &EdwardsPoint,
) -> Option<Opening> {
let shared_key = KeyGenerator::from_key(view_pair, *tx_pubkey).get_rvn_scalar(index);
let (amount, blinding_factor) = match self {
EcdhInfo::Standard { mask, amount } => {
let shared_sec1 = hash::Hash::new(shared_key.as_bytes()).to_bytes();
let shared_sec2 = hash::Hash::new(&shared_sec1).to_bytes();
let mask_scalar = Scalar::from_bytes_mod_order(mask.key)
- Scalar::from_bytes_mod_order(shared_sec1);
let amount_scalar = Scalar::from_bytes_mod_order(amount.key)
- Scalar::from_bytes_mod_order(shared_sec2);
let amount_significant_bytes = amount_scalar.to_bytes()[0..8]
.try_into()
.expect("Can't fail");
let amount = u64::from_le_bytes(amount_significant_bytes);
(amount, mask_scalar)
}
EcdhInfo::Bulletproof { amount } => {
let amount = xor_amount(amount.0, shared_key.scalar);
let mask = mask(shared_key.scalar);
(u64::from_le_bytes(amount), mask)
}
};
let amount_scalar = Scalar::from(amount);
let expected_commitment = ED25519_BASEPOINT_POINT * blinding_factor
+ H.point.decompress().unwrap() * amount_scalar;
if &expected_commitment != candidate_commitment {
return None;
}
Some(Opening {
amount,
blinding_factor,
commitment: expected_commitment,
})
}
}
#[derive(Debug)]
pub struct Opening {
pub amount: u64,
pub blinding_factor: Scalar,
pub commitment: EdwardsPoint,
}
fn xor_amount(amount: [u8; 8], shared_key: Scalar) -> [u8; 8] {
let mut amount_key = b"amount".to_vec();
amount_key.extend(shared_key.as_bytes());
let hash_shared_key = hash::Hash::new(&amount_key).to_fixed_bytes();
let hash_shared_key_significant_bytes = hash_shared_key[0..8]
.try_into()
.expect("hash_shared_key create above has 32 bytes");
(u64::from_le_bytes(amount) ^ u64::from_le_bytes(hash_shared_key_significant_bytes))
.to_le_bytes()
}
fn mask(scalar: Scalar) -> Scalar {
let mut commitment_key = b"commitment_mask".to_vec();
commitment_key.extend(scalar.as_bytes());
hash::Hash::hash_to_scalar(&commitment_key).scalar
}
impl fmt::Display for EcdhInfo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self {
EcdhInfo::Standard { mask, amount } => {
writeln!(fmt, "Standard")?;
writeln!(fmt, "Mask: {}", mask)?;
writeln!(fmt, "Amount: {}", amount)?;
}
EcdhInfo::Bulletproof { amount } => {
writeln!(fmt, "Bulletproof2")?;
writeln!(fmt, "Amount: {}", amount)?;
}
};
Ok(())
}
}
impl EcdhInfo {
fn consensus_decode<D: io::Read>(
d: &mut D,
rct_type: RctType,
) -> Result<EcdhInfo, encode::Error> {
match rct_type {
RctType::Full
| RctType::Simple
| RctType::FullBulletproof
| RctType::SimpleBulletproof
| RctType::Bulletproof
| RctType::Null => Ok(EcdhInfo::Standard {
mask: Decodable::consensus_decode(d)?,
amount: Decodable::consensus_decode(d)?,
}),
RctType::Bulletproof2 | RctType::Clsag | RctType::BulletproofPlus => {
Ok(EcdhInfo::Bulletproof {
amount: Decodable::consensus_decode(d)?,
})
}
}
}
}
#[sealed]
impl crate::consensus::encode::Encodable for EcdhInfo {
fn consensus_encode<S: io::Write>(&self, s: &mut S) -> Result<usize, io::Error> {
let mut len = 0;
match self {
EcdhInfo::Standard { mask, amount } => {
len += mask.consensus_encode(s)?;
len += amount.consensus_encode(s)?;
}
EcdhInfo::Bulletproof { amount } => {
len += amount.consensus_encode(s)?;
}
}
Ok(len)
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
pub struct BoroSig {
pub s0: Key64,
pub s1: Key64,
pub ee: Key,
}
impl_consensus_encoding!(BoroSig, s0, s1, ee);
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
pub struct MgSig {
pub ss: Vec<Vec<Key>>,
pub cc: Key,
}
#[sealed]
impl crate::consensus::encode::Encodable for MgSig {
fn consensus_encode<S: io::Write>(&self, s: &mut S) -> Result<usize, io::Error> {
let mut len = 0;
for ss in self.ss.iter() {
len += encode_sized_vec!(ss, s);
}
len += self.cc.consensus_encode(s)?;
Ok(len)
}
}
#[derive(Debug, Clone)]
#[allow(non_snake_case)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
pub struct Clsag {
pub s: Vec<Key>,
pub c1: Key,
pub D: Key,
}
#[sealed]
impl crate::consensus::encode::Encodable for Clsag {
fn consensus_encode<S: io::Write>(&self, s: &mut S) -> Result<usize, io::Error> {
let mut len = 0;
len += encode_sized_vec!(self.s, s);
len += self.c1.consensus_encode(s)?;
len += self.D.consensus_encode(s)?;
Ok(len)
}
}
#[derive(Debug, Clone)]
#[allow(non_snake_case)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
pub struct RangeSig {
pub asig: BoroSig,
pub Ci: Key64,
}
impl_consensus_encoding!(RangeSig, asig, Ci);
#[derive(Debug, Clone)]
#[allow(non_snake_case)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
pub struct Bulletproof {
pub A: Key,
pub S: Key,
pub T1: Key,
pub T2: Key,
pub taux: Key,
pub mu: Key,
pub L: Vec<Key>,
pub R: Vec<Key>,
pub a: Key,
pub b: Key,
pub t: Key,
}
impl_consensus_encoding!(Bulletproof, A, S, T1, T2, taux, mu, L, R, a, b, t);
#[derive(Debug, Clone)]
#[allow(non_snake_case)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
pub struct BulletproofPlus {
pub A: Key,
pub A1: Key,
pub B: Key,
pub r1: Key,
pub s1: Key,
pub d1: Key,
pub L: Vec<Key>,
pub R: Vec<Key>,
}
impl_consensus_encoding!(BulletproofPlus, A, A1, B, r1, s1, d1, L, R);
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
pub struct RctSigBase {
pub rct_type: RctType,
pub txn_fee: VarInt,
pub pseudo_outs: Vec<Key>,
pub ecdh_info: Vec<EcdhInfo>,
pub out_pk: Vec<CtKey>,
}
impl fmt::Display for RctSigBase {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
writeln!(fmt, "RCT type: {}", self.rct_type)?;
writeln!(fmt, "Tx fee: {}", self.txn_fee)?;
for out in &self.pseudo_outs {
writeln!(fmt, "Pseudo out: {}", out)?;
}
for ecdh in &self.ecdh_info {
writeln!(fmt, "Ecdh info: {}", ecdh)?;
}
for out in &self.out_pk {
writeln!(fmt, "Out pk: {}", out)?;
}
Ok(())
}
}
impl RctSigBase {
pub fn consensus_decode<D: io::Read>(
d: &mut D,
inputs: usize,
outputs: usize,
) -> Result<Option<RctSigBase>, encode::Error> {
let rct_type: RctType = Decodable::consensus_decode(d)?;
match rct_type {
RctType::Null => Ok(Some(RctSigBase {
rct_type: RctType::Null,
txn_fee: Default::default(),
pseudo_outs: vec![],
ecdh_info: vec![],
out_pk: vec![],
})),
RctType::Full
| RctType::Simple
| RctType::FullBulletproof
| RctType::SimpleBulletproof
| RctType::Bulletproof
| RctType::Bulletproof2
| RctType::Clsag
| RctType::BulletproofPlus => {
let mut pseudo_outs: Vec<Key> = vec![];
let txn_fee: VarInt = Decodable::consensus_decode(d)?;
if rct_type == RctType::Simple {
pseudo_outs = decode_sized_vec!(inputs, d);
}
let mut ecdh_info: Vec<EcdhInfo> = vec![];
for _ in 0..outputs {
ecdh_info.push(EcdhInfo::consensus_decode(d, rct_type)?);
}
let out_pk: Vec<CtKey> = decode_sized_vec!(outputs, d);
Ok(Some(RctSigBase {
rct_type,
txn_fee,
pseudo_outs,
ecdh_info,
out_pk,
}))
}
}
}
}
#[sealed]
impl crate::consensus::encode::Encodable for RctSigBase {
fn consensus_encode<S: io::Write>(&self, s: &mut S) -> Result<usize, io::Error> {
let mut len = 0;
len += self.rct_type.consensus_encode(s)?;
match self.rct_type {
RctType::Null => Ok(len),
RctType::Full
| RctType::Simple
| RctType::FullBulletproof
| RctType::SimpleBulletproof
| RctType::Bulletproof
| RctType::Bulletproof2
| RctType::Clsag
| RctType::BulletproofPlus => {
len += self.txn_fee.consensus_encode(s)?;
if self.rct_type == RctType::Simple {
len += encode_sized_vec!(self.pseudo_outs, s);
}
len += encode_sized_vec!(self.ecdh_info, s);
len += encode_sized_vec!(self.out_pk, s);
Ok(len)
}
}
}
}
impl hash::Hashable for RctSigBase {
fn hash(&self) -> hash::Hash {
hash::Hash::new(&serialize(self))
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
pub enum RctType {
Null,
Full,
Simple,
FullBulletproof,
SimpleBulletproof,
Bulletproof,
Bulletproof2,
Clsag,
BulletproofPlus,
}
impl fmt::Display for RctType {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
let rct_type = match self {
RctType::Null => "Null",
RctType::Full => "Full",
RctType::Simple => "Simple",
RctType::FullBulletproof => "FullBulletproof",
RctType::SimpleBulletproof => "SimpleBulletproof",
RctType::Bulletproof => "Bulletproof",
RctType::Bulletproof2 => "Bulletproof2",
RctType::Clsag => "Clsag",
RctType::BulletproofPlus => "Bulletproof+",
};
write!(fmt, "{}", rct_type)
}
}
impl RctType {
pub fn is_rct_bp(self) -> bool {
matches!(
self,
RctType::FullBulletproof
| RctType::SimpleBulletproof
| RctType::Bulletproof
| RctType::Bulletproof2
| RctType::Clsag
)
}
pub fn is_rct_bp_plus(self) -> bool {
matches!(self, RctType::BulletproofPlus)
}
}
impl Decodable for RctType {
fn consensus_decode<D: io::Read>(d: &mut D) -> Result<RctType, encode::Error> {
let rct_type: u8 = Decodable::consensus_decode(d)?;
match rct_type {
0 => Ok(RctType::Null),
1 => Ok(RctType::Full),
2 => Ok(RctType::Simple),
3 => Ok(RctType::FullBulletproof),
4 => Ok(RctType::SimpleBulletproof),
5 => Ok(RctType::Bulletproof),
6 => Ok(RctType::Bulletproof2),
7 => Ok(RctType::Clsag),
8 => Ok(RctType::BulletproofPlus),
_ => Err(Error::UnknownRctType.into()),
}
}
}
#[sealed]
impl crate::consensus::encode::Encodable for RctType {
fn consensus_encode<S: io::Write>(&self, s: &mut S) -> Result<usize, io::Error> {
match self {
RctType::Null => 0u8.consensus_encode(s),
RctType::Full => 1u8.consensus_encode(s),
RctType::Simple => 2u8.consensus_encode(s),
RctType::FullBulletproof => 3u8.consensus_encode(s),
RctType::SimpleBulletproof => 4u8.consensus_encode(s),
RctType::Bulletproof => 5u8.consensus_encode(s),
RctType::Bulletproof2 => 6u8.consensus_encode(s),
RctType::Clsag => 7u8.consensus_encode(s),
RctType::BulletproofPlus => 8u8.consensus_encode(s),
}
}
}
#[derive(Debug, Clone)]
#[allow(non_snake_case)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
pub struct RctSigPrunable {
pub range_sigs: Vec<RangeSig>,
pub bulletproofs: Vec<Bulletproof>,
pub bulletproofplus: Vec<BulletproofPlus>,
pub MGs: Vec<MgSig>,
pub Clsags: Vec<Clsag>,
pub pseudo_outs: Vec<Key>,
}
impl RctSigPrunable {
#[allow(non_snake_case)]
pub fn consensus_decode<D: io::Read>(
d: &mut D,
rct_type: RctType,
inputs: usize,
outputs: usize,
mixin: usize,
) -> Result<Option<RctSigPrunable>, encode::Error> {
match rct_type {
RctType::Null => Ok(None),
RctType::Full
| RctType::Simple
| RctType::FullBulletproof
| RctType::SimpleBulletproof
| RctType::Bulletproof
| RctType::Bulletproof2
| RctType::Clsag
| RctType::BulletproofPlus => {
let mut bulletproofs: Vec<Bulletproof> = vec![];
let mut bulletproofplus: Vec<BulletproofPlus> = vec![];
let mut range_sigs: Vec<RangeSig> = vec![];
if rct_type.is_rct_bp() {
match rct_type {
RctType::Bulletproof2 | RctType::Clsag | RctType::BulletproofPlus => {
bulletproofs = Decodable::consensus_decode(d)?;
}
RctType::FullBulletproof | RctType::SimpleBulletproof => {
let size: u32 = outputs as u32;
bulletproofs = decode_sized_vec!(size, d);
}
_ => {
let size: u32 = Decodable::consensus_decode(d)?;
bulletproofs = decode_sized_vec!(size, d);
}
}
} else if rct_type.is_rct_bp_plus() {
let size: u8 = Decodable::consensus_decode(d)?;
bulletproofplus = decode_sized_vec!(size, d);
} else {
range_sigs = decode_sized_vec!(outputs, d);
}
let mut Clsags: Vec<Clsag> = vec![];
let mut MGs: Vec<MgSig> = vec![];
match rct_type {
RctType::Clsag | RctType::BulletproofPlus => {
for _ in 0..inputs {
let mut s: Vec<Key> = vec![];
for _ in 0..=mixin {
let s_elems: Key = Decodable::consensus_decode(d)?;
s.push(s_elems);
}
let c1 = Decodable::consensus_decode(d)?;
let D = Decodable::consensus_decode(d)?;
Clsags.push(Clsag { s, c1, D });
}
}
_ => {
let is_full =
rct_type == RctType::Full || rct_type == RctType::FullBulletproof;
let mg_elements = if !is_full { inputs } else { 1 };
for _ in 0..mg_elements {
let mut ss: Vec<Vec<Key>> = vec![];
for _ in 0..=mixin {
let mg_ss2_elements = if !is_full { 2 } else { 1 + inputs };
let ss_elems: Vec<Key> = decode_sized_vec!(mg_ss2_elements, d);
ss.push(ss_elems);
}
let cc = Decodable::consensus_decode(d)?;
MGs.push(MgSig { ss, cc });
}
}
}
let mut pseudo_outs: Vec<Key> = vec![];
match rct_type {
RctType::Bulletproof
| RctType::SimpleBulletproof
| RctType::Bulletproof2
| RctType::Clsag
| RctType::BulletproofPlus => {
pseudo_outs = decode_sized_vec!(inputs, d);
}
_ => (),
}
Ok(Some(RctSigPrunable {
range_sigs,
bulletproofs,
bulletproofplus,
MGs,
Clsags,
pseudo_outs,
}))
}
}
}
pub fn consensus_encode<S: io::Write>(
&self,
s: &mut S,
rct_type: RctType,
) -> Result<usize, io::Error> {
match rct_type {
RctType::Null => Ok(0),
RctType::Full
| RctType::Simple
| RctType::FullBulletproof
| RctType::SimpleBulletproof
| RctType::Bulletproof
| RctType::Bulletproof2
| RctType::Clsag
| RctType::BulletproofPlus => {
let mut len = 0;
if rct_type.is_rct_bp() {
match rct_type {
RctType::Bulletproof2 | RctType::Clsag => {
len += self.bulletproofs.consensus_encode(s)?;
}
RctType::FullBulletproof | RctType::SimpleBulletproof => {
len += encode_sized_vec!(self.bulletproofs, s);
}
_ => {
let size: u32 = self.bulletproofs.len() as u32;
len += size.consensus_encode(s)?;
len += encode_sized_vec!(self.bulletproofs, s);
}
}
} else if rct_type.is_rct_bp_plus() {
let size: u8 = self.bulletproofplus.len() as u8;
len += size.consensus_encode(s)?;
len += encode_sized_vec!(self.bulletproofplus, s);
} else {
len += encode_sized_vec!(self.range_sigs, s);
}
match rct_type {
RctType::Clsag | RctType::BulletproofPlus => {
len += encode_sized_vec!(self.Clsags, s)
}
_ => len += encode_sized_vec!(self.MGs, s),
}
match rct_type {
RctType::Bulletproof
| RctType::SimpleBulletproof
| RctType::Bulletproof2
| RctType::Clsag
| RctType::BulletproofPlus => {
len += encode_sized_vec!(self.pseudo_outs, s);
}
_ => (),
}
Ok(len)
}
}
}
}
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
pub struct RctSig {
pub sig: Option<RctSigBase>,
pub p: Option<RctSigPrunable>,
}
impl fmt::Display for RctSig {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match &self.sig {
Some(v) => writeln!(fmt, "Signature: {}", v)?,
None => writeln!(fmt, "Signature: None")?,
};
Ok(())
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(crate = "serde_crate"))]
pub struct Signature {
pub c: Key,
pub r: Key,
}
impl fmt::Display for Signature {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
writeln!(fmt, "C: {}", self.c)?;
writeln!(fmt, "R: {}", self.r)
}
}
impl_consensus_encoding!(Signature, c, r);