2021-06-29 00:04:12 -07:00
use crate ::chain ::{ Nf , NfRef } ;
2021-07-16 01:42:29 -07:00
use crate ::taddr ::{ derive_tkeys , BIP44_PATH } ;
2021-07-30 01:11:44 -07:00
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 ;
2021-07-30 01:11:44 -07:00
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-08-09 07:13:10 -07:00
use chrono ::NaiveDateTime ;
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 ) ? ;
2021-07-30 01:11:44 -07:00
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-08-09 07:13:10 -07:00
migration ::init_db ( & self . connection ) ? ;
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 ) ? ;
2021-07-30 01:11:44 -07:00
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 ( ) ;
2021-07-30 01:11:44 -07:00
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 ( ( ) )
}
2021-07-30 01:11:44 -07:00
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 ) ? ;
2021-07-30 01:11:44 -07:00
let ivk : String = row . get ( 3 ) ? ;
Ok ( ( account , height , tx_hash , ivk ) )
2021-07-16 01:42:29 -07:00
} ,
) ? ;
2021-07-30 01:11:44 -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 = [ 0 u8 ; 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 = [ 0 u8 ; 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 = [ 0 u8 ; 11 ] ;
diversifer_bytes . copy_from_slice ( & diversifier ) ;
let diversifier = Diversifier ( diversifer_bytes ) ;
let mut rcm_bytes = [ 0 u8 ; 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-08-10 04:51:39 -07:00
pub fn store_contact ( & self , contact : & Contact ) -> anyhow ::Result < ( ) > {
2021-07-19 01:17:05 -07:00
log ::info! ( " {:?} " , contact ) ;
if contact . name . is_empty ( ) {
2021-07-30 01:11:44 -07:00
self . connection . execute (
" DELETE FROM contacts WHERE account = ?1 AND address = ?2 " ,
2021-08-10 04:51:39 -07:00
params! [ contact . account , contact . address ] ,
2021-07-30 01:11:44 -07:00
) ? ;
} 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
2021-07-30 01:11:44 -07:00
name = excluded . name " ,
2021-08-10 04:51:39 -07:00
params! [ contact . account , & contact . name , & contact . address ] ,
2021-07-30 01:11:44 -07:00
) ? ;
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 " ) ;
2021-07-30 01:11:44 -07:00
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 = [ 0 u8 ; 11 ] ;
div . copy_from_slice ( & d ) ;
Ok ( div )
} ,
)
. optional ( ) ?
. unwrap_or_else ( | | [ 0 u8 ; 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-08-09 07:13:10 -07:00
pub fn get_missing_prices_timestamp ( & self , currency : & str ) -> anyhow ::Result < Vec < i64 > > {
let mut statement = self . connection . prepare (
" WITH t AS (SELECT timestamp, timestamp/86400 AS day FROM transactions), p AS (SELECT price, timestamp/86400 AS day FROM historical_prices WHERE currency = ?1) \
SELECT t . timestamp FROM t LEFT JOIN p ON t . day = p . day WHERE p . price IS NULL " )?;
let res = statement . query_map ( params! [ currency ] , | row | {
let timestamp : i64 = row . get ( 0 ) ? ;
Ok ( timestamp )
} ) ? ;
let mut timestamps : Vec < i64 > = vec! [ ] ;
for ts in res {
let ts = NaiveDateTime ::from_timestamp ( ts ? , 0 ) ;
let ts_date = ts . date ( ) . and_hms ( 0 , 0 , 0 ) ; // at midnight
timestamps . push ( ts_date . timestamp ( ) ) ;
}
timestamps . sort ( ) ;
timestamps . dedup ( ) ;
Ok ( timestamps )
}
pub fn store_historical_prices ( & mut self , prices : Vec < ( i64 , f64 ) > , currency : & str ) -> anyhow ::Result < ( ) > {
let db_transaction = self . connection . transaction ( ) ? ;
{
let mut statement = db_transaction . prepare ( " INSERT INTO historical_prices(timestamp, price, currency) VALUES (?1, ?2, ?3) " ) ? ;
for ( ts , px ) in prices {
statement . execute ( params! [ ts , px , currency ] ) ? ;
}
}
db_transaction . commit ( ) ? ;
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 , & [ 0 u8 ; 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 ) ;
}
}