zcash_client_sqlite: Update received note during scan if present

Fixes a bug where rewinding a block that contained a received note would
cause a constraint violation.
This commit is contained in:
Jack Grigg 2020-03-06 09:36:50 +13:00
parent 9363ec36d9
commit 3036064cd0
2 changed files with 39 additions and 6 deletions

View File

@ -470,5 +470,11 @@ mod tests {
// Account balance should only contain the first received note
assert_eq!(get_balance(db_data, 0).unwrap(), value);
// Scan the cache again
scan_cached_blocks(db_cache, db_data, None).unwrap();
// Account balance should again reflect both received notes
assert_eq!(get_balance(db_data, 0).unwrap(), value + value2);
}
}

View File

@ -143,10 +143,17 @@ pub fn scan_cached_blocks<P: AsRef<Path>, Q: AsRef<Path>>(
let mut stmt_select_tx = data.prepare("SELECT id_tx FROM transactions WHERE txid = ?")?;
let mut stmt_mark_spent_note =
data.prepare("UPDATE received_notes SET spent = ? WHERE nf = ?")?;
let mut stmt_update_note = data.prepare(
"UPDATE received_notes
SET account = ?, diversifier = ?, value = ?, rcm = ?, nf = ?, is_change = ?
WHERE tx = ? AND output_index = ?",
)?;
let mut stmt_insert_note = data.prepare(
"INSERT INTO received_notes (tx, output_index, account, diversifier, value, rcm, nf, is_change)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
)?;
let mut stmt_select_note =
data.prepare("SELECT id_note FROM received_notes WHERE tx = ? AND output_index = ?")?;
let mut stmt_insert_witness = data.prepare(
"INSERT INTO sapling_witnesses (note, block, witness)
VALUES (?, ?, ?)",
@ -267,21 +274,41 @@ pub fn scan_cached_blocks<P: AsRef<Path>, Q: AsRef<Path>>(
&JUBJUB,
);
// Insert received note into the database.
// Assumptions:
// - A transaction will not contain more than 2^63 shielded outputs.
// - A note value will never exceed 2^63 zatoshis.
stmt_insert_note.execute(&[
tx_row.to_sql()?,
(output.index as i64).to_sql()?,
// First try updating an existing received note into the database.
let note_row = if stmt_update_note.execute(&[
(output.account as i64).to_sql()?,
output.to.diversifier().0.to_sql()?,
(output.note.value as i64).to_sql()?,
rcm.as_ref().to_sql()?,
nf.to_sql()?,
output.is_change.to_sql()?,
])?;
let note_row = data.last_insert_rowid();
tx_row.to_sql()?,
(output.index as i64).to_sql()?,
])? == 0
{
// It isn't there, so insert our note into the database.
stmt_insert_note.execute(&[
tx_row.to_sql()?,
(output.index as i64).to_sql()?,
(output.account as i64).to_sql()?,
output.to.diversifier().0.to_sql()?,
(output.note.value as i64).to_sql()?,
rcm.as_ref().to_sql()?,
nf.to_sql()?,
output.is_change.to_sql()?,
])?;
data.last_insert_rowid()
} else {
// It was there, so grab its row number.
stmt_select_note.query_row(
&[tx_row.to_sql()?, (output.index as i64).to_sql()?],
|row| row.get(0),
)?
};
// Save witness for note.
witnesses.push(WitnessRow {