diff --git a/zcash_client_sqlite/CHANGELOG.md b/zcash_client_sqlite/CHANGELOG.md index 4465d3637e..59e90a30b5 100644 --- a/zcash_client_sqlite/CHANGELOG.md +++ b/zcash_client_sqlite/CHANGELOG.md @@ -38,6 +38,11 @@ and this library adheres to Rust's notion of method to be used in contexts where a transaction has just been constructed, rather than only in the case that a transaction has been decrypted after being retrieved from the network. +- A new non-null column, `output_pool` has been added to the `sent_notes` + table to enable distinguising between Sapling and transparent outputs + (and in the future, outputs to other pools). This will require a migration, + which may need to be performed in multiple steps. Values for this column + should be assigned by inference from the address type in the stored data. ### Deprecated - A number of public API methods that are used internally to support the diff --git a/zcash_client_sqlite/src/lib.rs b/zcash_client_sqlite/src/lib.rs index e89ab37697..50acbac7a2 100644 --- a/zcash_client_sqlite/src/lib.rs +++ b/zcash_client_sqlite/src/lib.rs @@ -181,11 +181,11 @@ impl WalletDb

{ stmt_update_sent_note: self.conn.prepare( "UPDATE sent_notes SET from_account = ?, address = ?, value = ?, memo = ? - WHERE tx = ? AND output_index = ?", + WHERE tx = ? AND output_pool = ? AND output_index = ?", )?, stmt_insert_sent_note: self.conn.prepare( - "INSERT INTO sent_notes (tx, output_index, from_account, address, value, memo) - VALUES (?, ?, ?, ?, ?, ?)", + "INSERT INTO sent_notes (tx, output_pool, output_index, from_account, address, value, memo) + VALUES (?, ?, ?, ?, ?, ?, ?)", )?, stmt_insert_witness: self.conn.prepare( "INSERT INTO sapling_witnesses (note, block, witness) diff --git a/zcash_client_sqlite/src/wallet.rs b/zcash_client_sqlite/src/wallet.rs index c8824ce83c..720873d7ae 100644 --- a/zcash_client_sqlite/src/wallet.rs +++ b/zcash_client_sqlite/src/wallet.rs @@ -49,6 +49,23 @@ use { pub mod init; pub mod transact; +enum PoolType { + Transparent, + Sapling, +} + +impl PoolType { + fn typecode(&self) -> i64 { + // These constants are *incidentally* shared with the typecodes + // for unified addresses, but this is exclusively an internal + // implementation detail. + match self { + PoolType::Transparent => 0i64, + PoolType::Sapling => 2i64, + } + } +} + /// This trait provides a generalization over shielded output representations. #[deprecated(note = "This trait will be removed in a future release.")] pub trait ShieldedOutput { @@ -580,18 +597,9 @@ pub(crate) fn rewind_to_height( &[u32::from(block_height)], )?; - // Rewind sent notes - wdb.conn.execute( - "DELETE FROM sent_notes - WHERE id_note IN ( - SELECT sn.id_note - FROM sent_notes sn - LEFT OUTER JOIN transactions tx - ON tx.id_tx = sn.tx - WHERE tx.block IS NOT NULL AND tx.block > ? - );", - &[u32::from(block_height)], - )?; + // Do not delete sent notes; this can contain data that is not recoverable + // from the chain. Wallets must continue to operate correctly in the + // presence of stale sent notes that link to unmined transactions. // Rewind utxos wdb.conn.execute( @@ -1096,7 +1104,8 @@ pub fn put_sent_note<'a, P: consensus::Parameters>( ivalue, &memo.map(|m| m.as_slice()), tx_ref, - output_index as i64 + PoolType::Sapling.typecode(), + output_index as i64, ])? == 0 { // It isn't there, so insert. @@ -1130,7 +1139,8 @@ pub fn put_sent_utxo<'a, P: consensus::Parameters>( ivalue, (None::<&[u8]>), tx_ref, - output_index as i64 + PoolType::Transparent.typecode(), + output_index as i64, ])? == 0 { // It isn't there, so insert. @@ -1164,6 +1174,7 @@ pub fn insert_sent_note<'a, P: consensus::Parameters>( let ivalue: i64 = value.into(); stmts.stmt_insert_sent_note.execute(params![ tx_ref, + PoolType::Sapling.typecode(), (output_index as i64), account.0, to_str, @@ -1192,11 +1203,12 @@ pub fn insert_sent_utxo<'a, P: consensus::Parameters>( let ivalue: i64 = value.into(); stmts.stmt_insert_sent_note.execute(params![ tx_ref, + PoolType::Transparent.typecode(), (output_index as i64), account.0, to_str, ivalue, - (None::<&[u8]>) + (None::<&[u8]>), ])?; Ok(()) diff --git a/zcash_client_sqlite/src/wallet/init.rs b/zcash_client_sqlite/src/wallet/init.rs index 1ca07ce915..28e0e3443e 100644 --- a/zcash_client_sqlite/src/wallet/init.rs +++ b/zcash_client_sqlite/src/wallet/init.rs @@ -111,6 +111,7 @@ pub fn init_wallet_db

(wdb: &WalletDb

) -> Result<(), rusqlite::Error> { "CREATE TABLE IF NOT EXISTS sent_notes ( id_note INTEGER PRIMARY KEY, tx INTEGER NOT NULL, + output_pool INTEGER NOT NULL, output_index INTEGER NOT NULL, from_account INTEGER NOT NULL, address TEXT NOT NULL, @@ -118,7 +119,7 @@ pub fn init_wallet_db

(wdb: &WalletDb

) -> Result<(), rusqlite::Error> { memo BLOB, FOREIGN KEY (tx) REFERENCES transactions(id_tx), FOREIGN KEY (from_account) REFERENCES accounts(account), - CONSTRAINT tx_output UNIQUE (tx, output_index) + CONSTRAINT tx_output UNIQUE (tx, output_pool, output_index) )", NO_PARAMS, )?;