Skip to content
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

zcash_client_sqlite: Assign UUIDs to each account #1631

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

str4d
Copy link
Contributor

@str4d str4d commented Nov 21, 2024

This provides equivalent uniqueness to the accounts table primary key, but avoids collisions across wallet recreation events (to defend against downstream crate users who don't flush any persisted account IDs at those events).

Closes #1629.

Copy link

codecov bot commented Nov 21, 2024

Codecov Report

Attention: Patch coverage is 69.56522% with 28 lines in your changes missing coverage. Please review.

Project coverage is 56.46%. Comparing base (2df4497) to head (bf42ec2).

Files with missing lines Patch % Lines
zcash_client_sqlite/src/wallet.rs 18.18% 18 Missing ⚠️
zcash_client_sqlite/src/lib.rs 0.00% 5 Missing ⚠️
...te/src/wallet/init/migrations/add_account_uuids.rs 92.18% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1631      +/-   ##
==========================================
+ Coverage   56.39%   56.46%   +0.07%     
==========================================
  Files         148      149       +1     
  Lines       19232    19319      +87     
==========================================
+ Hits        10845    10908      +63     
- Misses       8387     8411      +24     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.


🚨 Try these New Features:

@str4d str4d marked this pull request as ready for review November 22, 2024 02:48
@str4d
Copy link
Contributor Author

str4d commented Nov 22, 2024

I discovered that the Android SDK is misusing v_tx_outputs by putting to_account_id into a type that actually stores a ZIP 32 account index. These previously were indeed the same type, until #1235 was merged. I think we need to alter the views to expose the account UUIDs instead of the account IDs, particularly as after this PR the account IDs cannot be extracted from AccountId and are intended to be entirely internal.

/// This is an ephemeral value for efficiently and generically working with accounts in a
/// [`WalletDb`]. To reference accounts in external contexts, use [`AccountUuid`].
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
pub struct AccountId(u32);
Copy link
Contributor

@daira daira Nov 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be useful to rename this to LocalAccountId? Doing so is obviously a more disruptive change, but it would force all code currently using AccountId [directly as a type] to explicitly choose between AccountUuid and LocalAccountId. It would also, I think, reduce the chance of confusing these with ZIP 32 account indices (which is something I've found to be a hazard on several occasions).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Such a rename serves little purpose, because it is named directly for the WalletRead::AccountId associated type that it gets set to, and the associated type is what actually gets used in most places.

I'm somewhat annoyed that we now have this split where our local account ID that needs to be in WalletRead is infallible concretely but fallible in the trait API, which was in part due to #1235 getting rushed into a crate release (something I expressed disagreement with at the time). As it is, we'd need Yet Another Large Refactor to fix it, and I explicitly do not want to do that at the same time as these changes; our changelogs get large enough as it is.

Copy link
Contributor

@daira daira Nov 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confusion with the associated type is one reason I want to do this change, actually. It makes no sense to me to have the associated type be named the same as one particular possible instantiation of it. They are conceptually not the same thing. (See also https://github.com/Electric-Coin-Company/zcash-swift-wallet-sdk/pull/1515/files#r1855209493 .)

daira
daira previously approved these changes Nov 22, 2024
Copy link
Contributor

@daira daira left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK with comments.

@str4d
Copy link
Contributor Author

str4d commented Nov 23, 2024

Force-pushed to replace account_id in the views with account_uuid (because account_id is now intended to be an internal-only value). cc @AArnott for awareness (though I've also documented it in the changelogs).

@@ -779,8 +794,9 @@ pub(crate) fn get_derived_account<P: consensus::Parameters>(
":hd_account_index": u32::from(account_index),
Copy link
Contributor

@daira daira Nov 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused: stmt uses :account_id as a named parameter, but the name specified here is :hd_account_index. How was this working?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. https://docs.rs/rusqlite/latest/rusqlite/trait.Params.html#named-parameters :

Note: Unbound named parameters will be left to the value they previously were bound with, falling back to NULL for parameters which have never been bound.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a bug, and we've had that kind of bug before. It would be nice to figure out a way to lint for this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd settle for a runtime error. Substituting NULL is just terrible. 😭

Copy link
Contributor

@daira daira Nov 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you could have a macro to generate a struct and an impl rusqlite::Params for that struct from the query string. Then you could pass an instance of the struct rather than named_params!, and the compiler would check whether you'd missed out fields or included undefined fields. It could be made completely typesafe; you would have the statement type returned from the macro be parameterized by the struct type, so that it will only accept the right parameters. Sounds like a lot of effort to get working, though, unless someone has already done something like it.

@@ -767,7 +782,7 @@ pub(crate) fn get_derived_account<P: consensus::Parameters>(
account_index: zip32::AccountId,
) -> Result<Option<Account>, SqliteClientError> {
let mut stmt = conn.prepare(
"SELECT id, ufvk
"SELECT id, uuid, ufvk
FROM accounts
WHERE hd_seed_fingerprint = :hd_seed_fingerprint
AND hd_account_index = :account_id",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
AND hd_account_index = :account_id",
AND hd_account_index = :hd_account_index",

Copy link
Contributor

@daira daira left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

zcash_client_sqlite: Implement the new multi-seed-compatible account ID
2 participants