Remove old fullbackup system

This commit is contained in:
Hanh 2022-11-23 11:18:54 +08:00
parent 56f4f5e73e
commit d0550c8908
9 changed files with 8 additions and 363 deletions

View File

@ -146,12 +146,6 @@ struct CResult_____c_char make_payment_uri(char *address, uint64_t amount, char
struct CResult_____c_char parse_payment_uri(char *uri);
struct CResult_____c_char generate_random_enc_key(void);
struct CResult_____c_char get_full_backup(char *key);
void restore_full_backup(char *key, char *backup);
struct CResult_____c_char generate_key(void);
struct CResult_u8 zip_backup(char *key, char *dst_dir);
@ -184,5 +178,3 @@ bool has_metal(void);
bool has_gpu(void);
void use_gpu(bool v);
void import_sync_file(uint8_t coin, char *path);

View File

@ -1,6 +1,5 @@
pub mod account;
pub mod contact;
pub mod fullbackup;
pub mod historical_prices;
pub mod message;
pub mod recipient;

View File

@ -9,15 +9,13 @@ use crate::orchard::OrchardKeyBytes;
use crate::taddr::{derive_taddr, derive_tkeys};
use crate::unified::UnifiedAddressType;
use crate::zip32::derive_zip32;
use crate::{AccountInfo, KeyPack};
use crate::KeyPack;
use anyhow::anyhow;
use bip39::{Language, Mnemonic};
use orchard::keys::{FullViewingKey, Scope};
use rand::rngs::OsRng;
use rand::RngCore;
use serde::Serialize;
use std::fs::File;
use std::io::BufReader;
use zcash_address::unified::{Address as UA, Receiver};
use zcash_address::{ToAddress, ZcashAddress};
use zcash_client_backend::encoding::{decode_extended_full_viewing_key, encode_payment_address};
@ -357,20 +355,6 @@ pub fn derive_keys(
derive_zip32(c.chain.network(), &seed, account, external, address)
}
/// Import synchronization data obtained from external source
/// # Arguments
/// * `coin`: 0 for zcash, 1 for ycash
/// * `file`: file that contains the synchronization data
pub async fn import_sync_data(coin: u8, file: &str) -> anyhow::Result<()> {
let c = CoinConfig::get(coin);
let mut db = c.db()?;
let file = File::open(file)?;
let file = BufReader::new(file);
let account_info: AccountInfo = serde_json::from_reader(file)?;
db.import_from_syncdata(&account_info)?;
Ok(())
}
/// Get the Unified address
/// # Arguments
/// * `coin`: 0 for zcash, 1 for ycash

View File

@ -620,40 +620,6 @@ pub unsafe extern "C" fn parse_payment_uri(uri: *mut c_char) -> CResult<*mut c_c
to_cresult_str(payment_json())
}
#[no_mangle]
pub unsafe extern "C" fn generate_random_enc_key() -> CResult<*mut c_char> {
to_cresult_str(crate::api::fullbackup::generate_random_enc_key())
}
#[no_mangle]
pub unsafe extern "C" fn get_full_backup(key: *mut c_char) -> CResult<*mut c_char> {
from_c_str!(key);
let res = || {
let mut accounts = vec![];
for coin in 0..MAX_COINS {
accounts.extend(crate::api::fullbackup::get_full_backup(coin)?);
}
let backup = crate::api::fullbackup::encrypt_backup(&accounts, &key)?;
Ok(backup)
};
to_cresult_str(res())
}
#[no_mangle]
pub unsafe extern "C" fn restore_full_backup(key: *mut c_char, backup: *mut c_char) {
from_c_str!(key);
from_c_str!(backup);
let res = || {
let accounts = crate::api::fullbackup::decrypt_backup(&key, &backup)?;
for coin in 0..MAX_COINS {
crate::api::fullbackup::restore_full_backup(coin, &accounts)?;
}
Ok(())
};
log_error(res())
}
#[no_mangle]
pub unsafe extern "C" fn generate_key() -> CResult<*mut c_char> {
let res = || {
@ -796,11 +762,3 @@ pub unsafe extern "C" fn has_gpu() -> bool {
pub unsafe extern "C" fn use_gpu(v: bool) {
crate::gpu::use_gpu(v)
}
#[tokio::main]
#[no_mangle]
pub async unsafe extern "C" fn import_sync_file(coin: u8, path: *mut c_char) {
from_c_str!(path);
let res = crate::api::account::import_sync_data(coin, &path).await;
log_error(res)
}

View File

@ -1,89 +0,0 @@
//! Save/Load account data as JSON
use crate::coinconfig::CoinConfig;
use crate::db::AccountBackup;
use bech32::{FromBase32, ToBase32, Variant};
use chacha20poly1305::aead::{Aead, NewAead};
use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce};
use rand::rngs::OsRng;
use rand::RngCore;
const NONCE: &[u8; 12] = b"unique nonce";
/// Return backup data of every account for a given coin
/// # Argument
/// * `coin`: 0 for zcash, 1 for ycash
pub fn get_full_backup(coin: u8) -> anyhow::Result<Vec<AccountBackup>> {
let c = CoinConfig::get(coin);
let db = c.db()?;
db.get_full_backup(coin)
}
/// Import backup data for a given coin
/// # Argument
/// * `coin`: 0 for zcash, 1 for ycash
/// * `accounts`: list of backups
pub fn restore_full_backup(coin: u8, accounts: &[AccountBackup]) -> anyhow::Result<()> {
let c = CoinConfig::get(coin);
let db = c.db()?;
db.restore_full_backup(accounts)
}
/// Encrypt a list of account backups
/// # Argument
/// * `accounts`: list of backups
/// * `key`: encryption key
pub fn encrypt_backup(accounts: &[AccountBackup], key: &str) -> anyhow::Result<String> {
let accounts_bin = bincode::serialize(&accounts)?;
let backup = if !key.is_empty() {
let (hrp, key, _) = bech32::decode(key)?;
if hrp != "zwk" {
anyhow::bail!("Invalid backup key")
}
let key = Vec::<u8>::from_base32(&key)?;
let key = Key::from_slice(&key);
let cipher = ChaCha20Poly1305::new(key);
// nonce is constant because we always use a different key!
let cipher_text = cipher
.encrypt(Nonce::from_slice(NONCE), &*accounts_bin)
.map_err(|_e| anyhow::anyhow!("Failed to encrypt backup"))?;
base64::encode(cipher_text)
} else {
base64::encode(accounts_bin)
};
Ok(backup)
}
/// Decrypt a list of account backups
/// # Argument
/// * `key`: encryption key
/// * `backup`: encrypted backup
pub fn decrypt_backup(key: &str, backup: &str) -> anyhow::Result<Vec<AccountBackup>> {
let backup = if !key.is_empty() {
let (hrp, key, _) = bech32::decode(key)?;
if hrp != "zwk" {
anyhow::bail!("Not a valid decryption key");
}
let key = Vec::<u8>::from_base32(&key)?;
let key = Key::from_slice(&key);
let cipher = ChaCha20Poly1305::new(key);
let backup = base64::decode(backup)?;
cipher
.decrypt(Nonce::from_slice(NONCE), &*backup)
.map_err(|_e| anyhow::anyhow!("Failed to decrypt backup"))?
} else {
base64::decode(backup)?
};
let accounts: Vec<AccountBackup> = bincode::deserialize(&backup)?;
Ok(accounts)
}
/// Generate a random encryption key
pub fn generate_random_enc_key() -> anyhow::Result<String> {
let mut key = [0u8; 32];
OsRng.fill_bytes(&mut key);
let key = bech32::encode("zwk", key.to_base32(), Variant::Bech32)?;
Ok(key)
}

202
src/db.rs
View File

@ -12,14 +12,13 @@ use crate::{sync, BlockId, CompactTxStreamerClient, Hash};
use orchard::keys::FullViewingKey;
use rusqlite::Error::QueryReturnedNoRows;
use rusqlite::{params, Connection, OpenFlags, OptionalExtension, Transaction};
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use serde::Serialize;
use std::collections::HashMap;
use std::convert::TryInto;
use tonic::transport::Channel;
use tonic::Request;
use zcash_client_backend::encoding::decode_extended_full_viewing_key;
use zcash_params::coin::{get_coin_chain, get_coin_id, CoinType};
use zcash_params::coin::{get_coin_chain, CoinType};
use zcash_primitives::consensus::{Network, NetworkUpgrade, Parameters};
use zcash_primitives::merkle_tree::IncrementalWitness;
use zcash_primitives::sapling::{Diversifier, Node, Note, SaplingIvk};
@ -80,22 +79,6 @@ pub struct AccountViewKey {
pub viewonly: bool,
}
#[derive(Serialize, Deserialize)]
pub struct AccountBackup {
pub version: u8,
pub coin: u8,
pub name: String,
pub seed: Option<String>,
pub index: u32,
pub z_sk: Option<String>,
pub ivk: String,
pub z_addr: String,
pub t_sk: Option<String>,
pub t_addr: Option<String>,
pub o_sk: Option<String>,
pub o_fvk: Option<String>,
}
pub fn wrap_query_no_rows(name: &'static str) -> impl Fn(rusqlite::Error) -> anyhow::Error {
move |err: rusqlite::Error| match err {
QueryReturnedNoRows => anyhow::anyhow!("Query {} returned no rows", name),
@ -1087,85 +1070,6 @@ impl DbAdapter {
Ok(())
}
pub fn get_full_backup(&self, coin: u8) -> anyhow::Result<Vec<AccountBackup>> {
let mut statement = self.connection.prepare(
"SELECT name, seed, aindex, a.sk AS z_sk, ivk, a.address AS z_addr, t.sk as t_sk, t.address AS t_addr, \
o.sk as o_sk, o.fvk as o_fvk \
FROM accounts a LEFT JOIN taddrs t ON a.id_account = t.account LEFT JOIN orchard_addrs o ON a.id_account = o.account")?;
let rows = statement.query_map([], |r| {
let name: String = r.get(0)?;
let seed: Option<String> = r.get(1)?;
let index: u32 = r.get(2)?;
let z_sk: Option<String> = r.get(3)?;
let ivk: String = r.get(4)?;
let z_addr: String = r.get(5)?;
let t_sk: Option<String> = r.get(6)?;
let t_addr: Option<String> = r.get(7)?;
let o_sk: Option<Vec<u8>> = r.get(8)?;
let o_fvk: Option<Vec<u8>> = r.get(9)?;
let o_sk: Option<String> = o_sk.map(|v| hex::encode(v));
let o_fvk: Option<String> = o_fvk.map(|v| hex::encode(v));
Ok(AccountBackup {
version: 2,
coin,
name,
seed,
index,
z_sk,
ivk,
z_addr,
t_sk,
t_addr,
o_sk,
o_fvk,
})
})?;
let mut accounts: Vec<AccountBackup> = vec![];
for r in rows {
accounts.push(r?);
}
Ok(accounts)
}
pub fn restore_full_backup(&self, accounts: &[AccountBackup]) -> anyhow::Result<()> {
let coin = get_coin_id(self.coin_type);
for a in accounts {
if a.version != 2 {
anyhow::bail!("Unsupported backup version")
}
log::info!("{} {} {}", a.name, a.coin, coin);
if a.coin == coin {
let do_insert = || {
self.connection.execute("INSERT INTO accounts(name, seed, aindex, sk, ivk, address) VALUES (?1,?2,?3,?4,?5,?6)",
params![a.name, a.seed, a.index, a.z_sk, a.ivk, a.z_addr])?;
let id_account = self.connection.last_insert_rowid() as u32;
if let Some(t_addr) = &a.t_addr {
self.connection.execute(
"INSERT INTO taddrs(account, sk, address) VALUES (?1,?2,?3)",
params![id_account, a.t_sk, t_addr],
)?;
}
if let Some(o_fvk) = &a.o_fvk {
self.connection.execute(
"INSERT INTO orchard_addrs(account, sk, fvk) VALUES (?1,?2,?3)",
params![
id_account,
a.o_sk.as_ref().map(|v| hex::decode(&v).unwrap()),
hex::decode(&o_fvk).unwrap()
],
)?;
}
Ok::<_, anyhow::Error>(())
};
if let Err(e) = do_insert() {
log::info!("{:?}", e);
}
}
}
Ok(())
}
pub fn store_message(&self, account: u32, message: &ZMessage) -> anyhow::Result<()> {
self.connection.execute("INSERT INTO messages(account, id_tx, sender, recipient, subject, body, timestamp, height, incoming, read) VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10)",
params![account, message.id_tx, message.sender, message.recipient, message.subject, message.body, message.timestamp, message.height, message.incoming, false])?;
@ -1279,65 +1183,6 @@ impl DbAdapter {
Ok(reqs)
}
pub fn import_from_syncdata(&mut self, account_info: &AccountInfo) -> anyhow::Result<Vec<u32>> {
// get id_account from fvk
// truncate received_notes, sapling_witnesses for account
// add new received_notes, sapling_witnesses
// add block
let id_account = self
.connection
.query_row(
"SELECT id_account FROM accounts WHERE ivk = ?1",
params![&account_info.fvk],
|row| {
let id_account: u32 = row.get(0)?;
Ok(id_account)
},
)
.map_err(wrap_query_no_rows("import_from_syncdata/id_account"))?;
self.connection.execute(
"DELETE FROM received_notes WHERE account = ?1",
params![id_account],
)?;
self.connection.execute(
"DELETE FROM transactions WHERE account = ?1",
params![id_account],
)?;
self.connection.execute(
"DELETE FROM messages WHERE account = ?1",
params![id_account],
)?;
let mut ids = vec![];
for tx in account_info.txs.iter() {
let mut tx_hash = hex::decode(&tx.hash)?;
tx_hash.reverse();
self.connection.execute("INSERT INTO transactions(account,txid,height,timestamp,value,address,memo,tx_index) VALUES (?1,?2,?3,?4,?5,'','',?6)",
params![id_account, &tx_hash, tx.height, tx.timestamp, tx.value, tx.index])?;
let id_tx = self.connection.last_insert_rowid() as u32;
ids.push(id_tx);
for n in tx.notes.iter() {
let spent = if n.spent == 0 { None } else { Some(n.spent) };
self.connection.execute("INSERT INTO received_notes(account,position,tx,height,output_index,diversifier,value,rcm,nf,spent,excluded) \
VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11)",
params![id_account, n.position as i64, id_tx, n.height, n.output_index, hex::decode(&n.diversifier)?, n.value as i64,
hex::decode(&n.rcm)?, &n.nf, spent, false])?;
let id_note = self.connection.last_insert_rowid() as u32;
self.connection.execute(
"INSERT INTO sapling_witnesses(note,height,witness) VALUES (?1,?2,?3) ON CONFLICT DO NOTHING",
params![
id_note,
account_info.height,
hex::decode(&n.witness).unwrap()
],
)?;
}
}
self.connection.execute("INSERT INTO blocks(height,hash,timestamp,sapling_tree) VALUES (?1,?2,?3,?4) ON CONFLICT(height) DO NOTHING",
params![account_info.height, hex::decode(&account_info.hash)?, account_info.timestamp, hex::decode(&account_info.sapling_tree)?])?;
self.trim_to_height(account_info.height)?;
Ok(ids)
}
fn network(&self) -> &'static Network {
let chain = get_coin_chain(self.coin_type);
chain.network()
@ -1385,49 +1230,6 @@ pub struct AccountRec {
address: String,
}
#[serde_as]
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct PlainNote {
pub height: u32,
pub value: u64,
#[serde_as(as = "serde_with::hex::Hex")]
pub nf: Vec<u8>,
pub position: u64,
pub tx_index: u32,
pub output_index: u32,
pub spent: u32,
pub witness: String,
pub rcm: String,
pub diversifier: String,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct TxInfo {
pub height: u32,
pub timestamp: u32,
pub index: i64,
pub hash: String,
pub value: i64,
pub notes: Vec<PlainNote>,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct OutputInfo {
pub tx_index: u32,
pub index: u32,
}
#[derive(Serialize, Deserialize)]
pub struct AccountInfo {
pub fvk: String,
pub height: u32,
pub balance: u64,
pub txs: Vec<TxInfo>,
pub hash: String,
pub timestamp: u32,
pub sapling_tree: String,
}
pub struct AccountData {
pub name: String,
pub seed: Option<String>,

View File

@ -1,8 +1,8 @@
use age::secrecy::ExposeSecret;
use serde::Serialize;
use anyhow::anyhow;
use rusqlite::backup::Backup;
use rusqlite::Connection;
use serde::Serialize;
use std::fs::File;
use std::io::{Cursor, Read, Write};
use std::path::{Path, PathBuf};
@ -43,8 +43,7 @@ impl FullEncryptedBackup {
pub fn close(&self, pk: &str) -> anyhow::Result<()> {
let data = self.make_zip()?;
let pubkey =
age::x25519::Recipient::from_str(pk).map_err(|e| anyhow!(e.to_string()))?;
let pubkey = age::x25519::Recipient::from_str(pk).map_err(|e| anyhow!(e.to_string()))?;
let mut encrypted_file = File::create(self.tmp_dir.join(YWALLET_BAK))?;
let encryptor = age::Encryptor::with_recipients(vec![Box::new(pubkey)]).unwrap();

View File

@ -119,7 +119,7 @@ pub use crate::chain::{connect_lightwalletd, get_best_server, ChainError};
pub use crate::coinconfig::{
init_coin, set_active, set_active_account, set_coin_lwd_url, CoinConfig, COIN_CONFIG,
};
pub use crate::db::{AccountData, AccountInfo, AccountRec, DbAdapter, DbAdapterBuilder, TxRec};
pub use crate::db::{AccountData, AccountRec, DbAdapter, DbAdapterBuilder, TxRec};
pub use crate::fountain::{FountainCodes, RaptorQDrops};
// pub use crate::key::KeyHelpers;
pub use crate::lw_rpc::compact_tx_streamer_client::CompactTxStreamerClient;

View File

@ -175,7 +175,7 @@ pub async fn run_mempool_loop<F: Fn(i64) + Send + Sync + 'static>(
}) = active
{
if coin != active_coin || id_account != active_account {
tx_shutdown.send(())?; // Close current connection
let _ = tx_shutdown.send(()); // Close current connection
let _ = tx_mesg.send(MemPoolMsg::Subscribe(coin, id_account)).await;
} else {
// same active account, just put it back
@ -194,7 +194,7 @@ pub async fn run_mempool_loop<F: Fn(i64) + Send + Sync + 'static>(
account: id_account,
tx_shutdown,
});
MemPoolHandler::run(mempool_handler, rx_shutdown)?;
let _ = MemPoolHandler::run(mempool_handler, rx_shutdown);
}
}
MemPoolMsg::Balance(coin, id_account, balance) => {