Better db error logging
This commit is contained in:
parent
fa137ffbf4
commit
c7c1a2e95e
|
@ -100,6 +100,8 @@ void mark_all_messages_read(bool read);
|
|||
|
||||
void truncate_data(void);
|
||||
|
||||
void truncate_sync_data(void);
|
||||
|
||||
void delete_account(uint8_t coin, uint32_t account);
|
||||
|
||||
char *make_payment_uri(char *address, uint64_t amount, char *memo);
|
||||
|
|
|
@ -150,6 +150,12 @@ pub fn truncate_data() -> anyhow::Result<()> {
|
|||
db.truncate_data()
|
||||
}
|
||||
|
||||
pub fn truncate_sync_data() -> anyhow::Result<()> {
|
||||
let c = CoinConfig::get_active();
|
||||
let db = c.db()?;
|
||||
db.truncate_sync_data()
|
||||
}
|
||||
|
||||
pub fn delete_account(coin: u8, account: u32) -> anyhow::Result<()> {
|
||||
let c = CoinConfig::get(coin);
|
||||
let db = c.db()?;
|
||||
|
|
|
@ -497,6 +497,12 @@ pub unsafe extern "C" fn truncate_data() {
|
|||
log_result(res)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn truncate_sync_data() {
|
||||
let res = crate::api::account::truncate_sync_data();
|
||||
log_result(res)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn delete_account(coin: u8, account: u32) {
|
||||
let res = crate::api::account::delete_account(coin, account);
|
||||
|
|
|
@ -91,7 +91,7 @@ pub async fn skip_to_last_height(coin: u8) -> anyhow::Result<()> {
|
|||
pub async fn rewind_to_height(height: u32) -> anyhow::Result<()> {
|
||||
let c = CoinConfig::get_active();
|
||||
let mut client = c.connect_lwd().await?;
|
||||
c.db()?.trim_to_height(height)?;
|
||||
c.db()?.trim_to_height(height, false)?;
|
||||
fetch_and_store_tree_state(c.coin, &mut client, height).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ impl CoinConfig {
|
|||
|
||||
pub fn set_db_path(&mut self, db_path: &str) -> anyhow::Result<()> {
|
||||
self.db_path = Some(db_path.to_string());
|
||||
let db = DbAdapter::new(self.coin_type, db_path)?;
|
||||
let mut db = DbAdapter::new(self.coin_type, db_path)?;
|
||||
db.init_db()?;
|
||||
self.db = Some(Arc::new(Mutex::new(db)));
|
||||
Ok(())
|
||||
|
|
257
src/db.rs
257
src/db.rs
|
@ -4,6 +4,7 @@ use crate::prices::Quote;
|
|||
use crate::taddr::{derive_tkeys, TBalance};
|
||||
use crate::transaction::TransactionInfo;
|
||||
use crate::{CTree, Witness};
|
||||
use rusqlite::Error::QueryReturnedNoRows;
|
||||
use rusqlite::{params, Connection, OptionalExtension, Transaction};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::serde_as;
|
||||
|
@ -74,6 +75,13 @@ pub struct AccountBackup {
|
|||
pub t_addr: 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),
|
||||
other => anyhow::anyhow!(other.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
impl DbAdapter {
|
||||
pub fn new(coin_type: CoinType, db_path: &str) -> anyhow::Result<DbAdapter> {
|
||||
let connection = Connection::open(db_path)?;
|
||||
|
@ -102,8 +110,9 @@ impl DbAdapter {
|
|||
// Ok(())
|
||||
// }
|
||||
//
|
||||
pub fn init_db(&self) -> anyhow::Result<()> {
|
||||
pub fn init_db(&mut self) -> anyhow::Result<()> {
|
||||
migration::init_db(&self.connection)?;
|
||||
self.delete_incomplete_scan()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -130,11 +139,16 @@ impl DbAdapter {
|
|||
ON CONFLICT DO NOTHING",
|
||||
params![name, seed, index, sk, ivk, address],
|
||||
)?;
|
||||
let id_account: u32 = self.connection.query_row(
|
||||
"SELECT id_account FROM accounts WHERE ivk = ?1",
|
||||
params![ivk],
|
||||
|row| row.get(0),
|
||||
)?;
|
||||
let id_account: u32 = self
|
||||
.connection
|
||||
.query_row(
|
||||
"SELECT id_account FROM accounts WHERE ivk = ?1",
|
||||
params![ivk],
|
||||
|row| row.get(0),
|
||||
)
|
||||
.map_err(wrap_query_no_rows(
|
||||
"store_account/id_account",
|
||||
))?;
|
||||
Ok((id_account, exists))
|
||||
}
|
||||
|
||||
|
@ -195,26 +209,48 @@ impl DbAdapter {
|
|||
Ok(fvks)
|
||||
}
|
||||
|
||||
pub fn trim_to_height(&mut self, height: u32) -> anyhow::Result<()> {
|
||||
pub fn trim_to_height(&mut self, height: u32, exact: bool) -> anyhow::Result<()> {
|
||||
// snap height to an existing checkpoint
|
||||
let height = if exact {
|
||||
self.connection
|
||||
.query_row(
|
||||
"SELECT 1 from blocks WHERE height = ?1",
|
||||
params![height],
|
||||
|row| Ok(()),
|
||||
)
|
||||
.map_err(wrap_query_no_rows("trim_to_height: height not found"))?;
|
||||
height
|
||||
} else {
|
||||
let height = self.connection.query_row(
|
||||
"SELECT MAX(height) from blocks WHERE height <= ?1",
|
||||
params![height],
|
||||
|row| {
|
||||
let height: Option<u32> = row.get(0)?;
|
||||
Ok(height)
|
||||
},
|
||||
)?;
|
||||
height.unwrap_or(0)
|
||||
};
|
||||
|
||||
let tx = self.connection.transaction()?;
|
||||
tx.execute("DELETE FROM blocks WHERE height >= ?1", params![height])?;
|
||||
tx.execute("DELETE FROM blocks WHERE height > ?1", params![height])?;
|
||||
tx.execute(
|
||||
"DELETE FROM sapling_witnesses WHERE height >= ?1",
|
||||
"DELETE FROM sapling_witnesses WHERE height > ?1",
|
||||
params![height],
|
||||
)?;
|
||||
tx.execute(
|
||||
"DELETE FROM received_notes WHERE height >= ?1",
|
||||
"DELETE FROM received_notes WHERE height > ?1",
|
||||
params![height],
|
||||
)?;
|
||||
tx.execute(
|
||||
"UPDATE received_notes SET spent = NULL WHERE spent >= ?1",
|
||||
"UPDATE received_notes SET spent = NULL WHERE spent > ?1",
|
||||
params![height],
|
||||
)?;
|
||||
tx.execute(
|
||||
"DELETE FROM transactions WHERE height >= ?1",
|
||||
"DELETE FROM transactions WHERE height > ?1",
|
||||
params![height],
|
||||
)?;
|
||||
tx.execute("DELETE FROM messages WHERE height >= ?1", params![height])?;
|
||||
tx.execute("DELETE FROM messages WHERE height > ?1", params![height])?;
|
||||
tx.commit()?;
|
||||
|
||||
Ok(())
|
||||
|
@ -232,7 +268,7 @@ impl DbAdapter {
|
|||
let ivk: String = row.get(4)?;
|
||||
Ok((account, height, timestamp, tx_hash, ivk))
|
||||
},
|
||||
)?;
|
||||
).map_err(wrap_query_no_rows("get_txhash"))?;
|
||||
Ok((account, height, timestamp, tx_hash, ivk))
|
||||
}
|
||||
|
||||
|
@ -271,11 +307,13 @@ impl DbAdapter {
|
|||
ON CONFLICT DO NOTHING",
|
||||
params![account, txid, height, timestamp, tx_index],
|
||||
)?;
|
||||
let id_tx: u32 = db_tx.query_row(
|
||||
"SELECT id_tx FROM transactions WHERE account = ?1 AND txid = ?2",
|
||||
params![account, txid],
|
||||
|row| row.get(0),
|
||||
)?;
|
||||
let id_tx: u32 = db_tx
|
||||
.query_row(
|
||||
"SELECT id_tx FROM transactions WHERE account = ?1 AND txid = ?2",
|
||||
params![account, txid],
|
||||
|row| row.get(0),
|
||||
)
|
||||
.map_err(wrap_query_no_rows("store_transaction/id_tx"))?;
|
||||
log::debug!("-transaction {}", id_tx);
|
||||
Ok(id_tx)
|
||||
}
|
||||
|
@ -290,11 +328,13 @@ impl DbAdapter {
|
|||
db_tx.execute("INSERT INTO received_notes(account, tx, height, position, output_index, diversifier, value, rcm, nf, spent)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)
|
||||
ON CONFLICT DO NOTHING", params![note.account, id_tx, note.height, position as u32, note.output_index, note.diversifier, note.value as i64, note.rcm, note.nf, note.spent])?;
|
||||
let id_note: u32 = db_tx.query_row(
|
||||
"SELECT id_note FROM received_notes WHERE tx = ?1 AND output_index = ?2",
|
||||
params![id_tx, note.output_index],
|
||||
|row| row.get(0),
|
||||
)?;
|
||||
let id_note: u32 = db_tx
|
||||
.query_row(
|
||||
"SELECT id_note FROM received_notes WHERE tx = ?1 AND output_index = ?2",
|
||||
params![id_tx, note.output_index],
|
||||
|row| row.get(0),
|
||||
)
|
||||
.map_err(wrap_query_no_rows("store_received_note/id_note"))?;
|
||||
log::debug!("-received_note");
|
||||
Ok(id_note)
|
||||
}
|
||||
|
@ -334,15 +374,17 @@ impl DbAdapter {
|
|||
}
|
||||
|
||||
pub fn get_received_note_value(nf: &Nf, db_tx: &Transaction) -> anyhow::Result<(u32, i64)> {
|
||||
let (account, value) = db_tx.query_row(
|
||||
"SELECT account, value FROM received_notes WHERE nf = ?1",
|
||||
params![nf.0.to_vec()],
|
||||
|row| {
|
||||
let account: u32 = row.get(0)?;
|
||||
let value: i64 = row.get(1)?;
|
||||
Ok((account, value))
|
||||
},
|
||||
)?;
|
||||
let (account, value) = db_tx
|
||||
.query_row(
|
||||
"SELECT account, value FROM received_notes WHERE nf = ?1",
|
||||
params![nf.0.to_vec()],
|
||||
|row| {
|
||||
let account: u32 = row.get(0)?;
|
||||
let value: i64 = row.get(1)?;
|
||||
Ok((account, value))
|
||||
},
|
||||
)
|
||||
.map_err(wrap_query_no_rows("get_received_note_value"))?;
|
||||
Ok((account, value))
|
||||
}
|
||||
|
||||
|
@ -356,9 +398,10 @@ impl DbAdapter {
|
|||
}
|
||||
|
||||
pub fn get_last_sync_height(&self) -> anyhow::Result<Option<u32>> {
|
||||
let height: Option<u32> =
|
||||
self.connection
|
||||
.query_row("SELECT MAX(height) FROM blocks", [], |row| row.get(0))?;
|
||||
let height: Option<u32> = self
|
||||
.connection
|
||||
.query_row("SELECT MAX(height) FROM blocks", [], |row| row.get(0))
|
||||
.map_err(wrap_query_no_rows(""))?;
|
||||
Ok(height)
|
||||
}
|
||||
|
||||
|
@ -546,7 +589,7 @@ impl DbAdapter {
|
|||
pub fn purge_old_witnesses(&mut self, height: u32) -> anyhow::Result<()> {
|
||||
log::debug!("+purge_old_witnesses");
|
||||
let min_height: Option<u32> = self.connection.query_row(
|
||||
"SELECT MAX(height) FROM sapling_witnesses WHERE height <= ?1",
|
||||
"SELECT MAX(height) FROM blocks WHERE height <= ?1",
|
||||
params![height],
|
||||
|row| row.get(0),
|
||||
)?;
|
||||
|
@ -608,73 +651,88 @@ impl DbAdapter {
|
|||
account: u32,
|
||||
) -> anyhow::Result<(Option<String>, Option<String>, String)> {
|
||||
log::debug!("+get_backup");
|
||||
let (seed, sk, ivk) = self.connection.query_row(
|
||||
"SELECT seed, sk, ivk FROM accounts WHERE id_account = ?1",
|
||||
params![account],
|
||||
|row| {
|
||||
let seed: Option<String> = row.get(0)?;
|
||||
let sk: Option<String> = row.get(1)?;
|
||||
let ivk: String = row.get(2)?;
|
||||
Ok((seed, sk, ivk))
|
||||
},
|
||||
)?;
|
||||
let (seed, sk, ivk) = self
|
||||
.connection
|
||||
.query_row(
|
||||
"SELECT seed, sk, ivk FROM accounts WHERE id_account = ?1",
|
||||
params![account],
|
||||
|row| {
|
||||
let seed: Option<String> = row.get(0)?;
|
||||
let sk: Option<String> = row.get(1)?;
|
||||
let ivk: String = row.get(2)?;
|
||||
Ok((seed, sk, ivk))
|
||||
},
|
||||
)
|
||||
.map_err(wrap_query_no_rows("get_backup"))?;
|
||||
log::debug!("-get_backup");
|
||||
Ok((seed, sk, ivk))
|
||||
}
|
||||
|
||||
pub fn get_seed(&self, account: u32) -> anyhow::Result<(Option<String>, u32)> {
|
||||
log::info!("+get_seed");
|
||||
let (seed, index) = self.connection.query_row(
|
||||
"SELECT seed, aindex FROM accounts WHERE id_account = ?1",
|
||||
params![account],
|
||||
|row| {
|
||||
let sk: Option<String> = row.get(0)?;
|
||||
let index: u32 = row.get(1)?;
|
||||
Ok((sk, index))
|
||||
},
|
||||
)?;
|
||||
let (seed, index) = self
|
||||
.connection
|
||||
.query_row(
|
||||
"SELECT seed, aindex FROM accounts WHERE id_account = ?1",
|
||||
params![account],
|
||||
|row| {
|
||||
let sk: Option<String> = row.get(0)?;
|
||||
let index: u32 = row.get(1)?;
|
||||
Ok((sk, index))
|
||||
},
|
||||
)
|
||||
.map_err(wrap_query_no_rows("get_seed"))?;
|
||||
log::info!("-get_seed");
|
||||
Ok((seed, index))
|
||||
}
|
||||
|
||||
pub fn get_sk(&self, account: u32) -> anyhow::Result<String> {
|
||||
log::info!("+get_sk");
|
||||
let sk = self.connection.query_row(
|
||||
"SELECT sk FROM accounts WHERE id_account = ?1",
|
||||
params![account],
|
||||
|row| {
|
||||
let sk: String = row.get(0)?;
|
||||
Ok(sk)
|
||||
},
|
||||
)?;
|
||||
let sk = self
|
||||
.connection
|
||||
.query_row(
|
||||
"SELECT sk FROM accounts WHERE id_account = ?1",
|
||||
params![account],
|
||||
|row| {
|
||||
let sk: String = row.get(0)?;
|
||||
Ok(sk)
|
||||
},
|
||||
)
|
||||
.map_err(wrap_query_no_rows("get_sk"))?;
|
||||
log::info!("-get_sk");
|
||||
Ok(sk)
|
||||
}
|
||||
|
||||
pub fn get_ivk(&self, account: u32) -> anyhow::Result<String> {
|
||||
log::debug!("+get_ivk");
|
||||
let ivk = self.connection.query_row(
|
||||
"SELECT ivk FROM accounts WHERE id_account = ?1",
|
||||
params![account],
|
||||
|row| {
|
||||
let ivk: String = row.get(0)?;
|
||||
Ok(ivk)
|
||||
},
|
||||
)?;
|
||||
let ivk = self
|
||||
.connection
|
||||
.query_row(
|
||||
"SELECT ivk FROM accounts WHERE id_account = ?1",
|
||||
params![account],
|
||||
|row| {
|
||||
let ivk: String = row.get(0)?;
|
||||
Ok(ivk)
|
||||
},
|
||||
)
|
||||
.map_err(wrap_query_no_rows("get_ivk"))?;
|
||||
log::debug!("-get_ivk");
|
||||
Ok(ivk)
|
||||
}
|
||||
|
||||
pub fn get_address(&self, account: u32) -> anyhow::Result<String> {
|
||||
log::debug!("+get_address");
|
||||
let address = self.connection.query_row(
|
||||
"SELECT address FROM accounts WHERE id_account = ?1",
|
||||
params![account],
|
||||
|row| {
|
||||
let address: String = row.get(0)?;
|
||||
Ok(address)
|
||||
},
|
||||
)?;
|
||||
let address = self
|
||||
.connection
|
||||
.query_row(
|
||||
"SELECT address FROM accounts WHERE id_account = ?1",
|
||||
params![account],
|
||||
|row| {
|
||||
let address: String = row.get(0)?;
|
||||
Ok(address)
|
||||
},
|
||||
)
|
||||
.map_err(wrap_query_no_rows("get_address"))?;
|
||||
log::debug!("-get_address");
|
||||
Ok(address)
|
||||
}
|
||||
|
@ -817,6 +875,12 @@ impl DbAdapter {
|
|||
}
|
||||
|
||||
pub fn truncate_data(&self) -> anyhow::Result<()> {
|
||||
self.truncate_sync_data()?;
|
||||
self.connection.execute("DELETE FROM diversifiers", [])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn truncate_sync_data(&self) -> anyhow::Result<()> {
|
||||
self.connection.execute("DELETE FROM blocks", [])?;
|
||||
self.connection.execute("DELETE FROM contacts", [])?;
|
||||
self.connection.execute("DELETE FROM diversifiers", [])?;
|
||||
|
@ -830,6 +894,14 @@ impl DbAdapter {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_incomplete_scan(&mut self) -> anyhow::Result<()> {
|
||||
let synced_height = self.get_last_sync_height()?;
|
||||
if let Some(synced_height) = synced_height {
|
||||
self.trim_to_height(synced_height, true)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_account(&self, account: u32) -> anyhow::Result<()> {
|
||||
self.connection.execute(
|
||||
"DELETE FROM received_notes WHERE account = ?1",
|
||||
|
@ -851,10 +923,6 @@ impl DbAdapter {
|
|||
.execute("DELETE FROM taddrs WHERE account = ?1", params![account])?;
|
||||
self.connection
|
||||
.execute("DELETE FROM messages WHERE account = ?1", params![account])?;
|
||||
self.connection.execute(
|
||||
"DELETE FROM secret_shares WHERE account = ?1",
|
||||
params![account],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1014,14 +1082,17 @@ impl DbAdapter {
|
|||
// 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)
|
||||
},
|
||||
)?;
|
||||
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],
|
||||
|
@ -1061,7 +1132,7 @@ impl DbAdapter {
|
|||
}
|
||||
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 + 1)?;
|
||||
self.trim_to_height(account_info.height, true)?;
|
||||
Ok(ids)
|
||||
}
|
||||
|
||||
|
|
|
@ -66,12 +66,13 @@ pub async fn sync_async(
|
|||
|
||||
let mut client = connect_lightwalletd(&ld_url).await?;
|
||||
let (start_height, prev_hash, vks) = {
|
||||
let db = DbAdapter::new(coin_type, &db_path)?;
|
||||
let mut db = DbAdapter::new(coin_type, &db_path)?;
|
||||
let height = db.get_db_height()?;
|
||||
let hash = db.get_db_hash(height)?;
|
||||
let vks = db.get_fvks()?;
|
||||
(height, hash, vks)
|
||||
};
|
||||
|
||||
let end_height = get_latest_height(&mut client).await?;
|
||||
let end_height = (end_height - target_height_offset).max(start_height);
|
||||
if start_height >= end_height {
|
||||
|
|
Loading…
Reference in New Issue