Skip to content

Commit 0b09546

Browse files
authored
Merge pull request #1978 from zcash/merge/zcash_client_sqlite-0.18.3
Post-release merge of `zcash_client_sqlite-0.18.3`
2 parents 78db000 + ddf80d0 commit 0b09546

7 files changed

Lines changed: 123 additions & 15 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

supply-chain/imports.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,8 +302,8 @@ user-login = "str4d"
302302
user-name = "Jack Grigg"
303303

304304
[[publisher.zcash_client_sqlite]]
305-
version = "0.18.2"
306-
when = "2025-09-29"
305+
version = "0.18.3"
306+
when = "2025-10-01"
307307
user-id = 169181
308308
user-login = "nuttycom"
309309
user-name = "Kris Nuttycombe"

zcash_client_sqlite/CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@ workspace.
1010

1111
## [Unreleased]
1212

13+
## [0.18.3] - 2025-09-30
14+
15+
### Changed
16+
- The `zcash_client_sqlite` implementation of `WalletWrite::update_chain_tip`
17+
now ensures that a transaction status request is queued for any transactions
18+
for which we do not have mined-height information and which are known to be
19+
unexpired.
20+
- Transaction status requests are no longer deleted until the transaction in
21+
question is positively known to be expired.
22+
1323
## [0.18.2] - 2025-09-28
1424

1525
### Changed

zcash_client_sqlite/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "zcash_client_sqlite"
33
description = "An SQLite-based Zcash light client"
4-
version = "0.18.2"
4+
version = "0.18.3"
55
authors = [
66
"Jack Grigg <jack@z.cash>",
77
"Kris Nuttycombe <kris@electriccoin.co>"

zcash_client_sqlite/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -896,7 +896,7 @@ impl<C: Borrow<rusqlite::Connection>, P: consensus::Parameters, CL, R> WalletRea
896896
}
897897

898898
fn get_tx_height(&self, txid: TxId) -> Result<Option<BlockHeight>, Self::Error> {
899-
wallet::get_tx_height(self.conn.borrow(), txid).map_err(SqliteClientError::from)
899+
wallet::get_tx_height(self.conn.borrow(), txid)
900900
}
901901

902902
fn get_unified_full_viewing_keys(

zcash_client_sqlite/src/wallet.rs

Lines changed: 102 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ use crate::{
130130
encoding::LEGACY_ADDRESS_INDEX_NULL,
131131
},
132132
AccountRef, AccountUuid, AddressRef, SqlTransaction, TransferType, TxRef,
133-
WalletCommitmentTrees, WalletDb, PRUNING_DEPTH,
133+
WalletCommitmentTrees, WalletDb, PRUNING_DEPTH, VERIFY_LOOKAHEAD,
134134
};
135135

