diff --git a/.gitignore b/.gitignore index f9b4f261f..883a511ab 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ stack.yaml.lock **/__pycache__ .idea + node_modules package-lock.json diff --git a/Cargo.toml b/Cargo.toml index 57d92f5b6..1978086b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,6 @@ members = [ "src/components/contracts/modules/evm/precompile/basic", "src/components/contracts/modules/evm/precompile/frc20", "src/components/contracts/modules/evm/precompile/modexp", - "src/components/contracts/modules/evm/precompile/sha3fips", "src/components/contracts/modules/evm/precompile/anemoi", "src/components/contracts/modules/evm/precompile/blake2", "src/components/contracts/modules/evm/precompile/bn128", @@ -37,22 +36,34 @@ members = [ ] [profile.dev] -incremental = false +opt-level = 3 +lto = "thin" +incremental = true +debug-assertions = true +debug = true +panic = 'abort' overflow-checks = true [profile.release] +opt-level = 3 +lto = "thin" incremental = false overflow-checks = true +panic = 'abort' [profile.bench] +opt-level = 3 +debug = false +rpath = false +lto = "thin" codegen-units = 1 +incremental = true +debug-assertions = false overflow-checks = false -[profile.dev.package.curve25519-dalek] -opt-level = 1 -overflow-checks = false - -[patch.crates-io] -ed25519-dalek = { git = "https://github.com/FindoraNetwork/ed25519-dalek", rev = "ad461f" } -curve25519-dalek = { git = "https://github.com/FindoraNetwork/curve25519-dalek", rev = "a2df65" } -x25519-dalek = { git = "https://github.com/FindoraNetwork/x25519-dalek", rev = "53bb1a" } +[profile.test] +opt-level = 2 +lto = "off" +incremental = true +debug-assertions = true +debug = true \ No newline at end of file diff --git a/README.md b/README.md index 1e2696eb3..167950ece 100644 --- a/README.md +++ b/README.md @@ -27,3 +27,5 @@ The primary license for Platform is the Business Source License 1.1 (`BUSL-1.1`) ### Exceptions - All files in `components/contracts` are licensed under `Apache-2.0` + + diff --git a/container/Dockerfile-binary-image-dev b/container/Dockerfile-binary-image-dev index 0b4306c7f..d9ad5e587 100644 --- a/container/Dockerfile-binary-image-dev +++ b/container/Dockerfile-binary-image-dev @@ -7,9 +7,9 @@ ENV PATH=$PATH:/root/.cargo/bin/ COPY . $WORK_DIR WORKDIR $WORK_DIR -RUN rustup toolchain install 1.70 && \ - rustup component add clippy --toolchain 1.70 && \ - rustup component add rustfmt --toolchain 1.70 +RUN rustup toolchain install stable && \ + rustup component add clippy --toolchain stable && \ + rustup component add rustfmt --toolchain stable RUN mkdir /binary RUN mkdir -p /binary/cleveldb && mkdir -p /binary/goleveldb diff --git a/container/Dockerfile-binary-image-release b/container/Dockerfile-binary-image-release index 4a3a5f0e6..44af02908 100644 --- a/container/Dockerfile-binary-image-release +++ b/container/Dockerfile-binary-image-release @@ -7,9 +7,9 @@ ENV PATH=$PATH:/root/.cargo/bin/ COPY . $WORK_DIR WORKDIR $WORK_DIR -RUN rustup toolchain install 1.70 && \ - rustup component add clippy --toolchain 1.70 && \ - rustup component add rustfmt --toolchain 1.70 +RUN rustup toolchain install stable && \ + rustup component add clippy --toolchain stable && \ + rustup component add rustfmt --toolchain stable RUN mkdir /binary RUN mkdir -p /binary/cleveldb && mkdir -p /binary/goleveldb diff --git a/rust-toolchain b/rust-toolchain index bfe79d0bd..2bf5ad044 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.70 +stable diff --git a/src/components/abciapp/Cargo.toml b/src/components/abciapp/Cargo.toml index e761d510d..5a0716399 100644 --- a/src/components/abciapp/Cargo.toml +++ b/src/components/abciapp/Cargo.toml @@ -15,18 +15,18 @@ path = "src/bins/abcid.rs" [dependencies] parking_lot = "0.12" -base64 = "0.12" +base64 = "0.13" bincode = "1.3.1" tracing = "0.1" rand = "0.8" -rand_chacha = "0.2" -rand_core = { version = "0.5", default-features = false, features = ["alloc"] } +rand_chacha = "0.3" +rand_core = { version = "0.6", default-features = false, features = ["alloc"] } attohttpc = { version = "0.23", default-features = false, features = ["compress", "json", "tls-rustls"] } serde = { version = "1.0.124", features = ["derive"] } serde_json = "1.0.40" lazy_static = "1.4.0" futures = { version = "0.3.16", features = ["thread-pool"] } -hex = "0.4.2" +hex = "0.4.3" ctrlc = { version = "=3.2.5", features = ["termination"] } protobuf = "2.16" toml = "0.5.8" @@ -41,15 +41,14 @@ percent-encoding = "2.1.0" nix = "0.22.1" -zei = { git = "https://github.com/FindoraNetwork/zei", branch = "stable-main" } +zei = { package="platform-lib-noah", git = "https://github.com/FindoraNetwork/platform-lib-noah", branch = "develop" } ruc = { version = "1.0.5", default-features = false, features = ["compact"] } abci = { git = "https://github.com/FindoraNetwork/tendermint-abci", tag = "0.7.6" } config = { path = "../config"} ledger = { path = "../../ledger" } -globutils = { git = "https://github.com/FindoraNetwork/platform-lib-utils", tag = "v1.0.0" } -cryptohash = { git = "https://github.com/FindoraNetwork/platform-lib-cryptohash", tag = "v1.0.0" } - +globutils = { git = "https://github.com/FindoraNetwork/platform-lib-utils", branch = "develop" } +cryptohash = { git = "https://github.com/FindoraNetwork/platform-lib-cryptohash", branch = "develop" } finutils = { path = "../finutils" } tempfile = "3.1.0" diff --git a/src/components/abciapp/src/abci/mod.rs b/src/components/abciapp/src/abci/mod.rs index 507078bd1..10ff8c609 100644 --- a/src/components/abciapp/src/abci/mod.rs +++ b/src/components/abciapp/src/abci/mod.rs @@ -94,7 +94,6 @@ pub fn run() -> Result<()> { "http://{}:{}", config.tendermint_host, config.tendermint_port ); - // keep them running in the background, // avoid being dropped by the jsonrpc crate. mem::forget(fc_rpc::start_web3_service( diff --git a/src/components/abciapp/src/abci/server/callback/mod.rs b/src/components/abciapp/src/abci/server/callback/mod.rs index d0ae04da8..8a1c9365e 100644 --- a/src/components/abciapp/src/abci/server/callback/mod.rs +++ b/src/components/abciapp/src/abci/server/callback/mod.rs @@ -2,13 +2,6 @@ //! # Impl function of tendermint ABCI //! -use globutils::wallet; -use ledger::{ - data_model::ASSET_TYPE_FRA, - staking::{FF_ADDR_EXTRA_120_0000, FF_ADDR_LIST}, -}; -use zei::xfr::asset_record::AssetRecordType; - mod utils; use { @@ -32,11 +25,15 @@ use { STATE_UPDATE_LIST, TXS, WEB3_SERVICE_START_HEIGHT, }, fp_storage::hash::{Sha256, StorageHasher}, + globutils::wallet, lazy_static::lazy_static, ledger::{ converter::is_convert_account, - data_model::Operation, - staking::{evm::EVM_STAKING, KEEP_HIST, VALIDATOR_UPDATE_BLOCK_ITV}, + data_model::{Operation, Transaction, ASSET_TYPE_FRA}, + staking::{ + evm::EVM_STAKING, FF_ADDR_EXTRA_120_0000, FF_ADDR_LIST, KEEP_HIST, + VALIDATOR_UPDATE_BLOCK_ITV, + }, store::{ api_cache, fbnc::{new_mapx, Mapx}, @@ -56,6 +53,7 @@ use { }, }, tracing::{error, info}, + zei::noah_api::xfr::asset_record::AssetRecordType, }; pub(crate) static TENDERMINT_BLOCK_HEIGHT: AtomicI64 = AtomicI64::new(0); @@ -100,9 +98,15 @@ pub fn info(s: &mut ABCISubmissionServer, req: &RequestInfo) -> ResponseInfo { && h < CFG.checkpoint.enable_frc20_height { resp.set_last_block_app_hash(la_hash); - } else { + } else if h < CFG.checkpoint.enable_triple_masking_height { let cs_hash = s.account_base_app.write().info(req).last_block_app_hash; resp.set_last_block_app_hash(app_hash("info", h, la_hash, cs_hash)); + } else { + let cs_hash = s.account_base_app.write().info(req).last_block_app_hash; + let tm_hash = state.get_anon_state_commitment().0; + resp.set_last_block_app_hash(app_hash_v2( + "info", h, la_hash, cs_hash, tm_hash, + )); } } @@ -140,6 +144,18 @@ pub fn check_tx(s: &mut ABCISubmissionServer, req: &RequestCheckTx) -> ResponseC TxCatalog::FindoraTx => { if matches!(req.field_type, CheckTxType::New) { if let Ok(tx) = convert_tx(req.get_tx()) { + for op in tx.body.operations.iter() { + if let Operation::TransferAnonAsset(op) = op { + let mut inputs = op.note.body.inputs.clone(); + inputs.sort(); + inputs.dedup(); + if inputs.len() != op.note.body.inputs.len() { + resp.log = "anon transfer input error".to_owned(); + resp.code = 1; + return resp; + } + } + } if td_height > CFG.checkpoint.check_signatures_num { for op in tx.body.operations.iter() { if let Operation::TransferAsset(op) = op { @@ -171,6 +187,11 @@ pub fn check_tx(s: &mut ABCISubmissionServer, req: &RequestCheckTx) -> ResponseC } else if TX_HISTORY.read().contains_key(&tx.hash_tm_rawbytes()) { resp.log = "Historical transaction".to_owned(); resp.code = 1; + } else if is_tm_transaction(&tx) + && td_height < CFG.checkpoint.enable_triple_masking_height + { + resp.code = 1; + resp.log = "Triple Masking is disabled".to_owned(); } } else { resp.log = "Invalid format".to_owned(); @@ -272,6 +293,18 @@ pub fn deliver_tx( match tx_catalog { TxCatalog::FindoraTx => { if let Ok(tx) = convert_tx(req.get_tx()) { + for op in tx.body.operations.iter() { + if let Operation::TransferAnonAsset(op) = op { + let mut inputs = op.note.body.inputs.clone(); + inputs.sort(); + inputs.dedup(); + if inputs.len() != op.note.body.inputs.len() { + resp.log = "anon Transfer input error".to_owned(); + resp.code = 1; + return resp; + } + } + } if td_height > CFG.checkpoint.check_signatures_num { for op in tx.body.operations.iter() { if let Operation::TransferAsset(op) = op { @@ -336,7 +369,7 @@ pub fn deliver_tx( if let Err(err) = s.account_base_app.write().deliver_findora_tx(&tx, &hash.0) { - info!(target: "abciapp", "deliver convert account tx failed: {err:?}"); + error!(target: "abciapp", "deliver convert account tx failed: {err:?}"); resp.code = 1; resp.log = @@ -375,6 +408,16 @@ pub fn deliver_tx( .db .write() .discard_session(); + } else if is_tm_transaction(&tx) + && td_height < CFG.checkpoint.enable_triple_masking_height + { + info!(target: "abciapp", + "Triple Masking transaction(FindoraTx) detected at early height {}: {:?}", + td_height, tx + ); + resp.code = 2; + resp.log = "Triple Masking is disabled".to_owned(); + return resp; } else if CFG.checkpoint.utxo_checktx_height < td_height { match tx.check_tx() { Ok(_) => { @@ -464,6 +507,7 @@ pub fn deliver_tx( } /// putting block in the ledgerState +#[allow(noop_method_call)] pub fn end_block( s: &mut ABCISubmissionServer, req: &RequestEndBlock, @@ -597,8 +641,11 @@ pub fn commit(s: &mut ABCISubmissionServer, req: &RequestCommit) -> ResponseComm && td_height < CFG.checkpoint.enable_frc20_height { r.set_data(la_hash); - } else { + } else if td_height < CFG.checkpoint.enable_triple_masking_height { r.set_data(app_hash("commit", td_height, la_hash, cs_hash)); + } else { + let tm_hash = state.get_anon_state_commitment().0; + r.set_data(app_hash_v2("commit", td_height, la_hash, cs_hash, tm_hash)); } IN_SAFE_ITV.store(false, Ordering::Release); @@ -728,3 +775,52 @@ fn app_hash( la_hash } } + +/// Combines ledger state hash and EVM chain state hash +/// and print app hashes for debugging +fn app_hash_v2( + when: &str, + height: i64, + mut la_hash: Vec, + cs_hash: Vec, + tm_hash: Vec, +) -> Vec { + info!(target: "abciapp", + "app_hash_{}: {}_{}_{}, height: {}", + when, + hex::encode(la_hash.clone()), + hex::encode(cs_hash.clone()), + hex::encode(tm_hash.clone()), + height + ); + + if !cs_hash.is_empty() { + la_hash.extend_from_slice(&cs_hash); + + if !tm_hash.is_empty() { + la_hash.extend_from_slice(&tm_hash); + } + + Sha256::hash(la_hash.as_slice()).to_vec() + } else if !tm_hash.is_empty() { + la_hash.extend([0u8; 32]); + la_hash.extend_from_slice(&tm_hash); + + Sha256::hash(la_hash.as_slice()).to_vec() + } else { + la_hash + } +} + +fn is_tm_transaction(tx: &Transaction) -> bool { + tx.body + .operations + .iter() + .try_for_each(|op| match op { + Operation::BarToAbar(_a) => None, + Operation::AbarToBar(_a) => None, + Operation::TransferAnonAsset(_a) => None, + _ => Some(()), + }) + .is_none() +} diff --git a/src/components/abciapp/src/abci/server/callback/utils.rs b/src/components/abciapp/src/abci/server/callback/utils.rs index 109bf5ec2..7f3e11946 100644 --- a/src/components/abciapp/src/abci/server/callback/utils.rs +++ b/src/components/abciapp/src/abci/server/callback/utils.rs @@ -6,7 +6,7 @@ use { protobuf::RepeatedField, serde::Serialize, std::time::SystemTime, - zei::xfr::structs::{XfrAmount, XfrAssetType}, + zei::noah_api::xfr::structs::{XfrAmount, XfrAssetType}, }; /// generate attr(tags) for index-ops of tendermint @@ -39,8 +39,13 @@ pub fn gen_tendermint_attr(tx: &Transaction) -> RepeatedField { res.push(ev); let (from, to) = gen_tendermint_attr_addr(tx); + let (nullifiers, commitments) = gen_tendermint_attr_anon(tx); - if !from.is_empty() || !to.is_empty() { + if !from.is_empty() + || !to.is_empty() + || !nullifiers.is_empty() + || !commitments.is_empty() + { let mut ev = Event::new(); ev.set_field_type("addr".to_owned()); @@ -76,6 +81,8 @@ pub fn gen_tendermint_attr(tx: &Transaction) -> RepeatedField { index_addr!(from, "addr.from"); index_addr!(to, "addr.to"); + index_addr!(nullifiers, "nullifier.used"); + index_addr!(commitments, "commitment.created"); } RepeatedField::from_vec(res) @@ -126,6 +133,59 @@ fn gen_tendermint_attr_addr(tx: &Transaction) -> (Vec, Vec) { Operation::UpdateMemo(d) => { append_attr!(d); } + Operation::BarToAbar(d) => { + let mut attr = TagAttr::default(); + attr.addr = globutils::wallet::public_key_to_bech32( + &d.input_record().public_key, + ); + base.0.push(attr); + } + Operation::AbarToBar(d) => { + let mut attr = TagAttr::default(); + attr.addr = globutils::wallet::public_key_to_bech32( + &d.note.get_public_key(), + ); + base.1.push(attr); + } + _ => {} + } + + base + }) +} + +fn gen_tendermint_attr_anon(tx: &Transaction) -> (Vec, Vec) { + tx.body + .operations + .iter() + .fold((vec![], vec![]), |mut base, op| { + match op { + Operation::BarToAbar(d) => { + let mut attr = TagAttr::default(); + attr.addr = globutils::wallet::commitment_to_base58( + &d.output_record().commitment, + ); + base.1.push(attr); + } + Operation::AbarToBar(d) => { + let mut attr = TagAttr::default(); + attr.addr = + globutils::wallet::nullifier_to_base58(&d.note.get_input()); + base.0.push(attr); + } + Operation::TransferAnonAsset(d) => { + for ix in &d.note.body.inputs { + let mut attr = TagAttr::default(); + attr.addr = globutils::wallet::nullifier_to_base58(ix); + base.0.push(attr); + } + for ox in &d.note.body.outputs { + let mut attr = TagAttr::default(); + attr.addr = + globutils::wallet::commitment_to_base58(&ox.commitment); + base.1.push(attr); + } + } _ => {} } diff --git a/src/components/abciapp/src/abci/staking/mod.rs b/src/components/abciapp/src/abci/staking/mod.rs index d65de6ae3..7e6f892b6 100644 --- a/src/components/abciapp/src/abci/staking/mod.rs +++ b/src/components/abciapp/src/abci/staking/mod.rs @@ -39,6 +39,7 @@ use { ops::{Deref, DerefMut}, sync::atomic::Ordering, }, + zei::XfrPublicKey, }; // The top 50~ candidate validators @@ -233,6 +234,7 @@ pub fn get_validators( /// - pay delegation rewards /// - pay proposer rewards(traditional block rewards) /// - do governance operations +#[allow(noop_method_call)] pub fn system_ops( la: &mut LedgerState, header: &Header, @@ -365,7 +367,7 @@ pub fn system_prism_mint_pay( let atc = AssetTypeCode { val: mint.asset }; let at = if let Some(mut at) = la.get_asset_type(&atc) { at.properties.issuer = IssuerPublicKey { - key: *BLACK_HOLE_PUBKEY_STAKING, + key: XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY_STAKING), }; if mint.max_supply != 0 { at.properties.asset_rules.max_units = Some(mint.max_supply); @@ -375,7 +377,7 @@ pub fn system_prism_mint_pay( } else { let mut at = AssetType::default(); at.properties.issuer = IssuerPublicKey { - key: *BLACK_HOLE_PUBKEY_STAKING, + key: XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY_STAKING), }; if mint.max_supply != 0 { diff --git a/src/components/abciapp/src/abci/staking/test.rs b/src/components/abciapp/src/abci/staking/test.rs index b046ba6fb..6dd104146 100644 --- a/src/components/abciapp/src/abci/staking/test.rs +++ b/src/components/abciapp/src/abci/staking/test.rs @@ -14,10 +14,12 @@ use { rand_chacha::ChaChaRng, rand_core::SeedableRng, ruc::*, - zei::xfr::{ - asset_record::{open_blind_asset_record, AssetRecordType}, - sig::{XfrKeyPair, XfrPublicKey}, - structs::{AssetRecordTemplate, XfrAmount}, + zei::{ + noah_api::xfr::{ + asset_record::{open_blind_asset_record, AssetRecordType}, + structs::{AssetRecordTemplate, XfrAmount}, + }, + XfrKeyPair, XfrPublicKey, }, }; @@ -51,7 +53,7 @@ fn check_block_rewards_rate() -> Result<()> { let tx = gen_transfer_tx( &ledger, &root_kp, - &FF_PK_LIST[random::() % FF_PK_LIST.len()], + &XfrPublicKey::from_noah(&FF_PK_LIST[random::() % FF_PK_LIST.len()]), FRA_PRE_ISSUE_AMOUNT / 200, seq_id, ) @@ -99,7 +101,8 @@ fn gen_transfer_tx( ) -> Result { let mut tx_builder = TransactionBuilder::from_seq_id(seq_id); - let target_list = vec![(target_pk, am), (&*BLACK_HOLE_PUBKEY, TX_FEE_MIN)]; + let binding = XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY); + let target_list = vec![(target_pk, am), (&binding, TX_FEE_MIN)]; let mut trans_builder = TransferOperationBuilder::new(); @@ -118,13 +121,17 @@ fn gen_transfer_tx( continue; } - open_blind_asset_record(&utxo.0.record, &owner_memo, owner_kp) - .c(d!()) - .and_then(|ob| { - trans_builder - .add_input(TxoRef::Absolute(sid), ob, None, None, i_am) - .c(d!()) - })?; + open_blind_asset_record( + &utxo.0.record.into_noah(), + &owner_memo.map(|o| o.into_noah()), + &owner_kp.into_noah(), + ) + .c(d!()) + .and_then(|ob| { + trans_builder + .add_input(TxoRef::Absolute(sid), ob, None, None, i_am) + .c(d!()) + })?; alt!(0 == am, break); } @@ -138,7 +145,7 @@ fn gen_transfer_tx( n, ASSET_TYPE_FRA, AssetRecordType::NonConfidentialAmount_NonConfidentialAssetType, - *pk, + pk.into_noah(), ) }); @@ -159,5 +166,5 @@ fn gen_transfer_tx( .c(d!())?; tx_builder.add_operation(op); - Ok(tx_builder.take_transaction()) + tx_builder.build_and_take_transaction() } diff --git a/src/components/abciapp/src/api/query_server/query_api/ledger_api.rs b/src/components/abciapp/src/api/query_server/query_api/ledger_api.rs index f40706e47..bc6e38cfb 100644 --- a/src/components/abciapp/src/api/query_server/query_api/ledger_api.rs +++ b/src/components/abciapp/src/api/query_server/query_api/ledger_api.rs @@ -10,11 +10,12 @@ use { DelegationInfo, DelegatorInfo, DelegatorList, NetworkRoute, Validator, ValidatorDetail, ValidatorList, }, - globutils::HashOf, + globutils::{wallet, HashOf}, ledger::{ data_model::{ - AssetType, AssetTypeCode, AuthenticatedUtxo, StateCommitmentData, TxnSID, - TxoSID, UnAuthenticatedUtxo, Utxo, + ABARData, ATxoSID, AssetType, AssetTypeCode, AssetTypePrefix, + AuthenticatedUtxo, StateCommitmentData, TxnSID, TxoSID, UnAuthenticatedUtxo, + Utxo, }, staking::{ DelegationRwdDetail, DelegationState, Staking, TendermintAddr, @@ -25,7 +26,7 @@ use { ruc::*, serde::{Deserialize, Serialize}, std::{collections::BTreeMap, mem, sync::Arc}, - zei::xfr::{sig::XfrPublicKey, structs::OwnerMemo}, + zei::{OwnerMemo, XfrPublicKey}, }; /// Ping route to check for liveness of API @@ -149,6 +150,27 @@ pub async fn query_asset( } } +/// get_derived asset code according to `AssetTypeCode` +pub async fn get_derived_asset_code( + data: web::Data>>, + info: web::Path, +) -> actix_web::Result> { + let qs = data.read(); + if let Ok(token_code) = AssetTypeCode::new_from_base64(&info) { + let derived_asset_code = AssetTypeCode::from_prefix_and_raw_asset_type_code( + AssetTypePrefix::UserDefined, + &token_code, + &CFG.checkpoint, + qs.ledger_cloned.get_tendermint_height(), + ); + Ok(web::Json(derived_asset_code.to_base64())) + } else { + Err(actix_web::error::ErrorBadRequest( + "Invalid asset definition encoding.", + )) + } +} + /// query tx according to `TxnSID` pub async fn query_txn( data: web::Data>>, @@ -201,8 +223,7 @@ pub async fn query_global_state( data: web::Data>>, ) -> web::Json<(HashOf>, u64, &'static str)> { let qs = data.read(); - let ledger = &qs.ledger_cloned; - let (hash, seq_id) = ledger.get_state_commitment(); + let (hash, seq_id) = qs.get_state_commitment_from_api_cache(); web::Json((hash, seq_id, "v4UVgkIBpj0eNYI1B1QhTTduJHCIHH126HcdesCxRdLkVGDKrVUPgwmNLCDafTVgC5e4oDhAGjPNt1VhUr6ZCQ==")) } @@ -691,6 +712,24 @@ pub async fn query_owned_utxos( .map(|pk| web::Json(pnk!(ledger.get_owned_utxos(&pk)))) } +// query utxos according to `commitment` +pub(super) async fn query_owned_abar( + data: web::Data>>, + com: web::Path, +) -> actix_web::Result>> { + let qs = data.read(); + let ledger = &qs.ledger_cloned; + globutils::wallet::commitment_from_base58(com.as_str()) + .c(d!()) + .map_err(|e| error::ErrorBadRequest(e.generate_log(None))) + .map(|com| { + web::Json(ledger.get_owned_abar(&com).map(|a| { + let c = wallet::commitment_to_base58(&com); + (a, ABARData { commitment: c }) + })) + }) +} + #[allow(missing_docs)] pub enum ApiRoutes { UtxoSid, @@ -698,11 +737,13 @@ pub enum ApiRoutes { UtxoSidList, AssetIssuanceNum, AssetToken, + GetDerivedAssetCode, GlobalState, TxnSid, TxnSidLight, GlobalStateVersion, OwnedUtxos, + OwnedAbars, ValidatorList, DelegationInfo, DelegatorList, @@ -717,6 +758,7 @@ impl NetworkRoute for ApiRoutes { ApiRoutes::UtxoSidList => "utxo_sid_list", ApiRoutes::AssetIssuanceNum => "asset_issuance_num", ApiRoutes::AssetToken => "asset_token", + ApiRoutes::GetDerivedAssetCode => "get_derived_asset_code", ApiRoutes::GlobalState => "global_state", ApiRoutes::TxnSid => "txn_sid", ApiRoutes::TxnSidLight => "txn_sid_light", @@ -726,6 +768,7 @@ impl NetworkRoute for ApiRoutes { ApiRoutes::DelegationInfo => "delegation_info", ApiRoutes::DelegatorList => "delegator_list", ApiRoutes::ValidatorDetail => "validator_detail", + ApiRoutes::OwnedAbars => "owned_abars", }; "/".to_owned() + endpoint } 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 e90d15ae2..05aad71c7 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 @@ -16,8 +16,8 @@ use { globutils::wallet, ledger::{ data_model::{ - b64dec, AssetTypeCode, DefineAsset, IssuerPublicKey, Transaction, TxOutput, - TxnIDHash, TxnSID, TxoSID, XfrAddress, BLACK_HOLE_PUBKEY, + b64dec, ATxoSID, AssetTypeCode, DefineAsset, IssuerPublicKey, Transaction, + TxOutput, TxnIDHash, TxnSID, TxoSID, XfrAddress, BLACK_HOLE_PUBKEY, }, staking::{ ops::mint_fra::MintEntry, FF_PK_EXTRA_120_0000, FRA, FRA_TOTAL_AMOUNT, @@ -29,13 +29,14 @@ use { serde::{Deserialize, Serialize}, server::QueryServer, std::{ - collections::{BTreeMap, HashSet}, + collections::{BTreeMap, HashMap, HashSet}, sync::Arc, }, tracing::info, zei::{ - serialization::ZeiFromToBytes, - xfr::{sig::XfrPublicKey, structs::OwnerMemo}, + noah_algebra::serialization::NoahFromToBytes, + noah_api::anon_xfr::structs::{AxfrOwnerMemo, Commitment, MTLeafInfo}, + OwnerMemo, XfrPublicKey, }, }; @@ -94,6 +95,44 @@ pub async fn get_owner_memo_batch( Ok(web::Json(resp)) } +/// Returns the owner memo required to decrypt the asset record stored at given index, if it exists. +#[allow(clippy::unnecessary_wraps)] +async fn get_abar_memo( + data: web::Data>>, + info: web::Path, +) -> actix_web::Result>, actix_web::error::Error> { + let server = data.read(); + Ok(web::Json(server.get_abar_memo(ATxoSID(*info)))) +} + +/// Returns the owner memos required to decrypt the asset record stored at between start and end, +/// include start and end, limit 100. +async fn get_abar_memos( + data: web::Data>>, + query: web::Query>, +) -> actix_web::Result>, actix_web::error::Error> { + match (query.get("start"), query.get("end")) { + (Some(start), Some(end)) => { + if end < start || end - start > 100 { + // return limit 100 error. + return Err(actix_web::error::ErrorBadRequest("Limit 100")); + } + let server = data.read(); + Ok(web::Json(server.get_abar_memos(*start, *end))) + } + _ => Err(actix_web::error::ErrorBadRequest("Missing start and end")), + } +} + +/// Return the abar commitment by sid. +async fn get_abar_commitment( + data: web::Data>>, + info: web::Path, +) -> actix_web::Result>, actix_web::error::Error> { + let server = data.read(); + Ok(web::Json(server.get_abar_commitment(ATxoSID(*info)))) +} + /// Returns an array of the utxo sids currently spendable by a given address pub async fn get_owned_utxos( data: web::Data>>, @@ -115,6 +154,53 @@ pub async fn get_owned_utxos( Ok(web::Json(utxos)) } +/// Returns the ATxo Sid currently spendable by a given commitment +async fn get_owned_abar( + data: web::Data>>, + com: web::Path, +) -> actix_web::Result>> { + let qs = data.read(); + let ledger = &qs.ledger_cloned; + //let read = qs.state.as_ref().unwrap().read(); + globutils::wallet::commitment_from_base58(com.as_str()) + .c(d!()) + .map_err(|e| error::ErrorBadRequest(e.generate_log(None))) + .map(|com| web::Json(ledger.get_owned_abar(&com))) +} + +/// Returns the Merkle proof for anonymous transactions +async fn get_abar_proof( + data: web::Data>>, + info: web::Path, +) -> actix_web::Result>, actix_web::error::Error> { + let server = data.read(); + 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()))) +} + +async fn get_max_atxo_sid( + data: web::Data>>, +) -> actix_web::Result>, actix_web::error::Error> { + let server = data.read(); + Ok(web::Json(server.max_atxo_sid())) +} + +async fn get_max_atxo_sid_at_height( + data: web::Data>>, + info: web::Path, +) -> actix_web::Result>, actix_web::error::Error> { + let server = data.read(); + Ok(web::Json(server.max_atxo_sid_at_height(*info))) +} + /// Define interface type #[allow(missing_docs)] pub enum QueryServerRoutes { @@ -122,6 +208,14 @@ pub enum QueryServerRoutes { GetOwnerMemo, GetOwnerMemoBatch, GetOwnedUtxos, + GetOwnedAbars, + GetAbarCommitment, + GetAbarMemo, + GetAbarMemos, + GetAbarProof, + CheckNullifierHash, + GetMaxATxoSid, + GetMaxATxoSidAtHeight, GetCreatedAssets, GetIssuedRecords, GetIssuedRecordsByCode, @@ -140,8 +234,16 @@ impl NetworkRoute for QueryServerRoutes { QueryServerRoutes::GetRelatedTxns => "get_related_txns", QueryServerRoutes::GetRelatedXfrs => "get_related_xfrs", QueryServerRoutes::GetOwnedUtxos => "get_owned_utxos", + QueryServerRoutes::GetOwnedAbars => "get_owned_abar", QueryServerRoutes::GetOwnerMemo => "get_owner_memo", QueryServerRoutes::GetOwnerMemoBatch => "get_owner_memo_batch", + QueryServerRoutes::GetAbarCommitment => "get_abar_commitment", + QueryServerRoutes::GetAbarMemo => "get_abar_memo", + QueryServerRoutes::GetAbarMemos => "get_abar_memos", + QueryServerRoutes::GetAbarProof => "get_abar_proof", + QueryServerRoutes::CheckNullifierHash => "check_nullifier_hash", + QueryServerRoutes::GetMaxATxoSid => "get_max_atxo_sid", + QueryServerRoutes::GetMaxATxoSidAtHeight => "get_max_atxo_sid_at_height", QueryServerRoutes::GetCreatedAssets => "get_created_assets", QueryServerRoutes::GetIssuedRecords => "get_issued_records", QueryServerRoutes::GetIssuedRecordsByCode => "get_issued_records_by_code", @@ -160,7 +262,7 @@ pub async fn get_created_assets( info: web::Path, ) -> actix_web::Result>> { // Convert from base64 representation - let key: XfrPublicKey = XfrPublicKey::zei_from_bytes( + let key: XfrPublicKey = XfrPublicKey::noah_from_bytes( &b64dec(&*info) .c(d!()) .map_err(|e| error::ErrorBadRequest(e.to_string()))?, @@ -178,7 +280,7 @@ pub async fn get_issued_records( info: web::Path, ) -> actix_web::Result)>>> { // Convert from base64 representation - let key: XfrPublicKey = XfrPublicKey::zei_from_bytes( + let key: XfrPublicKey = XfrPublicKey::noah_from_bytes( &b64dec(&*info) .c(d!()) .map_err(|e| error::ErrorBadRequest(e.to_string()))?, @@ -388,7 +490,7 @@ pub async fn get_related_txns( info: web::Path, ) -> actix_web::Result>> { // Convert from base64 representation - let key: XfrPublicKey = XfrPublicKey::zei_from_bytes( + let key: XfrPublicKey = XfrPublicKey::noah_from_bytes( &b64dec(&*info) .c(d!()) .map_err(|e| error::ErrorBadRequest(e.to_string()))?, @@ -452,8 +554,8 @@ pub async fn get_total_supply( data: web::Data>>, ) -> actix_web::Result>, actix_web::error::Error> { let l = data.read(); - let burn_pubkey = *BLACK_HOLE_PUBKEY; - let extra_pubkey = *FF_PK_EXTRA_120_0000; + let burn_pubkey = XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY); + let extra_pubkey = XfrPublicKey::from_noah(&FF_PK_EXTRA_120_0000); let burn_balance = l .ledger_cloned @@ -522,6 +624,10 @@ impl QueryApi { &QueryServerRoutes::GetOwnedUtxos.with_arg_template("address"), web::get().to(get_owned_utxos), ) + .route( + &QueryServerRoutes::GetOwnedAbars.with_arg_template("commitment"), + web::get().to(get_owned_abar), + ) .route( &QueryServerRoutes::GetOwnerMemo.with_arg_template("txo_sid"), web::get().to(get_owner_memo), @@ -531,6 +637,36 @@ impl QueryApi { .with_arg_template("txo_sid_list"), web::get().to(get_owner_memo_batch), ) + .route( + &QueryServerRoutes::GetAbarCommitment.with_arg_template("atxo_sid"), + web::get().to(get_abar_commitment), + ) + .route( + &QueryServerRoutes::GetAbarMemo.with_arg_template("atxo_sid"), + web::get().to(get_abar_memo), + ) + .route( + &QueryServerRoutes::GetAbarMemos.route(), + web::get().to(get_abar_memos), + ) + .route( + &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::GetMaxATxoSid.route(), + web::get().to(get_max_atxo_sid), + ) + .route( + &QueryServerRoutes::GetMaxATxoSidAtHeight + .with_arg_template("height"), + web::get().to(get_max_atxo_sid_at_height), + ) .route( &QueryServerRoutes::GetRelatedTxns.with_arg_template("address"), web::get().to(get_related_txns), @@ -596,6 +732,10 @@ impl QueryApi { &ApiRoutes::AssetToken.with_arg_template("code"), web::get().to(query_asset), ) + .route( + &ApiRoutes::GetDerivedAssetCode.with_arg_template("code"), + web::get().to(get_derived_asset_code), + ) .route( &ApiRoutes::GlobalState.route(), web::get().to(query_global_state), @@ -616,6 +756,10 @@ impl QueryApi { &ApiRoutes::OwnedUtxos.with_arg_template("owner"), web::get().to(query_owned_utxos), ) + .route( + &ApiRoutes::OwnedAbars.with_arg_template("owner"), + web::get().to(query_owned_abar), + ) .route( &ApiRoutes::ValidatorList.route(), web::get().to(query_validators), 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 f05e5ebf2..bca977019 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 @@ -3,11 +3,12 @@ //! use { + globutils::HashOf, lazy_static::lazy_static, ledger::{ data_model::{ - AssetTypeCode, DefineAsset, IssuerPublicKey, Transaction, TxOutput, - TxnIDHash, TxnSID, TxoSID, XfrAddress, + ATxoSID, AssetTypeCode, DefineAsset, IssuerPublicKey, StateCommitmentData, + Transaction, TxOutput, TxnIDHash, TxnSID, TxoSID, XfrAddress, }, staking::{ops::mint_fra::MintEntry, BlockHeight}, store::LedgerState, @@ -15,7 +16,10 @@ use { parking_lot::{Condvar, Mutex, RwLock}, ruc::*, std::{collections::HashSet, sync::Arc}, - zei::xfr::structs::OwnerMemo, + zei::{ + noah_api::anon_xfr::structs::{AxfrOwnerMemo, Commitment, MTLeafInfo}, + OwnerMemo, + }, }; lazy_static! { @@ -300,6 +304,81 @@ impl QueryServer { .get(&txo_sid) } + /// Returns the abar owner memo required to decrypt the asset record stored at given index, if it exists. + #[inline(always)] + pub fn get_abar_memo(&self, atxo_sid: ATxoSID) -> Option { + self.ledger_cloned + .api_cache + .as_ref() + .and_then(|api| api.abar_memos.get(&atxo_sid)) + } + + /// Returns the owner memos required to decrypt the asset record stored at between start and end, + /// include start and end, limit 100. + #[inline(always)] + pub fn get_abar_memos(&self, start: u64, end: u64) -> Vec<(u64, AxfrOwnerMemo)> { + let mut memos = vec![]; + let cache = self.ledger_cloned.api_cache.as_ref().unwrap(); + for i in start..=end { + if let Some(memo) = cache.abar_memos.get(&ATxoSID(i)) { + memos.push((i, memo)); + } + } + memos + } + + #[inline(always)] + #[allow(missing_docs)] + pub fn get_state_commitment_from_api_cache( + &self, + ) -> (HashOf>, u64) { + let block_count = self.ledger_cloned.get_block_commit_count(); + let commitment = self + .ledger_cloned + .api_cache + .as_ref() + .unwrap() + .state_commitment_version + .clone() + .unwrap_or_else(|| HashOf::new(&None)); + (commitment, block_count) + } + + /// Returns the abar commitment by given index, if it exists. + pub fn get_abar_commitment(&self, atxo_sid: ATxoSID) -> Option { + self.ledger_cloned.get_abar(&atxo_sid) + } + + /// Returns the Merkle proof from the given ATxoSID + #[inline(always)] + pub fn get_abar_proof(&self, atxo_sid: ATxoSID) -> Option { + self.ledger_cloned.get_abar_proof(atxo_sid).ok() + } + + /// Returns a bool value from the given hash + #[inline(always)] + pub fn check_nullifier_hash(&self, null_hash: String) -> Option { + self.ledger_cloned.check_nullifier_hash(null_hash).ok() + } + + /// Returns an int value for the max ATxoSid + #[inline(always)] + pub fn max_atxo_sid(&self) -> Option { + self.ledger_cloned + .api_cache + .as_ref() + .and_then(|api| api.abar_memos.len().checked_sub(1)) + } + + /// Returns an int value for the max ATxoSid at a given block height + #[inline(always)] + pub fn max_atxo_sid_at_height(&self, height: BlockHeight) -> Option { + self.ledger_cloned + .api_cache + .as_ref() + .and_then(|api| api.height_to_max_atxo.get(&height).unwrap_or(None)) + } + /// 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/config/Cargo.toml b/src/components/config/Cargo.toml index ec2670aee..fc41b5e6b 100644 --- a/src/components/config/Cargo.toml +++ b/src/components/config/Cargo.toml @@ -16,7 +16,7 @@ serde_json = "1.0" serde-strz = "1.1.1" toml = "0.5.8" -globutils = { git = "https://github.com/FindoraNetwork/platform-lib-utils", tag = "v1.0.0" } +globutils = { git = "https://github.com/FindoraNetwork/platform-lib-utils", branch = "develop" } [target.'cfg(target_os= "linux")'.dependencies] btm = "0.1.6" diff --git a/src/components/config/src/abci/mod.rs b/src/components/config/src/abci/mod.rs index 66b6ec98b..e06eb0468 100644 --- a/src/components/config/src/abci/mod.rs +++ b/src/components/config/src/abci/mod.rs @@ -92,6 +92,8 @@ pub struct CheckPointConfig { pub fix_exec_code: i64, + pub enable_triple_masking_height: i64, + #[serde(default = "def_check_signatures_num")] pub check_signatures_num: i64, @@ -103,6 +105,9 @@ pub struct CheckPointConfig { #[serde(default = "def_utxo_asset_prefix_height")] pub utxo_asset_prefix_height: u64, + #[serde(default = "def_utxo_asset_prefix_height_2nd_update")] + pub utxo_asset_prefix_height_2nd_update: u64, + #[serde(default = "def_prismxx_inital_height")] pub prismxx_inital_height: i64, @@ -168,6 +173,10 @@ fn def_utxo_asset_prefix_height() -> u64 { DEFAULT_CHECKPOINT_CONFIG.utxo_asset_prefix_height } +fn def_utxo_asset_prefix_height_2nd_update() -> u64 { + DEFAULT_CHECKPOINT_CONFIG.utxo_asset_prefix_height_2nd_update +} + fn def_prismxx_inital_height() -> i64 { DEFAULT_CHECKPOINT_CONFIG.prismxx_inital_height } @@ -252,9 +261,11 @@ lazy_static! { evm_substate_v2_height: 0, disable_delegate_frc20: 0, fix_exec_code: 0, + enable_triple_masking_height: 0, check_signatures_num: 0, fix_deliver_tx_revert_nonce_height: 0, utxo_asset_prefix_height: 0, + utxo_asset_prefix_height_2nd_update: 0, prismxx_inital_height: 128, prism_bridge_address: "0x5f9552fEd754F20B636C996DaDB32806554Bb995".to_owned(), remove_fake_staking_hash: 0, @@ -299,9 +310,11 @@ lazy_static! { evm_substate_v2_height: 3351349, disable_delegate_frc20: 3401450, fix_exec_code: 3401450, + enable_triple_masking_height: 5000_0000, check_signatures_num: 4004430, fix_deliver_tx_revert_nonce_height: 4004430, utxo_asset_prefix_height: 4004430, + utxo_asset_prefix_height_2nd_update: 5000_0000, prismxx_inital_height: 4004430, prism_bridge_address: "0x4672372fDB139B7295Fc59b55b43EC5fF2761A0b".to_owned(), remove_fake_staking_hash: 4004430, diff --git a/src/components/config/src/findora/mod.rs b/src/components/config/src/findora/mod.rs index c40b5bc67..c23877ba4 100644 --- a/src/components/config/src/findora/mod.rs +++ b/src/components/config/src/findora/mod.rs @@ -3,6 +3,7 @@ pub mod init { ruc::*, std::{fs, str}, }; + const QA01_GENESIS_URL: &str = "https://dev-qa01.dev.findora.org:26657/genesis"; const QA02_GENESIS_URL: &str = "https://dev-qa02.dev.findora.org:26657/genesis"; diff --git a/src/components/contracts/baseapp/Cargo.toml b/src/components/contracts/baseapp/Cargo.toml index 5a2598c54..996605b1a 100644 --- a/src/components/contracts/baseapp/Cargo.toml +++ b/src/components/contracts/baseapp/Cargo.toml @@ -22,10 +22,10 @@ protobuf = "2.16" ruc = "1.0" serde = {version = "1.0.124", features = ["derive"]} serde_json = "1.0.40" -storage = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.5" } -fin_db = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.5" } -sha3 = "0.8" -zei = { git = "https://github.com/FindoraNetwork/zei", branch = "stable-main" } +storage = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.6" } +fin_db = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.6" } +sha3 = "0.10" +zei = { package="platform-lib-noah", git = "https://github.com/FindoraNetwork/platform-lib-noah", branch = "develop" } config = { path = "../../config"} @@ -48,9 +48,12 @@ evm-precompile = {path = "../modules/evm/precompile"} evm-precompile-basic = {path = "../modules/evm/precompile/basic"} evm-precompile-frc20 = {path = "../modules/evm/precompile/frc20"} evm-precompile-modexp = {path = "../modules/evm/precompile/modexp"} -evm-precompile-sha3fips = {path = "../modules/evm/precompile/sha3fips"} +evm-precompile-blake2 = {path = "../modules/evm/precompile/blake2"} +evm-precompile-bn128 = {path = "../modules/evm/precompile/bn128"} +evm-precompile-anemoi = {path = "../modules/evm/precompile/anemoi"} + [features] abci_mock = [] benchmark = ["module-evm/benchmark","module-ethereum/benchmark"] -debug_env = [] \ No newline at end of file +debug_env = [] diff --git a/src/components/contracts/baseapp/src/lib.rs b/src/components/contracts/baseapp/src/lib.rs index 19c0c42af..8caa73d15 100644 --- a/src/components/contracts/baseapp/src/lib.rs +++ b/src/components/contracts/baseapp/src/lib.rs @@ -164,10 +164,13 @@ impl module_evm::Config for BaseApp { evm_precompile_basic::Ripemd160, evm_precompile_basic::Identity, evm_precompile_modexp::Modexp, - evm_precompile_basic::ECRecoverPublicKey, - evm_precompile_sha3fips::Sha3FIPS256, - evm_precompile_sha3fips::Sha3FIPS512, + evm_precompile_bn128::Bn128Add, + evm_precompile_bn128::Bn128Mul, + evm_precompile_bn128::Bn128Pairing, + evm_precompile_blake2::Blake2F, evm_precompile_frc20::FRC20, + evm_precompile_anemoi::Anemoi254, + evm_precompile_anemoi::Anemoi381, ); type PrecompilesType = FindoraPrecompiles; type PrecompilesValue = PrecompilesValue; diff --git a/src/components/contracts/baseapp/src/staking.rs b/src/components/contracts/baseapp/src/staking.rs index a99f360f2..b588ab99f 100644 --- a/src/components/contracts/baseapp/src/staking.rs +++ b/src/components/contracts/baseapp/src/staking.rs @@ -15,7 +15,7 @@ use module_evm::{ use ruc::{d, Result, RucResult}; use sha3::{Digest, Keccak256}; use std::{collections::BTreeMap, str::FromStr}; -use zei::xfr::sig::XfrPublicKey; +use zei::{noah_algebra::prelude::NoahFromToBytes, XfrPublicKey}; impl EVMStaking for BaseApp { fn import_validators( @@ -41,7 +41,7 @@ impl EVMStaking for BaseApp { memo: serde_json::to_string(&v.memo).c(d!())?, rate: mapping_rate(v.commission_rate), staker: mapping_address(&v.id), - staker_pk: v.id.as_bytes().to_vec(), + staker_pk: v.id.noah_to_bytes(), power: U256::from(v.td_power), begin_block: U256::from(begin_block), }); @@ -111,7 +111,7 @@ impl EVMStaking for BaseApp { delegators.push(DelegatorParam { validator: *validator_address, delegator: delegator_address, - delegator_pk: public_key.as_bytes().to_vec(), + delegator_pk: public_key.noah_to_bytes(), bound_amount, unbound_amount, }); @@ -182,7 +182,7 @@ impl EVMStaking for BaseApp { memo: String, rate: [u64; 2], ) -> Result<()> { - let staker_pk = staker.as_bytes().to_vec(); + let staker_pk = staker.noah_to_bytes(); let staker_address = mapping_address(staker); let amount = @@ -224,7 +224,7 @@ impl EVMStaking for BaseApp { amount: u64, td_addr: &[u8], ) -> Result<()> { - let delegator_pk = delegator.as_bytes().to_vec(); + let delegator_pk = delegator.noah_to_bytes(); let delegator_address = mapping_address(delegator); let amount = @@ -376,6 +376,6 @@ fn mapping_rate(rate: [u64; 2]) -> U256 { } pub fn mapping_address(pk: &XfrPublicKey) -> H160 { - let result = Keccak256::digest(pk.as_bytes()); + let result = Keccak256::digest(pk.noah_to_bytes()); H160::from_slice(&result.as_slice()[..20]) } diff --git a/src/components/contracts/modules/account/Cargo.toml b/src/components/contracts/modules/account/Cargo.toml index 5fedd1cad..c12d7b5aa 100644 --- a/src/components/contracts/modules/account/Cargo.toml +++ b/src/components/contracts/modules/account/Cargo.toml @@ -15,7 +15,7 @@ primitive-types = { version = "0.11.1", default-features = false, features = ["r ruc = "1.0" serde = { version = "1.0.124", features = ["derive"] } serde_json = "1.0.64" -storage = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.5" } +storage = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.6" } # primitives, don't depend on any modules fp-core = { path = "../../primitives/core" } @@ -24,9 +24,9 @@ fp-traits = { path = "../../primitives/traits" } fp-types = { path = "../../primitives/types" } enterprise-web3 = { path = "../../primitives/enterprise-web3" } config = { path = "../../../config"} +zei = { package="platform-lib-noah", git = "https://github.com/FindoraNetwork/platform-lib-noah", branch = "develop" } [dev-dependencies] -rand_chacha = "0.2" +rand_chacha = "0.3" parking_lot = "0.12" -zei = { git = "https://github.com/FindoraNetwork/zei", branch = "stable-main" } -fin_db = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.5" } +fin_db = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.6" } diff --git a/src/components/contracts/modules/account/src/tests.rs b/src/components/contracts/modules/account/src/tests.rs index aa07f72f4..2ce7dbc7f 100644 --- a/src/components/contracts/modules/account/src/tests.rs +++ b/src/components/contracts/modules/account/src/tests.rs @@ -11,7 +11,7 @@ use rand_chacha::rand_core::SeedableRng; use rand_chacha::ChaChaRng; use std::{env::temp_dir, sync::Arc, time::SystemTime}; use storage::state::ChainState; -use zei::xfr::sig::XfrKeyPair; +use zei::XfrKeyPair; fn setup() -> Context { let time = SystemTime::now() diff --git a/src/components/contracts/modules/ethereum/Cargo.toml b/src/components/contracts/modules/ethereum/Cargo.toml index c40e523aa..ca374d979 100644 --- a/src/components/contracts/modules/ethereum/Cargo.toml +++ b/src/components/contracts/modules/ethereum/Cargo.toml @@ -19,7 +19,7 @@ rlp = "0.5" ruc = "1.0" serde = { version = "1.0.124", features = ["derive"] } serde_json = "1.0.64" -sha3 = "0.8" +sha3 = "0.10" lazy_static = "1.4.0" # primitives, don't depend on any modules @@ -37,8 +37,8 @@ enterprise-web3 = { path = "../../primitives/enterprise-web3" } baseapp = { path = "../../baseapp" } fp-mocks = { path = "../../primitives/mocks" } module-account = { path = "../account" } -storage = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.5" } -fin_db = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.5" } +storage = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.6" } +fin_db = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.6" } [features] default = [] diff --git a/src/components/contracts/modules/ethereum/src/impls.rs b/src/components/contracts/modules/ethereum/src/impls.rs index f28cde195..7c6d7567d 100644 --- a/src/components/contracts/modules/ethereum/src/impls.rs +++ b/src/components/contracts/modules/ethereum/src/impls.rs @@ -73,7 +73,7 @@ impl App { let pubkey = secp256k1_ecdsa_recover(&sig, &msg).ok()?; Some(H160::from(H256::from_slice( - Keccak256::digest(&pubkey).as_slice(), + Keccak256::digest(pubkey).as_slice(), ))) } diff --git a/src/components/contracts/modules/evm/Cargo.toml b/src/components/contracts/modules/evm/Cargo.toml index 4f57c3c6e..b865a7885 100644 --- a/src/components/contracts/modules/evm/Cargo.toml +++ b/src/components/contracts/modules/evm/Cargo.toml @@ -22,9 +22,9 @@ ruc = "1.0" serde = { version = "1.0.124", features = ["derive"] } serde_json = "1.0.64" sha3 = { version = "0.10", default-features = false } -hex = "0.4.2" +hex = "0.4.3" ethabi = "17.1.0" -zei = { git = "https://github.com/FindoraNetwork/zei", branch = "stable-main" } +zei = { package="platform-lib-noah", git = "https://github.com/FindoraNetwork/platform-lib-noah", branch = "develop" } protobuf = "2.16" # primitives, don't depend on any modules @@ -35,8 +35,8 @@ fp-traits = { path = "../../primitives/traits" } fp-types = { path = "../../primitives/types" } fp-utils = { path = "../../primitives/utils" } config = { path = "../../../config"} -storage = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.5" } -fin_db = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.5" } +storage = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.6" } +fin_db = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.6" } ledger = { path = "../../../../ledger" } enterprise-web3 = { path = "../../primitives/enterprise-web3" } module-ethereum = { path = "../ethereum" } @@ -44,7 +44,7 @@ module-ethereum = { path = "../ethereum" } [dev-dependencies] baseapp = { path = "../../baseapp" } fp-mocks = { path = "../../primitives/mocks" } -hex = "0.4.2" +hex = "0.4.3" module-account = { path = "../account" } serde_json = "1.0.64" diff --git a/src/components/contracts/modules/evm/precompile/Cargo.toml b/src/components/contracts/modules/evm/precompile/Cargo.toml index 875e9a0cf..943af994c 100644 --- a/src/components/contracts/modules/evm/precompile/Cargo.toml +++ b/src/components/contracts/modules/evm/precompile/Cargo.toml @@ -14,10 +14,10 @@ ethereum-types = "0.13.1" evm-precompile-basic = {path = "./basic"} evm-precompile-frc20 = {path = "./frc20"} evm-precompile-modexp = {path = "./modexp"} -evm-precompile-sha3fips = {path = "./sha3fips"} evm-precompile-anemoi = {path = "./anemoi"} evm-precompile-blake2 = {path = "./blake2"} evm-precompile-bn128 = {path = "./bn128"} fp-core = {path = "../../../primitives/core"} module-evm = {path = "../../../modules/evm"} parking_lot = "0.12" +evm-precompile-eth-pairings = { path = "./eth-pairings" } diff --git a/src/components/contracts/modules/evm/precompile/anemoi/Cargo.toml b/src/components/contracts/modules/evm/precompile/anemoi/Cargo.toml index 05897e824..c8e970ed0 100644 --- a/src/components/contracts/modules/evm/precompile/anemoi/Cargo.toml +++ b/src/components/contracts/modules/evm/precompile/anemoi/Cargo.toml @@ -16,8 +16,7 @@ evm-precompile-utils = { path = "../utils"} tracing = "0.1" module-evm = { path = "../../../../modules/evm"} num_enum = { version = "0.5.4", default-features = false } -noah-algebra = { git = "https://github.com/FindoraNetwork/noah", tag = "v0.4.3-1" } -noah-crypto = { git = "https://github.com/FindoraNetwork/noah", tag = "v0.4.3-1" } +platform-lib-noah = { git = "https://github.com/FindoraNetwork/platform-lib-noah", branch = "develop" } [dev-dependencies] baseapp = { path = "../../../../baseapp" } diff --git a/src/components/contracts/modules/evm/precompile/anemoi/benches/anemoi.rs b/src/components/contracts/modules/evm/precompile/anemoi/benches/anemoi.rs index a03a28479..98fdd54e2 100644 --- a/src/components/contracts/modules/evm/precompile/anemoi/benches/anemoi.rs +++ b/src/components/contracts/modules/evm/precompile/anemoi/benches/anemoi.rs @@ -1,4 +1,4 @@ -use evm_precompile_anemoi::Anemoi; +use evm_precompile_anemoi::Anemoi381; use noah_algebra::bls12_381::BLSScalar; use noah_algebra::prelude::Scalar; use std::time::Instant; @@ -19,10 +19,10 @@ fn main() { println!("Benchmarking 2 field elements for 1000 times"); let start = Instant::now(); for _ in 0..1000 { - _ = Anemoi::execute_with_input_and_gas(&data[0..64], None); + _ = Anemoi381::execute_with_input_and_gas(&data[0..64], None); } let time = start.elapsed().as_nanos() / 1000; - let gas = Anemoi::GAS_PER_PERM; + let gas = Anemoi381::GAS_PER_PERM; println!("Result = {} ns every time", time); println!("Cost = {} every time", gas); println!("NS per gas = {}", (time as f64) / (gas as f64)); @@ -30,10 +30,10 @@ fn main() { println!("Benchmarking 32 field elements for 1000 times"); let start = Instant::now(); for _ in 0..1000 { - _ = Anemoi::execute_with_input_and_gas(&data[0..1024], None); + _ = Anemoi381::execute_with_input_and_gas(&data[0..1024], None); } let time = start.elapsed().as_nanos() / 1000; - let gas = Anemoi::GAS_PER_PERM * 11; + let gas = Anemoi381::GAS_PER_PERM * 11; println!("Result = {} ns every time", time); println!("Cost = {} every time", gas); println!("NS per gas = {}", (time as f64) / (gas as f64)); diff --git a/src/components/contracts/modules/evm/precompile/anemoi/src/lib.rs b/src/components/contracts/modules/evm/precompile/anemoi/src/lib.rs index 49e824828..5204468aa 100644 --- a/src/components/contracts/modules/evm/precompile/anemoi/src/lib.rs +++ b/src/components/contracts/modules/evm/precompile/anemoi/src/lib.rs @@ -1,16 +1,21 @@ use evm::executor::stack::{PrecompileFailure, PrecompileOutput}; use evm::{Context, ExitError, ExitSucceed}; use module_evm::precompile::{FinState, Precompile, PrecompileId, PrecompileResult}; -use noah_algebra::bls12_381::BLSScalar; -use noah_algebra::prelude::Scalar; -use noah_crypto::basic::anemoi_jive::{AnemoiJive, AnemoiJive381}; +use platform_lib_noah::noah_algebra::{ + bls12_381::BLSScalar, bn254::BN254Scalar, prelude::Scalar, +}; +#[allow(deprecated)] +use platform_lib_noah::noah_crypto::anemoi_jive::{ + bls12_381_deprecated::AnemoiJive381Deprecated, AnemoiJive, AnemoiJive254, +}; -/// The Anemoi precompile -pub struct Anemoi; +/// The Anemoi precompile for BLS12-381 +pub struct Anemoi381; -impl Anemoi { +impl Anemoi381 { pub const GAS_PER_PERM: u64 = 125000; + #[allow(deprecated)] pub fn execute_with_input_and_gas( input: &[u8], target_gas: Option, @@ -55,7 +60,8 @@ impl Anemoi { } } - let mut res = AnemoiJive381::eval_variable_length_hash(&field_elems).to_bytes(); + let mut res = + AnemoiJive381Deprecated::eval_variable_length_hash(&field_elems).to_bytes(); res.reverse(); Ok(PrecompileOutput { @@ -67,13 +73,92 @@ impl Anemoi { } } -impl PrecompileId for Anemoi { +impl PrecompileId for Anemoi381 { fn contract_id() -> u64 { 0x2002 } } -impl Precompile for Anemoi { +impl Precompile for Anemoi381 { + fn execute( + input: &[u8], + target_gas: Option, + _context: &Context, + _state: &FinState, + ) -> PrecompileResult { + Self::execute_with_input_and_gas(input, target_gas) + } +} + +/// The Anemoi precompile for BN254 +pub struct Anemoi254; + +impl Anemoi254 { + pub const GAS_PER_PERM: u64 = 125000; + + pub fn execute_with_input_and_gas( + input: &[u8], + target_gas: Option, + ) -> PrecompileResult { + if input.len() % 32 != 0 { + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other( + "input must be multiplies of 32 bytes".into(), + ), + }); + } + + let num_elems = input.len() / 32; + + let gas_cost = ((num_elems + 2) / 3) as u64 * Self::GAS_PER_PERM; + if let Some(gas_left) = target_gas { + if gas_left < gas_cost { + return Err(PrecompileFailure::Error { + exit_status: ExitError::OutOfGas, + }); + } + }; + + let mut field_elems = Vec::with_capacity(num_elems); + + for i in 0..num_elems { + let res = BN254Scalar::from_bytes( + &input[i * 32..(i + 1) * 32] + .iter() + .rev() + .copied() + .collect::>(), + ); + if let Ok(res) = res { + field_elems.push(res); + } else { + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other( + "Cannot convert bytes to field elements".into(), + ), + }); + } + } + + let mut res = AnemoiJive254::eval_variable_length_hash(&field_elems).to_bytes(); + res.reverse(); + + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + cost: gas_cost, + output: res.to_vec(), + logs: Default::default(), + }) + } +} + +impl PrecompileId for Anemoi254 { + fn contract_id() -> u64 { + 0x2003 + } +} + +impl Precompile for Anemoi254 { fn execute( input: &[u8], target_gas: Option, diff --git a/src/components/contracts/modules/evm/precompile/basic/Cargo.toml b/src/components/contracts/modules/evm/precompile/basic/Cargo.toml index 5f60c3d6e..5aa4d2c9d 100644 --- a/src/components/contracts/modules/evm/precompile/basic/Cargo.toml +++ b/src/components/contracts/modules/evm/precompile/basic/Cargo.toml @@ -16,10 +16,7 @@ harness = false [dependencies] evm = { version = "0.35.0", default-features = false, features = ["with-serde"] } module-evm = { path = "../../../../modules/evm"} -ripemd160 = "0.9" +ripemd = "0.1" -# primitives -#fp-core = { path = "../../../../primitives/core" } -#fp-evm = { path = "../../../../primitives/evm" } fp-types = { path = "../../../../primitives/types" } fp-utils = { path = "../../../../primitives/utils" } \ No newline at end of file diff --git a/src/components/contracts/modules/evm/precompile/basic/src/lib.rs b/src/components/contracts/modules/evm/precompile/basic/src/lib.rs index 4a8ebab8d..8dce50720 100644 --- a/src/components/contracts/modules/evm/precompile/basic/src/lib.rs +++ b/src/components/contracts/modules/evm/precompile/basic/src/lib.rs @@ -16,7 +16,7 @@ // limitations under the License. use core::cmp::min; -use evm::{executor::stack::PrecompileFailure, ExitError, ExitSucceed}; +use evm::{executor::stack::PrecompileFailure, ExitSucceed}; use module_evm::precompile::{LinearCostPrecompile, PrecompileId}; /// The identity precompile. @@ -98,10 +98,10 @@ impl LinearCostPrecompile for Ripemd160 { input: &[u8], _cost: u64, ) -> core::result::Result<(ExitSucceed, Vec), PrecompileFailure> { - use ripemd160::Digest; + use ripemd::Digest; let mut ret = [0u8; 32]; - ret[12..32].copy_from_slice(&ripemd160::Ripemd160::digest(input)); + ret[12..32].copy_from_slice(&ripemd::Ripemd160::digest(input)); Ok((ExitSucceed::Returned, ret.to_vec())) } } @@ -127,43 +127,3 @@ impl LinearCostPrecompile for Sha256 { Ok((ExitSucceed::Returned, ret.to_vec())) } } - -/// The ECRecoverPublicKey precompile. -/// Similar to ECRecover, but returns the pubkey (not the corresponding Ethereum address) -pub struct ECRecoverPublicKey; - -impl PrecompileId for ECRecoverPublicKey { - fn contract_id() -> u64 { - 0x6 - } -} - -impl LinearCostPrecompile for ECRecoverPublicKey { - const BASE: u64 = 3000; - const WORD: u64 = 0; - - fn execute( - i: &[u8], - _: u64, - ) -> core::result::Result<(ExitSucceed, Vec), PrecompileFailure> { - let mut input = [0u8; 128]; - input[..min(i.len(), 128)].copy_from_slice(&i[..min(i.len(), 128)]); - - let mut msg = [0u8; 32]; - let mut sig = [0u8; 65]; - - msg[0..32].copy_from_slice(&input[0..32]); - sig[0..32].copy_from_slice(&input[64..96]); - sig[32..64].copy_from_slice(&input[96..128]); - sig[64] = input[63]; - - let pubkey = - fp_types::crypto::secp256k1_ecdsa_recover(&sig, &msg).map_err(|_| { - PrecompileFailure::Error { - exit_status: ExitError::Other("Public key recover failed".into()), - } - })?; - - Ok((ExitSucceed::Returned, pubkey.to_vec())) - } -} diff --git a/src/components/contracts/modules/evm/precompile/eth-pairings/src/lib.rs b/src/components/contracts/modules/evm/precompile/eth-pairings/src/lib.rs index 66f28d8d3..470fec3c3 100644 --- a/src/components/contracts/modules/evm/precompile/eth-pairings/src/lib.rs +++ b/src/components/contracts/modules/evm/precompile/eth-pairings/src/lib.rs @@ -3,11 +3,7 @@ mod tests; use eth_pairings::public_interface::{perform_operation, ApiError, OperationType}; use evm::executor::stack::{PrecompileFailure, PrecompileOutput}; -use evm::{ - // Context, - ExitError, - ExitSucceed, -}; +use evm::{Context, ExitError, ExitSucceed}; use evm_precompile_utils::{EvmDataReader, EvmDataWriter, EvmResult, Gasometer}; use module_evm::precompile::{FinState, Precompile, PrecompileId, PrecompileResult}; use tracing::debug; @@ -29,12 +25,11 @@ pub enum Call { impl Precompile for EthPairing { fn execute( - handle: &mut impl PrecompileHandle, + input: &[u8], + target_gas: Option, + _context: &Context, _state: &FinState, ) -> PrecompileResult { - let input = handle.input(); - let target_gas = handle.gas_limit(); - let mut input = EvmDataReader::new(input); let selector = match input.read_selector::() { Ok(v) => v, @@ -91,11 +86,11 @@ impl EthPairing { Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, - // cost: gasometer.used_gas(), + cost: gasometer.used_gas(), output: EvmDataWriter::new() .write_raw_bytes(result.as_slice()) .build(), - // logs: vec![], + logs: vec![], }) } } diff --git a/src/components/contracts/modules/evm/precompile/eth-pairings/src/tests.rs b/src/components/contracts/modules/evm/precompile/eth-pairings/src/tests.rs new file mode 100644 index 000000000..2ff06a41d --- /dev/null +++ b/src/components/contracts/modules/evm/precompile/eth-pairings/src/tests.rs @@ -0,0 +1,29 @@ +use crate::*; +use ethereum_types::H160; +use fp_mocks::*; + +// Test from eth-pairings (eip1962) repository https://github.com/FindoraNetwork/eip1962 +#[test] +fn test_bls12_pairing() { + use hex; + let hex_string = "07202912811758d871b77a9c3635c28570dc020576f9fc2719d8d0494439162b2b89000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011603e65409b693c8a08aeb3478d10aa3732a6672ba06d12912811758d871b77a9c3635c28570dc020576f9fc2719d8d0494439162b2b84000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010206059efde42f3701020127ccb2831f0011c1d547c491b9dffbd4cfdb87598a4b370f5873ea62094a2f201faa6cb7f6fcca66de3f308a25776dac3f61edb792948fbe53e4d18a3d8aefbe011a7e9f75f3fdc77b83a97f7acd58326a0545f8aa37b69bfb32c52dc195763e8c17176e0ad4ee94d9c720e922d42688127c4b812cd7c2f8cf6126acd4c3d7568121e48b3fefe66c279f2ec71f0d6f8156a3343d1cfa54b808d747cd02419278290ad2d7d03f5de1e7b3c97732f53dbe1dfd42e51f9571f7fee3d9c1785d5a1ed6010b4f7f211a0a5f4425728e2df580196d3e3b85ef148ed769acd23e9be6e8440726cb40655787f48eaf46154cb740e2a58db5b96fa02d83fb9d0f94320da1471e0104ece4c46ac4f05a7c28ecda84292f15999747bb77c530c65448f1f837a47dd70e972c4065d0b39d40b5d550a55901516afa7f02b395963d1535fcba1705e31a117cb4beab1dc582198c4ab0c02e96a22f7bd10dde3bbbdbc9182a9596cb0ed32121616b692e8036437efb4c3816f018f11e643c6e0a049da431986a3a722b06"; + let data = hex::decode(hex_string).unwrap(); + + let output = EthPairing::execute( + &EvmDataWriter::new() + .write_selector(Call::ExecuteOperation) + .write_raw_bytes(&data) + .build(), + None, + &evm::Context { + address: H160::from_low_u64_be(2001), + caller: ALICE_ECDSA.address, + apparent_value: From::from(0), + }, + &BASE_APP.lock().unwrap().deliver_state, + ); + + assert!(output.is_ok()); + assert_eq!(output.as_ref().unwrap().cost, 164986); + assert_eq!(output.unwrap().output, vec![0x1]); +} diff --git a/src/components/contracts/modules/evm/precompile/frc20/Cargo.toml b/src/components/contracts/modules/evm/precompile/frc20/Cargo.toml index e5845d173..b4d318269 100644 --- a/src/components/contracts/modules/evm/precompile/frc20/Cargo.toml +++ b/src/components/contracts/modules/evm/precompile/frc20/Cargo.toml @@ -22,4 +22,4 @@ config = { path = "../../../../../config" } [dev-dependencies] baseapp = { path = "../../../../baseapp" } fp-mocks = { path = "../../../../primitives/mocks" } -sha3 = "0.8" +sha3 = "0.10" diff --git a/src/components/contracts/modules/evm/precompile/modexp/Cargo.toml b/src/components/contracts/modules/evm/precompile/modexp/Cargo.toml index 1e046763a..27630ce3b 100644 --- a/src/components/contracts/modules/evm/precompile/modexp/Cargo.toml +++ b/src/components/contracts/modules/evm/precompile/modexp/Cargo.toml @@ -15,5 +15,5 @@ num = { version = "0.4", features = ["alloc"] } ethereum-types = { version = "0.13.1", default-features = false } [dev-dependencies] -hex = "0.4.2" +hex = "0.4.3" fp-mocks = { path = "../../../../primitives/mocks" } diff --git a/src/components/contracts/modules/evm/precompile/sha3fips/Cargo.toml b/src/components/contracts/modules/evm/precompile/sha3fips/Cargo.toml deleted file mode 100644 index 01c867e60..000000000 --- a/src/components/contracts/modules/evm/precompile/sha3fips/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "evm-precompile-sha3fips" -version = "0.1.0" -authors = ["FindoraNetwork"] -edition = "2021" -homepage = "https://findora.org/technology" -repository = "https://github.com/findoranetwork/platform/" -description = "SHA3 FIPS202 precompiles for EVM module." -readme = "README.md" - -[dependencies] -evm = { version = "0.35.0", default-features = false, features = ["with-serde"] } -module-evm = { path = "../../../../modules/evm"} -tiny-keccak = { version = "2.0", features = ["fips202"] } diff --git a/src/components/contracts/modules/evm/precompile/sha3fips/src/lib.rs b/src/components/contracts/modules/evm/precompile/sha3fips/src/lib.rs deleted file mode 100644 index e49880c72..000000000 --- a/src/components/contracts/modules/evm/precompile/sha3fips/src/lib.rs +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// This file is part of Frontier. -// -// Copyright (c) 2020 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use evm::executor::stack::PrecompileFailure; -use evm::ExitSucceed; -use module_evm::precompile::{LinearCostPrecompile, PrecompileId}; -use tiny_keccak::Hasher; - -pub struct Sha3FIPS256; - -impl PrecompileId for Sha3FIPS256 { - fn contract_id() -> u64 { - 0x7 - } -} - -impl LinearCostPrecompile for Sha3FIPS256 { - const BASE: u64 = 60; - const WORD: u64 = 12; - - fn execute( - input: &[u8], - _: u64, - ) -> core::result::Result<(ExitSucceed, Vec), PrecompileFailure> { - let mut output = [0; 32]; - let mut sha3 = tiny_keccak::Sha3::v256(); - sha3.update(input); - sha3.finalize(&mut output); - Ok((ExitSucceed::Returned, output.to_vec())) - } -} - -pub struct Sha3FIPS512; - -impl PrecompileId for Sha3FIPS512 { - fn contract_id() -> u64 { - 0x8 - } -} - -impl LinearCostPrecompile for Sha3FIPS512 { - const BASE: u64 = 60; - const WORD: u64 = 12; - - fn execute( - input: &[u8], - _: u64, - ) -> core::result::Result<(ExitSucceed, Vec), PrecompileFailure> { - let mut output = [0; 64]; - let mut sha3 = tiny_keccak::Sha3::v512(); - sha3.update(input); - sha3.finalize(&mut output); - Ok((ExitSucceed::Returned, output.to_vec())) - } -} diff --git a/src/components/contracts/modules/evm/precompile/src/lib.rs b/src/components/contracts/modules/evm/precompile/src/lib.rs index 994ec14ae..0c4b3afa9 100644 --- a/src/components/contracts/modules/evm/precompile/src/lib.rs +++ b/src/components/contracts/modules/evm/precompile/src/lib.rs @@ -3,7 +3,7 @@ use evm::{executor::stack::PrecompileSet, Context}; use module_evm::precompile::{Precompile, PrecompileResult}; use std::marker::PhantomData; -use evm_precompile_anemoi::Anemoi; +use evm_precompile_anemoi::{Anemoi254, Anemoi381}; use evm_precompile_basic::{ECRecover, Identity, Ripemd160, Sha256}; use evm_precompile_blake2::Blake2F; use evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing}; @@ -23,7 +23,7 @@ where Self(Default::default(), ctx) } pub fn used_addresses() -> std::vec::Vec { - std::vec![1, 2, 3, 4, 5, 1024, 1025] + std::vec![0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x1000, 0x2001, 0x2002] .into_iter() .map(hash) .collect() @@ -45,7 +45,7 @@ where let ctx = &self.1; match address { - // Ethereum precompiles : + // Ethereum precompiles: a if a == H160::from_low_u64_be(ECRecover::contract_id()) => { Some(ECRecover::execute(input, target_gas, context, ctx)) } @@ -76,8 +76,11 @@ where a if a == H160::from_low_u64_be(FRC20::::contract_id()) => { Some(FRC20::::execute(input, target_gas, context, ctx)) } - a if a == H160::from_low_u64_be(Anemoi::contract_id()) => { - Some(Anemoi::execute(input, target_gas, context, ctx)) + a if a == H160::from_low_u64_be(Anemoi381::contract_id()) => { + Some(Anemoi381::execute(input, target_gas, context, ctx)) + } + a if a == H160::from_low_u64_be(Anemoi254::contract_id()) => { + Some(Anemoi254::execute(input, target_gas, context, ctx)) } //a if a == H160::from_low_u64_be(EthPairing::contract_id()) => { // Some(EthPairing::execute(input, target_gas, context, ctx)) diff --git a/src/components/contracts/modules/evm/precompile/utils/Cargo.toml b/src/components/contracts/modules/evm/precompile/utils/Cargo.toml index d525e8599..5a2091980 100644 --- a/src/components/contracts/modules/evm/precompile/utils/Cargo.toml +++ b/src/components/contracts/modules/evm/precompile/utils/Cargo.toml @@ -12,4 +12,4 @@ evm = { version = "0.35.0", default-features = false, features = ["with-serde"] tracing = "0.1" num_enum = { version = "0.5.3", default-features = false } precompile-utils-macro = { path = "macro" } -sha3 = { version = "0.8", default-features = false } +sha3 = { version = "0.10", default-features = false } diff --git a/src/components/contracts/modules/evm/precompile/utils/macro/Cargo.toml b/src/components/contracts/modules/evm/precompile/utils/macro/Cargo.toml index e3eb436e3..f0e736630 100644 --- a/src/components/contracts/modules/evm/precompile/utils/macro/Cargo.toml +++ b/src/components/contracts/modules/evm/precompile/utils/macro/Cargo.toml @@ -15,5 +15,5 @@ path = "tests/tests.rs" [dependencies] quote = "1.0" proc-macro2 = "1.0" -sha3 = "0.8" +sha3 = "0.10" syn = { version = "1.0", features = ["full", "fold", "extra-traits", "visit"] } diff --git a/src/components/contracts/modules/evm/precompile/utils/macro/src/lib.rs b/src/components/contracts/modules/evm/precompile/utils/macro/src/lib.rs index d1aad50a5..cb5263844 100644 --- a/src/components/contracts/modules/evm/precompile/utils/macro/src/lib.rs +++ b/src/components/contracts/modules/evm/precompile/utils/macro/src/lib.rs @@ -64,9 +64,7 @@ pub fn generate_function_selector(_: TokenStream, input: TokenStream) -> TokenSt Some((_, Expr::Lit(ExprLit { lit, .. }))) => { if let Lit::Str(lit_str) = lit { let selector = u32::from_be_bytes( - Keccak256::digest(lit_str.value().as_ref())[..4] - .try_into() - .unwrap(), + Keccak256::digest(lit_str.value())[..4].try_into().unwrap(), ); ident_expressions.push(variant.ident); variant_expressions.push(Expr::Lit(ExprLit { diff --git a/src/components/contracts/modules/evm/precompile/utils/src/data.rs b/src/components/contracts/modules/evm/precompile/utils/src/data.rs index 99684f5f6..0958189bc 100644 --- a/src/components/contracts/modules/evm/precompile/utils/src/data.rs +++ b/src/components/contracts/modules/evm/precompile/utils/src/data.rs @@ -115,6 +115,11 @@ impl<'a> EvmDataReader<'a> { Ok(start..end) } + + /// Get slice from cursor to the end of buffer + pub fn get_slice(&mut self) -> &[u8] { + &self.input[self.cursor..] + } } /// Help build an EVM input/output data. diff --git a/src/components/contracts/modules/evm/src/lib.rs b/src/components/contracts/modules/evm/src/lib.rs index 60d6400d2..20ddb498b 100644 --- a/src/components/contracts/modules/evm/src/lib.rs +++ b/src/components/contracts/modules/evm/src/lib.rs @@ -16,8 +16,7 @@ use ethabi::Token; use ethereum::{ Log, ReceiptV0 as Receipt, TransactionAction, TransactionSignature, TransactionV0, }; -use ethereum_types::U256; -use ethereum_types::{Bloom, BloomInput, H160, H256}; +use ethereum_types::{Bloom, BloomInput, H160, H256, U256}; use evm::executor::stack::PrecompileSet as EvmPrecompileSet; use fp_core::{ context::Context, @@ -33,10 +32,10 @@ use fp_traits::{ account::AccountAsset, evm::{AddressMapping, BlockHashMapping, DecimalsMapping, FeeCalculator}, }; -use fp_types::crypto::HA256; + use fp_types::{ actions::evm::Action, - crypto::{Address, HA160}, + crypto::{Address, HA160, HA256}, }; use ledger::staking::evm::EVM_STAKING_MINTS; use ledger::staking::FRA_PRE_ISSUE_AMOUNT; @@ -50,7 +49,7 @@ use std::marker::PhantomData; use std::str::FromStr; use system_contracts::{SystemContracts, SYSTEM_ADDR}; use utils::parse_evm_staking_coinbase_mint_event; -use zei::xfr::sig::XfrPublicKey; +use zei::{noah_algebra::serialization::NoahFromToBytes, XfrPublicKey}; use crate::utils::parse_evm_staking_mint_event; @@ -149,7 +148,7 @@ impl App { let asset = Token::FixedBytes(Vec::from(_asset)); - let from = Token::Bytes(from.as_bytes().to_vec()); + let from = Token::Bytes(from.noah_to_bytes()); let to = Token::Address(*to); diff --git a/src/components/contracts/modules/evm/src/precompile.rs b/src/components/contracts/modules/evm/src/precompile.rs index ac28fe117..5eb302871 100644 --- a/src/components/contracts/modules/evm/src/precompile.rs +++ b/src/components/contracts/modules/evm/src/precompile.rs @@ -2,10 +2,7 @@ use crate::runtime::stack::FindoraStackState; use ethereum_types::H160; use evm::{ executor::stack::{PrecompileFailure, PrecompileOutput}, - Context, - // Context, - ExitError, - ExitSucceed, + Context, ExitError, ExitSucceed, }; use impl_trait_for_tuples::impl_for_tuples; diff --git a/src/components/contracts/modules/evm/src/utils.rs b/src/components/contracts/modules/evm/src/utils.rs index a10e5a9c8..8ff1898d3 100644 --- a/src/components/contracts/modules/evm/src/utils.rs +++ b/src/components/contracts/modules/evm/src/utils.rs @@ -7,11 +7,9 @@ use fp_types::actions::xhub::NonConfidentialOutput; use ledger::data_model::ASSET_TYPE_FRA; use ruc::*; use zei::{ - serialization::ZeiFromToBytes, - xfr::{ - sig::XfrPublicKey, - structs::{AssetType, ASSET_TYPE_LENGTH}, - }, + noah_algebra::serialization::NoahFromToBytes, + noah_api::xfr::structs::{AssetType, ASSET_TYPE_LENGTH}, + XfrPublicKey, }; pub fn deposit_asset_event() -> Event { @@ -72,7 +70,7 @@ pub fn parse_deposit_asset_event(data: Vec) -> Result .clone() .into_bytes() .unwrap_or_default(); - let target = XfrPublicKey::zei_from_bytes(receiver.as_slice()).c(d!())?; + let target = XfrPublicKey::noah_from_bytes(receiver.as_slice()).c(d!())?; let amount = result.params[2].value.clone().into_uint().c(d!())?; @@ -235,7 +233,8 @@ pub fn parse_evm_staking_mint_event( let result = event.parse_log(log).map_err(|e| eg!(e))?; let public_key_bytes = result.params[0].value.clone().into_bytes().c(d!())?; - let public_key = XfrPublicKey::zei_from_bytes(public_key_bytes.as_slice())?; + let public_key = + XfrPublicKey::noah_from_bytes(public_key_bytes.as_slice()).c(d!())?; let amount = result.params[1].value.clone().into_uint().c(d!())?.as_u64(); @@ -292,7 +291,8 @@ pub fn parse_evm_staking_coinbase_mint_event( if public_key_bytes.is_empty() { return Ok((delegator, None, amount)); } - let public_key = XfrPublicKey::zei_from_bytes(public_key_bytes.as_slice())?; + let public_key = + XfrPublicKey::noah_from_bytes(public_key_bytes.as_slice()).c(d!())?; Ok((delegator, Some(public_key), amount)) } diff --git a/src/components/contracts/primitives/core/Cargo.toml b/src/components/contracts/primitives/core/Cargo.toml index e265036a7..ce27fb03e 100644 --- a/src/components/contracts/primitives/core/Cargo.toml +++ b/src/components/contracts/primitives/core/Cargo.toml @@ -16,9 +16,9 @@ parking_lot = "0.12" primitive-types = { version = "0.11.1", default-features = false, features = ["rlp", "byteorder", "serde"] } ruc = "1.0" serde = { version = "1.0.124", features = ["derive"] } -storage = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.5", optional = true } -fin_db = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.5", optional = true } -serde_with = { version = "1.9.4"} +storage = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.6", optional = true } +fin_db = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.6", optional = true } +serde_with = { version = "2.0.1"} # primitives fp-types = { path = "../types" } diff --git a/src/components/contracts/primitives/core/src/module.rs b/src/components/contracts/primitives/core/src/module.rs index 7a7e843a4..cf0c284c7 100644 --- a/src/components/contracts/primitives/core/src/module.rs +++ b/src/components/contracts/primitives/core/src/module.rs @@ -1,6 +1,6 @@ use crate::context::Context; use abci::*; -use primitive_types::U256; +use fp_types::U256; use ruc::Result; /// AppModuleBasic is the standard form for basic non-dependant elements of an application module. @@ -45,4 +45,13 @@ pub trait AppModule: AppModuleBasic { ) -> (ResponseEndBlock, U256) { Default::default() } + + fn commit( + &mut self, + _ctx: &mut Context, + _height: U256, + _root_hash: &[u8], + ) -> Result<()> { + Ok(()) + } } diff --git a/src/components/contracts/primitives/mocks/Cargo.toml b/src/components/contracts/primitives/mocks/Cargo.toml index de327d5c9..681dc54b4 100644 --- a/src/components/contracts/primitives/mocks/Cargo.toml +++ b/src/components/contracts/primitives/mocks/Cargo.toml @@ -14,11 +14,11 @@ ethereum = { version = "0.12.0", default-features = false, features = ["with-ser lazy_static = "1.4.0" libsecp256k1 = { version = "0.7", features = ["static-context", "hmac"] } primitive-types = { version = "0.11.1", default-features = false, features = ["rlp", "byteorder", "serde"] } -rand_chacha = "0.2" +rand_chacha = "0.3" rlp = "0.5" serde_json = "1.0" -sha3 = "0.8" -zei = { git = "https://github.com/FindoraNetwork/zei", branch = "stable-main" } +sha3 = "0.10" +zei = { package="platform-lib-noah", git = "https://github.com/FindoraNetwork/platform-lib-noah", branch = "develop" } # primitives fp-traits = { path = "../traits" } diff --git a/src/components/contracts/primitives/mocks/src/lib.rs b/src/components/contracts/primitives/mocks/src/lib.rs index 37819ccf9..687ae1a52 100644 --- a/src/components/contracts/primitives/mocks/src/lib.rs +++ b/src/components/contracts/primitives/mocks/src/lib.rs @@ -20,7 +20,7 @@ use std::env::temp_dir; use std::path::PathBuf; use std::sync::Mutex; use std::time::SystemTime; -use zei::xfr::sig::XfrKeyPair; +use zei::XfrKeyPair; lazy_static! { pub static ref BASE_APP: Mutex = Mutex::new( @@ -68,7 +68,7 @@ pub fn build_signed_transaction( let signer: Address = who.get_pk().into(); let msg = serde_json::to_vec(&(function.clone(), extra.clone())).unwrap(); - let sig = who.get_sk_ref().sign(msg.as_slice(), who.get_pk_ref()); + let sig = who.get_sk_ref().sign(msg.as_slice()).unwrap(); let signature = MultiSignature::from(sig); UncheckedTransaction::new_signed(function, signer, signature, extra) diff --git a/src/components/contracts/primitives/storage/Cargo.toml b/src/components/contracts/primitives/storage/Cargo.toml index bab9bfc4c..6dca51852 100644 --- a/src/components/contracts/primitives/storage/Cargo.toml +++ b/src/components/contracts/primitives/storage/Cargo.toml @@ -14,12 +14,12 @@ paste = "1.0" ruc = "1.0" serde = { version = "1.0.124", features = ["derive"] } serde_json = "1.0" -sha2 = "0.9.5" -storage = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.5" } +sha2 = "0.10" +storage = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.6" } # primitives fp-core = { path = "../core" } config = { path = "../../../config"} [dev-dependencies] -temp_db = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.5" } +temp_db = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.6" } diff --git a/src/components/contracts/primitives/types/Cargo.toml b/src/components/contracts/primitives/types/Cargo.toml index 8b974d520..497e24d50 100644 --- a/src/components/contracts/primitives/types/Cargo.toml +++ b/src/components/contracts/primitives/types/Cargo.toml @@ -11,21 +11,22 @@ readme = "README.md" [dependencies] bech32 = "0.7.2" ethereum = { version = "0.12.0", default-features = false, features = ["with-serde"] } -hex = "0.4.2" +hex = "0.4.3" -globutils = { git = "https://github.com/FindoraNetwork/platform-lib-utils", tag = "v1.0.0" } +globutils = { git = "https://github.com/FindoraNetwork/platform-lib-utils", branch = "develop" } libsecp256k1 = { version = "0.7", features = ["static-context", "hmac"] } primitive-types = { version = "0.11.1", default-features = false, features = ["rlp", "byteorder", "serde"] } ruc = "1.0" serde = { version = "1.0.124", features = ["derive"] } serde_json = "1.0" -sha3 = "0.8" -zei = { git = "https://github.com/FindoraNetwork/zei", branch = "stable-main" } -fixed-hash = "0.7.0" +serde-big-array = "0.4" +sha3 = "0.10" +zei = { package="platform-lib-noah", git = "https://github.com/FindoraNetwork/platform-lib-noah", branch = "develop" } +fixed-hash = "0.8.0" # primitives fp-utils = { path = "../utils" } [dev-dependencies] -rand_chacha = "0.2" +rand_chacha = "0.3" diff --git a/src/components/contracts/primitives/types/src/actions/account.rs b/src/components/contracts/primitives/types/src/actions/account.rs index 701e091f9..4814f4b04 100644 --- a/src/components/contracts/primitives/types/src/actions/account.rs +++ b/src/components/contracts/primitives/types/src/actions/account.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; -use zei::xfr::sig::XfrPublicKey; -use zei::xfr::structs::AssetType; +use zei::{ + noah_api::xfr::structs::AssetType, XfrPublicKey +}; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum Action { diff --git a/src/components/contracts/primitives/types/src/actions/xhub.rs b/src/components/contracts/primitives/types/src/actions/xhub.rs index 622de4050..f74e53676 100644 --- a/src/components/contracts/primitives/types/src/actions/xhub.rs +++ b/src/components/contracts/primitives/types/src/actions/xhub.rs @@ -1,6 +1,5 @@ use serde::{Deserialize, Serialize}; -use zei::xfr::sig::XfrPublicKey; -use zei::xfr::structs::AssetType; +use zei::{noah_api::xfr::structs::AssetType, XfrPublicKey}; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum Action { diff --git a/src/components/contracts/primitives/types/src/crypto.rs b/src/components/contracts/primitives/types/src/crypto.rs index 7dcb34a4a..6345fc11e 100644 --- a/src/components/contracts/primitives/types/src/crypto.rs +++ b/src/components/contracts/primitives/types/src/crypto.rs @@ -1,19 +1,20 @@ -use bech32::{FromBase32, ToBase32}; -use core::convert::TryFrom; -use core::fmt::Formatter; -use core::str::FromStr; -use fp_utils::{ecdsa, hashing::keccak_256}; -use globutils::wallet; -use hex::FromHex; -use primitive_types::{H160, H256}; -use ruc::{d, eg, RucResult}; -use serde::{Deserialize, Serialize}; -use sha3::{Digest, Keccak256}; -use std::ops::{Deref, DerefMut}; -use zei::serialization::ZeiFromToBytes; -use zei::xfr::sig::{XfrPublicKey, XfrSignature}; - -/// An opaque 32-byte cryptographic identifier. +use { + bech32::{FromBase32, ToBase32}, + core::convert::TryFrom, + core::fmt::Formatter, + core::str::FromStr, + fp_utils::{ecdsa, hashing::keccak_256}, + globutils::wallet, + hex::FromHex, + primitive_types::{H160, H256}, + ruc::{d, eg, RucResult}, + serde::{Deserialize, Serialize}, + sha3::{Digest, Keccak256}, + std::ops::{Deref, DerefMut}, + zei::{noah_algebra::serialization::NoahFromToBytes, XfrPublicKey, XfrSignature}, +}; + +/// An opaque 34-byte cryptographic identifier. #[derive( Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash, Serialize, Deserialize, Debug, )] @@ -83,7 +84,7 @@ impl<'a> TryFrom<&'a [u8]> for Address32 { impl From for Address32 { fn from(k: XfrPublicKey) -> Self { - Address32::try_from(k.zei_to_bytes().as_slice()).unwrap() + Address32::try_from(k.noah_to_bytes().as_slice()).unwrap() } } @@ -205,7 +206,7 @@ pub trait Verify { /// Signature verify that can work with any known signature types.. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum MultiSignature { - /// An zei xfr signature. + /// An noah xfr signature. Xfr(XfrSignature), /// An ECDSA/SECP256k1 signature. Ecdsa(ecdsa::Signature), @@ -250,26 +251,20 @@ impl Verify for MultiSignature { fn verify(&self, msg: &[u8], signer: &Address32) -> bool { match self { - Self::Xfr(ref sig) => match XfrPublicKey::zei_from_bytes(signer.as_ref()) { - Ok(who) => sig.verify(msg, &who), - _ => false, - }, - // Self::Ecdsa(ref sig) => match sig.recover(msg) { - // Some(pubkey) => { - // &keccak_256(pubkey.as_ref()) - // == >::as_ref(signer) - // } - // _ => false, - // }, + Self::Xfr(ref sig) => { + let mut bytes = [0u8; 32]; + bytes[0..32].copy_from_slice(signer.as_ref()); + match XfrPublicKey::noah_from_bytes(&bytes) { + Ok(who) => sig.verify(msg, &who), + _ => false, + } + } Self::Ecdsa(ref sig) => { - // let mut msg_hashed = [0u8; 32]; - // msg_hashed.copy_from_slice(msg); - let msg_hashed = keccak_256(msg); match secp256k1_ecdsa_recover(sig.as_ref(), &msg_hashed) { Ok(pubkey) => { Address32::from(H160::from(H256::from_slice( - Keccak256::digest(&pubkey).as_slice(), + Keccak256::digest(pubkey).as_slice(), ))) == signer.clone() } _ => false, @@ -281,7 +276,7 @@ impl Verify for MultiSignature { #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum MultiSigner { - /// An zei xfr identity. + /// An noah xfr identity. Xfr(XfrPublicKey), // /// An SECP256k1/ECDSA identity (actually, the keccak 256 hash of the compressed pub key). // Ecdsa(ecdsa::Public), @@ -434,13 +429,13 @@ mod tests { use super::*; use rand_chacha::rand_core::SeedableRng; use rand_chacha::ChaChaRng; - use zei::xfr::sig::XfrKeyPair; + use zei::XfrKeyPair; #[test] fn xfr_sign_verify_work() { let mut prng = ChaChaRng::from_entropy(); let alice = XfrKeyPair::generate(&mut prng); - let sig = alice.get_sk_ref().sign(b"hello", alice.get_pk_ref()); + let sig = alice.get_sk_ref().sign(b"hello").unwrap(); let signer = MultiSigner::from(alice.get_pk()); let sig = MultiSignature::from(sig); assert!( diff --git a/src/components/contracts/primitives/utils/Cargo.toml b/src/components/contracts/primitives/utils/Cargo.toml index 936d60bd9..98e9c2403 100644 --- a/src/components/contracts/primitives/utils/Cargo.toml +++ b/src/components/contracts/primitives/utils/Cargo.toml @@ -10,19 +10,19 @@ readme = "README.md" [dependencies] futures = "0.3.16" -base64 = "0.12" +base64 = "0.13" bip0039 = "0.8.0" blake2-rfc = "0.2.18" byteorder = "1.4.3" -hex = "0.4.2" +hex = "0.4.3" libsecp256k1 = { version = "0.7", features = ["static-context", "hmac"] } primitive-types = { version = "0.11.1", default-features = false, features = ["rlp", "byteorder", "serde"] } protobuf = "2.16" rand = "0.8" ruc = "1.0" serde = { version = "1.0.124", features = ["derive"] } -sha2 = "0.9.5" -sha3 = "0.8" +sha2 = "0.10" +sha3 = "0.10" bip32 = "0.2.1" tiny-keccak = { version = "2.0", features = ["keccak"] } twox-hash = "1.6.0" diff --git a/src/components/contracts/primitives/utils/src/ecdsa.rs b/src/components/contracts/primitives/utils/src/ecdsa.rs index b01ed4483..0cb896f26 100644 --- a/src/components/contracts/primitives/utils/src/ecdsa.rs +++ b/src/components/contracts/primitives/utils/src/ecdsa.rs @@ -356,6 +356,7 @@ impl SecpPair { } /// Ethereum address format. + #[allow(clippy::needless_borrow)] pub fn address(&self) -> H160 { let mut res = [0u8; 64]; res.copy_from_slice(&self.public.serialize()[1..65]); diff --git a/src/components/contracts/primitives/wasm/Cargo.toml b/src/components/contracts/primitives/wasm/Cargo.toml index 3e91916d9..106d875df 100644 --- a/src/components/contracts/primitives/wasm/Cargo.toml +++ b/src/components/contracts/primitives/wasm/Cargo.toml @@ -20,7 +20,7 @@ fp-types = { path = "../../primitives/types" } fp-utils = { path = "../../primitives/utils" } rlp = "0.5" ruc = "1.0" -sha3 = "0.8" +sha3 = "0.10" serde_json = "1.0" wasm-bindgen = { version = "=0.2.84", features = ["serde-serialize"] } diff --git a/src/components/contracts/primitives/wasm/src/wasm.rs b/src/components/contracts/primitives/wasm/src/wasm.rs index 7db2c4315..41c51ed99 100644 --- a/src/components/contracts/primitives/wasm/src/wasm.rs +++ b/src/components/contracts/primitives/wasm/src/wasm.rs @@ -29,7 +29,7 @@ pub fn recover_signer(transaction: &Transaction) -> Option { let pubkey = secp256k1_ecdsa_recover(&sig, &msg).ok()?; Some(H160::from(H256::from_slice( - Keccak256::digest(&pubkey).as_slice(), + Keccak256::digest(pubkey).as_slice(), ))) } diff --git a/src/components/contracts/rpc/Cargo.toml b/src/components/contracts/rpc/Cargo.toml index 3194801d3..a7eef232f 100644 --- a/src/components/contracts/rpc/Cargo.toml +++ b/src/components/contracts/rpc/Cargo.toml @@ -26,7 +26,7 @@ jsonrpc-derive = { git = "https://github.com/FindoraNetwork/jsonrpc.git", packag jsonrpc-pubsub = { git = "https://github.com/FindoraNetwork/jsonrpc.git", package = "jsonrpc-pubsub" } jsonrpc-http-server = { git = "https://github.com/FindoraNetwork/jsonrpc.git", package = "jsonrpc-http-server" } jsonrpc-tcp-server = { git = "https://github.com/FindoraNetwork/jsonrpc.git", package = "jsonrpc-tcp-server" } -libsecp256k1 = { version = "0.5", features = ["static-context", "hmac"] } +libsecp256k1 = { version = "0.7", features = ["static-context", "hmac"] } lazy_static = "1.4.0" tracing = "0.1" rand = "0.8" @@ -38,9 +38,9 @@ rustc-hex = { version = "2.1.0" } rustc_version = "0.4.0" semver = "1.0.4" serde_json = "1.0" -sha3 = "0.8" -tendermint = { git = "https://github.com/FindoraNetwork/tendermint-rs", tag = "v0.19.0a-fk" } -tendermint-rpc = { git = "https://github.com/FindoraNetwork/tendermint-rs", features = ["http-client", "websocket-client"], tag = "v0.19.0a-fk" } +sha3 = "0.10" +tendermint = { git = "https://github.com/FindoraNetwork/tendermint-rs", tag = "v0.19.0c" } +tendermint-rpc = { git = "https://github.com/FindoraNetwork/tendermint-rs", features = ["http-client", "websocket-client"], tag = "v0.19.0c" } tokio = { version = "1.10.1", features = ["full"] } lru = "0.7" num_cpus = "1.13" diff --git a/src/components/contracts/rpc/src/eth.rs b/src/components/contracts/rpc/src/eth.rs index 7a4ffff85..75ce0a2a8 100644 --- a/src/components/contracts/rpc/src/eth.rs +++ b/src/components/contracts/rpc/src/eth.rs @@ -1437,7 +1437,7 @@ fn transaction_build( { match pubkey { Some(pk) => { - H160::from(H256::from_slice(Keccak256::digest(&pk).as_slice())) + H160::from(H256::from_slice(Keccak256::digest(pk).as_slice())) } _ => H160::default(), } @@ -1613,11 +1613,11 @@ fn dummy_block(height: u64, full: bool) -> Rich { let hash = if height == (CFG.checkpoint.evm_first_block_height as u64) - 1 { H256([0; 32]) } else { - H256::from_slice(&sha3::Keccak256::digest(&height.to_le_bytes())) + H256::from_slice(&sha3::Keccak256::digest(height.to_le_bytes())) }; let parent_hash = - H256::from_slice(&sha3::Keccak256::digest(&(height - 1).to_le_bytes())); + H256::from_slice(&sha3::Keccak256::digest((height - 1).to_le_bytes())); let transactions = if full { BlockTransactions::Full(vec![]) diff --git a/src/components/contracts/rpc/src/eth_pubsub.rs b/src/components/contracts/rpc/src/eth_pubsub.rs index 5f63d76fc..25b744a44 100644 --- a/src/components/contracts/rpc/src/eth_pubsub.rs +++ b/src/components/contracts/rpc/src/eth_pubsub.rs @@ -219,9 +219,8 @@ impl SubscriptionResult { receipts: Vec, params: &FilteredParams, ) -> Vec { - let block_hash = Some(H256::from_slice( - Keccak256::digest(&rlp::encode(&block.header)).as_slice(), - )); + let block_hash = + H256::from_slice(Keccak256::digest(&rlp::encode(&block.header)).as_slice()); let mut logs: Vec = vec![]; let mut log_index: u32 = 0; for (receipt_index, receipt) in receipts.into_iter().enumerate() { @@ -234,12 +233,12 @@ impl SubscriptionResult { None }; for (transaction_log_index, log) in receipt.logs.into_iter().enumerate() { - if self.add_log(block_hash.unwrap(), &log, &block, params) { + if self.add_log(block_hash, &log, &block, params) { logs.push(Log { address: log.address, topics: log.topics, data: Bytes(log.data), - block_hash, + block_hash: Some(block_hash), block_number: Some(block.header.number), transaction_hash, transaction_index: Some(U256::from(receipt_index)), diff --git a/src/components/contracts/rpc/src/web3.rs b/src/components/contracts/rpc/src/web3.rs index 40237395d..3cba4b8ff 100644 --- a/src/components/contracts/rpc/src/web3.rs +++ b/src/components/contracts/rpc/src/web3.rs @@ -31,7 +31,7 @@ impl Web3Api for Web3ApiImpl { fn sha3(&self, input: Bytes) -> Result { Ok(H256::from_slice( - Keccak256::digest(&input.into_vec()).as_slice(), + Keccak256::digest(input.into_vec()).as_slice(), )) } } diff --git a/src/components/finutils/Cargo.toml b/src/components/finutils/Cargo.toml index d76cef1cd..4c427d9cb 100644 --- a/src/components/finutils/Cargo.toml +++ b/src/components/finutils/Cargo.toml @@ -7,39 +7,40 @@ edition = "2021" [dependencies] -hex = "0.4.2" -base64 = "0.12" +hex = "0.4.3" +base64 = "0.13" clap = { version = "2.33.3", features = ["yaml"] } lazy_static = "1.4.0" attohttpc = { version = "0.23", default-features = false, features = ["compress", "json", "tls-rustls"] } serde_json = "1.0.40" serde = { version = "1.0.124", features = ["derive"] } rand = "0.8" -rand_core = { version = "0.5", default-features = false, features = ["alloc"] } -rand_chacha = "0.2" -curve25519-dalek = { version = "3.0", features = ["serde"] } +rand_core = { version = "0.6", default-features = false, features = ["alloc"] } +rand_chacha = "0.3" +curve25519-dalek = { package = "noah-curve25519-dalek", version = "4.0.0", default-features = false, features = ['serde'] } wasm-bindgen = { version = "=0.2.84", features = ["serde-serialize"] } sha2 = "0.10" +digest = '0.10' +parking_lot = "0.12" +getrandom = "0.2" -zei = { git = "https://github.com/FindoraNetwork/zei", branch = "stable-main" } +zei = { package="platform-lib-noah", git = "https://github.com/FindoraNetwork/platform-lib-noah", branch = "develop" } ruc = "1.0" rucv4 = { package = "ruc", version = "4.0" } nix = "0.25" -ledger = { path = "../../ledger" } - -globutils = { git = "https://github.com/FindoraNetwork/platform-lib-utils", tag = "v1.0.0" } -credentials = { git = "https://github.com/FindoraNetwork/platform-lib-credentials", tag = "v1.0.0" } +ledger = { path = "../../ledger", default-features = false } +globutils = { git = "https://github.com/FindoraNetwork/platform-lib-utils", branch = "develop" } +credentials = { git = "https://github.com/FindoraNetwork/platform-lib-credentials", branch = "develop" } eth_checksum = { version = "0.1.2", optional = true } fp-core = { path = "../contracts/primitives/core", default-features = false } fp-utils = { path = "../contracts/primitives/utils" } fp-types = { path = "../contracts/primitives/types" } -tendermint = { git = "https://github.com/FindoraNetwork/tendermint-rs", tag = "v0.19.0a-fk" } -tendermint-rpc = { git = "https://github.com/FindoraNetwork/tendermint-rs", features = ["http-client", "websocket-client"], optional = true, tag = "v0.19.0a-fk" } +tendermint = { git = "https://github.com/FindoraNetwork/tendermint-rs", tag = "v0.19.0c" } +tendermint-rpc = { git = "https://github.com/FindoraNetwork/tendermint-rs", features = ["http-client", "websocket-client"], optional = true, tag = "v0.19.0c" } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -# chaindev = { path = "../../../../chaindev" } chaindev = { git = "https://github.com/FindoraNetwork/chaindev", branch = "platform", default-features = false, features = ["tendermint_based", "vsdb_sled_engine"] } web3 = "0.19.0" tokio = "1.10.1" diff --git a/src/components/finutils/src/bins/cfg_generator.rs b/src/components/finutils/src/bins/cfg_generator.rs index d42aa7aaf..8f0f7d950 100644 --- a/src/components/finutils/src/bins/cfg_generator.rs +++ b/src/components/finutils/src/bins/cfg_generator.rs @@ -63,7 +63,7 @@ fn gen() -> Result<()> { cfg_template .valiators .iter_mut() - .zip(id_list.into_iter()) + .zip(id_list) .for_each(|(v, id)| { v.id = id; }); diff --git a/src/components/finutils/src/bins/fn.rs b/src/components/finutils/src/bins/fn.rs index 40889dc9f..83c45275f 100644 --- a/src/components/finutils/src/bins/fn.rs +++ b/src/components/finutils/src/bins/fn.rs @@ -31,15 +31,16 @@ use fp_types::H160; use { clap::{crate_authors, load_yaml, App}, - finutils::common::{self, evm::*}, + finutils::common::{self, evm::*, get_keypair, utils}, fp_utils::ecdsa::SecpPair, globutils::wallet, ledger::{ - data_model::{AssetTypeCode, FRA_DECIMALS}, + data_model::{AssetTypeCode, ASSET_TYPE_FRA, FRA_DECIMALS}, staking::{StakerMemo, VALIDATORS_MIN}, }, ruc::*, std::{fmt, fs}, + zei::{noah_api::anon_xfr::structs::OpenAnonAssetRecordBuilder, XfrSecretKey}, }; fn main() { @@ -59,11 +60,13 @@ fn run() -> Result<()> { if matches.is_present("version") { println!("{}", env!("VERGEN_SHA")); - } else if matches.is_present("genkey") { - common::gen_key_and_print(); + } else if let Some(m) = matches.subcommand_matches("genkey") { + let gen_eth_address = m.is_present("gen-eth-address"); + common::gen_key_and_print(gen_eth_address); } else if let Some(m) = matches.subcommand_matches("wallet") { if m.is_present("create") { - common::gen_key_and_print(); + let is_address_eth = m.is_present("gen-eth-address"); + common::gen_key_and_print(is_address_eth); } else if m.is_present("show") { let seckey = match m.value_of("seckey") { Some(path) => { @@ -71,6 +74,8 @@ fn run() -> Result<()> { } None => None, }; + let is_address_eth = m.is_present("use-default-eth-address"); + // FRA asset is the default case let asset = if let Some(code) = m.value_of("asset") { match code.to_lowercase().as_str() { @@ -80,7 +85,7 @@ fn run() -> Result<()> { } else { None }; - common::show_account(seckey.as_deref(), asset).c(d!())?; + common::show_account(seckey.as_deref(), asset, is_address_eth).c(d!())?; } else { println!("{}", m.usage()); } @@ -94,16 +99,18 @@ fn run() -> Result<()> { let amount = m.value_of("amount"); let validator = m.value_of("validator"); let show_info = m.is_present("info"); + let is_address_eth = m.is_present("use-default-eth-address"); if amount.is_some() && validator.is_some() { common::delegate( seckey.as_deref(), amount.unwrap().parse::().c(d!())?, validator.unwrap(), + is_address_eth, ) .c(d!())?; } else if show_info { - common::show_delegations(seckey.as_deref()).c(d!())?; + common::show_delegations(seckey.as_deref(), is_address_eth).c(d!())?; } else { println!("{}", m.usage()); } @@ -116,6 +123,7 @@ fn run() -> Result<()> { }; let amount = m.value_of("amount"); let validator = m.value_of("validator"); + let is_address_eth = m.is_present("use-default-eth-address"); if (amount.is_none() && validator.is_some()) || (amount.is_some() && validator.is_none()) { @@ -127,21 +135,18 @@ fn run() -> Result<()> { } else { None }; - common::undelegate(seckey.as_deref(), param).c(d!())?; + common::undelegate(seckey.as_deref(), param, is_address_eth).c(d!())?; } else if let Some(m) = matches.subcommand_matches("asset") { if m.is_present("create") { - let seckey = match m.value_of("seckey") { - Some(path) => { - Some(fs::read_to_string(path).c(d!("Failed to read seckey file"))?) - } - None => None, - }; + let seckey = read_file_path(m.value_of("seckey")).c(d!())?; let memo = m.value_of("memo"); if memo.is_none() { println!("{}", m.usage()); return Ok(()); } let transferable = m.is_present("transferable"); + let is_address_eth = m.is_present("use-default-eth-address"); + let decimal = if let Some(num) = m.value_of("decimal") { num.parse::() .c(d!("decimal should be an 8-bits unsinged integer"))? @@ -164,6 +169,7 @@ fn run() -> Result<()> { max_units, transferable, token_code, + is_address_eth, ) .c(d!())?; } else if m.is_present("show") { @@ -192,12 +198,19 @@ fn run() -> Result<()> { .parse::() .c(d!("amount should be a 64-bits unsigned integer"))?; let hidden = m.is_present("hidden"); + let is_address_eth = m.is_present("use-default-eth-address"); - common::issue_asset(seckey.as_deref(), code.unwrap(), amount, hidden) - .c(d!())?; + common::issue_asset( + seckey.as_deref(), + code.unwrap(), + amount, + hidden, + is_address_eth, + ) + .c(d!())?; } else { let help = "fn asset [--create | --issue | --show]"; - println!("{help}"); + println!("{help}",); } } else if let Some(m) = matches.subcommand_matches("staker-update") { let vm = if let Some(memo) = m.value_of("validator-memo") { @@ -228,6 +241,7 @@ fn run() -> Result<()> { } } }; + let is_address_eth = m.is_present("use-eth-address"); let cr = m.value_of("commission-rate"); if vm.is_none() && cr.is_none() { @@ -236,7 +250,7 @@ fn run() -> Result<()> { "Tips: to update the information of your node, please specify commission-rate or memo" ); } else { - common::staker_update(cr, vm).c(d!())?; + common::staker_update(cr, vm, is_address_eth).c(d!())?; } } else if let Some(m) = matches.subcommand_matches("stake") { let am = m.value_of("amount"); @@ -248,22 +262,31 @@ fn run() -> Result<()> { None => None, }; let td_addr = m.value_of("validator-td-addr"); + let is_address_eth = m.is_present("use-default-eth-address"); if am.is_none() { println!("{}", m.usage()); } else { - common::stake_append(am.unwrap(), staker.as_deref(), td_addr).c(d!())?; + common::stake_append( + am.unwrap(), + staker.as_deref(), + td_addr, + is_address_eth, + ) + .c(d!())?; } } else { let cr = m.value_of("commission-rate"); let vm = m.value_of("validator-memo"); let force = m.is_present("force"); + let is_address_eth = m.is_present("use-default-eth-address"); if am.is_none() || cr.is_none() { println!("{}", m.usage()); println!( "Tips: if you want to raise the power of your node, please use `fn stake --append [OPTIONS]`" ); } else { - common::stake(am.unwrap(), cr.unwrap(), vm, force).c(d!())?; + common::stake(am.unwrap(), cr.unwrap(), vm, force, is_address_eth) + .c(d!())?; } } } else if let Some(m) = matches.subcommand_matches("unstake") { @@ -275,9 +298,11 @@ fn run() -> Result<()> { None => None, }; let td_addr = m.value_of("validator-td-addr"); - common::unstake(am, staker.as_deref(), td_addr).c(d!())?; + let is_address_eth = m.is_present("use-default-eth-address"); + common::unstake(am, staker.as_deref(), td_addr, is_address_eth).c(d!())?; } else if let Some(m) = matches.subcommand_matches("claim") { let am = m.value_of("amount"); + let is_address_eth = m.is_present("use-default-eth-address"); let seckey = match m.value_of("seckey") { Some(path) => { Some(fs::read_to_string(path).c(d!("Failed to read seckey file"))?) @@ -291,10 +316,11 @@ fn run() -> Result<()> { return Ok(()); } }; - common::claim(td_addr, am, seckey.as_deref()).c(d!())?; + common::claim(td_addr, am, seckey.as_deref(), is_address_eth).c(d!())?; } else if let Some(m) = matches.subcommand_matches("show") { let basic = m.is_present("basic"); - common::show(basic).c(d!())?; + let is_address_eth = m.is_present("eth-address"); + common::show(basic, is_address_eth).c(d!())?; } else if let Some(m) = matches.subcommand_matches("setup") { let sa = m.value_of("serv-addr"); let om = m.value_of("owner-mnemonic-path"); @@ -305,12 +331,7 @@ fn run() -> Result<()> { common::setup(sa, om, tp).c(d!())?; } } else if let Some(m) = matches.subcommand_matches("transfer") { - let f = match m.value_of("from-seckey") { - Some(path) => { - Some(fs::read_to_string(path).c(d!("Failed to read seckey file"))?) - } - None => None, - }; + let f = read_file_path(m.value_of("from-seckey")).c(d!())?; let asset = m.value_of("asset").unwrap_or("FRA"); let t = m .value_of("to-pubkey") @@ -322,6 +343,7 @@ fn run() -> Result<()> { }) })?; let am = m.value_of("amount"); + let is_address_eth = m.is_present("use-default-eth-address"); if am.is_none() { println!("{}", m.usage()); @@ -338,6 +360,7 @@ fn run() -> Result<()> { am.unwrap(), m.is_present("confidential-amount"), m.is_present("confidential-type"), + is_address_eth, ) .c(d!())?; } @@ -371,6 +394,7 @@ fn run() -> Result<()> { }) })?; let am = m.value_of("amount"); + let is_address_eth = m.is_present("use-default-eth-address"); if am.is_none() || t.is_empty() { println!("{}", m.usage()); @@ -382,6 +406,7 @@ fn run() -> Result<()> { am.unwrap(), m.is_present("confidential-amount"), m.is_present("confidential-type"), + is_address_eth, ) .c(d!())?; } @@ -396,26 +421,340 @@ fn run() -> Result<()> { ); } else if let Some(m) = matches.subcommand_matches("account") { let address = m.value_of("addr"); - let (account, info) = contract_account_info(address)?; - println!("AccountId: {account}\n{info:#?}\n"); + let sec_key = m.value_of("sec-key"); + let is_address_eth = m.is_present("use-default-eth-address"); + + // FRA asset is the default case + let asset = if let Some(code) = m.value_of("asset") { + match code.to_lowercase().as_str() { + "fra" => None, + _ => Some(code), + } + } else { + None + }; + if sec_key.is_some() { + // Asset defaults to fra + common::show_account(sec_key, asset, is_address_eth).c(d!())?; + } + if address.is_some() { + let (account, info) = contract_account_info(address, is_address_eth)?; + println!("AccountId: {account}\n{info:#?}\n"); + } } else if let Some(m) = matches.subcommand_matches("contract-deposit") { let amount = m.value_of("amount").c(d!())?; let address = m.value_of("addr"); let asset = m.value_of("asset"); let lowlevel_data = m.value_of("lowlevel-data"); + let is_address_eth = m.is_present("eth-address"); transfer_to_account( amount.parse::().c(d!())?, address, asset, lowlevel_data, - ) - .c(d!())? + is_address_eth, + )? } else if let Some(m) = matches.subcommand_matches("contract-withdraw") { let amount = m.value_of("amount").c(d!())?; let address = m.value_of("addr"); let eth_key = m.value_of("eth-key"); - transfer_from_account(amount.parse::().c(d!())?, address, eth_key) - .c(d!())? + let is_address_eth = m.is_present("eth-address"); + transfer_from_account( + amount.parse::().c(d!())?, + address, + eth_key, + is_address_eth, + )? + } else if let Some(m) = matches.subcommand_matches("convert-bar-to-abar") { + // sender Xfr secret key + let owner_sk = read_file_path(m.value_of("from-seckey")).c(d!())?; + + // the receiver Xfr address + let target_addr = m.value_of("to-address").c(d!())?; + + // The TxoSID to be spent for conversion to ABAR (Anon Blind Asset Record) + let txo_sid = m.value_of("txo-sid"); + let is_address_eth = m.is_present("use-default-eth-address"); + + if txo_sid.is_none() { + println!("{}", m.usage()); + } else { + // call the convert function to build and send transaction + // it takes owner Xfr secret key, Axfr address and TxoSID + let r = common::convert_bar2abar( + owner_sk.as_ref(), + target_addr, + txo_sid.unwrap(), + is_address_eth, + ) + .c(d!())?; + + // Print commitment to terminal + println!( + "\x1b[31;01m Commitment: {}\x1b[00m", + wallet::commitment_to_base58(&r) + ); + // write the commitment base64 form to the owned_commitments file + let mut file = fs::OpenOptions::new() + .append(true) + .create(true) + .open("owned_commitments") + .expect("cannot open commitments file"); + std::io::Write::write_all( + &mut file, + ("\n".to_owned() + &wallet::commitment_to_base58(&r)).as_bytes(), + ) + .expect("commitment write failed"); + } + } else if let Some(m) = matches.subcommand_matches("convert-abar-to-bar") { + let is_address_eth = m.is_present("use-default-eth-address"); + // sender Xfr secret key + let owner_sk = read_file_path(m.value_of("from-seckey")).c(d!())?; + + // get the BAR receiver address + let to = m + .value_of("to-pubkey") + .c(d!()) + .and_then(wallet::public_key_from_base64) + .or_else(|_| { + m.value_of("to-wallet-address").c(d!()).and_then(|addr| { + wallet::public_key_from_bech32(addr).c(d!("invalid wallet address")) + }) + })?; + + // get the commitments for abar conversion and anon_fee + let commitment = m.value_of("commitment"); + + if commitment.is_none() { + println!("{}", m.usage()); + } else { + // Build transaction and submit to network + common::convert_abar2bar( + owner_sk, + commitment.unwrap(), + &to, + m.is_present("confidential-amount"), + m.is_present("confidential-type"), + is_address_eth, + ) + .c(d!())?; + } + } else if let Some(m) = matches.subcommand_matches("owned-abars") { + let is_address_eth = m.is_present("use-default-eth-address"); + // sender Xfr secret key + let owner_sk = read_file_path(m.value_of("from-seckey")).c(d!())?; + // parse sender XfrSecretKey or generate from Mnemonic setup with wallet + let from = match owner_sk { + Some(str) => { + ruc::info!(serde_json::from_str::(&format!("\"{str}\""))) + .c(d!())? + .into_keypair() + } + None => get_keypair(is_address_eth).c(d!())?, + }; + + let commitments_list = m + .value_of("commitments") + .unwrap_or_else(|| panic!("Commitment list missing \n {}", m.usage())); + + common::get_owned_abars(from, commitments_list)?; + } else if let Some(m) = matches.subcommand_matches("anon-balance") { + let is_address_eth = m.is_present("use-default-eth-address"); + // Generates a list of owned Abars (both spent and unspent) + // sender Xfr secret key + let owner_sk = read_file_path(m.value_of("from-seckey")).c(d!())?; + // parse sender XfrSecretKey or generate from Mnemonic setup with wallet + let from = match owner_sk { + Some(str) => { + ruc::info!(serde_json::from_str::(&format!("\"{str}\""))) + .c(d!(str))? + .into_keypair() + } + None => get_keypair(is_address_eth).c(d!())?, + }; + let asset = m.value_of("asset"); + + let commitments_list = m + .value_of("commitments") + .unwrap_or_else(|| panic!("Commitment list missing \n {}", m.usage())); + + common::anon_balance(from, commitments_list, asset)?; + } else if let Some(m) = matches.subcommand_matches("owned-open-abars") { + let is_address_eth = m.is_present("use-default-eth-address"); + // sender Xfr secret key + let owner_sk = read_file_path(m.value_of("from-seckey")).c(d!())?; + // parse sender XfrSecretKey or generate from Mnemonic setup with wallet + let from = match owner_sk { + Some(str) => { + ruc::info!(serde_json::from_str::(&format!("\"{str}\""))) + .c(d!())? + .into_keypair() + } + None => get_keypair(is_address_eth).c(d!())?, + }; + let commitment_str = m.value_of("commitment"); + + // create derived public key + let commitment = wallet::commitment_from_base58(commitment_str.unwrap())?; + + // get results from query server and print + let (uid, abar) = utils::get_owned_abar(&commitment).c(d!())?; + let memo = utils::get_abar_memo(&uid).unwrap().unwrap(); + let oabar = + OpenAnonAssetRecordBuilder::from_abar(&abar, memo, &from.into_noah()) + .unwrap() + .build() + .unwrap(); + + println!( + "(AtxoSID, ABAR, OABAR) : {}", + serde_json::to_string(&(uid, abar, oabar)).c(d!())? + ); + } else if let Some(m) = matches.subcommand_matches("owned-utxos") { + // All assets are shown in the default case + let asset = m.value_of("asset"); + let is_address_eth = m.is_present("eth-address"); + + // fetch filtered list by asset + let list = common::get_owned_utxos(asset, is_address_eth)?; + let pk = wallet::public_key_to_base64( + get_keypair(is_address_eth).unwrap().get_pk_ref(), + ); + + // Print UTXO table + println!("Owned utxos for {pk:?}",); + println!("{:-^1$}", "", 100); + println!( + "{0: <8} | {1: <18} | {2: <45} ", + "ATxoSID", "Amount", "AssetType" + ); + for (a, b, c) in list.iter() { + let amt = b + .get_amount() + .map_or_else(|| "Confidential".to_string(), |a| a.to_string()); + let at = c.get_asset_type().map_or_else( + || "Confidential".to_string(), + |at| AssetTypeCode { val: at }.to_base64(), + ); + + println!("{0: <8} | {1: <18} | {2: <45} ", a.0, amt, at); + } + } else if let Some(m) = matches.subcommand_matches("anon-transfer") { + let is_eth_address = m.is_present("use-default-eth-address"); + // sender Xfr secret key + let owner_sk = read_file_path(m.value_of("from-seckey")).c(d!())?; + + // get commitments + let commitment = m.value_of("commitment"); + let fee_commitment = m.value_of("fra-commitment"); + + // get receiver keys and amount + let to_address = m.value_of("to-address"); + let amount = m.value_of("amount"); + + if commitment.is_none() || to_address.is_none() || amount.is_none() { + println!("{}", m.usage()); + } else { + // build transaction and submit + common::gen_anon_transfer_op( + owner_sk, + commitment.unwrap(), + fee_commitment, + amount.unwrap(), + to_address.unwrap(), + is_eth_address, + ) + .c(d!())?; + } + } else if let Some(m) = matches.subcommand_matches("anon-transfer-batch") { + let is_eth_address = m.is_present("use-default-eth-address"); + + // sender Xfr secret key + let owner_sk = read_file_path(m.value_of("from-seckey")).c(d!())?; + + let to_axfr_public_keys = + m.value_of("to-address-file").c(d!()).and_then(|f| { + fs::read_to_string(f).c(d!()).and_then(|pks| { + pks.lines() + .map(|pk| wallet::public_key_from_bech32(pk.trim())) + .collect::>>() + .c(d!("invalid file")) + }) + })?; + let mut commitments = m.value_of("commitment-file").c(d!()).and_then(|f| { + fs::read_to_string(f) + .c(d!()) + .map(|rms| rms.lines().map(String::from).collect::>()) + })?; + commitments.sort(); + commitments.dedup(); + let amounts = m.value_of("amount-file").c(d!()).and_then(|f| { + fs::read_to_string(f) + .c(d!()) + .map(|ams| ams.lines().map(String::from).collect::>()) + })?; + let assets = m.value_of("asset-file").c(d!()).and_then(|f| { + let token_code = |asset: &str| { + if asset.to_uppercase() == "FRA" { + AssetTypeCode { + val: ASSET_TYPE_FRA, + } + } else { + AssetTypeCode::new_from_base64(asset).unwrap_or(AssetTypeCode { + val: ASSET_TYPE_FRA, + }) + } + }; + fs::read_to_string(f) + .c(d!()) + .map(|ams| ams.lines().map(token_code).collect::>()) + })?; + + if to_axfr_public_keys.is_empty() + || commitments.is_empty() + || amounts.is_empty() + || assets.is_empty() + { + println!("{}", m.usage()); + } else { + common::gen_oabar_add_op_x( + owner_sk, + to_axfr_public_keys, + commitments, + amounts, + assets, + is_eth_address, + ) + .c(d!())?; + } + } else if let Some(m) = matches.subcommand_matches("anon-fetch-merkle-proof") { + let atxo_sid = m.value_of("atxo-sid"); + + if atxo_sid.is_none() { + println!("{}", m.usage()); + } else { + let mt_leaf_info = common::get_mtleaf_info(atxo_sid.unwrap()).c(d!())?; + println!("{:?}", serde_json::to_string_pretty(&mt_leaf_info)); + } + } else if let Some(m) = matches.subcommand_matches("check-abar-status") { + let is_address_eth = m.is_present("use-default-eth-address"); + // sender Xfr secret key + let owner_sk = read_file_path(m.value_of("from-seckey")).c(d!())?; + // parse sender XfrSecretKey or generate from Mnemonic setup with wallet + let from = match owner_sk { + Some(str) => { + ruc::info!(serde_json::from_str::(&format!("\"{str}\""))) + .c(d!())? + .into_keypair() + } + None => get_keypair(is_address_eth).c(d!())?, + }; + + let commitment_str = m.value_of("commitment"); + let commitment = wallet::commitment_from_base58(commitment_str.unwrap())?; + + let abar = utils::get_owned_abar(&commitment).c(d!())?; + common::check_abar_status(from, abar).c(d!())?; } else if let Some(m) = matches.subcommand_matches("replace_staker") { let target = m .value_of("target") @@ -428,7 +767,8 @@ fn run() -> Result<()> { return Ok(()); } }; - common::replace_staker(target, td_addr)?; + let is_address_eth = m.is_present("eth-address"); + common::replace_staker(target, td_addr, is_address_eth)?; } else if let Some(m) = matches.subcommand_matches("dev") { #[cfg(not(target_arch = "wasm32"))] { @@ -776,6 +1116,18 @@ fn run() -> Result<()> { Ok(()) } +fn read_file_path(path: Option<&str>) -> Result> { + Ok(match path { + Some(path) => Some( + fs::read_to_string(path) + .c(d!("Failed to read seckey file"))? + .trim() + .to_string(), + ), + None => None, + }) +} + fn tip_fail(e: impl fmt::Display) { eprintln!("\n\x1b[31;01mFAIL !!!\x1b[00m"); eprintln!( diff --git a/src/components/finutils/src/bins/fn.yml b/src/components/finutils/src/bins/fn.yml index 98a3a95b8..02043a839 100644 --- a/src/components/finutils/src/bins/fn.yml +++ b/src/components/finutils/src/bins/fn.yml @@ -10,19 +10,26 @@ args: subcommands: - genkey: - about: Generate a random Findora public key/private key Pair + about: Generate a new Findora key pair + args: + - gen-eth-address: + help: generate eth address + long: gen-eth-address - show: - about: View Validator status and accumulated rewards + about: View the validator status and accumulated rewards args: - basic: help: show basic account info short: b long: basic + - eth-address: + help: use the eth address + long: eth-address - setup: - about: Setup environment variables for staking transactions + about: Set up environment variables for staking transactions args: - serv-addr: - help: a node address of Findora Network + help: a node address of the Findora network short: S long: serv-addr takes_value: true @@ -40,7 +47,7 @@ subcommands: takes_value: true value_name: Path - stake: - about: Stake tokens (i.e. bond tokens) from a Findora account to a Validator + about: Stake tokens (i.e. bond tokens) from a Findora account to a validator args: - amount: help: how much `FRA unit`s you want to stake @@ -66,7 +73,7 @@ subcommands: short: a long: append - staker-priv-key: - help: the file which contains private key (in base64 format) of proposer + help: the file which contains the mnemonic of proposer short: S long: staker-priv-key takes_value: true @@ -80,6 +87,11 @@ subcommands: - force: help: ignore warning and stake FRAs to your target node long: force + - use-default-eth-address: + help: use a private key of the eth address if `staker-priv-key` is not provided + long: use-default-eth-address + conflicts_with: + - staker-priv-key groups: - staking-flags: args: @@ -119,11 +131,14 @@ subcommands: long: validator-memo-logo takes_value: true value_name: Logo + - use-eth-address: + help: use a private key of the eth address + long: use-eth-address - unstake: about: Unstake tokens (i.e. unbond tokens) from a Validator args: - staker-priv-key: - help: the file which contains private key (in base64 format) of proposer + help: the file which contains the mnemonic of proposer short: S long: staker-priv-key takes_value: true @@ -140,6 +155,11 @@ subcommands: long: amount takes_value: true value_name: Amount + - use-default-eth-address: + help: use a private key of the eth address if `staker-priv-key` is not provided + long: use-default-eth-address + conflicts_with: + - staker-priv-key - claim: about: Claim accumulated FRA rewards args: @@ -156,10 +176,15 @@ subcommands: takes_value: true value_name: Amount - seckey: - help: the file which contains base64-formated `XfrPrivateKey` of an existing wallet + help: the file which contains base64-formatted `XfrPrivateKey` of an existing wallet long: seckey takes_value: true value_name: SECRET KEY + - use-default-eth-address: + help: use a private key of the eth address if `seckey` is not provided + long: use-default-eth-address + conflicts_with: + - seckey - delegate: about: Delegating operations args: @@ -170,7 +195,7 @@ subcommands: takes_value: true value_name: AMOUNT - seckey: - help: the file which contains base64-formated `XfrPrivateKey` of an existing wallet + help: the file which contains base64-formatted `XfrPrivateKey` of an existing wallet long: seckey takes_value: true value_name: SECRET KEY @@ -185,6 +210,11 @@ subcommands: conflicts_with: - amount - validator + - use-default-eth-address: + help: use a private key of the eth address if `seckey` is not provided + long: use-default-eth-address + conflicts_with: + - seckey - undelegate: about: Undelegating operations args: @@ -195,7 +225,7 @@ subcommands: takes_value: true value_name: AMOUNT - seckey: - help: the file which contains base64-formated `XfrPrivateKey` of an existing wallet + help: the file which contains base64-formatted `XfrPrivateKey` of an existing wallet long: seckey takes_value: true value_name: SECRET KEY @@ -204,26 +234,33 @@ subcommands: long: validator takes_value: true value_name: VALIDATOR ADDRESS + - use-default-eth-address: + help: use a private key of the eth address if `seckey` is not provided + long: use-default-eth-address + conflicts_with: + - seckey - transfer: about: Transfer tokens from one address to another args: - asset: - help: asset code which you want to tansfer + help: asset code which you want to transfer long: asset takes_value: true value_name: ASSET + allow_hyphen_values: true - from-seckey: - help: the file which contains base64-formated `XfrPrivateKey` of the receiver + help: the file which contains base64-formatted `XfrPrivateKey` of the sender short: f long: from-seckey takes_value: true value_name: SecKey - to-pubkey: - help: base64-formated `XfrPublicKey` of the receiver + help: base64-formatted `XfrPublicKey` of the receiver short: t long: to-pubkey takes_value: true value_name: PubKey + allow_hyphen_values: true - to-wallet-address: help: fra prefixed address of FindoraNetwork short: T @@ -245,11 +282,16 @@ subcommands: - confidential-type: help: mask the asset type sent on the transaction log long: confidential-type + - use-default-eth-address: + help: use a private key of the eth address if `from-seckey` is not provided + long: use-default-eth-address + conflicts_with: + - from-seckey - transfer-batch: about: Transfer tokens from one address to many others args: - from-seckey: - help: the file which contains base64-formated `XfrPrivateKey` of the receiver + help: the file which contains base64-formatted `XfrPrivateKey` of the receiver short: f long: from-seckey takes_value: true @@ -279,6 +321,11 @@ subcommands: - confidential-type: help: mask the asset type sent on the transaction log long: confidential-type + - use-default-eth-address: + help: use a private key of the eth address if `from-seckey` is not provided + long: use-default-eth-address + conflicts_with: + - from-seckey - wallet: about: manipulates a findora wallet args: @@ -295,15 +342,29 @@ subcommands: long: asset takes_value: true value_name: ASSET + allow_hyphen_values: true conflicts_with: - create - seckey: - help: the file which contains base64-formated `XfrPrivateKey` of an existing wallet + help: the file which contains base64-formatted `XfrPrivateKey` of an existing wallet long: seckey takes_value: true value_name: SECRET KEY conflicts_with: - create + - gen-eth-address: + help: generate the keypair of an eth address + long: gen-eth-address + conflicts_with: + - show + - seckey + - asset + - use-default-eth-address: + help: use a private key of the eth address if `seckey` is not provided + long: use-default-eth-address + conflicts_with: + - seckey + - create - asset: about: manipulate custom asset groups: @@ -365,13 +426,14 @@ subcommands: long: code takes_value: true value_name: ASSET CODE + allow_hyphen_values: true - addr: help: Findora wallet address long: addr takes_value: true value_name: WALLET ADDRESS - seckey: - help: the file which contains base64-formated `XfrPrivateKey` of findora account + help: the file which contains base64-formatted `XfrPrivateKey` of findora account long: seckey takes_value: true value_name: SECRET KEY @@ -401,26 +463,13 @@ subcommands: - hidden: help: hidden asset amount when issuing asset on ledger long: hidden - #- history - # about: query operating history - # args: - # - coinbase: - # help: show coinbase history - # long: coinbase - # conflicts_with: - # - transaction - # - transaction: - # help: show transaction history - # conflicts_with: - # - coinbase - # - wallet: - # help: wallet nick name - # long: wallet - # takes_value: true - # value_name: WALLET - # required: true + - use-default-eth-address: + help: use a private key of the eth address if `seckey` is not provided + long: use-default-eth-address + conflicts_with: + - seckey - account: - about: Return user contract account information + about: Return user contract account information or the balance if secret key is provided args: - addr: help: findora account(eg:fra1rkv...) or Ethereum address(g:0xd3Bf...) @@ -428,19 +477,35 @@ subcommands: long: addr takes_value: true value_name: WALLET ADDRESS - required: true + - sec-key: + help: base64-formatted `XfrPrivateKey` + short: s + long: sec-key + takes_value: true + value_name: SECRET KEY + - asset: + help: code of asset, such as `fra` + long: asset + takes_value: true + value_name: ASSET + allow_hyphen_values: true + - use-default-eth-address: + help: use a private key of the eth address if `sec-key` is not provided + long: use-default-eth-address + conflicts_with: + - sec-key - contract-deposit: - about: Transfer FRA from a Findora account to the specified Ethereum address + about: Transfer an asset from the UTXO chain to the EVM chain args: - addr: - help: ethereum address to receive FRA, eg:0xd3Bf... + help: ethereum address to receive asset, eg:0xd3Bf... short: a long: addr takes_value: true value_name: WALLET ADDRESS required: true - amount: - help: deposit FRA amount + help: deposit asset amount short: n long: amount takes_value: true @@ -460,11 +525,14 @@ subcommands: takes_value: true value_name: LOWLEVEL required: false + - eth-address: + help: use the eth address + long: eth-address - contract-withdraw: about: Transfer FRA from an Ethereum address to the specified Findora account args: - addr: - help: findora account to receive FRA, eg:fra1rkv... + help: Findora account to receive FRA, eg:fra1rkv... short: a long: addr takes_value: true @@ -477,14 +545,297 @@ subcommands: value_name: AMOUNT required: true - eth-key: - help: ethereum account mnemonic phrase sign withdraw tx + help: mnemonic phrase for the EVM account to sign the withdraw tx short: e long: eth-key takes_value: true value_name: MNEMONIC required: true + - eth-address: + help: use the eth address + long: eth-address - gen-eth-key: about: Generate an Ethereum address + - owned-abars: + about: Get Anon UTXOs for a keypair using the commitment + args: + - commitments: + help: Commitment of the ABAR + short: c + long: commitments + takes_value: true + value_name: COMMITMENT + required: true + allow_hyphen_values: true + - from-seckey: + help: Xfr secret key file path of receiver + short: s + long: from-seckey + takes_value: true + value_name: SECRET KEY PATH + - asset: + help: code of asset, such as `fra` + long: asset + takes_value: true + value_name: ASSET + allow_hyphen_values: true + - use-default-eth-address: + help: use a private key of the eth address if `from-seckey` is not provided + long: use-default-eth-address + conflicts_with: + - from-seckey + - anon-balance: + about: List Anon balance and spending status for a public key and a list of commitments + args: + - commitments: + help: the list of commitments in base64 form. + short: c + long: commitments + takes_value: true + value_name: COMMITMENT + required: true + allow_hyphen_values: true + - from-seckey: + help: Xfr secret key file path of converter + short: s + long: from-seckey + takes_value: true + value_name: SECRET KEY PATH + required: true + - asset: + help: code of asset, such as `fra` + long: asset + takes_value: true + value_name: ASSET + allow_hyphen_values: true + - use-default-eth-address: + help: use a private key of the eth address if `from-seckey` is not provided + long: use-default-eth-address + conflicts_with: + - from-seckey + - owned-open-abars: + about: Get Open Anon UTXOs for a keypair using commitment + args: + - commitment: + help: The commitment of ABAR + short: c + long: commitment + takes_value: true + value_name: COMMITMENT + required: true + allow_hyphen_values: true + - from-seckey: + help: Xfr secret key file path of converter + short: s + long: from-seckey + takes_value: true + value_name: SECRET KEY PATH + required: true + - use-default-eth-address: + help: use a private key of the eth address if `from-seckey` is not provided + long: use-default-eth-address + conflicts_with: + - from-seckey + - owned-utxos: + about: List owned UTXOs for a public key + args: + - asset: + help: asset code which you want to tansfer + long: asset + takes_value: true + value_name: ASSET + allow_hyphen_values: true + - eth-address: + help: use the eth address + long: eth-address + - convert-bar-to-abar: + about: Convert a BAR to Anon BAR for yourself + args: + - from-seckey: + help: Xfr secret key file path of converter + short: s + long: from-seckey + takes_value: true + value_name: SECRET KEY PATH + - to-address: + help: bech32 address of receiver keys + short: a + long: to-address + takes_value: true + value_name: TO ADDRESS + required: true + - txo-sid: + help: Txo Sid of input to convert + short: t + long: txo-sid + takes_value: true + value_name: TXO SID + required: true + - use-default-eth-address: + help: use a private key of the eth address if `from-seckey` is not provided + long: use-default-eth-address + conflicts_with: + - from-seckey + - convert-abar-to-bar: + about: Convert an ABAR to BAR + args: + - from-seckey: + help: Xfr secret key file path of converter + short: s + long: from-seckey + takes_value: true + value_name: SECRET KEY PATH + - commitment: + help: Commitment for the input Anon BAR + short: c + long: commitment + takes_value: true + value_name: COMMITMENT + required: true + allow_hyphen_values: true + - to-pubkey: + help: base64-formatted `XfrPublicKey` of the receiver + short: t + long: to-pubkey + takes_value: true + value_name: PubKey + allow_hyphen_values: true + - to-wallet-address: + help: Xfr public key of the receiver + short: T + long: to-wallet-address + takes_value: true + value_name: XFR WALLET ADDRESS + conflicts_with: + - to-pubkey + - confidential-amount: + help: mask the amount sent on the transaction log + long: confidential-amount + - confidential-type: + help: mask the asset type sent on the transaction log + long: confidential-type + - use-default-eth-address: + help: use a private key of the eth address if `from-seckey` is not provided + long: use-default-eth-address + conflicts_with: + - from-seckey + - anon-transfer: + about: Perform an anonymous transfer + args: + - from-seckey: + help: Xfr secret key file path of sender + short: s + long: from-seckey + takes_value: true + value_name: SECRET KEY PATH + - commitment: + help: Commitment for the input Anon BAR + short: c + long: commitment + takes_value: true + value_name: COMMITMENT + required: true + allow_hyphen_values: true + - fra-commitment: + help: Commitment for the input FRA Anon BAR + long: fra-commitment + takes_value: true + value_name: FRA COMMITMENT + allow_hyphen_values: true + - to-address: + help: Address of the receiver + long: to-address + takes_value: true + value_name: ADDRESS + required: true + allow_hyphen_values: true + - amount: + help: how much units to transfer + short: n + long: amount + takes_value: true + value_name: Amount + required: true + - use-default-eth-address: + help: use a private key of the eth address if `from-seckey` is not provided + long: use-default-eth-address + conflicts_with: + - from-seckey + - anon-transfer-batch: + about: Anonymous Transfer of tokens from multiple inputs to multiple outputs + args: + - from-seckey: + help: Xfr secret key file path of sender + short: s + long: from-seckey + takes_value: true + value_name: SECRET KEY PATH + required: true + - commitment-file: + help: Commitments for the input Anon BARs + short: c + long: commitment-file + takes_value: true + value_name: COMMITMENT + required: true + - to-address-file: + help: Xfr public keys of the receivers + long: to-address-file + takes_value: true + value_name: PUBLIC KEY + required: true + - amount-file: + help: how much units to transfer for each receiver + short: n + long: amount-file + takes_value: true + value_name: Amount + required: true + - asset-file: + help: Relative asset code type. + short: a + long: asset-file + takes_value: true + value_name: Asset + required: true + - use-default-eth-address: + help: use a private key of the eth address if `from-seckey` is not provided + long: use-default-eth-address + conflicts_with: + - from-seckey + - anon-fetch-merkle-proof: + about: Query Merkle tree leaf info + args: + - atxo-sid: + help: ATXO SID of the ABAR + short: a + long: atxo-sid + takes_value: true + value_name: ATXO SID + required: true + - check-abar-status: + about: Check the spending status and balance of ABAR + args: + - commitment: + help: Commitment of the ABAR + short: c + long: commitment + takes_value: true + value_name: COMMITMENT + required: true + allow_hyphen_values: true + - from-seckey: + help: Xfr secret key file path of sender + short: s + long: from-seckey + takes_value: true + value_name: SECRET KEY PATH + required: true + - use-default-eth-address: + help: use a private key of the eth address if `from-seckey` is not provided + long: use-default-eth-address + conflicts_with: + - from-seckey - replace_staker: about: Replace the staker of the validator with target address args: @@ -501,6 +852,23 @@ subcommands: takes_value: true value_name: TARGET PUBLIC KEY required: true + allow_hyphen_values: true + - td_address: + help: the tendermint address that you may want to replace. + long: td_address + takes_value: true + value_name: TENDERMINT ADDRESS + required: false + - td_pubkey: + help: the tendermint public key that you may want to replace. + long: td_pubkey + takes_value: true + value_name: TENDERMINT PUBKEY + required: false + allow_hyphen_values: true + - eth-address: + help: use the eth address + long: eth-address - dev: about: Manage development clusters on your localhost args: diff --git a/src/components/finutils/src/bins/key_generator.rs b/src/components/finutils/src/bins/key_generator.rs index 71582f04c..c58945946 100644 --- a/src/components/finutils/src/bins/key_generator.rs +++ b/src/components/finutils/src/bins/key_generator.rs @@ -5,5 +5,5 @@ fn main() { .nth(1) .unwrap_or_else(|| "1".to_owned()) .parse::()); - (0..n).for_each(|_| gen_key_and_print()); + (0..n).for_each(|_| gen_key_and_print(false)); } diff --git a/src/components/finutils/src/bins/stt/init/mod.rs b/src/components/finutils/src/bins/stt/init/mod.rs index a5a372eb4..606f9931f 100644 --- a/src/components/finutils/src/bins/stt/init/mod.rs +++ b/src/components/finutils/src/bins/stt/init/mod.rs @@ -44,7 +44,7 @@ pub fn init(mut interval: u64, is_mainnet: bool, skip_validator: bool) -> Result .values() .map(|u| &u.pubkey) .chain(VALIDATOR_LIST.values().map(|v| &v.pubkey)) - .map(|pk| (pk, FRA_PRE_ISSUE_AMOUNT / 2_000)) + .map(|pk| (*pk, FRA_PRE_ISSUE_AMOUNT / 2_000)) .collect::>(); // Wallet Address: fra18xkez3fum44jq0zhvwq380rfme7u624cccn3z56fjeex6uuhpq6qv9e4g5 @@ -56,7 +56,7 @@ pub fn init(mut interval: u64, is_mainnet: bool, skip_validator: bool) -> Result let bank = pnk!(wallet::public_key_from_base64( "Oa2RRTzdayA8V2OBE7xp3n3NKrjGJxFTSZZybXOXCDQ=" )); - target_list.push((&bank, FRA_PRE_ISSUE_AMOUNT / 100 * 98)); + target_list.push((bank, FRA_PRE_ISSUE_AMOUNT / 100 * 98)); println!(">>> Transfer FRAs to validators ..."); common::utils::transfer_batch(&root_kp, target_list, None, true, true) diff --git a/src/components/finutils/src/bins/stt/stt.rs b/src/components/finutils/src/bins/stt/stt.rs index 84eb29643..887f2bcc2 100644 --- a/src/components/finutils/src/bins/stt/stt.rs +++ b/src/components/finutils/src/bins/stt/stt.rs @@ -27,7 +27,7 @@ use { ruc::*, serde::Serialize, std::{collections::BTreeMap, env}, - zei::xfr::sig::{XfrKeyPair, XfrPublicKey}, + zei::{BlindAssetRecord, XfrKeyPair, XfrPublicKey}, }; lazy_static! { @@ -210,10 +210,12 @@ mod issue { }, rand_chacha::rand_core::SeedableRng, rand_chacha::ChaChaRng, - zei::setup::PublicParams, - zei::xfr::{ - asset_record::{build_blind_asset_record, AssetRecordType}, - structs::AssetRecordTemplate, + zei::{ + noah_algebra::ristretto::PedersenCommitmentRistretto, + noah_api::xfr::{ + asset_record::{build_blind_asset_record, AssetRecordType}, + structs::AssetRecordTemplate, + }, }, }; @@ -233,21 +235,21 @@ mod issue { FRA_PRE_ISSUE_AMOUNT / 2, ASSET_TYPE_FRA, AssetRecordType::NonConfidentialAmount_NonConfidentialAssetType, - root_kp.get_pk(), + root_kp.get_pk().into_noah(), ); - let params = PublicParams::default(); + let pc_gens = PedersenCommitmentRistretto::default(); let outputs = (0..2) .map(|_| { let (ba, _, _) = build_blind_asset_record( &mut ChaChaRng::from_entropy(), - ¶ms.pc_gens, + &pc_gens, &template, vec![], ); ( TxOutput { id: None, - record: ba, + record: BlindAssetRecord::from_noah(&ba), lien: None, }, None, @@ -266,12 +268,12 @@ mod issue { IssueAsset::new(aib, &IssuerKeyPair { keypair: &root_kp }).c(d!())?; builder.add_operation(Operation::IssueAsset(asset_issuance_operation)); - Ok(builder.take_transaction()) + builder.build_and_take_transaction() } } mod delegate { - use {super::*, zei::xfr::asset_record::AssetRecordType}; + use {super::*, zei::noah_api::xfr::asset_record::AssetRecordType}; pub fn gen_tx( user: NameRef, @@ -291,7 +293,7 @@ mod delegate { common::utils::gen_transfer_op( owner_kp, - vec![(&BLACK_HOLE_PUBKEY_STAKING, amount)], + vec![(XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY_STAKING), amount)], None, false, false, @@ -303,7 +305,7 @@ mod delegate { builder.add_operation_delegation(owner_kp, amount, validator.to_owned()); })?; - let mut tx = builder.take_transaction(); + let mut tx = builder.build_and_take_transaction()?; tx.sign(owner_kp); Ok(tx) } @@ -341,7 +343,7 @@ mod undelegate { } })?; - Ok(builder.take_transaction()) + builder.build_and_take_transaction() } } @@ -358,7 +360,7 @@ mod claim { builder.add_operation_claim(None, owner_kp, amount); })?; - Ok(builder.take_transaction()) + builder.build_and_take_transaction() } } diff --git a/src/components/finutils/src/common/ddev/init.rs b/src/components/finutils/src/common/ddev/init.rs index 3db94fca6..a6504448f 100644 --- a/src/components/finutils/src/common/ddev/init.rs +++ b/src/components/finutils/src/common/ddev/init.rs @@ -17,9 +17,8 @@ use ledger::{ }; use ruc::*; use serde::{Deserialize, Serialize}; -use zei::xfr::{ - asset_record::AssetRecordType, - sig::{XfrKeyPair, XfrPublicKey, XfrSecretKey}, +use zei::{ + noah_api::xfr::asset_record::AssetRecordType, XfrKeyPair, XfrPublicKey, XfrSecretKey, }; #[derive(Deserialize)] @@ -59,7 +58,7 @@ pub(super) fn init(env: &mut Env) -> Result<()> { .and_then(|b| serde_json::from_slice::(&b).c(d!()))?; tm_validators.result.validators.into_iter().for_each(|v| { - let xfr_key = common::gen_key(); + let xfr_key = common::gen_key(false); let iv = InitialValidator { tendermint_addr: v.address, tendermint_pubkey: v.pub_key.value, @@ -102,7 +101,7 @@ pub(super) fn init(env: &mut Env) -> Result<()> { .custom_data .initial_validators .iter() - .map(|v| (v.xfr_keypair.get_pk_ref(), 500_0000 * FRA)) + .map(|v| (v.xfr_keypair.get_pk(), 500_0000 * FRA)) .collect::>(); println!("[ {} ] >>> Transfer FRAs to validators ...", &env.name); @@ -118,7 +117,7 @@ pub(super) fn init(env: &mut Env) -> Result<()> { gen_transfer_op_xx( Some(&gen_8668_endpoint(env)), &v.xfr_keypair, - vec![(&BLACK_HOLE_PUBKEY_STAKING, am)], + vec![(XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY_STAKING), am)], None, true, false, @@ -134,9 +133,13 @@ pub(super) fn init(env: &mut Env) -> Result<()> { v.tendermint_addr.clone(), ); })?; - let mut tx = builder.take_transaction(); - tx.sign(&v.xfr_keypair); - send_tx(env, &tx).c(d!())?; + builder + .build_and_take_transaction() + .c(d!()) + .and_then(|mut tx| { + tx.sign(&v.xfr_keypair); + send_tx(env, &tx).c(d!()) + })?; } println!("[ {} ] >>> Init work done !", &env.name); @@ -154,7 +157,10 @@ fn setup_initial_validators(env: &Env) -> Result<()> { .collect::>(); builder.add_operation_update_validator(&[], 1, vs).c(d!())?; - send_tx(env, &builder.take_transaction()).c(d!()) + builder + .build_and_take_transaction() + .c(d!()) + .and_then(|tx| send_tx(env, &tx).c(d!())) } fn send_tx(env: &Env, tx: &Transaction) -> Result<()> { @@ -174,7 +180,7 @@ fn send_tx(env: &Env, tx: &Transaction) -> Result<()> { fn transfer_batch( env: &Env, owner_kp: &XfrKeyPair, - target_list: Vec<(&XfrPublicKey, u64)>, + target_list: Vec<(XfrPublicKey, u64)>, token_code: Option, confidential_am: bool, confidential_ty: bool, @@ -193,10 +199,13 @@ fn transfer_batch( .c(d!())?; builder.add_operation(op); - let mut tx = builder.take_transaction(); - tx.sign(owner_kp); - - send_tx(env, &tx).c(d!()) + builder + .build_and_take_transaction() + .c(d!()) + .and_then(|mut tx| { + tx.sign(owner_kp); + send_tx(env, &tx).c(d!()) + }) } fn new_tx_builder(env: &Env) -> Result { diff --git a/src/components/finutils/src/common/evm.rs b/src/components/finutils/src/common/evm.rs index 14dfde63e..586a23916 100644 --- a/src/components/finutils/src/common/evm.rs +++ b/src/components/finutils/src/common/evm.rs @@ -18,18 +18,15 @@ use fp_types::{ transaction::UncheckedTransaction, U256, }; -use fp_utils::ecdsa::SecpPair; -use fp_utils::tx::EvmRawTxWrapper; -use ledger::data_model::AssetTypeCode; -use ledger::data_model::ASSET_TYPE_FRA; -use ledger::data_model::BLACK_HOLE_PUBKEY_STAKING; +use fp_utils::{ecdsa::SecpPair, tx::EvmRawTxWrapper}; +use ledger::data_model::{AssetTypeCode, ASSET_TYPE_FRA, BLACK_HOLE_PUBKEY_STAKING}; use ruc::*; use std::str::FromStr; use tendermint::block::Height; use tendermint_rpc::endpoint::abci_query::AbciQuery; use tendermint_rpc::{Client, HttpClient}; use tokio::runtime::Runtime; -use zei::xfr::{asset_record::AssetRecordType, sig::XfrKeyPair}; +use zei::{noah_api::xfr::asset_record::AssetRecordType, XfrKeyPair, XfrPublicKey}; /// transfer utxo assets to account(ed25519 or ecdsa address) balance. pub fn transfer_to_account( @@ -37,10 +34,11 @@ pub fn transfer_to_account( address: Option<&str>, asset: Option<&str>, lowlevel_data: Option<&str>, + is_address_eth: bool, ) -> Result<()> { let mut builder = utils::new_tx_builder().c(d!())?; - let kp = get_keypair().c(d!())?; + let kp = get_keypair(is_address_eth).c(d!())?; let asset = if let Some(asset) = asset { let asset = AssetTypeCode::new_from_base64(asset)?; @@ -51,13 +49,14 @@ pub fn transfer_to_account( let transfer_op = utils::gen_transfer_op( &kp, - vec![(&BLACK_HOLE_PUBKEY_STAKING, amount)], + vec![(XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY_STAKING), amount)], asset, false, false, Some(AssetRecordType::NonConfidentialAmount_NonConfidentialAssetType), ) .c(d!())?; + let target_address = match address { Some(s) => MultiSigner::from_str(s).c(d!())?, None => MultiSigner::Xfr(kp.get_pk()), @@ -76,7 +75,7 @@ pub fn transfer_to_account( .c(d!())? .sign(&kp); - let mut tx = builder.take_transaction(); + let mut tx = builder.build_and_take_transaction()?; tx.sign_to_map(&kp); utils::send_tx(&tx).c(d!()) @@ -94,7 +93,7 @@ impl Keypair { match self { Keypair::Ecdsa(kp) => MultiSignature::from(kp.sign(data)), Keypair::Ed25519(kp) => { - MultiSignature::from(kp.get_sk_ref().sign(data, kp.get_pk_ref())) + MultiSignature::from(kp.get_sk_ref().sign(data).unwrap()) } } } @@ -105,8 +104,9 @@ pub fn transfer_from_account( amount: u64, address: Option<&str>, eth_phrase: Option<&str>, + is_address_eth: bool, ) -> Result<()> { - let fra_kp = get_keypair()?; + let fra_kp = get_keypair(is_address_eth)?; let target = match address { Some(s) => { @@ -204,8 +204,11 @@ fn one_shot_abci_query( } /// Query contract account info by abci/query -pub fn contract_account_info(address: Option<&str>) -> Result<(Address, SmartAccount)> { - let fra_kp = get_keypair()?; +pub fn contract_account_info( + address: Option<&str>, + is_address_eth: bool, +) -> Result<(Address, SmartAccount)> { + let fra_kp = get_keypair(is_address_eth)?; let address = match address { Some(s) => MultiSigner::from_str(s).c(d!())?, diff --git a/src/components/finutils/src/common/mod.rs b/src/components/finutils/src/common/mod.rs index 534364f6c..573cc0159 100644 --- a/src/components/finutils/src/common/mod.rs +++ b/src/components/finutils/src/common/mod.rs @@ -17,12 +17,17 @@ pub mod utils; use { self::utils::{get_evm_staking_address, get_validator_memo_and_rate}, - crate::api::DelegationInfo, + crate::{ + api::DelegationInfo, + common::utils::{new_tx_builder, send_tx}, + txn_builder::TransactionBuilder, + }, globutils::wallet, lazy_static::lazy_static, ledger::{ data_model::{ - gen_random_keypair, AssetRules, AssetTypeCode, AssetTypePrefix, Transaction, + gen_random_keypair, get_abar_commitment, ATxoSID, AssetRules, AssetTypeCode, + AssetTypePrefix, Transaction, TxoSID, ASSET_TYPE_FRA, BLACK_HOLE_PUBKEY_STAKING, }, staking::{ @@ -31,17 +36,30 @@ use { TendermintAddrRef, }, }, + rand_chacha::ChaChaRng, + rand_core::SeedableRng, ruc::*, std::{env, fs}, tendermint::PrivateKey, utils::{get_block_height, get_local_block_height, parse_td_validator_keys}, web3::types::H160, zei::{ - setup::PublicParams, - xfr::{ - asset_record::AssetRecordType, - sig::{XfrKeyPair, XfrPublicKey, XfrSecretKey}, + noah_api::{ + anon_xfr::{ + nullify, + structs::{ + AnonAssetRecord, Commitment, MTLeafInfo, OpenAnonAssetRecordBuilder, + }, + }, + xfr::{ + asset_record::{ + AssetRecordType, + AssetRecordType::NonConfidentialAmount_NonConfidentialAssetType, + }, + structs::{XfrAmount, XfrAssetType}, + }, }, + XfrKeyPair, XfrPublicKey, XfrSecretKey, }, }; @@ -61,7 +79,11 @@ lazy_static! { } /// Updating the information of a staker includes commission_rate and staker_memo -pub fn staker_update(cr: Option<&str>, memo: Option) -> Result<()> { +pub fn staker_update( + cr: Option<&str>, + memo: Option, + is_address_eth: bool, +) -> Result<()> { let pub_key = get_td_pubkey() .map(|i| td_pubkey_to_td_addr_bytes(&i)) .c(d!())?; @@ -83,7 +105,7 @@ pub fn staker_update(cr: Option<&str>, memo: Option) -> Result<()> { let td_pubkey = get_td_pubkey().c(d!())?; - let kp = get_keypair().c(d!())?; + let kp = get_keypair(is_address_eth).c(d!())?; let vkp = get_td_privkey().c(d!())?; let mut builder = utils::new_tx_builder().c(d!())?; @@ -95,7 +117,7 @@ pub fn staker_update(cr: Option<&str>, memo: Option) -> Result<()> { .c(d!()) .map(|op| builder.add_operation(op))?; - let mut tx = builder.take_transaction(); + let mut tx = builder.build_and_take_transaction()?; tx.sign_to_map(&kp); utils::send_tx(&tx).c(d!()) @@ -108,6 +130,7 @@ pub fn stake( commission_rate: &str, memo: Option<&str>, force: bool, + is_address_eth: bool, ) -> Result<()> { let am = amount.parse::().c(d!("'amount' must be an integer"))?; check_delegation_amount(am, false).c(d!())?; @@ -117,7 +140,7 @@ pub fn stake( .and_then(|cr| convert_commission_rate(cr).c(d!()))?; let td_pubkey = get_td_pubkey().c(d!())?; - let kp = get_keypair().c(d!())?; + let kp = get_keypair(is_address_eth).c(d!())?; let vkp = get_td_privkey().c(d!())?; macro_rules! diff { @@ -151,7 +174,7 @@ pub fn stake( .c(d!())?; utils::gen_transfer_op( &kp, - vec![(&BLACK_HOLE_PUBKEY_STAKING, am)], + vec![(XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY_STAKING), am)], None, false, false, @@ -160,7 +183,7 @@ pub fn stake( .c(d!()) .map(|principal_op| builder.add_operation(principal_op))?; - let mut tx = builder.take_transaction(); + let mut tx = builder.build_and_take_transaction()?; tx.sign_to_map(&kp); utils::send_tx(&tx).c(d!()) @@ -171,6 +194,7 @@ pub fn stake_append( amount: &str, staker: Option<&str>, td_addr: Option, + is_address_eth: bool, ) -> Result<()> { let am = amount.parse::().c(d!("'amount' must be an integer"))?; check_delegation_amount(am, true).c(d!())?; @@ -184,13 +208,13 @@ pub fn stake_append( let kp = staker .c(d!()) .and_then(|sk| wallet::restore_keypair_from_mnemonic_default(sk).c(d!())) - .or_else(|_| get_keypair().c(d!()))?; + .or_else(|_| get_keypair(is_address_eth).c(d!()))?; let mut builder = utils::new_tx_builder().c(d!())?; builder.add_operation_delegation(&kp, am, td_addr); utils::gen_transfer_op( &kp, - vec![(&BLACK_HOLE_PUBKEY_STAKING, am)], + vec![(XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY_STAKING), am)], None, false, false, @@ -198,7 +222,8 @@ pub fn stake_append( ) .c(d!()) .map(|principal_op| builder.add_operation(principal_op))?; - let mut tx = builder.take_transaction(); + + let mut tx = builder.build_and_take_transaction()?; tx.sign_to_map(&kp); utils::send_tx(&tx).c(d!()) @@ -209,6 +234,7 @@ pub fn unstake( am: Option<&str>, staker: Option<&str>, td_addr: Option, + is_address_eth: bool, ) -> Result<()> { let am = if let Some(i) = am { Some(i.parse::().c(d!("'amount' must be an integer"))?) @@ -219,7 +245,7 @@ pub fn unstake( let kp = staker .c(d!()) .and_then(|sk| wallet::restore_keypair_from_mnemonic_default(sk).c(d!())) - .or_else(|_| get_keypair().c(d!()))?; + .or_else(|_| get_keypair(is_address_eth).c(d!()))?; let td_addr_bytes = td_addr .c(d!()) .and_then(|ta| td_addr_to_bytes(ta).c(d!())) @@ -248,14 +274,19 @@ pub fn unstake( } })?; - let mut tx = builder.take_transaction(); + let mut tx = builder.build_and_take_transaction()?; tx.sign_to_map(&kp); utils::send_tx(&tx).c(d!()) } /// Claim rewards from findora network -pub fn claim(td_addr: &str, am: Option<&str>, sk_str: Option<&str>) -> Result<()> { +pub fn claim( + td_addr: &str, + am: Option<&str>, + sk_str: Option<&str>, + is_address_eth: bool, +) -> Result<()> { let td_addr = hex::decode(td_addr).c(d!())?; let am = if let Some(i) = am { @@ -264,7 +295,7 @@ pub fn claim(td_addr: &str, am: Option<&str>, sk_str: Option<&str>) -> Result<() None }; - let kp = restore_keypair_from_str_with_default(sk_str)?; + let kp = restore_keypair_from_str_with_default(sk_str, is_address_eth)?; let mut builder = utils::new_tx_builder().c(d!())?; @@ -273,7 +304,7 @@ pub fn claim(td_addr: &str, am: Option<&str>, sk_str: Option<&str>) -> Result<() builder.add_operation_claim(Some(td_addr), &kp, am); })?; - let mut tx = builder.take_transaction(); + let mut tx = builder.build_and_take_transaction()?; tx.sign_to_map(&kp); utils::send_tx(&tx).c(d!()) @@ -288,14 +319,14 @@ pub fn claim(td_addr: &str, am: Option<&str>, sk_str: Option<&str>) -> Result<() /// Delegation Information /// Validator Detail (if already staked) /// -pub fn show(basic: bool) -> Result<()> { - let kp = get_keypair().c(d!())?; +pub fn show(basic: bool, is_address_eth: bool) -> Result<()> { + let kp = get_keypair(is_address_eth).c(d!())?; let serv_addr = ruc::info!(get_serv_addr()).map(|i| { println!("\x1b[31;01mServer URL:\x1b[00m\n{i}\n"); }); - let xfr_account = ruc::info!(get_keypair()).map(|i| { + let xfr_account = ruc::info!(get_keypair(is_address_eth)).map(|i| { println!( "\x1b[31;01mFindora Address:\x1b[00m\n{}\n", wallet::public_key_to_bech32(&i.get_pk()) @@ -304,6 +335,10 @@ pub fn show(basic: bool) -> Result<()> { "\x1b[31;01mFindora Public Key:\x1b[00m\n{}\n", wallet::public_key_to_base64(&i.get_pk()) ); + println!( + "\x1b[31;01mFindora Public Key in hex:\x1b[00m\n{}\n", + wallet::public_key_to_hex(&i.get_pk()) + ); }); let self_balance = ruc::info!(utils::get_balance(&kp)).map(|i| { @@ -415,6 +450,7 @@ pub fn transfer_asset( am: &str, confidential_am: bool, confidential_ty: bool, + is_address_eth: bool, ) -> Result<()> { transfer_asset_batch( owner_sk, @@ -423,6 +459,7 @@ pub fn transfer_asset( am, confidential_am, confidential_ty, + is_address_eth, ) .c(d!()) } @@ -455,8 +492,9 @@ pub fn transfer_asset_batch( am: &str, confidential_am: bool, confidential_ty: bool, + is_address_eth: bool, ) -> Result<()> { - let from = restore_keypair_from_str_with_default(owner_sk)?; + let from = restore_keypair_from_str_with_default(owner_sk, is_address_eth)?; let am = am.parse::().c(d!("'amount' must be an integer"))?; transfer_asset_batch_x( @@ -481,7 +519,7 @@ pub fn transfer_asset_batch_x( ) -> Result<()> { utils::transfer_batch( kp, - target_addr.iter().map(|addr| (addr, am)).collect(), + target_addr.iter().map(|addr| (*addr, am)).collect(), token_code, confidential_am, confidential_ty, @@ -505,15 +543,21 @@ pub fn get_serv_addr() -> Result<&'static str> { } /// Get keypair from config file -pub fn get_keypair() -> Result { +pub fn get_keypair(is_address_eth: bool) -> Result { if let Some(m_path) = MNEMONIC.as_ref() { fs::read_to_string(m_path) .c(d!("can not read mnemonic from 'owner-mnemonic-path'")) .and_then(|m| { let k = m.trim(); - wallet::restore_keypair_from_mnemonic_default(k) - .c(d!("invalid 'owner-mnemonic'")) - .or_else(|e| wallet::restore_keypair_from_seckey_base64(k).c(d!(e))) + let kp = if is_address_eth { + wallet::restore_keypair_from_mnemonic_secp256k1(k) + .c(d!("invalid 'owner-mnemonic'")) + } else { + wallet::restore_keypair_from_mnemonic_default(k) + .c(d!("invalid 'owner-mnemonic'")) + }; + + kp.or_else(|e| wallet::restore_keypair_from_seckey_base64(k).c(d!(e))) }) } else { Err(eg!("'owner-mnemonic-path' has not been set")) @@ -559,10 +603,15 @@ pub fn convert_commission_rate(cr: f64) -> Result<[u64; 2]> { } #[allow(missing_docs)] -pub fn gen_key() -> (String, String, String, XfrKeyPair) { +pub fn gen_key(is_address_eth: bool) -> (String, String, String, XfrKeyPair) { let (mnemonic, key, kp) = loop { let mnemonic = pnk!(wallet::generate_mnemonic_custom(24, "en")); - let kp = pnk!(wallet::restore_keypair_from_mnemonic_default(&mnemonic)); + let kp = if is_address_eth { + pnk!(wallet::restore_keypair_from_mnemonic_secp256k1(&mnemonic)) + } else { + pnk!(wallet::restore_keypair_from_mnemonic_default(&mnemonic)) + }; + if let Some(key) = serde_json::to_string_pretty(&kp) .ok() .filter(|s| s.matches("\": \"-").next().is_none()) @@ -577,26 +626,33 @@ pub fn gen_key() -> (String, String, String, XfrKeyPair) { } #[allow(missing_docs)] -pub fn gen_key_and_print() { - let (wallet_addr, mnemonic, key, _) = gen_key(); +pub fn gen_key_and_print(is_address_eth: bool) { + let (wallet_addr, mnemonic, key, _) = gen_key(is_address_eth); println!( "\n\x1b[31;01mWallet Address:\x1b[00m {wallet_addr}\n\x1b[31;01mMnemonic:\x1b[00m {mnemonic}\n\x1b[31;01mKey:\x1b[00m {key}\n", ); } -fn restore_keypair_from_str_with_default(sk_str: Option<&str>) -> Result { +fn restore_keypair_from_str_with_default( + sk_str: Option<&str>, + is_address_eth: bool, +) -> Result { if let Some(sk) = sk_str { serde_json::from_str::(&format!("\"{}\"", sk.trim())) .map(|sk| sk.into_keypair()) .c(d!("Invalid secret key")) } else { - get_keypair().c(d!()) + get_keypair(is_address_eth).c(d!()) } } /// Show the asset balance of a findora account -pub fn show_account(sk_str: Option<&str>, _asset: Option<&str>) -> Result<()> { - let kp = restore_keypair_from_str_with_default(sk_str)?; +pub fn show_account( + sk_str: Option<&str>, + _asset: Option<&str>, + is_address_eth: bool, +) -> Result<()> { + let kp = restore_keypair_from_str_with_default(sk_str, is_address_eth)?; // let token_code = asset // .map(|asset| AssetTypeCode::new_from_base64(asset).c(d!("Invalid asset code"))) // .transpose()?; @@ -617,8 +673,13 @@ pub fn show_account(sk_str: Option<&str>, _asset: Option<&str>) -> Result<()> { #[inline(always)] #[allow(missing_docs)] -pub fn delegate(sk_str: Option<&str>, amount: u64, validator: &str) -> Result<()> { - restore_keypair_from_str_with_default(sk_str) +pub fn delegate( + sk_str: Option<&str>, + amount: u64, + validator: &str, + is_address_eth: bool, +) -> Result<()> { + restore_keypair_from_str_with_default(sk_str, is_address_eth) .c(d!()) .and_then(|kp| delegate_x(&kp, amount, validator).c(d!())) } @@ -633,8 +694,12 @@ pub fn delegate_x(kp: &XfrKeyPair, amount: u64, validator: &str) -> Result<()> { #[inline(always)] #[allow(missing_docs)] -pub fn undelegate(sk_str: Option<&str>, param: Option<(u64, &str)>) -> Result<()> { - restore_keypair_from_str_with_default(sk_str) +pub fn undelegate( + sk_str: Option<&str>, + param: Option<(u64, &str)>, + is_address_eth: bool, +) -> Result<()> { + restore_keypair_from_str_with_default(sk_str, is_address_eth) .c(d!()) .and_then(|kp| undelegate_x(&kp, param).c(d!())) } @@ -648,8 +713,8 @@ pub fn undelegate_x(kp: &XfrKeyPair, param: Option<(u64, &str)>) -> Result<()> { } /// Display delegation information of a findora account -pub fn show_delegations(sk_str: Option<&str>) -> Result<()> { - let pk = restore_keypair_from_str_with_default(sk_str)?.get_pk(); +pub fn show_delegations(sk_str: Option<&str>, is_address_eth: bool) -> Result<()> { + let pk = restore_keypair_from_str_with_default(sk_str, is_address_eth)?.get_pk(); println!( "{}", @@ -684,7 +749,7 @@ fn gen_undelegate_tx( builder.add_operation_undelegation(owner_kp, None); } - let mut tx = builder.take_transaction(); + let mut tx = builder.build_and_take_transaction()?; tx.sign_to_map(owner_kp); Ok(tx) @@ -699,7 +764,7 @@ fn gen_delegate_tx( utils::gen_transfer_op( owner_kp, - vec![(&BLACK_HOLE_PUBKEY_STAKING, amount)], + vec![(XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY_STAKING), amount)], None, false, false, @@ -711,7 +776,7 @@ fn gen_delegate_tx( builder.add_operation_delegation(owner_kp, amount, validator.to_owned()); })?; - let mut tx = builder.take_transaction(); + let mut tx = builder.build_and_take_transaction()?; tx.sign_to_map(owner_kp); @@ -726,8 +791,9 @@ pub fn create_asset( max_units: Option, transferable: bool, token_code: Option<&str>, + is_address_eth: bool, ) -> Result<()> { - let kp = restore_keypair_from_str_with_default(sk_str)?; + let kp = restore_keypair_from_str_with_default(sk_str, is_address_eth)?; let code = if token_code.is_none() { AssetTypeCode::gen_random() @@ -753,7 +819,7 @@ pub fn create_asset_x( code: Option, ) -> Result { let code = code.unwrap_or_else(AssetTypeCode::gen_random); - let asset_code = AssetTypeCode::from_prefix_and_raw_asset_type_code( + let asset_code = AssetTypeCode::from_prefix_and_raw_asset_type_code_2nd_update( AssetTypePrefix::UserDefined, &code, ); @@ -771,7 +837,7 @@ pub fn create_asset_x( .c(d!()) .map(|op| builder.add_operation(op))?; - let mut tx = builder.take_transaction(); + let mut tx = builder.build_and_take_transaction()?; tx.sign_to_map(kp); utils::send_tx(&tx).map(|_| asset_code) @@ -783,8 +849,9 @@ pub fn issue_asset( asset: &str, amount: u64, hidden: bool, + is_address_eth: bool, ) -> Result<()> { - let kp = restore_keypair_from_str_with_default(sk_str)?; + let kp = restore_keypair_from_str_with_default(sk_str, is_address_eth)?; let code = AssetTypeCode::new_from_base64(asset).c(d!())?; issue_asset_x(&kp, &code, amount, hidden).c(d!()) } @@ -806,14 +873,13 @@ pub fn issue_asset_x( builder.get_seq_id(), amount, confidentiality_flags, - &PublicParams::default(), ) .c(d!())?; utils::gen_fee_op(kp) .c(d!()) .map(|op| builder.add_operation(op))?; - let mut tx = builder.take_transaction(); + let mut tx = builder.build_and_take_transaction()?; tx.sign_to_map(kp); utils::send_tx(&tx) @@ -831,15 +897,651 @@ pub fn show_asset(addr: &str) -> Result<()> { Ok(()) } +/// Builds a transaction for a BAR to ABAR conversion with fees and sends it to network +/// # Arguments +/// * owner_sk - Optional secret key Xfr in json form +/// * target_addr - ABAR receiving AXfr pub key after conversion in base64 +/// * TxoSID - sid of BAR to convert +pub fn convert_bar2abar( + owner_sk: Option<&String>, + target_addr: &str, + txo_sid: &str, + is_address_eth: bool, +) -> Result { + // parse sender XfrSecretKey or generate from Mnemonic setup with wallet + let from = match owner_sk { + Some(str) => { + ruc::info!(serde_json::from_str::(&format!("\"{str}\"",))) + .c(d!())? + .into_keypair() + } + None => get_keypair(is_address_eth).c(d!())?, + }; + // parse receiver AxfrPubKey + let to = + wallet::public_key_from_bech32(target_addr).c(d!("invalid 'target-addr'"))?; + let sid = txo_sid.parse::().c(d!("error parsing TxoSID"))?; + + // Get OpenAssetRecord from given Owner XfrKeyPair and TxoSID + let record = + utils::get_oar(&from, TxoSID(sid)).c(d!("error fetching open asset record"))?; + let is_bar_transparent = + record.1.get_record_type() == NonConfidentialAmount_NonConfidentialAssetType; + + // Generate the transaction and transmit it to network + let c = utils::generate_bar2abar_op( + &from, + &to, + TxoSID(sid), + &record.0, + is_bar_transparent, + ) + .c(d!("Bar to abar failed"))?; + + Ok(c) +} + +/// Convert an ABAR to a Blind Asset Record +/// # Arguments +/// * axfr_secret_key - the anon_secret_key in base64 +/// * com - commitment of ABAR in base64 +/// * to - Bar receiver's XfrPublicKey pointer +/// * com_fra - commitment of the FRA ABAR to pay fee in base64 +/// * confidential_am - if the output BAR should have confidential amount +/// * confidential_ty - if the output BAR should have confidential type +pub fn convert_abar2bar( + owner_sk: Option, + com: &str, + to: &XfrPublicKey, + confidential_am: bool, + confidential_ty: bool, + is_address_eth: bool, +) -> Result<()> { + let from = match owner_sk { + Some(str) => { + ruc::info!(serde_json::from_str::(&format!("\"{str}\""))) + .c(d!())? + .into_keypair() + } + None => get_keypair(is_address_eth).c(d!())?, + }; + // Get the owned ABAR from pub_key and commitment + let com = wallet::commitment_from_base58(com).c(d!())?; + let axtxo_abar = utils::get_owned_abar(&com).c(d!())?; + + // get OwnerMemo and Merkle Proof of ABAR + let owner_memo = utils::get_abar_memo(&axtxo_abar.0).c(d!())?.unwrap(); + let mt_leaf_info = utils::get_abar_proof(&axtxo_abar.0).c(d!())?.unwrap(); + let mt_leaf_uid = mt_leaf_info.uid; + + // Open ABAR with OwnerMemo & attach merkle proof + let oabar_in = OpenAnonAssetRecordBuilder::from_abar( + &axtxo_abar.1, + owner_memo, + &from.into_noah(), + ) + .unwrap() + .mt_leaf_info(mt_leaf_info) + .build() + .unwrap(); + + // check oabar is unspent. If already spent return error + // create nullifier + let n = nullify( + &from.into_noah(), + oabar_in.get_amount(), + oabar_in.get_asset_type().as_scalar(), + mt_leaf_uid, + ) + .c(d!())?; + let hash = wallet::nullifier_to_base58(&n.0); + // check if hash is present in nullifier set + let null_status = utils::check_nullifier_hash(&hash) + .c(d!())? + .ok_or(d!("The ABAR corresponding to this commitment is missing"))?; + if null_status { + return Err(eg!( + "The ABAR corresponding to this commitment is already spent" + )); + } + println!("Nullifier: {}", wallet::nullifier_to_base58(&n.0)); + + // Create New AssetRecordType for new BAR + let art = match (confidential_am, confidential_ty) { + (true, true) => AssetRecordType::ConfidentialAmount_ConfidentialAssetType, + (true, false) => AssetRecordType::ConfidentialAmount_NonConfidentialAssetType, + (false, true) => AssetRecordType::NonConfidentialAmount_ConfidentialAssetType, + _ => AssetRecordType::NonConfidentialAmount_NonConfidentialAssetType, + }; + + // Build AbarToBar Transaction and submit + utils::generate_abar2bar_op(&oabar_in, &from, to, art).c(d!())?; + + Ok(()) +} + +/// Generate OABAR and add anonymous transfer operation +/// # Arguments +/// * axfr_secret_key - AXfrKeyPair in base64 form +/// * com - Commitment in base64 form +/// * com_fra - Commitment for paying fee +/// * amount - amount to transfer +/// * to_axfr_public_key - AXfrPublicKey in base64 form +pub fn gen_anon_transfer_op( + owner_sk: Option, + com: &str, + com_fra: Option<&str>, + amount: &str, + to_address: &str, + is_address_eth: bool, +) -> Result<()> { + // parse sender keys + // parse sender XfrSecretKey or generate from Mnemonic setup with wallet + let from = match owner_sk { + Some(str) => { + ruc::info!(serde_json::from_str::(&format!("\"{str}\""))) + .c(d!())? + .into_keypair() + } + None => get_keypair(is_address_eth).c(d!())?, + }; + let axfr_amount = amount.parse::().c(d!("error parsing amount"))?; + + let to = wallet::public_key_from_bech32(to_address) + .c(d!("invalid 'to-xfr-public-key'"))?; + + let mut commitments = vec![com]; + if let Some(fra) = com_fra { + commitments.push(fra); + } + let mut inputs = vec![]; + // For each commitment add input to transfer operation + for com in commitments { + let c = wallet::commitment_from_base58(com).c(d!())?; + + // get unspent ABARs & their Merkle proof for commitment + let axtxo_abar = utils::get_owned_abar(&c).c(d!())?; + let owner_memo = utils::get_abar_memo(&axtxo_abar.0).c(d!())?.unwrap(); + let mt_leaf_info = utils::get_abar_proof(&axtxo_abar.0).c(d!())?.unwrap(); + let mt_leaf_uid = mt_leaf_info.uid; + + // Create Open ABAR from input information + let oabar_in = OpenAnonAssetRecordBuilder::from_abar( + &axtxo_abar.1, + owner_memo, + &from.into_noah(), + ) + .unwrap() + .mt_leaf_info(mt_leaf_info) + .build() + .unwrap(); + + // check oabar is unspent. + let n = nullify( + &from.into_noah(), + oabar_in.get_amount(), + oabar_in.get_asset_type().as_scalar(), + mt_leaf_uid, + ) + .c(d!())?; + let hash = wallet::nullifier_to_base58(&n.0); + let null_status = utils::check_nullifier_hash(&hash).c(d!())?.ok_or(d!( + "The ABAR corresponding to this commitment is missing {}", + com + ))?; + if null_status { + return Err(eg!( + "The ABAR corresponding to this commitment is already spent {}", + com + )); + } + + println!("Nullifier: {}", wallet::nullifier_to_base58(&n.0)); + inputs.push(oabar_in); + } + + // build output + let mut prng = ChaChaRng::from_entropy(); + let oabar_out = OpenAnonAssetRecordBuilder::new() + .amount(axfr_amount) + .asset_type(inputs[0].get_asset_type()) + .pub_key(&to.into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(); + + let mut builder: TransactionBuilder = new_tx_builder().c(d!())?; + let (_, note, rem_oabars) = builder + .add_operation_anon_transfer_fees_remainder(&inputs, &[oabar_out], &from) + .c(d!())?; + + send_tx(&builder.build_and_take_transaction()?).c(d!())?; + + let com_out = if !note.body.outputs.is_empty() { + Some(note.body.outputs[0].commitment) + } else { + None + }; + + if let Some(com) = com_out { + println!( + "\x1b[31;01m Commitment: {}\x1b[00m", + wallet::commitment_to_base58(&com) + ); + + // Append receiver's commitment to `sent_commitment` file + let mut file = fs::OpenOptions::new() + .append(true) + .create(true) + .open("sent_commitments") + .expect("cannot open commitments file"); + std::io::Write::write_all( + &mut file, + ("\n".to_owned() + &wallet::commitment_to_base58(&com)).as_bytes(), + ) + .expect("commitment write failed"); + } + + // Append sender's fee balance commitment to `owned_commitments` file + let mut file = fs::OpenOptions::new() + .append(true) + .create(true) + .open("owned_commitments") + .expect("cannot open commitments file"); + for rem_oabar in rem_oabars.iter() { + let c = get_abar_commitment(rem_oabar.clone()); + println!( + "\x1b[31;01m Remainder Commitment: {}\x1b[00m", + wallet::commitment_to_base58(&c) + ); + + std::io::Write::write_all( + &mut file, + ("\n".to_owned() + &wallet::commitment_to_base58(&c)).as_bytes(), + ) + .expect("commitment write failed"); + } + + println!("AxfrNote: {:?}", serde_json::to_string_pretty(¬e.body)); + Ok(()) +} + +/// Batch anon transfer - Generate OABAR and add anonymous transfer operation +/// Note - if multiple anon keys are used, we consider the last key in the list for remainder. +/// # Arguments +/// * axfr_secret_key - list of secret keys for senders' ABAR UTXOs +/// * to_axfr_public_keys - receiver AXfr Public keys +/// * to_enc_keys - List of receiver Encryption keys +/// * commitments - List of sender commitments in base64 format +/// * amounts - List of receiver amounts +/// * assets - List of receiver Asset Types +/// returns an error if Operation build fails +pub fn gen_oabar_add_op_x( + owner_sk: Option, + to_axfr_public_keys: Vec, + commitments: Vec, + amounts: Vec, + assets: Vec, + is_address_eth: bool, +) -> Result<()> { + let from = match owner_sk { + Some(str) => { + ruc::info!(serde_json::from_str::(&format!("\"{str}\""))) + .c(d!())? + .into_keypair() + } + None => get_keypair(is_address_eth).c(d!())?, + }; + let receiver_count = to_axfr_public_keys.len(); + + // check if input counts tally + if receiver_count != amounts.len() || receiver_count != assets.len() { + return Err(eg!( + "The Parameters: from-sk/dec-keys/commitments or to-pk/to-enc-keys not match!" + )); + } + + // Create Input Open Abars with input keys, radomizers and Owner memos + let mut oabars_in = Vec::new(); + for comm in commitments { + let c = wallet::commitment_from_base58(comm.as_str()).c(d!())?; + + // Get OwnerMemo + let axtxo_abar = utils::get_owned_abar(&c).c(d!())?; + let owner_memo = utils::get_abar_memo(&axtxo_abar.0).c(d!(comm))?.unwrap(); + // Get Merkle Proof + let mt_leaf_info = utils::get_abar_proof(&axtxo_abar.0).c(d!())?.unwrap(); + let mt_leaf_uid = mt_leaf_info.uid; + + // Build Abar + let oabar_in = OpenAnonAssetRecordBuilder::from_abar( + &axtxo_abar.1, + owner_memo, + &from.into_noah(), + ) + .unwrap() + .mt_leaf_info(mt_leaf_info) + .build() + .unwrap(); + + // check oabar is unspent. + let n = nullify( + &from.into_noah(), + oabar_in.get_amount(), + oabar_in.get_asset_type().as_scalar(), + mt_leaf_uid, + ) + .c(d!())?; + let hash = wallet::nullifier_to_base58(&n.0); + let null_status = utils::check_nullifier_hash(&hash) + .c(d!())? + .ok_or(d!("The ABAR corresponding to this commitment is missing"))?; + if null_status { + return Err(eg!( + "The ABAR corresponding to this commitment is already spent" + )); + } + println!("Nullifier: {}", wallet::nullifier_to_base58(&n.0)); + + oabars_in.push(oabar_in); + } + + // Create output Open ABARs + let mut oabars_out = Vec::new(); + for i in 0..receiver_count { + let mut prng = ChaChaRng::from_entropy(); + let to = to_axfr_public_keys[i]; + let axfr_amount = amounts[i].parse::().c(d!("error parsing amount"))?; + let asset_type = assets[i]; + + let oabar_out = OpenAnonAssetRecordBuilder::new() + .amount(axfr_amount) + .asset_type(asset_type.val) + .pub_key(&to.into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(); + + oabars_out.push(oabar_out); + } + + // Add a output for fees balance + let mut builder: TransactionBuilder = new_tx_builder().c(d!())?; + let (_, note, rem_oabars) = builder + .add_operation_anon_transfer_fees_remainder( + &oabars_in[..], + &oabars_out[..], + &from, + ) + .c(d!())?; + + // Send the transaction to the network + send_tx(&builder.build_and_take_transaction()?).c(d!())?; + + // Append receiver's commitment to `sent_commitments` file + let mut s_file = fs::OpenOptions::new() + .append(true) + .create(true) + .open("sent_commitments") + .expect("cannot open commitments file"); + for oabar_out in oabars_out { + let c_out = get_abar_commitment(oabar_out); + println!( + "\x1b[31;01m Commitment: {}\x1b[00m", + wallet::commitment_to_base58(&c_out) + ); + + std::io::Write::write_all( + &mut s_file, + ("\n".to_owned() + &wallet::commitment_to_base58(&c_out)).as_bytes(), + ) + .expect("commitment write failed"); + } + + let mut o_file = fs::OpenOptions::new() + .append(true) + .create(true) + .open("owned_commitments") + .expect("cannot open commitments file"); + for rem_oabar in rem_oabars.iter() { + let c_rem = get_abar_commitment(rem_oabar.clone()); + + println!( + "\x1b[31;01m Remainder Commitment: {}\x1b[00m", + wallet::commitment_to_base58(&c_rem) + ); + std::io::Write::write_all( + &mut o_file, + ("\n".to_owned() + &wallet::commitment_to_base58(&c_rem)).as_bytes(), + ) + .expect("commitment write failed"); + } + + println!("AxfrNote: {:?}", serde_json::to_string_pretty(¬e.body)); + Ok(()) +} + +/// Get merkle proof - Generate MTLeafInfo from ATxoSID +pub fn get_mtleaf_info(atxo_sid: &str) -> Result { + let asid = atxo_sid.parse::().c(d!("error parsing ATxoSID"))?; + let mt_leaf_info = utils::get_abar_proof(&ATxoSID(asid)) + .c(d!("error fetching abar proof"))? + .unwrap(); + Ok(mt_leaf_info) +} + +/// Fetches list of owned TxoSIDs from LedgerStatus +pub fn get_owned_utxos( + asset: Option<&str>, + is_address_eth: bool, +) -> Result> { + // get KeyPair from current setup wallet + let kp = get_keypair(is_address_eth).c(d!())?; + + // Parse Asset Type for filtering if provided + let mut asset_type = ASSET_TYPE_FRA; + if let Some(a) = asset { + asset_type = if a.to_uppercase() == "FRA" { + ASSET_TYPE_FRA + } else { + AssetTypeCode::new_from_base64(asset.unwrap()).unwrap().val + }; + } + + let list: Vec<(TxoSID, XfrAmount, XfrAssetType)> = + utils::get_owned_utxos(&kp.pub_key)? + .iter() + .filter(|a| { + // Filter by asset type if given or read all + if asset.is_none() { + true + } else { + match a.1.clone().0 .0.record.asset_type { + XfrAssetType::Confidential(_) => false, + XfrAssetType::NonConfidential(x) => asset_type == x, + } + } + }) + .map(|a| { + let record = a.1.clone().0 .0.record; + (*a.0, record.amount, record.asset_type) + }) + .collect(); + + Ok(list) +} + +/// Check the spending status of an ABAR from AnonKeys and commitment +pub fn check_abar_status( + from: XfrKeyPair, + axtxo_abar: (ATxoSID, AnonAssetRecord), +) -> Result<()> { + let owner_memo = utils::get_abar_memo(&axtxo_abar.0).c(d!())?.unwrap(); + let mt_leaf_info = utils::get_abar_proof(&axtxo_abar.0).c(d!())?.unwrap(); + let mt_leaf_uid = mt_leaf_info.uid; + + let oabar = OpenAnonAssetRecordBuilder::from_abar( + &axtxo_abar.1, + owner_memo, + &from.into_noah(), + ) + .unwrap() + .mt_leaf_info(mt_leaf_info) + .build() + .unwrap(); + + let n = nullify( + &from.into_noah(), + oabar.get_amount(), + oabar.get_asset_type().as_scalar(), + mt_leaf_uid, + ) + .c(d!())?; + let hash = wallet::nullifier_to_base58(&n.0); + let null_status = utils::check_nullifier_hash(&hash).c(d!())?.unwrap(); + if null_status { + println!("The ABAR corresponding to this commitment is already spent"); + } else { + println!("The ABAR corresponding to this commitment is unspent and has a balance {:?}", oabar.get_amount()); + } + Ok(()) +} + +/// Prints a dainty list of Abar info with spent status for a given AxfrKeyPair and a list of +/// commitments. +pub fn get_owned_abars( + axfr_secret_key: XfrKeyPair, + commitments_list: &str, +) -> Result<()> { + println!("Abar data for commitments: {commitments_list}",); + println!(); + println!( + "{0: <8} | {1: <18} | {2: <45} | {3: <9} | {4: <45}", + "ATxoSID", "Amount", "AssetType", "IsSpent", "Commitment" + ); + println!("{:-^1$}", "", 184); + commitments_list + .split(',') + .try_for_each(|com| -> ruc::Result<()> { + let commitment = wallet::commitment_from_base58(com).c(d!())?; + let (sid, abar) = utils::get_owned_abar(&commitment).c(d!())?; + let memo = utils::get_abar_memo(&sid).unwrap().unwrap(); + let oabar = OpenAnonAssetRecordBuilder::from_abar( + &abar, + memo, + &axfr_secret_key.into_noah(), + ) + .unwrap() + .build() + .unwrap(); + + let n = nullify( + &axfr_secret_key.into_noah(), + oabar.get_amount(), + oabar.get_asset_type().as_scalar(), + sid.0, + ) + .c(d!())?; + let hash = wallet::nullifier_to_base58(&n.0); + let null_status = utils::check_nullifier_hash(&hash).c(d!())?.unwrap(); + println!( + "{0: <8} | {1: <18} | {2: <45} | {3: <9} | {4: <45}", + sid.0, + oabar.get_amount(), + AssetTypeCode { + val: oabar.get_asset_type() + } + .to_base64(), + null_status, + com + ); + + Ok(()) + })?; + + Ok(()) +} + +/// Prints a dainty list of Abar info with spent status for a given AxfrKeyPair and a list of +/// commitments. +pub fn anon_balance( + axfr_secret_key: XfrKeyPair, + commitments_list: &str, + asset: Option<&str>, +) -> Result<()> { + // Parse Asset Type for filtering if provided + let mut asset_type = ASSET_TYPE_FRA; + if let Some(a) = asset { + asset_type = if a.to_uppercase() == "FRA" { + ASSET_TYPE_FRA + } else { + AssetTypeCode::new_from_base64(asset.unwrap()).unwrap().val + }; + } + + let mut balance = 0u64; + commitments_list + .split(',') + .try_for_each(|com| -> ruc::Result<()> { + let commitment = wallet::commitment_from_base58(com).c(d!())?; + + let result = utils::get_owned_abar(&commitment); + match result { + Err(e) => { + if e.msg_eq(eg!("missing abar").as_ref()) { + Ok(()) + } else { + Err(e) + } + } + Ok((sid, abar)) => { + let memo = utils::get_abar_memo(&sid).unwrap().unwrap(); + let oabar = OpenAnonAssetRecordBuilder::from_abar( + &abar, + memo, + &axfr_secret_key.into_noah(), + ) + .unwrap() + .build() + .unwrap(); + + let n = nullify( + &axfr_secret_key.into_noah(), + oabar.get_amount(), + oabar.get_asset_type().as_scalar(), + sid.0, + ) + .c(d!())?; + let hash = wallet::nullifier_to_base58(&n.0); + let is_spent = utils::check_nullifier_hash(&hash).c(d!())?.unwrap(); + if !is_spent && oabar.get_asset_type() == asset_type { + balance += oabar.get_amount(); + } + + Ok(()) + } + } + })?; + + println!("{}: {}", asset.unwrap_or("FRA"), balance); + Ok(()) +} + /// Return the built version. pub fn version() -> &'static str { concat!(env!("VERGEN_SHA"), " ", env!("VERGEN_BUILD_DATE")) } ///operation to replace the staker. -pub fn replace_staker(target_addr: fp_types::H160, td_addr: &str) -> Result<()> { +pub fn replace_staker( + target_addr: fp_types::H160, + td_addr: &str, + is_address_eth: bool, +) -> Result<()> { let td_addr = hex::decode(td_addr).c(d!())?; - let keypair = get_keypair()?; + let keypair = get_keypair(is_address_eth)?; let mut builder = utils::new_tx_builder().c(d!())?; @@ -848,7 +1550,7 @@ pub fn replace_staker(target_addr: fp_types::H160, td_addr: &str) -> Result<()> })?; builder.add_operation_replace_staker(&keypair, target_addr, td_addr)?; - let mut tx = builder.take_transaction(); + let mut tx = builder.build_and_take_transaction()?; tx.sign_to_map(&keypair); utils::send_tx(&tx).c(d!())?; diff --git a/src/components/finutils/src/common/utils.rs b/src/components/finutils/src/common/utils.rs index 9b0dfc99a..0d794aa8a 100644 --- a/src/components/finutils/src/common/utils.rs +++ b/src/components/finutils/src/common/utils.rs @@ -11,9 +11,9 @@ use { globutils::{wallet, HashOf, SignatureOf}, ledger::{ data_model::{ - AssetType, AssetTypeCode, DefineAsset, Operation, StateCommitmentData, - Transaction, TransferType, TxoRef, TxoSID, Utxo, ASSET_TYPE_FRA, - BLACK_HOLE_PUBKEY, TX_FEE_MIN, + ABARData, ATxoSID, AssetType, AssetTypeCode, DefineAsset, Operation, + StateCommitmentData, Transaction, TransferType, TxoRef, TxoSID, Utxo, + ASSET_TYPE_FRA, BAR_TO_ABAR_TX_FEE_MIN, BLACK_HOLE_PUBKEY, TX_FEE_MIN, }, staking::{ init::get_inital_validators, StakerMemo, TendermintAddrRef, FRA_TOTAL_AMOUNT, @@ -35,10 +35,18 @@ use { types::{BlockId, BlockNumber, Bytes, CallRequest, H160}, Web3, }, - zei::xfr::{ - asset_record::{open_blind_asset_record, AssetRecordType}, - sig::{XfrKeyPair, XfrPublicKey}, - structs::{AssetRecordTemplate, OwnerMemo}, + zei::{ + noah_api::{ + anon_xfr::structs::{ + AnonAssetRecord, AxfrOwnerMemo, Commitment, MTLeafInfo, + OpenAnonAssetRecord, + }, + xfr::{ + asset_record::{open_blind_asset_record, AssetRecordType}, + structs::{AssetRecordTemplate, OpenAssetRecord, OwnerMemo}, + }, + }, + BlindAssetRecord, XfrKeyPair, XfrPublicKey, }, }; @@ -81,7 +89,17 @@ pub fn set_initial_validators() -> Result<()> { let vs = get_inital_validators().c(d!())?; builder.add_operation_update_validator(&[], 1, vs).c(d!())?; - send_tx(&builder.take_transaction()).c(d!()) + send_tx(&builder.build_and_take_transaction()?).c(d!()) +} + +///load the tendermint key from the `priv_validator_key.json` file. +pub fn load_tendermint_priv_validator_key( + key_path: impl AsRef, +) -> Result { + let k = + std::fs::read_to_string(key_path).c(d!("can not read key file from path"))?; + let v_keys = parse_td_validator_keys(&k).c(d!())?; + Ok(v_keys) } #[inline(always)] @@ -102,7 +120,7 @@ pub fn transfer( } transfer_batch( owner_kp, - vec![(target_pk, am)], + vec![(*target_pk, am)], token_code, confidential_am, confidential_ty, @@ -114,7 +132,7 @@ pub fn transfer( #[allow(missing_docs)] pub fn transfer_batch( owner_kp: &XfrKeyPair, - target_list: Vec<(&XfrPublicKey, u64)>, + target_list: Vec<(XfrPublicKey, u64)>, token_code: Option, confidential_am: bool, confidential_ty: bool, @@ -131,7 +149,7 @@ pub fn transfer_batch( .c(d!())?; builder.add_operation(op); - let mut tx = builder.take_transaction(); + let mut tx = builder.build_and_take_transaction()?; tx.sign_to_map(owner_kp); send_tx(&tx).c(d!()) @@ -142,7 +160,7 @@ pub fn transfer_batch( #[inline(always)] pub fn gen_transfer_op( owner_kp: &XfrKeyPair, - target_list: Vec<(&XfrPublicKey, u64)>, + target_list: Vec<(XfrPublicKey, u64)>, token_code: Option, confidential_am: bool, confidential_ty: bool, @@ -163,7 +181,7 @@ pub fn gen_transfer_op( #[allow(missing_docs)] pub fn gen_transfer_op_x( owner_kp: &XfrKeyPair, - target_list: Vec<(&XfrPublicKey, u64)>, + target_list: Vec<(XfrPublicKey, u64)>, token_code: Option, auto_fee: bool, confidential_am: bool, @@ -188,7 +206,7 @@ pub fn gen_transfer_op_x( pub fn gen_transfer_op_xx( rpc_endpoint: Option<&str>, owner_kp: &XfrKeyPair, - mut target_list: Vec<(&XfrPublicKey, u64)>, + mut target_list: Vec<(XfrPublicKey, u64)>, token_code: Option, auto_fee: bool, confidential_am: bool, @@ -197,7 +215,7 @@ pub fn gen_transfer_op_xx( ) -> Result { let mut op_fee: u64 = 0; if auto_fee { - target_list.push((&*BLACK_HOLE_PUBKEY, TX_FEE_MIN)); + target_list.push((XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY), TX_FEE_MIN)); op_fee += TX_FEE_MIN; } let asset_type = token_code.map(|code| code.val).unwrap_or(ASSET_TYPE_FRA); @@ -217,8 +235,12 @@ pub fn gen_transfer_op_xx( .into_iter(); for (sid, (utxo, owner_memo)) in utxos { - let oar = - open_blind_asset_record(&utxo.0.record, &owner_memo, owner_kp).c(d!())?; + let oar = open_blind_asset_record( + &utxo.0.record.into_noah(), + &owner_memo, + &owner_kp.into_noah(), + ) + .c(d!())?; if oar.asset_type != asset_type && oar.asset_type != ASSET_TYPE_FRA { continue; @@ -278,7 +300,7 @@ pub fn gen_transfer_op_xx( n, token_code.map(|code| code.val).unwrap_or(ASSET_TYPE_FRA), art, - *pk, + pk.into_noah(), ) }); @@ -306,6 +328,92 @@ pub fn gen_fee_op(owner_kp: &XfrKeyPair) -> Result { gen_transfer_op(owner_kp, vec![], None, false, false, None).c(d!()) } +/// fee for bar to abar conversion +#[inline(always)] +pub fn gen_fee_bar_to_abar( + owner_kp: &XfrKeyPair, + avoid_input: TxoSID, +) -> Result { + let mut op_fee: u64 = BAR_TO_ABAR_TX_FEE_MIN; + let mut trans_builder = TransferOperationBuilder::new(); + trans_builder + .add_output( + &AssetRecordTemplate::with_no_asset_tracing( + BAR_TO_ABAR_TX_FEE_MIN, + ASSET_TYPE_FRA, + AssetRecordType::NonConfidentialAmount_NonConfidentialAssetType, + *BLACK_HOLE_PUBKEY, + ), + None, + None, + None, + ) + .c(d!())?; + + let utxos = get_owned_utxos(owner_kp.get_pk_ref()).c(d!())?.into_iter(); + for (sid, (utxo, owner_memo)) in utxos { + let oar = open_blind_asset_record( + &utxo.0.record.into_noah(), + &owner_memo, + &owner_kp.into_noah(), + ) + .c(d!())?; + + if op_fee == 0 { + break; + } + if oar.asset_type == ASSET_TYPE_FRA + && oar.get_record_type() + == AssetRecordType::NonConfidentialAmount_NonConfidentialAssetType + && op_fee != 0 + && sid != avoid_input + { + let i_am = oar.amount; + if oar.amount <= op_fee { + op_fee -= i_am; + + trans_builder + .add_input(TxoRef::Absolute(sid), oar, None, None, i_am) + .c(d!())?; + } else { + trans_builder + .add_input(TxoRef::Absolute(sid), oar, None, None, i_am) + .c(d!())?; + + trans_builder + .add_output( + &AssetRecordTemplate::with_no_asset_tracing( + i_am - op_fee, + ASSET_TYPE_FRA, + AssetRecordType::NonConfidentialAmount_NonConfidentialAssetType, + owner_kp.pub_key.into_noah(), + ), + None, + None, + None, + ) + .c(d!())?; + + op_fee = 0; + } + } + } + + if op_fee != 0 { + return Err(eg!("Insufficient balance to pay Txn fees")); + } + + trans_builder + .balance(None) + .c(d!())? + .create(TransferType::Standard) + .c(d!())? + .sign(owner_kp) + .c(d!())? + .transaction() + .c(d!()) +} + ///////////////////////////////////////// // Part 2: utils for query infomations // ///////////////////////////////////////// @@ -468,9 +576,13 @@ pub fn get_asset_balance(kp: &XfrKeyPair, asset: Option) -> Resul .c(d!())? .values() .map(|(utxo, owner_memo)| { - open_blind_asset_record(&utxo.0.record, owner_memo, kp) - .c(d!()) - .map(|obr| alt!(obr.asset_type == asset_type, obr.amount, 0)) + open_blind_asset_record( + &utxo.0.record.into_noah(), + owner_memo, + &kp.into_noah(), + ) + .c(d!()) + .map(|obr| alt!(obr.asset_type == asset_type, obr.amount, 0)) }) .collect::>>() .c(d!())? @@ -488,7 +600,9 @@ pub fn get_asset_all(kp: &XfrKeyPair) -> Result> { let mut set = BTreeMap::new(); for (_k, v) in info { - let res = open_blind_asset_record(&v.0 .0.record, &v.1, kp)?; + let res = + open_blind_asset_record(&v.0 .0.record.into_noah(), &v.1, &kp.into_noah()) + .c(d!())?; let code = AssetTypeCode { val: res.asset_type, @@ -504,7 +618,8 @@ pub fn get_asset_all(kp: &XfrKeyPair) -> Result> { Ok(set) } -fn get_owned_utxos( +#[allow(missing_docs)] +pub fn get_owned_utxos( addr: &XfrPublicKey, ) -> Result)>> { get_owned_utxos_x(None, addr).c(d!()) @@ -534,6 +649,33 @@ fn get_owned_utxos_x( }) } +/// Return the ABAR by commitment. +pub fn get_owned_abar(com: &Commitment) -> Result<(ATxoSID, AnonAssetRecord)> { + let url = format!( + "{}:8668/owned_abars/{}", + get_serv_addr().c(d!())?, + wallet::commitment_to_base58(com) + ); + + attohttpc::get(url) + .send() + .c(d!())? + .error_for_status() + .c(d!())? + .bytes() + .c(d!()) + .and_then(|b| { + serde_json::from_slice::>(&b) + .c(d!())? + .ok_or(eg!("missing abar")) + }) + .and_then(|(sid, data)| { + wallet::commitment_from_base58(&data.commitment) + .map(|commitment| (sid, AnonAssetRecord { commitment })) + .map_err(|_| eg!("commitment invalid")) + }) +} + #[inline(always)] fn get_seq_id() -> Result { type Resp = ( @@ -579,6 +721,61 @@ pub fn get_owner_memo_batch(ids: &[TxoSID]) -> Result>> { .and_then(|b| serde_json::from_slice(&b).c(d!())) } +#[inline(always)] +#[allow(missing_docs)] +pub fn get_abar_memo(id: &ATxoSID) -> Result> { + let id = id.0.to_string(); + let url = format!("{}:8667/get_abar_memo/{}", get_serv_addr().c(d!())?, id); + + attohttpc::get(url) + .send() + .c(d!())? + .error_for_status() + .c(d!())? + .bytes() + .c(d!()) + .and_then(|b| serde_json::from_slice(&b).c(d!())) +} + +#[inline(always)] +#[allow(missing_docs)] +pub fn get_abar_proof(atxo_sid: &ATxoSID) -> Result> { + let atxo_sid = atxo_sid.0.to_string(); + let url = format!( + "{}:8667/get_abar_proof/{}", + get_serv_addr().c(d!())?, + atxo_sid + ); + + attohttpc::get(url) + .send() + .c(d!())? + .error_for_status() + .c(d!())? + .bytes() + .c(d!()) + .and_then(|b| serde_json::from_slice(&b).c(d!())) +} + +#[inline(always)] +#[allow(missing_docs)] +pub fn check_nullifier_hash(null_hash: &str) -> 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!( @@ -745,3 +942,113 @@ pub fn get_validator_memo_and_rate( }; Ok((memo, rate)) } + +#[inline(always)] +/// Generates a BarToAbar Operation and an accompanying FeeOP and sends it to the network and return the Randomizer +/// # Arguments +/// * `auth_key_pair` - XfrKeyPair of the owner BAR for conversion +/// * `abar_pub_key` - AXfrPubKey of the receiver ABAR after conversion +/// * `txo_sid` - TxoSID of the BAR to convert +/// * `input_record` - OpenAssetRecord of the BAR to convert +/// * `is_bar_transparent` - if transparent bar (ar) +pub fn generate_bar2abar_op( + auth_key_pair: &XfrKeyPair, + abar_pub_key: &XfrPublicKey, + txo_sid: TxoSID, + input_record: &OpenAssetRecord, + is_bar_transparent: bool, +) -> Result { + // add operation bar_to_abar in a new Tx Builder + + let mut seed = [0u8; 32]; + + getrandom::getrandom(&mut seed).c(d!())?; + + let mut builder: TransactionBuilder = new_tx_builder().c(d!())?; + let (_, c) = builder + .add_operation_bar_to_abar( + seed, + auth_key_pair, + abar_pub_key, + txo_sid, + input_record, + is_bar_transparent, + ) + .c(d!("Failed to generate operation bar to abar"))?; + + // Add a transparent fee operation for conversion which is required to process the bar + // In this step a transparent FRA AssetRecord is chosen from user owned UTXOs to pay the fee. + // If the user doesn't own such a UTXO then this method throws an error. + let feeop = + gen_fee_bar_to_abar(auth_key_pair, txo_sid).c(d!("Failed to generate fee"))?; + builder.add_operation(feeop); + + let mut tx = builder.build_and_take_transaction()?; + + tx.sign(auth_key_pair); + + // submit transaction to network + send_tx(&tx).c(d!("Failed to submit Bar to Abar txn"))?; + + Ok(c) +} + +#[inline(always)] +/// Create AbarToBar transaction with given Open ABAR & Open Bar and submit it to network +/// # Arguments +/// * oabar_in - Abar to convert in open form +/// * fee_oabar - Abar to pay anon fee in open form +/// * out_fee_oabar - Abar to get balance back after paying fee +/// * from - AXfrKeyPair of person converting ABAR +/// * to - XfrPublicKey of person receiving new BAR +/// * art - AssetRecordType of the new BAR +pub fn generate_abar2bar_op( + oabar_in: &OpenAnonAssetRecord, + from: &XfrKeyPair, + to: &XfrPublicKey, + art: AssetRecordType, +) -> Result<()> { + let mut builder: TransactionBuilder = new_tx_builder().c(d!())?; + // create and add AbarToBar Operation + builder + .add_operation_abar_to_bar(oabar_in, from, to, art) + .c(d!())?; + + // submit transaction + send_tx(&builder.build_and_take_transaction()?).c(d!())?; + Ok(()) +} + +#[inline(always)] +#[allow(missing_docs)] +pub fn get_oar( + owner_kp: &XfrKeyPair, + txo_sid: TxoSID, +) -> Result<(OpenAssetRecord, BlindAssetRecord)> { + let utxos = get_owned_utxos(owner_kp.get_pk_ref()).c(d!())?.into_iter(); + + for (sid, (utxo, owner_memo)) in utxos { + if sid != txo_sid { + continue; + } + + let oar = open_blind_asset_record( + &utxo.0.record.into_noah(), + &owner_memo, + &owner_kp.into_noah(), + ) + .c(d!())?; + + return Ok((oar, utxo.0.record)); + } + + Err(eg!("utxo not found")) +} + +#[inline(always)] +#[allow(missing_docs)] +pub fn get_abar_data(abar: AnonAssetRecord) -> ABARData { + ABARData { + commitment: wallet::commitment_to_base58(&abar.commitment), + } +} diff --git a/src/components/finutils/src/txn_builder/mod.rs b/src/components/finutils/src/txn_builder/mod.rs index dc4e37c45..1bd6fc208 100644 --- a/src/components/finutils/src/txn_builder/mod.rs +++ b/src/components/finutils/src/txn_builder/mod.rs @@ -8,17 +8,19 @@ use { credentials::CredUserSecretKey, curve25519_dalek::scalar::Scalar, + digest::Digest, fp_types::{crypto::MultiSigner, H160}, - globutils::SignatureOf, + globutils::{wallet, Serialized, SignatureOf}, ledger::{ converter::ConvertAccount, data_model::{ - AssetRules, AssetTypeCode, ConfidentialMemo, DefineAsset, DefineAssetBody, - IndexedSignature, IssueAsset, IssueAssetBody, IssuerKeyPair, - IssuerPublicKey, Memo, NoReplayToken, Operation, Transaction, + get_abar_commitment, AbarConvNote, AbarToBarOps, AnonTransferOps, + AssetRules, AssetTypeCode, BarAnonConvNote, BarToAbarOps, ConfidentialMemo, + DefineAsset, DefineAssetBody, IndexedSignature, IssueAsset, IssueAssetBody, + IssuerKeyPair, IssuerPublicKey, Memo, NoReplayToken, Operation, Transaction, TransactionBody, TransferAsset, TransferAssetBody, TransferType, TxOutput, - TxoRef, UpdateMemo, UpdateMemoBody, ASSET_TYPE_FRA, BLACK_HOLE_PUBKEY, - TX_FEE_MIN, + TxoRef, TxoSID, UpdateMemo, UpdateMemoBody, ASSET_TYPE_FRA, + BAR_TO_ABAR_TX_FEE_MIN, BLACK_HOLE_PUBKEY, FEE_CALCULATING_FUNC, TX_FEE_MIN, }, staking::{ is_valid_tendermint_addr, @@ -37,33 +39,49 @@ use { }, }, rand_chacha::ChaChaRng, - rand_core::{CryptoRng, RngCore, SeedableRng}, + rand_core::SeedableRng, ruc::*, serde::{Deserialize, Serialize}, + sha2::Sha512, std::{ cmp::Ordering, - collections::{BTreeMap, HashSet}, + collections::{BTreeMap, HashMap, HashSet}, }, tendermint::PrivateKey, zei::{ - api::anon_creds::{ - ac_confidential_open_commitment, ACCommitment, ACCommitmentKey, - ConfidentialAC, Credential, - }, - serialization::ZeiFromToBytes, - setup::PublicParams, - xfr::{ - asset_record::{ - build_blind_asset_record, build_open_asset_record, - open_blind_asset_record, AssetRecordType, + noah_algebra::{prelude::*, ristretto::PedersenCommitmentRistretto}, + noah_api::{ + anon_creds::{ + ac_confidential_open_commitment, ACCommitment, ACCommitmentKey, + ConfidentialAC, Credential, + }, + anon_xfr::{ + abar_to_abar::{finish_anon_xfr_note, init_anon_xfr_note, AXfrPreNote}, + abar_to_ar::{ + finish_abar_to_ar_note, init_abar_to_ar_note, AbarToArPreNote, + }, + abar_to_bar::{ + finish_abar_to_bar_note, init_abar_to_bar_note, AbarToBarPreNote, + }, + ar_to_abar::gen_ar_to_abar_note, + bar_to_abar::gen_bar_to_abar_note, + structs::{Commitment, OpenAnonAssetRecord, OpenAnonAssetRecordBuilder}, }, - lib::XfrNotePolicies, - sig::{XfrKeyPair, XfrPublicKey}, - structs::{ - AssetRecord, AssetRecordTemplate, BlindAssetRecord, OpenAssetRecord, - OwnerMemo, TracingPolicies, TracingPolicy, + keys::SecretKey, + parameters::{AddressFormat, ProverParams}, + xfr::{ + asset_record::{ + build_blind_asset_record, build_open_asset_record, + open_blind_asset_record, AssetRecordType, + }, + structs::{ + AssetRecord, AssetRecordTemplate, AssetType, OpenAssetRecord, + TracingPolicies, TracingPolicy, + }, + XfrNotePolicies, }, }, + BlindAssetRecord, OwnerMemo, XfrKeyPair, XfrPublicKey, }, }; @@ -119,6 +137,12 @@ pub struct TransactionBuilder { outputs: u64, #[allow(missing_docs)] pub no_replay_token: NoReplayToken, + #[serde(skip)] + abar_bar_cache: Vec, + #[serde(skip)] + abar_ar_cache: Vec, + #[serde(skip)] + abar_abar_cache: Vec, } impl TransactionBuilder { @@ -142,7 +166,7 @@ impl TransactionBuilder { .outputs .iter() .zip($d.body.transfer.owners_memos.iter()) - .map(|(r, om)| (r.clone(), om.clone())) + .map(|(r, om)| (r.clone(), om.clone().map(|it| it))) .collect() }; } @@ -179,9 +203,13 @@ impl TransactionBuilder { let mut am = TX_FEE_MIN; for (idx, (o, om)) in outputs.into_iter().enumerate() { if 0 < am { - if let Ok(oar) = open_blind_asset_record(&o, &om, &kp) { + if let Ok(oar) = open_blind_asset_record( + &o.into_noah(), + &om.map(|o| o.into_noah()), + &kp.into_noah(), + ) { if ASSET_TYPE_FRA == oar.asset_type - && kp.get_pk_ref().as_bytes() == o.public_key.as_bytes() + && kp.get_pk_ref().to_bytes() == o.public_key.to_bytes() { let n = alt!(oar.amount > am, am, oar.amount); am = am.saturating_sub(oar.amount); @@ -214,14 +242,39 @@ impl TransactionBuilder { /// As the last operation of any transaction, /// add a static fee to the transaction. pub fn add_fee(&mut self, inputs: FeeInputs) -> Result<&mut TransactionBuilder> { + self.add_fee_custom(inputs, TX_FEE_MIN) + } + + /// As the last operation of bar_to_abar transaction, + /// add a static fee to the transaction. + pub fn add_fee_bar_to_abar( + &mut self, + inputs: FeeInputs, + ) -> Result<&mut TransactionBuilder> { + self.add_fee_custom(inputs, BAR_TO_ABAR_TX_FEE_MIN) + } + + /// As the last operation of any transaction, + /// add a custom static fee to the transaction. + pub fn add_fee_custom( + &mut self, + inputs: FeeInputs, + fee: u64, + ) -> Result<&mut TransactionBuilder> { let mut kps = vec![]; let mut opb = TransferOperationBuilder::default(); + let mut am = fee; for i in inputs.inner.into_iter() { - open_blind_asset_record(&i.ar.record, &i.om, &i.kp) + open_blind_asset_record(&i.ar.record.into_noah(), &i.om.map(|o| o.into_noah()), &i.kp.into_noah()) .c(d!()) .and_then(|oar| { - opb.add_input(i.tr, oar, None, None, i.am) + if oar.asset_type != ASSET_TYPE_FRA { + return Err(eg!("Incorrect fee input asset_type, expected Findora AssetType record")); + } + let n = alt!(oar.amount > am, am, oar.amount); + am = am.saturating_sub(oar.amount); + opb.add_input(i.tr, oar, None, None, n) .map(|_| { kps.push(i.kp); }) @@ -231,7 +284,7 @@ impl TransactionBuilder { opb.add_output( &AssetRecordTemplate::with_no_asset_tracing( - TX_FEE_MIN, + fee, ASSET_TYPE_FRA, AssetRecordType::from_flags(false, false), *BLACK_HOLE_PUBKEY, @@ -245,7 +298,7 @@ impl TransactionBuilder { .and_then(|o| o.create(TransferType::Standard).c(d!())) .and_then(|o| { let cmp = |a: &XfrKeyPair, b: &XfrKeyPair| { - a.get_pk().as_bytes().cmp(b.get_pk().as_bytes()) + a.get_pk().to_bytes().cmp(&b.get_pk().to_bytes()) }; kps.sort_by(cmp); kps.dedup_by(|a, b| matches!(cmp(a, b), Ordering::Equal)); @@ -265,8 +318,8 @@ impl TransactionBuilder { } #[allow(missing_docs)] - pub fn get_owner_memo_ref(&self, idx: usize) -> Option<&OwnerMemo> { - self.txn.get_owner_memos_ref()[idx] + pub fn get_owner_memo_ref(&self, idx: usize) -> Option { + self.txn.get_owner_memos_ref()[idx].clone() } #[allow(missing_docs)] @@ -281,6 +334,9 @@ impl TransactionBuilder { TransactionBuilder { txn: Transaction::from_seq_id(seq_id), outputs: 0, + abar_abar_cache: vec![], + abar_bar_cache: vec![], + abar_ar_cache: vec![], no_replay_token, } } @@ -300,18 +356,18 @@ impl TransactionBuilder { seq_num: u64, amount: u64, confidentiality_flags: AssetRecordType, - zei_params: &PublicParams, ) -> Result<&mut Self> { let mut prng = ChaChaRng::from_entropy(); let ar = AssetRecordTemplate::with_no_asset_tracing( amount, token_code.val, confidentiality_flags, - key_pair.get_pk(), + key_pair.get_pk().into_noah(), ); + let pc_gens = PedersenCommitmentRistretto::default(); let (ba, _, owner_memo) = - build_blind_asset_record(&mut prng, &zei_params.pc_gens, &ar, vec![]); + build_blind_asset_record(&mut prng, &pc_gens, &ar, vec![]); self.add_operation_issue_asset( key_pair, token_code, @@ -319,10 +375,10 @@ impl TransactionBuilder { &[( TxOutput { id: None, - record: ba, + record: BlindAssetRecord::from_noah(&ba), lien: None, }, - owner_memo, + owner_memo.map(|om| OwnerMemo::from_noah(&om).unwrap()), )], ) .c(d!()) @@ -338,6 +394,157 @@ impl TransactionBuilder { self.txn } + #[allow(missing_docs)] + pub fn build_and_take_transaction(&mut self) -> Result { + self.build()?; + Ok(self.txn.clone()) + } + + /// Build a transaction from various pre-notes of operations + pub fn build(&mut self) -> Result<()> { + let mut prng = ChaChaRng::from_entropy(); + + // hasher txn. (IMPORTANT! KEEP THE same order) + let mut hasher = Sha512::new(); + let mut bytes = self.txn.body.digest(); + for i in &self.abar_abar_cache { + bytes.extend_from_slice(Serialized::new(&i.body).as_ref()); + } + for i in &self.abar_bar_cache { + bytes.extend_from_slice(Serialized::new(&i.body).as_ref()); + } + for i in &self.abar_ar_cache { + bytes.extend_from_slice(Serialized::new(&i.body).as_ref()); + } + hasher.update(bytes.as_slice()); + + // finish abar to abar + if !self.abar_abar_cache.is_empty() { + let mut params: HashMap<(usize, usize, u8), ProverParams> = HashMap::new(); + for pre_note in &self.abar_abar_cache { + let (af, ak) = match pre_note.input_keypair.get_sk_ref() { + SecretKey::Secp256k1(_) => (AddressFormat::SECP256K1, 0), + SecretKey::Ed25519(_) => (AddressFormat::ED25519, 1), + }; + let key = (pre_note.body.inputs.len(), pre_note.body.outputs.len(), ak); + let param = if let Some(key) = params.get(&key) { + key + } else { + let param = + ProverParams::gen_abar_to_abar(key.0, key.1, af).c(d!())?; + params.insert(key, param); + params.get(&key).unwrap() // safe, checked. + }; + + let note = finish_anon_xfr_note( + &mut prng, + param, + pre_note.clone(), + hasher.clone(), + ) + .c(d!())?; + + // Add operation + let inp = AnonTransferOps::new(note, self.no_replay_token).c(d!())?; + let op = Operation::TransferAnonAsset(Box::new(inp)); + self.txn.add_operation(op); + } + } + + // finish abar to bar + if !self.abar_bar_cache.is_empty() { + let mut abar_bar_params = [None, None]; + for pre_note in &self.abar_bar_cache { + let params = match pre_note.input_keypair.get_sk_ref() { + SecretKey::Secp256k1(_) => { + if abar_bar_params[0].is_none() { + abar_bar_params[0] = Some( + ProverParams::gen_abar_to_bar(AddressFormat::SECP256K1) + .c(d!())?, + ); + } + abar_bar_params[0].as_ref() + } + SecretKey::Ed25519(_) => { + if abar_bar_params[1].is_none() { + abar_bar_params[1] = Some( + ProverParams::gen_abar_to_bar(AddressFormat::ED25519) + .c(d!())?, + ); + } + abar_bar_params[1].as_ref() + } + }; + + let note = finish_abar_to_bar_note( + &mut prng, + params.unwrap(), + pre_note.clone(), + hasher.clone(), + ) + .c(d!())?; + + // Create operation + let conv = AbarToBarOps::new( + AbarConvNote::AbarToBar(Box::new(note)), + self.no_replay_token, + ) + .c(d!())?; + let op = Operation::AbarToBar(Box::from(conv)); + + // Add operation to transaction + self.txn.add_operation(op); + } + } + + // finish abar to ar + if !self.abar_ar_cache.is_empty() { + let mut abar_ar_params = [None, None]; + for pre_note in &self.abar_ar_cache { + let params = match pre_note.input_keypair.get_sk_ref() { + SecretKey::Secp256k1(_) => { + if abar_ar_params[0].is_none() { + abar_ar_params[0] = Some( + ProverParams::gen_abar_to_ar(AddressFormat::SECP256K1) + .c(d!())?, + ); + } + abar_ar_params[0].as_ref() + } + SecretKey::Ed25519(_) => { + if abar_ar_params[1].is_none() { + abar_ar_params[1] = Some( + ProverParams::gen_abar_to_ar(AddressFormat::ED25519) + .c(d!())?, + ); + } + abar_ar_params[1].as_ref() + } + }; + let note = finish_abar_to_ar_note( + &mut prng, + params.unwrap(), + pre_note.clone(), + hasher.clone(), + ) + .c(d!())?; + + // Create operation + let conv = AbarToBarOps::new( + AbarConvNote::AbarToAr(Box::new(note)), + self.no_replay_token, + ) + .c(d!())?; + let op = Operation::AbarToBar(Box::from(conv)); + + // Add operation to transaction + self.txn.add_operation(op); + } + } + + Ok(()) + } + /// Append a transaction memo pub fn add_memo(&mut self, memo: Memo) -> &mut Self { self.txn.body.memos.push(memo); @@ -469,7 +676,216 @@ impl TransactionBuilder { self } - /// Add a operation to delegating findora account to a tendermint validator. + /// Add an operation to convert a Blind Asset Record to a Anonymous record and return the Commitment + /// # Arguments + /// * `auth_key_pair` - XfrKeyPair of the owner BAR for conversion + /// * `abar_pub_key` - AXfrPubKey of the receiver ABAR after conversion + /// * `txo_sid` - TxoSID of the BAR to convert + /// * `input_record` - OpenAssetRecord of the BAR to convert + /// * `enc_key` - XPublicKey of OwnerMemo encryption of receiver + /// * `is_bar_transparent` - if transparent bar (ar) + #[allow(clippy::too_many_arguments)] + pub fn add_operation_bar_to_abar( + &mut self, + seed: [u8; 32], + auth_key_pair: &XfrKeyPair, + abar_pub_key: &XfrPublicKey, + txo_sid: TxoSID, + input_record: &OpenAssetRecord, + is_bar_transparent: bool, + ) -> Result<(&mut Self, Commitment)> { + // generate the BarToAbarNote with the ZKP + let (note, c) = gen_bar_conv_note( + seed, + input_record, + auth_key_pair, + abar_pub_key, + is_bar_transparent, + ) + .c(d!())?; + + // Create the BarToAbarOps + let bar_to_abar = BarToAbarOps::new(note, txo_sid, self.no_replay_token)?; + + // Add the generated operation to the transaction + let op = Operation::BarToAbar(Box::from(bar_to_abar)); + self.txn.add_operation(op); + Ok((self, c)) + } + + /// Create a new operation to convert from Anonymous record to Blind Asset Record + /// # Arguments + /// * input - ABAR to be converted + /// * input_keypair - owner keypair of ABAR to be converted + /// * bar_pub_key - Pubkey of the receiver of the new BAR + /// * asset_record_type - The type of confidentiality of new BAR + pub fn add_operation_abar_to_bar( + &mut self, + input: &OpenAnonAssetRecord, + input_keypair: &XfrKeyPair, + bar_pub_key: &XfrPublicKey, + asset_record_type: AssetRecordType, + ) -> Result<&mut Self> { + let mut prng = ChaChaRng::from_entropy(); + match asset_record_type { + AssetRecordType::NonConfidentialAmount_NonConfidentialAssetType => { + let note = init_abar_to_ar_note( + &mut prng, + input, + &input_keypair.into_noah(), + &bar_pub_key.into_noah(), + ) + .c(d!())?; + self.abar_ar_cache.push(note); + } + _ => { + let note = init_abar_to_bar_note( + &mut prng, + input, + &input_keypair.into_noah(), + &bar_pub_key.into_noah(), + asset_record_type, + ) + .c(d!())?; + self.abar_bar_cache.push(note); + } + } + + Ok(self) + } + + /// Add an operation to transfer assets held in Anonymous Blind Asset Record. + /// Note - Input and output amounts should be balanced, including FRA implicit fee + /// Use op add_operation_anon_transfer_fees_remainder for automatic remainder and fee handling + /// # Arguments + /// * inputs - List of input ABARs to be used for the transfer + /// * outputs - List of output ABARs + /// * input_keypair - list of AXfrKeyPair of the sender + #[allow(dead_code)] + pub fn add_operation_anon_transfer( + &mut self, + inputs: &[OpenAnonAssetRecord], + outputs: &[OpenAnonAssetRecord], + input_keypair: &XfrKeyPair, + ) -> Result<(&mut Self, AXfrPreNote)> { + let fee = FEE_CALCULATING_FUNC(inputs.len() as u32, outputs.len() as u32); + + // generate anon transfer note + let note = init_anon_xfr_note(inputs, outputs, fee, &input_keypair.into_noah()) + .c(d!())?; + self.abar_abar_cache.push(note.clone()); + + Ok((self, note)) + } + + /// Create an operation for ABAR transfer and generates remainder abars for balance amounts + /// # Arguments + /// * inputs - List of input ABARs to be used for the transfer + /// * outputs - List of output ABARs + /// * input_keypair - list of AXfrKeyPair of the sender + /// * enc_key - The encryption key of the sender to send the remainder abar + pub fn add_operation_anon_transfer_fees_remainder( + &mut self, + inputs: &[OpenAnonAssetRecord], + outputs: &[OpenAnonAssetRecord], + input_keypair: &XfrKeyPair, + ) -> Result<(&mut Self, AXfrPreNote, Vec)> { + let mut prng = ChaChaRng::from_entropy(); + + let mut vec_outputs = outputs.to_vec(); + let mut vec_changes = vec![]; + let mut remainders = HashMap::new(); + let remainder_pk = input_keypair.get_pk(); + + // Create a remainders hashmap with remainder amount for each asset type + for input in inputs { + // add each input amount to the asset type entry + remainders + .entry(input.get_asset_type()) + .and_modify(|rem| *rem += input.get_amount() as i64) + .or_insert(input.get_amount() as i64); + } + for output in outputs { + // subtract each output amount from the asset type entry + remainders + .entry(output.get_asset_type()) + .and_modify(|rem| *rem -= output.get_amount() as i64) + .or_insert(-(output.get_amount() as i64)); + } + + // Check if at least one input is of FRA asset type + let fra_rem = remainders.remove(&ASSET_TYPE_FRA); + if fra_rem.is_none() { + return Err(eg!("Must include an FRA ABAR to pay FEE!")); + } + + // Create remainder OABARs for non-FRA asset types + for (asset_type, remainder) in remainders { + println!( + "Transaction Asset: {:?} Remainder Amount: {:?}", + base64::encode(&asset_type.0), + remainder + ); + + if remainder < 0 { + return Err(eg!("Transfer Asset token input less than output!")); + } + + if remainder > 0 { + let oabar_money_back = OpenAnonAssetRecordBuilder::new() + .amount(remainder as u64) + .asset_type(asset_type) + .pub_key(&remainder_pk.into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(); + + // Add the oabar to list of outputs and a list of new oabars created + vec_outputs.push(oabar_money_back.clone()); + vec_changes.push(oabar_money_back); + } + } + + // Calculate implicit fees that will get deducted and subtract from FRA remainder + let fees = + FEE_CALCULATING_FUNC(inputs.len() as u32, vec_outputs.len() as u32 + 1); + let fra_remainder = fra_rem.unwrap() - (fees as i64); // safe. checked. + if fra_remainder < 0 { + return Err(eg!("insufficient FRA to pay fees!")); + } + if fra_remainder > 0 { + println!("Transaction FRA Remainder Amount: {fra_remainder:?}",); + let oabar_money_back = OpenAnonAssetRecordBuilder::new() + .amount(fra_remainder as u64) + .asset_type(ASSET_TYPE_FRA) + .pub_key(&remainder_pk.into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(); + + // Add FRA remainder oabar to outputs + vec_outputs.push(oabar_money_back.clone()); + vec_changes.push(oabar_money_back); + } + + if vec_outputs.len() > 5 { + return Err(eg!( + "Total outputs (incl. remainders) cannot be greater than 5" + )); + } + + let note = + init_anon_xfr_note(inputs, &vec_outputs, fees, &input_keypair.into_noah()) + .c(d!())?; + self.abar_abar_cache.push(note.clone()); + + // return a list of all new remainder abars generated + Ok((self, note, vec_changes)) + } + + /// Add a operation to delegating finddra accmount to a tendermint validator. /// The transfer operation to BLACK_HOLE_PUBKEY_STAKING should be sent along with. pub fn add_operation_delegation( &mut self, @@ -741,10 +1157,10 @@ pub(crate) fn build_record_and_get_blinds( } }; // 2. Use record template and ciphertexts to build open asset record - let params = PublicParams::default(); + let pc_gens = PedersenCommitmentRistretto::default(); let (open_asset_record, asset_tracing_memos, owner_memo) = build_open_asset_record( prng, - ¶ms.pc_gens, + &pc_gens, template, vec![attr_ctext.unwrap_or_default()], ); @@ -772,6 +1188,49 @@ pub(crate) fn build_record_and_get_blinds( )) } +fn gen_bar_conv_note( + seed: [u8; 32], + input_record: &OpenAssetRecord, + auth_key_pair: &XfrKeyPair, + abar_pub_key: &XfrPublicKey, + is_bar_transparent: bool, +) -> Result<(BarAnonConvNote, Commitment)> { + // let mut prng = ChaChaRng::from_entropy(); + let mut prng = ChaChaRng::from_seed(seed); + + if is_bar_transparent { + let prover_params = ProverParams::gen_ar_to_abar().c(d!())?; + + // generate the BarToAbarNote with the ZKP + let note = gen_ar_to_abar_note( + &mut prng, + &prover_params, + input_record, + &auth_key_pair.into_noah(), + &abar_pub_key.into_noah(), + ) + .c(d!())?; + + let c = note.body.output.commitment; + Ok((BarAnonConvNote::ArNote(Box::new(note)), c)) + } else { + // generate params for Bar to Abar conversion + let prover_params = ProverParams::gen_bar_to_abar().c(d!())?; + + // generate the BarToAbarNote with the ZKP + let note = gen_bar_to_abar_note( + &mut prng, + &prover_params, + input_record, + &auth_key_pair.into_noah(), + &abar_pub_key.into_noah(), + ) + .c(d!())?; + let c = note.body.output.commitment; + Ok((BarAnonConvNote::BarNote(Box::new(note)), c)) + } +} + /// TransferOperationBuilder constructs transfer operations using the factory pattern /// Inputs and outputs are added iteratively before being signed by all input record owners #[derive(Clone, Serialize, Deserialize, Default)] @@ -941,7 +1400,6 @@ impl TransferOperationBuilder { } // Check if outputs and inputs are balanced - fn check_balance(&self) -> Result<()> { let input_total: u64 = self .input_records @@ -1129,11 +1587,11 @@ impl TransferOperationBuilder { if !sig.verify(&trn.body) { return Err(eg!(("Invalid signature"))); } - sig_keys.insert(sig.address.key.zei_to_bytes()); + sig_keys.insert(sig.address.key.noah_to_bytes()); } for record in &trn.body.transfer.inputs { - if !sig_keys.contains(&record.public_key.zei_to_bytes()) { + if !sig_keys.contains(&record.public_key.noah_to_bytes()) { return Err(eg!(("Not all signatures present"))); } } @@ -1141,19 +1599,439 @@ impl TransferOperationBuilder { } } +/// AnonTransferOperationBuilder builders anon transfer operation using the factory pattern. +/// This is used for the wasm interface in building a multi-input/output anon transfer operation. +#[derive(Default)] +pub struct AnonTransferOperationBuilder { + inputs: Vec, + outputs: Vec, + keypair: Option, + pre_note: Option, + commitments: Vec, + + nonce: NoReplayToken, + txn: Transaction, +} + +impl AnonTransferOperationBuilder { + /// default returns a fresh default builder + pub fn new_from_seq_id(seq_id: u64) -> Self { + let mut prng = ChaChaRng::from_entropy(); + let no_replay_token = NoReplayToken::new(&mut prng, seq_id); + + AnonTransferOperationBuilder { + inputs: Vec::default(), + outputs: Vec::default(), + keypair: None, + pre_note: None, + commitments: Vec::default(), + nonce: no_replay_token, + txn: Transaction::from_seq_id(seq_id), + } + } + + /// add_input is used for adding an input source to the Anon Transfer Operation factory, it takes + /// an ABAR and a Keypair as input + pub fn add_input(&mut self, abar: OpenAnonAssetRecord) -> Result<&mut Self> { + if self.inputs.len() >= 5 { + return Err(eg!("Total inputs (incl. fees) cannot be greater than 5")); + } + self.inputs.push(abar); + Ok(self) + } + + /// add_output is used to add a output record to the Anon Transfer factory + pub fn add_output(&mut self, abar: OpenAnonAssetRecord) -> Result<&mut Self> { + if self.outputs.len() >= 5 { + return Err(eg!( + "Total outputs (incl. remainders) cannot be greater than 5" + )); + } + self.commitments.push(get_abar_commitment(abar.clone())); + self.outputs.push(abar); + Ok(self) + } + + /// add_keypair is used to specify the input keypair for nullifier generation + pub fn add_keypair(&mut self, keypair: XfrKeyPair) -> &mut Self { + self.keypair = Some(keypair); + self + } + + #[allow(missing_docs)] + pub fn extra_fee_estimation(&self) -> Result { + if self.inputs.len() > 5 { + return Err(eg!("Total inputs (incl. fees) cannot be greater than 5")); + } + + let input_sums = + self.inputs + .iter() + .fold(HashMap::new(), |mut asset_amounts, i| { + let sum = asset_amounts.entry(i.get_asset_type()).or_insert(0u64); + *sum += i.get_amount(); + asset_amounts + }); + let output_sums = + self.outputs + .iter() + .fold(HashMap::new(), |mut asset_amounts, o| { + let sum = asset_amounts.entry(o.get_asset_type()).or_insert(0u64); + *sum += o.get_amount(); + asset_amounts + }); + + let fra_input_sum = *input_sums.get(&ASSET_TYPE_FRA).unwrap_or(&0u64); + let fra_output_sum = *output_sums.get(&ASSET_TYPE_FRA).unwrap_or(&0u64); + if output_sums.len() > input_sums.len() { + return Err(eg!("Output assets cannot be more than input assets")); + } + + let extra_remainders = input_sums.iter().try_fold( + 0usize, + |extra_remainders, (asset, inp_amount)| { + if *asset == ASSET_TYPE_FRA { + Ok(extra_remainders) + } else { + match output_sums.get(asset) { + None => Ok(extra_remainders + 1), + Some(op_sum) => match op_sum.cmp(inp_amount) { + Ordering::Equal => Ok(extra_remainders), + Ordering::Less => Ok(extra_remainders + 1), + Ordering::Greater => { + Err(eg!("Output cannot be greater than Input")) + } + }, + } + } + }, + )?; + + let estimated_fees_without_extra_fra_ip_op = FEE_CALCULATING_FUNC( + self.inputs.len() as u32, + (self.outputs.len() + extra_remainders) as u32, + ) as u64; + + let fra_extra_output_sum = + fra_output_sum + estimated_fees_without_extra_fra_ip_op; + match fra_input_sum.cmp(&fra_extra_output_sum) { + Ordering::Equal => Ok(0u64), + Ordering::Greater => { + let estimated_fees_with_fra_remainder = FEE_CALCULATING_FUNC( + self.inputs.len() as u32, + (self.outputs.len() + extra_remainders + 1) as u32, + ) as u64; + + if fra_input_sum >= (fra_output_sum + estimated_fees_with_fra_remainder) + { + Ok(0u64) + } else { + let estimated_fees_with_extra_input_and_remainder = + FEE_CALCULATING_FUNC( + (self.inputs.len() + 1) as u32, + (self.outputs.len() + extra_remainders + 1) as u32, + ) as u64; + + Ok( + fra_output_sum + estimated_fees_with_extra_input_and_remainder + - fra_input_sum, + ) + } + } + Ordering::Less => { + // case where input is insufficient + let estimated_fees_with_extra_input_and_remainder = FEE_CALCULATING_FUNC( + (self.inputs.len() + 1) as u32, + (self.outputs.len() + extra_remainders + 1) as u32, + ) + as u64; + Ok( + fra_output_sum + estimated_fees_with_extra_input_and_remainder + - fra_input_sum, + ) + } + } + } + + #[allow(missing_docs)] + pub fn get_total_fee_estimation(&self) -> Result { + let input_sums = + self.inputs + .iter() + .fold(HashMap::new(), |mut asset_amounts, i| { + let sum = asset_amounts.entry(i.get_asset_type()).or_insert(0u64); + *sum += i.get_amount(); + asset_amounts + }); + let output_sums = + self.outputs + .iter() + .fold(HashMap::new(), |mut asset_amounts, o| { + let sum = asset_amounts.entry(o.get_asset_type()).or_insert(0u64); + *sum += o.get_amount(); + asset_amounts + }); + + let fra_input_sum = *input_sums.get(&ASSET_TYPE_FRA).unwrap_or(&0u64); + let fra_output_sum = *output_sums.get(&ASSET_TYPE_FRA).unwrap_or(&0u64); + if output_sums.len() > input_sums.len() { + return Err(eg!("Output assets cannot be more than input assets")); + } + + let extra_remainders = input_sums.iter().try_fold( + 0usize, + |extra_remainders, (asset, inp_amount)| { + if *asset == ASSET_TYPE_FRA { + Ok(extra_remainders) + } else { + match output_sums.get(asset) { + None => Ok(extra_remainders + 1), + Some(op_sum) => match op_sum.cmp(inp_amount) { + Ordering::Equal => Ok(extra_remainders), + Ordering::Less => Ok(extra_remainders + 1), + Ordering::Greater => { + Err(eg!("Output cannot be greater than Input")) + } + }, + } + } + }, + )?; + + let estimated_fees_without_extra_fra_ip_op = FEE_CALCULATING_FUNC( + self.inputs.len() as u32, + (self.outputs.len() + extra_remainders) as u32, + ) as u64; + + let fra_extra_output_sum = + fra_output_sum + estimated_fees_without_extra_fra_ip_op; + match fra_input_sum.cmp(&fra_extra_output_sum) { + Ordering::Equal => Ok(fra_input_sum - fra_output_sum), + Ordering::Greater => { + let estimated_fees_with_fra_remainder = FEE_CALCULATING_FUNC( + self.inputs.len() as u32, + (self.outputs.len() + extra_remainders + 1) as u32, + ) as u64; + + if fra_input_sum >= (fra_output_sum + estimated_fees_with_fra_remainder) + { + Ok(estimated_fees_with_fra_remainder) + } else { + let estimated_fees_with_extra_input_and_remainder = + FEE_CALCULATING_FUNC( + (self.inputs.len() + 1) as u32, + (self.outputs.len() + extra_remainders + 1) as u32, + ) as u64; + + Ok(estimated_fees_with_extra_input_and_remainder) + } + } + Ordering::Less => { + // case where input is insufficient + let estimated_fees_with_extra_input_and_remainder = FEE_CALCULATING_FUNC( + (self.inputs.len() + 1) as u32, + (self.outputs.len() + extra_remainders + 1) as u32, + ) + as u64; + Ok(estimated_fees_with_extra_input_and_remainder) + } + } + } + + /// get_commitments fetches the commitments for the different outputs. + pub fn get_commitments(&self) -> Vec { + self.commitments.clone() + } + + /// get a hashmap of commitment, public key, asset, amount + pub fn get_commitment_map(&self) -> HashMap { + let mut commitment_map = HashMap::new(); + for out_abar in self.outputs.iter() { + let abar_rand = + wallet::commitment_to_base58(&get_abar_commitment(out_abar.clone())); + let abar_pkey = *out_abar.pub_key_ref(); + let abar_asset = out_abar.get_asset_type(); + let abar_amt = out_abar.get_amount(); + commitment_map.insert( + abar_rand, + (XfrPublicKey::from_noah(&abar_pkey), abar_asset, abar_amt), + ); + } + commitment_map + } + + /// build generates the anon transfer body with the Zero Knowledge Proof. + pub fn build(&mut self) -> Result<&mut Self> { + if self.inputs.len() > 5 { + return Err(eg!("Total inputs (incl. fees) cannot be greater than 5")); + } + if self.outputs.len() > 5 { + return Err(eg!( + "Total outputs (incl. remainders) cannot be greater than 5" + )); + } + + if self.keypair.is_none() { + return Err(eg!("keypair not set for build")); + } + let keypair = self.keypair.as_ref().unwrap(); + + let mut prng = ChaChaRng::from_entropy(); + let input_asset_list: HashSet = self + .inputs + .iter() + .map(|a| a.get_asset_type()) + .collect::>() + .drain(..) + .collect(); + let mut fees_in_fra = 0u32; + + for asset in input_asset_list { + let mut sum_input = 0; + let mut sum_output = 0; + for input in self.inputs.clone() { + if asset == input.get_asset_type() { + sum_input += input.get_amount(); + } + } + for output in self.outputs.clone() { + if asset == output.get_asset_type() { + sum_output += output.get_amount(); + } + } + + let fees = if asset == ASSET_TYPE_FRA { + fees_in_fra = FEE_CALCULATING_FUNC( + self.inputs.len() as u32, + self.outputs.len() as u32, + ); + fees_in_fra as u64 + } else { + 0u64 + }; + + if sum_output + fees > sum_input { + return if asset == ASSET_TYPE_FRA { + Err(eg!( + "Insufficient FRA balance to pay fees {} + {} > {}", + sum_output, + fees, + sum_input + )) + } else { + Err(eg!( + "Insufficient {:?} balance to pay fees {} + {} > {}", + asset, + sum_output, + fees, + sum_input + )) + }; + } + + let remainder = sum_input - sum_output - fees; + if remainder > 0 { + let oabar_money_back = OpenAnonAssetRecordBuilder::new() + .amount(remainder) + .asset_type(asset) + .pub_key(&keypair.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(); + + let commitment = get_abar_commitment(oabar_money_back.clone()); + self.outputs.push(oabar_money_back); + self.commitments.push(commitment); + } + } + + if self.outputs.len() > 5 { + return Err(eg!( + "Total outputs (incl. remainders) cannot be greater than 5" + )); + } + let note = init_anon_xfr_note( + self.inputs.as_slice(), + self.outputs.as_slice(), + fees_in_fra, + &keypair.into_noah(), + ) + .c(d!("error from init_anon_xfr_note"))?; + + self.pre_note = Some(note); + + Ok(self) + } + + /// Add operation to the transaction + pub fn build_txn(&mut self) -> Result<()> { + let mut prng = ChaChaRng::from_entropy(); + let pre_note = self.pre_note.clone().unwrap(); + let af = match pre_note.input_keypair.get_sk_ref() { + SecretKey::Secp256k1(_) => AddressFormat::SECP256K1, + SecretKey::Ed25519(_) => AddressFormat::ED25519, + }; + let param = + ProverParams::gen_abar_to_abar(self.inputs.len(), self.outputs.len(), af) + .c(d!())?; + + let mut hasher = Sha512::new(); + + let mut bytes = self.txn.body.digest(); + bytes.extend_from_slice(Serialized::new(&pre_note.body).as_ref()); + hasher.update(bytes); + + let note = finish_anon_xfr_note(&mut prng, ¶m, pre_note, hasher).c(d!())?; + + // Add operation + let inp = AnonTransferOps::new(note, self.nonce).c(d!())?; + let op = Operation::TransferAnonAsset(Box::new(inp)); + self.txn.add_operation(op); + + Ok(()) + } + + /// Calculates the Anon fee given the number of inputs and outputs + pub fn get_anon_fee(n_inputs: u32, n_outputs: u32) -> u32 { + FEE_CALCULATING_FUNC(n_inputs, n_outputs) + } + + /// transaction method wraps the anon transfer note in an Operation and returns it + pub fn serialize_str(&self) -> Result { + if self.pre_note.is_none() { + return Err(eg!("Anon transfer not built and signed")); + } + // Unwrap is safe because the underlying transaction is guaranteed to be serializable. + serde_json::to_string(&self.txn).c(d!()) + } +} + #[cfg(test)] #[allow(missing_docs)] mod tests { use { super::*, - ledger::data_model::{TxnEffect, TxoRef}, - ledger::store::{utils::fra_gen_initial_tx, LedgerState}, + ledger::{ + data_model::{ATxoSID, BlockEffect, TxnEffect, TxoRef}, + store::utils::fra_gen_initial_tx, + store::LedgerState, + }, rand_chacha::ChaChaRng, rand_core::SeedableRng, - zei::setup::PublicParams, - zei::xfr::asset_record::AssetRecordType::NonConfidentialAmount_NonConfidentialAssetType, - zei::xfr::asset_record::{build_blind_asset_record, open_blind_asset_record}, - zei::xfr::sig::XfrKeyPair, + zei::{ + noah_algebra::ristretto::PedersenCommitmentRistretto, + noah_api::{ + anon_xfr::structs::{AnonAssetRecord, OpenAnonAssetRecordBuilder}, + xfr::{ + asset_record::{ + build_blind_asset_record, open_blind_asset_record, + AssetRecordType::NonConfidentialAmount_NonConfidentialAssetType, + }, + structs::AssetType as AT, + }, + }, + }, }; // Defines an asset type @@ -1183,7 +2061,7 @@ mod tests { fn test_transfer_op_builder_inner() -> Result<()> { let mut prng = ChaChaRng::from_entropy(); - let params = PublicParams::default(); + let pc_gens = PedersenCommitmentRistretto::default(); let code_1 = AssetTypeCode::gen_random(); let code_2 = AssetTypeCode::gen_random(); let alice = XfrKeyPair::generate(&mut prng); @@ -1195,18 +2073,18 @@ mod tests { 1000, code_1.val, NonConfidentialAmount_NonConfidentialAssetType, - alice.get_pk(), + alice.get_pk().into_noah(), ); let ar_2 = AssetRecordTemplate::with_no_asset_tracing( 1000, code_2.val, NonConfidentialAmount_NonConfidentialAssetType, - bob.get_pk(), + bob.get_pk().into_noah(), ); let (ba_1, _, memo1) = - build_blind_asset_record(&mut prng, ¶ms.pc_gens, &ar_1, vec![]); + build_blind_asset_record(&mut prng, &pc_gens, &ar_1, vec![]); let (ba_2, _, memo2) = - build_blind_asset_record(&mut prng, ¶ms.pc_gens, &ar_2, vec![]); + build_blind_asset_record(&mut prng, &pc_gens, &ar_2, vec![]); // Attempt to spend too much let mut invalid_outputs_transfer_op = TransferOperationBuilder::new(); @@ -1214,12 +2092,12 @@ mod tests { 25, code_1.val, NonConfidentialAmount_NonConfidentialAssetType, - bob.get_pk(), + bob.get_pk().into_noah(), ); let res = invalid_outputs_transfer_op .add_input( TxoRef::Relative(1), - open_blind_asset_record(&ba_1, &memo1, &alice).c(d!())?, + open_blind_asset_record(&ba_1, &memo1, &alice.into_noah()).c(d!())?, None, None, 20, @@ -1237,12 +2115,12 @@ mod tests { 20, code_1.val, NonConfidentialAmount_NonConfidentialAssetType, - bob.get_pk(), + bob.get_pk().into_noah(), ); let res = invalid_sig_op .add_input( TxoRef::Relative(1), - open_blind_asset_record(&ba_1, &memo1, &alice).c(d!())?, + open_blind_asset_record(&ba_1, &memo1, &alice.into_noah()).c(d!())?, None, None, 20, @@ -1265,12 +2143,12 @@ mod tests { 20, code_1.val, NonConfidentialAmount_NonConfidentialAssetType, - bob.get_pk(), + bob.get_pk().into_noah(), ); let res = missing_sig_op .add_input( TxoRef::Relative(1), - open_blind_asset_record(&ba_1, &memo1, &alice).c(d!())?, + open_blind_asset_record(&ba_1, &memo1, &alice.into_noah()).c(d!())?, None, None, 20, @@ -1291,42 +2169,42 @@ mod tests { 5, code_1.val, NonConfidentialAmount_NonConfidentialAssetType, - bob.get_pk(), + bob.get_pk().into_noah(), ); let output_charlie13_code1_template = AssetRecordTemplate::with_no_asset_tracing( 13, code_1.val, NonConfidentialAmount_NonConfidentialAssetType, - charlie.get_pk(), + charlie.get_pk().into_noah(), ); let output_ben2_code1_template = AssetRecordTemplate::with_no_asset_tracing( 2, code_1.val, NonConfidentialAmount_NonConfidentialAssetType, - ben.get_pk(), + ben.get_pk().into_noah(), ); let output_bob5_code2_template = AssetRecordTemplate::with_no_asset_tracing( 5, code_2.val, NonConfidentialAmount_NonConfidentialAssetType, - bob.get_pk(), + bob.get_pk().into_noah(), ); let output_charlie13_code2_template = AssetRecordTemplate::with_no_asset_tracing( 13, code_2.val, NonConfidentialAmount_NonConfidentialAssetType, - charlie.get_pk(), + charlie.get_pk().into_noah(), ); let output_ben2_code2_template = AssetRecordTemplate::with_no_asset_tracing( 2, code_2.val, NonConfidentialAmount_NonConfidentialAssetType, - ben.get_pk(), + ben.get_pk().into_noah(), ); let _valid_transfer_op = TransferOperationBuilder::new() .add_input( TxoRef::Relative(1), - open_blind_asset_record(&ba_1, &memo1, &alice).c(d!())?, + open_blind_asset_record(&ba_1, &memo1, &alice.into_noah()).c(d!())?, None, None, 20, @@ -1334,7 +2212,7 @@ mod tests { .c(d!())? .add_input( TxoRef::Relative(2), - open_blind_asset_record(&ba_2, &memo2, &bob).c(d!())?, + open_blind_asset_record(&ba_2, &memo2, &bob.into_noah()).c(d!())?, None, None, 20, @@ -1371,8 +2249,8 @@ mod tests { let fra_owner_kp = XfrKeyPair::generate(&mut ChaChaRng::from_entropy()); let bob_kp = XfrKeyPair::generate(&mut ChaChaRng::from_entropy()); assert_eq!( - bob_kp.get_sk().into_keypair().zei_to_bytes(), - bob_kp.zei_to_bytes() + bob_kp.get_sk().into_keypair().noah_to_bytes(), + bob_kp.noah_to_bytes() ); let mut tx = fra_gen_initial_tx(&fra_owner_kp); @@ -1400,9 +2278,15 @@ mod tests { .add_input( TxoRef::Absolute($txo_sid), open_blind_asset_record( - &ledger.get_utxo_light($txo_sid).unwrap().utxo.0.record, + &ledger + .get_utxo_light($txo_sid) + .unwrap() + .utxo + .0 + .record + .into_noah(), &None, - &fra_owner_kp, + &fra_owner_kp.into_noah(), ) .unwrap(), None, @@ -1424,7 +2308,7 @@ mod tests { } let mut tx2 = TransactionBuilder::from_seq_id(1); - tx2.add_operation(transfer_to_bob!(txo_sid, bob_kp.get_pk())) + tx2.add_operation(transfer_to_bob!(txo_sid, bob_kp.get_pk().into_noah())) .add_fee_relative_auto(&fra_owner_kp) .unwrap(); assert!(tx2.check_fee()); @@ -1449,12 +2333,12 @@ mod tests { TX_FEE_MIN, TxoRef::Absolute(txo_sid[0]), utxo.utxo.0, - utxo.txn.txn.get_owner_memos_ref()[utxo.utxo_location.0].cloned(), + utxo.txn.txn.get_owner_memos_ref()[utxo.utxo_location.0].clone(), bob_kp.get_sk().into_keypair(), ); let mut tx3 = TransactionBuilder::from_seq_id(2); pnk!(tx3 - .add_operation(transfer_to_bob!(txo_sid[2], bob_kp.get_pk())) + .add_operation(transfer_to_bob!(txo_sid[2], bob_kp.get_pk().into_noah())) .add_fee(fi)); assert!(tx3.check_fee()); @@ -1479,11 +2363,11 @@ mod tests { TX_FEE_MIN, TxoRef::Absolute(txo_sid[0]), utxo.utxo.0, - utxo.txn.txn.get_owner_memos_ref()[utxo.utxo_location.0].cloned(), + utxo.txn.txn.get_owner_memos_ref()[utxo.utxo_location.0].clone(), bob_kp.get_sk().into_keypair(), ); let mut tx4 = TransactionBuilder::from_seq_id(3); - tx4.add_operation(transfer_to_bob!(txo_sid[1], bob_kp.get_pk())) + tx4.add_operation(transfer_to_bob!(txo_sid[1], bob_kp.get_pk().into_noah())) .add_fee(fi) .unwrap(); assert!(tx4.check_fee()); @@ -1500,4 +2384,557 @@ mod tests { let mut block = ledger.start_block().unwrap(); assert!(ledger.apply_transaction(&mut block, effect).is_err()); } + + #[test] + fn test_operation_bar_to_abar() { + let mut builder = TransactionBuilder::from_seq_id(1); + + let mut prng = ChaChaRng::from_seed([0u8; 32]); + let from = XfrKeyPair::generate(&mut prng); + let to = XfrKeyPair::generate(&mut prng).get_pk(); + + let ar = AssetRecordTemplate::with_no_asset_tracing( + 10u64, + AT([1u8; 32]), + AssetRecordType::ConfidentialAmount_ConfidentialAssetType, + from.get_pk().into_noah(), + ); + let pc_gens = PedersenCommitmentRistretto::default(); + let (bar, _, memo) = build_blind_asset_record(&mut prng, &pc_gens, &ar, vec![]); + let dummy_input = + open_blind_asset_record(&bar, &memo, &from.into_noah()).unwrap(); + + let mut seed = [0u8; 32]; + + getrandom::getrandom(&mut seed).unwrap(); + + let _ = builder + .add_operation_bar_to_abar( + seed, + &from, + &to, + TxoSID(123), + &dummy_input, + false, + ) + .is_ok(); + + let txn = builder.build_and_take_transaction().unwrap(); + + if let Operation::BarToAbar(note) = txn.body.operations[0].clone() { + let result = note.verify(); + assert!(result.is_ok()); + } + } + + #[test] + // This test calls multiple functions used in the anonymous transfer workflow + fn test_axfr_workflow() { + let mut prng = ChaChaRng::from_seed([0u8; 32]); + let amount = 6000000u64; + //let fee_amount = FEE_CALCULATING_FUNC(2, 1) as u64; + let amount_output = 1000000u64; + let asset_type = ASSET_TYPE_FRA; + + // simulate input abar + let (mut oabar, keypair_in) = gen_oabar_and_keys(&mut prng, amount, asset_type); + let abar = AnonAssetRecord::from_oabar(&oabar); + let _test_owner_memo = oabar.get_owner_memo().unwrap(); + + // simulate output abar + let (oabar_out, _keypair_out) = + gen_oabar_and_keys(&mut prng, amount_output, asset_type); + + // initialize ledger and add abars to merkle tree + let mut ledger_state = LedgerState::tmp_ledger(); + let _ledger_status = ledger_state.get_status(); + let uid = ledger_state.add_abar(&abar).unwrap(); + ledger_state.compute_and_append_txns_hash(&BlockEffect::default()); + ledger_state.compute_and_save_state_commitment_data(1); + + let mt_leaf_info = ledger_state.get_abar_proof(uid).unwrap(); + oabar.update_mt_leaf_info(mt_leaf_info); + + // build and submit transaction + let vec_inputs = vec![oabar]; + let vec_outputs = vec![oabar_out]; + + let mut builder = TransactionBuilder::from_seq_id(1); + let result = builder.add_operation_anon_transfer_fees_remainder( + &vec_inputs, + &vec_outputs, + &keypair_in, + ); + assert!(result.is_ok()); + + // post transaction steps test + let txn = builder.build_and_take_transaction().unwrap(); + let compute_effect = TxnEffect::compute_effect(txn).unwrap(); + let mut block = BlockEffect::default(); + let block_result = block.add_txn_effect(compute_effect); + assert!(block_result.is_ok()); + + // test nullifier functions + for n in block.new_nullifiers.iter() { + let _str = wallet::nullifier_to_base58(&n); + } + + let txn_sid_result = ledger_state.finish_block(block); + assert!(txn_sid_result.is_ok()); + } + + // Negative tests added + #[test] + #[ignore] + fn axfr_create_verify_unit_with_negative_tests() { + let mut prng = ChaChaRng::from_seed([0u8; 32]); + let amount = 10u64; + // Here the Asset Type is generated as a 32 byte and each of them are zero + let asset_type = AT::from_identical_byte(0); + + // simulate input abar + let (oabar, keypair_in) = gen_oabar_and_keys(&mut prng, amount, asset_type); + // simulate another oabar just to get new keypair + let (_, another_keypair) = gen_oabar_and_keys(&mut prng, amount, asset_type); + // negative test for input keypairs + assert_eq!(keypair_in.get_pk().into_noah(), *oabar.pub_key_ref()); + assert_ne!(keypair_in.get_pk(), another_keypair.get_pk()); + assert_ne!(another_keypair.get_pk().into_noah(), *oabar.pub_key_ref()); + + // Simulate output abar + let (oabar_out, _keypair_out) = + gen_oabar_and_keys(&mut prng, amount, asset_type); + let _abar_out = AnonAssetRecord::from_oabar(&oabar_out); + let mut builder = TransactionBuilder::from_seq_id(1); + + let wrong_key_result = builder.add_operation_anon_transfer( + &[oabar], + &[oabar_out], + &another_keypair, + ); + // negative test for keys + assert!(wrong_key_result.is_err()); + + // negative test for asset type + let wrong_asset_type_out = AT::from_identical_byte(1); + let (oabar, keypair_in) = gen_oabar_and_keys(&mut prng, amount, asset_type); + let (oabar_out, _keypair_out) = + gen_oabar_and_keys(&mut prng, amount, wrong_asset_type_out); + let wrong_asset_type_result = + builder.add_operation_anon_transfer(&[oabar], &[oabar_out], &keypair_in); + // Here we have an error due to the asset type input being unequal to the asset type output + assert!(wrong_asset_type_result.is_err()); + + // The happy path + let (mut oabar, keypair_in) = gen_oabar_and_keys(&mut prng, amount, asset_type); + let (oabar_out, _keypair_out) = + gen_oabar_and_keys(&mut prng, amount, asset_type); + let abar = AnonAssetRecord::from_oabar(&oabar); + + let owner_memo = oabar.get_owner_memo().unwrap(); + + // Trying to decrypt asset type and amount from owner memo using wrong keys + let new_xfrkeys = XfrKeyPair::generate(&mut prng); + let result_decrypt = owner_memo.decrypt(&new_xfrkeys.get_sk().into_noah()); + assert!(result_decrypt.is_err()); + + // initialize ledger and add abar to merkle tree + let mut ledger_state = LedgerState::tmp_ledger(); + let _ledger_status = ledger_state.get_status(); + let uid = ledger_state.add_abar(&abar).unwrap(); + ledger_state.compute_and_append_txns_hash(&BlockEffect::default()); + ledger_state.compute_and_save_state_commitment_data(1); + + // negative test for merkle tree proof + let mt_leaf_result_fail = ledger_state.get_abar_proof(ATxoSID(100u64)); + // 100 is not a valid uid, so we will catch an error + assert!(mt_leaf_result_fail.is_err()); + + let mt_leaf_info = ledger_state.get_abar_proof(uid).unwrap(); + oabar.update_mt_leaf_info(mt_leaf_info); + + let result = + builder.add_operation_anon_transfer(&[oabar], &[oabar_out], &keypair_in); + assert!(result.is_ok()); + + let txn = builder.build_and_take_transaction().unwrap(); + let compute_effect = TxnEffect::compute_effect(txn).unwrap(); + let mut block = BlockEffect::default(); + let block_result = block.add_txn_effect(compute_effect); + assert!(block_result.is_ok()); + + for n in block.new_nullifiers.iter() { + let _str = wallet::nullifier_to_base58(&n); + } + + let txn_sid_result = ledger_state.finish_block(block); + assert!(txn_sid_result.is_ok()); + } + + fn gen_oabar_and_keys( + prng: &mut R, + amount: u64, + asset_type: AT, + ) -> (OpenAnonAssetRecord, XfrKeyPair) { + let keypair = XfrKeyPair::generate(prng); + let oabar = OpenAnonAssetRecordBuilder::new() + .amount(amount) + .asset_type(asset_type) + .pub_key(&keypair.get_pk().into_noah()) + .finalize(prng) + .unwrap() + .build() + .unwrap(); + (oabar, keypair) + } + + #[test] + pub fn test_extra_fee_estimation_only_fra() { + let mut prng = ChaChaRng::from_seed([0u8; 32]); + let k = XfrKeyPair::generate(&mut prng); + { + let mut b = AnonTransferOperationBuilder::new_from_seq_id(0); + + let _ = b.add_input( + OpenAnonAssetRecordBuilder::new() + .amount(1000000) + .asset_type(ASSET_TYPE_FRA) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + let _fee = FEE_CALCULATING_FUNC(1, 1) as u64; + assert!(b.extra_fee_estimation().is_ok()); + assert_eq!(b.extra_fee_estimation().unwrap(), 0); + } + { + let mut b = AnonTransferOperationBuilder::new_from_seq_id(0); + + let _ = b.add_input( + OpenAnonAssetRecordBuilder::new() + .amount(1000000) + .asset_type(ASSET_TYPE_FRA) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + + let _ = b.add_output( + OpenAnonAssetRecordBuilder::new() + .amount(1000000) + .asset_type(ASSET_TYPE_FRA) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + + let fee = FEE_CALCULATING_FUNC(2, 2) as u64; + assert!(b.extra_fee_estimation().is_ok()); + assert_eq!(b.extra_fee_estimation().unwrap(), fee); + } + { + // case with perfect balance for fee + let mut b = AnonTransferOperationBuilder::new_from_seq_id(0); + + let _ = b.add_input( + OpenAnonAssetRecordBuilder::new() + .amount(1000000) + .asset_type(ASSET_TYPE_FRA) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + + let _ = b.add_output( + OpenAnonAssetRecordBuilder::new() + .amount(200000) + .asset_type(ASSET_TYPE_FRA) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + + assert!(b.extra_fee_estimation().is_ok()); + assert_eq!(b.extra_fee_estimation().unwrap(), 0); + } + { + // case where input is insufficient + let mut b = AnonTransferOperationBuilder::new_from_seq_id(0); + + let _ = b.add_input( + OpenAnonAssetRecordBuilder::new() + .amount(10) + .asset_type(ASSET_TYPE_FRA) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + let _ = b.add_output( + OpenAnonAssetRecordBuilder::new() + .amount(200000) + .asset_type(ASSET_TYPE_FRA) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + + let extra_fra = FEE_CALCULATING_FUNC(2, 2) as u64 + 200000 - 10; + assert!(b.extra_fee_estimation().is_ok()); + assert_eq!(b.extra_fee_estimation().unwrap(), extra_fra); + } + } + + #[test] + pub fn test_extra_fee_estimation_multi_asset() { + let mut prng = ChaChaRng::from_seed([0u8; 32]); + let k = XfrKeyPair::generate(&mut prng); + + let asset1 = AT::from_identical_byte(1u8); + let _asset2 = AT::from_identical_byte(2u8); + + { + let mut b = AnonTransferOperationBuilder::new_from_seq_id(0); + + let _ = b.add_input( + OpenAnonAssetRecordBuilder::new() + .amount(1000000) + .asset_type(asset1) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + let extra_fra = FEE_CALCULATING_FUNC(2, 2) as u64; + assert!(b.extra_fee_estimation().is_ok()); + assert_eq!(b.extra_fee_estimation().unwrap(), extra_fra); + } + { + let mut b = AnonTransferOperationBuilder::new_from_seq_id(0); + + let _ = b.add_input( + OpenAnonAssetRecordBuilder::new() + .amount(1000000) + .asset_type(asset1) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + let _ = b.add_input( + OpenAnonAssetRecordBuilder::new() + .amount(1100000) + .asset_type(ASSET_TYPE_FRA) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + let extra_fra = FEE_CALCULATING_FUNC(2, 2) as u64 - 1100000; + assert!(b.extra_fee_estimation().is_ok()); + assert_eq!(b.extra_fee_estimation().unwrap(), extra_fra); + } + { + let mut b = AnonTransferOperationBuilder::new_from_seq_id(0); + + let _ = b.add_input( + OpenAnonAssetRecordBuilder::new() + .amount(1000000) + .asset_type(asset1) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + let _ = b.add_output( + OpenAnonAssetRecordBuilder::new() + .amount(1000000) + .asset_type(asset1) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + let _ = b.add_input( + OpenAnonAssetRecordBuilder::new() + .amount(1100000) + .asset_type(ASSET_TYPE_FRA) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + let extra_fra = FEE_CALCULATING_FUNC(2, 2) as u64 - 1100000; + assert!(b.extra_fee_estimation().is_ok()); + assert_eq!(b.extra_fee_estimation().unwrap(), extra_fra); + } + { + let mut b = AnonTransferOperationBuilder::new_from_seq_id(0); + + let _ = b.add_input( + OpenAnonAssetRecordBuilder::new() + .amount(1000000) + .asset_type(asset1) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + let _ = b.add_output( + OpenAnonAssetRecordBuilder::new() + .amount(1000000) + .asset_type(asset1) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + let fee = FEE_CALCULATING_FUNC(2, 1) as u64; + let _ = b.add_input( + OpenAnonAssetRecordBuilder::new() + .amount(fee) + .asset_type(ASSET_TYPE_FRA) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + let extra_fra = FEE_CALCULATING_FUNC(2, 1) as u64 - fee; + assert!(b.extra_fee_estimation().is_ok()); + assert_eq!(b.extra_fee_estimation().unwrap(), extra_fra); + } + { + let mut b = AnonTransferOperationBuilder::new_from_seq_id(0); + + let _ = b.add_input( + OpenAnonAssetRecordBuilder::new() + .amount(1000000) + .asset_type(asset1) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + let _ = b.add_output( + OpenAnonAssetRecordBuilder::new() + .amount(900000) + .asset_type(asset1) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + let fee = FEE_CALCULATING_FUNC(2, 2) as u64; + let _ = b.add_input( + OpenAnonAssetRecordBuilder::new() + .amount(fee) + .asset_type(ASSET_TYPE_FRA) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + let extra_fra = FEE_CALCULATING_FUNC(2, 2) as u64 - fee; + assert!(b.extra_fee_estimation().is_ok()); + assert_eq!(b.extra_fee_estimation().unwrap(), extra_fra); + } + { + let mut b = AnonTransferOperationBuilder::new_from_seq_id(0); + + let _ = b.add_input( + OpenAnonAssetRecordBuilder::new() + .amount(800000) + .asset_type(asset1) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + let _ = b.add_output( + OpenAnonAssetRecordBuilder::new() + .amount(900000) + .asset_type(asset1) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + let fee = FEE_CALCULATING_FUNC(2, 2) as u64; + let _ = b.add_input( + OpenAnonAssetRecordBuilder::new() + .amount(fee) + .asset_type(ASSET_TYPE_FRA) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + assert!(b.extra_fee_estimation().is_err()); + } + { + let mut b = AnonTransferOperationBuilder::new_from_seq_id(0); + + let _ = b.add_input( + OpenAnonAssetRecordBuilder::new() + .amount(1000000) + .asset_type(asset1) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + let _ = b.add_output( + OpenAnonAssetRecordBuilder::new() + .amount(900000) + .asset_type(asset1) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + let fee = FEE_CALCULATING_FUNC(2, 3) as u64; + let _ = b.add_input( + OpenAnonAssetRecordBuilder::new() + .amount(2 * fee) + .asset_type(ASSET_TYPE_FRA) + .pub_key(&k.get_pk().into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(), + ); + assert!(b.extra_fee_estimation().is_ok()); + assert_eq!(b.extra_fee_estimation().unwrap(), 0); + } + } } diff --git a/src/components/wallet_mobile/Cargo.toml b/src/components/wallet_mobile/Cargo.toml index 46ea6f1cd..78472bc71 100644 --- a/src/components/wallet_mobile/Cargo.toml +++ b/src/components/wallet_mobile/Cargo.toml @@ -16,30 +16,30 @@ crate-type = ["cdylib", "staticlib", "rlib"] wasm-opt = false [dependencies] -aes-gcm = "0.9.0" +aes-gcm = "^0.10.1" base64 = "0.13" bech32 = "0.7.2" ffi-support = "0.4" futures = "0.3.16" getrandom = { version = "0.2", features = ["js"] } -hex = "0.4.2" +hex = "0.4.3" js-sys = "0.3.27" rand = { version = "0.7", features = ["wasm-bindgen"] } -rand_chacha = "0.2.0" -rand_core = { version = "0.5", default-features = false, features = ["alloc"] } +rand_chacha = "0.3" +rand_core = { version = "0.6", default-features = false, features = ["alloc"] } ring = "0.16.19" ruc = "1.0" serde = { version = "1.0.124", features = ["derive"] } -serde_derive = "1.0" +serde_derive = "^1.0.59" serde_json = "1.0" -zei = { git = "https://github.com/FindoraNetwork/zei", branch = "stable-main" } +zei = { package="platform-lib-noah", git = "https://github.com/FindoraNetwork/platform-lib-noah", branch = "develop" } finutils = { path = "../finutils", default-features = false, features = []} fp-types = { path = "../contracts/primitives/types" } fp-utils = { path = "../contracts/primitives/utils" } -globutils = { git = "https://github.com/FindoraNetwork/platform-lib-utils", tag = "v1.0.0" } -credentials = { git = "https://github.com/FindoraNetwork/platform-lib-credentials", tag = "v1.0.0" } -cryptohash = { git = "https://github.com/FindoraNetwork/platform-lib-cryptohash", tag = "v1.0.0" } +globutils = { git = "https://github.com/FindoraNetwork/platform-lib-utils", branch = "develop" } +credentials = { git = "https://github.com/FindoraNetwork/platform-lib-credentials", branch = "develop" } +cryptohash = { git = "https://github.com/FindoraNetwork/platform-lib-cryptohash", branch = "develop" } ledger = { path = "../../ledger" } @@ -66,5 +66,4 @@ safer-ffi = "0.0.10" [build-dependencies] cbindgen = "0.24" -vergen = "3.1.0" - +vergen = "3.1.0" \ No newline at end of file diff --git a/src/components/wallet_mobile/src/android/constructor.rs b/src/components/wallet_mobile/src/android/constructor.rs index 4303b99c2..944fe10d4 100644 --- a/src/components/wallet_mobile/src/android/constructor.rs +++ b/src/components/wallet_mobile/src/android/constructor.rs @@ -4,8 +4,7 @@ use jni::sys::{jbyteArray, jlong}; use jni::JNIEnv; use rand_chacha::ChaChaRng; use rand_core::SeedableRng; -use zei::xfr::sig::XfrKeyPair as RawXfrKeyPair; -use zei::xfr::structs::ASSET_TYPE_LENGTH; +use zei::noah_api::{keys::KeyPair as RawXfrKeyPair, xfr::structs::ASSET_TYPE_LENGTH}; #[no_mangle] pub unsafe extern "system" fn Java_com_findora_JniApi_xfrKeyPairNew( @@ -17,7 +16,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_xfrKeyPairNew( let mut buf = [0u8; ASSET_TYPE_LENGTH]; buf.copy_from_slice(input.as_ref()); let mut prng = ChaChaRng::from_seed(buf); - let val = types::XfrKeyPair::from(RawXfrKeyPair::generate(&mut prng)); + let val = types::XfrKeyPair::from(RawXfrKeyPair::generate_ed25519(&mut prng)); Box::into_raw(Box::new(val)) as jlong } diff --git a/src/components/wallet_mobile/src/android/evm.rs b/src/components/wallet_mobile/src/android/evm.rs index 0271a4903..7ac82504b 100644 --- a/src/components/wallet_mobile/src/android/evm.rs +++ b/src/components/wallet_mobile/src/android/evm.rs @@ -2,7 +2,7 @@ use crate::rust::account::{get_serialized_address, EVMTransactionBuilder}; use jni::objects::{JClass, JString}; use jni::sys::{jlong, jstring}; use jni::JNIEnv; -use zei::xfr::sig::XfrPublicKey; +use zei::{noah_api::keys::PublicKey, XfrPublicKey}; use super::{jStringToString, parseU64}; @@ -27,10 +27,13 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transferToUtxoFromAccount( let sk = jStringToString(env, sk); - let recipient = *(recipient as *mut XfrPublicKey); + let recipient = *(recipient as *mut PublicKey); let ser_tx = EVMTransactionBuilder::new_transfer_to_utxo_from_account( - recipient, amount, sk, nonce, + XfrPublicKey::from_noah(&recipient).unwrap(), + amount, + sk, + nonce, ) .unwrap(); diff --git a/src/components/wallet_mobile/src/android/mod.rs b/src/components/wallet_mobile/src/android/mod.rs index 5e41115c1..e63880d29 100644 --- a/src/components/wallet_mobile/src/android/mod.rs +++ b/src/components/wallet_mobile/src/android/mod.rs @@ -13,7 +13,8 @@ use jni::objects::{JClass, JString}; use jni::sys::{jboolean, jbyteArray, jint, jlong, jstring}; use jni::JNIEnv; use ledger::data_model::AssetTypeCode; -use zei::xfr::structs::ASSET_TYPE_LENGTH; +use zei::{noah_api::xfr::structs::ASSET_TYPE_LENGTH, XfrKeyPair, XfrPublicKey}; + #[no_mangle] /// Returns the git commit hash and commit date of the commit this library was built against. pub extern "system" fn Java_com_findora_JniApi_buildId( @@ -150,7 +151,7 @@ pub extern "system" fn Java_com_findora_JniApi_keypairFromStr( .get_string(text) .expect("Couldn't get java string!") .into(); - let val = types::XfrKeyPair::from(keypair_from_str(text)); + let val = types::XfrKeyPair::from(keypair_from_str(text).into_noah().unwrap()); Box::into_raw(Box::new(val)) as jlong } @@ -164,7 +165,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_publicKeyToBech32( xfr_public_key_ptr: jlong, ) -> jstring { let key = &*(xfr_public_key_ptr as *mut types::XfrPublicKey); - let res = public_key_to_bech32(key); + let res = public_key_to_bech32(&XfrPublicKey::from_noah(key).unwrap()); let output = env.new_string(res).expect("Couldn't create java string!"); **output } @@ -179,7 +180,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_getPubKeyStr( xfr_keypair_ptr: jlong, ) -> jstring { let key = &*(xfr_keypair_ptr as *mut types::XfrKeyPair); - let pubkey = get_pub_key_str(key); + let pubkey = get_pub_key_str(&XfrKeyPair::from_noah(key).unwrap()); let output = env .new_string(pubkey) .expect("Couldn't create java string!"); @@ -196,7 +197,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_getPrivKeyStr( xfr_keypair_ptr: jlong, ) -> jstring { let key = &*(xfr_keypair_ptr as *mut types::XfrKeyPair); - let prikey = get_priv_key_str(key); + let prikey = get_priv_key_str(&XfrKeyPair::from_noah(key).unwrap()); let output = env .new_string(prikey) .expect("Couldn't create java string!"); @@ -218,7 +219,9 @@ pub extern "system" fn Java_com_findora_JniApi_restoreKeypairFromMnemonicDefault .expect("Couldn't get java string!") .into(); if let Ok(keypair) = rs_restore_keypair_from_mnemonic_default(phrase.as_str()) { - Box::into_raw(Box::new(types::XfrKeyPair::from(keypair))) as jlong + Box::into_raw(Box::new(types::XfrKeyPair::from( + keypair.into_noah().unwrap(), + ))) as jlong } else { ::std::ptr::null_mut::<()>() as jlong } @@ -235,7 +238,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_keypairToStr( xfr_keypair_ptr: jlong, ) -> jstring { let key = &*(xfr_keypair_ptr as *mut types::XfrKeyPair); - let res = keypair_to_str(key); + let res = keypair_to_str(&XfrKeyPair::from_noah(key).unwrap()); let output = env.new_string(res).expect("Couldn't create java string!"); **output } @@ -251,7 +254,9 @@ pub extern "system" fn Java_com_findora_JniApi_createKeypairFromSecret( .expect("Couldn't get java string!") .into(); if let Some(keypair) = create_keypair_from_secret(sk) { - Box::into_raw(Box::new(types::XfrKeyPair::from(keypair))) as jlong + Box::into_raw(Box::new(types::XfrKeyPair::from( + keypair.into_noah().unwrap(), + ))) as jlong } else { ::std::ptr::null_mut::<()>() as jlong } @@ -266,8 +271,8 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_getPkFromKeypair( xfr_keypair_ptr: jlong, ) -> jlong { let kp = &*(xfr_keypair_ptr as *mut types::XfrKeyPair); - let pk = get_pk_from_keypair(kp); - Box::into_raw(Box::new(types::XfrPublicKey::from(pk))) as jlong + let pk = get_pk_from_keypair(&XfrKeyPair::from_noah(kp).unwrap()); + Box::into_raw(Box::new(types::XfrPublicKey::from(pk.into_noah().unwrap()))) as jlong } #[no_mangle] @@ -277,7 +282,9 @@ pub extern "system" fn Java_com_findora_JniApi_newKeypair( _: JClass, ) -> jlong { let keypair = new_keypair(); - Box::into_raw(Box::new(types::XfrKeyPair::from(keypair))) as jlong + Box::into_raw(Box::new(types::XfrKeyPair::from( + keypair.into_noah().unwrap(), + ))) as jlong } #[no_mangle] @@ -340,7 +347,11 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_openClientAssetRecord( let keypair = &*(keypair_ptr as *mut types::XfrKeyPair); let oar = throw_exception!( env, - rs_open_client_asset_record(record, owner_memo, keypair) + rs_open_client_asset_record( + record, + owner_memo, + &XfrKeyPair::from_noah(keypair).unwrap() + ) ); Box::into_raw(Box::new(types::OpenAssetRecord::from(oar))) as jlong } diff --git a/src/components/wallet_mobile/src/android/transfer.rs b/src/components/wallet_mobile/src/android/transfer.rs index 05ddd9da9..613c07e95 100644 --- a/src/components/wallet_mobile/src/android/transfer.rs +++ b/src/components/wallet_mobile/src/android/transfer.rs @@ -3,8 +3,13 @@ use jni::objects::{JClass, JString}; use jni::sys::{jboolean, jint, jlong, jstring, jvalue, JNI_TRUE}; use jni::JNIEnv; use ledger::data_model::AssetType as PlatformAssetType; -use zei::xfr::sig::{XfrKeyPair, XfrPublicKey}; -use zei::xfr::structs::OwnerMemo as ZeiOwnerMemo; +use zei::{ + noah_api::{ + keys::{KeyPair, PublicKey}, + xfr::structs::OwnerMemo as NoahOwnerMemo, + }, + XfrKeyPair, XfrPublicKey, +}; use super::{jStringToString, parseU64}; @@ -189,8 +194,8 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_ownerMemoFromJson( .expect("Couldn't get java string!") .into(); - let zei_owner_memo: ZeiOwnerMemo = serde_json::from_str(val.as_str()).unwrap(); - Box::into_raw(Box::new(OwnerMemo::from_json(zei_owner_memo).unwrap())) as jlong + let noah_owner_memo: NoahOwnerMemo = serde_json::from_str(val.as_str()).unwrap(); + Box::into_raw(Box::new(OwnerMemo::from_json(noah_owner_memo).unwrap())) as jlong } #[no_mangle] @@ -273,7 +278,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transferOperationBuilderAd Some(memo.clone()) }; let tracing_policies = &*(tracing_policies_ptr as *mut TracingPolicies); - let key = &*(key_ptr as *mut XfrKeyPair); + let key = &*(key_ptr as *mut KeyPair); let amount = parseU64(env, amount); let builder = builder @@ -283,7 +288,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transferOperationBuilderAd asset_record.clone(), owner_memo, tracing_policies, - key, + &XfrKeyPair::from_noah(key).unwrap(), amount, ) .unwrap(); @@ -328,12 +333,18 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transferOperationBuilderAd let memo = &*(owner_memo_ptr as *mut OwnerMemo); Some(memo.clone()) }; - let key = &*(key_ptr as *mut XfrKeyPair); + let key = &*(key_ptr as *mut KeyPair); let amount = parseU64(env, amount); let builder = builder .clone() - .add_input_no_tracing(txo_ref, asset_record, owner_memo, key, amount) + .add_input_no_tracing( + txo_ref, + asset_record, + owner_memo, + &XfrKeyPair::from_noah(key).unwrap(), + amount, + ) .unwrap(); Box::into_raw(Box::new(builder)) as jlong } @@ -369,7 +380,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transferOperationBuilderAd ) -> jlong { let builder = &*(builder as *mut TransferOperationBuilder); let tracing_policies = &*(tracing_policies_ptr as *mut TracingPolicies); - let recipient = &*(recipient as *mut XfrPublicKey); + let recipient = &*(recipient as *mut PublicKey); let amount = parseU64(env, amount); let code = jStringToString(env, code); @@ -377,7 +388,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transferOperationBuilderAd .clone() .add_output_with_tracing( amount, - recipient, + &XfrPublicKey::from_noah(recipient).unwrap(), tracing_policies, code, conf_amount == JNI_TRUE, @@ -413,7 +424,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transferOperationBuilderAd conf_type: jboolean, ) -> jlong { let builder = &*(builder as *mut TransferOperationBuilder); - let recipient = &*(recipient as *mut XfrPublicKey); + let recipient = &*(recipient as *mut PublicKey); let amount = parseU64(env, amount); let code = jStringToString(env, code); @@ -421,7 +432,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transferOperationBuilderAd .clone() .add_output_no_tracing( amount, - recipient, + &XfrPublicKey::from_noah(recipient).unwrap(), code, conf_amount == JNI_TRUE, conf_type == JNI_TRUE, @@ -459,7 +470,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transferOperationBuilderAd let policies = &*(tracing_policies_ptr as *mut TracingPolicies); Some(policies) }; - let key = &*(key_ptr as *mut XfrKeyPair); + let key = &*(key_ptr as *mut KeyPair); let builder = builder .clone() @@ -468,7 +479,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transferOperationBuilderAd asset_record, owner_memo, tracing_policies, - key, + &XfrKeyPair::from_noah(key).unwrap(), parseU64(env, amount), ) .unwrap(); @@ -496,7 +507,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transferOperationBuilderAd let policies = &*(tracing_policies_ptr as *mut TracingPolicies); Some(policies) }; - let recipient = &*(recipient as *mut XfrPublicKey); + let recipient = &*(recipient as *mut PublicKey); let code: String = env .get_string(code) .expect("Couldn't get java string!") @@ -506,7 +517,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transferOperationBuilderAd .clone() .add_output( parseU64(env, amount), - recipient, + &XfrPublicKey::from_noah(&recipient).unwrap(), tracing_policies, code, conf_amount == JNI_TRUE, @@ -562,9 +573,14 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transferOperationBuilderSi key_ptr: jlong, ) -> jlong { let builder = &*(builder as *mut TransferOperationBuilder); - let key = &*(key_ptr as *mut XfrKeyPair); + let key = &*(key_ptr as *mut KeyPair); - Box::into_raw(Box::new(builder.clone().sign(key).unwrap())) as jlong + Box::into_raw(Box::new( + builder + .clone() + .sign(&XfrKeyPair::from_noah(&key).unwrap()) + .unwrap(), + )) as jlong } #[no_mangle] diff --git a/src/components/wallet_mobile/src/android/tx_builder.rs b/src/components/wallet_mobile/src/android/tx_builder.rs index 264dac2fe..c89a4b9e8 100644 --- a/src/components/wallet_mobile/src/android/tx_builder.rs +++ b/src/components/wallet_mobile/src/android/tx_builder.rs @@ -4,10 +4,10 @@ use jni::objects::{JClass, JString}; use jni::sys::{jboolean, jint, jlong, jstring, JNI_TRUE}; use jni::JNIEnv; use ledger::data_model::AssetTypeCode; -use zei::xfr::sig::XfrKeyPair; +use zei::XfrKeyPair; #[no_mangle] /// # Safety -/// @param kp: owner's XfrKeyPair +/// @param kp: owner's KeyPair pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderAddFeeRelativeAuto( _env: JNIEnv, _: JClass, @@ -15,8 +15,11 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderAddFeeRe kp: jlong, ) -> jlong { let builder = &*(builder as *mut TransactionBuilder); - let kp = &*(kp as *mut XfrKeyPair); - let builder = builder.clone().add_fee_relative_auto(kp.clone()).unwrap(); + let kp = &*(kp as *mut KeyPair); + let builder = builder + .clone() + .add_fee_relative_auto(XfrKeyPair::from_noah(kp).unwrap()) + .unwrap(); Box::into_raw(Box::new(builder)) as jlong } @@ -91,7 +94,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderNew( /// console.log(err) /// } /// -/// @param {XfrKeyPair} key_pair - Issuer XfrKeyPair. +/// @param {KeyPair} key_pair - Issuer KeyPair. /// @param {string} memo - Text field for asset definition. /// @param {string} token_code - Optional Base64 string representing the token code of the asset to be issued. /// If empty, a token code will be chosen at random. @@ -107,7 +110,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderAddOpera asset_rules: jlong, ) -> jlong { let builder = &*(builder as *mut TransactionBuilder); - let key_pair = &*(key_pair as *mut XfrKeyPair); + let key_pair = &*(key_pair as *mut KeyPair); let memo: String = env .get_string(memo) .expect("Couldn't get java string!") @@ -119,7 +122,12 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderAddOpera let asset_rules = &*(asset_rules as *mut AssetRules); let builder = builder .clone() - .add_operation_create_asset(key_pair, memo, token_code, asset_rules.clone()) + .add_operation_create_asset( + &XfrKeyPair::from_noah(key_pair).unwrap(), + memo, + token_code, + asset_rules.clone(), + ) .unwrap(); Box::into_raw(Box::new(builder)) as jlong } @@ -130,13 +138,12 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderAddOpera /// /// Use this function for simple one-shot issuances. /// -/// @param {XfrKeyPair} key_pair - Issuer XfrKeyPair. +/// @param {KeyPair} key_pair - Issuer KeyPair. /// and types of traced assets. /// @param {string} code - base64 string representing the token code of the asset to be issued. /// @param {BigInt} seq_num - Issuance sequence number. Every subsequent issuance of a given asset type must have a higher sequence number than before. /// @param {BigInt} amount - Amount to be issued. /// @param {boolean} conf_amount - `true` means the asset amount is confidential, and `false` means it's nonconfidential. -/// @param {PublicParams} zei_params - Public parameters necessary to generate asset records. pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderAddBasicIssueAsset( env: JNIEnv, _: JClass, @@ -146,24 +153,21 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderAddBasic seq_num: jlong, amount: JString, conf_amount: jboolean, - zei_params: jlong, ) -> jlong { let builder = &*(builder as *mut TransactionBuilder); - let key_pair = &*(key_pair as *mut XfrKeyPair); + let key_pair = &*(key_pair as *mut KeyPair); let code: String = env .get_string(code) .expect("Couldn't get java string!") .into(); - let zei_params = &*(zei_params as *mut PublicParams); let builder = builder .clone() .add_basic_issue_asset( - key_pair, + &XfrKeyPair::from_noah(key_pair).unwrap(), code, seq_num as u64, parseU64(env, amount), conf_amount == JNI_TRUE, - zei_params, ) .unwrap(); Box::into_raw(Box::new(builder)) as jlong @@ -173,7 +177,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderAddBasic /// # Safety /// Adds an operation to the transaction builder that adds a hash to the ledger's custom data /// store. -/// @param {XfrKeyPair} auth_key_pair - Asset creator key pair. +/// @param {KeyPair} auth_key_pair - Asset creator key pair. /// @param {String} code - base64 string representing token code of the asset whose memo will be updated. /// transaction validates. /// @param {String} new_memo - The new asset memo. @@ -188,7 +192,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderAddOpera new_memo: JString, ) -> jlong { let builder = &*(builder as *mut TransactionBuilder); - let auth_key_pair = &*(auth_key_pair as *mut XfrKeyPair); + let auth_key_pair = &*(auth_key_pair as *mut KeyPair); let code: String = env .get_string(code) .expect("Couldn't get java string!") @@ -199,7 +203,11 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderAddOpera .into(); let builder = builder .clone() - .add_operation_update_memo(auth_key_pair, code, new_memo) + .add_operation_update_memo( + &XfrKeyPair::from_noah(auth_key_pair).unwrap(), + code, + new_memo, + ) .unwrap(); Box::into_raw(Box::new(builder)) as jlong } @@ -216,7 +224,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderAddOpera validator: JString, ) -> jlong { let builder = &*(builder as *mut TransactionBuilder); - let keypair = &*(keypair as *mut XfrKeyPair); + let keypair = &*(keypair as *mut KeyPair); let validator: String = env .get_string(validator) .expect("Couldn't get java string!") @@ -224,7 +232,11 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderAddOpera let builder = builder .clone() - .add_operation_delegate(keypair, parseU64(env, amount), validator) + .add_operation_delegate( + &XfrKeyPair::from_noah(keypair).unwrap(), + parseU64(env, amount), + validator, + ) .unwrap(); Box::into_raw(Box::new(builder)) as jlong } @@ -239,8 +251,11 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderAddOpera keypair: jlong, ) -> jlong { let builder = &*(builder as *mut TransactionBuilder); - let keypair = &*(keypair as *mut XfrKeyPair); - let builder = builder.clone().add_operation_undelegate(keypair).unwrap(); + let keypair = &*(keypair as *mut KeyPair); + let builder = builder + .clone() + .add_operation_undelegate(&XfrKeyPair::from_noah(keypair).unwrap()) + .unwrap(); Box::into_raw(Box::new(builder)) as jlong } @@ -256,7 +271,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderAddOpera validator: JString, ) -> jlong { let builder = &*(builder as *mut TransactionBuilder); - let keypair = &*(keypair as *mut XfrKeyPair); + let keypair = &*(keypair as *mut KeyPair); let validator: String = env .get_string(validator) @@ -264,7 +279,11 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderAddOpera .into(); let builder = builder .clone() - .add_operation_undelegate_partially(keypair, parseU64(env, am), validator) + .add_operation_undelegate_partially( + &XfrKeyPair::from_noah(keypair).unwrap(), + parseU64(env, am), + validator, + ) .unwrap(); Box::into_raw(Box::new(builder)) as jlong } @@ -285,9 +304,12 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderAddOpera .expect("Couldn't get java string!") .into(); let addr = td_addr.strip_prefix("0x").unwrap_or(&td_addr); - let td_address = hex::decode(addr) .expect("addr format error!"); + let td_address = hex::decode(addr).expect("addr format error!"); let keypair = &*(keypair as *mut XfrKeyPair); - let builder = builder.clone().add_operation_claim(td_address,keypair).unwrap(); + let builder = builder + .clone() + .add_operation_claim(td_address, keypair) + .unwrap(); Box::into_raw(Box::new(builder)) as jlong } @@ -303,16 +325,16 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderAddOpera am: JString, ) -> jlong { let builder = &*(builder as *mut TransactionBuilder); - let keypair = &*(keypair as *mut XfrKeyPair); + let keypair = &*(keypair as *mut KeyPair); let td_addr: String = env - .get_string(td_addr) - .expect("Couldn't get java string!") - .into(); + .get_string(td_addr) + .expect("Couldn't get java string!") + .into(); let addr = td_addr.strip_prefix("0x").unwrap_or(&td_addr); - let td_address = hex::decode(addr) .expect("addr format error!"); + let td_address = hex::decode(addr).expect("addr format error!"); let builder = builder .clone() - .add_operation_claim_custom(td_address,keypair, parseU64(env, am)) + .add_operation_claim_custom(td_address, keypair, parseU64(env, am)) .unwrap(); Box::into_raw(Box::new(builder)) as jlong } @@ -344,7 +366,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderAddTrans /// /// Adds a serialized transfer-account operation to transaction builder instance. /// @param {string} amount - amount to transfer. -/// @param {XfrKeyPair} keypair - FRA account key pair. +/// @param {KeyPair} keypair - FRA account key pair. /// @param {String} address - FRA account key pair. /// @throws Will throw an error if `address` is invalid. pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderAddOperationConvertAccount( @@ -363,7 +385,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderAddOpera .expect("Couldn't get java string!") .into(); - let fra_kp = &*(keypair as *mut XfrKeyPair); + let fra_kp = &*(keypair as *mut KeyPair); let asset_str: String = env .get_string(asset) @@ -390,7 +412,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderAddOpera .add_transfer_to_account_operation( parseU64(env, amount), Some(addr), - fra_kp, + &XfrKeyPair::from_noah(fra_kp).unwrap(), asset, lowlevel_data, ) @@ -408,8 +430,11 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderSign( kp: jlong, ) -> jlong { let builder = &*(builder as *mut TransactionBuilder); - let kp = &*(kp as *mut XfrKeyPair); - let builder = builder.clone().sign(kp).unwrap(); + let kp = &*(kp as *mut KeyPair); + let builder = builder + .clone() + .sign(&XfrKeyPair::from_noah(kp).unwrap()) + .unwrap(); Box::into_raw(Box::new(builder)) as jlong } @@ -422,7 +447,7 @@ pub unsafe extern "system" fn Java_com_findora_JniApi_transactionBuilderTransact _: JClass, builder: jlong, ) -> jstring { - let builder = &*(builder as *mut TransactionBuilder); + let builder = &mut *(builder as *mut TransactionBuilder); let output = env .new_string(builder.transaction()) .expect("Couldn't create java string!"); diff --git a/src/components/wallet_mobile/src/ios/evm.rs b/src/components/wallet_mobile/src/ios/evm.rs index 527c5e11b..f313ab63d 100644 --- a/src/components/wallet_mobile/src/ios/evm.rs +++ b/src/components/wallet_mobile/src/ios/evm.rs @@ -1,5 +1,5 @@ use std::os::raw::c_char; -use zei::xfr::sig::XfrPublicKey; +use zei::XfrPublicKey; use crate::rust::{ self, account::EVMTransactionBuilder, c_char_to_string, string_to_c_char, diff --git a/src/components/wallet_mobile/src/ios/mod.rs b/src/components/wallet_mobile/src/ios/mod.rs index 4212e5e0b..80e80b05a 100644 --- a/src/components/wallet_mobile/src/ios/mod.rs +++ b/src/components/wallet_mobile/src/ios/mod.rs @@ -13,7 +13,7 @@ use rand_core::SeedableRng; use std::ffi::{CStr, CString}; use std::os::raw::c_char; use std::ptr; -use zei::xfr::structs::ASSET_TYPE_LENGTH; +use zei::noah_api::xfr::structs::ASSET_TYPE_LENGTH; #[no_mangle] /// Returns the git commit hash and commit date of the commit this library was built against. diff --git a/src/components/wallet_mobile/src/ios/tx_builder.rs b/src/components/wallet_mobile/src/ios/tx_builder.rs index 13049fb38..9d3a05c9d 100644 --- a/src/components/wallet_mobile/src/ios/tx_builder.rs +++ b/src/components/wallet_mobile/src/ios/tx_builder.rs @@ -1,11 +1,11 @@ use super::parse_u64; use crate::rust::{ c_char_to_string, string_to_c_char, AssetRules, ClientAssetRecord, FeeInputs, - OwnerMemo, PublicParams, TransactionBuilder, + OwnerMemo, TransactionBuilder, }; use ledger::data_model::AssetTypeCode; use std::os::raw::c_char; -use zei::xfr::sig::XfrKeyPair; +use zei::XfrKeyPair; #[no_mangle] /// @param kp: owner's XfrKeyPair @@ -108,7 +108,6 @@ pub extern "C" fn findora_ffi_transaction_builder_add_operation_create_asset( /// @param {BigInt} seq_num - Issuance sequence number. Every subsequent issuance of a given asset type must have a higher sequence number than before. /// @param {BigInt} amount - Amount to be issued. /// @param {boolean} conf_amount - `true` means the asset amount is confidential, and `false` means it's nonconfidential. -/// @param {PublicParams} zei_params - Public parameters necessary to generate asset records. #[no_mangle] pub extern "C" fn findora_ffi_transaction_builder_add_basic_issue_asset( builder: &TransactionBuilder, @@ -117,7 +116,6 @@ pub extern "C" fn findora_ffi_transaction_builder_add_basic_issue_asset( seq_num: u64, amount: *const c_char, conf_amount: bool, - zei_params: &PublicParams, ) -> *mut TransactionBuilder { let amount = parse_u64(amount); if let Ok(info) = builder.clone().add_basic_issue_asset( @@ -126,7 +124,6 @@ pub extern "C" fn findora_ffi_transaction_builder_add_basic_issue_asset( seq_num, amount, conf_amount, - zei_params, ) { Box::into_raw(Box::new(info)) } else { @@ -219,7 +216,7 @@ pub extern "C" fn findora_ffi_transaction_builder_add_operation_claim( let td_addr = c_char_to_string(td_addr); let addr = td_addr.strip_prefix("0x").unwrap_or(&td_addr); let td_address = hex::decode(addr).expect("addr format error!"); - if let Ok(info) = builder.clone().add_operation_claim(td_address,keypair) { + if let Ok(info) = builder.clone().add_operation_claim(td_address, keypair) { Box::into_raw(Box::new(info)) } else { std::ptr::null_mut() @@ -237,7 +234,10 @@ pub extern "C" fn findora_ffi_transaction_builder_add_operation_claim_custom( let td_addr = c_char_to_string(td_addr); let addr = td_addr.strip_prefix("0x").unwrap_or(&td_addr); let td_address = hex::decode(addr).expect("addr format error!"); - if let Ok(info) = builder.clone().add_operation_claim_custom(td_address,keypair, am) { + if let Ok(info) = builder + .clone() + .add_operation_claim_custom(td_address, keypair, am) + { Box::into_raw(Box::new(info)) } else { std::ptr::null_mut() diff --git a/src/components/wallet_mobile/src/ios/tx_op_builder.rs b/src/components/wallet_mobile/src/ios/tx_op_builder.rs index 24a6fa28b..9f318d0d3 100644 --- a/src/components/wallet_mobile/src/ios/tx_op_builder.rs +++ b/src/components/wallet_mobile/src/ios/tx_op_builder.rs @@ -1,5 +1,5 @@ use std::os::raw::c_char; -use zei::xfr::sig::{XfrKeyPair, XfrPublicKey}; +use zei::{XfrKeyPair, XfrPublicKey}; use super::parse_u64; use crate::rust::TransferOperationBuilder; diff --git a/src/components/wallet_mobile/src/rust/account.rs b/src/components/wallet_mobile/src/rust/account.rs index 0c6588a54..963921863 100644 --- a/src/components/wallet_mobile/src/rust/account.rs +++ b/src/components/wallet_mobile/src/rust/account.rs @@ -1,7 +1,7 @@ use core::str::FromStr; use ledger::data_model::{AssetTypeCode, ASSET_TYPE_FRA}; use ruc::{d, Result, RucResult}; -use zei::xfr::sig::{XfrKeyPair, XfrPublicKey}; +use zei::{XfrKeyPair, XfrPublicKey}; use super::transaction::TransactionBuilder; @@ -29,7 +29,7 @@ impl Keypair { match self { Keypair::Ecdsa(kp) => MultiSignature::from(kp.sign(data)), Keypair::Ed25519(kp) => { - MultiSignature::from(kp.get_sk_ref().sign(data, kp.get_pk_ref())) + MultiSignature::from(kp.get_sk_ref().sign(data).unwrap()) } } } diff --git a/src/components/wallet_mobile/src/rust/crypto.rs b/src/components/wallet_mobile/src/rust/crypto.rs index 86059b624..98d31ddca 100644 --- a/src/components/wallet_mobile/src/rust/crypto.rs +++ b/src/components/wallet_mobile/src/rust/crypto.rs @@ -2,7 +2,7 @@ use wasm_bindgen::prelude::*; use super::data_model::*; -use aes_gcm::aead::{generic_array::GenericArray, Aead, NewAead}; +use aes_gcm::aead::{generic_array::GenericArray, Aead, KeyInit}; use aes_gcm::Aes256Gcm; use credentials::{ credential_commit, credential_issuer_key_gen, credential_open_commitment, @@ -11,27 +11,28 @@ use credentials::{ CredUserPublicKey, CredUserSecretKey, Credential as PlatformCredential, }; use cryptohash::sha256; +use getrandom::getrandom; use globutils::wallet; use ledger::{ - data_model::{ - AssetTypeCode, ASSET_TYPE_FRA, BLACK_HOLE_PUBKEY, BLACK_HOLE_PUBKEY_STAKING, - TX_FEE_MIN, - }, + data_model::{AssetTypeCode, ASSET_TYPE_FRA, BLACK_HOLE_PUBKEY_STAKING, TX_FEE_MIN}, staking::{MAX_DELEGATION_AMOUNT, MIN_DELEGATION_AMOUNT}, }; -use rand::{thread_rng, Rng}; use rand_chacha::ChaChaRng; use rand_core::SeedableRng; use ring::pbkdf2; -use ruc::Result; +use ruc::{d, Result, RucResult}; use std::num::NonZeroU32; use std::str; -use zei::serialization::ZeiFromToBytes; -use zei::xfr::asset_record::open_blind_asset_record as open_bar; -use zei::xfr::lib::trace_assets as zei_trace_assets; -use zei::xfr::sig::{XfrKeyPair, XfrPublicKey, XfrSecretKey}; -use zei::xfr::structs::{ - AssetType as ZeiAssetType, OpenAssetRecord, XfrBody, ASSET_TYPE_LENGTH, +use zei::{ + noah_algebra::serialization::NoahFromToBytes, + noah_api::xfr::{ + asset_record::open_blind_asset_record as open_bar, + structs::{ + AssetType as NoahAssetType, OpenAssetRecord, XfrBody, ASSET_TYPE_LENGTH, + }, + trace_assets as noah_trace_assets, + }, + XfrKeyPair, XfrPublicKey, XfrSecretKey, }; #[cfg_attr(target_arch = "wasm32", wasm_bindgen)] @@ -47,7 +48,7 @@ pub fn random_asset_type() -> String { /// Generates asset type as a Base64 string from given code. pub fn rs_asset_type_from_value(code: [u8; ASSET_TYPE_LENGTH]) -> String { AssetTypeCode { - val: ZeiAssetType(code), + val: NoahAssetType(code), } .to_base64() } @@ -57,7 +58,8 @@ pub fn rs_trace_assets( xfr_body: XfrBody, tracer_keypair: &AssetTracerKeyPair, ) -> Result> { - Ok(zei_trace_assets(&xfr_body, tracer_keypair.get_keys())? + Ok(noah_trace_assets(&xfr_body, tracer_keypair.get_keys()) + .c(d!())? .iter() .map(|(amt, asset_type, _, _)| { let asset_type_code = AssetTypeCode { val: *asset_type }; @@ -71,7 +73,7 @@ pub fn rs_trace_assets( /// Returns an address to use for cancelling debt tokens in a debt swap. /// @ignore pub fn get_null_pk() -> XfrPublicKey { - XfrPublicKey::zei_from_bytes(&[0; 32]).unwrap() + XfrPublicKey::noah_from_bytes(&[0; 32]).unwrap() } /// Returns a JavaScript object containing decrypted owner record information, @@ -82,10 +84,11 @@ pub fn rs_open_client_asset_record( keypair: &XfrKeyPair, ) -> Result { open_bar( - record.get_bar_ref(), + &record.get_bar_ref().into_noah(), &owner_memo.map(|memo| memo.get_memo_ref().clone()), - keypair, + &keypair.into_noah(), ) + .c(d!()) } #[cfg_attr(target_arch = "wasm32", wasm_bindgen)] @@ -103,8 +106,8 @@ pub fn get_priv_key_str(key_pair: &XfrKeyPair) -> String { #[cfg_attr(target_arch = "wasm32", wasm_bindgen)] /// Creates a new transfer key pair. pub fn new_keypair() -> XfrKeyPair { - let mut small_rng = rand::thread_rng(); - XfrKeyPair::generate(&mut small_rng) + let mut prng = ChaChaRng::from_entropy(); + XfrKeyPair::generate(&mut prng) } #[cfg_attr(target_arch = "wasm32", wasm_bindgen)] @@ -131,14 +134,14 @@ pub fn rs_public_key_from_base64(pk: &str) -> Result { /// Expresses a transfer key pair as a hex-encoded string. /// To decode the string, use `keypair_from_str` function. pub fn keypair_to_str(key_pair: &XfrKeyPair) -> String { - hex::encode(key_pair.zei_to_bytes()) + hex::encode(key_pair.noah_to_bytes()) } #[cfg_attr(target_arch = "wasm32", wasm_bindgen)] /// Constructs a transfer key pair from a hex-encoded string. /// The encode a key pair, use `keypair_to_str` function. pub fn keypair_from_str(str: String) -> XfrKeyPair { - XfrKeyPair::zei_from_bytes(&hex::decode(str).unwrap()).unwrap() + XfrKeyPair::noah_from_bytes(&hex::decode(str).unwrap()).unwrap() } /// Generates a new credential issuer key. @@ -167,7 +170,7 @@ pub fn rs_wasm_credential_verify_commitment( issuer_pub_key, commitment.get_ref(), pok.get_ref(), - xfr_pk.as_bytes(), + &xfr_pk.noah_to_bytes(), ) } @@ -248,7 +251,7 @@ pub fn rs_wasm_credential_commit( &mut prng, user_secret_key, credential.get_cred_ref(), - user_public_key.as_bytes(), + &user_public_key.noah_to_bytes(), )?; Ok(CredentialCommitmentData { commitment: CredentialCommitment { commitment }, @@ -319,10 +322,9 @@ pub fn encryption_pbkdf2_aes256gcm(key_pair: String, password: String) -> Vec Vec u64 { /// The destination for fee to be transfered to. #[cfg_attr(target_arch = "wasm32", wasm_bindgen)] pub fn fra_get_dest_pubkey() -> XfrPublicKey { - *BLACK_HOLE_PUBKEY + XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY_STAKING) } /// The system address used to reveive delegation principals. @@ -486,13 +488,13 @@ pub fn get_delegation_target_address() -> String { #[cfg_attr(target_arch = "wasm32", wasm_bindgen)] #[allow(missing_docs)] pub fn get_coinbase_address() -> String { - wallet::public_key_to_base64(&BLACK_HOLE_PUBKEY_STAKING) + wallet::public_key_to_base64(&XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY_STAKING)) } #[cfg_attr(target_arch = "wasm32", wasm_bindgen)] #[allow(missing_docs)] pub fn get_coinbase_principal_address() -> String { - wallet::public_key_to_base64(&BLACK_HOLE_PUBKEY_STAKING) + wallet::public_key_to_base64(&XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY_STAKING)) } #[cfg_attr(target_arch = "wasm32", wasm_bindgen)] diff --git a/src/components/wallet_mobile/src/rust/data_model.rs b/src/components/wallet_mobile/src/rust/data_model.rs index 12733a3f3..d630af796 100644 --- a/src/components/wallet_mobile/src/rust/data_model.rs +++ b/src/components/wallet_mobile/src/rust/data_model.rs @@ -20,40 +20,16 @@ use rand_core::SeedableRng; use ruc::Result as RUCResult; use ruc::{d, err::RucResult}; use serde::{Deserialize, Serialize}; -use zei::setup::PublicParams as ZeiPublicParams; -use zei::xfr::sig::XfrPublicKey; -use zei::xfr::structs::{ - AssetTracerDecKeys, AssetTracerEncKeys, AssetTracerKeyPair as ZeiAssetTracerKeyPair, - BlindAssetRecord, IdentityRevealPolicy, OwnerMemo as ZeiOwnerMemo, - TracingPolicies as ZeiTracingPolicies, TracingPolicy as ZeiTracingPolicy, +use zei::{ + noah_api::xfr::structs::{ + AssetTracerDecKeys, AssetTracerEncKeys, + AssetTracerKeyPair as NoahAssetTracerKeyPair, IdentityRevealPolicy, + OwnerMemo as NoahOwnerMemo, TracingPolicies as NoahTracingPolicies, + TracingPolicy as NoahTracingPolicy, + }, + BlindAssetRecord, XfrPublicKey, }; -#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] -/// Public parameters necessary for generating asset records. Generating this is expensive and -/// should be done as infrequently as possible. -/// @see {@link module:Findora-Wasm~TransactionBuilder#add_basic_issue_asset|add_basic_issue_asset} -/// for information using public parameters to create issuance asset records. -pub struct PublicParams { - pub(crate) params: ZeiPublicParams, -} - -#[allow(clippy::new_without_default)] -#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] -impl PublicParams { - /// Generates a new set of parameters. - pub fn new() -> PublicParams { - PublicParams { - params: ZeiPublicParams::default(), - } - } -} - -impl PublicParams { - pub fn get_ref(&self) -> &ZeiPublicParams { - &self.params - } -} - #[cfg_attr(target_arch = "wasm32", wasm_bindgen)] /// Indicates whether the TXO ref is an absolute or relative value. #[derive(Copy, Clone)] @@ -238,7 +214,7 @@ impl ClientAssetRecord { /// @see {@link module:Findora-Wasm~AssetRules#add_tracing_policy|add_tracing_policy} for information about how to add a tracing policy to /// an asset definition. pub struct AssetTracerKeyPair { - pub(crate) keypair: ZeiAssetTracerKeyPair, + pub(crate) keypair: NoahAssetTracerKeyPair, } #[cfg_attr(target_arch = "wasm32", wasm_bindgen)] @@ -247,7 +223,7 @@ impl AssetTracerKeyPair { pub fn new() -> Self { let mut small_rng = ChaChaRng::from_entropy(); AssetTracerKeyPair { - keypair: ZeiAssetTracerKeyPair::generate(&mut small_rng), + keypair: NoahAssetTracerKeyPair::generate(&mut small_rng), } } } @@ -266,7 +242,7 @@ impl AssetTracerKeyPair { &self.keypair.dec_key } - pub fn get_keys(&self) -> &ZeiAssetTracerKeyPair { + pub fn get_keys(&self) -> &NoahAssetTracerKeyPair { &self.keypair } } @@ -276,7 +252,7 @@ impl AssetTracerKeyPair { /// Asset owner memo. Contains information needed to decrypt an asset record. /// @see {@link module:Findora-Wasm.ClientAssetRecord|ClientAssetRecord} for more details about asset records. pub struct OwnerMemo { - pub(crate) memo: ZeiOwnerMemo, + pub(crate) memo: NoahOwnerMemo, } #[cfg_attr(target_arch = "wasm32", wasm_bindgen)] @@ -293,29 +269,27 @@ impl OwnerMemo { /// "lock":{"ciphertext":[119,54,117,136,125,133,112,193],"encoded_rand":"8KDql2JphPB5WLd7-aYE1bxTQAcweFSmrqymLvPDntM="} /// } pub fn from_json(val: &JsValue) -> Result { - let zei_owner_memo: ZeiOwnerMemo = + let noah_owner_memo: NoahOwnerMemo = val.into_serde().c(d!()).map_err(error_to_jsvalue)?; Ok(OwnerMemo { - memo: ZeiOwnerMemo { - blind_share: zei_owner_memo.blind_share, - lock: zei_owner_memo.lock, - }, + memo: noah_owner_memo, }) } #[cfg(not(target_arch = "wasm32"))] - pub fn from_json(zei_owner_memo: ZeiOwnerMemo) -> RUCResult { + pub fn from_json(noah_owner_memo: NoahOwnerMemo) -> RUCResult { Ok(OwnerMemo { - memo: ZeiOwnerMemo { - blind_share: zei_owner_memo.blind_share, - lock: zei_owner_memo.lock, + memo: NoahOwnerMemo { + key_type: noah_owner_memo.key_type, + blind_share_bytes: noah_owner_memo.blind_share_bytes, + lock_bytes: noah_owner_memo.lock_bytes, }, }) } } impl OwnerMemo { - pub fn get_memo_ref(&self) -> &ZeiOwnerMemo { + pub fn get_memo_ref(&self) -> &NoahOwnerMemo { &self.memo } } @@ -369,7 +343,7 @@ impl CredentialRevealSig { /// credential commitment. pub fn get_commitment(&self) -> CredentialCommitment { CredentialCommitment { - commitment: self.sig.sig_commitment.clone(), + commitment: self.sig.cm.clone(), } } /// Returns the underlying proof of knowledge that the credential is valid. @@ -377,7 +351,7 @@ impl CredentialRevealSig { /// credential commitment. pub fn get_pok(&self) -> CredentialPoK { CredentialPoK { - pok: self.sig.pok.clone(), + pok: self.sig.proof_open.clone(), } } } @@ -669,11 +643,11 @@ impl SignatureRules { /// A collection of tracing policies. Use this object when constructing asset transfers to generate /// the correct tracing proofs for traceable assets. pub struct TracingPolicies { - pub(crate) policies: ZeiTracingPolicies, + pub(crate) policies: NoahTracingPolicies, } impl TracingPolicies { - pub fn get_policies_ref(&self) -> &ZeiTracingPolicies { + pub fn get_policies_ref(&self) -> &NoahTracingPolicies { &self.policies } } @@ -682,13 +656,13 @@ impl TracingPolicies { /// Tracing policy for asset transfers. Can be configured to track credentials, the asset type and /// amount, or both. pub struct TracingPolicy { - pub(crate) policy: ZeiTracingPolicy, + pub(crate) policy: NoahTracingPolicy, } #[cfg_attr(target_arch = "wasm32", wasm_bindgen)] impl TracingPolicy { pub fn new_with_tracing(tracing_key: &AssetTracerKeyPair) -> Self { - let policy = ZeiTracingPolicy { + let policy = NoahTracingPolicy { enc_keys: tracing_key.get_enc_key().clone(), asset_tracing: true, identity_tracing: None, @@ -710,7 +684,7 @@ impl TracingPolicy { cred_issuer_pub_key: cred_issuer_key.get_ref().clone(), reveal_map, }; - let policy = ZeiTracingPolicy { + let policy = NoahTracingPolicy { enc_keys: tracing_key.get_enc_key().clone(), asset_tracing: tracing, identity_tracing: Some(identity_policy), @@ -729,7 +703,7 @@ impl TracingPolicy { cred_issuer_pub_key: cred_issuer_key.get_ref().clone(), reveal_map, }; - let policy = ZeiTracingPolicy { + let policy = NoahTracingPolicy { enc_keys: tracing_key.get_enc_key().clone(), asset_tracing: tracing, identity_tracing: Some(identity_policy), @@ -739,7 +713,7 @@ impl TracingPolicy { } impl TracingPolicy { - pub fn get_ref(&self) -> &ZeiTracingPolicy { + pub fn get_ref(&self) -> &NoahTracingPolicy { &self.policy } } diff --git a/src/components/wallet_mobile/src/rust/transaction.rs b/src/components/wallet_mobile/src/rust/transaction.rs index a7c22f5be..03a637404 100644 --- a/src/components/wallet_mobile/src/rust/transaction.rs +++ b/src/components/wallet_mobile/src/rust/transaction.rs @@ -15,11 +15,15 @@ use ledger::{ }, staking::{td_addr_to_bytes, PartialUnDelegation, TendermintAddr}, }; -use ruc::{eg, Result as RucResult}; +use ruc::{d, eg, err::RucResult as NewRucResult, Result as RucResult}; use serde_json::Result; -use zei::xfr::asset_record::{open_blind_asset_record as open_bar, AssetRecordType}; -use zei::xfr::sig::{XfrKeyPair, XfrPublicKey}; -use zei::xfr::structs::AssetRecordTemplate; +use zei::{ + noah_api::xfr::{ + asset_record::{open_blind_asset_record as open_bar, AssetRecordType}, + structs::AssetRecordTemplate, + }, + OwnerMemo as NoahOwnerMemo, XfrKeyPair, XfrPublicKey, +}; /// Given a serialized state commitment and transaction, returns true if the transaction correctly /// hashes up to the state commitment and false otherwise. @@ -53,7 +57,7 @@ impl From for PlatformFeeInput { am: fi.am, tr: fi.tr.txo_ref, ar: fi.ar.txo, - om: fi.om.map(|om| om.memo), + om: fi.om.map(|om| NoahOwnerMemo::from_noah(&om.memo).unwrap()), kp: fi.kp, } } @@ -225,7 +229,6 @@ impl TransactionBuilder { seq_num: u64, amount: u64, conf_amount: bool, - zei_params: &PublicParams, ) -> RucResult { let asset_token = AssetTypeCode::new_from_base64(&code)?; @@ -239,7 +242,6 @@ impl TransactionBuilder { seq_num, amount, confidentiality_flags, - zei_params.get_ref(), )?; Ok(self) } @@ -340,7 +342,8 @@ impl TransactionBuilder { } /// Extracts the serialized form of a transaction. - pub fn transaction(&self) -> String { + pub fn transaction(&mut self) -> String { + self.get_builder_mut().build().unwrap(); self.get_builder().serialize_str() } @@ -362,7 +365,9 @@ impl TransactionBuilder { pub fn get_owner_memo(&self, idx: usize) -> Option { self.get_builder() .get_owner_memo_ref(idx) - .map(|memo| OwnerMemo { memo: memo.clone() }) + .map(|memo| OwnerMemo { + memo: memo.into_noah(), + }) } } @@ -393,10 +398,11 @@ impl TransferOperationBuilder { amount: u64, ) -> RucResult { let oar = open_bar( - asset_record.get_bar_ref(), + &asset_record.get_bar_ref().into_noah(), &owner_memo.map(|memo| memo.get_memo_ref().clone()), - key, - )?; + &key.into_noah(), + ) + .c(d!())?; self.get_builder_mut().add_input( *txo_ref.get_txo(), oar, @@ -425,7 +431,7 @@ impl TransferOperationBuilder { amount, code.val, asset_record_type, - *recipient, + recipient.into_noah(), policies.get_policies_ref().clone(), ) } else { @@ -433,7 +439,7 @@ impl TransferOperationBuilder { amount, code.val, asset_record_type, - *recipient, + recipient.into_noah(), ) }; self.get_builder_mut().add_output( diff --git a/src/components/wallet_mobile/src/rust/types.rs b/src/components/wallet_mobile/src/rust/types.rs index 765072d30..d63b50e0a 100644 --- a/src/components/wallet_mobile/src/rust/types.rs +++ b/src/components/wallet_mobile/src/rust/types.rs @@ -5,21 +5,23 @@ use credentials::{ CredUserSecretKey as PlatformCredUserSecretKey, }; use std::ops::{Deref, DerefMut}; -use zei::xfr::sig::{XfrKeyPair as ZeiXfrKeyPair, XfrPublicKey as ZeiXfrPublicKey}; -use zei::xfr::structs::OpenAssetRecord as ZeiOpenAssetRecord; +use zei::noah_api::{ + keys::{KeyPair as NoahXfrKeyPair, PublicKey as NoahXfrPublicKey}, + xfr::structs::OpenAssetRecord as NoahOpenAssetRecord, +}; //////////////////////////////////////////////////////////////////////////////// -pub struct XfrPublicKey(ZeiXfrPublicKey); +pub struct XfrPublicKey(NoahXfrPublicKey); -impl From for XfrPublicKey { - fn from(v: ZeiXfrPublicKey) -> XfrPublicKey { +impl From for XfrPublicKey { + fn from(v: NoahXfrPublicKey) -> XfrPublicKey { XfrPublicKey(v) } } impl Deref for XfrPublicKey { - type Target = ZeiXfrPublicKey; + type Target = NoahXfrPublicKey; fn deref(&self) -> &Self::Target { &self.0 @@ -35,16 +37,16 @@ impl DerefMut for XfrPublicKey { //////////////////////////////////////////////////////////////////////////////// #[derive(Clone)] -pub struct XfrKeyPair(ZeiXfrKeyPair); +pub struct XfrKeyPair(NoahXfrKeyPair); -impl From for XfrKeyPair { - fn from(v: ZeiXfrKeyPair) -> XfrKeyPair { +impl From for XfrKeyPair { + fn from(v: NoahXfrKeyPair) -> XfrKeyPair { XfrKeyPair(v) } } impl Deref for XfrKeyPair { - type Target = ZeiXfrKeyPair; + type Target = NoahXfrKeyPair; fn deref(&self) -> &Self::Target { &self.0 @@ -60,16 +62,16 @@ impl DerefMut for XfrKeyPair { //////////////////////////////////////////////////////////////////////////////// #[derive(Clone)] -pub struct OpenAssetRecord(ZeiOpenAssetRecord); +pub struct OpenAssetRecord(NoahOpenAssetRecord); -impl From for OpenAssetRecord { - fn from(v: ZeiOpenAssetRecord) -> OpenAssetRecord { +impl From for OpenAssetRecord { + fn from(v: NoahOpenAssetRecord) -> OpenAssetRecord { OpenAssetRecord(v) } } impl Deref for OpenAssetRecord { - type Target = ZeiOpenAssetRecord; + type Target = NoahOpenAssetRecord; fn deref(&self) -> &Self::Target { &self.0 diff --git a/src/components/wallet_mobile/src/rust/util.rs b/src/components/wallet_mobile/src/rust/util.rs index b81986a94..7f101e254 100644 --- a/src/components/wallet_mobile/src/rust/util.rs +++ b/src/components/wallet_mobile/src/rust/util.rs @@ -21,6 +21,6 @@ pub fn string_to_c_char(r_string: String) -> *mut c_char { #[cfg(target_arch = "wasm32")] #[inline(always)] -pub fn error_to_jsvalue(e: T) -> JsVal { +pub fn error_to_jsvalue(e: T) -> JsValue { JsValue::from_str(&e.to_string()) } diff --git a/src/components/wallet_mobile/src/wasm/mod.rs b/src/components/wallet_mobile/src/wasm/mod.rs index 14a7fdf23..1361fcd5b 100644 --- a/src/components/wallet_mobile/src/wasm/mod.rs +++ b/src/components/wallet_mobile/src/wasm/mod.rs @@ -6,8 +6,7 @@ use credentials::{ }; use ruc::{d, err::RucResult}; use wasm_bindgen::prelude::*; -use zei::xfr::sig::{XfrKeyPair, XfrPublicKey}; -use zei::xfr::structs::ASSET_TYPE_LENGTH; +use zei::{noah_api::xfr::structs::ASSET_TYPE_LENGTH, XfrKeyPair, XfrPublicKey}; #[wasm_bindgen] /// Generates asset type as a Base64 string from a JSON-serialized JavaScript value. @@ -163,7 +162,6 @@ impl TransactionBuilder { /// @param {BigInt} seq_num - Issuance sequence number. Every subsequent issuance of a given asset type must have a higher sequence number than before. /// @param {BigInt} amount - Amount to be issued. /// @param {boolean} conf_amount - `true` means the asset amount is confidential, and `false` means it's nonconfidential. - /// @param {PublicParams} zei_params - Public parameters necessary to generate asset records. pub fn add_basic_issue_asset( self, key_pair: &XfrKeyPair, @@ -171,18 +169,10 @@ impl TransactionBuilder { seq_num: u64, amount: u64, conf_amount: bool, - zei_params: &PublicParams, ) -> Result { let builder = self .0 - .add_basic_issue_asset( - key_pair, - code, - seq_num, - amount, - conf_amount, - zei_params, - ) + .add_basic_issue_asset(key_pair, code, seq_num, amount, conf_amount) .c(d!()) .map_err(error_to_jsvalue)?; @@ -234,7 +224,7 @@ impl TransactionBuilder { } /// Extracts the serialized form of a transaction. - pub fn transaction(&self) -> String { + pub fn transaction(&mut self) -> String { self.0.transaction() } @@ -403,7 +393,7 @@ impl TransferOperationBuilder { /// @throws Will throw an error if the transaction cannot be balanced. pub fn balance(self) -> Result { let builder = - self.0.balance().c(d!()).map_err(|e| { + self.0.balance(None).c(d!()).map_err(|e| { JsValue::from_str(&format!("Error balancing txn: {}", e)) })?; Ok(TransferOperationBuilder(builder)) diff --git a/src/components/wasm/Cargo.toml b/src/components/wasm/Cargo.toml index de2e9f212..6feae46eb 100644 --- a/src/components/wasm/Cargo.toml +++ b/src/components/wasm/Cargo.toml @@ -14,33 +14,34 @@ license = "" wasm-opt = false [dependencies] -base64 = "0.12" -hex = "0.4.2" +base64 = "0.13" +hex = "0.4.3" js-sys = "0.3.27" -rand_chacha = "0.2" -rand_core = { version = "0.5", default-features = false, features = ["alloc"] } -rand = { version = "0.7", features = ["wasm-bindgen"] } +rand_chacha = "0.3" +rand_core = { version = "0.6", default-features = false, features = ["alloc"] } serde = { version = "1.0.124", features = ["derive"] } serde_json = "1.0" wasm-bindgen = { version = "=0.2.84", features = ["serde-serialize"] } +wasm-bindgen-futures = "^0.4.34" fbnc = { version = "0.2.9", default-features = false} ring = "0.16.19" -aes-gcm = "0.9.0" +aes-gcm = "^0.10.1" bech32 = "0.7.2" +ruc = "1.0" +bs58 = "0.4" # Must enable the "js"-feature, # OR the compiling will fail. getrandom = { version = "0.2", features = ["js"] } -zei = { git = "https://github.com/FindoraNetwork/zei", branch = "stable-main" } -ruc = "1.0" +zei = { package="platform-lib-noah", git = "https://github.com/FindoraNetwork/platform-lib-noah", branch = "develop" } finutils = { path = "../finutils", default-features = false } -globutils = { git = "https://github.com/FindoraNetwork/platform-lib-utils", tag = "v1.0.0" } -credentials = { git = "https://github.com/FindoraNetwork/platform-lib-credentials", tag = "v1.0.0" } -cryptohash = { git = "https://github.com/FindoraNetwork/platform-lib-cryptohash", tag = "v1.0.0" } +globutils = { git = "https://github.com/FindoraNetwork/platform-lib-utils", branch = "develop" } +credentials = { git = "https://github.com/FindoraNetwork/platform-lib-credentials", branch = "develop" } +cryptohash = { git = "https://github.com/FindoraNetwork/platform-lib-cryptohash", branch = "develop" } ledger = { path = "../../ledger" } @@ -68,9 +69,12 @@ features = [ serde = "1.0.124" serde_json = "1.0.41" vergen = "=3.1.0" -wasm-bindgen = { version = "=0.2.84", features = ["serde-serialize"] } [dev-dependencies] # Must enable the "js"-feature, # OR the compiling will fail. getrandom = { version = "0.2", features = ["js"] } +wasm-bindgen-test = "0.3.0" + +[features] +lightweight = ["zei/lightweight"] # Minimize size for only AR2ABAR and ABAR2AR. diff --git a/src/components/wasm/src/wasm.rs b/src/components/wasm/src/wasm.rs index 4bf909c71..6e477d542 100644 --- a/src/components/wasm/src/wasm.rs +++ b/src/components/wasm/src/wasm.rs @@ -16,12 +16,14 @@ mod wasm_data_model; use { crate::wasm_data_model::{ - error_to_jsvalue, AssetRules, AssetTracerKeyPair, AttributeAssignment, - AttributeDefinition, ClientAssetRecord, Credential, CredentialCommitment, - CredentialCommitmentData, CredentialCommitmentKey, CredentialIssuerKeyPair, - CredentialPoK, CredentialRevealSig, CredentialSignature, CredentialUserKeyPair, - OwnerMemo, PublicParams, TracingPolicies, TxoRef, + error_to_jsvalue, AssetRules, AssetTracerKeyPair, AssetType, + AttributeAssignment, AttributeDefinition, AxfrOwnerMemo, AxfrOwnerMemoInfo, + ClientAssetRecord, Credential, CredentialCommitment, CredentialCommitmentData, + CredentialCommitmentKey, CredentialIssuerKeyPair, CredentialPoK, + CredentialRevealSig, CredentialSignature, CredentialUserKeyPair, MTLeafInfo, + OwnerMemo, TracingPolicies, TxoRef, }, + core::str::FromStr, credentials::{ credential_commit, credential_issuer_key_gen, credential_open_commitment, credential_reveal, credential_sign, credential_user_key_gen, credential_verify, @@ -31,6 +33,7 @@ use { cryptohash::sha256, fbnc::NumKey, finutils::txn_builder::{ + AnonTransferOperationBuilder as PlatformAnonTransferOperationBuilder, FeeInput as PlatformFeeInput, FeeInputs as PlatformFeeInputs, TransactionBuilder as PlatformTransactionBuilder, TransferOperationBuilder as PlatformTransferOperationBuilder, @@ -48,7 +51,7 @@ use { globutils::{wallet, HashOf}, ledger::{ data_model::{ - gen_random_keypair, AssetTypeCode, AssetTypePrefix, + gen_random_keypair, get_abar_commitment, AssetTypeCode, AssetTypePrefix, AuthenticatedTransaction, Operation, TransferType, TxOutput, ASSET_TYPE_FRA, BLACK_HOLE_PUBKEY, BLACK_HOLE_PUBKEY_STAKING, TX_FEE_MIN, }, @@ -59,20 +62,36 @@ use { }, rand_chacha::ChaChaRng, rand_core::SeedableRng, - ruc::{d, err::RucResult}, - std::str::FromStr, + ruc::{d, eg, err::RucResult}, + serde::{Deserialize, Serialize}, + std::convert::From, wasm_bindgen::prelude::*, zei::{ - serialization::ZeiFromToBytes, - xfr::{ - asset_record::{open_blind_asset_record as open_bar, AssetRecordType}, - lib::trace_assets as zei_trace_assets, - sig::{XfrKeyPair, XfrPublicKey, XfrSecretKey}, - structs::{ - AssetRecordTemplate, AssetType as ZeiAssetType, XfrBody, - ASSET_TYPE_LENGTH, + noah_algebra::{ + bn254::BN254Scalar, + prelude::{NoahFromToBytes, Scalar}, + }, + noah_api::{ + anon_xfr::{ + decrypt_memo, nullify, parse_memo, + structs::{ + AnonAssetRecord, Commitment, OpenAnonAssetRecord, + OpenAnonAssetRecordBuilder, + }, + }, + xfr::{ + asset_record::{ + open_blind_asset_record as open_bar, AssetRecordType, + AssetRecordType::NonConfidentialAmount_NonConfidentialAssetType, + }, + structs::{ + AssetRecordTemplate, AssetType as NoahAssetType, ASSET_TYPE_LENGTH, + }, + trace_assets as noah_trace_assets, }, }, + noah_crypto::hybrid_encryption::{XPublicKey, XSecretKey}, + OwnerMemo as NoahOwnerMemo, XfrBody, XfrKeyPair, XfrPublicKey, XfrSecretKey, }, }; @@ -80,6 +99,13 @@ use { /// against. const BUILD_ID: &str = concat!(env!("VERGEN_SHA_SHORT"), " ", env!("VERGEN_BUILD_DATE")); +/// Init noah anon xfr +#[cfg(target_arch = "wasm32")] +#[wasm_bindgen] +pub async fn init_noah() -> Result<(), JsValue> { + zei::noah_api::anon_xfr::init_anon_xfr().await +} + #[wasm_bindgen] /// Returns the git commit hash and commit date of the commit this library was built against. pub fn build_id() -> String { @@ -99,28 +125,13 @@ pub fn random_asset_type() -> String { AssetTypeCode::gen_random().to_base64() } -#[wasm_bindgen] -/// Creates a new asset code with prefixing-hashing the original code to query the ledger. -pub fn hash_asset_code(asset_code_string: String) -> Result { - let original_asset_code = AssetTypeCode::new_from_base64(&asset_code_string) - .c(d!()) - .map_err(error_to_jsvalue)?; - - let derived_asset_code = AssetTypeCode::from_prefix_and_raw_asset_type_code( - AssetTypePrefix::UserDefined, - &original_asset_code, - ); - - Ok(derived_asset_code.to_base64()) -} - #[wasm_bindgen] /// Generates asset type as a Base64 string from a JSON-serialized JavaScript value. pub fn asset_type_from_jsvalue(val: &JsValue) -> Result { let code: [u8; ASSET_TYPE_LENGTH] = val.into_serde().c(d!()).map_err(error_to_jsvalue)?; Ok(AssetTypeCode { - val: ZeiAssetType(code), + val: NoahAssetType(code), } .to_base64()) } @@ -155,13 +166,20 @@ pub fn verify_authenticated_txn( #[wasm_bindgen] /// ... pub fn get_null_pk() -> XfrPublicKey { - XfrPublicKey::zei_from_bytes(&[0; 32]).unwrap() + XfrPublicKey::noah_from_bytes(&[0; 32]).unwrap() +} + +/// struct to return list of commitment strings +#[derive(Serialize, Deserialize)] +pub struct CommitmentStringArray { + commitments: Vec, } #[wasm_bindgen] /// Structure that allows users to construct arbitrary transactions. pub struct TransactionBuilder { transaction_builder: PlatformTransactionBuilder, + commitments: Vec, } impl TransactionBuilder { @@ -195,7 +213,7 @@ impl From for PlatformFeeInput { am: fi.am, tr: fi.tr.txo_ref, ar: fi.ar.txo, - om: fi.om.map(|om| om.memo), + om: fi.om.map(|om| NoahOwnerMemo::from_noah(&om.memo).unwrap()), kp: fi.kp, } } @@ -221,7 +239,7 @@ impl FeeInputs { #[allow(missing_docs)] pub fn new() -> Self { FeeInputs { - inner: Vec::with_capacity(1), + inner: Vec::with_capacity(10), } } @@ -244,9 +262,15 @@ impl FeeInputs { tr: TxoRef, ar: ClientAssetRecord, om: Option, - kp: XfrKeyPair, + kp: &XfrKeyPair, ) -> Self { - self.inner.push(FeeInput { am, tr, ar, om, kp }); + self.inner.push(FeeInput { + am, + tr, + ar, + om, + kp: kp.clone(), + }); self } } @@ -300,6 +324,19 @@ impl TransactionBuilder { Ok(self) } + /// As the last operation of BarToAbar transaction, + /// add a static fee to the transaction. + pub fn add_fee_bar_to_abar( + mut self, + inputs: FeeInputs, + ) -> Result { + self.transaction_builder + .add_fee_bar_to_abar(inputs.into()) + .c(d!()) + .map_err(error_to_jsvalue)?; + Ok(self) + } + /// A simple fee checker for mainnet v1.0. /// /// SEE [check_fee](ledger::data_model::Transaction::check_fee) @@ -312,9 +349,20 @@ impl TransactionBuilder { pub fn new(seq_id: u64) -> Self { TransactionBuilder { transaction_builder: PlatformTransactionBuilder::from_seq_id(seq_id), + commitments: Default::default(), } } + /// Deserialize transaction builder from string. + pub fn from_string(s: String) -> Result { + let transaction_builder = serde_json::from_str(&s).map_err(error_to_jsvalue)?; + + Ok(TransactionBuilder { + transaction_builder, + commitments: Default::default(), + }) + } + /// Wraps around TransactionBuilder to add an asset definition operation to a transaction builder instance. /// @example Error handling /// try { @@ -385,7 +433,6 @@ impl TransactionBuilder { /// @param {BigInt} seq_num - Issuance sequence number. Every subsequent issuance of a given asset type must have a higher sequence number than before. /// @param {BigInt} amount - Amount to be issued. /// @param {boolean} conf_amount - `true` means the asset amount is confidential, and `false` means it's nonconfidential. - /// @param {PublicParams} zei_params - Public parameters necessary to generate asset records. pub fn add_basic_issue_asset( mut self, key_pair: &XfrKeyPair, @@ -409,7 +456,6 @@ impl TransactionBuilder { seq_num, amount, confidentiality_flags, - PublicParams::new().get_ref(), ) .c(d!()) .map_err(error_to_jsvalue)?; @@ -440,6 +486,208 @@ impl TransactionBuilder { Ok(self) } + /// Adds an operation to the transaction builder that converts a bar to abar. + /// + /// @param {XfrKeyPair} auth_key_pair - input bar owner key pair + /// @param {AXfrPubKey} abar_pubkey - abar receiver's public key + /// @param {TxoSID} input_sid - txo sid of input bar + /// @param {ClientAssetRecord} input_record - + pub fn add_operation_bar_to_abar( + mut self, + seed: String, + auth_key_pair: &XfrKeyPair, + abar_pubkey: &XfrPublicKey, + txo_sid: u64, + input_record: &ClientAssetRecord, + owner_memo: Option, + ) -> Result { + use hex::FromHex; + + let oar = open_bar( + &input_record.get_bar_ref().into_noah(), + &owner_memo.map(|memo| memo.get_memo_ref().clone()), + &auth_key_pair.into_noah(), + ) + .c(d!()) + .map_err(|e| { + JsValue::from_str(&format!("Could not open asset record: {}", e)) + })?; + let is_bar_transparent = + oar.get_record_type() == NonConfidentialAmount_NonConfidentialAssetType; + + let mut seed = <[u8; 32]>::from_hex(seed).c(d!()).map_err(|e| { + JsValue::from_str(&format!("Failed to parse seed from hex: {}", e)) + })?; + + let (_, c) = self + .get_builder_mut() + .add_operation_bar_to_abar( + seed, + &auth_key_pair.clone(), + &abar_pubkey.clone(), + TxoSID(txo_sid), + &oar, + is_bar_transparent, + ) + .c(d!()) + .map_err(|e| { + JsValue::from_str(&format!("Could not add operation: {}", e)) + })?; + + self.commitments.push(c); + Ok(self) + } + + /// Adds an operation to transaction builder which converts an abar to a bar. + /// + /// @param {AnonAssetRecord} input - the ABAR to be converted + /// @param {AxfrOwnerMemo} axfr owner_memo - the corresponding owner_memo of the ABAR to be converted + /// @param {MTLeafInfo} mt_leaf_info - the Merkle Proof of the ABAR + /// @param {AXfrKeyPair} from_keypair - the owners Anon Key pair + /// @param {XfrPublic} recipient - the BAR owner public key + /// @param {bool} conf_amount - whether the BAR amount should be confidential + /// @param {bool} conf_type - whether the BAR asset type should be confidential + pub fn add_operation_abar_to_bar( + mut self, + input: AnonAssetRecord, + owner_memo: AxfrOwnerMemo, + mt_leaf_info: MTLeafInfo, + from_keypair: &XfrKeyPair, + recipient: &XfrPublicKey, + conf_amount: bool, + conf_type: bool, + ) -> Result { + let oabar = OpenAnonAssetRecordBuilder::from_abar( + &input, + owner_memo.memo, + &from_keypair.into_noah(), + ) + .c(d!()) + .map_err(|e| { + JsValue::from_str(&format!( + "Builder from_abar error: {}", + e.get_lowest_msg() + )) + })? + .mt_leaf_info(mt_leaf_info.get_noah_mt_leaf_info().clone()) + .build() + .c(d!()) + .map_err(|e| { + JsValue::from_str(&format!("Builder build error: {}", e.get_lowest_msg())) + })?; + + let art = match (conf_amount, conf_type) { + (true, true) => AssetRecordType::ConfidentialAmount_ConfidentialAssetType, + (true, false) => { + AssetRecordType::ConfidentialAmount_NonConfidentialAssetType + } + (false, true) => { + AssetRecordType::NonConfidentialAmount_ConfidentialAssetType + } + _ => AssetRecordType::NonConfidentialAmount_NonConfidentialAssetType, + }; + + self.get_builder_mut() + .add_operation_abar_to_bar( + &oabar, + &from_keypair.clone(), + &recipient.clone(), + art, + ) + .c(d!()) + .map_err(|e| { + JsValue::from_str(&format!( + "builder add_operation_abar_to_bar error: {}", + e.get_lowest_msg() + )) + })?; + + Ok(self) + } + + /// Returns a list of commitment base64 strings as json + pub fn get_commitments(&self) -> JsValue { + let r = CommitmentStringArray { + commitments: self + .commitments + .iter() + .map(wallet::commitment_to_base58) + .collect(), + }; + + JsValue::from_serde(&r).unwrap() + } + + /// Adds an operation to transaction builder which transfer a Anon Blind Asset Record + /// + /// @param {AnonAssetRecord} input - input abar + /// @param {AxfrOwnerMemo} axfr owner_memo - input owner memo + /// @param {AXfrKeyPair} from_keypair - abar sender's private key + /// @param {AXfrPubKey} to_pub_key - receiver's Anon public key + /// @param {u64} to_amount - amount to send to receiver + #[allow(clippy::too_many_arguments)] + pub fn add_operation_anon_transfer( + mut self, + input: AnonAssetRecord, + owner_memo: AxfrOwnerMemo, + mt_leaf_info: MTLeafInfo, + from_keypair: &XfrKeyPair, + to_pub_key: &XfrPublicKey, + to_amount: u64, + ) -> Result { + let mut prng = ChaChaRng::from_entropy(); + let input_oabar = OpenAnonAssetRecordBuilder::from_abar( + &input, + owner_memo.memo, + &from_keypair.into_noah(), + ) + .c(d!()) + .map_err(|e| JsValue::from_str(&format!("Could not add operation: {}", e)))? + .mt_leaf_info(mt_leaf_info.get_noah_mt_leaf_info().clone()) + .build() + .c(d!()) + .map_err(|e| JsValue::from_str(&format!("Could not add operation: {}", e)))?; + + if input_oabar.get_amount() <= to_amount { + return Err(JsValue::from_str(&format!( + "Insufficient amount for the input abar: {}", + input_oabar.get_amount() + ))); + } + + let output_oabar = OpenAnonAssetRecordBuilder::new() + .amount(to_amount) + .asset_type(input_oabar.get_asset_type()) + .pub_key(&to_pub_key.into_noah()) + .finalize(&mut prng) + .c(d!()) + .map_err(|e| JsValue::from_str(&format!("Could not add operation: {}", e)))? + .build() + .map_err(|e| { + JsValue::from_str(&format!("Could not add operation: {}", e)) + })?; + let r1 = get_abar_commitment(output_oabar.clone()); + self.commitments.push(r1); + + let (_, note, rem_oabars) = self + .get_builder_mut() + .add_operation_anon_transfer_fees_remainder( + &[input_oabar], + &[output_oabar], + &from_keypair.clone(), + ) + .c(d!()) + .map_err(|e| { + JsValue::from_str(&format!("Could not add operation: {}", e)) + })?; + + for rem_oabar in rem_oabars { + self.commitments.push(get_abar_commitment(rem_oabar)); + } + + Ok(self) + } + #[allow(missing_docs)] pub fn add_operation_delegate( mut self, @@ -562,8 +810,12 @@ impl TransactionBuilder { Ok(self) } - /// Do nothing, compatible with frontend + /// Builds the anon operations from pre-notes pub fn build(mut self) -> Result { + self.get_builder_mut() + .build() + .c(d!()) + .map_err(error_to_jsvalue)?; Ok(self) } @@ -585,7 +837,7 @@ impl TransactionBuilder { } /// Extracts the serialized form of a transaction. - pub fn transaction(&self) -> String { + pub fn transaction(&mut self) -> String { self.get_builder().serialize_str() } @@ -607,7 +859,9 @@ impl TransactionBuilder { pub fn get_owner_memo(&self, idx: usize) -> Option { self.get_builder() .get_owner_memo_ref(idx) - .map(|memo| OwnerMemo { memo: memo.clone() }) + .map(|memo| OwnerMemo { + memo: memo.into_noah(), + }) } } @@ -627,6 +881,11 @@ pub fn transfer_to_utxo_from_account( sk: String, nonce: u64, ) -> Result { + if !recipient.is_ed25519() { + return Err(eg!("recipient can only be ed25519 address")) + .map_err(error_to_jsvalue); + } + let seed = hex::decode(sk).map_err(error_to_jsvalue)?; let mut s = [0u8; 32]; s.copy_from_slice(&seed); @@ -690,6 +949,93 @@ pub fn get_serialized_address(address: String) -> Result { String::from_utf8(sa).map_err(error_to_jsvalue) } +/// Get balance for an Anonymous Blind Asset Record +/// @param {AnonAssetRecord} abar - ABAR for which balance needs to be queried +/// @param {AxfrOwnerMemo} memo - memo corresponding to the abar +/// @param keypair {AXfrKeyPair} - AXfrKeyPair of the ABAR owner +/// @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_anon_balance( + abar: AnonAssetRecord, + memo: AxfrOwnerMemo, + keypair: XfrKeyPair, + mt_leaf_info: MTLeafInfo, +) -> Result { + let oabar = + OpenAnonAssetRecordBuilder::from_abar(&abar, memo.memo, &keypair.into_noah()) + .c(d!()) + .map_err(error_to_jsvalue)? + .mt_leaf_info(mt_leaf_info.get_noah_mt_leaf_info().clone()) + .build() + .c(d!()) + .map_err(error_to_jsvalue)?; + + Ok(oabar.get_amount()) +} + +/// Get OABAR (Open ABAR) using the ABAR, OwnerMemo and MTLeafInfo +/// @param {AnonAssetRecord} abar - ABAR which needs to be opened +/// @param {AxfrOwnerMemo} memo - memo corresponding to the abar +/// @param keypair {AXfrKeyPair} - AXfrKeyPair of the ABAR owner +/// @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: AnonAssetRecord, + memo: AxfrOwnerMemo, + keypair: XfrKeyPair, + mt_leaf_info: MTLeafInfo, +) -> Result { + let oabar = + OpenAnonAssetRecordBuilder::from_abar(&abar, memo.memo, &keypair.into_noah()) + .c(d!()) + .map_err(error_to_jsvalue)? + .mt_leaf_info(mt_leaf_info.get_noah_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) +} + +/// Generate nullifier hash using ABAR, OwnerMemo and MTLeafInfo +/// @param {AnonAssetRecord} abar - ABAR for which balance needs to be queried +/// @param {AxfrOwnerMemo} memo - memo corresponding to the abar +/// @param keypair {AXfrKeyPair} - AXfrKeyPair of the ABAR owner +/// @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: AnonAssetRecord, + memo: AxfrOwnerMemo, + keypair: XfrKeyPair, + mt_leaf_info: MTLeafInfo, +) -> Result { + let oabar = + OpenAnonAssetRecordBuilder::from_abar(&abar, memo.memo, &keypair.into_noah()) + .c(d!()) + .map_err(error_to_jsvalue)? + .mt_leaf_info(mt_leaf_info.get_noah_mt_leaf_info().clone()) + .build() + .c(d!()) + .map_err(error_to_jsvalue)?; + + let n = nullify( + &keypair.into_noah(), + oabar.get_amount(), + oabar.get_asset_type().as_scalar(), + mt_leaf_info.get_noah_mt_leaf_info().uid, + ) + .c(d!()) + .map_err(error_to_jsvalue)?; + let hash = wallet::nullifier_to_base58(&n.0); + Ok(hash) +} + #[wasm_bindgen] #[derive(Default)] /// Structure that enables clients to construct complex transfers. @@ -721,9 +1067,9 @@ impl TransferOperationBuilder { amount: u64, ) -> Result { let oar = open_bar( - asset_record.get_bar_ref(), + &asset_record.get_bar_ref().into_noah(), &owner_memo.map(|memo| memo.get_memo_ref().clone()), - &key, + &key.into_noah(), ) .c(d!()) .map_err(|e| { @@ -763,7 +1109,7 @@ impl TransferOperationBuilder { amount, code.val, asset_record_type, - *recipient, + recipient.into_noah(), policies.get_policies_ref().clone(), ) } else { @@ -771,7 +1117,7 @@ impl TransferOperationBuilder { amount, code.val, asset_record_type, - *recipient, + recipient.into_noah(), ) }; self.get_builder_mut() @@ -940,6 +1286,12 @@ impl TransferOperationBuilder { serde_json::to_string(self.get_builder()).unwrap() } + #[allow(missing_docs)] + pub fn from_string(s: String) -> Result { + let op_builder = serde_json::from_str(&s).c(d!()).map_err(error_to_jsvalue)?; + Ok(TransferOperationBuilder { op_builder }) + } + /// Wraps around TransferOperationBuilder to extract an operation expression as JSON. pub fn transaction(&self) -> Result { let op = self @@ -951,6 +1303,167 @@ impl TransferOperationBuilder { } } +#[wasm_bindgen] +/// Structure that enables clients to construct complex transfers. +pub struct AnonTransferOperationBuilder { + op_builder: PlatformAnonTransferOperationBuilder, +} + +impl AnonTransferOperationBuilder { + #[allow(missing_docs)] + pub fn get_builder(&self) -> &PlatformAnonTransferOperationBuilder { + &self.op_builder + } + + #[allow(missing_docs)] + pub fn get_builder_mut(&mut self) -> &mut PlatformAnonTransferOperationBuilder { + &mut self.op_builder + } +} + +#[wasm_bindgen] +impl AnonTransferOperationBuilder { + /// new is a constructor for AnonTransferOperationBuilder + pub fn new(seq_id: u64) -> Self { + AnonTransferOperationBuilder { + op_builder: PlatformAnonTransferOperationBuilder::new_from_seq_id(seq_id), + } + } + + /// add_input is used to add a new input source for Anon Transfer + /// @param {AnonAssetRecord} abar - input ABAR to transfer + /// @param {AxfrOwnerMemo} memo - memo corresponding to the input abar + /// @param keypair {AXfrKeyPair} - AXfrKeyPair of the ABAR owner + /// @param MTLeafInfo {mt_leaf_info} - the Merkle proof of the ABAR from commitment tree + /// @throws Will throw an error if abar fails to open, input fails to get added to Operation + pub fn add_input( + mut self, + abar: &AnonAssetRecord, + memo: &AxfrOwnerMemo, + keypair: &XfrKeyPair, + mt_leaf_info: MTLeafInfo, + ) -> Result { + let oabar = OpenAnonAssetRecordBuilder::from_abar( + &abar.clone(), + memo.memo.clone(), + &keypair.into_noah(), + ) + .c(d!()) + .map_err(error_to_jsvalue)? + .mt_leaf_info(mt_leaf_info.get_noah_mt_leaf_info().clone()) + .build() + .c(d!()) + .map_err(error_to_jsvalue)?; + + self.get_builder_mut() + .add_input(oabar) + .c(d!()) + .map_err(error_to_jsvalue)?; + + Ok(self) + } + + /// add_output is used to add a output to the Anon Transfer + /// @param amount {u64} - amount to be sent to the receiver + /// @param to {AXfrPubKey} - original pub key of receiver + /// @throws error if ABAR fails to be built + pub fn add_output( + mut self, + amount: u64, + asset_type: String, + to: XfrPublicKey, + ) -> Result { + let mut prng = ChaChaRng::from_entropy(); + + let at = AssetTypeCode::new_from_base64(asset_type.as_str()) + .map_err(error_to_jsvalue)?; + + let oabar_out = OpenAnonAssetRecordBuilder::new() + .amount(amount) + .asset_type(at.val) + .pub_key(&to.into_noah()) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(); + + self.get_builder_mut() + .add_output(oabar_out) + .c(d!()) + .map_err(error_to_jsvalue)?; + + Ok(self) + } + + /// add_keypair is used to add the sender's keypair for the nullifier generation + /// @param to {AXfrKeyPair} - original keypair of sender + /// @throws error if ABAR fails to be built + pub fn add_keypair(mut self, keypair: &XfrKeyPair) -> AnonTransferOperationBuilder { + self.get_builder_mut().add_keypair(keypair.clone()); + + self + } + + /// get_expected_fee is used to gather extra FRA that needs to be spent to make the transaction + /// have enough fees. + pub fn get_expected_fee(&self) -> Result { + self.get_builder() + .extra_fee_estimation() + .map_err(error_to_jsvalue) + } + + /// get_total_fee_estimate + pub fn get_total_fee_estimate(&self) -> Result { + self.get_builder() + .get_total_fee_estimation() + .map_err(error_to_jsvalue) + } + + /// get_commitments returns a list of all the commitments for receiver public keys + pub fn get_commitments(&self) -> JsValue { + let r = CommitmentStringArray { + commitments: self + .get_builder() + .get_commitments() + .iter() + .map(wallet::commitment_to_base58) + .collect(), + }; + + JsValue::from_serde(&r).unwrap() + } + + /// get_commitment_map returns a hashmap of all the commitments mapped to public key, asset, amount + pub fn get_commitment_map(&self) -> JsValue { + let commitment_map = self.get_builder().get_commitment_map(); + JsValue::from_serde(&commitment_map).unwrap() + } + + /// build is used to build proof the Transfer Operation + pub fn build(mut self) -> Result { + self.get_builder_mut() + .build() + .c(d!("error in txn_builder: build")) + .map_err(error_to_jsvalue)?; + + self.get_builder_mut() + .build_txn() + .c(d!()) + .map_err(error_to_jsvalue)?; + + Ok(self) + } + + /// transaction returns the prepared Anon Transfer Operation + /// @param nonce {NoReplayToken} - nonce of the txn to be added to the operation + pub fn transaction(self) -> Result { + self.get_builder() + .serialize_str() + .c(d!()) + .map_err(error_to_jsvalue) + } +} + ///////////// CRYPTO ////////////////////// #[wasm_bindgen] /// Returns a JavaScript object containing decrypted owner record information, @@ -967,9 +1480,9 @@ pub fn open_client_asset_record( keypair: &XfrKeyPair, ) -> Result { open_bar( - record.get_bar_ref(), + &record.get_bar_ref().into_noah(), &owner_memo.map(|memo| memo.get_memo_ref().clone()), - &keypair, + &keypair.into_noah(), ) .c(d!()) .map_err(|e| JsValue::from_str(&format!("Could not open asset record: {}", e))) @@ -988,12 +1501,35 @@ pub fn get_priv_key_str(key_pair: &XfrKeyPair) -> String { serde_json::to_string(key_pair.get_sk_ref()).unwrap() } +#[wasm_bindgen] +/// +pub fn get_priv_key_hex_str_by_mnemonic( + phrase: &str, + num: u32, +) -> Result { + let key_pair = wallet::restore_keypair_from_mnemonic_cus(phrase, 0, 0, num) + .map_err(error_to_jsvalue)?; + let data = key_pair.get_sk_ref().to_bytes(); + Ok(format!("0x{}", hex::encode(&data[1..]))) +} + +#[wasm_bindgen] +/// Extracts the private key as a string from a transfer key pair. +pub fn get_priv_key_str_old(key_pair: &XfrKeyPair) -> String { + base64::encode_config(&key_pair.get_sk_ref().to_bytes(), base64::URL_SAFE) +} + #[wasm_bindgen] /// Creates a new transfer key pair. pub fn new_keypair() -> XfrKeyPair { gen_random_keypair() } +#[wasm_bindgen] +/// Creates a new transfer key pair. +pub fn new_keypair_old() -> XfrKeyPair { + XfrKeyPair::generate(&mut ChaChaRng::from_entropy()) +} #[wasm_bindgen] /// Generates a new keypair deterministically from a seed string and an optional name. pub fn new_keypair_from_seed(seed_str: String, name: Option) -> XfrKeyPair { @@ -1021,14 +1557,14 @@ pub fn public_key_from_base64(pk: &str) -> Result { /// Expresses a transfer key pair as a hex-encoded string. /// To decode the string, use `keypair_from_str` function. pub fn keypair_to_str(key_pair: &XfrKeyPair) -> String { - hex::encode(key_pair.zei_to_bytes()) + hex::encode(key_pair.noah_to_bytes()) } #[wasm_bindgen] /// Constructs a transfer key pair from a hex-encoded string. /// The encode a key pair, use `keypair_to_str` function. pub fn keypair_from_str(str: String) -> XfrKeyPair { - XfrKeyPair::zei_from_bytes(&hex::decode(str).unwrap()).unwrap() + XfrKeyPair::noah_from_bytes(&hex::decode(str).unwrap()).unwrap() } #[wasm_bindgen] @@ -1070,7 +1606,7 @@ pub fn wasm_credential_verify_commitment( issuer_pub_key, commitment.get_ref(), pok.get_ref(), - xfr_pk.as_bytes(), + &xfr_pk.to_bytes(), ) .c(d!()) .map_err(error_to_jsvalue) @@ -1182,7 +1718,7 @@ pub fn wasm_credential_commit( &mut prng, &user_secret_key, credential.get_cred_ref(), - &user_public_key.as_bytes(), + &user_public_key.to_bytes(), ) .c(d!()) .map_err(error_to_jsvalue)?; @@ -1262,15 +1798,11 @@ pub fn trace_assets( // let candidate_assets: Vec = // candidate_assets.into_serde().c(d!()).map_err(error_to_jsvalue)?; let xfr_body: XfrBody = xfr_body.into_serde().c(d!()).map_err(error_to_jsvalue)?; - // let candidate_assets: Vec = candidate_assets - // .iter() - // .map(|asset_type_str| { - // AssetTypeCode::new_from_str(&asset_type_str.to_string()).val - // }) - // .collect(); - let record_data = zei_trace_assets(&xfr_body, tracer_keypair.get_keys()) - .c(d!()) - .map_err(error_to_jsvalue)?; + + let record_data = + noah_trace_assets(&xfr_body.into_noah(), tracer_keypair.get_keys()) + .c(d!()) + .map_err(error_to_jsvalue)?; let record_data: Vec<(u64, String)> = record_data .iter() .map(|(amt, asset_type, _, _)| { @@ -1288,9 +1820,19 @@ pub fn trace_assets( // Author: Chao Ma, github.com/chaosma. // ////////////////////////////////////////// -use aes_gcm::aead::{generic_array::GenericArray, Aead, NewAead}; -use aes_gcm::Aes256Gcm; -use rand::{thread_rng, Rng}; +use crate::wasm_data_model::{AmountAssetType, AnonKeys}; +use aes_gcm::{ + aead::{generic_array::GenericArray, Aead, KeyInit}, + Aes256Gcm, +}; +use base64::URL_SAFE; +use fp_types::H160; +use getrandom::getrandom; +use js_sys::JsString; +use ledger::data_model::{ABARData, TxoSID, BAR_TO_ABAR_TX_FEE_MIN}; +use ledger::staking::Amount; + +use rand_core::{CryptoRng, RngCore}; use ring::pbkdf2; use std::num::NonZeroU32; use std::str; @@ -1316,6 +1858,13 @@ pub fn bech32_to_base64(pk: &str) -> Result { Ok(public_key_to_base64(&pub_key)) } +#[wasm_bindgen] +#[allow(missing_docs)] +pub fn bech32_to_base64_old(pk: &str) -> Result { + public_key_from_bech32(pk) + .map(|pk| base64::encode_config(&pk.to_bytes(), base64::URL_SAFE)) +} + #[wasm_bindgen] #[allow(missing_docs)] pub fn base64_to_bech32(pk: &str) -> Result { @@ -1323,16 +1872,26 @@ pub fn base64_to_bech32(pk: &str) -> Result { Ok(public_key_to_bech32(&pub_key)) } +#[wasm_bindgen] +#[allow(missing_docs)] +pub fn base64_to_base58(data: &str) -> Result { + let byts = base64::decode_config(data, URL_SAFE) + .c(d!()) + .map_err(error_to_jsvalue)?; + + let dat = bs58::encode(byts).into_string(); + Ok(dat) +} + #[wasm_bindgen] #[allow(missing_docs)] pub fn encryption_pbkdf2_aes256gcm(key_pair: String, password: String) -> Vec { const CREDENTIAL_LEN: usize = 32; const IV_LEN: usize = 12; let n_iter = NonZeroU32::new(32).unwrap(); - let mut rng = thread_rng(); let mut salt = [0u8; CREDENTIAL_LEN]; - rng.fill(&mut salt); + getrandom(&mut salt).unwrap(); let mut derived_key = [0u8; CREDENTIAL_LEN]; pbkdf2::derive( pbkdf2::PBKDF2_HMAC_SHA512, @@ -1343,7 +1902,7 @@ pub fn encryption_pbkdf2_aes256gcm(key_pair: String, password: String) -> Vec, password: String) -> S #[wasm_bindgen] #[allow(missing_docs)] -pub fn create_keypair_from_secret(sk_str: String) -> Option { - serde_json::from_str::(&sk_str) - .map(|sk| sk.into_keypair()) - .ok() +pub fn create_keypair_from_secret(sk_str: String) -> Result { + let sk = serde_json::from_str::(&sk_str) + .c(d!()) + .map_err(error_to_jsvalue)?; + + Ok(sk.into_keypair()) } #[wasm_bindgen] @@ -1461,6 +2022,28 @@ pub fn restore_keypair_from_mnemonic_default( .map_err(error_to_jsvalue) } +#[wasm_bindgen] +/// Restore the XfrKeyPair from a mnemonic with a default bip44-path, +/// that is "m/44'/917'/0'/0/0" ("m/44'/coin'/account'/change/address"). +pub fn restore_keypair_from_mnemonic_ed25519( + phrase: &str, +) -> Result { + wallet::restore_keypair_from_mnemonic_ed25519(phrase) + .c(d!()) + .map_err(error_to_jsvalue) +} + +#[wasm_bindgen] +/// Restore the XfrKeyPair from a mnemonic with a default bip44-path, +/// that is "m/44'/917'/0'/0/0" ("m/44'/coin'/account'/change/address"). +pub fn restore_keypair_from_mnemonic_secp256k1( + phrase: &str, +) -> Result { + wallet::restore_keypair_from_mnemonic_secp256k1(phrase) + .c(d!()) + .map_err(error_to_jsvalue) +} + #[wasm_bindgen] /// Restore the XfrKeyPair from a mnemonic with custom params, /// in bip44 form. @@ -1502,10 +2085,22 @@ pub fn fra_get_minimal_fee() -> u64 { TX_FEE_MIN } +/// Fee smaller than this value will be denied. +#[wasm_bindgen] +pub fn fra_get_minimal_fee_for_bar_to_abar() -> u64 { + BAR_TO_ABAR_TX_FEE_MIN +} + +/// Anon fee for a given number of inputs & outputs #[wasm_bindgen] +pub fn get_anon_fee(n_inputs: u32, n_outputs: u32) -> u32 { + PlatformAnonTransferOperationBuilder::get_anon_fee(n_inputs, n_outputs) +} + /// The destination for fee to be transfered to. +#[wasm_bindgen] pub fn fra_get_dest_pubkey() -> XfrPublicKey { - *BLACK_HOLE_PUBKEY + XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY) } #[wasm_bindgen] @@ -1517,13 +2112,13 @@ pub fn get_delegation_target_address() -> String { #[wasm_bindgen] #[allow(missing_docs)] pub fn get_coinbase_address() -> String { - wallet::public_key_to_base64(&BLACK_HOLE_PUBKEY_STAKING) + wallet::public_key_to_base64(&XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY_STAKING)) } #[wasm_bindgen] #[allow(missing_docs)] pub fn get_coinbase_principal_address() -> String { - wallet::public_key_to_base64(&BLACK_HOLE_PUBKEY_STAKING) + wallet::public_key_to_base64(&XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY_STAKING)) } #[wasm_bindgen] @@ -1538,10 +2133,197 @@ pub fn get_delegation_max_amount() -> u64 { MAX_DELEGATION_AMOUNT } +#[wasm_bindgen] +#[allow(missing_docs)] +pub fn x_pubkey_from_string(key_str: &str) -> Result { + wallet::x_public_key_from_base64(key_str) + .c(d!()) + .map_err(error_to_jsvalue) +} + +#[wasm_bindgen] +#[allow(missing_docs)] +pub fn x_secretkey_from_string(key_str: &str) -> Result { + wallet::x_secret_key_from_base64(key_str) + .c(d!()) + .map_err(error_to_jsvalue) +} + +#[wasm_bindgen] +#[allow(missing_docs)] +pub fn abar_from_json(json: JsValue) -> Result { + let abar: ABARData = json.into_serde().c(d!()).map_err(error_to_jsvalue)?; + let c = wallet::commitment_from_base58(abar.commitment.as_str()) + .c(d!()) + .map_err(error_to_jsvalue)?; + + Ok(AnonAssetRecord { commitment: c }) +} + +#[wasm_bindgen] +/// Decrypts an ABAR with owner memo and decryption key +pub fn open_abar( + abar: AnonAssetRecord, + memo: AxfrOwnerMemo, + keypair: &XfrKeyPair, +) -> Result { + let oabar = + OpenAnonAssetRecordBuilder::from_abar(&abar, memo.memo, &keypair.into_noah()) + .map_err(error_to_jsvalue)? + .build() + .map_err(error_to_jsvalue)?; + + let at = AssetTypeCode { + val: oabar.get_asset_type(), + }; + + Ok(AmountAssetType { + amount: oabar.get_amount(), + asset_type: at.to_base64(), + }) +} + +#[wasm_bindgen] +/// Decrypts the owner anon memo. +/// * `memo` - Owner anon memo to decrypt +/// * `key_pair` - Owner anon keypair +/// * `abar` - Associated anonymous blind asset record to check memo info against. +/// Return Error if memo info does not match the commitment or public key. +/// Return Ok(amount, asset_type, blinding) otherwise. +pub fn decrypt_axfr_memo( + memo: &AxfrOwnerMemo, + key_pair: &XfrKeyPair, + abar: &AnonAssetRecord, +) -> Result { + let (amount, asset_type, blind) = + decrypt_memo(&memo.memo, &key_pair.into_noah(), abar) + .c(d!()) + .map_err(error_to_jsvalue)?; + Ok(AxfrOwnerMemoInfo { + amount, + blind, + asset_type: AssetTypeCode { val: asset_type }.to_base64(), + }) +} + +#[wasm_bindgen] +/// Try to decrypt the owner memo to check if it is own. +/// * `memo` - Owner anon memo need to decrypt. +/// * `key_pair` - the memo bytes. +/// Return Ok(amount, asset_type, blinding) if memo is own. +pub fn try_decrypt_axfr_memo( + memo: &AxfrOwnerMemo, + key_pair: &XfrKeyPair, +) -> Result, JsValue> { + let secret_key = key_pair.get_sk_ref().into_noah(); + let res = memo + .get_memo_ref() + .decrypt(&secret_key) + .c(d!()) + .map_err(error_to_jsvalue)?; + Ok(res) +} + +#[wasm_bindgen] +/// Parse the owner memo from bytes. +/// * `bytes` - the memo plain bytes. +/// * `key_pair` - the memo bytes. +/// * `abar` - Associated anonymous blind asset record to check memo info against. +/// Return Error if memo info does not match the commitment. +/// Return Ok(amount, asset_type, blinding) otherwise. +pub fn parse_axfr_memo( + bytes: &[u8], + key_pair: &XfrKeyPair, + abar: &AnonAssetRecord, +) -> Result { + let (amount, asset_type, blind) = parse_memo(bytes, &key_pair.into_noah(), abar) + .c(d!()) + .map_err(error_to_jsvalue)?; + Ok(AxfrOwnerMemoInfo { + amount, + blind, + asset_type: AssetTypeCode { val: asset_type }.to_base64(), + }) +} + +#[wasm_bindgen] +/// Convert Commitment to AnonAssetRecord. +pub fn commitment_to_aar(commitment: Commitment) -> AnonAssetRecord { + AnonAssetRecord { commitment } +} + #[cfg(test)] #[allow(missing_docs)] mod test { use super::*; + use wasm_bindgen_test::*; + + #[wasm_bindgen_test] + //This contains only the positive tests with the fees included + fn extra_fee_test() { + let mut prng = ChaChaRng::from_seed([0u8; 32]); + + let amount = 6000000000u64; + + //let amount_output = amount / 3; + let amount_output = amount; + + let asset_type = ASSET_TYPE_FRA; + + // simulate input abar + let (mut oabar, keypair_in) = gen_oabar_and_keys(&mut prng, amount, asset_type); + + let asset_type_out = ASSET_TYPE_FRA; + + //Simulate output abar + let (mut oabar_out, _keypair_out) = + gen_oabar_and_keys(&mut prng, amount_output, asset_type_out); + + let mut ts = AnonTransferOperationBuilder::new(1); + + ts.get_builder_mut().add_input(oabar); + + ts.get_builder_mut().add_output(oabar_out); + + /* + Extra_fee_estimation works as follows + 1.- compute estimated_fees + 2.- compute FRA_excess + fra_excess = fra_input_sum - fra_output_sum; + if (fra_excess >= estimated_fees) => 0 + else (estimated_fees > fra_excess) => new_fees_estimation(n + 1 inputs, m + 1 outputs) + */ + + let estimated_fees_gt_fra_excess = ts.get_expected_fee(); + + assert!(estimated_fees_gt_fra_excess.unwrap() > 0); + + let (mut oabar_2, keypair_in_2) = + gen_oabar_and_keys(&mut prng, 2 * amount, asset_type); + + ts.get_builder_mut().add_input(oabar_2); + + let fra_excess_gt_fees_estimation = ts.get_expected_fee(); + + assert_eq!(fra_excess_gt_fees_estimation, Ok(0)); + } + + fn gen_oabar_and_keys( + prng: &mut R, + amount: u64, + asset_type: NoahAssetType, + ) -> (OpenAnonAssetRecord, XfrKeyPair) { + let keypair = XfrKeyPair::generate(prng); + let oabar = OpenAnonAssetRecordBuilder::new() + .amount(u64::from(amount)) + .asset_type(asset_type) + .pub_key(&keypair.get_pk().into_noah()) + .finalize(prng) + .unwrap() + .build() + .unwrap(); + (oabar, keypair) + } #[test] fn t_keypair_conversion() { @@ -1629,4 +2411,26 @@ mod test { serde_json::from_str::(&actual_serialized_json).unwrap(); assert_eq!(res.max_units, None); } + + #[test] + fn test_keypair_from_mnemonic() { + let phrase1 = "museum combine night carry artefact actress sugar amount kitchen change ill room walk potato beef similar claw fossil gate chalk domain chronic utility engage"; + let phrase2 = "museum combine night carry artefact actress sugar amount kitchen change ill room walk potato beef similar claw fossil gate chalk domain chronic utility engage"; + + let kp1 = restore_keypair_from_mnemonic_default(phrase1).unwrap(); + println!( + "{} {}", + serde_json::to_string_pretty(&kp1).unwrap(), + wallet::public_key_to_bech32(kp1.get_pk_ref()) + ); + + let kp2 = restore_keypair_from_mnemonic_default(phrase2).unwrap(); + println!( + "{} {}", + serde_json::to_string_pretty(&kp2).unwrap(), + wallet::public_key_to_bech32(kp2.get_pk_ref()) + ); + + assert_eq!(kp1.get_sk(), kp2.get_sk()); + } } diff --git a/src/components/wasm/src/wasm_data_model.rs b/src/components/wasm/src/wasm_data_model.rs index 4b9666eb0..cb3eaed5e 100644 --- a/src/components/wasm/src/wasm_data_model.rs +++ b/src/components/wasm/src/wasm_data_model.rs @@ -6,6 +6,7 @@ use { Credential as PlatformCredential, }, globutils::{wallet, HashOf}, + js_sys::JsString, ledger::data_model::{ AssetRules as PlatformAssetRules, AssetType as PlatformAssetType, AuthenticatedUtxo, SignatureRules as PlatformSignatureRules, TxOutput, @@ -17,46 +18,23 @@ use { serde::{Deserialize, Serialize}, wasm_bindgen::prelude::*, zei::{ - setup::PublicParams as ZeiPublicParams, - xfr::{ - sig::XfrPublicKey, - structs::{ + noah_algebra::{bn254::BN254Scalar, ristretto::PedersenCommitmentRistretto}, + noah_api::{ + anon_xfr::structs::{ + AnonAssetRecord, AxfrOwnerMemo as NoahAxfrOwnerMemo, + MTLeafInfo as NoahMTLeafInfo, + }, + xfr::structs::{ AssetTracerDecKeys, AssetTracerEncKeys, - AssetTracerKeyPair as ZeiAssetTracerKeyPair, BlindAssetRecord, - IdentityRevealPolicy, OwnerMemo as ZeiOwnerMemo, - TracingPolicies as ZeiTracingPolicies, - TracingPolicy as ZeiTracingPolicy, + AssetTracerKeyPair as NoahAssetTracerKeyPair, IdentityRevealPolicy, + OwnerMemo as NoahOwnerMemo, TracingPolicies as NoahTracingPolicies, + TracingPolicy as NoahTracingPolicy, }, }, + BlindAssetRecord, XfrPublicKey, }, }; -#[wasm_bindgen] -/// Public parameters necessary for generating asset records. Generating this is expensive and -/// should be done as infrequently as possible. -/// @see {@link module:Findora-Wasm~TransactionBuilder#add_basic_issue_asset|add_basic_issue_asset} -/// for information using public parameters to create issuance asset records. -pub struct PublicParams { - pub(crate) params: ZeiPublicParams, -} - -#[wasm_bindgen] -#[allow(clippy::new_without_default)] -impl PublicParams { - /// Generates a new set of parameters. - pub fn new() -> PublicParams { - PublicParams { - params: ZeiPublicParams::default(), - } - } -} - -impl PublicParams { - pub fn get_ref(&self) -> &ZeiPublicParams { - &self.params - } -} - #[wasm_bindgen] /// Indicates whether the TXO ref is an absolute or relative value. #[derive(Copy, Clone)] @@ -183,7 +161,10 @@ impl ClientAssetRecord { /// fetch an asset record from the ledger server. pub fn from_json(val: &JsValue) -> Result { Ok(ClientAssetRecord { - txo: val.into_serde().c(d!()).map_err(error_to_jsvalue)?, + txo: val + .into_serde() + .c(d!()) + .map_err(|_| JsValue::from_str("format json error"))?, }) } @@ -204,7 +185,7 @@ impl ClientAssetRecord { /// @see {@link module:Findora-Wasm~AssetRules#add_tracing_policy|add_tracing_policy} for information about how to add a tracing policy to /// an asset definition. pub struct AssetTracerKeyPair { - pub(crate) keypair: ZeiAssetTracerKeyPair, + pub(crate) keypair: NoahAssetTracerKeyPair, } #[wasm_bindgen] @@ -213,7 +194,7 @@ impl AssetTracerKeyPair { pub fn new() -> Self { let mut small_rng = ChaChaRng::from_entropy(); AssetTracerKeyPair { - keypair: ZeiAssetTracerKeyPair::generate(&mut small_rng), + keypair: NoahAssetTracerKeyPair::generate(&mut small_rng), } } } @@ -232,7 +213,7 @@ impl AssetTracerKeyPair { &self.keypair.dec_key } - pub fn get_keys(&self) -> &ZeiAssetTracerKeyPair { + pub fn get_keys(&self) -> &NoahAssetTracerKeyPair { &self.keypair } } @@ -242,7 +223,7 @@ impl AssetTracerKeyPair { /// Asset owner memo. Contains information needed to decrypt an asset record. /// @see {@link module:Findora-Wasm.ClientAssetRecord|ClientAssetRecord} for more details about asset records. pub struct OwnerMemo { - pub(crate) memo: ZeiOwnerMemo, + pub(crate) memo: NoahOwnerMemo, } #[wasm_bindgen] @@ -257,12 +238,13 @@ impl OwnerMemo { /// "lock":{"ciphertext":[119,54,117,136,125,133,112,193],"encoded_rand":"8KDql2JphPB5WLd7-aYE1bxTQAcweFSmrqymLvPDntM="} /// } pub fn from_json(val: &JsValue) -> Result { - let zei_owner_memo: ZeiOwnerMemo = + let noah_owner_memo: NoahOwnerMemo = val.into_serde().c(d!()).map_err(error_to_jsvalue)?; Ok(OwnerMemo { - memo: ZeiOwnerMemo { - blind_share: zei_owner_memo.blind_share, - lock: zei_owner_memo.lock, + memo: NoahOwnerMemo { + key_type: noah_owner_memo.key_type, + blind_share_bytes: noah_owner_memo.blind_share_bytes, + lock_bytes: noah_owner_memo.lock_bytes, }, }) } @@ -276,11 +258,79 @@ impl OwnerMemo { } impl OwnerMemo { - pub fn get_memo_ref(&self) -> &ZeiOwnerMemo { + pub fn get_memo_ref(&self) -> &NoahOwnerMemo { + &self.memo + } +} + +#[wasm_bindgen] +#[derive(Deserialize, Clone)] +/// Asset owner memo. Contains information needed to decrypt an asset record. +/// @see {@link module:Findora-Wasm.ClientAssetRecord|ClientAssetRecord} for more details about asset records. +pub struct AxfrOwnerMemo { + pub(crate) memo: NoahAxfrOwnerMemo, +} + +#[wasm_bindgen] +impl AxfrOwnerMemo { + /// Builds an owner memo from a JSON-serialized JavaScript value. + /// @param {JsValue} val - JSON owner memo fetched from query server with the `get_owner_memo/{sid}` route, + /// where `sid` can be fetched from the query server with the `get_owned_utxos/{address}` route. See the example below. + /// + /// @example + /// { + /// "blind_share":[91,251,44,28,7,221,67,155,175,213,25,183,70,90,119,232,212,238,226,142,159,200,54,19,60,115,38,221,248,202,74,248], + /// "lock":{"ciphertext":[119,54,117,136,125,133,112,193],"encoded_rand":"8KDql2JphPB5WLd7-aYE1bxTQAcweFSmrqymLvPDntM="} + /// } + pub fn from_json(val: &JsValue) -> Result { + let noah_owner_memo: NoahAxfrOwnerMemo = + val.into_serde().c(d!()).map_err(error_to_jsvalue)?; + Ok(AxfrOwnerMemo { + memo: noah_owner_memo, + }) + } + + /// Creates a clone of the owner memo. + pub fn clone(&self) -> Self { + AxfrOwnerMemo { + memo: self.memo.clone(), + } + } +} + +impl AxfrOwnerMemo { + pub fn get_memo_ref(&self) -> &NoahAxfrOwnerMemo { &self.memo } } +#[wasm_bindgen] +/// Asset owner memo decrypted info. contains amount, asset_type and blind. +pub struct AxfrOwnerMemoInfo { + pub(crate) amount: u64, + pub(crate) asset_type: String, + pub(crate) blind: BN254Scalar, +} + +#[wasm_bindgen] +#[allow(missing_docs)] +impl AxfrOwnerMemoInfo { + #[wasm_bindgen(getter)] + pub fn amount(&self) -> u64 { + self.amount + } + + #[wasm_bindgen(getter)] + pub fn asset_type(&self) -> String { + self.asset_type.clone() + } + + #[wasm_bindgen(getter)] + pub fn blind(&self) -> BN254Scalar { + self.blind + } +} + #[derive(Serialize, Deserialize)] pub(crate) struct AttributeDefinition { pub name: String, @@ -330,7 +380,7 @@ impl CredentialRevealSig { /// credential commitment. pub fn get_commitment(&self) -> CredentialCommitment { CredentialCommitment { - commitment: self.sig.sig_commitment.clone(), + commitment: self.sig.cm.clone(), } } /// Returns the underlying proof of knowledge that the credential is valid. @@ -338,7 +388,7 @@ impl CredentialRevealSig { /// credential commitment. pub fn get_pok(&self) -> CredentialPoK { CredentialPoK { - pok: self.sig.pok.clone(), + pok: self.sig.proof_open.clone(), } } } @@ -576,11 +626,11 @@ impl SignatureRules { /// A collection of tracing policies. Use this object when constructing asset transfers to generate /// the correct tracing proofs for traceable assets. pub struct TracingPolicies { - pub(crate) policies: ZeiTracingPolicies, + pub(crate) policies: NoahTracingPolicies, } impl TracingPolicies { - pub fn get_policies_ref(&self) -> &ZeiTracingPolicies { + pub fn get_policies_ref(&self) -> &NoahTracingPolicies { &self.policies } } @@ -589,13 +639,13 @@ impl TracingPolicies { /// Tracing policy for asset transfers. Can be configured to track credentials, the asset type and /// amount, or both. pub struct TracingPolicy { - pub(crate) policy: ZeiTracingPolicy, + pub(crate) policy: NoahTracingPolicy, } #[wasm_bindgen] impl TracingPolicy { pub fn new_with_tracing(tracing_key: &AssetTracerKeyPair) -> Self { - let policy = ZeiTracingPolicy { + let policy = NoahTracingPolicy { enc_keys: tracing_key.get_enc_key().clone(), asset_tracing: true, identity_tracing: None, @@ -615,7 +665,7 @@ impl TracingPolicy { cred_issuer_pub_key: cred_issuer_key.get_ref().clone(), reveal_map, }; - let policy = ZeiTracingPolicy { + let policy = NoahTracingPolicy { enc_keys: tracing_key.get_enc_key().clone(), asset_tracing: tracing, identity_tracing: Some(identity_policy), @@ -625,7 +675,7 @@ impl TracingPolicy { } impl TracingPolicy { - pub fn get_ref(&self) -> &ZeiTracingPolicy { + pub fn get_ref(&self) -> &NoahTracingPolicy { &self.policy } } @@ -719,3 +769,94 @@ impl AssetRules { pub(crate) fn error_to_jsvalue(e: T) -> JsValue { JsValue::from_str(&e.to_string()) } + +#[wasm_bindgen] +#[derive(Default, Clone)] +pub struct MTLeafInfo { + object: NoahMTLeafInfo, +} + +impl MTLeafInfo { + pub fn get_noah_mt_leaf_info(&self) -> &NoahMTLeafInfo { + &self.object + } +} + +#[wasm_bindgen] +impl MTLeafInfo { + pub fn from_json(json: &JsValue) -> Result { + let mt_leaf_info: NoahMTLeafInfo = json + .into_serde() + .c(d!()) + .map_err(|_| JsValue::from_str("format json error"))?; + Ok(MTLeafInfo { + object: mt_leaf_info, + }) + } + + pub fn to_json(&self) -> Result { + serde_json::to_string(&self.object) + .map(|s| JsValue::from_str(&s)) + .c(d!()) + .map_err(error_to_jsvalue) + } +} + +#[wasm_bindgen] +pub struct AmountAssetType { + pub amount: u64, + pub(crate) asset_type: String, +} + +#[wasm_bindgen] +impl AmountAssetType { + #[wasm_bindgen(getter)] + pub fn asset_type(&self) -> String { + self.asset_type.clone() + } +} + +/// AnonKeys is used to store keys for Anon proofs +#[wasm_bindgen] +#[derive(Serialize, Deserialize)] +pub struct AnonKeys { + pub(crate) secret_key: String, + pub(crate) pub_key: String, +} + +/// AnonKeys is a struct to store keys required for anon transfer +#[wasm_bindgen] +#[allow(missing_docs)] +impl AnonKeys { + pub fn from_json(json: &JsValue) -> Result { + let anon_keys: AnonKeys = json.into_serde().c(d!()).map_err(error_to_jsvalue)?; + Ok(anon_keys) + } + + pub fn to_json(&self) -> Result { + serde_json::to_string(&self) + .map(|s| JsValue::from_str(&s)) + .c(d!()) + .map_err(error_to_jsvalue) + } + + #[wasm_bindgen(getter)] + pub fn secret_key(&self) -> String { + self.secret_key.clone() + } + + #[wasm_bindgen(setter)] + pub fn set_secret_key(&mut self, secret_key: String) { + self.secret_key = secret_key; + } + + #[wasm_bindgen(getter)] + pub fn pub_key(&self) -> String { + self.pub_key.clone() + } + + #[wasm_bindgen(setter)] + pub fn set_pub_key(&mut self, pub_key: String) { + self.pub_key = pub_key; + } +} diff --git a/src/ledger/Cargo.toml b/src/ledger/Cargo.toml index 9ea5afd6c..3fb8a1e77 100644 --- a/src/ledger/Cargo.toml +++ b/src/ledger/Cargo.toml @@ -6,51 +6,52 @@ edition = "2021" build = "build.rs" [dependencies] -base64 = "0.12" +base64 = "0.13" +bs58 = "0.4" bincode = "1.3.1" -byteorder = "1.0.0" -curve25519-dalek = { version = "3.0", features = ["serde"] } -ed25519-dalek = "1.0.0" -hex = "0.4.2" +byteorder = "1.0.0" +curve25519-dalek = { package = "noah-curve25519-dalek", version = "4.0.0", default-features = false, features = ['serde'] } +ed25519-dalek = { package = "noah-ed25519-dalek", git = "https://github.com/FindoraNetwork/ed25519-dalek", tag = "v4.0.0" } +digest = '0.10' +hex = "0.4.3" lazy_static = { version = "1.2.0" } tracing = "0.1" -rand = "=0.7.3" -rand_chacha = "0.2" -rand_core = { version = "0.5", default-features = false, features = ["alloc"] } +rand = "0.8" +rand_chacha = "0.3" +rand_core = { version = "0.6", default-features = false, features = ["alloc"] } serde = { version = "1.0.124", features = ["derive"] } serde_derive = "1.0" serde_json = "1.0" serde-strz = "1.1.1" -sha2 = "0.9.5" +sha2 = "0.10" unicode-normalization = "0.1.13" time = "0.3" -tendermint = { git = "https://github.com/FindoraNetwork/tendermint-rs", tag = "v0.19.0a-fk" } +tendermint = { git = "https://github.com/FindoraNetwork/tendermint-rs", tag = "v0.19.0c" } indexmap = { version = "1.6.2", features = ["serde"] } config = { path = "../components/config" } fp-types = { path = "../components/contracts/primitives/types" } fp-utils = { path = "../components/contracts/primitives/utils" } ruc = "1.0" -zei = { git = "https://github.com/FindoraNetwork/zei", branch = "stable-main" } -zeiutils = { package = "utils", git = "https://github.com/FindoraNetwork/zei", branch = "stable-main" } +zei = { package="platform-lib-noah", git = "https://github.com/FindoraNetwork/platform-lib-noah", branch = "develop" } bulletproofs = { package = "bulletproofs", git = "https://github.com/FindoraNetwork/bp", rev = "57633a", features = ["yoloproofs"] } -noah-algebra = { git = "https://github.com/FindoraNetwork/noah", tag = "v0.4.3-1" } -noah-crypto = { git = "https://github.com/FindoraNetwork/noah", tag = "v0.4.3-1" } +itertools = "0.10" fbnc = { version = "0.2.9", default-features = false} once_cell = "1" num-bigint = "0.4.3" -globutils = { git = "https://github.com/FindoraNetwork/platform-lib-utils", tag = "v1.0.0" } -bitmap = { git = "https://github.com/FindoraNetwork/platform-lib-bitmap", tag = "v1.0.0" } -cryptohash = { git = "https://github.com/FindoraNetwork/platform-lib-cryptohash", tag = "v1.0.0" } -credentials = { git = "https://github.com/FindoraNetwork/platform-lib-credentials", tag = "v1.0.0" } -merkle_tree = { git = "https://github.com/FindoraNetwork/platform-lib-merkle", tag = "v1.0.0" } -sliding_set = { git = "https://github.com/FindoraNetwork/platform-lib-slidingset", tag = "v1.0.0" } +globutils = { git = "https://github.com/FindoraNetwork/platform-lib-utils", branch = "develop" } +bitmap = { git = "https://github.com/FindoraNetwork/platform-lib-bitmap", branch = "develop" } +cryptohash = { git = "https://github.com/FindoraNetwork/platform-lib-cryptohash", branch = "develop" } +credentials = { git = "https://github.com/FindoraNetwork/platform-lib-credentials", branch = "develop" } +merkle_tree = { git = "https://github.com/FindoraNetwork/platform-lib-merkle", branch = "develop" } +sliding_set = { git = "https://github.com/FindoraNetwork/platform-lib-slidingset", branch = "develop" } [features] -default = [] +default = ["fin_storage"] diskcache = ["fbnc/diskcache"] debug_env = ["config/debug_env"] abci_mock = [] +fin_storage = ["storage", "fin_db"] [dev-dependencies] lazy_static = "1.4.0" @@ -61,11 +62,13 @@ features = ["f16", "serde"] [target.'cfg(not(target_arch = "wasm32"))'.dependencies] parking_lot = "0.12" -# sodiumoxide = "0.2.1" fs2 = "0.4" +storage = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.6", optional = true } +fin_db = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v1.1.6", optional = true } +sparse_merkle_tree = { git = "https://github.com/FindoraNetwork/platform-lib-sparse-merkle", branch = "develop" } [target.'cfg(target_arch = "wasm32")'.dependencies] parking_lot = { version = "0.11.1", features = ["wasm-bindgen"] } [build-dependencies] -vergen = "=3.1.0" +vergen = "=3.1.0" \ No newline at end of file diff --git a/src/ledger/src/converter/mod.rs b/src/ledger/src/converter/mod.rs index 08526f293..802ecc9d9 100644 --- a/src/ledger/src/converter/mod.rs +++ b/src/ledger/src/converter/mod.rs @@ -13,9 +13,9 @@ use { ruc::*, serde::{Deserialize, Serialize}, std::sync::atomic::{AtomicI64, Ordering}, - zei::xfr::{ - sig::XfrPublicKey, - structs::{AssetType, XfrAmount, XfrAssetType}, + zei::{ + noah_api::xfr::structs::{AssetType, XfrAmount, XfrAssetType}, + XfrPublicKey, }, }; #[allow(missing_docs)] @@ -155,7 +155,8 @@ pub fn check_convert_account( )); } if let XfrAssetType::NonConfidential(ty) = o.record.asset_type { - if o.record.public_key == *BLACK_HOLE_PUBKEY_STAKING + if o.record.public_key + == XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY_STAKING) && ty == expected_asset { if let XfrAmount::NonConfidential(amount) = o.record.amount { diff --git a/src/ledger/src/data_model/__trash__.rs b/src/ledger/src/data_model/__trash__.rs index 54eb3a04b..b73009afc 100644 --- a/src/ledger/src/data_model/__trash__.rs +++ b/src/ledger/src/data_model/__trash__.rs @@ -12,8 +12,7 @@ use { crate::data_model::AssetTypeCode, fixed::types::I20F12, serde::{Deserialize, Serialize}, - zei::xfr::sig::XfrPublicKey, - zei::xfr::structs::AssetType, + zei::{noah_api::xfr::structs::AssetType, XfrPublicKey}, }; #[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)] diff --git a/src/ledger/src/data_model/effects.rs b/src/ledger/src/data_model/effects.rs index 491d0835d..494edbef2 100644 --- a/src/ledger/src/data_model/effects.rs +++ b/src/ledger/src/data_model/effects.rs @@ -1,9 +1,10 @@ use { crate::{ data_model::{ - AssetType, AssetTypeCode, DefineAsset, IssueAsset, IssuerPublicKey, Memo, - NoReplayToken, Operation, Transaction, TransferAsset, TransferType, - TxOutput, TxnTempSID, TxoRef, TxoSID, UpdateMemo, + AbarConvNote, AbarToBarOps, AnonTransferOps, AssetType, AssetTypeCode, + BarToAbarOps, DefineAsset, IssueAsset, IssuerPublicKey, Memo, NoReplayToken, + Operation, Transaction, TransferAsset, TransferType, TxOutput, TxnTempSID, + TxoRef, TxoSID, UpdateMemo, }, staking::{ self, @@ -28,21 +29,27 @@ use { sync::Arc, }, zei::{ - serialization::ZeiFromToBytes, - setup::PublicParams, - xfr::{ - lib::verify_xfr_body, - sig::XfrPublicKey, - structs::{XfrAmount, XfrAssetType}, + noah_algebra::serialization::NoahFromToBytes, + noah_api::{ + anon_xfr::{ + abar_to_abar::AXfrNote, + structs::{AnonAssetRecord, Nullifier}, + }, + parameters::bulletproofs::BulletproofParams, + xfr::{ + structs::{XfrAmount, XfrAssetType}, + verify_xfr_body, + }, }, + XfrPublicKey, }, }; lazy_static! { static ref PRNG: Arc> = Arc::new(Mutex::new(ChaChaRng::from_entropy())); - static ref PARAMS: Arc> = - Arc::new(Mutex::new(PublicParams::default())); + static ref PARAMS: Arc> = + Arc::new(Mutex::new(BulletproofParams::default())); } /// Check operations in the context of a tx, partially. @@ -91,6 +98,12 @@ pub struct TxnEffect { pub fra_distributions: Vec, /// Staking operations pub update_stakers: Vec, + /// Newly created Anon Blind Asset Records + pub bar_conv_abars: Vec, + /// Body of Abar to Bar conversions + pub abar_conv_inputs: Vec, + /// New anon transfer bodies + pub axfr_bodies: Vec, /// replace staker operations pub replace_stakers: Vec, } @@ -197,6 +210,18 @@ impl TxnEffect { Operation::ConvertAccount(i) => { check_nonce!(i) } + Operation::BarToAbar(i) => { + check_nonce!(i); + te.add_bar_to_abar(i).c(d!())?; + } + Operation::AbarToBar(i) => { + check_nonce!(i); + te.add_abar_to_bar(i).c(d!())?; + } + Operation::TransferAnonAsset(i) => { + check_nonce!(i); + te.add_anon_transfer(i).c(d!())?; + } } } @@ -262,7 +287,7 @@ impl TxnEffect { // (1), within this transaction //let v = vec![]; - let iss_nums = self.new_issuance_nums.entry(code).or_insert_with(Vec::new); + let iss_nums = self.new_issuance_nums.entry(code).or_default(); if let Some(last_num) = iss_nums.last() { if seq_num <= *last_num { @@ -323,10 +348,10 @@ impl TxnEffect { // 1) The signatures on the body (a) all are valid and (b) // there is a signature for each input key // - Fully checked here - // 2) The UTXOs (a) exist on the ledger and (b) match the zei transaction. + // 2) The UTXOs (a) exist on the ledger and (b) match the noah transaction. // - Partially checked here -- anything which hasn't // been checked will appear in `input_txos` - // 3) The zei transaction is valid. + // 3) The noah transaction is valid. // - Checked here and in check_txn_effects // 4) Lien assignments match up // - Checked within a transaction here, recorded for @@ -348,14 +373,72 @@ impl TxnEffect { return Err(eg!()); } - // Transfer outputs must match outputs zei transaction + // Refuse any transfer with policies for now + let c1 = trn + .body + .policies + .inputs_tracing_policies + .iter() + .any(|x| !x.is_empty()); + let c2 = trn + .body + .policies + .outputs_tracing_policies + .iter() + .any(|x| !x.is_empty()); + let c3 = trn + .body + .policies + .inputs_sig_commitments + .iter() + .any(|x| !x.is_none()); + let c4 = trn + .body + .policies + .outputs_sig_commitments + .iter() + .any(|x| !x.is_none()); + let c5 = trn + .body + .transfer + .asset_tracing_memos + .iter() + .any(|x| !x.is_empty()); + let c6 = trn + .body + .transfer + .proofs + .asset_tracing_proof + .inputs_identity_proofs + .iter() + .any(|x| !x.is_empty()); + let c7 = trn + .body + .transfer + .proofs + .asset_tracing_proof + .outputs_identity_proofs + .iter() + .any(|x| !x.is_empty()); + let c8 = !trn + .body + .transfer + .proofs + .asset_tracing_proof + .asset_type_and_amount_proofs + .is_empty(); + if c1 || c2 || c3 || c4 || c5 || c6 || c7 || c8 { + return Err(eg!()); + } + + // Transfer outputs must match outputs noah transaction for (output, record) in trn .body .outputs .iter() .zip(trn.body.transfer.outputs.iter()) { - if output.record != *record { + if output.record != record.clone() { return Err(eg!()); } } @@ -405,12 +488,12 @@ impl TxnEffect { if !trn.body.verify_body_signature(sig) { return Err(eg!()); } - input_keys.insert(sig.address.key.zei_to_bytes()); + input_keys.insert(sig.address.key.noah_to_bytes()); } // (1b) all input record owners have signed for record in trn.body.transfer.inputs.iter() { - if !input_keys.contains(&record.public_key.zei_to_bytes()) { + if !input_keys.contains(&record.public_key.noah_to_bytes()) { return Err(eg!()); } } @@ -418,7 +501,7 @@ impl TxnEffect { verify_xfr_body( prng, params, - &trn.body.transfer, + &trn.body.transfer.into_noah(), &trn.body.policies.to_ref(), ) .c(d!())?; @@ -455,7 +538,8 @@ impl TxnEffect { } Some(txo) => { // (2).(b) - if &txo.record != record || txo.lien != lien.cloned() { + if txo.record != record.clone() || txo.lien != lien.cloned() + { return Err(eg!()); } self.internally_spent_txos.push(txo.clone()); @@ -535,6 +619,75 @@ impl TxnEffect { Ok(()) } + + /// A bar to abar note is valid iff + /// 1. the signature is correct, + /// 2. the ZKP can be verified, + /// 3. the input txos are unspent. (checked in finish block) + /// # Arguments + /// * `bar_to_abar` - the BarToAbar Operation body + /// returns error if validation fails + fn add_bar_to_abar(&mut self, bar_to_abar: &BarToAbarOps) -> Result<()> { + // verify the note signature & Plonk proof + bar_to_abar.verify()?; + + // list input_txo to spend + self.input_txos.insert( + bar_to_abar.txo_sid, + TxOutput { + id: None, + record: bar_to_abar.input_record(), + lien: None, + }, + ); + // push new ABAR created + self.bar_conv_abars.push(bar_to_abar.output_record()); + Ok(()) + } + + /// An abar to bar note is valid iff + /// 1. the signature is correct, + /// 2. the ZKP can be verified, + /// 3. the input ABARs are unspent. (checked in finish block) + /// # Arguments + /// * abar_to_bar - The Operation for AbarToBar + /// returns an error if validation fails + fn add_abar_to_bar(&mut self, abar_to_bar: &AbarToBarOps) -> Result<()> { + // collect body in TxnEffect to verify ZKP later with merkle root + self.abar_conv_inputs.push(abar_to_bar.note.clone()); + // collect newly created BARs + self.txos.push(Some(TxOutput { + id: None, + record: abar_to_bar.note.get_output(), + lien: None, + })); + + Ok(()) + } + + /// An anon transfer note is valid iff + /// 1. no double spending in the txn, + /// 2. the signature is correct, + /// 3. ZKP can be verified, + /// 4. the input ABARs are unspent. (checked in finish block) + /// # Arguments + /// * anon_transfer - The Operation for Anon Transfer + /// returns an error if validation fails + fn add_anon_transfer(&mut self, anon_transfer: &AnonTransferOps) -> Result<()> { + // verify nullifiers not double spent within txn + for i in &anon_transfer.note.body.inputs { + if self + .axfr_bodies + .iter() + .flat_map(|ab| ab.body.inputs.iter()) + .any(|n| n == i) + { + return Err(eg!("Transaction has duplicate nullifiers")); + } + } + self.axfr_bodies.push(anon_transfer.note.clone()); + Ok(()) + } } /// Check tx in the context of a block, partially. @@ -550,8 +703,12 @@ pub struct BlockEffect { /// Internally-spent TXOs are None, UTXOs are Some(...) /// Should line up element-wise with `txns` pub txos: Vec>>, + /// New ABARs created + pub output_abars: Vec>, /// Which TXOs this consumes pub input_txos: HashMap, + /// Which new nullifiers are created + pub new_nullifiers: Vec, /// Which new asset types this defines pub new_asset_codes: HashMap, /// Which new TXO issuance sequence numbers are used, in sorted order @@ -614,6 +771,29 @@ impl BlockEffect { self.memo_updates.insert(code, memo); } + // collect ABARs generated from BAR to ABAR + let mut current_txn_abars: Vec = vec![]; + for abar in txn_effect.bar_conv_abars { + current_txn_abars.push(abar); + } + + // collect Nullifiers generated from ABAR to BAR + for inputs in txn_effect.abar_conv_inputs.iter() { + self.new_nullifiers.push(inputs.get_input()); + } + + // collect ABARs and Nullifiers from Anon Transfers + for axfr_note in txn_effect.axfr_bodies { + for n in axfr_note.body.inputs { + self.new_nullifiers.push(n); + } + for abar in axfr_note.body.outputs { + current_txn_abars.push(abar) + } + } + + self.output_abars.push(current_txn_abars); + Ok(temp_sid) } @@ -625,6 +805,21 @@ impl BlockEffect { } } + // Check that no nullifier is created twice in the same block + // for anon_transfer and abar to bar conversion + for axfr_note in txn_effect.axfr_bodies.iter() { + for nullifier in axfr_note.body.inputs.iter() { + if self.new_nullifiers.contains(nullifier) { + return Err(eg!()); + } + } + } + for inputs in txn_effect.abar_conv_inputs.iter() { + if self.new_nullifiers.contains(&inputs.get_input()) { + return Err(eg!()); + } + } + // Check that no AssetType is affected by both the block so far and // this transaction { diff --git a/src/ledger/src/data_model/mod.rs b/src/ledger/src/data_model/mod.rs index d3b2a54e7..25f4d3cce 100644 --- a/src/ledger/src/data_model/mod.rs +++ b/src/ledger/src/data_model/mod.rs @@ -10,25 +10,26 @@ mod effects; mod test; pub use effects::{BlockEffect, TxnEffect}; -use noah_algebra::bls12_381::BLSScalar; -use noah_algebra::prelude::Scalar; -use noah_crypto::basic::anemoi_jive::{AnemoiJive, AnemoiJive381}; use { - crate::converter::ConvertAccount, - crate::staking::{ - ops::{ - claim::ClaimOps, delegation::DelegationOps, - fra_distribution::FraDistributionOps, governance::GovernanceOps, - mint_fra::MintFraOps, replace_staker::ReplaceStakerOps, - undelegation::UnDelegationOps, update_staker::UpdateStakerOps, - update_validator::UpdateValidatorOps, + crate::{ + converter::ConvertAccount, + staking::{ + ops::{ + claim::ClaimOps, delegation::DelegationOps, + fra_distribution::FraDistributionOps, governance::GovernanceOps, + mint_fra::MintFraOps, replace_staker::ReplaceStakerOps, + undelegation::UnDelegationOps, update_staker::UpdateStakerOps, + update_validator::UpdateValidatorOps, + }, + Staking, }, - Staking, }, __trash__::{Policy, PolicyGlobals, TxnPolicyData}, bitmap::SparseMap, + config::abci::CheckPointConfig, cryptohash::{sha256::Digest as BitDigest, HashValue}, + digest::{consts::U64, Digest}, fbnc::NumKey, globutils::wallet::public_key_to_base64, globutils::{HashOf, ProofOf, Serialized, SignatureOf}, @@ -49,16 +50,36 @@ use { }, unicode_normalization::UnicodeNormalization, zei::{ - serialization::ZeiFromToBytes, - xfr::{ - lib::{gen_xfr_body, XfrNotePolicies}, - sig::{XfrKeyPair, XfrPublicKey}, - structs::{ - AssetRecord, AssetType as ZeiAssetType, BlindAssetRecord, OwnerMemo, - TracingPolicies, TracingPolicy, XfrAmount, XfrAssetType, XfrBody, - ASSET_TYPE_LENGTH, + noah_algebra::{ + bls12_381::BLSScalar, bn254::BN254Scalar, serialization::NoahFromToBytes, + traits::Scalar, + }, + noah_api::{ + anon_xfr::{ + abar_to_abar::AXfrNote, + abar_to_ar::{verify_abar_to_ar_note, AbarToArNote}, + abar_to_bar::{verify_abar_to_bar_note, AbarToBarNote}, + ar_to_abar::{verify_ar_to_abar_note, ArToAbarNote}, + bar_to_abar::{verify_bar_to_abar_note, BarToAbarNote}, + commit, + structs::{ + AnonAssetRecord, AxfrOwnerMemo, Nullifier, OpenAnonAssetRecord, + }, + AXfrAddressFoldingInstance, + }, + keys::PublicKey as NoahXfrPublicKey, + parameters::{AddressFormat, VerifierParams}, + xfr::{ + gen_xfr_body, + structs::{ + AssetRecord, AssetType as NoahAssetType, TracingPolicies, + TracingPolicy, XfrAmount, XfrAssetType, ASSET_TYPE_LENGTH, + }, + XfrNotePolicies, }, }, + noah_crypto::anemoi_jive::{AnemoiJive, AnemoiJive254}, + BlindAssetRecord, OwnerMemo, XfrBody, XfrKeyPair, XfrPublicKey, }, }; @@ -94,7 +115,7 @@ fn is_default(x: &T) -> bool { /// Findora asset type code pub struct AssetTypeCode { /// Internal asset type - pub val: ZeiAssetType, + pub val: NoahAssetType, } impl NumKey for AssetTypeCode { @@ -105,7 +126,7 @@ impl NumKey for AssetTypeCode { let mut b = b.to_owned(); b.resize(ASSET_TYPE_LENGTH, 0u8); Ok(Self { - val: ZeiAssetType( + val: NoahAssetType( <[u8; ASSET_TYPE_LENGTH]>::try_from(b.as_slice()).c(d!())?, ), }) @@ -119,7 +140,7 @@ impl Default for AssetTypeCode { #[inline(always)] fn default() -> Self { AssetTypeCode { - val: ZeiAssetType([255; ASSET_TYPE_LENGTH]), + val: NoahAssetType([255; ASSET_TYPE_LENGTH]), } } } @@ -147,7 +168,7 @@ impl AssetTypeCode { pub fn gen_random_with_rng(prng: &mut R) -> Self { let val: [u8; ASSET_TYPE_LENGTH] = prng.gen(); Self { - val: ZeiAssetType(val), + val: NoahAssetType(val), } } @@ -162,7 +183,7 @@ impl AssetTypeCode { pub fn new_from_vec(mut bytes: Vec) -> Self { bytes.resize(ASSET_TYPE_LENGTH, 0u8); Self { - val: ZeiAssetType( + val: NoahAssetType( <[u8; ASSET_TYPE_LENGTH]>::try_from(bytes.as_slice()).unwrap(), ), } @@ -226,7 +247,7 @@ impl AssetTypeCode { as_vec.resize(ASSET_TYPE_LENGTH, 0u8); let buf = <[u8; ASSET_TYPE_LENGTH]>::try_from(as_vec.as_slice()).unwrap(); Self { - val: ZeiAssetType(buf), + val: NoahAssetType(buf), } } @@ -240,7 +261,7 @@ impl AssetTypeCode { bin.resize(ASSET_TYPE_LENGTH, 0u8); let buf = <[u8; ASSET_TYPE_LENGTH]>::try_from(bin.as_slice()).c(d!())?; Ok(Self { - val: ZeiAssetType(buf), + val: NoahAssetType(buf), }) } Err(e) => Err(eg!((format!("Failed to deserialize base64 '{b64}': {e}",)))), @@ -261,13 +282,38 @@ impl AssetTypeCode { /// Generates the asset type code from the prefix and the Anemoi hash function #[inline(always)] - pub fn from_prefix_and_raw_asset_type_code( + pub fn from_prefix_and_raw_asset_type_code_2nd_update( prefix: AssetTypePrefix, raw_asset_type_code: &AssetTypeCode, ) -> Self { let mut f = Vec::with_capacity(3); f.push(prefix.to_field_element()); + let mut bytes = vec![0u8; 32]; + bytes[..31].copy_from_slice(&raw_asset_type_code.val.0[..31]); + f.push(BN254Scalar::from_bytes(&bytes).unwrap()); + + let mut bytes = vec![0u8; 32]; + bytes[0] = raw_asset_type_code.val.0[31]; + f.push(BN254Scalar::from_bytes(&bytes).unwrap()); + + let res = AnemoiJive254::eval_variable_length_hash(&f); + Self::new_from_vec(res.to_bytes()) + } + + /// Former version, now deprecated way to derive the asset code. + /// This version uses BLS12-381. + #[inline(always)] + #[deprecated] + pub fn from_prefix_and_raw_asset_type_code_1st_update( + prefix: AssetTypePrefix, + raw_asset_type_code: &AssetTypeCode, + ) -> Self { + let mut f = Vec::with_capacity(3); + + #[allow(deprecated)] + f.push(prefix.to_field_element_old()); + let mut bytes = vec![0u8; 32]; bytes[..31].copy_from_slice(&raw_asset_type_code.val.0[..31]); f.push(BLSScalar::from_bytes(&bytes).unwrap()); @@ -276,8 +322,42 @@ impl AssetTypeCode { bytes[0] = raw_asset_type_code.val.0[31]; f.push(BLSScalar::from_bytes(&bytes).unwrap()); - let res = AnemoiJive381::eval_variable_length_hash(&f); - Self::new_from_vec(res.to_bytes()) + #[allow(deprecated)] + { + use zei::noah_crypto::anemoi_jive::bls12_381_deprecated::AnemoiJive381Deprecated; + let res = AnemoiJive381Deprecated::eval_variable_length_hash(&f); + Self::new_from_vec(res.to_bytes()) + } + } + + /// Select the right asset code based on the global setting. + pub fn from_prefix_and_raw_asset_type_code( + prefix: AssetTypePrefix, + raw_asset_type_code: &AssetTypeCode, + checkpoint: &CheckPointConfig, + cur_height: u64, + ) -> Self { + if raw_asset_type_code.val == ASSET_TYPE_FRA + || core::cmp::min( + checkpoint.utxo_asset_prefix_height, + checkpoint.utxo_asset_prefix_height_2nd_update, + ) > cur_height + { + *raw_asset_type_code + } else if checkpoint.utxo_asset_prefix_height_2nd_update > cur_height + && checkpoint.utxo_asset_prefix_height <= cur_height + { + #[allow(deprecated)] + AssetTypeCode::from_prefix_and_raw_asset_type_code_1st_update( + prefix, + &raw_asset_type_code, + ) + } else { + AssetTypeCode::from_prefix_and_raw_asset_type_code_2nd_update( + prefix, + &raw_asset_type_code, + ) + } } } @@ -415,9 +495,9 @@ pub struct XfrAddress { } impl XfrAddress { - #[cfg(not(target_arch = "wasm32"))] + #[cfg(all(not(target_arch = "wasm32"), feature = "fin_storage"))] pub(crate) fn to_base64(self) -> String { - b64enc(&self.key.as_bytes()) + b64enc(&self.key.to_bytes()) } // pub(crate) fn to_bytes(self) -> Vec { @@ -428,7 +508,7 @@ impl XfrAddress { impl Hash for XfrAddress { #[inline(always)] fn hash(&self, state: &mut H) { - self.key.as_bytes().hash(state); + self.key.to_bytes().hash(state); } } @@ -441,9 +521,9 @@ pub struct IssuerPublicKey { } impl IssuerPublicKey { - #[cfg(not(target_arch = "wasm32"))] + #[cfg(all(not(target_arch = "wasm32"), feature = "fin_storage"))] pub(crate) fn to_base64(self) -> String { - b64enc(self.key.as_bytes()) + b64enc(&self.key.noah_to_bytes().as_slice()) } // pub(crate) fn to_bytes(&self) -> Vec { @@ -454,7 +534,7 @@ impl IssuerPublicKey { impl Hash for IssuerPublicKey { #[inline(always)] fn hash(&self, state: &mut H) { - self.key.as_bytes().hash(state); + self.key.to_bytes().hash(state); } } @@ -514,15 +594,15 @@ impl SignatureRules { /// Keyset must store XfrPublicKeys in byte form. pub fn check_signature_set(&self, keyset: &HashSet>) -> Result<()> { let mut sum: u64 = 0; - let mut weight_map = HashMap::new(); + let mut weight_map: HashMap, u64> = HashMap::new(); // Convert to map for (key, weight) in self.weights.iter() { - weight_map.insert(key.as_bytes(), *weight); + weight_map.insert(key.to_bytes(), *weight); } // Calculate weighted sum for key in keyset.iter() { sum = sum - .checked_add(*weight_map.get(&key[..]).unwrap_or(&0)) + .checked_add(*weight_map.get::<[u8]>(&key.as_slice()).unwrap_or(&0)) .c(d!())?; } @@ -731,6 +811,22 @@ impl NumKey for TxoSID { #[allow(missing_docs)] pub type TxoSIDList = Vec; +#[derive( + Clone, + Copy, + Debug, + Default, + Deserialize, + Eq, + Hash, + PartialEq, + Serialize, + Ord, + PartialOrd, +)] +#[allow(missing_docs)] +pub struct ATxoSID(pub u64); + #[allow(missing_docs)] #[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct OutputPosition(pub usize); @@ -805,7 +901,7 @@ pub enum UtxoStatus { pub struct Utxo(pub TxOutput); impl Utxo { - #[cfg(not(target_arch = "wasm32"))] + #[cfg(all(not(target_arch = "wasm32"), feature = "fin_storage"))] #[inline(always)] pub(crate) fn get_nonconfidential_balance(&self) -> u64 { if let XfrAmount::NonConfidential(n) = self.0.record.amount { @@ -920,8 +1016,9 @@ impl TransferAssetBody { return Err(eg!()); } - let transfer = - Box::new(gen_xfr_body(prng, input_records, output_records).c(d!())?); + let transfer = Box::new(XfrBody::from_noah( + &gen_xfr_body(prng, input_records, output_records).c(d!())?, + )); let outputs = transfer .outputs .iter() @@ -949,10 +1046,10 @@ impl TransferAssetBody { keypair: &XfrKeyPair, input_idx: Option, ) -> IndexedSignature { - let public_key = keypair.get_pk_ref(); + let public_key = keypair.get_pk(); IndexedSignature { - signature: SignatureOf::new(keypair, &(self.clone(), input_idx)), - address: XfrAddress { key: *public_key }, + signature: SignatureOf::new(&keypair, &(self.clone(), input_idx)), + address: XfrAddress { key: public_key }, input_idx, } } @@ -1083,7 +1180,13 @@ impl AssetTypePrefix { } #[allow(missing_docs)] - pub fn to_field_element(&self) -> BLSScalar { + pub fn to_field_element(&self) -> BN254Scalar { + BN254Scalar::from_bytes(&self.bytes()).unwrap() + } + + #[allow(missing_docs)] + #[deprecated] + pub fn to_field_element_old(&self) -> BLSScalar { BLSScalar::from_bytes(&self.bytes()).unwrap() } } @@ -1161,13 +1264,8 @@ impl TransferAsset { #[inline(always)] #[allow(missing_docs)] - pub fn get_owner_memos_ref(&self) -> Vec> { - self.body - .transfer - .owners_memos - .iter() - .map(|mem| mem.as_ref()) - .collect() + pub fn get_owner_memos_ref(&self) -> Vec> { + self.body.transfer.owners_memos.to_vec() } #[inline(always)] @@ -1215,11 +1313,11 @@ impl IssueAsset { #[inline(always)] #[allow(missing_docs)] - pub fn get_owner_memos_ref(&self) -> Vec> { + pub fn get_owner_memos_ref(&self) -> Vec> { self.body .records .iter() - .map(|(_, memo)| memo.as_ref()) + .map(|(_, memo)| memo.clone()) .collect() } @@ -1275,7 +1373,7 @@ impl UpdateMemo { update_memo_body: UpdateMemoBody, signing_key: &XfrKeyPair, ) -> UpdateMemo { - let signature = SignatureOf::new(signing_key, &update_memo_body); + let signature = SignatureOf::new(&signing_key, &update_memo_body); UpdateMemo { body: update_memo_body, pubkey: *signing_key.get_pk_ref(), @@ -1284,6 +1382,281 @@ impl UpdateMemo { } } +/// A note which enumerates the transparent and confidential BAR to +/// Anon Asset record conversion. +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub enum BarAnonConvNote { + /// A transfer note with ZKP for a confidential asset record + BarNote(Box), + /// A transfer note with ZKP for a non-confidential asset record + ArNote(Box), +} + +/// Operation for converting a Blind Asset Record to an Anonymous record +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct BarToAbarOps { + /// the note which contains the inp/op and ZKP + pub note: BarAnonConvNote, + /// The TxoSID of the the input BAR + pub txo_sid: TxoSID, + nonce: NoReplayToken, +} + +impl BarToAbarOps { + /// Generates a new BarToAbarOps object + /// # Arguments + /// * bar_to_abar_note - The BarToAbarNote of the conversion + /// * txo_sid - the TxoSID of the converting BAR + /// * nonce + pub fn new( + note: BarAnonConvNote, + txo_sid: TxoSID, + nonce: NoReplayToken, + ) -> Result { + Ok(BarToAbarOps { + note, + txo_sid, + nonce, + }) + } + + /// verifies the signatures and proof of the note + pub fn verify(&self) -> Result<()> { + match &self.note { + BarAnonConvNote::BarNote(note) => { + // fetch the verifier Node Params for PlonkProof + let node_params = VerifierParams::get_bar_to_abar().c(d!())?; + // verify the Plonk proof and signature + verify_bar_to_abar_note(&node_params, ¬e, ¬e.body.input.public_key) + .c(d!()) + } + BarAnonConvNote::ArNote(note) => { + // fetch the verifier Node Params for PlonkProof + let node_params = VerifierParams::get_ar_to_abar().c(d!())?; + // verify the Plonk proof and signature + verify_ar_to_abar_note(&node_params, note).c(d!()) + } + } + } + + /// provides a copy of the input record in the note + pub fn input_record(&self) -> BlindAssetRecord { + match &self.note { + BarAnonConvNote::BarNote(n) => BlindAssetRecord::from_noah(&n.body.input), + BarAnonConvNote::ArNote(n) => BlindAssetRecord::from_noah(&n.body.input), + } + } + + /// provides a copy of the output record of the note. + pub fn output_record(&self) -> AnonAssetRecord { + match &self.note { + BarAnonConvNote::BarNote(n) => n.body.output.clone(), + BarAnonConvNote::ArNote(n) => n.body.output.clone(), + } + } + + /// provides a copy of the AxfrOwnerMemo in the note + pub fn axfr_memo(&self) -> AxfrOwnerMemo { + match &self.note { + BarAnonConvNote::BarNote(n) => n.body.memo.clone(), + BarAnonConvNote::ArNote(n) => n.body.memo.clone(), + } + } + + #[inline(always)] + /// Sets the nonce for the operation + pub fn set_nonce(&mut self, nonce: NoReplayToken) { + self.nonce = nonce; + } + + #[inline(always)] + /// Fetches the nonce of the operation + pub fn get_nonce(&self) -> NoReplayToken { + self.nonce + } +} + +/// AbarConvNote +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub enum AbarConvNote { + /// Conversion to a amount or type confidential BAR + AbarToBar(Box), + /// Conversion to a transparent BAR + AbarToAr(Box), +} + +impl AbarConvNote { + /// Verifies the ZKP based on the type of conversion + pub fn verify + Default>( + &self, + merkle_root: BN254Scalar, + hasher: D, + ) -> ruc::Result<()> { + match self { + AbarConvNote::AbarToBar(note) => { + let af = match note.folding_instance { + AXfrAddressFoldingInstance::Secp256k1(_) => AddressFormat::SECP256K1, + AXfrAddressFoldingInstance::Ed25519(_) => AddressFormat::ED25519, + }; + let abar_to_bar_verifier_params = + VerifierParams::get_abar_to_bar(af).c(d!())?; + // An axfr_abar_conv requires versioned merkle root hash for verification. + // verify zk proof with merkle root + verify_abar_to_bar_note( + &abar_to_bar_verifier_params, + ¬e, + &merkle_root, + hasher, + ) + .c(d!("Abar to Bar conversion proof verification failed")) + } + AbarConvNote::AbarToAr(note) => { + let af = match note.folding_instance { + AXfrAddressFoldingInstance::Secp256k1(_) => AddressFormat::SECP256K1, + AXfrAddressFoldingInstance::Ed25519(_) => AddressFormat::ED25519, + }; + let abar_to_ar_verifier_params = + VerifierParams::get_abar_to_ar(af).c(d!())?; + // An axfr_abar_conv requires versioned merkle root hash for verification. + // verify zk proof with merkle root + verify_abar_to_ar_note( + &abar_to_ar_verifier_params, + ¬e, + &merkle_root, + hasher, + ) + .c(d!("Abar to AR conversion proof verification failed")) + } + } + } + + /// input nullifier in the note body + pub fn get_input(&self) -> Nullifier { + match self { + AbarConvNote::AbarToBar(note) => note.body.input, + AbarConvNote::AbarToAr(note) => note.body.input, + } + } + + /// merkle root version of the proof + pub fn get_merkle_root_version(&self) -> u64 { + match self { + AbarConvNote::AbarToBar(note) => note.body.merkle_root_version, + AbarConvNote::AbarToAr(note) => note.body.merkle_root_version, + } + } + + /// public key of the note body + pub fn get_public_key(&self) -> XfrPublicKey { + match self { + AbarConvNote::AbarToBar(note) => { + XfrPublicKey::from_noah(¬e.body.output.public_key) + } + AbarConvNote::AbarToAr(note) => { + XfrPublicKey::from_noah(¬e.body.output.public_key) + } + } + } + + /// output BAR of the note body + pub fn get_output(&self) -> BlindAssetRecord { + match self { + AbarConvNote::AbarToBar(note) => { + BlindAssetRecord::from_noah(¬e.body.output) + } + AbarConvNote::AbarToAr(note) => { + BlindAssetRecord::from_noah(¬e.body.output) + } + } + } + + /// gets address of owner memo in the note body + pub fn get_owner_memos_ref(&self) -> Vec> { + match self { + AbarConvNote::AbarToBar(note) => { + vec![note + .body + .memo + .as_ref() + .map(|om| OwnerMemo::from_noah(om).unwrap())] + } + AbarConvNote::AbarToAr(note) => { + vec![note + .body + .memo + .as_ref() + .map(|om| OwnerMemo::from_noah(om).unwrap())] + } + } + } + + /// get serialized bytes for signature and prove (only ABAR body). + pub fn digest(&self) -> Vec { + match self { + AbarConvNote::AbarToBar(note) => { + Serialized::new(¬e.body).as_ref().to_vec() + } + AbarConvNote::AbarToAr(note) => { + Serialized::new(¬e.body).as_ref().to_vec() + } + } + } +} + +/// Operation for converting a Blind Asset Record to a Anonymous record +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct AbarToBarOps { + /// the note which contains the inp/op and ZKP + pub note: AbarConvNote, + nonce: NoReplayToken, +} + +impl AbarToBarOps { + /// Generates a new BarToAbarOps object + pub fn new(note: AbarConvNote, nonce: NoReplayToken) -> Result { + Ok(AbarToBarOps { note, nonce }) + } + + #[inline(always)] + /// Sets the nonce for the operation + pub fn set_nonce(&mut self, nonce: NoReplayToken) { + self.nonce = nonce; + } + + #[inline(always)] + /// Fetches the nonce of the operation + pub fn get_nonce(&self) -> NoReplayToken { + self.nonce + } +} + +/// A struct to hold the transfer ops +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct AnonTransferOps { + /// The note which holds the signatures, the ZKF and memo + pub note: AXfrNote, + nonce: NoReplayToken, +} +impl AnonTransferOps { + /// Generates the anon transfer note + pub fn new(note: AXfrNote, nonce: NoReplayToken) -> Result { + Ok(AnonTransferOps { note, nonce }) + } + + /// Sets the nonce for the operation + #[inline(always)] + #[allow(dead_code)] + fn set_nonce(&mut self, nonce: NoReplayToken) { + self.nonce = nonce; + } + + /// Fetches the nonce of the operation + #[inline(always)] + fn get_nonce(&self) -> NoReplayToken { + self.nonce + } +} + /// Operation list supported in findora network #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub enum Operation { @@ -1313,35 +1686,57 @@ pub enum Operation { MintFra(MintFraOps), /// Convert UTXOs to EVM Account balance ConvertAccount(ConvertAccount), + /// Anonymous conversion operation + BarToAbar(Box), + /// De-anonymize ABAR operation + AbarToBar(Box), + /// Anonymous transfer operation + TransferAnonAsset(Box), ///replace staker. ReplaceStaker(ReplaceStakerOps), } +impl Operation { + /// get serialized bytes for signature and prove. + pub fn digest(&self) -> Vec { + match self { + Operation::UpdateStaker(i) => Serialized::new(i).as_ref().to_vec(), + Operation::Delegation(i) => Serialized::new(i).as_ref().to_vec(), + Operation::UnDelegation(i) => Serialized::new(i).as_ref().to_vec(), + Operation::Claim(i) => Serialized::new(i).as_ref().to_vec(), + Operation::FraDistribution(i) => Serialized::new(i).as_ref().to_vec(), + Operation::UpdateValidator(i) => Serialized::new(i).as_ref().to_vec(), + Operation::Governance(i) => Serialized::new(i).as_ref().to_vec(), + Operation::UpdateMemo(i) => Serialized::new(i).as_ref().to_vec(), + Operation::ConvertAccount(i) => Serialized::new(i).as_ref().to_vec(), + Operation::BarToAbar(i) => Serialized::new(i).as_ref().to_vec(), + Operation::ReplaceStaker(i) => Serialized::new(i).as_ref().to_vec(), + Operation::TransferAsset(i) => Serialized::new(i).as_ref().to_vec(), + Operation::IssueAsset(i) => Serialized::new(i).as_ref().to_vec(), + Operation::DefineAsset(i) => Serialized::new(i).as_ref().to_vec(), + Operation::MintFra(i) => Serialized::new(i).as_ref().to_vec(), + Operation::AbarToBar(i) => i.note.digest(), + Operation::TransferAnonAsset(i) => { + Serialized::new(&i.note.body).as_ref().to_vec() + } + } + } +} + fn set_no_replay_token(op: &mut Operation, no_replay_token: NoReplayToken) { match op { - Operation::UpdateStaker(i) => { - i.set_nonce(no_replay_token); - } - Operation::Delegation(i) => { - i.set_nonce(no_replay_token); - } - Operation::UnDelegation(i) => { - i.set_nonce(no_replay_token); - } - Operation::Claim(i) => { - i.set_nonce(no_replay_token); - } - Operation::FraDistribution(i) => { - i.set_nonce(no_replay_token); - } - Operation::UpdateValidator(i) => { - i.set_nonce(no_replay_token); - } - Operation::Governance(i) => { - i.set_nonce(no_replay_token); - } + Operation::UpdateStaker(i) => i.set_nonce(no_replay_token), + Operation::Delegation(i) => i.set_nonce(no_replay_token), + Operation::UnDelegation(i) => i.set_nonce(no_replay_token), + Operation::Claim(i) => i.set_nonce(no_replay_token), + Operation::FraDistribution(i) => i.set_nonce(no_replay_token), + Operation::UpdateValidator(i) => i.set_nonce(no_replay_token), + Operation::Governance(i) => i.set_nonce(no_replay_token), Operation::UpdateMemo(i) => i.body.no_replay_token = no_replay_token, Operation::ConvertAccount(i) => i.set_nonce(no_replay_token), + Operation::BarToAbar(i) => i.set_nonce(no_replay_token), + Operation::AbarToBar(i) => i.set_nonce(no_replay_token), + Operation::TransferAnonAsset(i) => i.set_nonce(no_replay_token), _ => {} } } @@ -1369,6 +1764,19 @@ impl TransactionBody { result.no_replay_token = no_replay_token; result } + + /// get serialized bytes for signature and prove. + pub fn digest(&self) -> Vec { + let mut bytes = vec![]; + bytes.extend_from_slice(Serialized::new(&self.no_replay_token).as_ref()); + bytes.extend_from_slice(Serialized::new(&self.credentials).as_ref()); + bytes.extend_from_slice(Serialized::new(&self.policy_options).as_ref()); + bytes.extend_from_slice(Serialized::new(&self.memos).as_ref()); + for o in &self.operations { + bytes.extend_from_slice(&o.digest()); + } + bytes + } } #[allow(missing_docs)] @@ -1389,6 +1797,8 @@ pub struct FinalizedTransaction { pub txn: Transaction, pub tx_id: TxnSID, pub txo_ids: Vec, + #[serde(default)] + pub atxo_ids: Vec, pub merkle_id: u64, } @@ -1658,7 +2068,7 @@ impl FinalizedTransaction { } /// Use pure zero bytes(aka [0, 0, ... , 0]) to express FRA. -pub const ASSET_TYPE_FRA: ZeiAssetType = ZeiAssetType([0; ASSET_TYPE_LENGTH]); +pub const ASSET_TYPE_FRA: NoahAssetType = NoahAssetType([0; ASSET_TYPE_LENGTH]); /// FRA decimals pub const FRA_DECIMALS: u8 = 6; @@ -1666,13 +2076,21 @@ pub const FRA_DECIMALS: u8 = 6; lazy_static! { /// The destination of Fee is an black hole, /// all token transfered to it will be burned. - pub static ref BLACK_HOLE_PUBKEY: XfrPublicKey = pnk!(XfrPublicKey::zei_from_bytes(&[0; ed25519_dalek::PUBLIC_KEY_LENGTH][..])); + pub static ref BLACK_HOLE_PUBKEY: NoahXfrPublicKey = pnk!(NoahXfrPublicKey::noah_from_bytes(&[0; ed25519_dalek::PUBLIC_KEY_LENGTH][..])); /// BlackHole of Staking - pub static ref BLACK_HOLE_PUBKEY_STAKING: XfrPublicKey = pnk!(XfrPublicKey::zei_from_bytes(&[1; ed25519_dalek::PUBLIC_KEY_LENGTH][..])); + pub static ref BLACK_HOLE_PUBKEY_STAKING: NoahXfrPublicKey = pnk!(NoahXfrPublicKey::noah_from_bytes(&[1; ed25519_dalek::PUBLIC_KEY_LENGTH][..])); } /// see [**mainnet-v0.1 defination**](https://www.notion.so/findora/Transaction-Fees-Analysis-d657247b70f44a699d50e1b01b8a2287) -pub const TX_FEE_MIN: u64 = 1_0000; +pub const TX_FEE_MIN: u64 = 10_000; // 0.01 FRA +/// Double the regular fee +pub const BAR_TO_ABAR_TX_FEE_MIN: u64 = 20_000; // 0.02 FRA (2*TX_FEE_MIN) + +/// Calculate the FEE with inputs and outputs number. +pub const FEE_CALCULATING_FUNC: fn(u32, u32) -> u32 = |x: u32, y: u32| { + let extra_outputs = y.saturating_sub(x); + 50_0000 + 10_0000 * x + 20_0000 * y + (10_000 * extra_outputs) +}; impl Transaction { #[inline(always)] @@ -1690,6 +2108,7 @@ impl Transaction { self.check_fee() && !self.is_coinbase_tx() } + #[allow(clippy::if_same_then_else)] /// A simple fee checker /// /// The check logic is as follows: @@ -1704,21 +2123,32 @@ impl Transaction { // // But it seems enough when we combine it with limiting // the payload size of submission-server's http-requests. + + let mut min_fee = TX_FEE_MIN; + // Charge double the min fee if the transaction is BarToAbar + for op in self.body.operations.iter() { + if let Operation::BarToAbar(_a) = op { + min_fee = BAR_TO_ABAR_TX_FEE_MIN; + } + } + self.is_coinbase_tx() || self.body.operations.iter().any(|ops| { if let Operation::TransferAsset(ref x) = ops { return x.body.outputs.iter().any(|o| { if let XfrAssetType::NonConfidential(ty) = o.record.asset_type { if ty == ASSET_TYPE_FRA - && *BLACK_HOLE_PUBKEY == o.record.public_key + && XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY) + == o.record.public_key { if let XfrAmount::NonConfidential(am) = o.record.amount { - if am > (TX_FEE_MIN - 1) { + if am > (min_fee - 1) { return true; } } } } + tracing::error!("Txn failed in check_fee {:?}", self); false }); } else if let Operation::DefineAsset(ref x) = ops { @@ -1729,9 +2159,16 @@ impl Transaction { if x.body.code.val == ASSET_TYPE_FRA { return true; } + } else if let Operation::TransferAnonAsset(_) = ops { + return true; + } else if let Operation::BarToAbar(_) = ops { + return true; + } else if let Operation::AbarToBar(_) = ops { + return true; } else if matches!(ops, Operation::UpdateValidator(_)) { return true; } + tracing::error!("Txn failed in check_fee {:?}", self); false }) } @@ -1828,7 +2265,7 @@ impl Transaction { #[inline(always)] #[allow(missing_docs)] - pub fn get_owner_memos_ref(&self) -> Vec> { + pub fn get_owner_memos_ref(&self) -> Vec> { let mut memos = Vec::new(); for op in self.body.operations.iter() { match op { @@ -1841,6 +2278,9 @@ impl Transaction { Operation::IssueAsset(issue_asset) => { memos.append(&mut issue_asset.get_owner_memos_ref()); } + Operation::AbarToBar(abar_to_bar) => { + memos.append(&mut abar_to_bar.note.get_owner_memos_ref()); + } _ => {} } } @@ -1877,7 +2317,7 @@ impl Transaction { pub fn check_has_signature(&self, public_key: &XfrPublicKey) -> Result<()> { let serialized = Serialized::new(&self.body); for sig in self.signatures.iter() { - match sig.0.verify(public_key, &serialized) { + match sig.0.verify(&public_key, &serialized) { Err(_) => {} Ok(_) => { return Ok(()); @@ -1903,6 +2343,7 @@ impl Transaction { /// NOTE: This method is used to verify the signature in the transaction, /// when the user constructs the transaction not only needs to sign each `operation`, /// but also needs to sign the whole transaction, otherwise it will not be passed here + #[allow(missing_docs)] #[inline(always)] pub fn check_tx(&self) -> Result<()> { let select_check = |tx: &Transaction, pk: &XfrPublicKey| -> Result<()> { @@ -1955,6 +2396,9 @@ impl Transaction { } } } + Operation::BarToAbar(_) => {} + Operation::AbarToBar(_) => {} + Operation::TransferAnonAsset(_) => {} } } @@ -1997,6 +2441,23 @@ impl StateCommitmentData { } } +/// Commitment data for Anon merkle trees +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct AnonStateCommitmentData { + /// Root hash of the latest committed version of abar merkle tree + pub abar_root_hash: BN254Scalar, + /// Root hash of the nullifier set merkle tree + pub nullifier_root_hash: BitDigest, +} + +impl AnonStateCommitmentData { + #[inline(always)] + #[allow(missing_docs)] + pub fn compute_commitment(&self) -> HashOf> { + HashOf::new(&Some(self).cloned()) + } +} + /// Used in `Staking` logic to create consensus-tmp XfrPublicKey #[derive(Clone, Copy, Debug, Deserialize, Serialize, Eq, PartialEq, Default)] pub struct ConsensusRng(u32); @@ -2022,3 +2483,22 @@ impl RngCore for ConsensusRng { pub fn gen_random_keypair() -> XfrKeyPair { XfrKeyPair::generate(&mut ChaChaRng::from_entropy()) } + +#[inline(always)] +#[allow(missing_docs)] +pub fn get_abar_commitment(oabar: OpenAnonAssetRecord) -> BN254Scalar { + let c = commit( + oabar.pub_key_ref(), + oabar.get_blind(), + oabar.get_amount(), + oabar.get_asset_type().as_scalar(), + ) + .unwrap(); + c.0 +} + +#[derive(Serialize, Deserialize)] +#[allow(missing_docs)] +pub struct ABARData { + pub commitment: String, +} diff --git a/src/ledger/src/data_model/test.rs b/src/ledger/src/data_model/test.rs old mode 100644 new mode 100755 index e08bf051a..01e157ea2 --- a/src/ledger/src/data_model/test.rs +++ b/src/ledger/src/data_model/test.rs @@ -6,12 +6,27 @@ use { rand_core::SeedableRng, std::cmp::min, zei::{ - ristretto, - xfr::structs::{AssetTypeAndAmountProof, XfrBody, XfrProofs}, + noah_api::{ + ristretto, + xfr::structs::{AssetTypeAndAmountProof, XfrProofs}, + }, + XfrBody, }, - zeiutils::msg_eq, }; +#[macro_export] +macro_rules! msg_eq { + ($noah_err: expr, $ruc_err: expr $(,)?) => { + assert!($ruc_err.msg_has_overloop(ruc::eg!($noah_err).as_ref())); + }; + ($noah_err: expr, $ruc_err: expr, $msg: expr $(,)?) => { + assert!( + $ruc_err.msg_has_overloop(ruc::eg!($noah_err).as_ref()), + $msg + ); + }; +} + const UTF8_ASSET_TYPES_WORK: bool = false; // This test may fail as it is a statistical test that sometimes fails (but very rarely) @@ -260,7 +275,7 @@ fn test_add_operation() { fn gen_fee_operation( amount: Option, - asset_type: Option, + asset_type: Option, dest_pubkey: XfrPublicKey, ) -> Operation { Operation::TransferAsset(TransferAsset { @@ -307,38 +322,41 @@ fn test_check_fee() { let mut tx = gen_sample_tx(); assert!(!tx.check_fee()); - let invalid_confidential_type = - gen_fee_operation(Some(TX_FEE_MIN), None, *BLACK_HOLE_PUBKEY); + let invalid_confidential_type = gen_fee_operation( + Some(TX_FEE_MIN), + None, + XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY), + ); let invalid_confidential_amount = gen_fee_operation( None, - Some(ZeiAssetType([0; ASSET_TYPE_LENGTH])), - *BLACK_HOLE_PUBKEY, + Some(NoahAssetType([0; ASSET_TYPE_LENGTH])), + XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY), ); let invalid_nonconfidential_not_fra_code = gen_fee_operation( Some(TX_FEE_MIN), - Some(ZeiAssetType([9; ASSET_TYPE_LENGTH])), - *BLACK_HOLE_PUBKEY, + Some(NoahAssetType([9; ASSET_TYPE_LENGTH])), + XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY), ); let invalid_nonconfidential_fee_too_little = gen_fee_operation( Some(TX_FEE_MIN - 1), - Some(ZeiAssetType([0; ASSET_TYPE_LENGTH])), - *BLACK_HOLE_PUBKEY, + Some(NoahAssetType([0; ASSET_TYPE_LENGTH])), + XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY), ); let invalid_destination_not_black_hole = gen_fee_operation( Some(TX_FEE_MIN), - Some(ZeiAssetType([0; ASSET_TYPE_LENGTH])), - XfrPublicKey::zei_from_bytes(&[9; ed25519_dalek::PUBLIC_KEY_LENGTH][..]) + Some(NoahAssetType([0; ASSET_TYPE_LENGTH])), + XfrPublicKey::noah_from_bytes(&[9; ed25519_dalek::PUBLIC_KEY_LENGTH][..]) .unwrap(), ); let valid = gen_fee_operation( Some(TX_FEE_MIN), - Some(ZeiAssetType([0; ASSET_TYPE_LENGTH])), - *BLACK_HOLE_PUBKEY, + Some(NoahAssetType([0; ASSET_TYPE_LENGTH])), + XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY), ); let valid2 = gen_fee_operation( Some(TX_FEE_MIN + 999), - Some(ZeiAssetType([0; ASSET_TYPE_LENGTH])), - *BLACK_HOLE_PUBKEY, + Some(NoahAssetType([0; ASSET_TYPE_LENGTH])), + XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY), ); // tx.add_operation(invalid_confidential_type.clone()); diff --git a/src/ledger/src/lib.rs b/src/ledger/src/lib.rs index 8d15bac66..10b83a333 100644 --- a/src/ledger/src/lib.rs +++ b/src/ledger/src/lib.rs @@ -10,7 +10,7 @@ pub mod data_model; pub mod converter; pub mod staking; -#[cfg(not(target_arch = "wasm32"))] +#[cfg(all(not(target_arch = "wasm32"), feature = "fin_storage"))] pub mod store; use {ruc::*, std::sync::atomic::AtomicI64}; diff --git a/src/ledger/src/staking/cosig.rs b/src/ledger/src/staking/cosig.rs index f96b8c4d9..1d2cc50eb 100644 --- a/src/ledger/src/staking/cosig.rs +++ b/src/ledger/src/staking/cosig.rs @@ -17,7 +17,7 @@ use { collections::BTreeMap, fmt::{self, Debug}, }, - zei::xfr::sig::{XfrKeyPair, XfrPublicKey, XfrSignature}, + zei::{XfrKeyPair, XfrPublicKey, XfrSignature}, }; /// A common structure for data with co-signatures. @@ -53,7 +53,7 @@ where .c(d!()) .map(|msg| { let k = kp.get_pk(); - let v = CoSig::new(kp.get_pk(), kp.sign(&msg)); + let v = CoSig::new(kp.get_pk(), kp.sign(&msg).unwrap()); self.cosigs.insert(k, v); }) } @@ -64,7 +64,7 @@ where let msg = bincode::serialize(&(self.nonce, &self.data)).c(d!())?; kps.iter().for_each(|kp| { let k = kp.get_pk(); - let v = CoSig::new(kp.get_pk(), kp.sign(&msg)); + let v = CoSig::new(kp.get_pk(), kp.sign(&msg).unwrap()); self.cosigs.insert(k, v); }); Ok(()) diff --git a/src/ledger/src/staking/evm.rs b/src/ledger/src/staking/evm.rs index dd048d4fa..f6d5e8560 100644 --- a/src/ledger/src/staking/evm.rs +++ b/src/ledger/src/staking/evm.rs @@ -6,7 +6,7 @@ use once_cell::sync::{Lazy, OnceCell}; use parking_lot::{Mutex, RwLock}; use ruc::Result; use std::{collections::BTreeMap, sync::Arc}; -use zei::xfr::sig::XfrPublicKey; +use zei::XfrPublicKey; ///EVM staking interface pub static EVM_STAKING: OnceCell>> = OnceCell::new(); diff --git a/src/ledger/src/staking/mod.rs b/src/ledger/src/staking/mod.rs index 3ec4585d3..de471b8d2 100644 --- a/src/ledger/src/staking/mod.rs +++ b/src/ledger/src/staking/mod.rs @@ -12,7 +12,7 @@ #![deny(missing_docs)] #![allow(clippy::upper_case_acronyms)] -#[cfg(not(target_arch = "wasm32"))] +#[cfg(all(not(target_arch = "wasm32"), feature = "fin_storage"))] use {num_bigint::BigUint, std::convert::TryFrom}; pub mod cosig; @@ -51,7 +51,7 @@ use { Arc, }, }, - zei::xfr::sig::{XfrKeyPair, XfrPublicKey}, + zei::{noah_api::keys::PublicKey as NoahXfrPublicKey, XfrKeyPair, XfrPublicKey}, }; // height, reward rate @@ -80,14 +80,14 @@ lazy_static! { pub static ref KEEP_HIST: bool = env::var("FINDORAD_KEEP_HIST").is_ok(); /// Reserved accounts of EcoSystem. - pub static ref FF_PK_LIST: Vec = FF_ADDR_LIST + pub static ref FF_PK_LIST: Vec = FF_ADDR_LIST .iter() - .map(|addr| pnk!(wallet::public_key_from_bech32(addr))) + .map(|addr| pnk!(wallet::public_key_from_bech32(addr)).into_noah()) .collect(); /// Reserved accounts of Findora Foundation. - pub static ref FF_PK_EXTRA_120_0000: XfrPublicKey = - pnk!(wallet::public_key_from_bech32(FF_ADDR_EXTRA_120_0000)); + pub static ref FF_PK_EXTRA_120_0000: NoahXfrPublicKey = + pnk!(wallet::public_key_from_bech32(FF_ADDR_EXTRA_120_0000)).into_noah(); #[allow(missing_docs)] pub static ref CHAN_GLOB_RATE_HIST: GRHCP = chan!(); @@ -809,7 +809,7 @@ impl Staking { self.delegation_info .end_height_map .entry(end_height) - .or_insert_with(BTreeSet::new) + .or_default() .insert(owner); // There should be no failure here !! @@ -903,7 +903,7 @@ impl Staking { self.delegation_info .end_height_map .entry(h + CFG.checkpoint.unbond_block_cnt) - .or_insert_with(BTreeSet::new) + .or_default() .insert(*addr); } @@ -995,7 +995,7 @@ impl Staking { self.delegation_info .end_height_map .entry(h + CFG.checkpoint.unbond_block_cnt) - .or_insert_with(BTreeSet::new) + .or_default() .insert(pu.new_delegator_id); // update delegator entries for pu target_validator @@ -1078,7 +1078,7 @@ impl Staking { self.delegation_info .end_height_map .entry(end_height) - .or_insert_with(BTreeSet::new) + .or_default() .insert(addr.to_owned()); Ok(()) } else { @@ -1604,7 +1604,7 @@ impl Staking { &self.coinbase.distribution_plan } - #[cfg(not(target_arch = "wasm32"))] + #[cfg(all(not(target_arch = "wasm32"), feature = "fin_storage"))] /// set_proposer_rewards sets the rewards for the block proposer /// All rewards are allocated to the proposer only pub(crate) fn set_proposer_rewards( @@ -1646,7 +1646,7 @@ impl Staking { .map(|_| ()) } - #[cfg(not(target_arch = "wasm32"))] + #[cfg(all(not(target_arch = "wasm32"), feature = "fin_storage"))] fn get_proposer_rewards_rate(vote_percent: [u64; 2]) -> Result<[u128; 2]> { let p = [vote_percent[0] as u128, vote_percent[1] as u128]; // p[0] = Validator power which voted for this block @@ -2099,7 +2099,7 @@ impl Delegation { } #[inline(always)] - #[cfg(not(target_arch = "wasm32"))] + #[cfg(all(not(target_arch = "wasm32"), feature = "fin_storage"))] pub(crate) fn validator_entry_exists(&self, validator: &XfrPublicKey) -> bool { self.delegations.contains_key(validator) } @@ -2119,7 +2119,7 @@ impl Delegation { // > **NOTE:** // > use 'AssignAdd' instead of 'Assign' // > to keep compatible with the logic of governance penalty. - #[cfg(not(target_arch = "wasm32"))] + #[cfg(all(not(target_arch = "wasm32"), feature = "fin_storage"))] #[allow(clippy::too_many_arguments)] pub(crate) fn set_delegation_rewards( &mut self, @@ -2208,7 +2208,7 @@ impl Delegation { // Calculate the amount(in FRA units) that // should be paid to the owner of this delegation. -#[cfg(not(target_arch = "wasm32"))] +#[cfg(all(not(target_arch = "wasm32"), feature = "fin_storage"))] fn calculate_delegation_rewards( return_rate: [u128; 2], amount: Amount, diff --git a/src/ledger/src/staking/ops/claim.rs b/src/ledger/src/staking/ops/claim.rs index bbdd9ce71..5a889b51a 100644 --- a/src/ledger/src/staking/ops/claim.rs +++ b/src/ledger/src/staking/ops/claim.rs @@ -12,7 +12,7 @@ use { config::abci::global_cfg::CFG, ruc::*, serde::{Deserialize, Serialize}, - zei::xfr::sig::{XfrKeyPair, XfrPublicKey, XfrSignature}, + zei::{XfrKeyPair, XfrPublicKey, XfrSignature}, }; /// Used as the inner object of a `Claim Operation`. @@ -81,7 +81,7 @@ impl ClaimOps { nonce: NoReplayToken, ) -> Self { let body = Data::new(amount, nonce); - let signature = keypair.sign(&body.to_bytes()); + let signature = keypair.sign(&body.to_bytes()).unwrap(); ClaimOps { body, pubkey: keypair.get_pk(), diff --git a/src/ledger/src/staking/ops/delegation.rs b/src/ledger/src/staking/ops/delegation.rs index 528a5eaaf..635706da2 100644 --- a/src/ledger/src/staking/ops/delegation.rs +++ b/src/ledger/src/staking/ops/delegation.rs @@ -21,9 +21,9 @@ use { serde::{Deserialize, Serialize}, std::collections::HashSet, tendermint::{signature::Ed25519Signature, PrivateKey, PublicKey, Signature}, - zei::xfr::{ - sig::{XfrKeyPair, XfrPublicKey, XfrSignature}, - structs::{XfrAmount, XfrAssetType}, + zei::{ + noah_api::xfr::structs::{XfrAmount, XfrAssetType}, + XfrKeyPair, XfrPublicKey, XfrSignature, }, }; @@ -148,7 +148,7 @@ impl DelegationOps { nonce: NoReplayToken, ) -> Self { let body = Box::new(Data::new(validator, new_validator, amount, nonce)); - let signature = keypair.sign(&body.to_bytes()); + let signature = keypair.sign(&body.to_bytes()).unwrap(); let v_signature: Option = vltor_key .and_then(|pk| pk.ed25519_keypair().map(|k| k.sign(&body.to_bytes()))); DelegationOps { @@ -243,7 +243,7 @@ fn check_delegation_context_principal( tx: &Transaction, owner: (XfrPublicKey, Amount), ) -> Result { - let target_pk = *BLACK_HOLE_PUBKEY_STAKING; + let target_pk = XfrPublicKey::from_noah(&BLACK_HOLE_PUBKEY_STAKING); let am = tx .body diff --git a/src/ledger/src/staking/ops/fra_distribution.rs b/src/ledger/src/staking/ops/fra_distribution.rs index e82d401c9..2e517222f 100644 --- a/src/ledger/src/staking/ops/fra_distribution.rs +++ b/src/ledger/src/staking/ops/fra_distribution.rs @@ -14,7 +14,7 @@ use { ruc::*, serde::{Deserialize, Serialize}, std::collections::BTreeMap, - zei::xfr::sig::{XfrKeyPair, XfrPublicKey}, + zei::{XfrKeyPair, XfrPublicKey}, }; /// Used as the inner object of a `FraDistribution Operation`. diff --git a/src/ledger/src/staking/ops/governance.rs b/src/ledger/src/staking/ops/governance.rs index e5967586d..9b301aed7 100644 --- a/src/ledger/src/staking/ops/governance.rs +++ b/src/ledger/src/staking/ops/governance.rs @@ -16,7 +16,7 @@ use { ruc::*, serde::{Deserialize, Serialize}, std::collections::BTreeMap, - zei::xfr::sig::{XfrKeyPair, XfrPublicKey}, + zei::{XfrKeyPair, XfrPublicKey}, }; lazy_static! { diff --git a/src/ledger/src/staking/ops/mint_fra.rs b/src/ledger/src/staking/ops/mint_fra.rs index 2738f3ff1..46b07fb0c 100644 --- a/src/ledger/src/staking/ops/mint_fra.rs +++ b/src/ledger/src/staking/ops/mint_fra.rs @@ -14,12 +14,12 @@ use { rand_core::SeedableRng, serde::{Deserialize, Serialize}, zei::{ - setup::PublicParams, - xfr::{ + noah_algebra::ristretto::PedersenCommitmentRistretto, + noah_api::xfr::{ asset_record::{build_blind_asset_record, AssetRecordType}, - sig::XfrPublicKey, - structs::{AssetRecordTemplate, AssetType, OwnerMemo}, + structs::{AssetRecordTemplate, AssetType}, }, + BlindAssetRecord, OwnerMemo, XfrPublicKey, }, }; @@ -48,7 +48,7 @@ impl MintFraOps { #[inline(always)] #[allow(missing_docs)] - pub fn get_owner_memos_ref(&self) -> Vec> { + pub fn get_owner_memos_ref(&self) -> Vec> { vec![None; self.entries.len()] } } @@ -78,14 +78,14 @@ impl MintEntry { amount, asset_type, AssetRecordType::NonConfidentialAmount_NonConfidentialAssetType, - receiver_pk.unwrap_or(target_pk), + receiver_pk.unwrap_or(target_pk).into_noah(), ); - let pc_gens = PublicParams::default().pc_gens; + let pc_gens = PedersenCommitmentRistretto::default(); let (ba, _, _) = build_blind_asset_record(&mut prng, &pc_gens, &ar, vec![]); let utxo = TxOutput { id: None, - record: ba, + record: BlindAssetRecord::from_noah(&ba), lien: None, }; diff --git a/src/ledger/src/staking/ops/replace_staker.rs b/src/ledger/src/staking/ops/replace_staker.rs index b88b3b44b..b668074f5 100644 --- a/src/ledger/src/staking/ops/replace_staker.rs +++ b/src/ledger/src/staking/ops/replace_staker.rs @@ -12,7 +12,7 @@ use { fp_types::H160, ruc::*, serde::{Deserialize, Serialize}, - zei::xfr::sig::{XfrKeyPair, XfrPublicKey, XfrSignature}, + zei::{XfrKeyPair, XfrPublicKey, XfrSignature}, }; /// Used for `Staker Replace Operation`. @@ -39,7 +39,7 @@ impl ReplaceStakerOps { nonce, }; - let signature = keypair.sign(&body.to_bytes()); + let signature = keypair.sign(&body.to_bytes()).unwrap(); ReplaceStakerOps { body, diff --git a/src/ledger/src/staking/ops/undelegation.rs b/src/ledger/src/staking/ops/undelegation.rs index 9ccd6c1ea..414dee9fd 100644 --- a/src/ledger/src/staking/ops/undelegation.rs +++ b/src/ledger/src/staking/ops/undelegation.rs @@ -12,7 +12,7 @@ use { config::abci::global_cfg::CFG, ruc::*, serde::{Deserialize, Serialize}, - zei::xfr::sig::{XfrKeyPair, XfrPublicKey, XfrSignature}, + zei::{XfrKeyPair, XfrPublicKey, XfrSignature}, }; /// Used as the inner object of a `UnDelegation Operation`. @@ -82,7 +82,7 @@ impl UnDelegationOps { pu: Option, ) -> Self { let body = Data::new(nonce, pu); - let signature = keypair.sign(&body.to_bytes()); + let signature = keypair.sign(&body.to_bytes()).unwrap(); UnDelegationOps { body, pubkey: keypair.get_pk(), diff --git a/src/ledger/src/staking/ops/update_staker.rs b/src/ledger/src/staking/ops/update_staker.rs index b64d7a1d5..f5ab2798e 100644 --- a/src/ledger/src/staking/ops/update_staker.rs +++ b/src/ledger/src/staking/ops/update_staker.rs @@ -16,7 +16,7 @@ use { ruc::*, serde::{Deserialize, Serialize}, tendermint::{signature::Ed25519Signature, PrivateKey, PublicKey, Signature}, - zei::xfr::sig::{XfrKeyPair, XfrPublicKey, XfrSignature}, + zei::{XfrKeyPair, XfrPublicKey, XfrSignature}, }; /// Used as the inner object of a `Staker Update Operation`. @@ -105,7 +105,7 @@ impl UpdateStakerOps { nonce: NoReplayToken, ) -> Self { let body = Box::new(Data::new(validator, new_validator, nonce)); - let signature = keypair.sign(&body.to_bytes()); + let signature = keypair.sign(&body.to_bytes()).unwrap(); let v_signature: Option = vltor_key .ed25519_keypair() .map(|k| k.sign(&body.to_bytes())); diff --git a/src/ledger/src/staking/ops/update_validator.rs b/src/ledger/src/staking/ops/update_validator.rs index ddac83a82..576ef22c7 100644 --- a/src/ledger/src/staking/ops/update_validator.rs +++ b/src/ledger/src/staking/ops/update_validator.rs @@ -14,7 +14,7 @@ use { }, }, ruc::*, - zei::xfr::sig::{XfrKeyPair, XfrPublicKey}, + zei::{XfrKeyPair, XfrPublicKey}, }; /// Used as the inner object of a `UpdateValidator Operation`. diff --git a/src/ledger/src/store/api_cache.rs b/src/ledger/src/store/api_cache.rs index 465613efd..ad7077bf5 100644 --- a/src/ledger/src/store/api_cache.rs +++ b/src/ledger/src/store/api_cache.rs @@ -4,9 +4,9 @@ use { crate::{ data_model::{ - AssetTypeCode, AssetTypePrefix, DefineAsset, IssueAsset, IssuerPublicKey, - Operation, Transaction, TxOutput, TxnIDHash, TxnSID, TxoSID, XfrAddress, - ASSET_TYPE_FRA, + ATxoSID, AssetTypeCode, AssetTypePrefix, DefineAsset, IssueAsset, + IssuerPublicKey, Operation, StateCommitmentData, Transaction, TxOutput, + TxnIDHash, TxnSID, TxoSID, XfrAddress, }, staking::{ ops::mint_fra::MintEntry, Amount, BlockHeight, DelegationRwdDetail, @@ -16,11 +16,11 @@ use { }, config::abci::global_cfg::CFG, fbnc::{new_mapx, new_mapxnk, Mapx, Mapxnk}, - globutils::wallet, + globutils::{wallet, HashOf}, ruc::*, serde::{Deserialize, Serialize}, std::collections::HashSet, - zei::xfr::{sig::XfrPublicKey, structs::OwnerMemo}, + zei::{noah_api::anon_xfr::structs::AxfrOwnerMemo, OwnerMemo, XfrPublicKey}, }; type Issuances = Vec<(TxOutput, Option)>; @@ -45,14 +45,20 @@ pub struct ApiCache { pub token_code_issuances: Mapx, /// used in confidential tx pub owner_memos: Mapxnk, + /// used in anonymous tx + pub abar_memos: Mapx, /// ownship of txo pub utxos_to_map_index: Mapxnk, /// txo(spent, unspent) to authenticated txn (sid, hash) pub txo_to_txnid: Mapxnk, + /// atxo to authenticated txn (sid, hash) + pub atxo_to_txnid: Mapx, /// txn sid to txn hash pub txn_sid_to_hash: Mapxnk, /// txn hash to txn sid pub txn_hash_to_sid: Mapx, + /// max (latest) atxo sid at block height + pub height_to_max_atxo: Mapxnk>, /// global rate history pub staking_global_rate_hist: Mapxnk, /// - self-delegation amount history @@ -67,6 +73,9 @@ pub struct ApiCache { Mapx>, /// there are no transactions lost before last_sid pub last_sid: Mapx, + /// State commitment history. + /// The BitDigest at index i is the state commitment of the ledger at block height i + 1. + pub state_commitment_version: Option>>, } impl ApiCache { @@ -89,15 +98,20 @@ impl ApiCache { "api_cache/{prefix}token_code_issuances", )), owner_memos: new_mapxnk!(format!("api_cache/{prefix}owner_memos",)), + abar_memos: new_mapx!(format!("api_cache/{prefix}abar_memos",)), utxos_to_map_index: new_mapxnk!(format!( "api_cache/{prefix}utxos_to_map_index", )), txo_to_txnid: new_mapxnk!(format!("api_cache/{prefix}txo_to_txnid",)), + atxo_to_txnid: new_mapx!(format!("api_cache/{prefix}atxo_to_txnid",)), txn_sid_to_hash: new_mapxnk!(format!("api_cache/{prefix}txn_sid_to_hash",)), txn_hash_to_sid: new_mapx!(format!("api_cache/{prefix}txn_hash_to_sid",)), staking_global_rate_hist: new_mapxnk!(format!( "api_cache/{prefix}staking_global_rate_hist", )), + height_to_max_atxo: new_mapxnk!(format!( + "api_cache/{prefix}height_to_max_atxo", + )), staking_self_delegation_hist: new_mapx!(format!( "api_cache/{prefix}staking_self_delegation_hist", )), @@ -108,6 +122,7 @@ impl ApiCache { "api_cache/{prefix}staking_delegation_rwd_hist", )), last_sid: new_mapx!(format!("api_cache/{prefix}last_sid",)), + state_commitment_version: None, } } @@ -115,16 +130,14 @@ impl ApiCache { #[inline(always)] pub fn add_created_asset(&mut self, creation: &DefineAsset, cur_height: u64) { let asset_code = creation.body.asset.code; - let code = if asset_code.val == ASSET_TYPE_FRA - || CFG.checkpoint.utxo_asset_prefix_height > cur_height - { - creation.body.asset.code - } else { - AssetTypeCode::from_prefix_and_raw_asset_type_code( - AssetTypePrefix::UserDefined, - &creation.body.asset.code, - ) - }; + + let code = AssetTypeCode::from_prefix_and_raw_asset_type_code( + AssetTypePrefix::UserDefined, + &asset_code, + &CFG.checkpoint, + cur_height, + ); + let prefix = self.prefix.clone(); let issuer = creation.pubkey; let mut tmp = creation.clone(); @@ -262,7 +275,19 @@ where Operation::Governance(i) => staking_gen!(i), Operation::FraDistribution(i) => staking_gen!(i), Operation::MintFra(i) => staking_gen!(i), - + Operation::BarToAbar(i) => { + related_addresses.insert(XfrAddress { + key: i.input_record().public_key, + }); + } + Operation::AbarToBar(i) => { + related_addresses.insert(XfrAddress { + key: i.note.get_public_key(), + }); + } + Operation::TransferAnonAsset(_) => { + // Anon + } Operation::ConvertAccount(i) => { related_addresses.insert(XfrAddress { key: i.get_related_address(), @@ -471,38 +496,45 @@ pub fn update_api_cache(ledger: &mut LedgerState) -> Result<()> { check_lost_data(ledger)?; - ledger.api_cache.as_mut().unwrap().cache_hist_data(); + let mut api_cache = ledger.api_cache.take().unwrap(); + + api_cache.cache_hist_data(); let block = if let Some(b) = ledger.blocks.last() { b } else { + ledger.api_cache = Some(api_cache); return Ok(()); }; - let prefix = ledger.api_cache.as_mut().unwrap().prefix.clone(); + let prefix = api_cache.prefix.clone(); + + // Update state commitment versions + api_cache.state_commitment_version = ledger.status.state_commitment_versions.last(); // Update ownership status - for (txn_sid, txo_sids) in block.txns.iter().map(|v| (v.tx_id, v.txo_ids.as_slice())) + for (txn_sid, txo_sids, atxo_sids) in block + .txns + .iter() + .map(|v| (v.tx_id, v.txo_ids.as_slice(), v.atxo_ids.as_slice())) { let curr_txn = ledger.get_transaction_light(txn_sid).c(d!())?.txn; // get the transaction, ownership addresses, and memos associated with each transaction let (addresses, owner_memos) = { - let addresses: Vec = txo_sids - .iter() - .map(|sid| XfrAddress { - key: ((ledger - .get_utxo_light(*sid) - .or_else(|| ledger.get_spent_utxo_light(*sid)) - .unwrap() - .utxo) - .0) - .record - .public_key, - }) - .collect(); + let mut addresses: Vec = vec![]; + for sid in txo_sids.iter() { + let key = ledger + .get_utxo_light(*sid) + .or_else(|| ledger.get_spent_utxo_light(*sid)) + .c(d!())? + .utxo + .0 + .record + .public_key; + addresses.push(XfrAddress { key }); + } let owner_memos = curr_txn.get_owner_memos_ref(); - (addresses, owner_memos) }; @@ -512,10 +544,7 @@ pub fn update_api_cache(ledger: &mut LedgerState) -> Result<()> { let key = XfrAddress { key: i.get_claim_publickey(), }; - ledger - .api_cache - .as_mut() - .unwrap() + api_cache .claim_hist_txns .entry(key) .or_insert_with(|| { @@ -532,13 +561,8 @@ pub fn update_api_cache(ledger: &mut LedgerState) -> Result<()> { key: me.utxo.record.public_key, }; #[allow(unused_mut)] - let mut hist = ledger - .api_cache - .as_mut() - .unwrap() - .coinbase_oper_hist - .entry(key) - .or_insert_with(|| { + let mut hist = + api_cache.coinbase_oper_hist.entry(key).or_insert_with(|| { new_mapxnk!(format!( "api_cache/{}coinbase_oper_hist/{}", prefix, @@ -555,10 +579,7 @@ pub fn update_api_cache(ledger: &mut LedgerState) -> Result<()> { // Apply classify_op for each operation in curr_txn let related_addresses = get_related_addresses(&curr_txn, classify_op); for address in &related_addresses { - ledger - .api_cache - .as_mut() - .unwrap() + api_cache .related_transactions .entry(*address) .or_insert_with(|| { @@ -574,10 +595,7 @@ pub fn update_api_cache(ledger: &mut LedgerState) -> Result<()> { // Update transferred nonconfidential assets let transferred_assets = get_transferred_nonconfidential_assets(&curr_txn); for asset in &transferred_assets { - ledger - .api_cache - .as_mut() - .unwrap() + api_cache .related_transfers .entry(*asset) .or_insert_with(|| { @@ -594,17 +612,13 @@ pub fn update_api_cache(ledger: &mut LedgerState) -> Result<()> { for op in &curr_txn.body.operations { match op { Operation::DefineAsset(define_asset) => { - ledger.api_cache.as_mut().unwrap().add_created_asset( + api_cache.add_created_asset( &define_asset, ledger.status.td_commit_height, ); } Operation::IssueAsset(issue_asset) => { - ledger - .api_cache - .as_mut() - .unwrap() - .cache_issuance(&issue_asset); + api_cache.cache_issuance(&issue_asset); } _ => {} }; @@ -615,41 +629,41 @@ pub fn update_api_cache(ledger: &mut LedgerState) -> Result<()> { .iter() .zip(addresses.iter().zip(owner_memos.iter())) { - ledger - .api_cache - .as_mut() - .unwrap() - .utxos_to_map_index - .insert(*txo_sid, *address); + api_cache.utxos_to_map_index.insert(*txo_sid, *address); let hash = curr_txn.hash_tm().hex().to_uppercase(); - ledger - .api_cache - .as_mut() - .unwrap() + api_cache .txo_to_txnid .insert(*txo_sid, (txn_sid, hash.clone())); - ledger - .api_cache - .as_mut() - .unwrap() - .txn_sid_to_hash - .insert(txn_sid, hash.clone()); - ledger - .api_cache - .as_mut() - .unwrap() - .txn_hash_to_sid - .insert(hash.clone(), txn_sid); + api_cache.txn_sid_to_hash.insert(txn_sid, hash.clone()); + api_cache.txn_hash_to_sid.insert(hash.clone(), txn_sid); if let Some(owner_memo) = owner_memo { - ledger - .api_cache - .as_mut() - .unwrap() + api_cache .owner_memos .insert(*txo_sid, (*owner_memo).clone()); } } + + let abar_memos = curr_txn.body.operations.iter().flat_map(|o| match o { + Operation::BarToAbar(b) => { + vec![b.axfr_memo()] + } + Operation::TransferAnonAsset(b) => b.note.body.owner_memos.clone(), + _ => vec![], + }); + + for (a, id) in abar_memos.zip(atxo_sids) { + api_cache.abar_memos.insert(*id, a); + let hash = curr_txn.hash_tm().hex().to_uppercase(); + api_cache.atxo_to_txnid.insert(*id, (txn_sid, hash.clone())); + } } + // Update block height to max atxo mapping + let max_atxo = api_cache.abar_memos.len().checked_sub(1); + let block_height = ledger.status.td_commit_height; + api_cache.height_to_max_atxo.insert(block_height, max_atxo); + + ledger.api_cache = Some(api_cache); + Ok(()) } diff --git a/src/ledger/src/store/helpers.rs b/src/ledger/src/store/helpers.rs index 8f6a3cf6b..57aa65b9f 100644 --- a/src/ledger/src/store/helpers.rs +++ b/src/ledger/src/store/helpers.rs @@ -4,27 +4,26 @@ use { super::{ - IssuerKeyPair, IssuerPublicKey, LedgerState, TracingPolicies, TracingPolicy, - TransferType, XfrNotePolicies, + IssuerPublicKey, LedgerState, TracingPolicies, TracingPolicy, XfrNotePolicies, }, crate::data_model::{ - Asset, AssetRules, AssetTypeCode, AssetTypePrefix, ConfidentialMemo, - DefineAsset, DefineAssetBody, IssueAsset, IssueAssetBody, Memo, Operation, - Transaction, TransferAsset, TransferAssetBody, TxOutput, TxnEffect, TxnSID, - TxoRef, TxoSID, ASSET_TYPE_FRA, + Asset, AssetRules, AssetTypeCode, ConfidentialMemo, DefineAsset, + DefineAssetBody, IssueAsset, IssueAssetBody, IssuerKeyPair, Memo, Operation, + Transaction, TransferAsset, TransferAssetBody, TransferType, TxOutput, + TxnEffect, TxnSID, TxoRef, TxoSID, }, globutils::SignatureOf, rand_core::{CryptoRng, RngCore}, ruc::*, std::fmt::Debug, zei::{ - setup::PublicParams, - xfr::{ + noah_algebra::ristretto::PedersenCommitmentRistretto, + noah_api::xfr::{ asset_record::AssetRecordType, asset_record::{build_blind_asset_record, open_blind_asset_record}, - sig::{XfrKeyPair, XfrPublicKey}, structs::{AssetRecord, AssetRecordTemplate}, }, + BlindAssetRecord, XfrKeyPair, XfrPublicKey, }, }; @@ -35,7 +34,7 @@ pub fn create_definition_transaction( asset_rules: AssetRules, memo: Option, seq_id: u64, -) -> Result<(Transaction, AssetTypeCode)> { +) -> Result { let issuer_key = IssuerPublicKey { key: *keypair.get_pk_ref(), }; @@ -44,18 +43,9 @@ pub fn create_definition_transaction( let asset_create = DefineAsset::new(asset_body, &IssuerKeyPair { keypair: &keypair }).c(d!())?; - let code = if code.val == ASSET_TYPE_FRA { - *code - } else { - AssetTypeCode::from_prefix_and_raw_asset_type_code( - AssetTypePrefix::UserDefined, - &code, - ) - }; - - Ok(( - Transaction::from_operation(Operation::DefineAsset(asset_create), seq_id), - code, + Ok(Transaction::from_operation( + Operation::DefineAsset(asset_create), + seq_id, )) } @@ -72,12 +62,7 @@ pub fn asset_creation_body( asset_rules: AssetRules, memo: Option, confidential_memo: Option, -) -> (DefineAssetBody, AssetTypeCode) { - let new_token_code = AssetTypeCode::from_prefix_and_raw_asset_type_code( - AssetTypePrefix::UserDefined, - token_code, - ); - +) -> DefineAssetBody { let mut token = Asset { code: *token_code, issuer: IssuerPublicKey { key: *issuer_key }, @@ -97,12 +82,9 @@ pub fn asset_creation_body( token.confidential_memo = ConfidentialMemo {}; } - ( - DefineAssetBody { - asset: Box::new(token), - }, - new_token_code, - ) + DefineAssetBody { + asset: Box::new(token), + } } #[allow(missing_docs)] @@ -148,7 +130,6 @@ pub fn apply_transaction( #[allow(missing_docs)] pub fn create_issue_and_transfer_txn( ledger: &mut LedgerState, - params: &PublicParams, code: &AssetTypeCode, amount: u64, issuer_keys: &XfrKeyPair, @@ -160,14 +141,11 @@ pub fn create_issue_and_transfer_txn( amount, code.val, AssetRecordType::NonConfidentialAmount_NonConfidentialAssetType, - issuer_keys.get_pk(), - ); - let (ba, _tracer_memo, owner_memo) = build_blind_asset_record( - &mut ledger.get_prng(), - ¶ms.pc_gens, - &ar_template, - vec![], + issuer_keys.get_pk().into_noah(), ); + let pc_gens = PedersenCommitmentRistretto::default(); + let (ba, _tracer_memo, owner_memo) = + build_blind_asset_record(&mut ledger.get_prng(), &pc_gens, &ar_template, vec![]); let asset_issuance_body = IssueAssetBody::new( &code, @@ -175,7 +153,7 @@ pub fn create_issue_and_transfer_txn( &[( TxOutput { id: None, - record: ba.clone(), + record: BlindAssetRecord::from_noah(&ba), lien: None, }, None, @@ -197,7 +175,7 @@ pub fn create_issue_and_transfer_txn( amount, code.val, AssetRecordType::NonConfidentialAmount_NonConfidentialAssetType, - *recipient_pk, + recipient_pk.into_noah(), ); let ar = AssetRecord::from_template_no_identity_tracing( &mut ledger.get_prng(), @@ -208,7 +186,7 @@ pub fn create_issue_and_transfer_txn( &mut ledger.get_prng(), vec![TxoRef::Relative(0)], &[AssetRecord::from_open_asset_record_no_asset_tracing( - open_blind_asset_record(&ba, &owner_memo, &issuer_keys).unwrap() + open_blind_asset_record(&ba, &owner_memo, &issuer_keys.into_noah()).unwrap() )], &[ar.clone()], None, @@ -227,7 +205,6 @@ pub fn create_issue_and_transfer_txn( #[allow(missing_docs)] pub fn create_issue_and_transfer_txn_with_asset_tracing( ledger: &mut LedgerState, - params: &PublicParams, code: &AssetTypeCode, amount: u64, issuer_keys: &XfrKeyPair, @@ -247,12 +224,13 @@ pub fn create_issue_and_transfer_txn_with_asset_tracing( amount, code.val, AssetRecordType::ConfidentialAmount_NonConfidentialAssetType, - issuer_keys.get_pk(), + issuer_keys.get_pk().into_noah(), tracing_policies.clone(), ); + let pc_gens = PedersenCommitmentRistretto::default(); let (ba, _tracer_memo, owner_memo) = build_blind_asset_record( &mut ledger.get_prng(), - ¶ms.pc_gens, + &pc_gens, &ar_template, vec![vec![]], ); @@ -263,7 +241,7 @@ pub fn create_issue_and_transfer_txn_with_asset_tracing( &[( TxOutput { id: None, - record: ba.clone(), + record: BlindAssetRecord::from_noah(&ba), lien: None, }, None, @@ -285,7 +263,7 @@ pub fn create_issue_and_transfer_txn_with_asset_tracing( amount, code.val, AssetRecordType::ConfidentialAmount_NonConfidentialAssetType, - *recipient_pk, + recipient_pk.into_noah(), tracing_policies.clone(), ); let ar = AssetRecord::from_template_no_identity_tracing( @@ -295,7 +273,7 @@ pub fn create_issue_and_transfer_txn_with_asset_tracing( .unwrap(); let tar = AssetRecord::from_open_asset_record_with_asset_tracing_but_no_identity( &mut ledger.get_prng(), - open_blind_asset_record(&ba, &owner_memo, &issuer_keys).unwrap(), + open_blind_asset_record(&ba, &owner_memo, &issuer_keys.into_noah()).unwrap(), tracing_policies, ) .unwrap(); @@ -324,7 +302,6 @@ pub fn create_issue_and_transfer_txn_with_asset_tracing( #[allow(missing_docs)] pub fn create_issuance_txn( ledger: &mut LedgerState, - params: &PublicParams, code: &AssetTypeCode, amount: u64, seq_num: u64, @@ -336,14 +313,11 @@ pub fn create_issuance_txn( amount, code.val, record_type, - issuer_keys.get_pk(), - ); - let (ba, _tracer_memo, _owner_memo) = build_blind_asset_record( - &mut ledger.get_prng(), - ¶ms.pc_gens, - &ar_template, - vec![], + issuer_keys.get_pk().into_noah(), ); + let pc_gens = PedersenCommitmentRistretto::default(); + let (ba, _tracer_memo, _owner_memo) = + build_blind_asset_record(&mut ledger.get_prng(), &pc_gens, &ar_template, vec![]); let asset_issuance_body = IssueAssetBody::new( &code, @@ -351,7 +325,7 @@ pub fn create_issuance_txn( &[( TxOutput { id: None, - record: ba, + record: BlindAssetRecord::from_noah(&ba), lien: None, }, None, diff --git a/src/ledger/src/store/mod.rs b/src/ledger/src/store/mod.rs index df4ffae74..28a2f42c8 100644 --- a/src/ledger/src/store/mod.rs +++ b/src/ledger/src/store/mod.rs @@ -12,12 +12,12 @@ pub use fbnc; use { crate::{ data_model::{ - AssetType, AssetTypeCode, AssetTypePrefix, AuthenticatedBlock, - AuthenticatedTransaction, AuthenticatedUtxo, AuthenticatedUtxoStatus, - BlockEffect, BlockSID, FinalizedBlock, FinalizedTransaction, IssuerKeyPair, - IssuerPublicKey, OutputPosition, StateCommitmentData, Transaction, - TransferType, TxnEffect, TxnSID, TxnTempSID, TxoSID, UnAuthenticatedUtxo, - Utxo, UtxoStatus, ASSET_TYPE_FRA, BLACK_HOLE_PUBKEY, + ATxoSID, AnonStateCommitmentData, AssetType, AssetTypeCode, AssetTypePrefix, + AuthenticatedBlock, AuthenticatedTransaction, AuthenticatedUtxo, + AuthenticatedUtxoStatus, BlockEffect, BlockSID, FinalizedBlock, + FinalizedTransaction, IssuerPublicKey, Operation, OutputPosition, + StateCommitmentData, Transaction, TxnEffect, TxnSID, TxnTempSID, TxoSID, + UnAuthenticatedUtxo, Utxo, UtxoStatus, BLACK_HOLE_PUBKEY, }, staking::{ Amount, Power, Staking, TendermintAddrRef, FF_PK_EXTRA_120_0000, FF_PK_LIST, @@ -29,7 +29,10 @@ use { bitmap::{BitMap, SparseMap}, config::abci::global_cfg::CFG, cryptohash::sha256::Digest as BitDigest, + digest::Digest, fbnc::{new_mapx, new_mapxnk, new_vecx, Mapx, Mapxnk, Vecx}, + fin_db::RocksDB, + globutils::wallet, globutils::{HashOf, ProofOf}, merkle_tree::AppendOnlyMerkle, parking_lot::RwLock, @@ -37,7 +40,9 @@ use { rand_core::SeedableRng, ruc::*, serde::{Deserialize, Serialize}, + sha2::Sha512, sliding_set::SlidingSet, + sparse_merkle_tree::{Key, SmtMap256}, std::{ collections::{BTreeMap, HashMap, HashSet}, env, @@ -47,14 +52,39 @@ use { ops::{Deref, DerefMut}, sync::Arc, }, - zei::xfr::{ - lib::XfrNotePolicies, - sig::XfrPublicKey, - structs::{OwnerMemo, TracingPolicies, TracingPolicy}, + storage::{ + state::{ChainState, State}, + store::{ImmutablePrefixedStore, PrefixedStore}, + }, + zei::{ + noah_accumulators::merkle_tree::{ + ImmutablePersistentMerkleTree, PersistentMerkleTree, Proof, TreePath, + }, + noah_algebra::{bn254::BN254Scalar, prelude::*}, + noah_api::{ + anon_xfr::{ + abar_to_abar::verify_anon_xfr_note, + structs::{ + AnonAssetRecord, AxfrOwnerMemo, Commitment, MTLeafInfo, MTNode, + MTPath, Nullifier, + }, + AXfrAddressFoldingInstance, TREE_DEPTH as MERKLE_TREE_DEPTH, + }, + parameters::{AddressFormat, VerifierParams}, + xfr::{ + structs::{TracingPolicies, TracingPolicy}, + XfrNotePolicies, + }, + }, + noah_crypto::anemoi_jive::{AnemoiJive, AnemoiJive254}, + OwnerMemo, XfrPublicKey, }, }; const TRANSACTION_WINDOW_WIDTH: u64 = 128; +const VERSION_WINDOW: u64 = 100; +const GENESIS_ANON_HASH: &str = + "2501917d72f915a3afb91ae561a0e4230d5d4edbb9b62fb7e2ea41f18c3038b5"; type TmpSidMap = HashMap)>; @@ -83,6 +113,10 @@ pub struct LedgerState { txn_merkle: Arc>, // Bitmap tracing all the live TXOs utxo_map: Arc>, + // Merkle Tree with all the ABARs created till now + abar_state: Arc>>, + // Sparse Merkle Tree to hold nullifier Set + nullifier_set: Arc>>, } impl LedgerState { @@ -117,7 +151,7 @@ impl LedgerState { ) -> Result { let tx = txe.txn.clone(); self.status - .check_txn_effects(&txe) + .check_txn_effects(&txe, &self.abar_state) .c(d!()) .and_then(|_| block.add_txn_effect(txe).c(d!())) .map(|tmpid| { @@ -182,7 +216,12 @@ impl LedgerState { Ok(()) } - fn update_state(&mut self, mut block: BlockEffect, tsm: &TmpSidMap) -> Result<()> { + fn update_state( + &mut self, + mut block: BlockEffect, + tsm: &TmpSidMap, + next_txn_sid: usize, + ) -> Result<()> { let mut tx_block = Vec::new(); let height = block.staking_simulator.cur_height(); @@ -211,6 +250,7 @@ impl LedgerState { txn: txn.clone(), tx_id: txn_sid, txo_ids: txo_sids.clone(), + atxo_ids: vec![], merkle_id, }); @@ -222,10 +262,21 @@ impl LedgerState { } drop(txn_merkle); + tx_block = self + .update_anon_stores( + block.new_nullifiers.clone(), + block.output_abars.clone(), + next_txn_sid, + tx_block, + ) + .c(d!())?; + // Checkpoint let block_merkle_id = self.checkpoint(&block).c(d!())?; block.temp_sids.clear(); block.txns.clear(); + block.output_abars.clear(); + block.new_nullifiers.clear(); let block_idx = self.blocks.len(); tx_block.iter().enumerate().for_each(|(tx_idx, tx)| { @@ -261,14 +312,61 @@ impl LedgerState { } } + let backup_next_txn_sid = self.status.next_txn.0; let (tsm, base_sid, max_sid) = self.status.apply_block_effects(&mut block); self.update_utxo_map(base_sid, max_sid, &block.temp_sids, &tsm) .c(d!()) - .and_then(|_| self.update_state(block, &tsm).c(d!())) + .and_then(|_| self.update_state(block, &tsm, backup_next_txn_sid).c(d!())) .map(|_| tsm) } + /// Apply the changes from current block + /// to the merkle trees holding anonymous data + pub fn update_anon_stores( + &mut self, + new_nullifiers: Vec, + output_abars: Vec>, + backup_next_txn_sid: usize, + mut tx_block: Vec, + ) -> Result> { + for n in new_nullifiers.iter() { + let d: Key = Key::from_bytes(n.noah_to_bytes()).c(d!())?; + + // if the nullifier hash is present in our nullifier set, fail the block + if self.nullifier_set.read().get(&d).c(d!())?.is_some() { + return Err(eg!("Nullifier hash already present in set")); + } + self.nullifier_set + .write() + .set(&d, Some(n.noah_to_bytes())) + .c(d!())?; + self.status.spent_abars.insert(*n, ()); + } + + let mut txn_sid = TxnSID(backup_next_txn_sid); + for (txn_abars, txn) in output_abars.iter().zip(tx_block.iter_mut()) { + let mut op_position = OutputPosition(0); + let mut atxo_ids: Vec = vec![]; + for abar in txn_abars { + let uid = self.add_abar(&abar).c(d!())?; + self.status.ax_utxos.insert(uid, abar.clone()); + self.status.owned_ax_utxos.insert(abar.commitment, uid); + self.status + .ax_txo_to_txn_location + .insert(uid, (txn_sid, op_position)); + + atxo_ids.push(uid); + self.status.next_atxo = ATxoSID(uid.0 + 1); + op_position = OutputPosition(op_position.0 + 1); + } + txn.atxo_ids = atxo_ids; + txn_sid = TxnSID(txn_sid.0 + 1); + } + + Ok(tx_block) + } + #[inline(always)] #[allow(missing_docs)] pub fn get_staking_mut(&mut self) -> &mut Staking { @@ -309,6 +407,7 @@ impl LedgerState { pub fn tmp_ledger() -> LedgerState { fbnc::clear(); let tmp_dir = globutils::fresh_tmp_dir().to_string_lossy().into_owned(); + env::set_var("FINDORAD_KEEP_HIST", "1"); LedgerState::new(&tmp_dir, Some("test")).unwrap() } @@ -316,7 +415,8 @@ impl LedgerState { // 1. Compute the hash of transactions in the block and update txns_in_block_hash // 2. Append txns_in_block_hash to block_merkle #[inline(always)] - fn compute_and_append_txns_hash(&mut self, block: &BlockEffect) -> u64 { + #[allow(missing_docs)] + pub fn compute_and_append_txns_hash(&mut self, block: &BlockEffect) -> u64 { // 1. Compute the hash of transactions in the block and update txns_in_block_hash let txns_in_block_hash = block.compute_txns_in_block_hash(); self.status.txns_in_block_hash = Some(txns_in_block_hash.clone()); @@ -332,7 +432,8 @@ impl LedgerState { ret } - fn compute_and_save_state_commitment_data(&mut self, pulse_count: u64) { + #[allow(missing_docs)] + pub fn compute_and_save_state_commitment_data(&mut self, pulse_count: u64) { let staking_data = if self.get_tendermint_height() < CFG.checkpoint.remove_fake_staking_hash && self.get_staking().has_been_inited() @@ -363,9 +464,97 @@ impl LedgerState { .state_commitment_versions .push(state_commitment_data.compute_commitment()); self.status.state_commitment_data = Some(state_commitment_data); + + // Commit Anon tree changes here following Tendermint protocol + pnk!(self.commit_anon_changes().c(d!())); + pnk!(self.commit_nullifier_changes().c(d!())); + + let abar_root_hash = + self.get_abar_root_hash().expect("failed to read root hash"); + + let anon_state_commitment_data = AnonStateCommitmentData { + abar_root_hash, + nullifier_root_hash: self + .nullifier_set + .read() + .merkle_root() + .unwrap_or(sparse_merkle_tree::ZERO_DIGEST), + }; + + let anon_hash = anon_state_commitment_data.compute_commitment(); + // don't push anon_state_commitment until any anon transactions is committed. + // This is to make sure the app hash changes occur for all nodes at the same time + if anon_hash.hex() != GENESIS_ANON_HASH { + self.status.anon_state_commitment_versions.push(anon_hash); + } + self.status.anon_state_commitment_data = Some(anon_state_commitment_data); + self.status.incr_block_commit_count(); } + #[inline(always)] + /// Adds a new abar to session cache and updates merkle hashes of ancestors + pub fn add_abar(&mut self, abar: &AnonAssetRecord) -> Result { + let mut abar_state_val = self.abar_state.write(); + let store = PrefixedStore::new("abar_store", &mut abar_state_val); + let mut mt = PersistentMerkleTree::new(store).c(d!())?; + + let leaf = hash_abar(mt.entry_count(), abar); + mt.add_commitment_hash(leaf).map(ATxoSID).c(d!()) + } + + #[inline(always)] + /// writes the changes from session cache to the RocksDB store + pub fn commit_anon_changes(&mut self) -> Result { + let mut abar_state_val = self.abar_state.write(); + let store = PrefixedStore::new("abar_store", &mut abar_state_val); + let mut mt = PersistentMerkleTree::new(store).c(d!())?; + + mt.commit().c(d!()) + } + + #[inline(always)] + /// writes the changes from session cache to the RocksDB store + pub fn commit_nullifier_changes(&mut self) -> Result { + self.nullifier_set.write().commit() + } + + #[inline(always)] + /// Fetches the root hash of the committed merkle tree of abar commitments directly from committed + /// state and ignore session cache + pub fn get_abar_root_hash(&self) -> Result { + let abar_query_state = State::new(self.abar_state.read().chain_state(), false); + let store = ImmutablePrefixedStore::new("abar_store", &abar_query_state); + let mt = ImmutablePersistentMerkleTree::new(store).c(d!())?; + + mt.get_root_with_depth(MERKLE_TREE_DEPTH).c(d!( + "probably due to badly constructed tree or data corruption" + )) + } + + #[inline(always)] + /// Generates a MTLeafInfo from the latest committed version of tree from committed state and + /// ignore session cache + pub fn get_abar_proof(&self, id: ATxoSID) -> Result { + let abar_query_state = State::new(self.abar_state.read().chain_state(), false); + let store = ImmutablePrefixedStore::new("abar_store", &abar_query_state); + let mt = ImmutablePersistentMerkleTree::new(store).c(d!())?; + + let t = mt + .generate_proof_with_depth(id.0, MERKLE_TREE_DEPTH) + .c(d!())?; + Ok(build_mt_leaf_info_from_proof(t, id.0)) + } + + /// Check if the nullifier hash is present in nullifier set + #[inline(always)] + pub fn check_nullifier_hash(&self, hash: String) -> Result { + let n = wallet::nullifier_from_base58(hash.as_str())?; + let d: Key = Key::from_bytes(n.noah_to_bytes()).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)] @@ -387,6 +576,25 @@ impl LedgerState { .and_then(|f| BitMap::open(f).c(d!())) } + // Initialize a persistent merkle tree for ABAR store. + #[inline(always)] + fn init_abar_state(path: &str) -> Result> { + let fdb = RocksDB::open(path).c(d!("failed to open db"))?; + let cs = Arc::new(RwLock::new(ChainState::new( + fdb, + "abar_db".to_string(), + VERSION_WINDOW, + ))); + Ok(State::new(cs, false)) + } + + // Initialize persistent Sparse Merkle tree for the Nullifier set + #[inline(always)] + fn init_nullifier_smt(path: &str) -> Result> { + let rdb = RocksDB::open(path).c(d!("failed to open db"))?; + Ok(SmtMap256::new(rdb)) + } + /// Initialize a new Ledger structure. pub fn new(basedir: &str, prefix: Option<&str>) -> Result { let prefix = if let Some(p) = prefix { @@ -398,6 +606,8 @@ impl LedgerState { let block_merkle_path = format!("{}/{}block_merkle", basedir, &prefix); let txn_merkle_path = format!("{}/{}txn_merkle", basedir, &prefix); let utxo_map_path = format!("{}/{}utxo_map", basedir, &prefix); + let abar_store_path = format!("{}/{}abar_store", basedir, &prefix); + let nullifier_store_path = format!("{}/{}nullifier_store", basedir, &prefix); // These iterms will be set under ${BNC_DATA_DIR} fs::create_dir_all(&basedir).c(d!())?; @@ -409,6 +619,12 @@ impl LedgerState { let blocks_path = prefix.clone() + "blocks"; let tx_to_block_location_path = prefix.clone() + "tx_to_block_location"; + let mut abar_state = LedgerState::init_abar_state(&abar_store_path).c(d!())?; + + // Initializing Merkle tree to set Empty tree root hash, which is a hash of null children + let store = PrefixedStore::new("abar_store", &mut abar_state); + let _ = PersistentMerkleTree::new(store).c(d!())?; + let mut ledger = LedgerState { status: LedgerStatus::new(&basedir, &snapshot_file).c(d!())?, block_merkle: Arc::new(RwLock::new( @@ -424,6 +640,10 @@ impl LedgerState { )), block_ctx: Some(BlockEffect::default()), api_cache: alt!(*KEEP_HIST, Some(ApiCache::new(&prefix)), None), + abar_state: Arc::new(RwLock::new(abar_state)), + nullifier_set: Arc::new(RwLock::new( + LedgerState::init_nullifier_smt(&nullifier_store_path).c(d!())?, + )), }; ledger.status.refresh_data(); @@ -617,7 +837,12 @@ impl LedgerState { - FF_PK_LIST .iter() .chain(extras.iter()) - .map(|pk| self.staking_get_nonconfidential_balance(pk).unwrap_or(0)) + .map(|pk| { + self.staking_get_nonconfidential_balance(&XfrPublicKey::from_noah( + &pk, + )) + .unwrap_or(0) + }) .sum::() - s.coinbase_balance() } @@ -801,7 +1026,7 @@ impl LedgerState { let res = sids .into_iter() - .zip(aus.into_iter()) + .zip(aus) .filter_map(|(sid, au)| au.map(|au| (sid, au))) .map(|(sid, au)| { ( @@ -812,7 +1037,7 @@ impl LedgerState { .txn .get_owner_memos_ref() .get(au.utxo_location.0) - .and_then(|i| i.cloned()), + .and_then(|i| i.clone()), ), ) }) @@ -821,6 +1046,46 @@ impl LedgerState { Ok(res) } + /// Get all abars with sid which are associated with a diversified public key + #[allow(dead_code)] + pub fn get_owned_abar(&self, com: &Commitment) -> Option { + self.status.owned_ax_utxos.get(com) + } + + /// Get abar commitment with sid + pub fn get_abar(&self, sid: &ATxoSID) -> Option { + self.status.get_abar(sid).map(|v| v.commitment) + } + + /// Get the owner memo of a abar by ATxoSID + #[allow(dead_code)] + pub fn get_abar_memo(&self, ax_id: ATxoSID) -> Option { + if let Some(txn_location) = self.status.ax_txo_to_txn_location.get(&ax_id) { + if let Ok(authenticated_txn) = self.get_transaction(txn_location.0) { + let memo: Vec = authenticated_txn + .finalized_txn + .txn + .body + .operations + .iter() + .flat_map(|o| match o { + Operation::BarToAbar(body) => vec![body.axfr_memo()], + Operation::TransferAnonAsset(body) => { + body.note.body.owner_memos.clone() + } + _ => vec![], + }) + .collect::>(); + + if memo.is_empty() { + return None; + } + return memo.get(txn_location.1 .0).cloned(); + }; + }; + None + } + #[inline(always)] #[allow(missing_docs)] pub fn get_issuance_num(&self, code: &AssetTypeCode) -> Option { @@ -857,6 +1122,16 @@ impl LedgerState { (commitment, block_count) } + #[inline(always)] + #[allow(missing_docs)] + pub fn get_anon_state_commitment(&self) -> (Vec, u64) { + let block_count = self.status.block_commit_count; + let commitment = self.status.anon_state_commitment_versions.last(); + + let hash = commitment.map_or_else(Vec::new, |c| c.as_ref().to_vec()); + (hash, block_count) + } + /// Get utxo status and its proof data pub fn get_utxo_status(&self, addr: TxoSID) -> AuthenticatedUtxoStatus { let state_commitment_data = self.status.state_commitment_data.as_ref().unwrap(); @@ -973,38 +1248,78 @@ impl LedgerState { pub struct LedgerStatus { /// the file path of the snapshot pub snapshot_file: String, - // all currently-unspent TXOs + /// all currently-unspent TXOs + #[serde(default = "default_status_utxos")] utxos: Mapxnk, + /// all non-confidential balances + #[serde(default = "default_status_nonconfidential_balances")] nonconfidential_balances: Mapx, + /// all owned utxos + #[serde(default = "default_status_owned_utxos")] owned_utxos: Mapx>, + /// all existing ax_utxos + #[serde(default = "default_status_ax_utxos")] + ax_utxos: Mapx, + /// all owned abars + #[serde(default = "default_status_owned_ax_utxos")] + owned_ax_utxos: Mapx, /// all spent TXOs + #[serde(default = "default_status_spent_utxos")] pub spent_utxos: Mapxnk, - // Map a TXO to its output position in a transaction + /// all spent abars + #[serde(default = "default_status_spent_abars")] + pub spent_abars: Mapx, + /// Map a TXO to its output position in a transaction + #[serde(default = "default_status_txo_to_txn_location")] txo_to_txn_location: Mapxnk, - // State commitment history. - // The BitDigest at index i is the state commitment of the ledger at block height i + 1. + /// Map a Anonymous TXO to its output position in a transaction + #[serde(default = "default_status_ax_txo_to_txn_location")] + ax_txo_to_txn_location: Mapx, + /// State commitment history. + /// The BitDigest at index i is the state commitment of the ledger at block height i + 1. + #[serde(default = "default_status_state_commitment_versions")] state_commitment_versions: Vecx>>, - // Registered asset types + /// Anon state commitment versions + #[serde(default = "default_status_anon_state_commitment_versions")] + anon_state_commitment_versions: Vecx>>, + /// Registered asset types + #[serde(default = "default_status_asset_types")] asset_types: Mapx, - // Issuance number is always increasing + /// Issuance number is always increasing + #[serde(default = "default_status_issuance_num")] issuance_num: Mapx, - // Issuance amounts for assets with limits + /// Issuance amounts for assets with limits + #[serde(default = "default_status_issuance_amounts")] issuance_amounts: Mapx, - // Should be equal to the count of transactions + /// Should be equal to the count of transactions + #[serde(default = "default_status_next_txn")] next_txn: TxnSID, - // Should be equal to the count of TXOs + /// Should be equal to the count of TXOs + #[serde(default = "default_status_next_txo")] next_txo: TxoSID, - // Each block corresponds to such a summary structure + /// Should be equal to the count of ABARs + #[serde(default = "default_status_next_atxo")] + next_atxo: ATxoSID, + /// Each block corresponds to such a summary structure + #[serde(default = "default_status_state_commitment_data")] state_commitment_data: Option, - // number of non-empty blocks, equal to: - + /// Anon state commitment + #[serde(default = "default_status_anon_state_commitment_data")] + anon_state_commitment_data: Option, + /// number of non-empty blocks, equal to: - + #[serde(default = "default_status_block_commit_count")] block_commit_count: u64, - // Hash of the transactions in the most recent block + /// Hash of the transactions in the most recent block + #[serde(default = "default_status_txns_in_block_hash")] txns_in_block_hash: Option>>, - // Sliding window of operations for replay attack prevention + /// Sliding window of operations for replay attack prevention + #[serde(default = "default_status_sliding_set")] sliding_set: SlidingSet<[u8; 8]>, - // POS-related implementations + /// POS-related implementations + #[serde(default = "default_status_staking")] staking: Staking, - // tendermint commit height + /// tendermint commit height + #[serde(default = "default_status_td_commit_height")] td_commit_height: u64, } @@ -1018,6 +1333,18 @@ impl LedgerStatus { .unwrap_or_default() } + #[inline(always)] + #[allow(missing_docs)] + pub fn get_owned_abar(&self, com: &Commitment) -> Option { + self.owned_ax_utxos.get(com) + } + + #[inline(always)] + #[allow(missing_docs)] + pub fn get_abar(&self, uid: &ATxoSID) -> Option { + self.ax_utxos.get(uid) + } + #[inline(always)] #[allow(missing_docs)] fn get_utxo(&self, id: TxoSID) -> Option { @@ -1079,42 +1406,34 @@ impl LedgerStatus { } fn create(snapshot_file: &str) -> Result { - let utxos_path = SNAPSHOT_ENTRIES_DIR.to_owned() + "/utxo"; - let nonconfidential_balances_path = - SNAPSHOT_ENTRIES_DIR.to_owned() + "/nonconfidential_balances"; - let spent_utxos_path = SNAPSHOT_ENTRIES_DIR.to_owned() + "/spent_utxos"; - let txo_to_txn_location_path = - SNAPSHOT_ENTRIES_DIR.to_owned() + "/txo_to_txn_location"; - let issuance_amounts_path = - SNAPSHOT_ENTRIES_DIR.to_owned() + "/issuance_amounts"; - let state_commitment_versions_path = - SNAPSHOT_ENTRIES_DIR.to_owned() + "/state_commitment_versions"; - let asset_types_path = SNAPSHOT_ENTRIES_DIR.to_owned() + "/asset_types"; - let issuance_num_path = SNAPSHOT_ENTRIES_DIR.to_owned() + "/issuance_num"; - let owned_utxos_path = SNAPSHOT_ENTRIES_DIR.to_owned() + "/owned_utxos"; - - let ledger = LedgerStatus { + Ok(LedgerStatus { snapshot_file: snapshot_file.to_owned(), - sliding_set: SlidingSet::<[u8; 8]>::new(TRANSACTION_WINDOW_WIDTH as usize), - utxos: new_mapxnk!(utxos_path.as_str()), - nonconfidential_balances: new_mapx!(nonconfidential_balances_path.as_str()), - owned_utxos: new_mapx!(owned_utxos_path.as_str()), - spent_utxos: new_mapxnk!(spent_utxos_path.as_str()), - txo_to_txn_location: new_mapxnk!(txo_to_txn_location_path.as_str()), - issuance_amounts: new_mapx!(issuance_amounts_path.as_str()), - state_commitment_versions: new_vecx!(state_commitment_versions_path.as_str()), - asset_types: new_mapx!(asset_types_path.as_str()), - issuance_num: new_mapx!(issuance_num_path.as_str()), - next_txn: TxnSID(0), - next_txo: TxoSID(0), - txns_in_block_hash: None, - state_commitment_data: None, - block_commit_count: 0, - staking: Staking::new(), - td_commit_height: 0, - }; - - Ok(ledger) + sliding_set: default_status_sliding_set(), + utxos: default_status_utxos(), + nonconfidential_balances: default_status_nonconfidential_balances(), + owned_utxos: default_status_owned_utxos(), + ax_utxos: default_status_ax_utxos(), + owned_ax_utxos: default_status_owned_ax_utxos(), + spent_utxos: default_status_spent_utxos(), + spent_abars: default_status_spent_abars(), + txo_to_txn_location: default_status_txo_to_txn_location(), + ax_txo_to_txn_location: default_status_ax_txo_to_txn_location(), + issuance_amounts: default_status_issuance_amounts(), + state_commitment_versions: default_status_state_commitment_versions(), + anon_state_commitment_versions: + default_status_anon_state_commitment_versions(), + asset_types: default_status_asset_types(), + issuance_num: default_status_issuance_num(), + next_txn: default_status_next_txn(), + next_txo: default_status_next_txo(), + next_atxo: default_status_next_atxo(), + txns_in_block_hash: default_status_txns_in_block_hash(), + state_commitment_data: default_status_state_commitment_data(), + anon_state_commitment_data: default_status_anon_state_commitment_data(), + block_commit_count: default_status_block_commit_count(), + staking: default_status_staking(), + td_commit_height: default_status_td_commit_height(), + }) } #[inline(always)] @@ -1132,7 +1451,11 @@ impl LedgerStatus { // // ledger.check_txn_effects(txn_effect); // block.add_txn_effect(txn_effect); - fn check_txn_effects(&self, txn_effect: &TxnEffect) -> Result<()> { + fn check_txn_effects( + &self, + txn_effect: &TxnEffect, + abar_state: &Arc>>, + ) -> Result<()> { // The current transactions seq_id must be within the sliding window over seq_ids let (rand, seq_id) = ( txn_effect.txn.body.no_replay_token.get_rand(), @@ -1224,6 +1547,22 @@ impl LedgerStatus { // Asset issuance should match the currently registered key } + let get_effect_asset = + |derived_asset_code: &AssetTypeCode| -> Option { + for (code, asset) in &txn_effect.new_asset_codes { + let dc = AssetTypeCode::from_prefix_and_raw_asset_type_code( + AssetTypePrefix::UserDefined, + &code, + &CFG.checkpoint, + self.td_commit_height, + ); + if dc == *derived_asset_code { + return Some(asset.clone()); + } + } + None + }; + // New issuance numbers // (1) Must refer to a created asset type // - NOTE: if the asset type is created in this transaction, this @@ -1237,7 +1576,7 @@ impl LedgerStatus { let asset_type = self .asset_types .get(&code) - .or_else(|| txn_effect.new_asset_codes.get(&code).cloned()) + .or_else(|| get_effect_asset(&code)) .c(d!())?; let proper_key = asset_type.properties.issuer; if *iss_key != proper_key { @@ -1319,6 +1658,69 @@ impl LedgerStatus { } } + // current merkle tree version. + let abar_query_state = State::new(abar_state.read().chain_state(), false); + let store = ImmutablePrefixedStore::new("abar_store", &abar_query_state); + let abar_mt = ImmutablePersistentMerkleTree::new(store).c(d!())?; + + let mut hasher = Sha512::new(); + hasher.update(txn_effect.txn.body.digest()); + + // An axfr_body requires versioned merkle root hash for verification. + // here with LedgerStatus available. + for axfr_note in txn_effect.axfr_bodies.iter() { + for input in &axfr_note.body.inputs { + if self.spent_abars.get(&input).is_some() { + return Err(eg!("Input abar must be unspent")); + } + } + + let af = match axfr_note.folding_instance { + AXfrAddressFoldingInstance::Secp256k1(_) => AddressFormat::SECP256K1, + AXfrAddressFoldingInstance::Ed25519(_) => AddressFormat::ED25519, + }; + let verifier_params = VerifierParams::get_abar_to_abar( + axfr_note.body.inputs.len(), + axfr_note.body.outputs.len(), + af, + ) + .c(d!())?; + let abar_version = axfr_note.body.merkle_root_version; + if abar_mt.version() - abar_version > VERSION_WINDOW { + return Err(eg!("Proof is old, need rebuild!")); + } + let version_root = abar_mt + .get_root_with_depth_and_version(MERKLE_TREE_DEPTH, abar_version) + .c(d!())?; + + verify_anon_xfr_note( + &verifier_params, + axfr_note, + &version_root, + hasher.clone(), + ) + .c(d!("Anon Transfer proof verification failed"))?; + } + + // An axfr_abar_conv requires versioned merkle root hash for verification. + for abar_conv in &txn_effect.abar_conv_inputs { + if self.spent_abars.get(&abar_conv.get_input()).is_some() { + return Err(eg!("Input abar must be unspent")); + } + + // Get verifier params + let abar_version = abar_conv.get_merkle_root_version(); + if abar_mt.version() - abar_version > VERSION_WINDOW { + return Err(eg!("Proof is old, need rebuild!")); + } + let version_root = abar_mt + .get_root_with_depth_and_version(MERKLE_TREE_DEPTH, abar_version) + .c(d!())?; + + // verify zk proof with merkle root + abar_conv.verify(version_root, hasher.clone())?; + } + Ok(()) } @@ -1328,19 +1730,17 @@ impl LedgerStatus { // that is ever false, it's a bug). // // This drains every field of `block` except `txns` and `temp_sids`. + #[allow(unused_mut)] + #[allow(clippy::unwrap_or_default)] fn apply_block_effects(&mut self, block: &mut BlockEffect) -> (TmpSidMap, u64, u64) { let base_sid = self.next_txo.0; - let handle_asset_type_code = |code: AssetTypeCode| -> AssetTypeCode { - if CFG.checkpoint.utxo_asset_prefix_height > self.td_commit_height - || code.val == ASSET_TYPE_FRA - { - code - } else { - AssetTypeCode::from_prefix_and_raw_asset_type_code( - AssetTypePrefix::UserDefined, - &code, - ) - } + let handle_asset_type_code = |code: AssetTypeCode| { + AssetTypeCode::from_prefix_and_raw_asset_type_code( + AssetTypePrefix::UserDefined, + &code, + &CFG.checkpoint, + self.td_commit_height, + ) }; for no_replay_token in block.no_replay_tokens.iter() { let (rand, seq_id) = ( @@ -1477,3 +1877,127 @@ pub struct LoggedBlock { pub fn flush_data() { fbnc::flush_data(); } + +fn build_mt_leaf_info_from_proof(proof: Proof, uid: u64) -> MTLeafInfo { + return MTLeafInfo { + path: MTPath { + nodes: proof + .nodes + .iter() + .map(|e| MTNode { + left: e.left, + mid: e.mid, + right: e.right, + is_left_child: (e.path == TreePath::Left) as u8, + is_mid_child: (e.path == TreePath::Middle) as u8, + is_right_child: (e.path == TreePath::Right) as u8, + }) + .collect(), + }, + root: proof.root, + root_version: proof.root_version, + uid, + }; +} + +fn hash_abar(uid: u64, abar: &AnonAssetRecord) -> BN254Scalar { + AnemoiJive254::eval_variable_length_hash(&[BN254Scalar::from(uid), abar.commitment]) +} + +fn default_status_utxos() -> Mapxnk { + new_mapxnk!(SNAPSHOT_ENTRIES_DIR.to_owned() + "/utxo") +} + +fn default_status_owned_utxos() -> Mapx> { + new_mapx!(SNAPSHOT_ENTRIES_DIR.to_owned() + "/owned_utxos") +} + +fn default_status_nonconfidential_balances() -> Mapx { + new_mapx!(SNAPSHOT_ENTRIES_DIR.to_owned() + "/nonconfidential_balances") +} + +fn default_status_ax_utxos() -> Mapx { + new_mapx!(SNAPSHOT_ENTRIES_DIR.to_owned() + "/ax_utxos") +} + +fn default_status_owned_ax_utxos() -> Mapx { + new_mapx!(SNAPSHOT_ENTRIES_DIR.to_owned() + "/owned_ax_utxos") +} + +fn default_status_spent_utxos() -> Mapxnk { + new_mapxnk!(SNAPSHOT_ENTRIES_DIR.to_owned() + "/spent_utxos") +} + +fn default_status_spent_abars() -> Mapx { + new_mapx!(SNAPSHOT_ENTRIES_DIR.to_owned() + "/spent_abars") +} + +fn default_status_txo_to_txn_location() -> Mapxnk { + new_mapxnk!(SNAPSHOT_ENTRIES_DIR.to_owned() + "/txo_to_txn_location") +} + +fn default_status_ax_txo_to_txn_location() -> Mapx { + new_mapx!(SNAPSHOT_ENTRIES_DIR.to_owned() + "/atxo_to_txn_location") +} + +fn default_status_issuance_amounts() -> Mapx { + new_mapx!(SNAPSHOT_ENTRIES_DIR.to_owned() + "/issuance_amounts") +} + +fn default_status_state_commitment_versions() -> Vecx>> +{ + new_vecx!(SNAPSHOT_ENTRIES_DIR.to_owned() + "/state_commitment_versions") +} + +fn default_status_anon_state_commitment_versions( +) -> Vecx>> { + new_vecx!(SNAPSHOT_ENTRIES_DIR.to_owned() + "/anon_state_commitment_versions") +} + +fn default_status_asset_types() -> Mapx { + new_mapx!(SNAPSHOT_ENTRIES_DIR.to_owned() + "/asset_types") +} + +fn default_status_issuance_num() -> Mapx { + new_mapx!(SNAPSHOT_ENTRIES_DIR.to_owned() + "/issuance_num") +} + +fn default_status_next_txn() -> TxnSID { + TxnSID(0) +} + +fn default_status_next_txo() -> TxoSID { + TxoSID(0) +} + +fn default_status_next_atxo() -> ATxoSID { + ATxoSID(0) +} + +fn default_status_txns_in_block_hash() -> Option>> { + None +} + +fn default_status_state_commitment_data() -> Option { + None +} + +fn default_status_anon_state_commitment_data() -> Option { + None +} + +fn default_status_block_commit_count() -> u64 { + 0 +} + +fn default_status_staking() -> Staking { + Staking::new() +} + +fn default_status_td_commit_height() -> u64 { + 0 +} + +fn default_status_sliding_set() -> SlidingSet<[u8; 8]> { + SlidingSet::<[u8; 8]>::new(TRANSACTION_WINDOW_WIDTH as usize) +} diff --git a/src/ledger/src/store/test.rs b/src/ledger/src/store/test.rs old mode 100644 new mode 100755 index b6cb5cb56..fb1677abc --- a/src/ledger/src/store/test.rs +++ b/src/ledger/src/store/test.rs @@ -1,23 +1,32 @@ #![cfg(test)] #![allow(missing_docs)] - use { super::{helpers::*, *}, - crate::data_model::{ - AssetRules, AssetTypeCode, IssueAsset, IssueAssetBody, Memo, Operation, - Transaction, TransferAsset, TransferAssetBody, TxOutput, TxnEffect, TxoRef, - TxoSID, ASSET_TYPE_FRA, BLACK_HOLE_PUBKEY, TX_FEE_MIN, + crate::{ + data_model::{ + get_abar_commitment, AssetRules, AssetTypeCode, IssueAsset, IssueAssetBody, + IssuerKeyPair, Memo, Operation, Transaction, TransferAsset, + TransferAssetBody, TransferType, TxOutput, TxnEffect, TxoRef, TxoSID, + ASSET_TYPE_FRA, BLACK_HOLE_PUBKEY, TX_FEE_MIN, + }, + store::{helpers::create_definition_transaction, utils::fra_gen_initial_tx}, }, rand_core::SeedableRng, zei::{ - setup::PublicParams, - xfr::{ - asset_record::{ - build_blind_asset_record, open_blind_asset_record, AssetRecordType, + noah_algebra::{ + prelude::{One, Zero}, + ristretto::PedersenCommitmentRistretto, + }, + noah_api::{ + anon_xfr::structs::OpenAnonAssetRecordBuilder, + xfr::{ + asset_record::{ + build_blind_asset_record, open_blind_asset_record, AssetRecordType, + }, + structs::{AssetRecord, AssetRecordTemplate}, }, - sig::XfrKeyPair, - structs::{AssetRecord, AssetRecordTemplate}, }, + BlindAssetRecord, XfrKeyPair, }, }; @@ -29,6 +38,8 @@ fn abort_block(block: BlockEffect) -> HashMap { block.temp_sids.drain(..).zip(txns).collect(); block.txos.clear(); + block.output_abars.clear(); + block.new_nullifiers.clear(); block.input_txos.clear(); block.new_asset_codes.clear(); block.new_issuance_nums.clear(); @@ -85,16 +96,19 @@ fn test_asset_creation_valid() { let keypair = build_keys(&mut prng); let code = AssetTypeCode::gen_random(); - let (asset_body, mut token_code) = asset_creation_body( + let asset_body = asset_creation_body( &code, keypair.get_pk_ref(), AssetRules::default(), None, None, ); - if CFG.checkpoint.utxo_asset_prefix_height > state.get_tendermint_height() { - token_code = code; - } + let token_code = AssetTypeCode::from_prefix_and_raw_asset_type_code( + AssetTypePrefix::UserDefined, + &code, + &CFG.checkpoint, + state.get_tendermint_height(), + ); let asset_create = asset_creation_operation(&asset_body, &keypair); let seq_id = state.get_block_commit_count(); let tx = Transaction::from_operation(Operation::DefineAsset(asset_create), seq_id); @@ -106,12 +120,6 @@ fn test_asset_creation_valid() { } assert!(state.get_asset_type(&token_code).is_some()); - - assert_eq!( - *asset_body.asset, - state.get_asset_type(&token_code).unwrap().properties - ); - assert_eq!(0, state.get_asset_type(&token_code).unwrap().units); } @@ -121,7 +129,7 @@ fn test_asset_creation_invalid_public_key() { // Create a valid asset creation operation. let mut prng = ChaChaRng::from_entropy(); let keypair = build_keys(&mut prng); - let (asset_body, _) = asset_creation_body( + let asset_body = asset_creation_body( &AssetTypeCode::gen_random(), keypair.get_pk_ref(), AssetRules::default(), @@ -141,16 +149,16 @@ fn test_asset_creation_invalid_public_key() { } #[test] +#[allow(clippy::redundant_clone)] fn test_asset_transfer() { let mut ledger = LedgerState::tmp_ledger(); - let params = PublicParams::default(); let code = AssetTypeCode::gen_random(); let mut prng = ChaChaRng::from_entropy(); let key_pair = XfrKeyPair::generate(&mut prng); let key_pair_adversary = XfrKeyPair::generate(&mut ledger.get_prng()); - let (tx, mut new_code) = create_definition_transaction( + let tx = create_definition_transaction( &code, &key_pair, AssetRules::default(), @@ -158,9 +166,12 @@ fn test_asset_transfer() { ledger.get_block_commit_count(), ) .unwrap(); - if CFG.checkpoint.utxo_asset_prefix_height > ledger.get_tendermint_height() { - new_code = code; - } + let new_code = AssetTypeCode::from_prefix_and_raw_asset_type_code( + AssetTypePrefix::UserDefined, + &code, + &CFG.checkpoint, + ledger.get_tendermint_height(), + ); let effect = TxnEffect::compute_effect(tx).unwrap(); { let mut block = ledger.start_block().unwrap(); @@ -174,15 +185,11 @@ fn test_asset_transfer() { 100, new_code.val, art, - key_pair.get_pk(), + key_pair.get_pk().into_noah(), ); - let (ba, _, _) = build_blind_asset_record( - &mut ledger.get_prng(), - ¶ms.pc_gens, - &template, - vec![], - ); - let second_ba = ba.clone(); + let pc_gens = PedersenCommitmentRistretto::default(); + let (ba, _, _) = + build_blind_asset_record(&mut ledger.get_prng(), &pc_gens, &template, vec![]); let asset_issuance_body = IssueAssetBody::new( &new_code, @@ -191,7 +198,7 @@ fn test_asset_transfer() { ( TxOutput { id: None, - record: ba, + record: BlindAssetRecord::from_noah(&ba), lien: None, }, None, @@ -199,7 +206,7 @@ fn test_asset_transfer() { ( TxOutput { id: None, - record: second_ba, + record: BlindAssetRecord::from_noah(&ba.clone()), lien: None, }, None, @@ -226,6 +233,8 @@ fn test_asset_transfer() { .unwrap() .remove(&temp_sid) .unwrap(); + ledger.api_cache.as_mut().unwrap().state_commitment_version = + ledger.status.state_commitment_versions.last(); let state_commitment = ledger.get_state_commitment().0; for txo_id in &txos { @@ -242,14 +251,16 @@ fn test_asset_transfer() { // Construct transfer operation let input_bar_proof = ledger.get_utxo(txo_sid).unwrap(); let input_bar = (input_bar_proof.clone().utxo.0).record; - let input_oar = open_blind_asset_record(&input_bar, &None, &key_pair).unwrap(); + let input_oar = + open_blind_asset_record(&input_bar.into_noah(), &None, &key_pair.into_noah()) + .unwrap(); assert!(input_bar_proof.is_valid(state_commitment)); let output_template = AssetRecordTemplate::with_no_asset_tracing( 100, new_code.val, art, - key_pair_adversary.get_pk(), + key_pair_adversary.get_pk().into_noah(), ); let output_ar = AssetRecord::from_template_no_identity_tracing( &mut ledger.get_prng(), @@ -289,6 +300,8 @@ fn test_asset_transfer() { .unwrap() .remove(&temp_sid) .unwrap(); + ledger.api_cache.as_mut().unwrap().state_commitment_version = + ledger.status.state_commitment_versions.last(); // Ensure that previous txo is now spent let state_commitment = ledger.get_state_commitment().0; let utxo_status = ledger.get_utxo_status(TxoSID(0)); @@ -317,7 +330,7 @@ fn test_asset_creation_invalid_signature() { let mut prng = ChaChaRng::from_entropy(); let keypair1 = build_keys(&mut prng); - let (asset_body, _) = asset_creation_body( + let asset_body = asset_creation_body( &AssetTypeCode::gen_random(), keypair1.get_pk_ref(), AssetRules::default(), @@ -340,13 +353,11 @@ fn test_asset_creation_invalid_signature() { fn asset_issued() { let mut ledger = LedgerState::tmp_ledger(); - let params = PublicParams::default(); - assert!(ledger.get_state_commitment() == (HashOf::new(&None), 0)); let token_code = AssetTypeCode::gen_random(); let keypair = build_keys(&mut ledger.get_prng()); let seq_id = ledger.get_block_commit_count(); - let (tx, mut new_token_code) = create_definition_transaction( + let tx = create_definition_transaction( &token_code, &keypair, AssetRules::default(), @@ -355,9 +366,12 @@ fn asset_issued() { ) .unwrap(); - if CFG.checkpoint.utxo_asset_prefix_height > ledger.get_tendermint_height() { - new_token_code = token_code; - } + let new_token_code = AssetTypeCode::from_prefix_and_raw_asset_type_code( + AssetTypePrefix::UserDefined, + &token_code, + &CFG.checkpoint, + ledger.get_tendermint_height(), + ); let effect = TxnEffect::compute_effect(tx).unwrap(); { @@ -371,18 +385,19 @@ fn asset_issued() { 100, new_token_code.val, art, - *keypair.get_pk_ref(), + keypair.get_pk_ref().into_noah(), ); + let pc_gens = PedersenCommitmentRistretto::default(); let (ba, _, _) = - build_blind_asset_record(&mut ledger.get_prng(), ¶ms.pc_gens, &ar, vec![]); + build_blind_asset_record(&mut ledger.get_prng(), &pc_gens, &ar, vec![]); let asset_issuance_body = IssueAssetBody::new( &new_token_code, 0, &[( TxOutput { id: None, - record: ba, + record: BlindAssetRecord::from_noah(&ba), lien: None, }, None, @@ -419,6 +434,8 @@ fn asset_issued() { let transaction = ledger.get_transaction(txn_sid).unwrap(); let txn_id = transaction.finalized_txn.tx_id; + ledger.api_cache.as_mut().unwrap().state_commitment_version = + ledger.status.state_commitment_versions.last(); let state_commitment_and_version = ledger.get_state_commitment(); println!("utxos = {:?}", ledger.status.utxos); @@ -476,7 +493,6 @@ fn asset_issued() { #[test] pub fn test_transferable() { let mut ledger = LedgerState::tmp_ledger(); - let params = PublicParams::default(); let issuer = XfrKeyPair::generate(&mut ledger.get_prng()); let alice = XfrKeyPair::generate(&mut ledger.get_prng()); let bob = XfrKeyPair::generate(&mut ledger.get_prng()); @@ -484,7 +500,7 @@ pub fn test_transferable() { // Define fiat token let code = AssetTypeCode::gen_random(); let seq_id = ledger.get_block_commit_count(); - let (tx, mut new_code) = create_definition_transaction( + let tx = create_definition_transaction( &code, &issuer, AssetRules::default().set_transferable(false).clone(), @@ -492,13 +508,16 @@ pub fn test_transferable() { seq_id, ) .unwrap(); - if CFG.checkpoint.utxo_asset_prefix_height > ledger.get_tendermint_height() { - new_code = code; - } + let new_code = AssetTypeCode::from_prefix_and_raw_asset_type_code( + AssetTypePrefix::UserDefined, + &code, + &CFG.checkpoint, + ledger.get_tendermint_height(), + ); + apply_transaction(&mut ledger, tx); let (tx, _) = create_issue_and_transfer_txn( &mut ledger, - ¶ms, &new_code, 100, &issuer, @@ -514,7 +533,7 @@ pub fn test_transferable() { 100, new_code.val, AssetRecordType::NonConfidentialAmount_NonConfidentialAssetType, - bob.get_pk(), + bob.get_pk().into_noah(), ); let record = AssetRecord::from_template_no_identity_tracing( &mut ledger.get_prng(), @@ -528,7 +547,8 @@ pub fn test_transferable() { &mut ledger.get_prng(), vec![TxoRef::Absolute(sid)], &[AssetRecord::from_open_asset_record_no_asset_tracing( - open_blind_asset_record(&bar, &None, &alice).unwrap(), + open_blind_asset_record(&bar.into_noah(), &None, &alice.into_noah()) + .unwrap(), )], &[record], None, @@ -551,7 +571,7 @@ pub fn test_transferable() { 100, new_code.val, AssetRecordType::ConfidentialAmount_ConfidentialAssetType, - bob.get_pk(), + bob.get_pk().into_noah(), ); let record = AssetRecord::from_template_no_identity_tracing( &mut ledger.get_prng(), @@ -565,7 +585,8 @@ pub fn test_transferable() { &mut ledger.get_prng(), vec![TxoRef::Absolute(sid)], &[AssetRecord::from_open_asset_record_no_asset_tracing( - open_blind_asset_record(&bar, &None, &alice).unwrap(), + open_blind_asset_record(&bar.into_noah(), &None, &alice.into_noah()) + .unwrap(), )], &[record], None, @@ -589,7 +610,7 @@ pub fn test_transferable() { 100, new_code.val, AssetRecordType::NonConfidentialAmount_NonConfidentialAssetType, - bob.get_pk(), + bob.get_pk().into_noah(), ); let second_record = AssetRecord::from_template_no_identity_tracing( &mut ledger.get_prng(), @@ -598,7 +619,6 @@ pub fn test_transferable() { .unwrap(); let (mut tx, ar) = create_issue_and_transfer_txn( &mut ledger, - ¶ms, &new_code, 100, &issuer, @@ -630,14 +650,13 @@ pub fn test_transferable() { #[test] pub fn test_max_units() { let mut ledger = LedgerState::tmp_ledger(); - let params = PublicParams::default(); let issuer = XfrKeyPair::generate(&mut ledger.get_prng()); // Define fiat token let code = AssetTypeCode::gen_random(); let seq_id = ledger.get_block_commit_count(); - let (tx, mut new_code) = create_definition_transaction( + let tx = create_definition_transaction( &code, &issuer, AssetRules::default().set_max_units(Some(100)).clone(), @@ -645,13 +664,16 @@ pub fn test_max_units() { seq_id, ) .unwrap(); - if CFG.checkpoint.utxo_asset_prefix_height > ledger.get_tendermint_height() { - new_code = code; - } + let new_code = AssetTypeCode::from_prefix_and_raw_asset_type_code( + AssetTypePrefix::UserDefined, + &code, + &CFG.checkpoint, + ledger.get_tendermint_height(), + ); + apply_transaction(&mut ledger, tx); let tx = create_issuance_txn( &mut ledger, - ¶ms, &new_code, 50, 0, @@ -663,7 +685,6 @@ pub fn test_max_units() { // Ensure that a single overlfowing transaction fails let tx = create_issuance_txn( &mut ledger, - ¶ms, &new_code, 51, 1, @@ -679,7 +700,6 @@ pub fn test_max_units() { // Ensure that cap can be reached let tx = create_issuance_txn( &mut ledger, - ¶ms, &new_code, 50, 1, @@ -693,7 +713,6 @@ pub fn test_max_units() { // Cant try to exceed asset cap by issuing confidentially let tx = create_issuance_txn( &mut ledger, - ¶ms, &new_code, 1, 2, @@ -718,13 +737,18 @@ fn gen_fee_operation( let input_bar_proof = l.get_utxo_light(txo_sid).unwrap(); let input_bar = (input_bar_proof.utxo.0).record; - let input_oar = open_blind_asset_record(&input_bar, &None, &fra_owner_kp).unwrap(); + let input_oar = open_blind_asset_record( + &input_bar.into_noah(), + &None, + &fra_owner_kp.into_noah(), + ) + .unwrap(); let output_template = AssetRecordTemplate::with_no_asset_tracing( input_oar.amount - TX_FEE_MIN, fra_code.val, AssetRecordType::NonConfidentialAmount_NonConfidentialAssetType, - fra_owner_kp.get_pk(), + fra_owner_kp.get_pk().into_noah(), ); let output_ar = AssetRecord::from_template_no_identity_tracing( &mut l.get_prng(), @@ -770,7 +794,7 @@ fn test_check_fee_with_ledger() { let mut ledger = LedgerState::tmp_ledger(); let fra_owner_kp = XfrKeyPair::generate(&mut ChaChaRng::from_entropy()); - let tx = utils::fra_gen_initial_tx(&fra_owner_kp); + let tx = fra_gen_initial_tx(&fra_owner_kp); assert!(tx.check_fee()); let effect = TxnEffect::compute_effect(tx.clone()).unwrap(); @@ -799,3 +823,86 @@ fn test_check_fee_with_ledger() { let mut block = ledger.start_block().unwrap(); assert!(ledger.apply_transaction(&mut block, effect).is_err()); } + +#[test] +fn test_update_anon_stores() { + let mut prng = ChaChaRng::from_seed([0u8; 32]); + + let mut state = LedgerState::tmp_ledger(); + + let nullifiers = vec![ + Nullifier::zero() as Nullifier, + Nullifier::one() as Nullifier, + ]; + + let pub_key = XfrKeyPair::generate(&mut prng).get_pk().into_noah(); + let oabar = OpenAnonAssetRecordBuilder::new() + .amount(123) + .asset_type(zei::noah_api::xfr::structs::AssetType([39u8; 32])) + .pub_key(&pub_key) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(); + let oabar2 = OpenAnonAssetRecordBuilder::new() + .amount(123) + .asset_type(zei::noah_api::xfr::structs::AssetType([39u8; 32])) + .pub_key(&pub_key) + .finalize(&mut prng) + .unwrap() + .build() + .unwrap(); + let output_abars = vec![ + vec![AnonAssetRecord::from_oabar(&oabar)], + vec![AnonAssetRecord::from_oabar(&oabar2)], + ]; + let new_com = get_abar_commitment(oabar); + let new_com2 = get_abar_commitment(oabar2); + let tx_block = vec![ + FinalizedTransaction { + txn: Default::default(), + tx_id: Default::default(), + txo_ids: vec![], + atxo_ids: vec![], + merkle_id: 0, + }, + FinalizedTransaction { + txn: Default::default(), + tx_id: Default::default(), + txo_ids: vec![], + atxo_ids: vec![], + merkle_id: 0, + }, + ]; + + let str0 = bs58::encode(&BN254Scalar::zero().noah_to_bytes()).into_string(); + let d0: Key = Key::from_base58(&str0).unwrap(); + assert!(state.nullifier_set.read().get(&d0).unwrap().is_none()); + + let str1 = bs58::encode(&BN254Scalar::one().noah_to_bytes()).into_string(); + let d1: Key = Key::from_base58(&str1).unwrap(); + assert!(state.nullifier_set.read().get(&d1).unwrap().is_none()); + + let res = state.update_anon_stores(nullifiers, output_abars, 0, tx_block); + assert!(res.is_ok()); + + let res2 = state.commit_anon_changes(); + assert!(res2.is_ok()); + assert_eq!(res2.unwrap(), 1); + + assert!(state.nullifier_set.read().get(&d0).unwrap().is_some()); + assert!(state.nullifier_set.read().get(&d1).unwrap().is_some()); + + assert_eq!(state.status.next_atxo.0, 2); + assert_eq!( + state.status.ax_txo_to_txn_location.get(&ATxoSID(0)), + Some((TxnSID(0), OutputPosition(0))) + ); + assert_eq!( + state.status.ax_txo_to_txn_location.get(&ATxoSID(1)), + Some((TxnSID(1), OutputPosition(0))) + ); + + assert_eq!(state.status.owned_ax_utxos.get(&new_com), Some(ATxoSID(0))); + assert_eq!(state.status.owned_ax_utxos.get(&new_com2), Some(ATxoSID(1))); +} diff --git a/src/ledger/src/store/utils.rs b/src/ledger/src/store/utils.rs index a7a1ed4ef..c036b9397 100644 --- a/src/ledger/src/store/utils.rs +++ b/src/ledger/src/store/utils.rs @@ -15,17 +15,18 @@ use { rand_core::SeedableRng, ruc::*, zei::{ - setup::PublicParams, - xfr::{ + noah_algebra::ristretto::PedersenCommitmentRistretto, + noah_api::xfr::{ asset_record::{build_blind_asset_record, AssetRecordType}, - sig::XfrKeyPair, structs::AssetRecordTemplate, }, + BlindAssetRecord, XfrKeyPair, }, }; /// Define and Issue FRA. /// Currently this should only be used for tests. +#[allow(unused)] pub fn fra_gen_initial_tx(fra_owner_kp: &XfrKeyPair) -> Transaction { /* * Define FRA @@ -35,7 +36,7 @@ pub fn fra_gen_initial_tx(fra_owner_kp: &XfrKeyPair) -> Transaction { val: ASSET_TYPE_FRA, }; - let (mut tx, _) = pnk!(helpers::create_definition_transaction( + let mut tx = pnk!(helpers::create_definition_transaction( &fra_code, fra_owner_kp, AssetRules { @@ -55,23 +56,23 @@ pub fn fra_gen_initial_tx(fra_owner_kp: &XfrKeyPair) -> Transaction { FRA_PRE_ISSUE_AMOUNT / 2, fra_code.val, AssetRecordType::NonConfidentialAmount_NonConfidentialAssetType, - fra_owner_kp.get_pk(), + fra_owner_kp.get_pk().into_noah(), ); - let params = PublicParams::default(); + let pc_gens = PedersenCommitmentRistretto::default(); let outputs = (0..2) .map(|_| { let (ba, _, _) = build_blind_asset_record( &mut ChaChaRng::from_entropy(), - ¶ms.pc_gens, + &pc_gens, &template, vec![], ); ( TxOutput { id: None, - record: ba, + record: BlindAssetRecord::from_noah(&ba), lien: None, }, None, diff --git a/tools/devnet/snapshot.sh b/tools/devnet/snapshot.sh index c03ee0007..32f5b48c4 100755 --- a/tools/devnet/snapshot.sh +++ b/tools/devnet/snapshot.sh @@ -41,6 +41,7 @@ $BIN/fn setup -S http://0.0.0.0 > /dev/null $BIN/fn setup -O $WALLET/mnenomic.key > /dev/null echo -e "host: http://0.0.0.0" echo -e "key : $WALLET/mnenomic.key" +sleep 3 #echo -e "$BIN/stt" $BIN/stt init -i $BLOCK_INTERVAL -s #./$stopnodes