Skip to content

Commit f8bedd8

Browse files
committed
Make ephemeral_addresses.address unique
This also provides additional documentation for why it's necessary to store ephemeral_addresses table entries at indicies that do not correspond to valid addresses.
1 parent 24b6d50 commit f8bedd8

File tree

4 files changed

+18
-18
lines changed

4 files changed

+18
-18
lines changed

zcash_client_sqlite/src/wallet/db.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ CREATE INDEX "addresses_accounts" ON "addresses" (
106106
///
107107
/// All but the last `GAP_LIMIT` addresses are defined to be "reserved" addresses. Since the next
108108
/// index to reserve is determined by dead reckoning from the last stored address, we use dummy
109-
/// entries after the maximum valid index in order to allow the last `GAP_LIMIT` addresses at the
110-
/// end of the index range to be used.
109+
/// entries having `NULL` for the value of the `address` column after the maximum valid index in
110+
/// order to allow the last `GAP_LIMIT` addresses at the end of the index range to be used.
111111
///
112112
/// Note that the fact that `used_in_tx` references a specific transaction is just a debugging aid.
113113
/// The same is mostly true of `seen_in_tx`, but we also take into account whether the referenced
@@ -116,13 +116,15 @@ pub(super) const TABLE_EPHEMERAL_ADDRESSES: &str = r#"
116116
CREATE TABLE ephemeral_addresses (
117117
account_id INTEGER NOT NULL,
118118
address_index INTEGER NOT NULL,
119+
-- nullability of this column is controlled by the index_range_and_address_nullity check
119120
address TEXT,
120121
used_in_tx INTEGER,
121122
seen_in_tx INTEGER,
122123
FOREIGN KEY (account_id) REFERENCES accounts(id),
123124
FOREIGN KEY (used_in_tx) REFERENCES transactions(id_tx),
124125
FOREIGN KEY (seen_in_tx) REFERENCES transactions(id_tx),
125126
PRIMARY KEY (account_id, address_index),
127+
CONSTRAINT ephemeral_addr_uniq UNIQUE (address),
126128
CONSTRAINT used_implies_seen CHECK (
127129
used_in_tx IS NULL OR seen_in_tx IS NOT NULL
128130
),
@@ -135,10 +137,6 @@ CREATE TABLE ephemeral_addresses (
135137
// libsqlite3-sys requires at least version 3.14.0.
136138
// "WITHOUT ROWID" tells SQLite to use a clustered index on the (composite) primary key.
137139
const_assert_eq!(GAP_LIMIT, 20);
138-
pub(super) const INDEX_EPHEMERAL_ADDRESSES_ADDRESS: &str = r#"
139-
CREATE INDEX ephemeral_addresses_address ON ephemeral_addresses (
140-
address ASC
141-
)"#;
142140

143141
/// Stores information about every block that the wallet has scanned.
144142
///

zcash_client_sqlite/src/wallet/init.rs

-1
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,6 @@ mod tests {
422422
db::INDEX_ACCOUNTS_UIVK,
423423
db::INDEX_HD_ACCOUNT,
424424
db::INDEX_ADDRESSES_ACCOUNTS,
425-
db::INDEX_EPHEMERAL_ADDRESSES_ADDRESS,
426425
db::INDEX_NF_MAP_LOCATOR_IDX,
427426
db::INDEX_ORCHARD_RECEIVED_NOTES_ACCOUNT,
428427
db::INDEX_ORCHARD_RECEIVED_NOTES_TX,

zcash_client_sqlite/src/wallet/init/migrations/ephemeral_addresses.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -45,24 +45,23 @@ impl<P: consensus::Parameters> RusqliteMigration for Migration<P> {
4545
"CREATE TABLE ephemeral_addresses (
4646
account_id INTEGER NOT NULL,
4747
address_index INTEGER NOT NULL,
48+
-- nullability of this column is controlled by the index_range_and_address_nullity check
4849
address TEXT,
4950
used_in_tx INTEGER,
5051
seen_in_tx INTEGER,
5152
FOREIGN KEY (account_id) REFERENCES accounts(id),
5253
FOREIGN KEY (used_in_tx) REFERENCES transactions(id_tx),
5354
FOREIGN KEY (seen_in_tx) REFERENCES transactions(id_tx),
5455
PRIMARY KEY (account_id, address_index),
56+
CONSTRAINT ephemeral_addr_uniq UNIQUE (address),
5557
CONSTRAINT used_implies_seen CHECK (
5658
used_in_tx IS NULL OR seen_in_tx IS NOT NULL
5759
),
5860
CONSTRAINT index_range_and_address_nullity CHECK (
5961
(address_index BETWEEN 0 AND 0x7FFFFFFF AND address IS NOT NULL) OR
6062
(address_index BETWEEN 0x80000000 AND 0x7FFFFFFF + 20 AND address IS NULL AND used_in_tx IS NULL AND seen_in_tx IS NULL)
6163
)
62-
) WITHOUT ROWID;
63-
CREATE INDEX ephemeral_addresses_address ON ephemeral_addresses (
64-
address ASC
65-
);",
64+
) WITHOUT ROWID;"
6665
)?;
6766

6867
// Make sure that at least `GAP_LIMIT` ephemeral transparent addresses are

zcash_client_sqlite/src/wallet/transparent/ephemeral.rs

+11-7
Original file line numberDiff line numberDiff line change
@@ -282,14 +282,18 @@ fn reserve_until<P: consensus::Parameters>(
282282
)?;
283283

284284
for raw_index in range_to_store {
285-
let address_str_opt = match NonHardenedChildIndex::from_index(raw_index) {
286-
Some(address_index) => Some(
285+
// The range to store may contain indicies that are out of the valid range of non hardened
286+
// child indices; we still store explicit rows in the ephemeral_addresses table for these
287+
// so that it's possible to find the first unused address using dead reckoning with the gap
288+
// limit.
289+
let address_str_opt = NonHardenedChildIndex::from_index(raw_index)
290+
.map(|address_index| {
287291
ephemeral_ivk
288-
.derive_ephemeral_address(address_index)?
289-
.encode(params),
290-
),
291-
None => None,
292-
};
292+
.derive_ephemeral_address(address_index)
293+
.map(|addr| addr.encode(params))
294+
})
295+
.transpose()?;
296+
293297
stmt_insert_ephemeral_address.execute(named_params![
294298
":account_id": account_id.0,
295299
":address_index": raw_index,

0 commit comments

Comments
 (0)