use crate::anon_xfr::{commit, decrypt_memo};
use crate::keys::{KeyPair, PublicKey, SecretKey};
use crate::xfr::structs::AssetType;
use noah_algebra::{bls12_381::BLSScalar, prelude::*};
use noah_plonk::plonk::constraint_system::VarIndex;
use serde::Serialize;
use wasm_bindgen::prelude::*;
pub type Nullifier = BLSScalar;
pub type Commitment = BLSScalar;
pub type BlindFactor = BLSScalar;
#[wasm_bindgen]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MTNode {
pub left: BLSScalar,
pub mid: BLSScalar,
pub right: BLSScalar,
pub is_left_child: u8,
pub is_mid_child: u8,
pub is_right_child: u8,
}
#[wasm_bindgen]
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct AnonAssetRecord {
pub commitment: BLSScalar,
}
impl AnonAssetRecord {
pub fn from_oabar(oabar: &OpenAnonAssetRecord) -> AnonAssetRecord {
let (commitment, _) = commit(
oabar.pub_key_ref(),
oabar.get_blind(),
oabar.get_amount(),
oabar.get_asset_type().as_scalar(),
)
.unwrap();
AnonAssetRecord { commitment }
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct MTLeafInfo {
pub path: MTPath,
pub root: BLSScalar,
pub root_version: u64,
pub uid: u64,
}
impl Default for MTLeafInfo {
fn default() -> Self {
MTLeafInfo {
path: MTPath::new(vec![]),
root: BLSScalar::zero(),
root_version: 0,
uid: 0,
}
}
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct OpenAnonAssetRecord {
pub(crate) amount: u64,
pub(crate) asset_type: AssetType,
pub(crate) blind: BLSScalar,
pub(crate) pub_key: PublicKey,
pub(crate) owner_memo: Option<AxfrOwnerMemo>,
pub(crate) mt_leaf_info: Option<MTLeafInfo>,
}
impl Default for OpenAnonAssetRecord {
fn default() -> Self {
Self {
amount: 0,
asset_type: AssetType::default(),
blind: BLSScalar::default(),
pub_key: PublicKey::default_secp256k1(),
owner_memo: None,
mt_leaf_info: None,
}
}
}
impl OpenAnonAssetRecord {
pub fn update_mt_leaf_info(&mut self, mt_leat_info: MTLeafInfo) {
self.mt_leaf_info = Some(mt_leat_info);
}
}
impl OpenAnonAssetRecord {
pub fn get_amount(&self) -> u64 {
self.amount
}
pub fn get_asset_type(&self) -> AssetType {
self.asset_type
}
pub fn pub_key_ref(&self) -> &PublicKey {
&self.pub_key
}
pub fn get_blind(&self) -> BLSScalar {
self.blind
}
pub fn get_owner_memo(&self) -> Option<AxfrOwnerMemo> {
self.owner_memo.clone()
}
}
#[derive(Default)]
pub struct OpenAnonAssetRecordBuilder {
pub(crate) oabar: OpenAnonAssetRecord,
}
impl OpenAnonAssetRecordBuilder {
pub fn new() -> Self {
OpenAnonAssetRecordBuilder {
..Default::default()
}
}
pub fn amount(mut self, amount: u64) -> Self {
self.oabar.amount = amount;
self
}
pub fn asset_type(mut self, asset_type: AssetType) -> Self {
self.oabar.asset_type = asset_type;
self
}
pub fn pub_key(mut self, pub_key: &PublicKey) -> Self {
self.oabar.pub_key = pub_key.clone();
self
}
pub fn mt_leaf_info(mut self, mt_leaf_info: MTLeafInfo) -> Self {
self.oabar.update_mt_leaf_info(mt_leaf_info);
self
}
pub fn finalize<R: CryptoRng + RngCore>(mut self, prng: &mut R) -> Result<Self> {
if self.oabar.owner_memo.is_some() {
return Err(eg!(NoahError::InconsistentStructureError));
}
self.oabar.blind = BLSScalar::random(prng);
let mut msg = vec![];
msg.extend_from_slice(&self.oabar.amount.to_le_bytes());
msg.extend_from_slice(&self.oabar.asset_type.0);
msg.extend_from_slice(&self.oabar.blind.to_bytes());
self.oabar.owner_memo = Some(AxfrOwnerMemo::new(prng, &self.oabar.pub_key, &msg)?);
Ok(self)
}
pub fn build(self) -> Result<OpenAnonAssetRecord> {
self.sanity_check().c(d!())?;
Ok(self.oabar)
}
}
impl OpenAnonAssetRecordBuilder {
pub fn from_abar(
record: &AnonAssetRecord,
owner_memo: AxfrOwnerMemo,
key_pair: &KeyPair,
) -> Result<Self> {
let (amount, asset_type, blind) = decrypt_memo(&owner_memo, key_pair, record).c(d!())?;
let mut builder = OpenAnonAssetRecordBuilder::new()
.pub_key(&key_pair.get_pk())
.amount(amount)
.asset_type(asset_type);
builder.oabar.blind = blind;
builder.oabar.owner_memo = Some(owner_memo);
Ok(builder)
}
fn sanity_check(&self) -> Result<()> {
if self.oabar.pub_key == PublicKey::default_secp256k1() {
return Err(eg!(NoahError::InconsistentStructureError));
}
if self.oabar.owner_memo.is_none() {
return Err(eg!(NoahError::InconsistentStructureError));
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MTPath {
pub nodes: Vec<MTNode>,
}
impl MTPath {
pub fn new(nodes: Vec<MTNode>) -> Self {
Self { nodes }
}
}
pub(crate) struct PayerWitnessVars {
pub(crate) uid: VarIndex,
pub(crate) amount: VarIndex,
pub(crate) asset_type: VarIndex,
pub(crate) path: MerklePathVars,
pub(crate) blind: VarIndex,
}
pub(crate) struct PayeeWitnessVars {
pub(crate) amount: VarIndex,
pub(crate) blind: VarIndex,
pub(crate) asset_type: VarIndex,
pub(crate) public_key_type: VarIndex,
pub(crate) public_key_scalars: [VarIndex; 3],
}
pub struct MerkleNodeVars {
pub left: VarIndex,
pub mid: VarIndex,
pub right: VarIndex,
pub is_left_child: VarIndex,
pub is_mid_child: VarIndex,
pub is_right_child: VarIndex,
}
pub struct MerklePathVars {
pub nodes: Vec<MerkleNodeVars>,
}
pub struct AccElemVars {
pub uid: VarIndex,
pub commitment: VarIndex,
}
#[derive(Debug, Clone)]
pub struct PayerWitness {
pub secret_key: SecretKey,
pub amount: u64,
pub asset_type: BLSScalar,
pub uid: u64,
pub path: MTPath,
pub blind: BlindFactor,
}
#[derive(Debug, Clone)]
pub struct PayeeWitness {
pub amount: u64,
pub blind: BlindFactor,
pub asset_type: BLSScalar,
pub public_key: PublicKey,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct AxfrOwnerMemo(Vec<u8>);
impl AxfrOwnerMemo {
pub fn new<R: CryptoRng + RngCore>(
prng: &mut R,
pub_key: &PublicKey,
msg: &[u8],
) -> Result<Self> {
let ctext = pub_key.hybrid_encrypt(prng, msg)?;
Ok(Self(ctext))
}
pub fn decrypt(&self, secret_key: &SecretKey) -> Result<Vec<u8>> {
secret_key.hybrid_decrypt(&self.0)
}
}
#[cfg(test)]
mod test {
use crate::anon_xfr::structs::PublicKey;
use crate::keys::KeyPair;
use noah_algebra::prelude::*;
#[test]
fn test_axfr_pub_key_serialization() {
let mut prng = test_rng();
let keypair: KeyPair = KeyPair::generate_secp256k1(&mut prng);
let pub_key: PublicKey = keypair.get_pk();
let bytes = pub_key.noah_to_bytes();
assert_ne!(bytes.len(), 0);
let reformed_pub_key = PublicKey::noah_from_bytes(bytes.as_slice()).unwrap();
assert_eq!(pub_key, reformed_pub_key);
}
#[test]
fn test_axfr_key_pair_serialization() {
let mut prng = test_rng();
let keypair: KeyPair = KeyPair::generate_secp256k1(&mut prng);
let bytes: Vec<u8> = keypair.noah_to_bytes();
assert_ne!(bytes.len(), 0);
let reformed_key_pair = KeyPair::noah_from_bytes(bytes.as_slice()).unwrap();
assert_eq!(keypair, reformed_key_pair);
}
}