-
Notifications
You must be signed in to change notification settings - Fork 251
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add transparent address gap limit handling & general address rotation functionality. #1673
base: main
Are you sure you want to change the base?
Add transparent address gap limit handling & general address rotation functionality. #1673
Conversation
62c1394
to
bd2df86
Compare
bd2df86
to
4b99663
Compare
1386dd1
to
49230de
Compare
…handling-prep Preparatory refactoring for #1673
b39c6c3
to
3ac0396
Compare
/// - `cached_transparent_receiver_address`: the transparent receiver component of address (which | ||
/// may be the same as `address` in the case of an internal-scope transparent change address or a | ||
/// ZIP 320 interstitial address). It is cached directly in the table to make account lookups for | ||
/// transparent outputs more efficient, enabling joins to [`TABLE_TRANSPARENT_RECEIVED_OUTPUTS`]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This documentation is now AFAICT incorrect. It is not "the transparent receiver component of address" because there are cases where address
does not contain a transparent receiver, and yet we still populate this column.
Assuming that this is guaranteed to always be the transparent receiver derived from the same diversifier index as address
:
/// - `cached_transparent_receiver_address`: the transparent receiver component of address (which | |
/// may be the same as `address` in the case of an internal-scope transparent change address or a | |
/// ZIP 320 interstitial address). It is cached directly in the table to make account lookups for | |
/// transparent outputs more efficient, enabling joins to [`TABLE_TRANSPARENT_RECEIVED_OUTPUTS`]. | |
/// - `cached_transparent_receiver_address`: the transparent address derived from the same | |
/// viewing key and at the same diversifier index as `address`. This may be the same as | |
/// `address` in the case of an internal-scope transparent change address or a | |
/// ZIP 320 interstitial address, and it may be a receiver within `address` in the case of a | |
/// Unified Address with transparent receiver. It is cached directly in the table to make account lookups for | |
/// transparent outputs more efficient, enabling joins to [`TABLE_TRANSPARENT_RECEIVED_OUTPUTS`]. |
/// - `transparent_child_index`: the diversifier index, if it is in the range of a non-hardened | ||
/// transparent address index. This is used for gap limit handling and is always populated if the | ||
/// diversifier index is in that range; since the diversifier index is stored as a byte array we | ||
/// cannot use SQL integer operations on it and thus need it as an integer as well. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// - `transparent_child_index`: the diversifier index, if it is in the range of a non-hardened | |
/// transparent address index. This is used for gap limit handling and is always populated if the | |
/// diversifier index is in that range; since the diversifier index is stored as a byte array we | |
/// cannot use SQL integer operations on it and thus need it as an integer as well. | |
/// - `transparent_child_index`: the diversifier index in integer form, if it is in the range of a `u31` | |
/// (i.e. a non-hardened transparent address index). It is used for gap limit handling, and is set | |
/// whenever a transparent address at a given index should be scanned at receive time. This | |
/// includes: | |
/// - Unified Addresses with transparent receivers (at any valid index). | |
/// - Unified Addresses without transparent receivers, but within the gap limit of potential | |
/// sequential transparent addresses. | |
/// - Transparent change addresses. | |
/// - ZIP 320 ephemeral addresses. | |
/// This column exists because the diversifier index is stored as a byte array, meaning that we | |
/// cannot use SQL integer operations on it for gap limit calculations, and thus need it as an | |
/// integer as well. |
3ac0396
to
24d611e
Compare
/// - `exposed_at_height`: The chain tip height at the time that the address was generated by an | ||
/// explicit request by the user or reserved for use in a ZIP 320 transaction. In the case of an | ||
/// address with its first use discovered in a transaction obtained by scanning the chain, this | ||
/// will be set to the mined height of that transaction. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// - `exposed_at_height`: The chain tip height at the time that the address was generated by an | |
/// explicit request by the user or reserved for use in a ZIP 320 transaction. In the case of an | |
/// address with its first use discovered in a transaction obtained by scanning the chain, this | |
/// will be set to the mined height of that transaction. | |
/// - `exposed_at_height`: Our best knowledge as to when this address was first exposed to | |
/// the wider ecosystem. | |
/// - For user-generated addresses, this is the chain tip height at the time that the address | |
/// was generated by an explicit request by the user or reserved for use in a ZIP 320 | |
/// transaction. These heights are not recoverable from chain. | |
/// - In the case of an address with its first use discovered in a transaction obtained by | |
/// scanning the chain, this will be set to the mined height of that transaction. In recover | |
/// from seed cases, this is what user-generated addresses will be assigned. |
SELECT account_id, accounts.uivk AS uivk, diversifier_index_be | ||
FROM addresses | ||
JOIN accounts ON accounts.id = account_id | ||
GROUP BY account_id, uivk, diversifier_index_be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should already be distinct, because all existing addresses were external, and (account_id, diversifier_index_be)
was already constrained to be unique.
cached_transparent_receiver_address = :t_addr | ||
WHERE account_id = :account_id | ||
AND diversifier_index_be = :diversifier_index_be | ||
AND key_scope = :external_scope_code |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not technically required as we just added this column so everything has external scope.
let diversifier_index = DiversifierIndex::from( | ||
u32::try_from(transparent_child_index).map_err(|_| { | ||
WalletMigrationError::CorruptedData( | ||
"ephermeral address indices must be in the range of `u32`".to_owned(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe go via NonHardenedChildIndex
before DiversifierIndex
to detect any "u32
but not u31
" corruption.
@@ -216,6 +186,7 @@ CREATE TABLE "sapling_received_notes" ( | |||
memo BLOB, | |||
commitment_tree_position INTEGER, | |||
recipient_key_scope INTEGER, | |||
address_id INTEGER REFERENCES addresses(id), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Document this new column (and its nullability).
@@ -269,6 +240,7 @@ CREATE TABLE orchard_received_notes ( | |||
memo BLOB, | |||
commitment_tree_position INTEGER, | |||
recipient_key_scope INTEGER, | |||
address_id INTEGER REFERENCES addresses(id), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Document this new column (and its nullability).
@@ -339,6 +311,7 @@ CREATE TABLE transparent_received_outputs ( | |||
script BLOB NOT NULL, | |||
value_zat INTEGER NOT NULL, | |||
max_observed_unspent_height INTEGER, | |||
address_id INTEGER REFERENCES addresses(id), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Document this new column (and the reason it was left nullable).
:cached_transparent_receiver_address, | ||
:exposed_at_height | ||
) | ||
ON CONFLICT (account_id, diversifier_index_be, key_scope) DO UPDATE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the case that this upsert is triggered by a note that was received in a pool that the conflicting row's address does not contain, we should upgrade the row to contain that receiver (similar to what we do in zcashd
), because this is evidence of that receiver being exposed to the ecosystem. It's not technically evidence of other receivers in address
being exposed, so we might only want to add the one receiver triggering the upsert to the conflicting row.
24d611e
to
68e1864
Compare
WITH offsets AS ( | ||
SELECT | ||
a.transparent_child_index, | ||
LEAD(a.transparent_child_index) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check what SQLite version introduced this syntax.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was introduced in 3.25.0
transparent_child_index + 1, | ||
next_child_index - transparent_child_index - 1 AS gap_len | ||
FROM offsets | ||
WHERE gap_len >= :gap_limit OR next_child_index IS NULL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a comment here clarifying that if gap_len
is equal to gap_limit
(and by extension, or greater than), then next_child_index
would not be detected by gap-limit-following scanning logic, and thus we've found the start of the corresponding gap.
) | ||
SELECT | ||
transparent_child_index + 1, | ||
next_child_index - transparent_child_index - 1 AS gap_len |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a comment here clarifying that both next_child_index
and transparent_child_index
are used, so the unused gap between them is one less than their separation.
ac23d07
to
81b05c1
Compare
This is a large change that unifies the handling of ephemeral transparent addresses for ZIP 320 support with generalized "gap limit" handling for transparent wallet recovery. The best way to understand this commit is to start from the `transparent_gap_limit_handling` database migration that drives the change in behavior.
81b05c1
to
6238af4
Compare
No description provided.