Skip to content

Commit

Permalink
Make ephemeral_addresses.address unique
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nuttycom committed Jul 17, 2024
1 parent 24b6d50 commit f8bedd8
Show file tree
Hide file tree
Showing 4 changed files with 18 additions and 18 deletions.
10 changes: 4 additions & 6 deletions zcash_client_sqlite/src/wallet/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ CREATE INDEX "addresses_accounts" ON "addresses" (
///
/// All but the last `GAP_LIMIT` addresses are defined to be "reserved" addresses. Since the next
/// index to reserve is determined by dead reckoning from the last stored address, we use dummy
/// entries after the maximum valid index in order to allow the last `GAP_LIMIT` addresses at the
/// end of the index range to be used.
/// entries having `NULL` for the value of the `address` column after the maximum valid index in
/// order to allow the last `GAP_LIMIT` addresses at the end of the index range to be used.
///
/// Note that the fact that `used_in_tx` references a specific transaction is just a debugging aid.
/// The same is mostly true of `seen_in_tx`, but we also take into account whether the referenced
Expand All @@ -116,13 +116,15 @@ pub(super) const TABLE_EPHEMERAL_ADDRESSES: &str = r#"
CREATE TABLE ephemeral_addresses (
account_id INTEGER NOT NULL,
address_index INTEGER NOT NULL,
-- nullability of this column is controlled by the index_range_and_address_nullity check
address TEXT,
used_in_tx INTEGER,
seen_in_tx INTEGER,
FOREIGN KEY (account_id) REFERENCES accounts(id),
FOREIGN KEY (used_in_tx) REFERENCES transactions(id_tx),
FOREIGN KEY (seen_in_tx) REFERENCES transactions(id_tx),
PRIMARY KEY (account_id, address_index),
CONSTRAINT ephemeral_addr_uniq UNIQUE (address),
CONSTRAINT used_implies_seen CHECK (
used_in_tx IS NULL OR seen_in_tx IS NOT NULL
),
Expand All @@ -135,10 +137,6 @@ CREATE TABLE ephemeral_addresses (
// libsqlite3-sys requires at least version 3.14.0.
// "WITHOUT ROWID" tells SQLite to use a clustered index on the (composite) primary key.
const_assert_eq!(GAP_LIMIT, 20);
pub(super) const INDEX_EPHEMERAL_ADDRESSES_ADDRESS: &str = r#"
CREATE INDEX ephemeral_addresses_address ON ephemeral_addresses (
address ASC
)"#;

/// Stores information about every block that the wallet has scanned.
///
Expand Down
1 change: 0 additions & 1 deletion zcash_client_sqlite/src/wallet/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,6 @@ mod tests {
db::INDEX_ACCOUNTS_UIVK,
db::INDEX_HD_ACCOUNT,
db::INDEX_ADDRESSES_ACCOUNTS,
db::INDEX_EPHEMERAL_ADDRESSES_ADDRESS,
db::INDEX_NF_MAP_LOCATOR_IDX,
db::INDEX_ORCHARD_RECEIVED_NOTES_ACCOUNT,
db::INDEX_ORCHARD_RECEIVED_NOTES_TX,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,24 +45,23 @@ impl<P: consensus::Parameters> RusqliteMigration for Migration<P> {
"CREATE TABLE ephemeral_addresses (
account_id INTEGER NOT NULL,
address_index INTEGER NOT NULL,
-- nullability of this column is controlled by the index_range_and_address_nullity check
address TEXT,
used_in_tx INTEGER,
seen_in_tx INTEGER,
FOREIGN KEY (account_id) REFERENCES accounts(id),
FOREIGN KEY (used_in_tx) REFERENCES transactions(id_tx),
FOREIGN KEY (seen_in_tx) REFERENCES transactions(id_tx),
PRIMARY KEY (account_id, address_index),
CONSTRAINT ephemeral_addr_uniq UNIQUE (address),
CONSTRAINT used_implies_seen CHECK (
used_in_tx IS NULL OR seen_in_tx IS NOT NULL
),
CONSTRAINT index_range_and_address_nullity CHECK (
(address_index BETWEEN 0 AND 0x7FFFFFFF AND address IS NOT NULL) OR
(address_index BETWEEN 0x80000000 AND 0x7FFFFFFF + 20 AND address IS NULL AND used_in_tx IS NULL AND seen_in_tx IS NULL)
)
) WITHOUT ROWID;
CREATE INDEX ephemeral_addresses_address ON ephemeral_addresses (
address ASC
);",
) WITHOUT ROWID;"
)?;

// Make sure that at least `GAP_LIMIT` ephemeral transparent addresses are
Expand Down
18 changes: 11 additions & 7 deletions zcash_client_sqlite/src/wallet/transparent/ephemeral.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,14 +282,18 @@ fn reserve_until<P: consensus::Parameters>(
)?;

for raw_index in range_to_store {
let address_str_opt = match NonHardenedChildIndex::from_index(raw_index) {
Some(address_index) => Some(
// The range to store may contain indicies that are out of the valid range of non hardened
// child indices; we still store explicit rows in the ephemeral_addresses table for these
// so that it's possible to find the first unused address using dead reckoning with the gap
// limit.
let address_str_opt = NonHardenedChildIndex::from_index(raw_index)
.map(|address_index| {
ephemeral_ivk
.derive_ephemeral_address(address_index)?
.encode(params),
),
None => None,
};
.derive_ephemeral_address(address_index)
.map(|addr| addr.encode(params))
})
.transpose()?;

stmt_insert_ephemeral_address.execute(named_params![
":account_id": account_id.0,
":address_index": raw_index,
Expand Down

0 comments on commit f8bedd8

Please sign in to comment.