Merge pull request #672 from nuttycom/wallet/autodetect_utxo_account
Remove `received_by_account` field from WalletTransparentOutput
This commit is contained in:
commit
2a00d65226
|
@ -31,7 +31,6 @@ pub struct WalletTx<N> {
|
||||||
|
|
||||||
#[cfg(feature = "transparent-inputs")]
|
#[cfg(feature = "transparent-inputs")]
|
||||||
pub struct WalletTransparentOutput {
|
pub struct WalletTransparentOutput {
|
||||||
pub received_by_account: AccountId,
|
|
||||||
pub outpoint: OutPoint,
|
pub outpoint: OutPoint,
|
||||||
pub txout: TxOut,
|
pub txout: TxOut,
|
||||||
pub height: BlockHeight,
|
pub height: BlockHeight,
|
||||||
|
|
|
@ -33,6 +33,53 @@ use {
|
||||||
zcash_primitives::transaction::components::transparent::OutPoint,
|
zcash_primitives::transaction::components::transparent::OutPoint,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub(crate) struct InsertAddress<'a> {
|
||||||
|
stmt: Statement<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> InsertAddress<'a> {
|
||||||
|
pub(crate) fn new(conn: &'a rusqlite::Connection) -> Result<Self, rusqlite::Error> {
|
||||||
|
Ok(InsertAddress {
|
||||||
|
stmt: conn.prepare(
|
||||||
|
"INSERT INTO addresses (
|
||||||
|
account,
|
||||||
|
diversifier_index_be,
|
||||||
|
address,
|
||||||
|
cached_transparent_receiver_address
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
:account,
|
||||||
|
:diversifier_index_be,
|
||||||
|
:address,
|
||||||
|
:cached_transparent_receiver_address
|
||||||
|
)",
|
||||||
|
)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds the given address and diversifier index to the addresses table.
|
||||||
|
///
|
||||||
|
/// Returns the database row for the newly-inserted address.
|
||||||
|
pub(crate) fn execute<P: consensus::Parameters>(
|
||||||
|
&mut self,
|
||||||
|
params: &P,
|
||||||
|
account: AccountId,
|
||||||
|
mut diversifier_index: DiversifierIndex,
|
||||||
|
address: &UnifiedAddress,
|
||||||
|
) -> Result<(), rusqlite::Error> {
|
||||||
|
// the diversifier index is stored in big-endian order to allow sorting
|
||||||
|
diversifier_index.0.reverse();
|
||||||
|
self.stmt.execute(named_params![
|
||||||
|
":account": &u32::from(account),
|
||||||
|
":diversifier_index_be": &&diversifier_index.0[..],
|
||||||
|
":address": &address.encode(params),
|
||||||
|
":cached_transparent_receiver_address": &address.transparent().map(|r| r.encode(params)),
|
||||||
|
])?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The primary type used to implement [`WalletWrite`] for the SQLite database.
|
/// The primary type used to implement [`WalletWrite`] for the SQLite database.
|
||||||
///
|
///
|
||||||
/// A data structure that stores the SQLite prepared statements that are
|
/// A data structure that stores the SQLite prepared statements that are
|
||||||
|
@ -70,7 +117,7 @@ pub struct DataConnStmtCache<'a, P> {
|
||||||
stmt_prune_witnesses: Statement<'a>,
|
stmt_prune_witnesses: Statement<'a>,
|
||||||
stmt_update_expired: Statement<'a>,
|
stmt_update_expired: Statement<'a>,
|
||||||
|
|
||||||
stmt_insert_address: Statement<'a>,
|
stmt_insert_address: InsertAddress<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, P> DataConnStmtCache<'a, P> {
|
impl<'a, P> DataConnStmtCache<'a, P> {
|
||||||
|
@ -119,22 +166,26 @@ impl<'a, P> DataConnStmtCache<'a, P> {
|
||||||
received_by_account, address,
|
received_by_account, address,
|
||||||
prevout_txid, prevout_idx, script,
|
prevout_txid, prevout_idx, script,
|
||||||
value_zat, height)
|
value_zat, height)
|
||||||
VALUES (
|
SELECT
|
||||||
:received_by_account, :address,
|
addresses.account, :address,
|
||||||
:prevout_txid, :prevout_idx, :script,
|
:prevout_txid, :prevout_idx, :script,
|
||||||
:value_zat, :height)
|
:value_zat, :height
|
||||||
|
FROM addresses
|
||||||
|
WHERE addresses.cached_transparent_receiver_address = :address
|
||||||
RETURNING id_utxo"
|
RETURNING id_utxo"
|
||||||
)?,
|
)?,
|
||||||
#[cfg(feature = "transparent-inputs")]
|
#[cfg(feature = "transparent-inputs")]
|
||||||
stmt_update_received_transparent_utxo: wallet_db.conn.prepare(
|
stmt_update_received_transparent_utxo: wallet_db.conn.prepare(
|
||||||
"UPDATE utxos
|
"UPDATE utxos
|
||||||
SET received_by_account = :received_by_account,
|
SET received_by_account = addresses.account,
|
||||||
height = :height,
|
height = :height,
|
||||||
address = :address,
|
address = :address,
|
||||||
script = :script,
|
script = :script,
|
||||||
value_zat = :value_zat
|
value_zat = :value_zat
|
||||||
|
FROM addresses
|
||||||
WHERE prevout_txid = :prevout_txid
|
WHERE prevout_txid = :prevout_txid
|
||||||
AND prevout_idx = :prevout_idx
|
AND prevout_idx = :prevout_idx
|
||||||
|
AND addresses.cached_transparent_receiver_address = :address
|
||||||
RETURNING id_utxo"
|
RETURNING id_utxo"
|
||||||
)?,
|
)?,
|
||||||
stmt_insert_received_note: wallet_db.conn.prepare(
|
stmt_insert_received_note: wallet_db.conn.prepare(
|
||||||
|
@ -187,10 +238,7 @@ impl<'a, P> DataConnStmtCache<'a, P> {
|
||||||
WHERE id_tx = received_notes.spent AND block IS NULL AND expiry_height < ?
|
WHERE id_tx = received_notes.spent AND block IS NULL AND expiry_height < ?
|
||||||
)",
|
)",
|
||||||
)?,
|
)?,
|
||||||
stmt_insert_address: wallet_db.conn.prepare(
|
stmt_insert_address: InsertAddress::new(&wallet_db.conn)?
|
||||||
"INSERT INTO addresses (account, diversifier_index_be, address)
|
|
||||||
VALUES (:account, :diversifier_index_be, :address)",
|
|
||||||
)?,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -465,7 +513,6 @@ impl<'a, P: consensus::Parameters> DataConnStmtCache<'a, P> {
|
||||||
self.stmt_insert_received_transparent_utxo
|
self.stmt_insert_received_transparent_utxo
|
||||||
.query_row(
|
.query_row(
|
||||||
named_params![
|
named_params![
|
||||||
":received_by_account": &u32::from(output.received_by_account),
|
|
||||||
":address": &output.address().encode(&self.wallet_db.params),
|
":address": &output.address().encode(&self.wallet_db.params),
|
||||||
":prevout_txid": &output.outpoint.hash().to_vec(),
|
":prevout_txid": &output.outpoint.hash().to_vec(),
|
||||||
":prevout_idx": &output.outpoint.n(),
|
":prevout_idx": &output.outpoint.n(),
|
||||||
|
@ -495,7 +542,6 @@ impl<'a, P: consensus::Parameters> DataConnStmtCache<'a, P> {
|
||||||
named_params![
|
named_params![
|
||||||
":prevout_txid": &output.outpoint.hash().to_vec(),
|
":prevout_txid": &output.outpoint.hash().to_vec(),
|
||||||
":prevout_idx": &output.outpoint.n(),
|
":prevout_idx": &output.outpoint.n(),
|
||||||
":received_by_account": &u32::from(output.received_by_account),
|
|
||||||
":address": &output.address().encode(&self.wallet_db.params),
|
":address": &output.address().encode(&self.wallet_db.params),
|
||||||
":script": &output.txout.script_pubkey.0,
|
":script": &output.txout.script_pubkey.0,
|
||||||
":value_zat": &i64::from(output.txout.value),
|
":value_zat": &i64::from(output.txout.value),
|
||||||
|
@ -516,19 +562,17 @@ impl<'a, P: consensus::Parameters> DataConnStmtCache<'a, P> {
|
||||||
pub(crate) fn stmt_insert_address(
|
pub(crate) fn stmt_insert_address(
|
||||||
&mut self,
|
&mut self,
|
||||||
account: AccountId,
|
account: AccountId,
|
||||||
mut diversifier_index: DiversifierIndex,
|
diversifier_index: DiversifierIndex,
|
||||||
address: &UnifiedAddress,
|
address: &UnifiedAddress,
|
||||||
) -> Result<i64, SqliteClientError> {
|
) -> Result<(), SqliteClientError> {
|
||||||
diversifier_index.0.reverse();
|
self.stmt_insert_address.execute(
|
||||||
let sql_args: &[(&str, &dyn ToSql)] = &[
|
&self.wallet_db.params,
|
||||||
(":account", &u32::from(account)),
|
account,
|
||||||
(":diversifier_index_be", &&diversifier_index.0[..]),
|
diversifier_index,
|
||||||
(":address", &address.encode(&self.wallet_db.params)),
|
address,
|
||||||
];
|
)?;
|
||||||
|
|
||||||
self.stmt_insert_address.execute(sql_args)?;
|
Ok(())
|
||||||
|
|
||||||
Ok(self.wallet_db.conn.last_insert_rowid())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,10 @@ use zcash_client_backend::{
|
||||||
DecryptedOutput,
|
DecryptedOutput,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{error::SqliteClientError, DataConnStmtCache, NoteId, WalletDb, PRUNING_HEIGHT};
|
use crate::{
|
||||||
|
error::SqliteClientError, prepared::InsertAddress, DataConnStmtCache, NoteId, WalletDb,
|
||||||
|
PRUNING_HEIGHT,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "transparent-inputs")]
|
#[cfg(feature = "transparent-inputs")]
|
||||||
use {
|
use {
|
||||||
|
@ -207,19 +210,8 @@ pub(crate) fn add_account_internal<P: consensus::Parameters, E: From<rusqlite::E
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Always derive the default Unified Address for the account.
|
// Always derive the default Unified Address for the account.
|
||||||
let (address, mut idx) = key.default_address();
|
let (address, d_idx) = key.default_address();
|
||||||
let address_str: String = address.encode(network);
|
InsertAddress::new(conn)?.execute(network, account, d_idx, &address)?;
|
||||||
// the diversifier index is stored in big-endian order to allow sorting
|
|
||||||
idx.0.reverse();
|
|
||||||
conn.execute(
|
|
||||||
"INSERT INTO addresses (account, diversifier_index_be, address)
|
|
||||||
VALUES (:account, :diversifier_index_be, :address)",
|
|
||||||
named_params![
|
|
||||||
":account": &<u32>::from(account),
|
|
||||||
":diversifier_index_be": &&idx.0[..],
|
|
||||||
":address": &address_str,
|
|
||||||
],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -929,8 +921,7 @@ pub(crate) fn get_unspent_transparent_outputs<P: consensus::Parameters>(
|
||||||
max_height: BlockHeight,
|
max_height: BlockHeight,
|
||||||
) -> Result<Vec<WalletTransparentOutput>, SqliteClientError> {
|
) -> Result<Vec<WalletTransparentOutput>, SqliteClientError> {
|
||||||
let mut stmt_blocks = wdb.conn.prepare(
|
let mut stmt_blocks = wdb.conn.prepare(
|
||||||
"SELECT u.received_by_account,
|
"SELECT u.prevout_txid, u.prevout_idx, u.script,
|
||||||
u.prevout_txid, u.prevout_idx, u.script,
|
|
||||||
u.value_zat, u.height, tx.block as block
|
u.value_zat, u.height, tx.block as block
|
||||||
FROM utxos u
|
FROM utxos u
|
||||||
LEFT OUTER JOIN transactions tx
|
LEFT OUTER JOIN transactions tx
|
||||||
|
@ -943,19 +934,16 @@ pub(crate) fn get_unspent_transparent_outputs<P: consensus::Parameters>(
|
||||||
let addr_str = address.encode(&wdb.params);
|
let addr_str = address.encode(&wdb.params);
|
||||||
|
|
||||||
let rows = stmt_blocks.query_map(params![addr_str, u32::from(max_height)], |row| {
|
let rows = stmt_blocks.query_map(params![addr_str, u32::from(max_height)], |row| {
|
||||||
let received_by_account: u32 = row.get(0)?;
|
let txid: Vec<u8> = row.get(0)?;
|
||||||
|
|
||||||
let txid: Vec<u8> = row.get(1)?;
|
|
||||||
let mut txid_bytes = [0u8; 32];
|
let mut txid_bytes = [0u8; 32];
|
||||||
txid_bytes.copy_from_slice(&txid);
|
txid_bytes.copy_from_slice(&txid);
|
||||||
|
|
||||||
let index: u32 = row.get(2)?;
|
let index: u32 = row.get(1)?;
|
||||||
let script_pubkey = Script(row.get(3)?);
|
let script_pubkey = Script(row.get(2)?);
|
||||||
let value = Amount::from_i64(row.get(4)?).unwrap();
|
let value = Amount::from_i64(row.get(3)?).unwrap();
|
||||||
let height: u32 = row.get(5)?;
|
let height: u32 = row.get(4)?;
|
||||||
|
|
||||||
Ok(WalletTransparentOutput {
|
Ok(WalletTransparentOutput {
|
||||||
received_by_account: AccountId::from(received_by_account),
|
|
||||||
outpoint: OutPoint::new(txid_bytes, index),
|
outpoint: OutPoint::new(txid_bytes, index),
|
||||||
txout: TxOut {
|
txout: TxOut {
|
||||||
value,
|
value,
|
||||||
|
@ -1224,7 +1212,9 @@ mod tests {
|
||||||
|
|
||||||
#[cfg(feature = "transparent-inputs")]
|
#[cfg(feature = "transparent-inputs")]
|
||||||
use {
|
use {
|
||||||
zcash_client_backend::{data_api::WalletWrite, wallet::WalletTransparentOutput},
|
zcash_client_backend::{
|
||||||
|
data_api::WalletWrite, encoding::AddressCodec, wallet::WalletTransparentOutput,
|
||||||
|
},
|
||||||
zcash_primitives::{
|
zcash_primitives::{
|
||||||
consensus::BlockHeight,
|
consensus::BlockHeight,
|
||||||
transaction::components::{OutPoint, TxOut},
|
transaction::components::{OutPoint, TxOut},
|
||||||
|
@ -1267,12 +1257,11 @@ mod tests {
|
||||||
// Add an account to the wallet
|
// Add an account to the wallet
|
||||||
let mut ops = db_data.get_update_ops().unwrap();
|
let mut ops = db_data.get_update_ops().unwrap();
|
||||||
let seed = Secret::new([0u8; 32].to_vec());
|
let seed = Secret::new([0u8; 32].to_vec());
|
||||||
let (account_id, usk) = ops.create_account(&seed).unwrap();
|
let (account_id, _usk) = ops.create_account(&seed).unwrap();
|
||||||
let (uaddr, _) = usk.to_unified_full_viewing_key().default_address();
|
let uaddr = db_data.get_current_address(account_id).unwrap().unwrap();
|
||||||
let taddr = uaddr.transparent().unwrap();
|
let taddr = uaddr.transparent().unwrap();
|
||||||
|
|
||||||
let mut utxo = WalletTransparentOutput {
|
let mut utxo = WalletTransparentOutput {
|
||||||
received_by_account: account_id,
|
|
||||||
outpoint: OutPoint::new([1u8; 32], 1),
|
outpoint: OutPoint::new([1u8; 32], 1),
|
||||||
txout: TxOut {
|
txout: TxOut {
|
||||||
value: Amount::from_u64(100000).unwrap(),
|
value: Amount::from_u64(100000).unwrap(),
|
||||||
|
@ -1310,5 +1299,18 @@ mod tests {
|
||||||
utxos.iter().any(|rutxo| rutxo.height == utxo.height)
|
utxos.iter().any(|rutxo| rutxo.height == utxo.height)
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// Artificially delete the address from the addresses table so that
|
||||||
|
// we can ensure the update fails if the join doesn't work.
|
||||||
|
db_data
|
||||||
|
.conn
|
||||||
|
.execute(
|
||||||
|
"DELETE FROM addresses WHERE cached_transparent_receiver_address = ?",
|
||||||
|
[Some(taddr.encode(&db_data.params))],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let res2 = super::put_received_transparent_utxo(&mut ops, &utxo);
|
||||||
|
assert!(matches!(res2, Err(_)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -299,8 +299,6 @@ mod tests {
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
|
|
||||||
use zcash_address::test_vectors;
|
|
||||||
|
|
||||||
use zcash_client_backend::{
|
use zcash_client_backend::{
|
||||||
address::RecipientAddress,
|
address::RecipientAddress,
|
||||||
encoding::{encode_extended_full_viewing_key, encode_payment_address},
|
encoding::{encode_extended_full_viewing_key, encode_payment_address},
|
||||||
|
@ -309,27 +307,30 @@ mod tests {
|
||||||
|
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
block::BlockHash,
|
block::BlockHash,
|
||||||
consensus::{BlockHeight, BranchId, Network, Parameters},
|
consensus::{BlockHeight, BranchId, Parameters},
|
||||||
transaction::{TransactionData, TxVersion},
|
transaction::{TransactionData, TxVersion},
|
||||||
zip32::{
|
zip32::sapling::{DiversifiableFullViewingKey, ExtendedFullViewingKey},
|
||||||
sapling::{DiversifiableFullViewingKey, ExtendedFullViewingKey},
|
|
||||||
DiversifierIndex,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::SqliteClientError,
|
error::SqliteClientError,
|
||||||
tests::{self, network},
|
tests::{self, network},
|
||||||
wallet::{self, get_address},
|
wallet::get_address,
|
||||||
AccountId, WalletDb, WalletWrite,
|
AccountId, WalletDb,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{init_accounts_table, init_blocks_table, init_wallet_db};
|
use super::{init_accounts_table, init_blocks_table, init_wallet_db};
|
||||||
|
|
||||||
#[cfg(feature = "transparent-inputs")]
|
#[cfg(feature = "transparent-inputs")]
|
||||||
use {
|
use {
|
||||||
crate::wallet::{pool_code, PoolType},
|
crate::{
|
||||||
zcash_primitives::legacy::keys as transparent,
|
wallet::{self, pool_code, PoolType},
|
||||||
|
WalletWrite,
|
||||||
|
},
|
||||||
|
zcash_address::test_vectors,
|
||||||
|
zcash_primitives::{
|
||||||
|
consensus::Network, legacy::keys as transparent, zip32::DiversifierIndex,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -350,6 +351,7 @@ mod tests {
|
||||||
account INTEGER NOT NULL,
|
account INTEGER NOT NULL,
|
||||||
diversifier_index_be BLOB NOT NULL,
|
diversifier_index_be BLOB NOT NULL,
|
||||||
address TEXT NOT NULL,
|
address TEXT NOT NULL,
|
||||||
|
cached_transparent_receiver_address TEXT,
|
||||||
FOREIGN KEY (account) REFERENCES accounts(account),
|
FOREIGN KEY (account) REFERENCES accounts(account),
|
||||||
CONSTRAINT diversification UNIQUE (account, diversifier_index_be)
|
CONSTRAINT diversification UNIQUE (account, diversifier_index_be)
|
||||||
)",
|
)",
|
||||||
|
|
|
@ -52,6 +52,7 @@ impl<P: consensus::Parameters> RusqliteMigration for Migration<P> {
|
||||||
account INTEGER NOT NULL,
|
account INTEGER NOT NULL,
|
||||||
diversifier_index_be BLOB NOT NULL,
|
diversifier_index_be BLOB NOT NULL,
|
||||||
address TEXT NOT NULL,
|
address TEXT NOT NULL,
|
||||||
|
cached_transparent_receiver_address TEXT,
|
||||||
FOREIGN KEY (account) REFERENCES accounts(account),
|
FOREIGN KEY (account) REFERENCES accounts(account),
|
||||||
CONSTRAINT diversification UNIQUE (account, diversifier_index_be)
|
CONSTRAINT diversification UNIQUE (account, diversifier_index_be)
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue