Skip to content

Commit

Permalink
Merging the develop branch into the main branch (rs v1.5.0, wasm v1.6.0)
Browse files Browse the repository at this point in the history
Removed shielded address prefixes map #77
  • Loading branch information
EvgenKor authored Jan 10, 2024
2 parents d0fc5ac + 218887b commit ecf4521
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 307 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.5.0"
version = "1.6.0"
authors = ["Dmitry Vdovin <voidxnull@gmail.com>"]
repository = "https://github.com/zkBob/libzkbob-rs/"
license = "MIT OR Apache-2.0"
Expand Down
50 changes: 27 additions & 23 deletions libzkbob-rs-wasm/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ use libzkbob_rs::{
client::{TxType as NativeTxType, UserAccount as NativeUserAccount, StateFragment},
merkle::{Hash, Node},
address::{parse_address, parse_address_ext},
pools::Pool
};

use serde::Serialize;
Expand Down Expand Up @@ -64,28 +63,13 @@ 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: 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> {
pub fn new(sk: &[u8], pool_id: u32, state: UserState) -> Result<UserAccount, JsValue> {
crate::utils::set_panic_hook();

let sk = Num::<Fs>::from_uint(NumRepr(Uint::from_little_endian(sk)))
.ok_or_else(|| js_err!("Invalid spending key"))?;

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

Ok(UserAccount {
inner: Rc::new(RefCell::new(account)),
Expand All @@ -109,11 +93,21 @@ impl UserAccount {
self.inner.borrow().gen_address_for_seed(seed)
}

#[wasm_bindgen(js_name = "generateUniversalAddressForSeed")]
pub fn generate_universal_address_for_seed(&self, seed: &[u8]) -> String {
self.inner.borrow().gen_universal_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 = "validateUniversalAddress")]
pub fn validate_universal_address(&self, address: &str) -> bool {
self.inner.borrow().validate_universal_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();
Expand All @@ -123,18 +117,28 @@ impl UserAccount {
self.inner.borrow().generate_address_from_components(d, p_d)
}

#[wasm_bindgen(js_name = "assembleUniversalAddress")]
pub fn assemble_universal_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_universal_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()))?;
parse_address::<PoolParams>(address, &POOL_PARAMS, self.inner.borrow().pool_id).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> {
pub fn parse_address(&self, address: &str, pool_id: Option<u32>) -> Result<IAddressComponents, JsValue> {
let desired_pool_id = pool_id.unwrap_or(self.inner.borrow().pool_id);
let (d, p_d, pool, format, checksum) =
parse_address_ext::<PoolParams>(address, &POOL_PARAMS).map_err(|err| js_err!(&err.to_string()))?;
parse_address_ext::<PoolParams>(address, &POOL_PARAMS, desired_pool_id).map_err(|err| js_err!(&err.to_string()))?;

#[derive(Serialize)]
struct Address {
Expand All @@ -152,9 +156,9 @@ impl UserAccount {
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() },
pool_id: if let Some(pool) = pool { format!("{}", pool) } 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 },
is_pool_valid: if let Some(pool) = pool { pool == self.inner.borrow().pool_id } else { true },
};

Ok(serde_wasm_bindgen::to_value(&address)
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.4.3"
version = "1.5.0"
authors = ["Dmitry Vdovin <voidxnull@gmail.com>"]
repository = "https://github.com/zkBob/libzkbob-rs/"
license = "MIT OR Apache-2.0"
Expand Down
122 changes: 53 additions & 69 deletions libzkbob-rs/src/address.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
use std::convert::{TryInto};
use std::convert::TryInto;

use crate::pools::Pool;
use crate::{utils::keccak256, pools::{GENERIC_ADDRESS_PREFIX, POOL_ID_BITS}};
const POOL_ID_BITS: usize = 24;
const POOL_ID_BYTES: usize = POOL_ID_BITS >> 3;

use crate::utils::keccak256;
use libzeropool::{
constants,
fawkes_crypto::{
borsh::{BorshDeserialize, BorshSerialize},
ff_uint::Num, native::ecc::EdwardsPoint,
ff_uint::{Num, Uint, NumRepr},

Check warning on line 11 in libzkbob-rs/src/address.rs

View workflow job for this annotation

GitHub Actions / clippy

unused imports: `NumRepr`, `Uint`

warning: unused imports: `NumRepr`, `Uint` --> libzkbob-rs/src/address.rs:11:24 | 11 | ff_uint::{Num, Uint, NumRepr}, | ^^^^ ^^^^^^^ | = note: `#[warn(unused_imports)]` on by default
native::ecc::EdwardsPoint,
},
native::boundednum::BoundedNum,
native::params::PoolParams,
Expand All @@ -31,16 +34,15 @@ pub enum AddressParseError {
DeserializationError(#[from] std::io::Error),
}

#[derive(PartialEq)]
pub enum AddressFormat {
Old,
PoolSpecific,
Generic,
}

impl AddressFormat {
pub fn name(&self) -> &str {
match self {
AddressFormat::Old => "old",
AddressFormat::PoolSpecific => "pool",
AddressFormat::Generic => "generic",
}
Expand All @@ -50,83 +52,64 @@ impl AddressFormat {
pub fn parse_address<P: PoolParams>(
address: &str,
params: &P,
pool_id: u32, // current pool id
) -> Result<
(
BoundedNum<P::Fr, { constants::DIVERSIFIER_SIZE_BITS }>, // d
Num<P::Fr>, // p_d
Option<Pool>, // None for generic addresses
AddressFormat,
),
AddressParseError,
>{
let (d, p_d, pool, _, _) = parse_address_ext(address, params)?;
Ok((d, p_d, pool))
let (d, p_d, _, format, _) = parse_address_ext(address, params, pool_id)?;
Ok((d, p_d, format))
}

pub fn parse_address_ext<P: PoolParams>(
address: &str,
params: &P,
pool_id: u32,
) -> Result<
(
BoundedNum<P::Fr, { constants::DIVERSIFIER_SIZE_BITS }>, // d
Num<P::Fr>, // p_d
Option<Pool>, // None for generic addresses
Option<u32>, // None for generic addresses (otherwse pool id)
AddressFormat,
[u8; 4], // checksum
),
AddressParseError,
>{
if address.find(':').is_some() {
// address with prefix
let addr_components: Vec<&str> = address.split(':').collect();
if addr_components.len() == 2 {
let pool = Pool::from_prefix(addr_components[0]);
// ignoring any prefixes, just try to validate checksum
let addr_components: Vec<&str> = address.split(':').filter(|s| !s.is_empty()).collect();
match addr_components.last() {
Some(addr) => {
// parse address
let (d,
p_d,
addr_hash,
checksum) = parse_address_raw(addr_components[1], params)?;

match pool {
Some(pool) => {
// pool-specific address
const POOL_ID_BYTES: usize = POOL_ID_BITS >> 3;
let mut hash_src: [u8; POOL_ID_BYTES + 32] = [0; POOL_ID_BYTES + 32];
pool.pool_id_bytes_be::<P::Fr>().serialize(& mut &mut hash_src[0..POOL_ID_BYTES]).unwrap();
hash_src[POOL_ID_BYTES..POOL_ID_BYTES + 32].clone_from_slice(&addr_hash);

if keccak256(&hash_src)[0..=3] != checksum {
return Err(AddressParseError::InvalidChecksum);
}
return Ok((d, p_d, Some(pool), AddressFormat::PoolSpecific, checksum));
},
None => {
if addr_components[0].to_lowercase() == GENERIC_ADDRESS_PREFIX {
// generic address
if addr_hash[0..=3] != checksum {
return Err(AddressParseError::InvalidChecksum);
}
return Ok((d, p_d, None, AddressFormat::Generic, checksum));
} else {
return Err(AddressParseError::InvalidPrefix(addr_components[0].to_string()))
}
},
};
}
checksum) = parse_address_raw(addr, params)?;

Err(AddressParseError::InvalidFormat)
} else {
// old format without prefix
let (d,
p_d,
addr_hash,
checksum) = parse_address_raw(address, params)?;

if addr_hash[0..=3] != checksum {
return Err(AddressParseError::InvalidChecksum);
}

// the old format should be acceptable on the Polygon BOB pool only
Ok((d, p_d, Some(Pool::PolygonUSDC), AddressFormat::Old, checksum))
if addr_hash[0..=3] != checksum {
// calcing checksum [pool-specific format]
let mut hash_src: [u8; POOL_ID_BYTES + 32] = [0; POOL_ID_BYTES + 32];
pool_id_to_bytes_be::<P>(pool_id).serialize(& mut &mut hash_src[0..POOL_ID_BYTES]).unwrap();
hash_src[POOL_ID_BYTES..POOL_ID_BYTES + 32].clone_from_slice(&addr_hash);

if keccak256(&hash_src)[0..=3] == checksum {
Ok((d, p_d, Some(pool_id), AddressFormat::PoolSpecific, checksum))
} else {
Err(AddressParseError::InvalidChecksum)
}
} else {
// generic format
Ok((d, p_d, None, AddressFormat::Generic, checksum))
}

},
None => Err(AddressParseError::InvalidFormat),
}

}

fn parse_address_raw<P: PoolParams>(
Expand Down Expand Up @@ -154,36 +137,37 @@ fn parse_address_raw<P: PoolParams>(
}
}

// generates shielded address in format "pool_prefix:base58(d ++ p_d ++ checksum)"
// generates shielded address in format "base58(d ++ p_d ++ checksum)"
// pool prefix doesn't append here
// both address types (pool-specific\generic) can generated here
pub fn format_address<P: PoolParams>(
d: BoundedNum<P::Fr, { constants::DIVERSIFIER_SIZE_BITS }>,
p_d: Num<P::Fr>,
pool: Option<Pool>, // set pool to None to generate universal address for all pools
pool_id: Option<u32>, // set pool_id to None to generate universal address for all pools
) -> String {
let mut buf: [u8; ADDR_LEN] = [0; ADDR_LEN];

d.serialize(&mut &mut buf[0..10]).unwrap();
p_d.serialize(&mut &mut buf[10..42]).unwrap();

// there are two ways for checksum calculation
let (checksum_hash, address_prefix) = match pool {
let checksum_hash = match pool_id {
// pool-specific format
Some(pool) => {
const POOL_ID_BYTES: usize = POOL_ID_BITS >> 3;
Some(pool_id) => {
let mut hash_src: [u8; POOL_ID_BYTES + 32] = [0; POOL_ID_BYTES + 32];
//let pool_id = pool.pool_id_num::<P::Fr>().to_num().to_uint().0.to_big_endian();
//let pool_id_be: [u8; POOL_ID_BYTES] = pool_id[32 - POOL_ID_BYTES..32].try_into().unwrap();
pool.pool_id_bytes_be::<P::Fr>().serialize(& mut &mut hash_src[0..POOL_ID_BYTES]).unwrap();
pool_id_to_bytes_be::<P>(pool_id).serialize(& mut &mut hash_src[0..POOL_ID_BYTES]).unwrap();
hash_src[POOL_ID_BYTES..POOL_ID_BYTES + 32].clone_from_slice(&keccak256(&buf[0..42]));
(keccak256(&hash_src), pool.address_prefix().to_owned())
keccak256(&hash_src)
},
// generic format (for all pools, when pool_id isn't specified)
None => (keccak256(&buf[0..42]), GENERIC_ADDRESS_PREFIX.to_string()),
None => keccak256(&buf[0..42]),
};
buf[42..ADDR_LEN].clone_from_slice(&checksum_hash[0..4]);

let address_part = bs58::encode(buf).into_string();

format!("{}:{}", address_prefix, address_part)
bs58::encode(buf).into_string()
}

fn pool_id_to_bytes_be<P: PoolParams>(pool_id: u32) -> [u8; POOL_ID_BYTES] {

Check warning on line 170 in libzkbob-rs/src/address.rs

View workflow job for this annotation

GitHub Actions / clippy

type parameter `P` goes unused in function definition

warning: type parameter `P` goes unused in function definition --> libzkbob-rs/src/address.rs:170:23 | 170 | fn pool_id_to_bytes_be<P: PoolParams>(pool_id: u32) -> [u8; POOL_ID_BYTES] { | ^^^^^^^^^^^^^^^ help: consider removing the parameter | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_type_parameters = note: `#[warn(clippy::extra_unused_type_parameters)]` on by default
// preparing pool id for checksum validation
pool_id.to_be_bytes()[4 - POOL_ID_BYTES..].try_into().unwrap()
}
Loading

0 comments on commit ecf4521

Please sign in to comment.