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")]
|
||||
pub struct WalletTransparentOutput {
|
||||
pub received_by_account: AccountId,
|
||||
pub outpoint: OutPoint,
|
||||
pub txout: TxOut,
|
||||
pub height: BlockHeight,
|
||||
|
|
|
@ -33,6 +33,53 @@ use {
|
|||
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.
|
||||
///
|
||||
/// 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_update_expired: Statement<'a>,
|
||||
|
||||
stmt_insert_address: Statement<'a>,
|
||||
stmt_insert_address: InsertAddress<'a>,
|
||||
}
|
||||
|
||||
impl<'a, P> DataConnStmtCache<'a, P> {
|
||||
|
@ -119,22 +166,26 @@ impl<'a, P> DataConnStmtCache<'a, P> {
|
|||
received_by_account, address,
|
||||
prevout_txid, prevout_idx, script,
|
||||
value_zat, height)
|
||||
VALUES (
|
||||
:received_by_account, :address,
|
||||
SELECT
|
||||
addresses.account, :address,
|
||||
:prevout_txid, :prevout_idx, :script,
|
||||
:value_zat, :height)
|
||||
:value_zat, :height
|
||||
FROM addresses
|
||||
WHERE addresses.cached_transparent_receiver_address = :address
|
||||
RETURNING id_utxo"
|
||||
)?,
|
||||
#[cfg(feature = "transparent-inputs")]
|
||||
stmt_update_received_transparent_utxo: wallet_db.conn.prepare(
|
||||
"UPDATE utxos
|
||||
SET received_by_account = :received_by_account,
|
||||
SET received_by_account = addresses.account,
|
||||
height = :height,
|
||||
address = :address,
|
||||
script = :script,
|
||||
value_zat = :value_zat
|
||||
FROM addresses
|
||||
WHERE prevout_txid = :prevout_txid
|
||||
AND prevout_idx = :prevout_idx
|
||||
AND addresses.cached_transparent_receiver_address = :address
|
||||
RETURNING id_utxo"
|
||||
)?,
|
||||
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 < ?
|
||||
)",
|
||||
)?,
|
||||
stmt_insert_address: wallet_db.conn.prepare(
|
||||
"INSERT INTO addresses (account, diversifier_index_be, address)
|
||||
VALUES (:account, :diversifier_index_be, :address)",
|
||||
)?,
|
||||
stmt_insert_address: InsertAddress::new(&wallet_db.conn)?
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -465,7 +513,6 @@ impl<'a, P: consensus::Parameters> DataConnStmtCache<'a, P> {
|
|||
self.stmt_insert_received_transparent_utxo
|
||||
.query_row(
|
||||
named_params![
|
||||
":received_by_account": &u32::from(output.received_by_account),
|
||||
":address": &output.address().encode(&self.wallet_db.params),
|
||||
":prevout_txid": &output.outpoint.hash().to_vec(),
|
||||
":prevout_idx": &output.outpoint.n(),
|
||||
|
@ -495,7 +542,6 @@ impl<'a, P: consensus::Parameters> DataConnStmtCache<'a, P> {
|
|||
named_params![
|
||||
":prevout_txid": &output.outpoint.hash().to_vec(),
|
||||
":prevout_idx": &output.outpoint.n(),
|
||||
":received_by_account": &u32::from(output.received_by_account),
|
||||
":address": &output.address().encode(&self.wallet_db.params),
|
||||
":script": &output.txout.script_pubkey.0,
|
||||
":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(
|
||||
&mut self,
|
||||
account: AccountId,
|
||||
mut diversifier_index: DiversifierIndex,
|
||||
diversifier_index: DiversifierIndex,
|
||||
address: &UnifiedAddress,
|
||||
) -> Result<i64, SqliteClientError> {
|
||||
diversifier_index.0.reverse();
|
||||
let sql_args: &[(&str, &dyn ToSql)] = &[
|
||||
(":account", &u32::from(account)),
|
||||
(":diversifier_index_be", &&diversifier_index.0[..]),
|
||||
(":address", &address.encode(&self.wallet_db.params)),
|
||||
];
|
||||
) -> Result<(), SqliteClientError> {
|
||||
self.stmt_insert_address.execute(
|
||||
&self.wallet_db.params,
|
||||
account,
|
||||
diversifier_index,
|
||||
address,
|
||||
)?;
|
||||
|
||||
self.stmt_insert_address.execute(sql_args)?;
|
||||
|
||||
Ok(self.wallet_db.conn.last_insert_rowid())
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,10 @@ use zcash_client_backend::{
|
|||
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")]
|
||||
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.
|
||||
let (address, mut idx) = key.default_address();
|
||||
let address_str: String = address.encode(network);
|
||||
// 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,
|
||||
],
|
||||
)?;
|
||||
let (address, d_idx) = key.default_address();
|
||||
InsertAddress::new(conn)?.execute(network, account, d_idx, &address)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -929,8 +921,7 @@ pub(crate) fn get_unspent_transparent_outputs<P: consensus::Parameters>(
|
|||
max_height: BlockHeight,
|
||||
) -> Result<Vec<WalletTransparentOutput>, SqliteClientError> {
|
||||
let mut stmt_blocks = wdb.conn.prepare(
|
||||
"SELECT u.received_by_account,
|
||||
u.prevout_txid, u.prevout_idx, u.script,
|
||||
"SELECT u.prevout_txid, u.prevout_idx, u.script,
|
||||
u.value_zat, u.height, tx.block as block
|
||||
FROM utxos u
|
||||
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 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(1)?;
|
||||
let txid: Vec<u8> = row.get(0)?;
|
||||
let mut txid_bytes = [0u8; 32];
|
||||
txid_bytes.copy_from_slice(&txid);
|
||||
|
||||
let index: u32 = row.get(2)?;
|
||||
let script_pubkey = Script(row.get(3)?);
|
||||
let value = Amount::from_i64(row.get(4)?).unwrap();
|
||||
let height: u32 = row.get(5)?;
|
||||
let index: u32 = row.get(1)?;
|
||||
let script_pubkey = Script(row.get(2)?);
|
||||
let value = Amount::from_i64(row.get(3)?).unwrap();
|
||||
let height: u32 = row.get(4)?;
|
||||
|
||||
Ok(WalletTransparentOutput {
|
||||
received_by_account: AccountId::from(received_by_account),
|
||||
outpoint: OutPoint::new(txid_bytes, index),
|
||||
txout: TxOut {
|
||||
value,
|
||||
|
@ -1224,7 +1212,9 @@ mod tests {
|
|||
|
||||
#[cfg(feature = "transparent-inputs")]
|
||||
use {
|
||||
zcash_client_backend::{data_api::WalletWrite, wallet::WalletTransparentOutput},
|
||||
zcash_client_backend::{
|
||||
data_api::WalletWrite, encoding::AddressCodec, wallet::WalletTransparentOutput,
|
||||
},
|
||||
zcash_primitives::{
|
||||
consensus::BlockHeight,
|
||||
transaction::components::{OutPoint, TxOut},
|
||||
|
@ -1267,12 +1257,11 @@ mod tests {
|
|||
// Add an account to the wallet
|
||||
let mut ops = db_data.get_update_ops().unwrap();
|
||||
let seed = Secret::new([0u8; 32].to_vec());
|
||||
let (account_id, usk) = ops.create_account(&seed).unwrap();
|
||||
let (uaddr, _) = usk.to_unified_full_viewing_key().default_address();
|
||||
let (account_id, _usk) = ops.create_account(&seed).unwrap();
|
||||
let uaddr = db_data.get_current_address(account_id).unwrap().unwrap();
|
||||
let taddr = uaddr.transparent().unwrap();
|
||||
|
||||
let mut utxo = WalletTransparentOutput {
|
||||
received_by_account: account_id,
|
||||
outpoint: OutPoint::new([1u8; 32], 1),
|
||||
txout: TxOut {
|
||||
value: Amount::from_u64(100000).unwrap(),
|
||||
|
@ -1310,5 +1299,18 @@ mod tests {
|
|||
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 tempfile::NamedTempFile;
|
||||
|
||||
use zcash_address::test_vectors;
|
||||
|
||||
use zcash_client_backend::{
|
||||
address::RecipientAddress,
|
||||
encoding::{encode_extended_full_viewing_key, encode_payment_address},
|
||||
|
@ -309,27 +307,30 @@ mod tests {
|
|||
|
||||
use zcash_primitives::{
|
||||
block::BlockHash,
|
||||
consensus::{BlockHeight, BranchId, Network, Parameters},
|
||||
consensus::{BlockHeight, BranchId, Parameters},
|
||||
transaction::{TransactionData, TxVersion},
|
||||
zip32::{
|
||||
sapling::{DiversifiableFullViewingKey, ExtendedFullViewingKey},
|
||||
DiversifierIndex,
|
||||
},
|
||||
zip32::sapling::{DiversifiableFullViewingKey, ExtendedFullViewingKey},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
error::SqliteClientError,
|
||||
tests::{self, network},
|
||||
wallet::{self, get_address},
|
||||
AccountId, WalletDb, WalletWrite,
|
||||
wallet::get_address,
|
||||
AccountId, WalletDb,
|
||||
};
|
||||
|
||||
use super::{init_accounts_table, init_blocks_table, init_wallet_db};
|
||||
|
||||
#[cfg(feature = "transparent-inputs")]
|
||||
use {
|
||||
crate::wallet::{pool_code, PoolType},
|
||||
zcash_primitives::legacy::keys as transparent,
|
||||
crate::{
|
||||
wallet::{self, pool_code, PoolType},
|
||||
WalletWrite,
|
||||
},
|
||||
zcash_address::test_vectors,
|
||||
zcash_primitives::{
|
||||
consensus::Network, legacy::keys as transparent, zip32::DiversifierIndex,
|
||||
},
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
@ -350,6 +351,7 @@ mod tests {
|
|||
account INTEGER NOT NULL,
|
||||
diversifier_index_be BLOB NOT NULL,
|
||||
address TEXT NOT NULL,
|
||||
cached_transparent_receiver_address TEXT,
|
||||
FOREIGN KEY (account) REFERENCES accounts(account),
|
||||
CONSTRAINT diversification UNIQUE (account, diversifier_index_be)
|
||||
)",
|
||||
|
|
|
@ -52,6 +52,7 @@ impl<P: consensus::Parameters> RusqliteMigration for Migration<P> {
|
|||
account INTEGER NOT NULL,
|
||||
diversifier_index_be BLOB NOT NULL,
|
||||
address TEXT NOT NULL,
|
||||
cached_transparent_receiver_address TEXT,
|
||||
FOREIGN KEY (account) REFERENCES accounts(account),
|
||||
CONSTRAINT diversification UNIQUE (account, diversifier_index_be)
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue