zcash-sync/src/db.rs

796 lines
26 KiB
Rust
Raw Normal View History

2021-06-29 00:04:12 -07:00
use crate::chain::{Nf, NfRef};
2021-07-16 01:42:29 -07:00
use crate::db::migration::{get_schema_version, update_schema_version};
use crate::taddr::{derive_tkeys, BIP44_PATH};
use crate::transaction::{Contact, TransactionInfo};
2021-07-27 22:07:20 -07:00
use crate::{CTree, Witness, NETWORK};
2021-06-26 02:52:03 -07:00
use rusqlite::{params, Connection, OptionalExtension, NO_PARAMS};
use std::collections::HashMap;
use zcash_client_backend::encoding::decode_extended_full_viewing_key;
2021-06-26 02:52:03 -07:00
use zcash_primitives::consensus::{NetworkUpgrade, Parameters};
use zcash_primitives::merkle_tree::IncrementalWitness;
2021-07-27 22:07:20 -07:00
use zcash_primitives::sapling::{Diversifier, Node, Note, Rseed, SaplingIvk};
2021-07-16 01:42:29 -07:00
use zcash_primitives::zip32::{DiversifierIndex, ExtendedFullViewingKey};
2021-07-09 22:44:13 -07:00
mod migration;
2021-06-26 02:52:03 -07:00
#[allow(dead_code)]
pub const DEFAULT_DB_PATH: &str = "zec.db";
2021-06-21 17:33:13 -07:00
pub struct DbAdapter {
2021-06-28 21:49:00 -07:00
pub connection: Connection,
2021-06-21 17:33:13 -07:00
}
pub struct ReceivedNote {
2021-06-29 00:04:12 -07:00
pub account: u32,
2021-06-21 17:33:13 -07:00
pub height: u32,
pub output_index: u32,
pub diversifier: Vec<u8>,
pub value: u64,
pub rcm: Vec<u8>,
pub nf: Vec<u8>,
2021-06-24 05:08:20 -07:00
pub spent: Option<u32>,
}
pub struct SpendableNote {
2021-06-28 21:49:00 -07:00
pub id: u32,
2021-06-24 05:08:20 -07:00
pub note: Note,
pub diversifier: Diversifier,
pub witness: IncrementalWitness<Node>,
2021-06-21 17:33:13 -07:00
}
2021-07-27 22:07:20 -07:00
pub struct AccountViewKey {
pub fvk: ExtendedFullViewingKey,
pub ivk: SaplingIvk,
pub viewonly: bool,
}
impl AccountViewKey {
pub fn from_fvk(fvk: &ExtendedFullViewingKey) -> AccountViewKey {
AccountViewKey {
fvk: fvk.clone(),
ivk: fvk.fvk.vk.ivk(),
viewonly: false,
}
}
}
2021-06-21 17:33:13 -07:00
impl DbAdapter {
pub fn new(db_path: &str) -> anyhow::Result<DbAdapter> {
let connection = Connection::open(db_path)?;
connection.execute("PRAGMA synchronous = off", NO_PARAMS)?;
2021-06-26 02:52:03 -07:00
Ok(DbAdapter { connection })
2021-06-21 17:33:13 -07:00
}
2021-07-27 22:07:20 -07:00
pub fn begin_transaction(&self) -> anyhow::Result<()> {
self.connection.execute("BEGIN TRANSACTION", NO_PARAMS)?;
Ok(())
}
pub fn commit(&self) -> anyhow::Result<()> {
self.connection.execute("COMMIT", NO_PARAMS)?;
Ok(())
}
2021-06-21 17:33:13 -07:00
pub fn init_db(&self) -> anyhow::Result<()> {
2021-07-09 22:44:13 -07:00
self.connection.execute(
"CREATE TABLE IF NOT EXISTS schema_version (
id INTEGER PRIMARY KEY NOT NULL,
version INTEGER NOT NULL)",
NO_PARAMS,
)?;
let version = get_schema_version(&self.connection)?;
2021-07-18 08:57:43 -07:00
if version < 1 {
2021-07-16 01:42:29 -07:00
self.connection.execute(
"CREATE TABLE IF NOT EXISTS accounts (
2021-06-24 05:08:20 -07:00
id_account INTEGER PRIMARY KEY,
2021-06-29 00:04:12 -07:00
name TEXT NOT NULL,
seed TEXT,
sk TEXT,
ivk TEXT NOT NULL UNIQUE,
2021-06-26 02:52:03 -07:00
address TEXT NOT NULL)",
2021-07-16 01:42:29 -07:00
NO_PARAMS,
)?;
2021-06-24 05:08:20 -07:00
2021-07-16 01:42:29 -07:00
self.connection.execute(
"CREATE TABLE IF NOT EXISTS blocks (
2021-06-21 17:33:13 -07:00
height INTEGER PRIMARY KEY,
hash BLOB NOT NULL,
2021-06-26 02:52:03 -07:00
timestamp INTEGER NOT NULL,
sapling_tree BLOB NOT NULL)",
2021-07-16 01:42:29 -07:00
NO_PARAMS,
)?;
2021-06-21 17:33:13 -07:00
2021-07-16 01:42:29 -07:00
self.connection.execute(
"CREATE TABLE IF NOT EXISTS transactions (
2021-06-21 17:33:13 -07:00
id_tx INTEGER PRIMARY KEY,
2021-06-29 00:04:12 -07:00
account INTEGER NOT NULL,
2021-07-09 22:44:13 -07:00
txid BLOB NOT NULL,
2021-06-26 02:52:03 -07:00
height INTEGER NOT NULL,
timestamp INTEGER NOT NULL,
value INTEGER NOT NULL,
2021-07-09 22:44:13 -07:00
address TEXT,
memo TEXT,
tx_index INTEGER,
CONSTRAINT tx_account UNIQUE (height, tx_index, account))",
2021-07-16 01:42:29 -07:00
NO_PARAMS,
)?;
2021-06-21 17:33:13 -07:00
2021-07-16 01:42:29 -07:00
self.connection.execute(
"CREATE TABLE IF NOT EXISTS received_notes (
2021-06-21 17:33:13 -07:00
id_note INTEGER PRIMARY KEY,
2021-06-29 00:04:12 -07:00
account INTEGER NOT NULL,
2021-06-21 17:33:13 -07:00
position INTEGER NOT NULL,
tx INTEGER NOT NULL,
height INTEGER NOT NULL,
output_index INTEGER NOT NULL,
diversifier BLOB NOT NULL,
value INTEGER NOT NULL,
rcm BLOB NOT NULL,
nf BLOB NOT NULL UNIQUE,
spent INTEGER,
2021-06-26 02:52:03 -07:00
CONSTRAINT tx_output UNIQUE (tx, output_index))",
2021-07-16 01:42:29 -07:00
NO_PARAMS,
)?;
2021-06-21 17:33:13 -07:00
2021-07-16 01:42:29 -07:00
self.connection.execute(
"CREATE TABLE IF NOT EXISTS sapling_witnesses (
2021-06-21 17:33:13 -07:00
id_witness INTEGER PRIMARY KEY,
note INTEGER NOT NULL,
height INTEGER NOT NULL,
witness BLOB NOT NULL,
2021-06-26 02:52:03 -07:00
CONSTRAINT witness_height UNIQUE (note, height))",
2021-07-16 01:42:29 -07:00
NO_PARAMS,
)?;
2021-06-24 05:08:20 -07:00
2021-07-16 01:42:29 -07:00
self.connection.execute(
"CREATE TABLE IF NOT EXISTS diversifiers (
2021-07-07 21:22:34 -07:00
account INTEGER PRIMARY KEY NOT NULL,
diversifier_index BLOB NOT NULL)",
2021-07-16 01:42:29 -07:00
NO_PARAMS,
)?;
2021-07-07 21:22:34 -07:00
2021-07-16 01:42:29 -07:00
self.connection.execute(
"CREATE TABLE IF NOT EXISTS taddrs (
2021-07-09 06:33:05 -07:00
account INTEGER PRIMARY KEY NOT NULL,
sk TEXT NOT NULL,
address TEXT NOT NULL)",
2021-07-16 01:42:29 -07:00
NO_PARAMS,
)?;
}
2021-07-18 08:57:43 -07:00
if version < 2 {
2021-07-16 01:42:29 -07:00
self.connection
.execute("ALTER TABLE received_notes ADD excluded BOOL", NO_PARAMS)?;
}
2021-07-09 06:33:05 -07:00
2021-07-18 08:57:43 -07:00
if version < 3 {
self.connection.execute(
"CREATE TABLE IF NOT EXISTS contacts (
account INTEGER NOT NULL,
name TEXT NOT NULL,
address TEXT NOT NULL,
PRIMARY KEY (account, address))",
NO_PARAMS,
2021-07-18 08:57:43 -07:00
)?;
}
update_schema_version(&self.connection, 3)?;
2021-07-09 22:44:13 -07:00
2021-06-24 05:08:20 -07:00
Ok(())
}
2021-06-21 17:33:13 -07:00
2021-07-16 01:42:29 -07:00
pub fn store_account(
&self,
name: &str,
seed: Option<&str>,
sk: Option<&str>,
ivk: &str,
address: &str,
) -> anyhow::Result<u32> {
2021-06-26 02:52:03 -07:00
self.connection.execute(
2021-06-29 00:04:12 -07:00
"INSERT INTO accounts(name, seed, sk, ivk, address) VALUES (?1, ?2, ?3, ?4, ?5)
2021-06-26 02:52:03 -07:00
ON CONFLICT DO NOTHING",
2021-06-29 00:04:12 -07:00
params![name, seed, sk, ivk, address],
2021-06-26 02:52:03 -07:00
)?;
2021-06-29 00:04:12 -07:00
let id_tx: u32 = self.connection.query_row(
2021-07-27 22:07:20 -07:00
"SELECT id_account FROM accounts WHERE ivk = ?1",
params![ivk],
2021-06-29 00:04:12 -07:00
|row| row.get(0),
)?;
Ok(id_tx)
2021-06-21 17:33:13 -07:00
}
2021-07-27 22:07:20 -07:00
pub fn get_fvks(&self) -> anyhow::Result<HashMap<u32, AccountViewKey>> {
2021-07-16 01:42:29 -07:00
let mut statement = self
.connection
2021-07-27 22:07:20 -07:00
.prepare("SELECT id_account, ivk, sk FROM accounts")?;
2021-06-29 00:04:12 -07:00
let rows = statement.query_map(NO_PARAMS, |row| {
let account: u32 = row.get(0)?;
let ivk: String = row.get(1)?;
2021-07-27 22:07:20 -07:00
let sk: Option<String> = row.get(2)?;
let fvk = decode_extended_full_viewing_key(
NETWORK.hrp_sapling_extended_full_viewing_key(),
&ivk,
)
.unwrap()
.unwrap();
2021-07-27 22:07:20 -07:00
let ivk = fvk.fvk.vk.ivk();
Ok((
account,
AccountViewKey {
fvk,
ivk,
viewonly: sk.is_none(),
},
))
2021-06-29 00:04:12 -07:00
})?;
2021-07-27 22:07:20 -07:00
let mut fvks: HashMap<u32, AccountViewKey> = HashMap::new();
2021-06-29 00:04:12 -07:00
for r in rows {
let row = r?;
fvks.insert(row.0, row.1);
}
Ok(fvks)
2021-06-26 02:52:03 -07:00
}
2021-06-21 17:33:13 -07:00
pub fn trim_to_height(&mut self, height: u32) -> anyhow::Result<()> {
let tx = self.connection.transaction()?;
tx.execute("DELETE FROM blocks WHERE height >= ?1", params![height])?;
2021-06-26 02:52:03 -07:00
tx.execute(
"DELETE FROM sapling_witnesses WHERE height >= ?1",
params![height],
)?;
tx.execute(
"DELETE FROM received_notes WHERE height >= ?1",
params![height],
)?;
tx.execute(
"DELETE FROM transactions WHERE height >= ?1",
params![height],
)?;
2021-06-21 17:33:13 -07:00
tx.commit()?;
Ok(())
}
pub fn get_txhash(&self, id_tx: u32) -> anyhow::Result<(u32, u32, Vec<u8>, String)> {
let (account, height, tx_hash, ivk) = self.connection.query_row(
"SELECT account, height, txid, ivk FROM transactions t, accounts a WHERE id_tx = ?1 AND t.account = a.id_account",
2021-07-16 01:42:29 -07:00
params![id_tx],
2021-07-09 22:44:13 -07:00
|row| {
let account: u32 = row.get(0)?;
let height: u32 = row.get(1)?;
let tx_hash: Vec<u8> = row.get(2)?;
let ivk: String = row.get(3)?;
Ok((account, height, tx_hash, ivk))
2021-07-16 01:42:29 -07:00
},
)?;
Ok((account, height, tx_hash, ivk))
2021-07-09 22:44:13 -07:00
}
2021-06-26 02:52:03 -07:00
pub fn store_block(
&self,
height: u32,
hash: &[u8],
timestamp: u32,
tree: &CTree,
) -> anyhow::Result<()> {
log::debug!("+block");
2021-06-21 17:33:13 -07:00
let mut bb: Vec<u8> = vec![];
tree.write(&mut bb)?;
2021-06-26 02:52:03 -07:00
self.connection.execute(
"INSERT INTO blocks(height, hash, timestamp, sapling_tree)
VALUES (?1, ?2, ?3, ?4)
ON CONFLICT DO NOTHING",
params![height, hash, timestamp, &bb],
)?;
log::debug!("-block");
2021-06-21 17:33:13 -07:00
Ok(())
}
2021-06-26 02:52:03 -07:00
pub fn store_transaction(
&self,
txid: &[u8],
2021-06-29 00:04:12 -07:00
account: u32,
2021-06-26 02:52:03 -07:00
height: u32,
timestamp: u32,
tx_index: u32,
) -> anyhow::Result<u32> {
log::debug!("+transaction");
self.connection.execute(
2021-06-29 00:04:12 -07:00
"INSERT INTO transactions(account, txid, height, timestamp, tx_index, value)
VALUES (?1, ?2, ?3, ?4, ?5, 0)
2021-06-26 02:52:03 -07:00
ON CONFLICT DO NOTHING",
2021-06-29 00:04:12 -07:00
params![account, txid, height, timestamp, tx_index],
2021-06-26 02:52:03 -07:00
)?;
let id_tx: u32 = self.connection.query_row(
2021-07-11 08:42:52 -07:00
"SELECT id_tx FROM transactions WHERE account = ?1 AND txid = ?2",
params![account, txid],
2021-06-26 02:52:03 -07:00
|row| row.get(0),
)?;
log::debug!("-transaction {}", id_tx);
2021-06-21 17:33:13 -07:00
Ok(id_tx)
}
2021-06-26 02:52:03 -07:00
pub fn store_received_note(
&self,
note: &ReceivedNote,
id_tx: u32,
position: usize,
) -> anyhow::Result<u32> {
log::debug!("+received_note {}", id_tx);
2021-06-29 00:04:12 -07:00
self.connection.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])?;
2021-06-26 02:52:03 -07:00
let id_note: u32 = self.connection.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),
)?;
log::debug!("-received_note");
2021-06-21 17:33:13 -07:00
Ok(id_note)
}
2021-06-26 02:52:03 -07:00
pub fn store_witnesses(
&self,
witness: &Witness,
height: u32,
id_note: u32,
) -> anyhow::Result<()> {
log::debug!("+witnesses");
2021-06-21 17:33:13 -07:00
let mut bb: Vec<u8> = vec![];
witness.write(&mut bb)?;
2021-06-26 02:52:03 -07:00
self.connection.execute(
"INSERT INTO sapling_witnesses(note, height, witness) VALUES (?1, ?2, ?3)
ON CONFLICT DO NOTHING",
params![id_note, height, bb],
)?;
log::debug!("-witnesses");
Ok(())
}
2021-07-09 22:44:13 -07:00
pub fn store_tx_metadata(&self, id_tx: u32, tx_info: &TransactionInfo) -> anyhow::Result<()> {
self.connection.execute(
2021-07-16 01:42:29 -07:00
"UPDATE transactions SET address = ?1, memo = ?2 WHERE id_tx = ?3",
2021-07-18 08:57:43 -07:00
params![tx_info.address, &tx_info.memo, id_tx],
2021-07-09 22:44:13 -07:00
)?;
Ok(())
}
2021-06-26 02:52:03 -07:00
pub fn add_value(&self, id_tx: u32, value: i64) -> anyhow::Result<()> {
self.connection.execute(
"UPDATE transactions SET value = value + ?2 WHERE id_tx = ?1",
params![id_tx, value],
)?;
2021-06-21 17:33:13 -07:00
Ok(())
}
2021-06-29 00:04:12 -07:00
pub fn get_received_note_value(&self, nf: &Nf) -> anyhow::Result<(u32, i64)> {
let (account, value) = self.connection.query_row(
"SELECT account, value FROM received_notes WHERE nf = ?1",
2021-06-26 02:52:03 -07:00
params![nf.0.to_vec()],
2021-06-29 00:04:12 -07:00
|row| {
let account: u32 = row.get(0)?;
let value: i64 = row.get(1)?;
Ok((account, value))
},
2021-06-26 02:52:03 -07:00
)?;
2021-06-29 00:04:12 -07:00
Ok((account, value))
2021-06-26 02:52:03 -07:00
}
2021-06-29 00:04:12 -07:00
pub fn get_balance(&self, account: u32) -> anyhow::Result<u64> {
2021-06-26 02:52:03 -07:00
let balance: Option<i64> = self.connection.query_row(
2021-06-29 00:04:12 -07:00
"SELECT SUM(value) FROM received_notes WHERE (spent IS NULL OR spent = 0) AND account = ?1",
params![account],
2021-06-26 02:52:03 -07:00
|row| row.get(0),
)?;
Ok(balance.unwrap_or(0) as u64)
}
2021-06-24 05:08:20 -07:00
pub fn get_last_sync_height(&self) -> anyhow::Result<Option<u32>> {
2021-06-26 02:52:03 -07:00
let height: Option<u32> =
self.connection
.query_row("SELECT MAX(height) FROM blocks", NO_PARAMS, |row| {
row.get(0)
})?;
2021-06-24 05:08:20 -07:00
Ok(height)
}
pub fn get_db_height(&self) -> anyhow::Result<u32> {
let height: u32 = self.get_last_sync_height()?.unwrap_or_else(|| {
crate::NETWORK
.activation_height(NetworkUpgrade::Sapling)
.unwrap()
.into()
});
2021-06-21 17:33:13 -07:00
Ok(height)
}
2021-06-26 02:52:03 -07:00
pub fn get_db_hash(&self, height: u32) -> anyhow::Result<Option<[u8; 32]>> {
2021-07-16 01:42:29 -07:00
let hash: Option<Vec<u8>> = self
.connection
.query_row(
"SELECT hash FROM blocks WHERE height = ?1",
params![height],
|row| row.get(0),
)
.optional()?;
2021-06-26 02:52:03 -07:00
Ok(hash.map(|h| {
let mut hash = [0u8; 32];
hash.copy_from_slice(&h);
hash
}))
}
2021-06-21 17:33:13 -07:00
pub fn get_tree(&self) -> anyhow::Result<(CTree, Vec<Witness>)> {
let res = self.connection.query_row(
"SELECT height, sapling_tree FROM blocks WHERE height = (SELECT MAX(height) FROM blocks)",
2021-06-24 05:08:20 -07:00
NO_PARAMS, |row| {
2021-06-21 17:33:13 -07:00
let height: u32 = row.get(0)?;
let tree: Vec<u8> = row.get(1)?;
Ok((height, tree))
}).optional()?;
Ok(match res {
Some((height, tree)) => {
let tree = CTree::read(&*tree)?;
let mut statement = self.connection.prepare(
2021-06-29 00:04:12 -07:00
"SELECT id_note, witness FROM sapling_witnesses w, received_notes n WHERE w.height = ?1 AND w.note = n.id_note AND (n.spent IS NULL OR n.spent = 0)")?;
2021-06-21 17:33:13 -07:00
let ws = statement.query_map(params![height], |row| {
let id_note: u32 = row.get(0)?;
2021-06-28 21:49:00 -07:00
let witness: Vec<u8> = row.get(1)?;
Ok(Witness::read(id_note, &*witness).unwrap())
2021-06-21 17:33:13 -07:00
})?;
let mut witnesses: Vec<Witness> = vec![];
for w in ws {
witnesses.push(w?);
}
(tree, witnesses)
2021-06-26 02:52:03 -07:00
}
None => (CTree::new(), vec![]),
2021-06-21 17:33:13 -07:00
})
}
2021-06-24 05:08:20 -07:00
2021-06-29 00:04:12 -07:00
pub fn get_nullifiers(&self) -> anyhow::Result<HashMap<Nf, NfRef>> {
2021-07-16 01:42:29 -07:00
let mut statement = self.connection.prepare(
"SELECT id_note, account, nf FROM received_notes WHERE spent IS NULL OR spent = 0",
)?;
2021-06-24 05:08:20 -07:00
let nfs_res = statement.query_map(NO_PARAMS, |row| {
let id_note: u32 = row.get(0)?;
2021-06-29 00:04:12 -07:00
let account: u32 = row.get(1)?;
let nf_vec: Vec<u8> = row.get(2)?;
2021-06-24 05:08:20 -07:00
let mut nf = [0u8; 32];
nf.clone_from_slice(&nf_vec);
2021-07-16 01:42:29 -07:00
let nf_ref = NfRef { id_note, account };
2021-06-29 00:04:12 -07:00
Ok((nf_ref, nf))
2021-06-24 05:08:20 -07:00
})?;
2021-06-29 00:04:12 -07:00
let mut nfs: HashMap<Nf, NfRef> = HashMap::new();
2021-06-24 05:08:20 -07:00
for n in nfs_res {
let n = n?;
nfs.insert(Nf(n.1), n.0);
}
Ok(nfs)
}
2021-07-16 01:42:29 -07:00
pub fn get_nullifier_amounts(
&self,
account: u32,
unspent_only: bool,
) -> anyhow::Result<HashMap<Vec<u8>, u64>> {
2021-07-09 22:44:13 -07:00
let mut sql = "SELECT value, nf FROM received_notes WHERE account = ?1".to_string();
if unspent_only {
sql += "AND (spent IS NULL OR spent = 0)";
}
2021-07-16 01:42:29 -07:00
let mut statement = self.connection.prepare(&sql)?;
2021-06-29 00:04:12 -07:00
let nfs_res = statement.query_map(params![account], |row| {
2021-06-26 02:52:03 -07:00
let amount: i64 = row.get(0)?;
let nf: Vec<u8> = row.get(1)?;
Ok((amount, nf))
})?;
let mut nfs: HashMap<Vec<u8>, u64> = HashMap::new();
for n in nfs_res {
let n = n?;
nfs.insert(n.1, n.0 as u64);
}
Ok(nfs)
}
2021-07-11 08:42:52 -07:00
pub fn get_nullifiers_raw(&self) -> anyhow::Result<Vec<(u32, u64, Vec<u8>)>> {
2021-07-16 01:42:29 -07:00
let mut statement = self
.connection
.prepare("SELECT account, value, nf FROM received_notes")?;
2021-07-11 08:42:52 -07:00
let res = statement.query_map(NO_PARAMS, |row| {
let account: u32 = row.get(0)?;
let amount: i64 = row.get(1)?;
let nf: Vec<u8> = row.get(2)?;
Ok((account, amount as u64, nf))
})?;
let mut v: Vec<(u32, u64, Vec<u8>)> = vec![];
for r in res {
v.push(r?);
}
Ok(v)
}
2021-06-26 02:52:03 -07:00
pub fn get_spendable_notes(
&self,
2021-06-29 00:04:12 -07:00
account: u32,
2021-06-26 02:52:03 -07:00
anchor_height: u32,
fvk: &ExtendedFullViewingKey,
) -> anyhow::Result<Vec<SpendableNote>> {
2021-06-24 05:08:20 -07:00
let mut statement = self.connection.prepare(
2021-06-29 00:04:12 -07:00
"SELECT id_note, diversifier, value, rcm, witness FROM received_notes r, sapling_witnesses w WHERE spent IS NULL AND account = ?2
2021-07-16 01:42:29 -07:00
AND (r.excluded IS NULL OR NOT r.excluded) AND w.height = (
2021-06-24 05:08:20 -07:00
SELECT MAX(height) FROM sapling_witnesses WHERE height <= ?1
) AND r.id_note = w.note")?;
2021-06-29 00:04:12 -07:00
let notes = statement.query_map(params![anchor_height, account], |row| {
2021-06-28 21:49:00 -07:00
let id_note: u32 = row.get(0)?;
let diversifier: Vec<u8> = row.get(1)?;
let value: i64 = row.get(2)?;
let rcm: Vec<u8> = row.get(3)?;
let witness: Vec<u8> = row.get(4)?;
2021-06-24 05:08:20 -07:00
let mut diversifer_bytes = [0u8; 11];
diversifer_bytes.copy_from_slice(&diversifier);
let diversifier = Diversifier(diversifer_bytes);
let mut rcm_bytes = [0u8; 32];
rcm_bytes.copy_from_slice(&rcm);
let rcm = jubjub::Fr::from_bytes(&rcm_bytes).unwrap();
let rseed = Rseed::BeforeZip212(rcm);
let witness = IncrementalWitness::<Node>::read(&*witness).unwrap();
let pa = fvk.fvk.vk.to_payment_address(diversifier).unwrap();
let note = pa.create_note(value as u64, rseed).unwrap();
Ok(SpendableNote {
2021-06-28 21:49:00 -07:00
id: id_note,
2021-06-24 05:08:20 -07:00
note,
diversifier,
2021-06-26 02:52:03 -07:00
witness,
2021-06-24 05:08:20 -07:00
})
})?;
let mut spendable_notes: Vec<SpendableNote> = vec![];
for n in notes {
spendable_notes.push(n?);
}
Ok(spendable_notes)
}
pub fn mark_spent(&self, id: u32, height: u32) -> anyhow::Result<()> {
2021-06-26 02:52:03 -07:00
log::debug!("+mark_spent");
self.connection.execute(
"UPDATE received_notes SET spent = ?1 WHERE id_note = ?2",
params![height, id],
)?;
log::debug!("-mark_spent");
2021-06-24 05:08:20 -07:00
Ok(())
}
2021-07-16 01:42:29 -07:00
pub fn purge_old_witnesses(&self, height: u32) -> anyhow::Result<()> {
log::debug!("+purge_old_witnesses");
self.connection.execute(
"DELETE FROM sapling_witnesses WHERE height < ?1",
params![height],
)?;
self.connection
.execute("DELETE FROM blocks WHERE height < ?1", params![height])?;
log::debug!("-purge_old_witnesses");
Ok(())
}
2021-07-18 08:57:43 -07:00
pub fn store_contact(&self, account: u32, contact: &Contact) -> anyhow::Result<()> {
2021-07-19 01:17:05 -07:00
log::info!("{:?}", contact);
if contact.name.is_empty() {
self.connection.execute(
"DELETE FROM contacts WHERE account = ?1 AND address = ?2",
params![account, contact.address],
)?;
} else {
self.connection.execute(
"INSERT INTO contacts(account, name, address)
2021-07-19 01:17:05 -07:00
VALUES (?1, ?2, ?3) ON CONFLICT (account, address) DO UPDATE SET
name = excluded.name",
params![account, &contact.name, &contact.address],
)?;
2021-07-19 01:17:05 -07:00
}
2021-07-18 08:57:43 -07:00
Ok(())
}
2021-07-16 01:42:29 -07:00
pub fn get_backup(
&self,
account: u32,
) -> anyhow::Result<(Option<String>, Option<String>, String)> {
2021-06-29 00:04:12 -07:00
log::debug!("+get_backup");
let (seed, sk, ivk) = self.connection.query_row(
"SELECT seed, sk, ivk FROM accounts WHERE id_account = ?1",
2021-06-28 21:49:00 -07:00
params![account],
|row| {
2021-06-29 00:04:12 -07:00
let seed: Option<String> = row.get(0)?;
let sk: Option<String> = row.get(0)?;
2021-06-28 21:49:00 -07:00
let ivk: String = row.get(0)?;
2021-06-29 00:04:12 -07:00
Ok((seed, sk, ivk))
2021-06-28 21:49:00 -07:00
},
)?;
2021-06-29 00:04:12 -07:00
log::debug!("-get_backup");
Ok((seed, sk, ivk))
2021-06-28 21:49:00 -07:00
}
2021-07-09 06:33:05 -07:00
pub fn get_seed(&self, account: u32) -> anyhow::Result<Option<String>> {
log::info!("+get_seed");
let seed = self.connection.query_row(
"SELECT seed FROM accounts WHERE id_account = ?1",
params![account],
|row| {
let sk: Option<String> = row.get(0)?;
Ok(sk)
},
)?;
2021-07-09 06:33:05 -07:00
log::info!("-get_seed");
Ok(seed)
}
2021-06-24 05:08:20 -07:00
pub fn get_sk(&self, account: u32) -> anyhow::Result<String> {
2021-06-29 00:04:12 -07:00
log::info!("+get_sk");
let sk = self.connection.query_row(
2021-06-26 02:52:03 -07:00
"SELECT sk FROM accounts WHERE id_account = ?1",
params![account],
|row| {
2021-06-29 00:04:12 -07:00
let sk: String = row.get(0)?;
Ok(sk)
2021-06-26 02:52:03 -07:00
},
)?;
2021-06-29 00:04:12 -07:00
log::info!("-get_sk");
Ok(sk)
2021-06-24 05:08:20 -07:00
}
pub fn get_ivk(&self, account: u32) -> anyhow::Result<String> {
2021-06-26 02:52:03 -07:00
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)
},
)?;
log::debug!("-get_ivk");
2021-06-24 05:08:20 -07:00
Ok(ivk)
}
2021-07-07 21:22:34 -07:00
2021-07-09 06:33:05 -07:00
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)
},
)?;
log::debug!("-get_address");
Ok(address)
}
2021-07-07 21:22:34 -07:00
pub fn get_diversifier(&self, account: u32) -> anyhow::Result<DiversifierIndex> {
2021-07-16 01:42:29 -07:00
let diversifier_index = self
.connection
.query_row(
"SELECT diversifier_index FROM diversifiers WHERE account = ?1",
params![account],
|row| {
let d: Vec<u8> = row.get(0)?;
let mut div = [0u8; 11];
div.copy_from_slice(&d);
Ok(div)
},
)
.optional()?
.unwrap_or_else(|| [0u8; 11]);
2021-07-07 21:22:34 -07:00
Ok(DiversifierIndex(diversifier_index))
}
2021-07-16 01:42:29 -07:00
pub fn store_diversifier(
&self,
account: u32,
diversifier_index: &DiversifierIndex,
) -> anyhow::Result<()> {
2021-07-07 21:22:34 -07:00
let diversifier_bytes = diversifier_index.0.to_vec();
self.connection.execute(
"INSERT INTO diversifiers(account, diversifier_index) VALUES (?1, ?2) ON CONFLICT \
(account) DO UPDATE SET diversifier_index = excluded.diversifier_index",
2021-07-16 01:42:29 -07:00
params![account, diversifier_bytes],
)?;
2021-07-07 21:22:34 -07:00
Ok(())
}
2021-07-09 06:33:05 -07:00
pub fn get_taddr(&self, account: u32) -> anyhow::Result<Option<String>> {
2021-07-16 01:42:29 -07:00
let address = self
.connection
.query_row(
"SELECT address FROM taddrs WHERE account = ?1",
params![account],
|row| {
let address: String = row.get(0)?;
Ok(address)
},
)
.optional()?;
2021-07-09 06:33:05 -07:00
Ok(address)
}
pub fn get_tsk(&self, account: u32) -> anyhow::Result<String> {
let sk = self.connection.query_row(
"SELECT sk FROM taddrs WHERE account = ?1",
params![account],
|row| {
let address: String = row.get(0)?;
Ok(address)
2021-07-16 01:42:29 -07:00
},
)?;
2021-07-09 06:33:05 -07:00
Ok(sk)
}
pub fn create_taddr(&self, account: u32) -> anyhow::Result<()> {
let seed = self.get_seed(account)?;
if let Some(seed) = seed {
let (sk, address) = derive_tkeys(&seed, BIP44_PATH)?;
2021-07-16 01:42:29 -07:00
self.connection.execute(
"INSERT INTO taddrs(account, sk, address) VALUES (?1, ?2, ?3) \
ON CONFLICT DO NOTHING",
params![account, &sk, &address],
)?;
2021-07-09 06:33:05 -07:00
}
Ok(())
}
2021-06-21 17:33:13 -07:00
}
#[cfg(test)]
mod tests {
2021-06-26 02:52:03 -07:00
use crate::db::{DbAdapter, ReceivedNote, DEFAULT_DB_PATH};
use crate::{CTree, Witness};
2021-06-21 17:33:13 -07:00
#[test]
fn test_db() {
2021-06-26 02:52:03 -07:00
let mut db = DbAdapter::new(DEFAULT_DB_PATH).unwrap();
2021-06-21 17:33:13 -07:00
db.init_db().unwrap();
db.trim_to_height(0).unwrap();
2021-06-26 02:52:03 -07:00
db.store_block(1, &[0u8; 32], 0, &CTree::new()).unwrap();
2021-06-29 00:04:12 -07:00
let id_tx = db.store_transaction(&[0; 32], 1, 1, 0, 20).unwrap();
2021-06-26 02:52:03 -07:00
db.store_received_note(
&ReceivedNote {
2021-06-29 00:04:12 -07:00
account: 1,
2021-06-26 02:52:03 -07:00
height: 1,
output_index: 0,
diversifier: vec![],
value: 0,
rcm: vec![],
nf: vec![],
spent: None,
},
id_tx,
5,
)
2021-07-16 01:42:29 -07:00
.unwrap();
2021-06-21 17:33:13 -07:00
let witness = Witness {
position: 10,
id_note: 0,
note: None,
tree: CTree::new(),
filled: vec![],
cursor: CTree::new(),
};
db.store_witnesses(&witness, 1000, 1).unwrap();
}
#[test]
fn test_balance() {
2021-06-26 02:52:03 -07:00
let db = DbAdapter::new(DEFAULT_DB_PATH).unwrap();
2021-06-29 00:04:12 -07:00
let balance = db.get_balance(1).unwrap();
2021-06-21 17:33:13 -07:00
println!("{}", balance);
}
}