diff --git a/src/components/abciapp/src/api/query_server/query_api/mod.rs b/src/components/abciapp/src/api/query_server/query_api/mod.rs index 5cd856495..3c911385b 100644 --- a/src/components/abciapp/src/api/query_server/query_api/mod.rs +++ b/src/components/abciapp/src/api/query_server/query_api/mod.rs @@ -140,6 +140,15 @@ async fn get_abar_proof( Ok(web::Json(server.get_abar_proof(ATxoSID(*info)))) } +/// Checks if a nullifier hash is present in nullifier set +async fn check_nullifier_hash( + data: web::Data>>, + info: web::Path, +) -> actix_web::Result>, actix_web::error::Error> { + let server = data.read(); + Ok(web::Json(server.check_nullifier_hash((*info).clone()))) +} + /// Define interface type #[allow(missing_docs)] pub enum QueryServerRoutes { @@ -150,6 +159,7 @@ pub enum QueryServerRoutes { GetOwnedAbars, GetAbarMemo, GetAbarProof, + CheckNullifierHash, GetCreatedAssets, GetIssuedRecords, GetIssuedRecordsByCode, @@ -173,6 +183,7 @@ impl NetworkRoute for QueryServerRoutes { QueryServerRoutes::GetOwnerMemoBatch => "get_owner_memo_batch", QueryServerRoutes::GetAbarMemo => "get_abar_memo", QueryServerRoutes::GetAbarProof => "get_abar_proof", + QueryServerRoutes::CheckNullifierHash => "check_nullifier_hash", QueryServerRoutes::GetCreatedAssets => "get_created_assets", QueryServerRoutes::GetIssuedRecords => "get_issued_records", QueryServerRoutes::GetIssuedRecordsByCode => "get_issued_records_by_code", @@ -567,6 +578,11 @@ impl QueryApi { &QueryServerRoutes::GetAbarProof.with_arg_template("atxo_sid"), web::get().to(get_abar_proof), ) + .route( + &QueryServerRoutes::CheckNullifierHash + .with_arg_template("null_hash"), + web::get().to(check_nullifier_hash), + ) .route( &QueryServerRoutes::GetRelatedTxns.with_arg_template("address"), web::get().to(get_related_txns), diff --git a/src/components/abciapp/src/api/query_server/query_api/server.rs b/src/components/abciapp/src/api/query_server/query_api/server.rs index a45264d2f..b50ade8b2 100644 --- a/src/components/abciapp/src/api/query_server/query_api/server.rs +++ b/src/components/abciapp/src/api/query_server/query_api/server.rs @@ -317,6 +317,12 @@ impl QueryServer { Some(self.ledger_cloned.get_abar_proof(atxo_sid).unwrap()) } + /// Returns a bool value from the given hash + #[inline(always)] + pub fn check_nullifier_hash(&self, null_hash: String) -> Option { + Some(self.ledger_cloned.check_nullifier_hash(null_hash).unwrap()) + } + /// retrieve block reward rate at specified block height #[inline(always)] pub fn query_block_rewards_rate(&self, height: &BlockHeight) -> Option<[u128; 2]> { diff --git a/src/components/finutils/src/common/utils.rs b/src/components/finutils/src/common/utils.rs index 1f1531572..d0b8f1b0c 100644 --- a/src/components/finutils/src/common/utils.rs +++ b/src/components/finutils/src/common/utils.rs @@ -639,6 +639,25 @@ pub fn get_abar_proof(atxo_sid: &ATxoSID) -> Result> { .and_then(|b| serde_json::from_slice(&b).c(d!())) } +#[inline(always)] +#[allow(missing_docs)] +pub fn check_nullifier_hash(null_hash: &String) -> Result> { + let url = format!( + "{}:8667/check_nullifier_hash/{}", + get_serv_addr().c(d!())?, + null_hash + ); + + attohttpc::get(&url) + .send() + .c(d!())? + .error_for_status() + .c(d!())? + .bytes() + .c(d!()) + .and_then(|b| serde_json::from_slice(&b).c(d!())) +} + /// Delegation info(and staking info if `pk` is a validator). pub fn get_delegation_info(pk: &XfrPublicKey) -> Result { let url = format!( diff --git a/src/components/finutils/src/txn_builder/mod.rs b/src/components/finutils/src/txn_builder/mod.rs index 62c99ab40..c7292a5cb 100644 --- a/src/components/finutils/src/txn_builder/mod.rs +++ b/src/components/finutils/src/txn_builder/mod.rs @@ -61,7 +61,7 @@ use { ConfidentialAC, Credential, }, serialization::ZeiFromToBytes, - setup::{PublicParams, UserParams, DEFAULT_BP_NUM_GENS}, + setup::{PublicParams, UserParams}, xfr::{ asset_record::{ build_blind_asset_record, build_open_asset_record, @@ -531,12 +531,8 @@ impl TransactionBuilder { ) -> Result<(&mut Self, AXfrNote)> { let mut prng = ChaChaRng::from_entropy(); let depth: usize = 41; - let user_params = UserParams::new( - inputs.len(), - outputs.len(), - Option::from(depth), - DEFAULT_BP_NUM_GENS, - ); + let user_params = + UserParams::new(inputs.len(), outputs.len(), Option::from(depth)); let (body, keypairs) = gen_anon_xfr_body(&mut prng, &user_params, inputs, outputs, input_keypairs) @@ -1254,14 +1250,8 @@ impl AnonTransferOperationBuilder { /// build generates the anon transfer body with the Zero Knowledge Proof. pub fn build(&mut self) -> Result<&mut Self> { let mut prng = ChaChaRng::from_entropy(); - let user_params = UserParams::from_file_if_exists( - self.inputs.len(), - self.outputs.len(), - Some(41), - DEFAULT_BP_NUM_GENS, - None, - ) - .c(d!())?; + let user_params = + UserParams::new(self.inputs.len(), self.outputs.len(), Some(41)); let (body, diversified_keypairs) = gen_anon_xfr_body( &mut prng, diff --git a/src/components/wasm/src/wasm.rs b/src/components/wasm/src/wasm.rs index 5a41da818..3ba4a394b 100644 --- a/src/components/wasm/src/wasm.rs +++ b/src/components/wasm/src/wasm.rs @@ -65,6 +65,11 @@ use { std::convert::From, wasm_bindgen::prelude::*, zei::{ + anon_xfr::{ + keys::{AXfrKeyPair, AXfrPubKey}, + nullifier, + structs::{AnonBlindAssetRecord, OpenAnonBlindAssetRecordBuilder}, + }, serialization::ZeiFromToBytes, xfr::{ asset_record::{open_blind_asset_record as open_bar, AssetRecordType}, @@ -76,7 +81,7 @@ use { }, }, }, - zeialgebra::jubjub::JubjubScalar, + zeialgebra::{groups::Scalar, jubjub::JubjubScalar}, }; /// Constant defining the git commit hash and commit date of the commit this library was built @@ -869,6 +874,79 @@ pub fn get_anon_balance( Ok(oabar.get_amount()) } +/// Get OABAR (Open ABAR) using the ABAR, OwnerMemo and MTLeafInfo +/// @param {AnonBlindAssetRecord} abar - ABAR which needs to be opened +/// @param {OwnerMemo} memo - memo corresponding to the abar +/// @param keypair {AXfrKeyPair} - AXfrKeyPair of the ABAR owner +/// @param dec_key {XSecretKey} - Decryption key of the abar owner to open the Owner Memo +/// @param MTLeafInfo {mt_leaf_info} - the Merkle proof of the ABAR from commitment tree +/// @throws Will throw an error if abar fails to open +#[wasm_bindgen] +pub fn get_open_abar( + abar: AnonBlindAssetRecord, + memo: OwnerMemo, + keypair: AXfrKeyPair, + dec_key: XSecretKey, + mt_leaf_info: MTLeafInfo, +) -> Result { + let oabar = + OpenAnonBlindAssetRecordBuilder::from_abar(&abar, memo.memo, &keypair, &dec_key) + .c(d!()) + .map_err(error_to_jsvalue)? + .mt_leaf_info(mt_leaf_info.get_zei_mt_leaf_info().clone()) + .build() + .c(d!()) + .map_err(error_to_jsvalue)?; + + let json = JsValue::from_serde(&oabar) + .c(d!()) + .map_err(error_to_jsvalue)?; + Ok(json) + //Ok(serde_json::to_string(&oabar).unwrap()) +} + +/// Generate nullifier hash using ABAR, OwnerMemo and MTLeafInfo +/// @param {AnonBlindAssetRecord} abar - ABAR for which balance needs to be queried +/// @param {OwnerMemo} memo - memo corresponding to the abar +/// @param keypair {AXfrKeyPair} - AXfrKeyPair of the ABAR owner +/// @param randomized_keypair {AXfrKeyPair} - Randomized AXfrKeyPair of the ABAR owner +/// @param dec_key {XSecretKey} - Decryption key of the abar owner to open the Owner Memo +/// @param MTLeafInfo {mt_leaf_info} - the Merkle proof of the ABAR from commitment tree +/// @throws Will throw an error if abar fails to open +#[wasm_bindgen] +pub fn gen_nullifier_hash( + abar: AnonBlindAssetRecord, + memo: OwnerMemo, + keypair: AXfrKeyPair, + randomized_keypair: AXfrKeyPair, + dec_key: XSecretKey, + mt_leaf_info: MTLeafInfo, +) -> Result { + let oabar = + OpenAnonBlindAssetRecordBuilder::from_abar(&abar, memo.memo, &keypair, &dec_key) + .c(d!()) + .map_err(error_to_jsvalue)? + .mt_leaf_info(mt_leaf_info.get_zei_mt_leaf_info().clone()) + .build() + .c(d!()) + .map_err(error_to_jsvalue)?; + + let n = nullifier( + &randomized_keypair, + oabar.get_amount(), + &oabar.get_asset_type(), + mt_leaf_info.get_zei_mt_leaf_info().uid, + ); + let input = base64::encode_config(&n.to_bytes(), base64::URL_SAFE); + let nullifier_hash = sha256::Digest::from_slice( + &base64::decode_config(&input, base64::URL_SAFE) + .c(d!()) + .unwrap(), + ); + + Ok(serde_json::to_string(&nullifier_hash).unwrap()) +} + #[wasm_bindgen] #[derive(Default)] /// Structure that enables clients to construct complex transfers. @@ -1606,8 +1684,6 @@ use rand::{thread_rng, Rng}; use ring::pbkdf2; use std::num::NonZeroU32; use std::str; -use zei::anon_xfr::keys::{AXfrKeyPair, AXfrPubKey}; -use zei::anon_xfr::structs::{AnonBlindAssetRecord, OpenAnonBlindAssetRecordBuilder}; #[wasm_bindgen] /// Returns bech32 encoded representation of an XfrPublicKey. diff --git a/src/ledger/src/store/mod.rs b/src/ledger/src/store/mod.rs index 5543bc0dd..927f4f3d6 100644 --- a/src/ledger/src/store/mod.rs +++ b/src/ledger/src/store/mod.rs @@ -61,7 +61,7 @@ use { verify_anon_xfr_body, }, serialization::ZeiFromToBytes, - setup::{NodeParams, UserParams, DEFAULT_BP_NUM_GENS}, + setup::{NodeParams, UserParams}, xfr::{ lib::XfrNotePolicies, sig::XfrPublicKey, @@ -529,6 +529,14 @@ impl LedgerState { Ok(create_mt_leaf_info(t)) } + /// Check if the nullifier hash is present in nullifier set + #[inline(always)] + pub fn check_nullifier_hash(&self, hash: String) -> Result { + let d: Key = Key::from_base64(&hash).c(d!())?; + let is_null_present = self.nullifier_set.read().get(&d).c(d!())?.is_some(); + Ok(is_null_present) + } + // Initialize a logged Merkle tree for the ledger. // We might be creating a new tree or opening an existing one. #[inline(always)] @@ -1653,14 +1661,11 @@ impl LedgerStatus { // here with LedgerStatus available. for axfr_body in txn_effect.axfr_bodies.iter() { // TODO: maybe load user_params in memory for inp_op mapping - let user_params = UserParams::from_file_if_exists( + let user_params = UserParams::new( axfr_body.inputs.len(), axfr_body.outputs.len(), Some(41), - DEFAULT_BP_NUM_GENS, - None, - ) - .unwrap(); + ); let node_params = NodeParams::from(user_params); let abar_version = axfr_body.proof.merkle_root_version; verify_anon_xfr_body(