use crate::{Ciphertext, Entry, Literal, Plaintext};
use snarkvm_console_network::prelude::*;
use snarkvm_console_types::{Boolean, Field, U64};
#[derive(Clone)]
pub enum Balance<N: Network, Private: Visibility> {
Public(U64<N>),
Private(Private),
}
impl<N: Network> Deref for Balance<N, Plaintext<N>> {
type Target = U64<N>;
fn deref(&self) -> &Self::Target {
match self {
Self::Public(public) => public,
Self::Private(Plaintext::Literal(Literal::U64(balance), ..)) => balance,
_ => N::halt("Internal error: plaintext deref corrupted in record balance"),
}
}
}
impl<N: Network, Private: Visibility> Balance<N, Private> {
pub const fn is_public(&self) -> bool {
matches!(self, Self::Public(..))
}
pub const fn is_private(&self) -> bool {
matches!(self, Self::Private(..))
}
}
impl<N: Network> Balance<N, Plaintext<N>> {
pub fn to_entry(&self) -> Entry<N, Plaintext<N>> {
match self {
Self::Public(balance) => Entry::Public(Plaintext::from(Literal::U64(*balance))),
Self::Private(plaintext, ..) => Entry::Private(plaintext.clone()),
}
}
}
impl<N: Network, Private: Visibility<Boolean = Boolean<N>>> Eq for Balance<N, Private> {}
impl<N: Network, Private: Visibility<Boolean = Boolean<N>>> PartialEq for Balance<N, Private> {
fn eq(&self, other: &Self) -> bool {
*self.is_equal(other)
}
}
impl<N: Network, Private: Visibility<Boolean = Boolean<N>>> Equal<Self> for Balance<N, Private> {
type Output = Boolean<N>;
fn is_equal(&self, other: &Self) -> Self::Output {
match (self, other) {
(Self::Public(a), Self::Public(b)) => a.is_equal(b),
(Self::Private(a), Self::Private(b)) => a.is_equal(b),
(Self::Public(_), _) | (Self::Private(_), _) => Boolean::new(false),
}
}
fn is_not_equal(&self, other: &Self) -> Self::Output {
match (self, other) {
(Self::Public(a), Self::Public(b)) => a.is_not_equal(b),
(Self::Private(a), Self::Private(b)) => a.is_not_equal(b),
(Self::Public(_), _) | (Self::Private(_), _) => Boolean::new(true),
}
}
}
impl<N: Network> Balance<N, Plaintext<N>> {
pub fn encrypt_with_randomizer(&self, randomizer: &[Field<N>]) -> Result<Balance<N, Ciphertext<N>>> {
match self {
Self::Public(balance) => {
ensure!(randomizer.is_empty(), "Expected 0 randomizers, found {}", randomizer.len());
ensure!(balance.to_bits_le()[52..].iter().all(|bit| !bit), "Attempted to encrypt an invalid balance");
Ok(Balance::Public(*balance))
}
Self::Private(Plaintext::Literal(Literal::U64(balance), ..)) => {
ensure!(randomizer.len() == 1, "Expected 1 randomizer, found {}", randomizer.len());
ensure!(balance.to_bits_le()[52..].iter().all(|bit| !bit), "Attempted to encrypt an invalid balance");
let ciphertext = balance.to_field()? + randomizer[0];
Ok(Balance::Private(Ciphertext::from_fields(&[ciphertext])?))
}
_ => bail!("Internal error: plaintext encryption corrupted in record balance"),
}
}
}
impl<N: Network> Balance<N, Ciphertext<N>> {
pub fn decrypt_with_randomizer(&self, randomizer: &[Field<N>]) -> Result<Balance<N, Plaintext<N>>> {
match self {
Self::Public(balance) => {
ensure!(randomizer.is_empty(), "Expected 0 randomizers, found {}", randomizer.len());
ensure!(balance.to_bits_le()[52..].iter().all(|bit| !bit), "Attempted to decrypt an invalid balance");
Ok(Balance::Public(*balance))
}
Self::Private(ciphertext) => {
ensure!(randomizer.len() == 1, "Expected 1 randomizer, found {}", randomizer.len());
ensure!(ciphertext.len() == 1, "Expected 1 ciphertext, found {}", ciphertext.len());
let balance = U64::from_field(&(ciphertext[0] - randomizer[0]))?;
ensure!(balance.to_bits_le()[52..].iter().all(|bit| !bit), "Attempted to decrypt an invalid balance");
Ok(Balance::Private(Plaintext::from(Literal::U64(balance))))
}
}
}
}
impl<N: Network> ToBits for Balance<N, Plaintext<N>> {
fn to_bits_le(&self) -> Vec<bool> {
let mut bits_le = vec![self.is_private()];
match self {
Self::Public(public) => bits_le.extend(public.to_bits_le()),
Self::Private(Plaintext::Literal(Literal::U64(balance), ..)) => bits_le.extend(balance.to_bits_le()),
_ => N::halt("Internal error: plaintext to_bits_le corrupted in record balance"),
}
bits_le
}
fn to_bits_be(&self) -> Vec<bool> {
let mut bits_be = vec![self.is_private()];
match self {
Self::Public(public) => bits_be.extend(public.to_bits_be()),
Self::Private(Plaintext::Literal(Literal::U64(balance), ..)) => bits_be.extend(balance.to_bits_be()),
_ => N::halt("Internal error: plaintext to_bits_be corrupted in record balance"),
}
bits_be
}
}
impl<N: Network> ToBits for Balance<N, Ciphertext<N>> {
fn to_bits_le(&self) -> Vec<bool> {
let mut bits_le = vec![self.is_private()];
match self {
Self::Public(public) => bits_le.extend(public.to_bits_le()),
Self::Private(ciphertext) => {
match ciphertext.len() == 1 {
true => bits_le.extend(ciphertext[0].to_bits_le()),
false => N::halt("Internal error: ciphertext to_bits_le corrupted in record balance"),
}
}
}
bits_le
}
fn to_bits_be(&self) -> Vec<bool> {
let mut bits_be = vec![self.is_private()];
match self {
Self::Public(public) => bits_be.extend(public.to_bits_be()),
Self::Private(ciphertext) => {
match ciphertext.len() == 1 {
true => bits_be.extend(ciphertext[0].to_bits_be()),
false => N::halt("Internal error: ciphertext to_bits_be corrupted in record balance"),
}
}
}
bits_be
}
}
impl<N: Network> Debug for Balance<N, Plaintext<N>> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}
impl<N: Network> Display for Balance<N, Plaintext<N>> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Public(balance) => write!(f, "{balance}.public"),
Self::Private(Plaintext::Literal(Literal::U64(balance), ..)) => write!(f, "{balance}.private"),
_ => N::halt("Internal error: plaintext fmt corrupted in record balance"),
}
}
}
impl<N: Network, Private: Visibility> FromBytes for Balance<N, Private> {
fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
let index = u8::read_le(&mut reader)?;
let balance = match index {
0 => Self::Public(U64::read_le(&mut reader)?),
1 => Self::Private(Private::read_le(&mut reader)?),
2.. => return Err(error(format!("Failed to decode balance variant {index}"))),
};
Ok(balance)
}
}
impl<N: Network, Private: Visibility> ToBytes for Balance<N, Private> {
fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
match self {
Self::Public(balance) => {
0u8.write_le(&mut writer)?;
balance.write_le(&mut writer)
}
Self::Private(balance) => {
1u8.write_le(&mut writer)?;
balance.write_le(&mut writer)
}
}
}
}