use std::fmt;
use crate::consensus::encode::{self, serialize, Decodable, Decoder, Encodable, Encoder, VarInt};
use crate::cryptonote::hash;
#[cfg(feature = "serde_support")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "serde_support")]
use serde_big_array_unchecked_docs::*;
#[allow(missing_docs)]
#[cfg(feature = "serde_support")]
pub mod serde_big_array_unchecked_docs {
use serde_big_array::big_array;
big_array! { BigArray; }
}
#[derive(Debug)]
pub enum Error {
UnknownRctType,
}
#[derive(Clone)]
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
pub struct Key {
pub key: [u8; 32],
}
impl_hex_display!(Key, key);
impl_consensus_encoding!(Key, key);
#[derive(Clone)]
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
pub struct Key64 {
#[cfg_attr(feature = "serde_support", serde(with = "BigArray"))]
pub key: [u8; 64],
}
impl_hex_display!(Key64, key);
impl_consensus_encoding!(Key64, key);
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
pub struct CtKey {
pub mask: Key,
}
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_support", derive(Serialize, Deserialize))]
pub enum EcdhInfo {
Standard {
mask: Key,
amount: Key,
},
Bulletproof2 {
amount: hash::Hash8,
},
}
impl EcdhInfo {
fn consensus_decode<D: Decoder>(
d: &mut D,
rct_type: RctType,
) -> Result<EcdhInfo, encode::Error> {
match rct_type {
RctType::Full | RctType::Simple | RctType::Bulletproof | RctType::Null => {
Ok(EcdhInfo::Standard {
mask: Decodable::consensus_decode(d)?,
amount: Decodable::consensus_decode(d)?,
})
}
RctType::Bulletproof2 => Ok(EcdhInfo::Bulletproof2 {
amount: Decodable::consensus_decode(d)?,
}),
}
}
}
impl<S: Encoder> Encodable<S> for EcdhInfo {
fn consensus_encode(&self, s: &mut S) -> Result<(), encode::Error> {
match self {
EcdhInfo::Standard { mask, amount } => {
mask.consensus_encode(s)?;
amount.consensus_encode(s)?;
}
EcdhInfo::Bulletproof2 { amount } => {
amount.consensus_encode(s)?;
}
}
Ok(())
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
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_support", derive(Serialize, Deserialize))]
pub struct MgSig {
pub ss: Vec<Vec<Key>>,
pub cc: Key,
}
impl<S: Encoder> Encodable<S> for MgSig {
fn consensus_encode(&self, s: &mut S) -> Result<(), encode::Error> {
for ss in self.ss.iter() {
encode_sized_vec!(ss, s);
}
self.cc.consensus_encode(s)
}
}
#[derive(Debug, Clone)]
#[allow(non_snake_case)]
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
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_support", derive(Serialize, Deserialize))]
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)]
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
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 RctSigBase {
pub fn consensus_decode<D: Decoder>(
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(None),
RctType::Full | RctType::Simple | RctType::Bulletproof | RctType::Bulletproof2 => {
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,
}))
}
}
}
}
impl<S: Encoder> Encodable<S> for RctSigBase {
fn consensus_encode(&self, s: &mut S) -> Result<(), encode::Error> {
self.rct_type.consensus_encode(s)?;
match self.rct_type {
RctType::Null => Ok(()),
RctType::Full | RctType::Simple | RctType::Bulletproof | RctType::Bulletproof2 => {
self.txn_fee.consensus_encode(s)?;
if self.rct_type == RctType::Simple {
encode_sized_vec!(self.pseudo_outs, s);
}
encode_sized_vec!(self.ecdh_info, s);
encode_sized_vec!(self.out_pk, s);
Ok(())
}
}
}
}
impl hash::Hashable for RctSigBase {
fn hash(&self) -> hash::Hash {
hash::Hash::hash(&serialize(self))
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
pub enum RctType {
Null,
Full,
Simple,
Bulletproof,
Bulletproof2,
}
impl RctType {
pub fn is_rct_bp(self) -> bool {
match self {
RctType::Bulletproof | RctType::Bulletproof2 => true,
_ => false,
}
}
}
impl<D: Decoder> Decodable<D> for RctType {
fn consensus_decode(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::Bulletproof),
4 => Ok(RctType::Bulletproof2),
_ => Err(Error::UnknownRctType.into()),
}
}
}
impl<S: Encoder> Encodable<S> for RctType {
fn consensus_encode(&self, s: &mut S) -> Result<(), encode::Error> {
match self {
RctType::Null => 0u8.consensus_encode(s)?,
RctType::Full => 1u8.consensus_encode(s)?,
RctType::Simple => 2u8.consensus_encode(s)?,
RctType::Bulletproof => 3u8.consensus_encode(s)?,
RctType::Bulletproof2 => 4u8.consensus_encode(s)?,
}
Ok(())
}
}
#[derive(Debug, Clone)]
#[allow(non_snake_case)]
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
pub struct RctSigPrunable {
pub range_sigs: Vec<RangeSig>,
pub bulletproofs: Vec<Bulletproof>,
pub MGs: Vec<MgSig>,
pub pseudo_outs: Vec<Key>,
}
impl RctSigPrunable {
#[allow(non_snake_case)]
pub fn consensus_decode<D: Decoder>(
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::Bulletproof | RctType::Bulletproof2 => {
let mut bulletproofs: Vec<Bulletproof> = vec![];
let mut range_sigs: Vec<RangeSig> = vec![];
if rct_type.is_rct_bp() {
match rct_type {
RctType::Bulletproof2 => {
bulletproofs = Decodable::consensus_decode(d)?;
}
_ => {
let size: u32 = Decodable::consensus_decode(d)?;
bulletproofs = decode_sized_vec!(size, d);
}
};
} else {
range_sigs = decode_sized_vec!(outputs, d);
}
let is_full = rct_type == RctType::Full;
let mg_elements = if is_full { 1 } else { inputs };
let mut MGs: Vec<MgSig> = vec![];
for _ in 0..mg_elements {
let mut ss: Vec<Vec<Key>> = vec![];
for _ in 0..=mixin {
let mg_ss2_elements = if is_full { 1 + inputs } else { 2 };
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::Bulletproof2 => {
pseudo_outs = decode_sized_vec!(inputs, d);
}
_ => (),
}
Ok(Some(RctSigPrunable {
range_sigs,
bulletproofs,
MGs,
pseudo_outs,
}))
}
}
}
pub fn consensus_encode<S: Encoder>(
&self,
s: &mut S,
rct_type: RctType,
) -> Result<(), encode::Error> {
match rct_type {
RctType::Null => Ok(()),
RctType::Full | RctType::Simple | RctType::Bulletproof | RctType::Bulletproof2 => {
if rct_type.is_rct_bp() {
match rct_type {
RctType::Bulletproof2 => {
self.bulletproofs.consensus_encode(s)?;
}
_ => {
let size: u32 = self.bulletproofs.len() as u32;
size.consensus_encode(s)?;
encode_sized_vec!(self.bulletproofs, s);
}
}
} else {
encode_sized_vec!(self.range_sigs, s);
}
encode_sized_vec!(self.MGs, s);
match rct_type {
RctType::Bulletproof | RctType::Bulletproof2 => {
encode_sized_vec!(self.pseudo_outs, s);
}
_ => (),
}
Ok(())
}
}
}
}
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
pub struct RctSig {
pub sig: Option<RctSigBase>,
pub p: Option<RctSigPrunable>,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
pub struct Signature {
pub c: Key,
pub r: Key,
}
impl_consensus_encoding!(Signature, c, r);