Add address retrieval.

This commit is contained in:
Kris Nuttycombe 2020-08-05 17:01:22 -06:00
parent b72251ee28
commit 70de11dd32
6 changed files with 114 additions and 45 deletions

View File

@ -1,6 +1,7 @@
use zcash_primitives::{
block::BlockHash,
consensus::{self, BlockHeight},
primitives::PaymentAddress,
//merkle_tree::{CommitmentTree, IncrementalWitness},
//sapling::Node,
//transaction::{
@ -18,23 +19,25 @@ pub mod error;
pub trait DBOps {
type Error;
type Account;
// type TxRef; // Backend-specific transaction handle
// type NoteRef; // Backend-specific note identifier`
fn init_db(&self) -> Result<(), Self::Error>;
fn init_accounts<P: consensus::Parameters>(
fn init_account_storage<P: consensus::Parameters>(
&self,
params: &P,
extfvks: &[ExtendedFullViewingKey],
) -> Result<(), Self::Error>;
// fn init_blocks(
// height: i32,
// hash: BlockHash,
// time: u32,
// sapling_tree: &[u8],
// ) -> Result<(), Self::Error>;
fn init_block_storage(
&self,
height: BlockHeight,
hash: BlockHash,
time: u32, //TODO: Newtype!
sapling_tree: &[u8], //TODO: Newtype!
) -> Result<(), Self::Error>;
fn block_height_extrema(&self) -> Result<Option<(BlockHeight, BlockHeight)>, Self::Error>;
@ -46,8 +49,12 @@ pub trait DBOps {
block_height: BlockHeight,
) -> Result<(), Self::Error>;
// fn get_address(account: Account) -> Result<String, Self::Error>;
//
fn get_address<P: consensus::Parameters>(
&self,
params: &P,
account: Self::Account,
) -> Result<Option<PaymentAddress>, Self::Error>;
// fn get_balance(account: Account) -> Result<Amount, Self::Error>;
//
// fn get_verified_balance(account: Account) -> Result<Amount, Self::Error>;

View File

@ -158,8 +158,8 @@ pub fn block_height_extrema(
Ok(Some((min_height.into(), max_height.into())))
},
)
// cannot use .optional() here because the result of a failed group
// operation is an error, not an empty row.
//.optional() doesn't work here because a failed aggregate function
//produces a runtime error, not an empty set of rows.
.or(Ok(None))
}

View File

@ -1,11 +1,14 @@
//! Functions for initializing the various databases.
use rusqlite::{types::ToSql, NO_PARAMS};
use zcash_client_backend::encoding::encode_extended_full_viewing_key;
use zcash_primitives::{block::BlockHash, consensus, zip32::ExtendedFullViewingKey};
use zcash_primitives::{
block::BlockHash,
consensus::{self, BlockHeight},
zip32::ExtendedFullViewingKey,
};
use zcash_client_backend::data_api::error::Error;
use zcash_client_backend::{data_api::error::Error, encoding::encode_extended_full_viewing_key};
use crate::{address_from_extfvk, error::SqliteClientError, CacheConnection, DataConnection};
@ -178,18 +181,20 @@ pub fn init_accounts_table<P: consensus::Parameters>(
// Insert accounts atomically
data.0.execute("BEGIN IMMEDIATE", NO_PARAMS)?;
for (account, extfvk) in extfvks.iter().enumerate() {
let address = address_from_extfvk(params, extfvk);
let extfvk = encode_extended_full_viewing_key(
let extfvk_str = encode_extended_full_viewing_key(
params.hrp_sapling_extended_full_viewing_key(),
extfvk,
);
let address_str = address_from_extfvk(params, extfvk);
data.0.execute(
"INSERT INTO accounts (account, extfvk, address)
VALUES (?, ?, ?)",
&[
(account as u32).to_sql()?,
extfvk.to_sql()?,
address.to_sql()?,
extfvk_str.to_sql()?,
address_str.to_sql()?,
],
)?;
}
@ -207,14 +212,17 @@ pub fn init_accounts_table<P: consensus::Parameters>(
///
/// ```
/// use tempfile::NamedTempFile;
/// use zcash_primitives::block::BlockHash;
/// use zcash_primitives::{
/// block::BlockHash,
/// consensus::BlockHeight,
/// };
/// use zcash_client_sqlite::{
/// DataConnection,
/// init::init_blocks_table,
/// };
///
/// // The block height.
/// let height = 500_000;
/// let height = BlockHeight(500_000);
/// // The hash of the block header.
/// let hash = BlockHash([0; 32]);
/// // The nTime field from the block header.
@ -229,7 +237,7 @@ pub fn init_accounts_table<P: consensus::Parameters>(
/// ```
pub fn init_blocks_table(
data: &DataConnection,
height: i32,
height: BlockHeight,
hash: BlockHash,
time: u32,
sapling_tree: &[u8],
@ -243,7 +251,7 @@ pub fn init_blocks_table(
"INSERT INTO blocks (height, hash, time, sapling_tree)
VALUES (?, ?, ?, ?)",
&[
height.to_sql()?,
u32::from(height).to_sql()?,
hash.0.to_sql()?,
time.to_sql()?,
sapling_tree.to_sql()?,
@ -260,13 +268,11 @@ mod tests {
use zcash_primitives::{
block::BlockHash,
consensus::Parameters,
consensus::BlockHeight,
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
};
use zcash_client_backend::encoding::decode_payment_address;
use crate::{query::get_address, tests, DataConnection};
use crate::{query::get_address, tests, Account, DataConnection};
use super::{init_accounts_table, init_blocks_table, init_data_database};
@ -298,10 +304,24 @@ mod tests {
init_data_database(&db_data).unwrap();
// First call with data should initialise the blocks table
init_blocks_table(&db_data, 1, BlockHash([1; 32]), 1, &[]).unwrap();
init_blocks_table(
&db_data,
BlockHeight::from(1u32),
BlockHash([1; 32]),
1,
&[],
)
.unwrap();
// Subsequent calls should return an error
init_blocks_table(&db_data, 2, BlockHash([2; 32]), 2, &[]).unwrap_err();
init_blocks_table(
&db_data,
BlockHeight::from(2u32),
BlockHash([2; 32]),
2,
&[],
)
.unwrap_err();
}
#[test]
@ -316,9 +336,7 @@ mod tests {
init_accounts_table(&db_data, &tests::network(), &extfvks).unwrap();
// The account's address should be in the data DB
let addr = get_address(&db_data, 0).unwrap();
let pa =
decode_payment_address(tests::network().hrp_sapling_payment_address(), &addr).unwrap();
let pa = get_address(&db_data, &tests::network(), Account(0)).unwrap();
assert_eq!(pa.unwrap(), extsk.default_address().unwrap().1);
}
}

View File

@ -30,6 +30,7 @@ use std::path::Path;
use zcash_primitives::{
block::BlockHash,
consensus::{self, BlockHeight},
primitives::PaymentAddress,
zip32::ExtendedFullViewingKey,
};
@ -46,7 +47,7 @@ pub mod query;
pub mod scan;
pub mod transact;
pub struct Account(u32);
pub struct Account(pub u32);
pub struct DataConnection(Connection);
@ -58,12 +59,13 @@ impl DataConnection {
impl DBOps for DataConnection {
type Error = Error<rusqlite::Error>;
type Account = Account;
fn init_db(&self) -> Result<(), Self::Error> {
init::init_data_database(self).map_err(Error::Database)
}
fn init_accounts<P: consensus::Parameters>(
fn init_account_storage<P: consensus::Parameters>(
&self,
params: &P,
extfvks: &[ExtendedFullViewingKey],
@ -71,6 +73,16 @@ impl DBOps for DataConnection {
init::init_accounts_table(self, params, extfvks).map_err(|e| e.0)
}
fn init_block_storage(
&self,
height: BlockHeight,
hash: BlockHash,
time: u32,
sapling_tree: &[u8],
) -> Result<(), Self::Error> {
init::init_blocks_table(self, height, hash, time, sapling_tree).map_err(|e| e.0)
}
fn block_height_extrema(&self) -> Result<Option<(BlockHeight, BlockHeight)>, Self::Error> {
chain::block_height_extrema(self).map_err(Error::Database)
}
@ -86,6 +98,14 @@ impl DBOps for DataConnection {
) -> Result<(), Self::Error> {
chain::rewind_to_height(self, parameters, block_height).map_err(|e| e.0)
}
fn get_address<P: consensus::Parameters>(
&self,
params: &P,
account: Self::Account,
) -> Result<Option<PaymentAddress>, Self::Error> {
query::get_address(self, params, account).map_err(|e| e.0)
}
}
pub struct CacheConnection(Connection);

View File

@ -1,10 +1,18 @@
//! Functions for querying information in the data database.
use zcash_primitives::{note_encryption::Memo, transaction::components::Amount};
use zcash_primitives::{
consensus::{self},
note_encryption::Memo,
primitives::PaymentAddress,
transaction::components::Amount,
};
use zcash_client_backend::data_api::{chain::get_target_and_anchor_heights, error::Error};
use zcash_client_backend::{
data_api::{chain::get_target_and_anchor_heights, error::Error},
encoding::decode_payment_address,
};
use crate::{error::SqliteClientError, DataConnection};
use crate::{error::SqliteClientError, Account, DataConnection};
/// Returns the address for the account.
///
@ -12,24 +20,33 @@ use crate::{error::SqliteClientError, DataConnection};
///
/// ```
/// use tempfile::NamedTempFile;
/// use zcash_primitives::{
/// consensus::{self, Network},
/// };
/// use zcash_client_sqlite::{
/// Account,
/// DataConnection,
/// query::get_address,
/// };
///
/// let data_file = NamedTempFile::new().unwrap();
/// let db = DataConnection::for_path(data_file).unwrap();
/// let addr = get_address(&db, 0);
/// let addr = get_address(&db, &Network::TestNetwork, Account(0));
/// ```
pub fn get_address(data: &DataConnection, account: u32) -> Result<String, rusqlite::Error> {
let addr = data.0.query_row(
pub fn get_address<P: consensus::Parameters>(
data: &DataConnection,
params: &P,
account: Account,
) -> Result<Option<PaymentAddress>, SqliteClientError> {
let addr: String = data.0.query_row(
"SELECT address FROM accounts
WHERE account = ?",
&[account],
&[account.0],
|row| row.get(0),
)?;
Ok(addr)
decode_payment_address(params.hrp_sapling_payment_address(), &addr)
.map_err(|e| SqliteClientError(e.into()))
}
/// Returns the balance for the account, including all mined unspent notes that we know
@ -200,7 +217,7 @@ mod tests {
use crate::{
init::{init_accounts_table, init_data_database},
tests, DataConnection,
tests, Account, DataConnection,
};
use super::{get_address, get_balance, get_verified_balance};
@ -227,7 +244,7 @@ mod tests {
}
// An invalid account has zero balance
assert!(get_address(&db_data, 1).is_err());
assert!(get_address(&db_data, &tests::network(), Account(1)).is_err());
assert_eq!(get_balance(&db_data, 1).unwrap(), Amount::zero());
}
}

View File

@ -377,7 +377,7 @@ mod tests {
use zcash_primitives::{
block::BlockHash,
consensus,
consensus::{self, BlockHeight},
note_encryption::try_sapling_output_recovery,
prover::TxProver,
transaction::{components::Amount, Transaction},
@ -487,7 +487,14 @@ mod tests {
let data_file = NamedTempFile::new().unwrap();
let db_data = DataConnection(Connection::open(data_file.path()).unwrap());
init_data_database(&db_data).unwrap();
init_blocks_table(&db_data, 1, BlockHash([1; 32]), 1, &[]).unwrap();
init_blocks_table(
&db_data,
BlockHeight::from(1u32),
BlockHash([1; 32]),
1,
&[],
)
.unwrap();
// Add an account to the wallet
let extsk = ExtendedSpendingKey::master(&[]);