Skip to content

Commit

Permalink
Multipool addresses support (#61)
Browse files Browse the repository at this point in the history
* Creating multipool addresses [wip]

* Multipool addresses support

* Fixed bugs

* Unit tests, Sepolia pool workaround

* Fix issues eliminated during testing

* Improving address routines

* Added convert address method (wasm lib); added OptimismETH pool support

* Increasing versions

* Apply suggestions from code review

Co-authored-by: Alexander Filippov <aleksander.fill@gmail.com>

* Pool id converting to big-endian in the separated function

---------

Co-authored-by: Alexander Filippov <aleksander.fill@gmail.com>
  • Loading branch information
EvgenKor and AllFi authored Apr 17, 2023
1 parent 83c8d06 commit 3334d84
Show file tree
Hide file tree
Showing 11 changed files with 515 additions and 153 deletions.
2 changes: 1 addition & 1 deletion libzkbob-rs-wasm/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "libzkbob-rs-wasm"
description = "A higher level zkBob API for Wasm"
version = "1.1.0"
version = "1.2.0"
authors = ["Dmitry Vdovin <voidxnull@gmail.com>"]
repository = "https://github.com/zkBob/libzkbob-rs/"
license = "MIT OR Apache-2.0"
Expand Down
123 changes: 97 additions & 26 deletions libzkbob-rs-wasm/src/client/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std::collections::HashMap;
use std::rc::Rc;
use std::{cell::RefCell, convert::TryInto};
use std::str::FromStr;

use libzkbob_rs::address::parse_address_ext;
#[cfg(feature = "multicore")]
use rayon::prelude::*;

Expand All @@ -10,32 +12,35 @@ use libzkbob_rs::libzeropool::{
constants,
fawkes_crypto::{
core::sizedvec::SizedVec,
ff_uint::Num,
ff_uint::{NumRepr, Uint},
ff_uint::{Num, NumRepr, Uint},
borsh::BorshDeserialize,
},
native::{
account::Account as NativeAccount,
note::Note as NativeNote,
boundednum::BoundedNum,
tx::{parse_delta, TransferPub as NativeTransferPub, TransferSec as NativeTransferSec}
},
};
use libzkbob_rs::{
client::{TxType as NativeTxType, UserAccount as NativeUserAccount, StateFragment},
merkle::{Hash, Node}
merkle::{Hash, Node},
address::parse_address,
pools::Pool
};
use serde::{Serialize};

use serde::Serialize;
use wasm_bindgen::{prelude::*, JsCast };
use wasm_bindgen_futures::future_to_promise;

use crate::ParseTxsColdStorageResult;
use crate::{ParseTxsColdStorageResult, IAddressComponents};
use crate::client::tx_parser::StateUpdate;

use crate::database::Database;
use crate::helpers::vec_into_iter;
use crate::ts_types::Hash as JsHash;
use crate::{
keys::reduce_sk, Account, Fr, Fs, Hashes,
Account, Fr, Fs, Hashes,
IDepositData, IDepositPermittableData, ITransferData, IWithdrawData,
IndexedNote, IndexedNotes, PoolParams, Transaction, UserState, POOL_PARAMS,
MerkleProof, Pair, TreeNode, TreeNodes,
Expand All @@ -60,39 +65,105 @@ pub struct UserAccount {
impl UserAccount {
#[wasm_bindgen(constructor)]
/// Initializes UserAccount with a spending key that has to be an element of the prime field Fs (p = 6554484396890773809930967563523245729705921265872317281365359162392183254199).
pub fn new(sk: &[u8], pool_id: u64, state: UserState) -> Result<UserAccount, JsValue> {
pub fn new(sk: &[u8], pool_id: u32, state: UserState, network: &str) -> Result<UserAccount, JsValue> {
crate::utils::set_panic_hook();

let pool = if network.to_lowercase() == "sepolia" && pool_id == Pool::SepoliaBOB.pool_id() {
// A workaround related with Sepolia pool_id issue
// (pool_id for Sepolia BOB pool is equal to Polygon BOB pool)
Ok(Pool::SepoliaBOB)
} else {
Pool::from_pool_id(pool_id)
.ok_or_else(|| js_err!("Unsupported pool with ID {}", pool_id))
}?;

UserAccount::create_internal(sk, pool, state)
}

fn create_internal(sk: &[u8], pool: Pool, state: UserState) -> Result<UserAccount, JsValue> {
crate::utils::set_panic_hook();

if pool_id >= 1 << 24 {
return Err(js_err!("PoolID should be less than {}", 1 << 24));
}
let sk = Num::<Fs>::from_uint(NumRepr(Uint::from_little_endian(sk)))
.ok_or_else(|| js_err!("Invalid spending key"))?;
let pool_id = Num::<Fr>::from_uint(NumRepr(Uint::from_u64(pool_id)))
.ok_or_else(|| js_err!("Invalid pool id"))?;

let account = NativeUserAccount::new(sk, pool_id, state.inner, POOL_PARAMS.clone());

let account = NativeUserAccount::new(sk, pool, state.inner, POOL_PARAMS.clone());

Ok(UserAccount {
inner: Rc::new(RefCell::new(account)),
})
}

// TODO: Is this safe?
#[wasm_bindgen(js_name = fromSeed)]
/// Same as constructor but accepts arbitrary data as spending key.
pub fn from_seed(seed: &[u8], pool_id: u64, state: UserState) -> Result<UserAccount, JsValue> {
let sk = reduce_sk(seed);
Self::new(&sk, pool_id, state)
}

#[wasm_bindgen(js_name = generateAddress)]
/// Generates a new private address.
#[wasm_bindgen(js_name = "generateAddress")]
/// Generates a new private address for the current pool
pub fn generate_address(&self) -> String {
self.inner.borrow().generate_address()
}

#[wasm_bindgen(js_name = decryptNotes)]
#[wasm_bindgen(js_name = "generateUniversalAddress")]
/// Generates a new private address for any pool
pub fn generate_universal_address(&self) -> String {
self.inner.borrow().generate_universal_address()
}

#[wasm_bindgen(js_name = "generateAddressForSeed")]
pub fn generate_address_for_seed(&self, seed: &[u8]) -> String {
self.inner.borrow().gen_address_for_seed(seed)
}

#[wasm_bindgen(js_name = "validateAddress")]
pub fn validate_address(&self, address: &str) -> bool {
self.inner.borrow().validate_address(address)
}

#[wasm_bindgen(js_name = "assembleAddress")]
pub fn assemble_address(&self, d: &str, p_d: &str) -> String {
let d = Num::from_str(d).unwrap();
let d = BoundedNum::new(d);
let p_d = Num::from_str(p_d).unwrap();

self.inner.borrow().generate_address_from_components(d, p_d)
}

#[wasm_bindgen(js_name = "convertAddressToChainSpecific")]
pub fn convert_address_to_chain_specific(&self, address: &str) -> Result<String, JsValue> {
let (d, p_d, _) =
parse_address::<PoolParams>(address, &POOL_PARAMS).map_err(|err| js_err!(&err.to_string()))?;

Ok(self.inner.borrow().generate_address_from_components(d, p_d))
}

#[wasm_bindgen(js_name = "parseAddress")]
pub fn parse_address(&self, address: &str) -> Result<IAddressComponents, JsValue> {
let (d, p_d, pool, format, checksum) =
parse_address_ext::<PoolParams>(address, &POOL_PARAMS).map_err(|err| js_err!(&err.to_string()))?;

#[derive(Serialize)]
struct Address {
format: String,
d: String,
p_d: String,
checksum: [u8; 4],
pool_id: String,
derived_from_our_sk: bool,
is_pool_valid: bool,
}

let address = Address {
format: format.name().to_string(),
d: d.to_num().to_string(),
p_d: p_d.to_string(),
checksum,
pool_id: if let Some(pool) = pool { format!("{}", pool.pool_id()) } else { "any".to_string() },
derived_from_our_sk: self.inner.borrow().is_derived_from_our_sk(d, p_d),
is_pool_valid: if let Some(pool) = pool { pool == self.inner.borrow().pool } else { true },
};

Ok(serde_wasm_bindgen::to_value(&address)
.unwrap()
.unchecked_into::<IAddressComponents>())
}

#[wasm_bindgen(js_name = "decryptNotes")]
/// Attempts to decrypt notes.
pub fn decrypt_notes(&self, data: Vec<u8>) -> Result<IndexedNotes, JsValue> {
let notes = self
Expand All @@ -115,7 +186,7 @@ impl UserAccount {
Ok(notes)
}

#[wasm_bindgen(js_name = decryptPair)]
#[wasm_bindgen(js_name = "decryptPair")]
/// Attempts to decrypt account and notes.
pub fn decrypt_pair(&self, data: Vec<u8>) -> Result<Option<Pair>, JsValue> {
#[derive(Serialize)]
Expand Down
29 changes: 3 additions & 26 deletions libzkbob-rs-wasm/src/keys.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,10 @@
use libzkbob_rs::{address::format_address, keys::reduce_sk as reduce_sk_native};
use libzkbob_rs::libzeropool::{
constants,
fawkes_crypto::{ff_uint::Uint, rand::Rng},
native::{boundednum::BoundedNum, key::derive_key_p_d},
POOL_PARAMS,
};
use libzkbob_rs::{ keys::reduce_sk as reduce_sk_native};
use libzkbob_rs::libzeropool::fawkes_crypto::ff_uint::Uint;
use wasm_bindgen::prelude::*;

use crate::{Fs, PoolParams};
use crate::Fs;

#[wasm_bindgen(js_name = reduceSpendingKey)]
pub fn reduce_sk(seed: &[u8]) -> Vec<u8> {
reduce_sk_native::<Fs>(seed).to_uint().0.to_little_endian()
}

#[wasm_bindgen(js_name = genBurnerAddress)]
pub fn gen_burner_address(pool_id: u64, seed: &[u8]) -> Result<String, JsValue> {
if pool_id >= 1 << 24 {
return Err(js_err!("PoolID should be less than {}", 1 << 24));
}
let mut rng = libzkbob_rs::random::CustomRng;

let sk = reduce_sk_native::<Fs>(seed);

let keys = libzkbob_rs::keys::Keys::derive(sk, &*POOL_PARAMS);

let d: BoundedNum<_, { constants::DIVERSIFIER_SIZE_BITS }> = rng.gen();

let pk_d = derive_key_p_d(d.to_num(), keys.eta, &*POOL_PARAMS);

Ok(format_address::<PoolParams>(d, pk_d.x))
}
40 changes: 1 addition & 39 deletions libzkbob-rs-wasm/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
use std::str::FromStr;

use libzkbob_rs::libzeropool::{
constants,
fawkes_crypto::{backend::bellman_groth16::engines::Bn256, ff_uint::Num},
fawkes_crypto::{backend::bellman_groth16::engines::Bn256},
native::{
boundednum::BoundedNum,
params::{PoolBN256, PoolParams as PoolParamsTrait},
},
POOL_PARAMS,
};
use libzkbob_rs::address::{format_address, parse_address};
use serde::Serialize;
use wasm_bindgen::{prelude::*, JsCast};

Expand Down Expand Up @@ -72,38 +68,4 @@ pub fn get_constants() -> Constants {
serde_wasm_bindgen::to_value(&*CONSTANTS)
.unwrap()
.unchecked_into::<Constants>()
}

#[wasm_bindgen(js_name = "validateAddress")]
pub fn validate_address(address: &str) -> bool {
parse_address::<PoolParams>(address, &POOL_PARAMS).is_ok()
}

#[wasm_bindgen(js_name = "assembleAddress")]
pub fn assemble_address(d: &str, p_d: &str) -> String {
let d = Num::from_str(d).unwrap();
let d = BoundedNum::new(d);
let p_d = Num::from_str(p_d).unwrap();

format_address::<PoolParams>(d, p_d)
}

#[wasm_bindgen(js_name = "parseAddress")]
pub fn parse_address_(address: &str) -> IAddressComponents {
let (d, p_d) = parse_address::<PoolParams>(address, &POOL_PARAMS).unwrap();

#[derive(Serialize)]
struct Address {
d: String,
p_d: String,
}

let address = Address {
d: d.to_num().to_string(),
p_d: p_d.to_string(),
};

serde_wasm_bindgen::to_value(&address)
.unwrap()
.unchecked_into::<IAddressComponents>()
}
5 changes: 5 additions & 0 deletions libzkbob-rs-wasm/src/ts_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,13 @@ export interface VK {
}
export interface IAddressComponents {
format: string;
d: string;
p_d: string;
checksum: Uint8Array;
pool_id: string;
derived_from_our_sk: boolean;
is_pool_valid: boolean;
}
export interface ITxBaseFields {
Expand Down
2 changes: 1 addition & 1 deletion libzkbob-rs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "libzkbob-rs"
description = "A higher level zkBob API"
version = "1.0.1"
version = "1.1.0"
authors = ["Dmitry Vdovin <voidxnull@gmail.com>"]
repository = "https://github.com/zkBob/libzkbob-rs/"
license = "MIT OR Apache-2.0"
Expand Down
Loading

0 comments on commit 3334d84

Please sign in to comment.