-
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
Give zcash_client_sqlite its own account ID #1175
Conversation
b5e03f1
to
059f84b
Compare
bed9551
to
c06baed
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1175 +/- ##
==========================================
+ Coverage 63.89% 64.20% +0.30%
==========================================
Files 115 116 +1
Lines 11930 12194 +264
==========================================
+ Hits 7623 7829 +206
- Misses 4307 4365 +58 ☔ View full report in Codecov by Sentry. |
I'll have to do a rebase to deal with the rebase in #1185. Assuming your policy is to merge PRs (which I like), I'll rebase not only to resolve the stale merge but to clean up my commits here too, many of which have junk messages. I'll probably just make one commit of all of them as that'll be simpler. Any concerns? |
This is work toward supporting arbitrary imported keys, which may not be based on seeds (as far as we know) and may consist only of a full viewing key or incoming viewing key. - Several tests needed to be updated because they assumed the result of `create_account` was a ZIP-32 account index where it was really only documented to return an account id or that the first account id would be 0. In fact in the sqlite implementation these two values are no longer the same. I've updated the docs and CHANGELOG to call out this behavior. - In addition to adding a few columns to the `accounts` table, I also *renamed* a couple of existing columns to avoid their being used with their old semantics. I also added indexes for the FK columns of each table that had to be migrated. - The `add_utxo_account` migration was improperly relying on internal crate code to query the database as part of migration. The problem with this is the internal crate code assumes the _latest_ db schema, whereas the migration must be able to run with an earlier schema. With this PR, a later migration occurs that breaks the schema in ways that required internal crate updates, and a migration that runs _before_ those updates then failed when calling into the 'newer' crate code. The fix is to remove the dependency: migrations should never make indirect sql calls through non-migration code. I copied the two functions from the regular code into the migration and reverted the changes I had made to them so that the migration works again. This code in the migration may be trimmable to something smaller to fit what the migration needs, but I didn't want to rewrite it. - The new db schema renames the `accounts.ufvk` column to `uvk` to allow for UFVKs today, and UIVKs tomorrow. Code remains in the crate itself that only tries to parse the value of this column as a UFVK because no UIVK type exists yet. But I've tried to model the APIs to allow for adding support for that with no further breaking changes.
a3afd81
to
862efdb
Compare
I've pushed the rebase. |
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.
My review isn't yet complete, but I'm flushing comments now as what I've requested thus far is a good chunk of work.
a319684
to
5e0e9a3
Compare
Yes, this makes sense to do. However, for reviewability it helps if you are able to separate the rebase (and merge conflict resolution) force push, from the force push that adjusts commit history. (I post comments like #1173 (comment) and #1135 (comment) when doing so, to help reviewers keep track.) |
zcash_client_sqlite/src/wallet/init/migrations/add_utxo_account.rs
Outdated
Show resolved
Hide resolved
zcash_client_sqlite/src/wallet/init/migrations/add_utxo_account.rs
Outdated
Show resolved
Hide resolved
zcash_client_sqlite/src/wallet/init/migrations/add_utxo_account.rs
Outdated
Show resolved
Hide resolved
zcash_client_sqlite/src/wallet/init/migrations/addresses_table.rs
Outdated
Show resolved
Hide resolved
zcash_client_sqlite/src/wallet/init/migrations/full_account_ids.rs
Outdated
Show resolved
Hide resolved
zcash_client_sqlite/src/wallet/init/migrations/full_account_ids.rs
Outdated
Show resolved
Hide resolved
@@ -64,9 +62,8 @@ impl<P: consensus::Parameters> RusqliteMigration for Migration<P> { | |||
|
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.
@daira the entire migration takes place inside a single 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.
utACK modulo comments.
- Also verify that the migration-provided seed matches the ufvk's already in the table.
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.
Reviewed as of cc6e715.
ivks.push(Ivk::P2pkh(transparent.serialize().try_into().map_err( | ||
|_| { | ||
SqliteClientError::BadAccountData( | ||
"Unable to serialize transparent IVK.".to_string(), | ||
) | ||
}, | ||
)?)); |
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.
Bug: This is serializing the transparent FVK, not the external transparent IVK.
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.
It's funny you bring this up, because in an earlier iteration I had the code first get the external transparent IVK. But that caused tests to fail when they took the UIVK's transparent component and repeated the external IVK derivation. Maybe I 'fixed' the test in the wrong place. I don't think of transparent keys offering an IVK option, so I just stored the whole public key in there.
But now that you mention it, I look at ZIP-316 and find this:
For a Transparent P2PKH FVK, the corresponding Transparent P2PKH IVK is obtained by deriving the child key with non-hardened index 0 as described in 32.
That seems to verify your expectation. I'll make the change.
}) | ||
} | ||
|
||
pub(crate) fn ufvk_to_uivk<P: consensus::Parameters>( |
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 method should either be documented as always producing the external UIVK, or take a zip32::Scope
argument. Probably the former, given the apparent invariant that if ufvk != NULL
, then uivk
MUST be the external UIVK.
for item in uivk.items() { | ||
if let Ivk::P2pkh(ivk) = item { | ||
let pubkey = AccountPubKey::deserialize(&ivk)?; | ||
return Ok(Some(pubkey.derive_external_ivk()?.default_address())); |
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.
Bug: the UIVK contains the transparent external IVK. This is related to the earlier bug; for UFVKs this line "cancels out" the earlier bug, but for imported UIVKs this will derive the wrong address. The old code that this is replacing used a UFVK here, so this derivation path was correct (only) in that context.
let ufvk = if let Some(ufvk_str) = ufvk_str { | ||
Some( | ||
UnifiedFullViewingKey::decode(&db.params, &ufvk_str[..]) | ||
.map_err(SqliteClientError::BadAccountData)?, | ||
) | ||
} else { | ||
None | ||
}; |
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.
Rust note: this is more clean and less brittle as:
let ufvk = if let Some(ufvk_str) = ufvk_str { | |
Some( | |
UnifiedFullViewingKey::decode(&db.params, &ufvk_str[..]) | |
.map_err(SqliteClientError::BadAccountData)?, | |
) | |
} else { | |
None | |
}; | |
let ufvk = ufvk_str.map(|ufvk_str| { | |
UnifiedFullViewingKey::decode(&db.params, &ufvk_str[..]) | |
.map_err(SqliteClientError::BadAccountData)?, | |
}); |
I've opened #1235 which is a squashed version of this PR. I will fix the bugs in that PR (as I cannot push to this PR). |
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.
Flushing comments.
params: &P, | ||
) -> Result<String, SqliteClientError> { | ||
let mut ivks: Vec<Ivk> = Vec::new(); | ||
if let Some(orchard) = ufvk.orchard() { |
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 will need to be behind #[cfg(feature = "orchard")]
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 is not currently necessary because we always enable zcash_keys/orchard
for backwards-compatibility. Once we finish Orchard support in zcash_client_sqlite
it will become necessary, but for now it's fine.
This is work toward supporting arbitrary imported keys, which may not be based on seeds (as far as we know) and may consist only of a full viewing key or incoming viewing key.
Closes #1218.
Additional work
UnifiedIncomingViewingKey
as a type and then aUnifiedViewingKey
enum that will allow code to start preparing for the possibility of UIVKs being in theaccounts
table instead of it always being UFVKs.migrations.md
file.Interesting things of note
create_account
was a ZIP-32 account index where it was really only documented to return an account id or that the first account id would be 0. In fact in the sqlite implementation these two values are no longer the same. I've updated the docs and CHANGELOG to call out this behavior.accounts
table, I also renamed a couple of existing columns to avoid their being used with their old semantics. I also added indexes for the FK columns of each table that had to be migrated.add_utxo_account
migration was improperly relying on internal crate code to query the database as part of migration. The problem with this is the internal crate code assumes the latest db schema, whereas the migration must be able to run with an earlier schema. With this PR, a later migration occurs that breaks the schema in ways that required internal crate updates, and a migration that runs before those updates then failed when calling into the 'newer' crate code. The fix is to remove the dependency: migrations should never make indirect sql calls through non-migration code. I copied the two functions from the regular code into the migration and reverted the changes I had made to them so that the migration works again. This code in the migration may be trimmable to something smaller to fit what the migration needs, but I didn't want to rewrite it.accounts.ufvk
column touvk
to allow for UFVKs today, and UIVKs tomorrow. Code remains in the crate itself that only tries to parse the value of this column as a UFVK because no UIVK type exists yet. But I've tried to model the APIs to allow for adding support for that with no further breaking changes.This contributes toward #1075.