This commit is contained in:
Hanh 2022-12-31 04:34:26 +08:00
parent d43c4884b8
commit 979e60bda2
9 changed files with 4906 additions and 4383 deletions

View File

@ -3,6 +3,9 @@
// Account creation
use crate::coinconfig::CoinConfig;
use crate::db::data_generated::fb::{
AddressBalance, AddressBalanceArgs, AddressBalanceVec, AddressBalanceVecArgs,
};
use crate::db::AccountData;
use crate::key2::decode_key;
use crate::orchard::OrchardKeyBytes;
@ -21,7 +24,6 @@ use zcash_address::{ToAddress, ZcashAddress};
use zcash_client_backend::encoding::{decode_extended_full_viewing_key, encode_payment_address};
use zcash_client_backend::keys::UnifiedFullViewingKey;
use zcash_primitives::consensus::Parameters;
use crate::db::data_generated::fb::{AddressBalance, AddressBalanceArgs, AddressBalanceVec, AddressBalanceVecArgs};
/// Create a new account
/// # Arguments
@ -264,22 +266,29 @@ pub async fn get_taddr_balance(coin: u8, id_account: u32) -> anyhow::Result<u64>
pub async fn scan_transparent_accounts(gap_limit: usize) -> anyhow::Result<Vec<u8>> {
let c = CoinConfig::get_active();
let mut client = c.connect_lwd().await?;
let addresses = crate::taddr::scan_transparent_accounts(c.chain.network(), &mut client, gap_limit).await?;
let addresses =
crate::taddr::scan_transparent_accounts(c.chain.network(), &mut client, gap_limit).await?;
let mut builder = flatbuffers::FlatBufferBuilder::new();
let mut addrs = vec![];
for a in addresses {
let address = builder.create_string(&a.address);
let ab = AddressBalance::create(&mut builder, &AddressBalanceArgs {
index: a.index,
address: Some(address),
balance: a.balance,
});
let ab = AddressBalance::create(
&mut builder,
&AddressBalanceArgs {
index: a.index,
address: Some(address),
balance: a.balance,
},
);
addrs.push(ab);
}
let addrs = builder.create_vector(&addrs);
let addrs = AddressBalanceVec::create(&mut builder, &AddressBalanceVecArgs {
values: Some(addrs)
});
let addrs = AddressBalanceVec::create(
&mut builder,
&AddressBalanceVecArgs {
values: Some(addrs),
},
);
builder.finish(addrs, None);
let data = builder.finished_data().to_vec();
Ok(data)

View File

@ -6,8 +6,8 @@ use crate::api::sync::get_latest_height;
use crate::coinconfig::CoinConfig;
use crate::contact::{serialize_contacts, Contact};
use crate::db::AccountData;
use zcash_primitives::memo::Memo;
use crate::TransactionPlan;
use zcash_primitives::memo::Memo;
/// Store contact in the database
/// # Arguments

View File

@ -1,4 +1,5 @@
use crate::coinconfig::{init_coin, CoinConfig, MEMPOOL, MEMPOOL_RUNNER};
use crate::db::data_generated::fb::SendTemplate;
use crate::db::FullEncryptedBackup;
use crate::note_selection::TransactionReport;
use crate::{ChainError, DbAdapter, TransactionPlan, Tx};
@ -6,14 +7,13 @@ use allo_isolate::{ffi, IntoDart};
use android_logger::Config;
use lazy_static::lazy_static;
use log::Level;
use rusqlite::Connection;
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use std::path::Path;
use std::sync::{Mutex, MutexGuard};
use rusqlite::Connection;
use tokio::sync::Semaphore;
use zcash_primitives::transaction::builder::Progress;
use crate::db::data_generated::fb::SendTemplate;
static mut POST_COBJ: Option<ffi::DartPostCObjectFnType> = None;
@ -78,7 +78,7 @@ fn to_cresult_bytes(res: Result<Vec<u8>, anyhow::Error>) -> CResult<*const u8> {
len: len as u32,
error: std::ptr::null_mut::<c_char>(),
}
},
}
Err(e) => {
log::error!("{}", e);
CResult {
@ -484,7 +484,8 @@ pub async unsafe extern "C" fn shield_taddr(
confirmations: u32,
) -> CResult<*mut c_char> {
let res = async move {
let tx_plan = crate::api::payment_v2::shield_taddr(coin, account, amount, confirmations).await?;
let tx_plan =
crate::api::payment_v2::shield_taddr(coin, account, amount, confirmations).await?;
let tx_plan_json = serde_json::to_string(&tx_plan)?;
Ok(tx_plan_json)
};
@ -804,7 +805,11 @@ pub unsafe extern "C" fn derive_zip32(
to_cresult_str(res())
}
fn with_account<T, F: Fn(&Connection) -> anyhow::Result<T>>(coin: u8, id: u32, f: F) -> anyhow::Result<T> {
fn with_account<T, F: Fn(&Connection) -> anyhow::Result<T>>(
coin: u8,
id: u32,
f: F,
) -> anyhow::Result<T> {
let c = CoinConfig::get(coin);
let db = c.db()?;
let connection = &db.connection;
@ -858,7 +863,11 @@ pub unsafe extern "C" fn update_account_name(coin: u8, id: u32, name: *mut c_cha
}
#[no_mangle]
pub unsafe extern "C" fn get_balances(coin: u8, id: u32, confirmed_height: u32) -> CResult<*const u8> {
pub unsafe extern "C" fn get_balances(
coin: u8,
id: u32,
confirmed_height: u32,
) -> CResult<*const u8> {
let res = |connection: &Connection| {
let data = crate::db::read::get_balances(connection, id, confirmed_height)?;
Ok(data)
@ -903,7 +912,12 @@ pub unsafe extern "C" fn get_messages(coin: u8, id: u32) -> CResult<*const u8> {
}
#[no_mangle]
pub unsafe extern "C" fn get_prev_next_message(coin: u8, id: u32, subject: *mut c_char, height: u32) -> CResult<*const u8> {
pub unsafe extern "C" fn get_prev_next_message(
coin: u8,
id: u32,
subject: *mut c_char,
height: u32,
) -> CResult<*const u8> {
from_c_str!(subject);
let res = |connection: &Connection| {
let data = crate::db::read::get_prev_next_message(connection, &subject, height, id)?;
@ -964,7 +978,11 @@ pub unsafe extern "C" fn get_pnl_txs(coin: u8, id: u32, timestamp: u32) -> CResu
}
#[no_mangle]
pub unsafe extern "C" fn get_historical_prices(coin: u8, timestamp: u32, currency: *mut c_char) -> CResult<*const u8> {
pub unsafe extern "C" fn get_historical_prices(
coin: u8,
timestamp: u32,
currency: *mut c_char,
) -> CResult<*const u8> {
from_c_str!(currency);
let res = |connection: &Connection| {
let data = crate::db::read::get_historical_prices(connection, timestamp, &currency)?;

View File

@ -11,10 +11,12 @@ use crate::unified::UnifiedAddressType;
use crate::{sync, BlockId, CompactTxStreamerClient, Hash};
use orchard::keys::FullViewingKey;
use rusqlite::Error::QueryReturnedNoRows;
use rusqlite::{params, Connection, OptionalExtension, Transaction};
use rusqlite::{params, Connection, OptionalExtension, Transaction, OpenFlags};
use serde::Serialize;
use std::collections::HashMap;
use std::convert::TryInto;
use std::path::Path;
use anyhow::anyhow;
use tonic::transport::Channel;
use tonic::Request;
use zcash_client_backend::encoding::decode_extended_full_viewing_key;
@ -25,12 +27,12 @@ use zcash_primitives::sapling::{Diversifier, Node, Note, SaplingIvk};
use zcash_primitives::zip32::{DiversifierIndex, ExtendedFullViewingKey};
mod backup;
mod migration;
pub mod data_generated;
mod migration;
pub mod read;
pub use backup::FullEncryptedBackup;
use crate::db::data_generated::fb::SendTemplate;
pub use backup::FullEncryptedBackup;
#[allow(dead_code)]
pub const DEFAULT_DB_PATH: &str = "zec.db";
@ -106,7 +108,9 @@ impl DbAdapter {
}
pub fn migrate_db(network: &Network, db_path: &str, has_ua: bool) -> anyhow::Result<()> {
let connection = Connection::open(db_path)?;
let dir = Path::new(db_path).parent().ok_or(anyhow!("Invalid db path"))?;
std::fs::create_dir_all(dir)?;
let connection = Connection::open_with_flags(db_path, OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE)?;
migration::init_db(&connection, network, has_ua)?;
Ok(())
}
@ -709,17 +713,29 @@ impl DbAdapter {
pub fn purge_old_witnesses(&mut self, height: u32) -> anyhow::Result<()> {
log::debug!("+purge_old_witnesses");
const BLOCKS_PER_HOUR: u32 = 60*60/75;
const BLOCKS_PER_DAY: u32 = 24*BLOCKS_PER_HOUR;
const BLOCKS_PER_MONTH: u32 = 30*BLOCKS_PER_DAY;
for i in 1..=24 { // 1 checkpoint per hour
self.prune_interval(height - i*BLOCKS_PER_HOUR, height - (i-1)*BLOCKS_PER_HOUR)?;
const BLOCKS_PER_HOUR: u32 = 60 * 60 / 75;
const BLOCKS_PER_DAY: u32 = 24 * BLOCKS_PER_HOUR;
const BLOCKS_PER_MONTH: u32 = 30 * BLOCKS_PER_DAY;
for i in 1..=24 {
// 1 checkpoint per hour
self.prune_interval(
height - i * BLOCKS_PER_HOUR,
height - (i - 1) * BLOCKS_PER_HOUR,
)?;
}
for i in 2..=30 { // 1 checkpoint per day
self.prune_interval(height - i*BLOCKS_PER_DAY, height - (i-1)*BLOCKS_PER_DAY)?;
for i in 2..=30 {
// 1 checkpoint per day
self.prune_interval(
height - i * BLOCKS_PER_DAY,
height - (i - 1) * BLOCKS_PER_DAY,
)?;
}
for i in 2..=12 { // 1 checkpoint per 30 days
self.prune_interval(height - i*BLOCKS_PER_MONTH, height - (i-1)*BLOCKS_PER_MONTH)?;
for i in 2..=12 {
// 1 checkpoint per 30 days
self.prune_interval(
height - i * BLOCKS_PER_MONTH,
height - (i - 1) * BLOCKS_PER_MONTH,
)?;
}
log::debug!("-purge_old_witnesses");
@ -745,8 +761,10 @@ impl DbAdapter {
"DELETE FROM orchard_witnesses WHERE height >= ?1 AND height < ?2 AND height != ?3",
params![low, high, keep_height],
)?;
transaction.execute("DELETE FROM blocks WHERE height >= ?1 AND height < ?2 AND height != ?3",
params![low, high, keep_height])?;
transaction.execute(
"DELETE FROM blocks WHERE height >= ?1 AND height < ?2 AND height != ?3",
params![low, high, keep_height],
)?;
transaction.execute(
"DELETE FROM sapling_tree WHERE height >= ?1 AND height < ?2 AND height != ?3",
params![low, high, keep_height],
@ -1102,7 +1120,7 @@ impl DbAdapter {
)?;
Ok(())
}
pub fn get_accounts(&self) -> anyhow::Result<Vec<AccountRec>> {
let mut s = self
.connection
@ -1181,11 +1199,10 @@ impl DbAdapter {
self.connection.execute("INSERT INTO \
send_templates(title, address, amount, fiat_amount, fee_included, fiat, include_reply_to, subject, body) \
VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9)",
params![t.title().unwrap(), t.address().unwrap(), t.amount(), t.fiat_amount(), t.fee_included(), t.fiat().unwrap(),
params![t.title().unwrap(), t.address().unwrap(), t.amount(), t.fiat_amount(), t.fee_included(), t.fiat(),
t.include_reply_to(), t.subject().unwrap(), t.body().unwrap()])?;
self.connection.last_insert_rowid() as u32
}
else {
} else {
self.connection.execute("UPDATE send_templates SET \
title=?1, address=?2, amount=?3, fiat_amount=?4, fee_included=?5, fiat=?6, include_reply_to=?7, subject=?8, body=?9 \
WHERE id_send_template=?10",
@ -1197,8 +1214,10 @@ impl DbAdapter {
}
pub fn delete_template(&self, id: u32) -> anyhow::Result<()> {
self.connection.execute("DELETE FROM send_templates WHERE id_send_template=?1",
params![id])?;
self.connection.execute(
"DELETE FROM send_templates WHERE id_send_template=?1",
params![id],
)?;
Ok(())
}

View File

@ -60,13 +60,15 @@ impl FullEncryptedBackup {
let mut f = File::open(data_path)?;
f.read_to_end(&mut cipher_text)?;
let decryptor = match age::Decryptor::new(&*cipher_text).map_err(|_| anyhow!("Decryption Error"))? {
age::Decryptor::Recipients(d) => d,
_ => unreachable!(),
};
let decryptor =
match age::Decryptor::new(&*cipher_text).map_err(|_| anyhow!("Decryption Error"))? {
age::Decryptor::Recipients(d) => d,
_ => unreachable!(),
};
let mut plain_text = vec![];
let mut reader = decryptor.decrypt(iter::once(&key as &dyn age::Identity))
let mut reader = decryptor
.decrypt(iter::once(&key as &dyn age::Identity))
.map_err(|_| anyhow!("Decryption Error"))?;
reader.read_to_end(&mut plain_text)?;

File diff suppressed because it is too large Load Diff

View File

@ -279,7 +279,9 @@ pub fn init_db(connection: &Connection, network: &Network, has_ua: bool) -> anyh
fiat TEXT,
include_reply_to BOOL NOT NULL,
subject TEXT NOT NULL,
body TEXT NOT NULL)", [])?;
body TEXT NOT NULL)",
[],
)?;
}
if version != LATEST_VERSION {

View File

@ -1,10 +1,12 @@
use rusqlite::{Connection, OptionalExtension, params};
use anyhow::Result;
use crate::db::data_generated::fb::*;
use crate::DbAdapter;
use anyhow::Result;
use rusqlite::{params, Connection, OptionalExtension};
pub fn has_account(connection: &Connection) -> Result<bool> {
let res = connection.query_row("SELECT 1 FROM accounts", [], |_| { Ok(()) }).optional()?;
let res = connection
.query_row("SELECT 1 FROM accounts", [], |_| Ok(()))
.optional()?;
Ok(res.is_some())
}
@ -18,11 +20,14 @@ pub fn get_account_list(connection: &Connection) -> Result<Vec<u8>> {
let name: String = row.get("name")?;
let balance: i64 = row.get("balance")?;
let name = builder.create_string(&name);
let account = Account::create(&mut builder, &AccountArgs {
id,
name: Some(name),
balance: balance as u64,
});
let account = Account::create(
&mut builder,
&AccountArgs {
id,
name: Some(name),
balance: balance as u64,
},
);
Ok(account)
})?;
let mut accounts = vec![];
@ -30,40 +35,64 @@ pub fn get_account_list(connection: &Connection) -> Result<Vec<u8>> {
accounts.push(r?);
}
let accounts = builder.create_vector(&accounts);
let accounts = AccountVec::create(&mut builder, &AccountVecArgs { accounts: Some(accounts) });
let accounts = AccountVec::create(
&mut builder,
&AccountVecArgs {
accounts: Some(accounts),
},
);
builder.finish(accounts, None);
let data = builder.finished_data().to_vec();
Ok(data)
}
pub fn get_available_account_id(connection: &Connection, id: u32) -> Result<u32> {
let r = connection.query_row("SELECT 1 FROM accounts WHERE id_account = ?1", [id], |_| { Ok(()) }).optional()?;
if r.is_some() { return Ok(id) }
let r = connection.query_row("SELECT MAX(id_account) FROM accounts", [], |row| {
let id: Option<u32> = row.get(0)?;
Ok(id)
})?.unwrap_or(0);
let r = connection
.query_row("SELECT 1 FROM accounts WHERE id_account = ?1", [id], |_| {
Ok(())
})
.optional()?;
if r.is_some() {
return Ok(id);
}
let r = connection
.query_row("SELECT MAX(id_account) FROM accounts", [], |row| {
let id: Option<u32> = row.get(0)?;
Ok(id)
})?
.unwrap_or(0);
Ok(r)
}
pub fn get_t_addr(connection: &Connection, id: u32) -> Result<String> {
let address = connection.query_row("SELECT address FROM taddrs WHERE account = ?1", [id], |row| {
let address: String = row.get(0)?;
Ok(address)
})?;
let address = connection.query_row(
"SELECT address FROM taddrs WHERE account = ?1",
[id],
|row| {
let address: String = row.get(0)?;
Ok(address)
},
)?;
Ok(address)
}
pub fn get_sk(connection: &Connection, id: u32) -> Result<String> {
let sk = connection.query_row("SELECT sk FROM accounts WHERE id_account = ?1", [id], |row| {
let sk: Option<String> = row.get(0)?;
Ok(sk.unwrap_or(String::new()))
})?;
let sk = connection.query_row(
"SELECT sk FROM accounts WHERE id_account = ?1",
[id],
|row| {
let sk: Option<String> = row.get(0)?;
Ok(sk.unwrap_or(String::new()))
},
)?;
Ok(sk)
}
pub fn update_account_name(connection: &Connection, id: u32, name: &str) -> Result<()> {
connection.execute("UPDATE accounts SET name = ?2 WHERE id_account = ?1", params![id, name])?;
connection.execute(
"UPDATE accounts SET name = ?2 WHERE id_account = ?1",
params![id, name],
)?;
Ok(())
}
@ -71,16 +100,20 @@ pub fn get_balances(connection: &Connection, id: u32, confirmed_height: u32) ->
let mut builder = flatbuffers::FlatBufferBuilder::new();
let shielded = connection.query_row(
"SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND spent IS NULL",
params![id], |row| {
params![id],
|row| {
let value: Option<i64> = row.get(0)?;
Ok(value.unwrap_or(0) as u64)
})?; // funds not spent yet
},
)?; // funds not spent yet
let unconfirmed_spent = connection.query_row(
"SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND spent = 0",
params![id], |row| {
params![id],
|row| {
let value: Option<i64> = row.get(0)?;
Ok(value.unwrap_or(0) as u64)
})?; // funds used in unconfirmed tx
},
)?; // funds used in unconfirmed tx
let balance = shielded + unconfirmed_spent;
let under_confirmed = connection.query_row(
"SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND spent IS NULL AND height > ?2",
@ -91,10 +124,12 @@ pub fn get_balances(connection: &Connection, id: u32, confirmed_height: u32) ->
let excluded = connection.query_row(
"SELECT SUM(value) FROM received_notes WHERE account = ?1 AND spent IS NULL \
AND height <= ?2 AND excluded",
params![id, confirmed_height], |row| {
params![id, confirmed_height],
|row| {
let value: Option<i64> = row.get(0)?;
Ok(value.unwrap_or(0) as u64)
})?; // funds excluded from spending
},
)?; // funds excluded from spending
let sapling = connection.query_row(
"SELECT SUM(value) FROM received_notes WHERE account = ?1 AND spent IS NULL AND orchard = 0 AND height <= ?2",
params![id, confirmed_height], |row| {
@ -108,15 +143,18 @@ pub fn get_balances(connection: &Connection, id: u32, confirmed_height: u32) ->
Ok(value.unwrap_or(0) as u64)
})?;
let balance = Balance::create(&mut builder, &BalanceArgs {
shielded,
unconfirmed_spent,
balance,
under_confirmed,
excluded,
sapling,
orchard
});
let balance = Balance::create(
&mut builder,
&BalanceArgs {
shielded,
unconfirmed_spent,
balance,
under_confirmed,
excluded,
sapling,
orchard,
},
);
builder.finish(balance, None);
let data = builder.finished_data().to_vec();
Ok(data)
@ -124,21 +162,24 @@ pub fn get_balances(connection: &Connection, id: u32, confirmed_height: u32) ->
pub fn get_db_height(connection: &Connection) -> Result<Vec<u8>> {
let mut builder = flatbuffers::FlatBufferBuilder::new();
let height = connection.query_row(
"SELECT height, timestamp FROM blocks WHERE height = (SELECT MAX(height) FROM blocks)",
[], |row| {
let height: u32 = row.get(0)?;
let timestamp: u32 = row.get(1)?;
let height = Height::create(&mut builder, &HeightArgs {
height,
timestamp,
});
Ok(height)
}).optional()?;
let data = height.map(|h| {
builder.finish(h, None);
builder.finished_data().to_vec()
}).unwrap_or(vec![]);
let height = connection
.query_row(
"SELECT height, timestamp FROM blocks WHERE height = (SELECT MAX(height) FROM blocks)",
[],
|row| {
let height: u32 = row.get(0)?;
let timestamp: u32 = row.get(1)?;
let height = Height::create(&mut builder, &HeightArgs { height, timestamp });
Ok(height)
},
)
.optional()?;
let data = height
.map(|h| {
builder.finish(h, None);
builder.finished_data().to_vec()
})
.unwrap_or(vec![]);
Ok(data)
}
@ -156,15 +197,18 @@ pub fn get_notes(connection: &Connection, id: u32) -> Result<Vec<u8>> {
let orchard: u8 = row.get("orchard")?;
let excluded: Option<bool> = row.get("excluded")?;
let spent: Option<u32> = row.get("spent")?;
let note = ShieldedNote::create(&mut builder, &ShieldedNoteArgs {
id,
height,
value: value as u64,
timestamp,
orchard: orchard == 1,
excluded: excluded.unwrap_or(false),
spent: spent.is_some()
});
let note = ShieldedNote::create(
&mut builder,
&ShieldedNoteArgs {
id,
height,
value: value as u64,
timestamp,
orchard: orchard == 1,
excluded: excluded.unwrap_or(false),
spent: spent.is_some(),
},
);
Ok(note)
})?;
let mut notes = vec![];
@ -206,17 +250,20 @@ pub fn get_txs(connection: &Connection, id: u32) -> Result<Vec<u8>> {
let name = builder.create_string(&name);
let address = builder.create_string(&address);
let memo = builder.create_string(&memo);
let tx = ShieldedTx::create(&mut builder, &ShieldedTxArgs {
id: id_tx,
height,
tx_id: Some(tx_id),
short_tx_id: Some(short_tx_id),
timestamp,
name: Some(name),
value: value as u64,
address: Some(address),
memo: Some(memo),
});
let tx = ShieldedTx::create(
&mut builder,
&ShieldedTxArgs {
id: id_tx,
height,
tx_id: Some(tx_id),
short_tx_id: Some(short_tx_id),
timestamp,
name: Some(name),
value: value as u64,
address: Some(address),
memo: Some(memo),
},
);
Ok(tx)
})?;
let mut txs = vec![];
@ -265,18 +312,21 @@ pub fn get_messages(connection: &Connection, id: u32) -> Result<Vec<u8>> {
let subject = builder.create_string(&subject);
let body = builder.create_string(&body);
let message = Message::create(&mut builder, &MessageArgs {
id_msg,
id_tx,
height,
timestamp,
from: Some(from),
to: Some(to),
subject: Some(subject),
body: Some(body),
read,
incoming
});
let message = Message::create(
&mut builder,
&MessageArgs {
id_msg,
id_tx,
height,
timestamp,
from: Some(from),
to: Some(to),
subject: Some(subject),
body: Some(body),
read,
incoming,
},
);
Ok(message)
})?;
let mut messages = vec![];
@ -284,24 +334,44 @@ pub fn get_messages(connection: &Connection, id: u32) -> Result<Vec<u8>> {
messages.push(r?);
}
let messages = builder.create_vector(&messages);
let messages = MessageVec::create(&mut builder, &MessageVecArgs { messages: Some(messages) });
let messages = MessageVec::create(
&mut builder,
&MessageVecArgs {
messages: Some(messages),
},
);
builder.finish(messages, None);
let data = builder.finished_data().to_vec();
Ok(data)
}
pub fn get_prev_next_message(connection: &Connection, subject: &str, height: u32, account: u32) -> Result<Vec<u8>> {
pub fn get_prev_next_message(
connection: &Connection,
subject: &str,
height: u32,
account: u32,
) -> Result<Vec<u8>> {
let mut builder = flatbuffers::FlatBufferBuilder::new();
let prev = connection.query_row("SELECT MAX(id) FROM messages WHERE subject = ?1 AND height < ?2 and account = ?3",
params![subject, height, account], |row| {
let id: Option<u32> = row.get(0)?;
Ok(id)
})?.unwrap_or(0);
let next = connection.query_row("SELECT MIN(id) FROM messages WHERE subject = ?1 AND height > ?2 and account = ?3",
params![subject, height, account], |row| {
let id: Option<u32> = row.get(0)?;
Ok(id)
})?.unwrap_or(0);
let prev = connection
.query_row(
"SELECT MAX(id) FROM messages WHERE subject = ?1 AND height < ?2 and account = ?3",
params![subject, height, account],
|row| {
let id: Option<u32> = row.get(0)?;
Ok(id)
},
)?
.unwrap_or(0);
let next = connection
.query_row(
"SELECT MIN(id) FROM messages WHERE subject = ?1 AND height > ?2 and account = ?3",
params![subject, height, account],
|row| {
let id: Option<u32> = row.get(0)?;
Ok(id)
},
)?
.unwrap_or(0);
let prev_next = PrevNext::create(&mut builder, &PrevNextArgs { prev, next });
builder.finish(prev_next, None);
let data = builder.finished_data().to_vec();
@ -319,30 +389,32 @@ pub fn get_templates(connection: &Connection) -> Result<Vec<u8>> {
let amount: i64 = row.get("amount")?;
let fiat_amount: f64 = row.get("fiat_amount")?;
let fee_included: bool = row.get("fee_included")?;
let fiat: String = row.get("fiat")?;
let fiat: Option<String> = row.get("fiat")?;
let include_reply_to: bool = row.get("include_reply_to")?;
let subject: String = row.get("subject")?;
let body: String = row.get("body")?;
let title = builder.create_string(&title);
let address = builder.create_string(&address);
let fiat = builder.create_string(&fiat);
let fiat = fiat.map(|fiat| builder.create_string(&fiat));
let subject = builder.create_string(&subject);
let body = builder.create_string(&body);
let template = SendTemplate::create(&mut builder, &SendTemplateArgs {
id: id_msg,
title: Some(title),
address: Some(address),
amount: amount as u64,
fiat_amount,
fee_included,
fiat: Some(fiat),
include_reply_to,
subject: Some(subject),
body: Some(body),
});
let template = SendTemplate::create(
&mut builder,
&SendTemplateArgs {
id: id_msg,
title: Some(title),
address: Some(address),
amount: amount as u64,
fiat_amount,
fee_included,
fiat,
include_reply_to,
subject: Some(subject),
body: Some(body),
},
);
Ok(template)
})?;
let mut templates = vec![];
@ -350,7 +422,12 @@ pub fn get_templates(connection: &Connection) -> Result<Vec<u8>> {
templates.push(r?);
}
let templates = builder.create_vector(&templates);
let templates = SendTemplateVec::create(&mut builder, &SendTemplateVecArgs { templates: Some(templates) });
let templates = SendTemplateVec::create(
&mut builder,
&SendTemplateVecArgs {
templates: Some(templates),
},
);
builder.finish(templates, None);
let data = builder.finished_data().to_vec();
Ok(data)
@ -358,19 +435,22 @@ pub fn get_templates(connection: &Connection) -> Result<Vec<u8>> {
pub fn get_contacts(connection: &Connection) -> Result<Vec<u8>> {
let mut builder = flatbuffers::FlatBufferBuilder::new();
let mut stmt = connection.prepare(
"SELECT id, name, address FROM contacts WHERE address <> '' ORDER BY name")?;
let mut stmt = connection
.prepare("SELECT id, name, address FROM contacts WHERE address <> '' ORDER BY name")?;
let rows = stmt.query_map([], |row| {
let id: u32 = row.get("id")?;
let name: String = row.get("name")?;
let address: String = row.get("address")?;
let name = builder.create_string(&name);
let address = builder.create_string(&address);
let contact = Contact::create(&mut builder, &ContactArgs {
id,
name: Some(name),
address: Some(address),
});
let contact = Contact::create(
&mut builder,
&ContactArgs {
id,
name: Some(name),
address: Some(address),
},
);
Ok(contact)
})?;
let mut contacts = vec![];
@ -378,7 +458,12 @@ pub fn get_contacts(connection: &Connection) -> Result<Vec<u8>> {
contacts.push(r?);
}
let contacts = builder.create_vector(&contacts);
let contacts = ContactVec::create(&mut builder, &ContactVecArgs { contacts: Some(contacts) });
let contacts = ContactVec::create(
&mut builder,
&ContactVecArgs {
contacts: Some(contacts),
},
);
builder.finish(contacts, None);
let data = builder.finished_data().to_vec();
Ok(data)
@ -391,8 +476,13 @@ pub fn get_pnl_txs(connection: &Connection, id: u32, timestamp: u32) -> Result<V
let rows = stmt.query_map([id, timestamp], |row| {
let timestamp: u32 = row.get(0)?;
let value: i64 = row.get(1)?;
let tx = TxTimeValue::create(&mut builder, &TxTimeValueArgs {
timestamp, value: value as u64 });
let tx = TxTimeValue::create(
&mut builder,
&TxTimeValueArgs {
timestamp,
value: value as u64,
},
);
Ok(tx)
})?;
let mut txs = vec![];
@ -406,15 +496,19 @@ pub fn get_pnl_txs(connection: &Connection, id: u32, timestamp: u32) -> Result<V
Ok(data)
}
pub fn get_historical_prices(connection: &Connection, timestamp: u32, currency: &str) -> Result<Vec<u8>> {
pub fn get_historical_prices(
connection: &Connection,
timestamp: u32,
currency: &str,
) -> Result<Vec<u8>> {
let mut builder = flatbuffers::FlatBufferBuilder::new();
let mut stmt = connection.prepare(
"SELECT timestamp, price FROM historical_prices WHERE timestamp >= ?2 AND currency = ?1")?;
"SELECT timestamp, price FROM historical_prices WHERE timestamp >= ?2 AND currency = ?1",
)?;
let rows = stmt.query_map(params![currency, timestamp], |row| {
let timestamp: u32 = row.get(0)?;
let price: f64 = row.get(1)?;
let quote = Quote::create(&mut builder, &QuoteArgs {
timestamp, price });
let quote = Quote::create(&mut builder, &QuoteArgs { timestamp, price });
Ok(quote)
})?;
let mut quotes = vec![];
@ -422,7 +516,12 @@ pub fn get_historical_prices(connection: &Connection, timestamp: u32, currency:
quotes.push(r?);
}
let quotes = builder.create_vector(&quotes);
let quotes = QuoteVec::create(&mut builder, &QuoteVecArgs { values: Some(quotes) });
let quotes = QuoteVec::create(
&mut builder,
&QuoteVecArgs {
values: Some(quotes),
},
);
builder.finish(quotes, None);
let data = builder.finished_data().to_vec();
Ok(data)
@ -442,10 +541,13 @@ pub fn get_spendings(connection: &Connection, id: u32, timestamp: u32) -> Result
let recipient = recipient.unwrap_or(String::new());
let recipient = builder.create_string(&recipient);
let spending = Spending::create(&mut builder, &SpendingArgs {
recipient: Some(recipient),
amount: (-value) as u64,
});
let spending = Spending::create(
&mut builder,
&SpendingArgs {
recipient: Some(recipient),
amount: (-value) as u64,
},
);
Ok(spending)
})?;
let mut spendings = vec![];
@ -453,80 +555,91 @@ pub fn get_spendings(connection: &Connection, id: u32, timestamp: u32) -> Result
spendings.push(r?);
}
let spendings = builder.create_vector(&spendings);
let spendings = SpendingVec::create(&mut builder, &SpendingVecArgs { values: Some(spendings) });
let spendings = SpendingVec::create(
&mut builder,
&SpendingVecArgs {
values: Some(spendings),
},
);
builder.finish(spendings, None);
let data = builder.finished_data().to_vec();
Ok(data)
}
pub fn update_excluded(connection: &Connection, id: u32, excluded: bool) -> Result<()> {
connection.execute("UPDATE received_notes SET excluded = ?2 WHERE id_note = ?1", params![id, excluded])?;
connection.execute(
"UPDATE received_notes SET excluded = ?2 WHERE id_note = ?1",
params![id, excluded],
)?;
Ok(())
}
pub fn invert_excluded(connection: &Connection, id: u32) -> Result<()> {
connection.execute("UPDATE received_notes SET excluded = NOT(COALESCE(excluded, 0)) WHERE account = ?1", [id])?;
connection.execute(
"UPDATE received_notes SET excluded = NOT(COALESCE(excluded, 0)) WHERE account = ?1",
[id],
)?;
Ok(())
}
/*
,
,
"SELECT timestamp, value FROM transactions WHERE account = ?1 AND timestamp >= ?2 ORDER BY timestamp DESC",
"SELECT timestamp, value FROM transactions WHERE account = ?1 AND timestamp >= ?2 ORDER BY timestamp DESC",
final List<Map> res = await db.rawQuery(
"SELECT SUM(value) as v, t.address, c.name FROM transactions t LEFT JOIN contacts c ON t.address = c.address "
"WHERE account = ?1 AND timestamp >= ?2 AND value < 0 GROUP BY t.address ORDER BY v ASC LIMIT 5",
[accountId, range.start ~/ 1000]);
final spendings = res.map((row) {
final address = row['address'] ?? "";
final value = -row['v'] / ZECUNIT;
final contact = row['name'];
return Spending(address, value, contact);
}).toList();
final List<Map> res = await db.rawQuery(
"SELECT SUM(value) as v, t.address, c.name FROM transactions t LEFT JOIN contacts c ON t.address = c.address "
"WHERE account = ?1 AND timestamp >= ?2 AND value < 0 GROUP BY t.address ORDER BY v ASC LIMIT 5",
[accountId, range.start ~/ 1000]);
final spendings = res.map((row) {
final address = row['address'] ?? "";
final value = -row['v'] / ZECUNIT;
final contact = row['name'];
return Spending(address, value, contact);
}).toList();
"SELECT timestamp, price FROM historical_prices WHERE timestamp >= ?2 AND currency = ?1",
"SELECT timestamp, price FROM historical_prices WHERE timestamp >= ?2 AND currency = ?1",
final List<Map> res1 = await db.rawQuery(
"SELECT timestamp, value FROM transactions WHERE timestamp >= ?2 AND account = ?1",
[accountId, range.start ~/ 1000]);
final List<Map> res1 = await db.rawQuery(
"SELECT timestamp, value FROM transactions WHERE timestamp >= ?2 AND account = ?1",
[accountId, range.start ~/ 1000]);
List<Map> res = await db.rawQuery(
"SELECT id, name, address FROM contacts WHERE address <> '' ORDER BY name");
for (var c in res) {
final contact = Contact(c['id'], c['name'], c['address']);
contacts.add(contact);
}
List<Map> res = await db.rawQuery(
"SELECT id, name, address FROM contacts WHERE address <> '' ORDER BY name");
for (var c in res) {
final contact = Contact(c['id'], c['name'], c['address']);
contacts.add(contact);
}
Future<int?> getPrevMessage(String subject, int height, int account) async {
final id = await Sqflite.firstIntValue(await db.rawQuery(
"SELECT MAX(id) FROM messages WHERE subject = ?1 AND height < ?2 and account = ?3",
[subject, height, account]));
return id;
}
Future<int?> getPrevMessage(String subject, int height, int account) async {
final id = await Sqflite.firstIntValue(await db.rawQuery(
"SELECT MAX(id) FROM messages WHERE subject = ?1 AND height < ?2 and account = ?3",
[subject, height, account]));
return id;
}
Future<int?> getNextMessage(String subject, int height, int account) async {
final id = await Sqflite.firstIntValue(await db.rawQuery(
"SELECT MIN(id) FROM messages WHERE subject = ?1 AND height > ?2 and account = ?3",
[subject, height, account]));
return id;
}
Future<int?> getNextMessage(String subject, int height, int account) async {
final id = await Sqflite.firstIntValue(await db.rawQuery(
"SELECT MIN(id) FROM messages WHERE subject = ?1 AND height > ?2 and account = ?3",
[subject, height, account]));
return id;
}
final id = row['id'];
final txId = row['id_tx'] ?? 0;
final timestamp = DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000);
final height = row['height'];
final sender = row['sender'];
final from = row['scontact'] ?? row['saccount'] ?? sender;
final recipient = row['recipient'];
final to = row['rcontact'] ?? row['raccount'] ?? recipient;
final subject = row['subject'];
final body = row['body'];
final read = row['read'] == 1;
final incoming = row['incoming'] == 1;
*/
final id = row['id'];
final txId = row['id_tx'] ?? 0;
final timestamp = DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000);
final height = row['height'];
final sender = row['sender'];
final from = row['scontact'] ?? row['saccount'] ?? sender;
final recipient = row['recipient'];
final to = row['rcontact'] ?? row['raccount'] ?? recipient;
final subject = row['subject'];
final body = row['body'];
final read = row['read'] == 1;
final incoming = row['incoming'] == 1;
*/

View File

@ -158,7 +158,7 @@ pub fn decode_transaction(
Ok(memo) if memo != Memo::Empty => {
tx_memo = memo;
}
_ => ()
_ => (),
}
}
if let Some((_note, pa, memo, ..)) =
@ -175,7 +175,7 @@ pub fn decode_transaction(
tx_memo = memo;
incoming = false;
}
_ => ()
_ => (),
}
}
}
@ -197,7 +197,7 @@ pub fn decode_transaction(
Ok(memo) if memo != Memo::Empty => {
tx_memo = memo;
}
_ => ()
_ => (),
}
}
if let Some((_note, pa, memo, ..)) = try_output_recovery_with_ovk(
@ -216,7 +216,7 @@ pub fn decode_transaction(
tx_memo = memo;
incoming = false;
}
_ => ()
_ => (),
}
}
}