Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
697 changes: 446 additions & 251 deletions native/Cargo.lock

Large diffs are not rendered by default.

20 changes: 6 additions & 14 deletions native/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ crate-type = ["cdylib"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
zingolib = { git="https://github.com/zingolabs/zingolib", default-features = true, branch = "stable", features = [ "testutils" ]}
pepper-sync = { git = "https://github.com/zingolabs/zingolib", branch = "stable" }
zingolib = { git="https://github.com/zingolabs/zingolib", default-features = true, branch = "dev" }
pepper-sync = { git = "https://github.com/zingolabs/zingolib", branch = "dev" }

zcash_address = { version = "0.10" }
zcash_client_backend = { version = "0.21", features = [
Expand All @@ -29,18 +29,18 @@ zcash_keys = { version = "0.12", features = [
"orchard",
] }
zcash_note_encryption = "0.4"
zcash_primitives = { version = "0.26" }
zcash_proofs = { version = "0.26" }
zcash_protocol = { version = "0.7" }
zcash_transparent = { version = "0.6" }

zebra-chain = { version = "3.0" }
zebra-chain = "5.0.0"

rusqlite = { version = "0.37.0", features = ["bundled"] }

zingo_common_components = { git = "https://github.com/zingolabs/zingo-common.git", branch = "dev" }
zingo_common_components = { version = "0.2", features = ["for_test"] }

bip0039 = { version = "0.12", features = [ "rand" ] }
bip0039 = { version = "0.13", features = [ "rand" ] }
zip32 = "0.2.0"
json = "0.12"
serde_json = "1"
hex = "0.4"
Expand All @@ -53,14 +53,6 @@ log = "0.4"
once_cell = "1"
lazy_static = "1.4.0"

[patch.crates-io]
# zcash_client_backend = { git = "https://github.com/zcash/librustzcash", rev = "3ba772c9b8"}
# zcash_address = { git = "https://github.com/zcash/librustzcash", rev = "3ba772c9b8"}
# zcash_keys = { git = "https://github.com/zcash/librustzcash", rev = "3ba772c9b8"}
# zcash_primitives = { git = "https://github.com/zcash/librustzcash", rev = "3ba772c9b8"}
# zcash_protocol = { git = "https://github.com/zcash/librustzcash", rev = "3ba772c9b8"}
# zcash_transparent = { git = "https://github.com/zcash/librustzcash", rev = "3ba772c9b8"}

[dependencies.neon]
version = "1"
default-features = true
Expand Down
139 changes: 99 additions & 40 deletions native/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ use rustls::crypto::{CryptoProvider, ring::default_provider};
use zcash_address::unified::{Container, Encoding, Ufvk};
use zcash_keys::address::Address;
use zcash_keys::keys::UnifiedFullViewingKey;
use zcash_primitives::consensus::BlockHeight;
use zcash_primitives::zip32::AccountId;
use zcash_protocol::consensus::BlockHeight;
use zip32::AccountId;
use zcash_protocol::consensus::NetworkType;

use pepper_sync::keys::transparent;
Expand All @@ -41,7 +41,7 @@ use zingolib::data::receivers::Receivers;
use zcash_address::ZcashAddress;
use zcash_protocol::{value::Zatoshis};
use tokio::runtime::Runtime;
use zcash_primitives::memo::MemoBytes;
use zcash_protocol::memo::MemoBytes;
use zingolib::data::receivers::transaction_request_from_receivers;
use zingolib::data::proposal::total_fee;

Expand All @@ -58,6 +58,7 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> {
cx.export_function("init_from_ufvk", init_from_ufvk)?;
cx.export_function("init_from_b64", init_from_b64)?;
cx.export_function("save_wallet_file", save_wallet_file)?;
cx.export_function("check_save_error", check_save_error)?;
cx.export_function("get_developer_donation_address", get_developer_donation_address)?;
cx.export_function("get_zennies_for_zingo_donation_address", get_zennies_for_zingo_donation_address)?;
cx.export_function("set_crypto_default_provider_to_ring", set_crypto_default_provider_to_ring)?;
Expand All @@ -84,7 +85,6 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> {
cx.export_function("get_total_value_to_address", get_total_value_to_address)?;
cx.export_function("get_total_spends_to_address", get_total_spends_to_address)?;
cx.export_function("zec_price", zec_price)?;
cx.export_function("resend_transaction", resend_transaction)?;
cx.export_function("remove_transaction", remove_transaction)?;
cx.export_function("get_spendable_balance_with_address", get_spendable_balance_with_address)?;
cx.export_function("get_spendable_balance_total", get_spendable_balance_total)?;
Expand Down Expand Up @@ -506,13 +506,15 @@ fn init_from_b64(mut cx: FunctionContext) -> JsResult<JsString> {
Ok(c) => c,
Err(e) => return Ok(format!("Error: {e}")),
};
let lightclient = match LightClient::create_from_wallet_path(config) {
let mut lightclient = match LightClient::create_from_wallet_path(config) {
Ok(l) => l,
Err(e) => return Ok(format!("Error: {e}")),
};
let has_seed = RT.block_on(async {
lightclient.wallet.read().await.mnemonic().is_some()
});
// save the wallet file here
RT.block_on(async { lightclient.save_task().await });
let _ = store_client(lightclient);

Ok(if has_seed { get_seed_string() } else { get_ufvk_string() })
Expand All @@ -524,14 +526,102 @@ fn init_from_b64(mut cx: FunctionContext) -> JsResult<JsString> {
}
}

fn write_to_path(wallet_path: &std::path::Path, bytes: &[u8]) -> std::io::Result<()> {
let temp_wallet_path: std::path::PathBuf = wallet_path.with_extension(
wallet_path
.extension()
.map(|e| format!("{}.tmp", e.to_string_lossy()))
.unwrap_or_else(|| "tmp".to_string()),
);
let file = std::fs::OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.open(&temp_wallet_path)
.map_err(|e| std::io::Error::new(e.kind(), format!("open temp {:?}: {}", temp_wallet_path, e)))?;

let mut writer = std::io::BufWriter::new(file);
std::io::Write::write_all(&mut writer, bytes)
.map_err(|e| std::io::Error::new(e.kind(), format!("write temp {:?}: {}", temp_wallet_path, e)))?;

let file = writer.into_inner()
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, format!("into_inner: {}", e)))?;

file.sync_all()
.map_err(|e| std::io::Error::new(e.kind(), format!("sync temp {:?}: {}", temp_wallet_path, e)))?;

std::fs::rename(&temp_wallet_path, wallet_path)
.map_err(|e| std::io::Error::new(e.kind(), format!("rename {:?} -> {:?}: {}", temp_wallet_path, wallet_path, e)))?;

#[cfg(unix)]
{
if let Some(parent) = wallet_path.parent() {
let wallet_dir = std::fs::File::open(parent)
.map_err(|e| std::io::Error::new(e.kind(), format!("open dir {:?}: {}", parent, e)))?;
wallet_dir.sync_all()
.map_err(|e| std::io::Error::new(e.kind(), format!("sync dir {:?}: {}", parent, e)))?;
}
}

Ok(())
}

fn save_wallet_file(mut cx: FunctionContext) -> JsResult<JsPromise> {
let promise = cx
.task(move || -> Result<String, ZingolibError> {
with_panic_guard(|| {
let mut guard = LIGHTCLIENT.write().map_err(|_| ZingolibError::LightclientLockPoisoned)?;
if let Some(lightclient) = &mut *guard {
RT.block_on(async move { lightclient.save_task().await });
Ok("Launching save task...".to_string())
Ok(RT.block_on(async move {
let wallet_path = lightclient.config.get_wallet_path();
let mut wallet = lightclient.wallet.write().await;
match wallet.save() {
Ok(Some(wallet_bytes)) => {
match write_to_path(&wallet_path, &wallet_bytes) {
Ok(_) => {
let size = wallet_bytes.len();
format!("Wallet saved successfully. Size: {} bytes.", size)
}
Err(e) => {
format!("Error: writing wallet file: {e}")
}
}
}
Ok(None) => {
"Wallet is empty. Nothing to save.".to_string()
}
Err(e) => {
format!("Error: {e}")
}
}
}))
} else {
Err(ZingolibError::LightclientNotInitialized)
}
})
})
.promise(move |mut cx, result| match result {
Ok(msg) => Ok(cx.string(msg)),
Err(err) => cx.throw_error(err.to_string()),
});

Ok(promise)
}

fn check_save_error(mut cx: FunctionContext) -> JsResult<JsPromise> {
let promise = cx
.task(move || -> Result<String, ZingolibError> {
with_panic_guard(|| {
let mut guard = LIGHTCLIENT.write().map_err(|_| ZingolibError::LightclientLockPoisoned)?;
if let Some(lightclient) = &mut *guard {
Ok(RT.block_on(async move {
match lightclient.check_save_error().await {
Ok(()) => String::new(),
Err(e) => {
format!("Error: save failed. {e}\nRestarting save task...")
}
}
}))
} else {
Err(ZingolibError::LightclientNotInitialized)
}
Expand Down Expand Up @@ -711,7 +801,7 @@ fn get_latest_block_wallet(mut cx: FunctionContext) -> JsResult<JsPromise> {
if let Some(lightclient) = &mut *guard {
Ok(RT.block_on(async move {
let wallet = lightclient.wallet.write().await;
object! { "height" => json::JsonValue::from(wallet.sync_state.wallet_height().map(u32::from).unwrap_or(0))}.pretty(2)
object! { "height" => json::JsonValue::from(wallet.sync_state.last_known_chain_height().map_or(0, u32::from))}.pretty(2)
}))
} else {
Err(ZingolibError::LightclientNotInitialized)
Expand Down Expand Up @@ -1373,37 +1463,6 @@ fn zec_price(mut cx: FunctionContext) -> JsResult<JsPromise> {
Ok(promise)
}

fn resend_transaction(mut cx: FunctionContext) -> JsResult<JsPromise> {
let txid = cx.argument::<JsString>(0)?.value(&mut cx);

let promise = cx
.task(move || -> Result<String, ZingolibError> {
with_panic_guard(|| {
let mut guard = LIGHTCLIENT.write().map_err(|_| ZingolibError::LightclientLockPoisoned)?;
if let Some(lightclient) = &mut *guard {
let txid = match txid_from_hex_encoded_str(&txid) {
Ok(txid) => txid,
Err(e) => return Ok(format!("Error: {e}")),
};
RT.block_on(async move {
match lightclient.resend(txid).await {
Ok(_) => Ok("Successfully resent transaction.".to_string()),
Err(e) => Ok(format!("Error: {e}")),
}
})
} else {
Err(ZingolibError::LightclientNotInitialized)
}
})
})
.promise(move |mut cx, result| match result {
Ok(msg) => Ok(cx.string(msg)),
Err(err) => cx.throw_error(err.to_string()),
});

Ok(promise)
}

fn remove_transaction(mut cx: FunctionContext) -> JsResult<JsPromise> {
let txid = cx.argument::<JsString>(0)?.value(&mut cx);

Expand All @@ -1421,7 +1480,7 @@ fn remove_transaction(mut cx: FunctionContext) -> JsResult<JsPromise> {
.wallet
.write()
.await
.remove_unconfirmed_transaction(txid)
.remove_failed_transaction(txid)
{
Ok(_) => "Successfully removed transaction.".to_string(),
Err(e) => format!("Error: {e}"),
Expand Down
1 change: 1 addition & 0 deletions src/components/appstate/enums/ValueTransferStatusEnum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export enum ValueTransferStatusEnum {
transmitted = 'transmitted',
mempool = 'mempool',
confirmed = 'confirmed',
failed = 'failed',
}
16 changes: 9 additions & 7 deletions src/components/balanceBlock/BalanceBlockHighlight.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import cstyles from "../common/Common.module.css";
import Utils from "../../utils/utils";
import BalanceBlockProps from "./components/BalanceBlockProps";
import { ValueTransferStatusEnum } from "../appstate";

const BalanceBlockHighlight: React.FC<BalanceBlockProps> = ({
zecValue,
zecValueConfirmed,
usdValue,
usdValueConfirmed,
topLabel,
currencyName,
status,
topLabel,
tooltip,
}) => {
const { bigPart, smallPart }: {bigPart: string, smallPart: string} =
Expand All @@ -31,27 +33,27 @@ const BalanceBlockHighlight: React.FC<BalanceBlockProps> = ({
)}

<div className={[cstyles.highlight, cstyles.xlarge].join(" ")}>
<span>
<span style={{ color: status === ValueTransferStatusEnum.failed ? Utils.getCssVariable('--color-error') : undefined }}>
{currencyName} {bigPart}
</span>
<span className={[cstyles.small, cstyles.zecsmallpart].join(" ")}>{smallPart}</span>
<span style={{ color: status === ValueTransferStatusEnum.failed ? Utils.getCssVariable('--color-error') : undefined }} className={[cstyles.small, cstyles.zecsmallpart].join(" ")}>{smallPart}</span>
</div>
{currencyName === 'ZEC' && (
<div className={[cstyles.sublight, cstyles.small].join(" ")}>{usdValue}</div>
<div style={{ color: status === ValueTransferStatusEnum.failed ? Utils.getCssVariable('--color-error') : undefined }} className={[cstyles.sublight, cstyles.small].join(" ")}>{usdValue}</div>
)}

{zecValueConfirmed !== undefined && zecValue !== zecValueConfirmed && (
<>
<div className={[cstyles.small].join(" ")}>{topLabel + ' Confirmed'}</div>
<div className={cstyles.horizontalflex}>
<div className={[cstyles.highlight, cstyles.small].join(" ")}>
<span>
<span style={{ color: status === ValueTransferStatusEnum.failed ? Utils.getCssVariable('--color-error') : undefined }}>
{currencyName} {bigPartConfirmed}
</span>
<span className={[cstyles.small, cstyles.zecsmallpart].join(" ")}>{smallPartConfirmed}</span>
<span style={{ color: status === ValueTransferStatusEnum.failed ? Utils.getCssVariable('--color-error') : undefined }} className={[cstyles.small, cstyles.zecsmallpart].join(" ")}>{smallPartConfirmed}</span>
</div>
{currencyName === 'ZEC' && (
<div style={{ marginLeft: 5 }} className={[cstyles.sublight, cstyles.small].join(" ")}>{usdValueConfirmed}</div>
<div style={{ color: status === ValueTransferStatusEnum.failed ? Utils.getCssVariable('--color-error') : undefined, marginLeft: 5 }} className={[cstyles.sublight, cstyles.small].join(" ")}>{usdValueConfirmed}</div>
)}
</div>
</>
Expand Down
3 changes: 3 additions & 0 deletions src/components/balanceBlock/components/BalanceBlockProps.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { ValueTransferStatusEnum } from "../../appstate";

type BalanceBlockProps = {
zecValue: number;
zecValueConfirmed?: number;
usdValue: string;
usdValueConfirmed?: string;
currencyName: string;
status?: ValueTransferStatusEnum | "";
topLabel?: string;
tooltip?: string;
};
Expand Down
4 changes: 2 additions & 2 deletions src/components/dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const Dashboard: React.FC<DashboardProps & RouteComponentProps> = ({ navigateToH
usdValue={Utils.getZecToUsdString(info.zecPrice, totalBalance.totalOrchardBalance + totalBalance.totalSaplingBalance + totalBalance.totalTransparentBalance)}
currencyName={info.currencyName}
zecValueConfirmed={totalBalance.confirmedOrchardBalance + totalBalance.confirmedSaplingBalance + totalBalance.confirmedTransparentBalance}
usdValueConfirmed={Utils.getZecToUsdString(info.zecPrice, totalBalance.confirmedOrchardBalance + totalBalance.confirmedSaplingBalance + totalBalance.confirmedTransparentBalance)}
usdValueConfirmed={Utils.getZecToUsdString(info.zecPrice, totalBalance.confirmedOrchardBalance + totalBalance.confirmedSaplingBalance + totalBalance.confirmedTransparentBalance)}
/>
{orchardPool && (
<BalanceBlock
Expand Down Expand Up @@ -294,7 +294,7 @@ const Dashboard: React.FC<DashboardProps & RouteComponentProps> = ({ navigateToH
{valueTransfers
.filter((_, index: number) => index < 5)
.map((vt: ValueTransferClass, index: number) => (
<DetailLine key={index} label={Utils.VTTypeWithConfirmations(vt.type, vt.confirmations)} value={'ZEC ' + Utils.maxPrecisionTrimmed(vt.amount)} />
<DetailLine key={index} label={Utils.VTTypeWithConfirmations(vt.type, vt.status, vt.confirmations)} value={'ZEC ' + Utils.maxPrecisionTrimmed(vt.amount)} />
))}
</div>
<div style={{ width: '100%', textAlign: 'right', color: Utils.getCssVariable('--color-primary'), marginTop: 20, cursor: 'pointer' }} onClick={() => navigateToHistory()}>
Expand Down
Loading