136136
#[cfg(feature = "transparent-inputs")]
@@ -3010,12 +3010,21 @@ pub(crate) fn set_transaction_status(
30103010
match status {
30113011
TransactionStatus::TxidNotRecognized | TransactionStatus::NotInMainChain => {
30123012
// Remove the txid from the retrieval queue unless an unexpired transaction having that
3013-
// txid exists in the transactions table.
3013+
// txid exists in the transactions table, with a `VERIFY_LOOKAHEAD` buffer.
30143014
if let Some(chain_tip) = chain_tip_height(conn)? {
30153015
conn.execute(
30163016
"DELETE FROM tx_retrieval_queue
30173017
WHERE txid = :txid
3018-
AND request_expiry <= :chain_tip",
3018+
AND request_expiry <= :chain_tip
3019+
AND txid NOT IN (
3020+
SELECT tx.txid
3021+
FROM transactions tx
3022+
WHERE tx.mined_height IS NULL
3023+
AND (
3024+
tx.expiry_height == 0 -- tx will never expire
3025+
OR tx.expiry_height > :chain_tip -- tx is unexpired
3026+
)
3027+
)",
30193028
named_params![
30203029
":txid": txid.as_ref(),
30213030
":chain_tip": u32::from(chain_tip)
@@ -3054,6 +3063,67 @@ pub(crate) fn set_transaction_status(
30543063
Ok(())
30553064
}
30563065

3066+
pub(crate) fn refresh_status_requests(
3067+
conn: &rusqlite::Transaction,
3068+
) -> Result<(), SqliteClientError> {
3069+
if let Some(chain_tip) = chain_tip_height(conn)? {
3070+
let mut unmined_query = conn.prepare(
3071+
"SELECT
3072+
t.txid,
3073+
t.expiry_height,
3074+
r.query_type IS NOT NULL AS status_request_exists
3075+
FROM transactions t
3076+
LEFT OUTER JOIN tx_retrieval_queue r ON r.txid = t.txid
3077+
WHERE t.mined_height IS NULL
3078+
AND (
3079+
t.expiry_height IS NULL -- expiry is unknown
3080+
OR t.expiry_height = 0 -- tx will note expire
3081+
OR t.expiry_height > :chain_tip -- tx is unexpired
3082+
)
3083+
AND (
3084+
r.query_type IS NULL
3085+
OR r.query_type = :status_type
3086+
)",
3087+
)?;
3088+
3089+
let mut rows = unmined_query.query(named_params! {
3090+
":chain_tip": u32::from(chain_tip),
3091+
":status_type": TxQueryType::Status.code(),
3092+
})?;
3093+
while let Some(row) = rows.next()? {
3094+
let txid = row.get::<_, Vec<u8>>("txid")?;
3095+
let tx_expiry = row.get::<_, Option<u32>>("expiry_height")?;
3096+
3097+
if row.get::<_, bool>("status_request_exists")? {
3098+
conn.execute(
3099+
"UPDATE tx_retrieval_queue
3100+
SET request_expiry = :new_expiry
3101+
WHERE txid = :txid
3102+
AND query_type = :status_type",
3103+
named_params! {
3104+
":new_expiry": tx_retrieval_expiry(chain_tip, tx_expiry),
3105+
":txid": &txid[..],
3106+
":status_type": TxQueryType::Status.code(),
3107+
},
3108+
)?;
3109+
} else {
3110+
conn.execute(
3111+
"INSERT INTO tx_retrieval_queue (txid, query_type, request_expiry)
3112+
VALUES (:txid, :status_type, :new_expiry)
3113+
ON CONFLICT (txid) DO NOTHING",
3114+
named_params! {
3115+
":new_expiry": tx_retrieval_expiry(chain_tip, tx_expiry),
3116+
":txid": &txid[..],
3117+
":status_type": TxQueryType::Status.code(),
3118+
},
3119+
)?;
3120+
}
3121+
}
3122+
}
3123+
3124+
Ok(())
3125+
}
3126+
30573127
/// Truncates the database to at most the given height.
30583128
///
30593129
/// If the requested height is greater than or equal to the height of the last scanned
@@ -4118,7 +4188,12 @@ pub(crate) fn queue_tx_retrieval(
41184188
txids: impl Iterator<Item = TxId>,
41194189
dependent_tx_ref: Option<TxRef>,
41204190
) -> Result<(), SqliteClientError> {
4121-
let chain_tip = chain_tip_height(conn)?;
4191+
let chain_tip = match chain_tip_height(conn)? {
4192+
Some(h) => h,
4193+
None => {
4194+
return Ok(());
4195+
}
4196+
};
41224197

41234198
let mut q_tx_expiry = conn.prepare_cached(
41244199
"SELECT expiry_height
@@ -4150,7 +4225,7 @@ pub(crate) fn queue_tx_retrieval(
41504225
)?;
41514226

41524227
for txid in txids {
4153-
let request_expiry = q_tx_expiry
4228+
let tx_expiry = q_tx_expiry
41544229
.query_row(
41554230
named_params! {
41564231
":txid": txid.as_ref(),
@@ -4165,16 +4240,34 @@ pub(crate) fn queue_tx_retrieval(
41654240
":status_type": TxQueryType::Status.code(),
41664241
":enhancement_type": TxQueryType::Enhancement.code(),
41674242
":dependent_transaction_id": dependent_tx_ref.map(|r| r.0),
4168-
":request_expiry": request_expiry.map_or_else(
4169-
|| chain_tip.map(|h| u32::from(h) + DEFAULT_TX_EXPIRY_DELTA),
4170-
Some
4171-
)
4243+
":request_expiry": tx_retrieval_expiry(chain_tip, tx_expiry)
41724244
})?;
41734245
}
41744246

41754247
Ok(())
41764248
}
41774249

4250+
// Computes a block height at which transaction status requests should be considered
4251+
// expired. Note that even expired requests will be serviced; expiry is only used
4252+
// to determine when a request can be deleted and not retried.
4253+
//
4254+
// TODO: the underlying `request_expiry` mechanism that this method is being used to configure is
4255+
// not sufficiently precise and should probably be scrapped in favor of something better; however,
4256+
// this will do for now.
4257+
fn tx_retrieval_expiry(chain_tip_height: BlockHeight, tx_expiry: Option<u32>) -> u32 {
4258+
match tx_expiry {
4259+
// If the transaction never expires or we don't know its expiry, keep checking for the next
4260+
// expiry delta from the chain tip.
4261+
Some(0) | None => u32::from(chain_tip_height) + DEFAULT_TX_EXPIRY_DELTA,
4262+
// If the transaction expires in the future, the request can expire with the transaction.
4263+
Some(h) if h > u32::from(chain_tip_height) => h + VERIFY_LOOKAHEAD,
4264+
// The transaction has already expired; we can keep checking for a few blocks, but if we
4265+
// don't get a positive response for the transaction being mined soon, we can assume it
4266+
// will never be mined.
4267+
Some(_) => u32::from(chain_tip_height) + VERIFY_LOOKAHEAD,
4268+
}
4269+
}
4270+
41784271
/// Returns the vector of [`TransactionDataRequest`]s that represents the information needed by the
41794272
/// wallet backend in order to be able to present a complete view of wallet history and memo data.
41804273
pub(crate) fn transaction_data_requests(

zcash_client_sqlite/src/wallet/scanning.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use zcash_protocol::{
1515
ShieldedProtocol,
1616
};
1717

18+
use crate::wallet::refresh_status_requests;
1819
use crate::TableConstants;
1920
use crate::{
2021
error::SqliteClientError,
@@ -548,13 +549,17 @@ pub(crate) fn update_chain_tip<P: consensus::Parameters>(
548549
None => tip_entry.block_range().clone(),
549550
};
550551

552+
// persist the updated scan queue entries
551553
replace_queue_entries::<SqliteClientError>(
552554
conn,
553555
&query_range,
554556
tip_shard_entry.into_iter().chain(Some(tip_entry)),
555557
false,
556558
)?;
557559

560+
// ensure that transaction status requests exist for any unmined, unexpired transactions
561+
refresh_status_requests(conn)?;
562+
558563
Ok(())
559564
}
560565

@@ -1702,7 +1707,7 @@ pub(crate) mod tests {
17021707
// Add blocks up to the chain tip.
17031708
let mut chain_tip_height = spanning_block_height;
17041709
for _ in 0..110 {
1705-
let (h, res, _) = st.generate_next_block_multi(&vec![fake_output(false)]);
1710+
let (h, res, _) = st.generate_next_block_multi(&[fake_output(false)]);
17061711
for c in res.orchard() {
17071712
final_orchard_tree.append(*c);
17081713
}

0 commit comments

Comments
 (0)