Skip to content

Implement z_importkey and z_exportkey for Sapling#400

Open
oxarbitrage wants to merge 1 commit intomainfrom
z_importexportkey
Open

Implement z_importkey and z_exportkey for Sapling#400
oxarbitrage wants to merge 1 commit intomainfrom
z_importexportkey

Conversation

@oxarbitrage
Copy link
Copy Markdown
Contributor

Adds two new RPC methods:

  • z_importkey: Imports a Sapling extended spending key into the wallet. The key is encrypted with age and stored in the keystore. A UFVK is derived and imported into the wallet database so the sync engine can track transactions. The rescan parameter controls whether historical blocks are scanned ("yes", "no", or "whenkeyisnew"), and start_height sets the birthday for the imported account. Real treestates are fetched from the chain indexer for non-zero start heights.

  • z_exportkey: Exports the spending key for a Sapling payment address. Requires the wallet to be unlocked. Looks up the key by iterating standalone sapling DFVKs and using decrypt_diversifier to match the address.

Also adds fetch_account_birthday to json_rpc::utils as a shared helper for building an AccountBirthday from a chain treestate.

Includes unit tests for parameter validation, key encoding/decoding roundtrips, and address parsing.

Close #69
Close #79

Adds two new RPC methods:

- `z_importkey`: Imports a Sapling extended spending key into the wallet.
  The key is encrypted with age and stored in the keystore. A UFVK is
  derived and imported into the wallet database so the sync engine can
  track transactions. The rescan parameter controls whether historical
  blocks are scanned ("yes", "no", or "whenkeyisnew"), and start_height
  sets the birthday for the imported account. Real treestates are fetched
  from the chain indexer for non-zero start heights.

- `z_exportkey`: Exports the spending key for a Sapling payment address.
  Requires the wallet to be unlocked. Looks up the key by iterating
  standalone sapling DFVKs and using decrypt_diversifier to match the
  address.

Also adds `fetch_account_birthday` to `json_rpc::utils` as a shared
helper for building an AccountBirthday from a chain treestate.

Includes unit tests for parameter validation, key encoding/decoding
roundtrips, and address parsing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@nuttycom
Copy link
Copy Markdown
Contributor

I'm not sure that it makes sense to implement the legacy versions of these methods; their utility is extremely limited and I would prefer that we avoid introducing API surface that we then have to maintain. Both of these methods got zero points in our survey of what people are using: https://docs.google.com/spreadsheets/d/1UJxH1cowexGqadU32Uei5Qak6jGhXjb18-T_QBPmDAA/edit?gid=0#gid=0

@nuttycom
Copy link
Copy Markdown
Contributor

I guess that I can see implementing z_importkey for Sapling paper wallet users. But key export in particular I'm skeptical of - it seems like an avenue to lose funds if you trust z_exportkey for backup without realizing that there might be Orchard funds associated with the same root of spending authority.

@oxarbitrage
Copy link
Copy Markdown
Contributor Author

Thanks for the feedback.

I'm not sure that it makes sense to implement the legacy versions of these methods; their utility is extremely limited and I would prefer that we avoid introducing API surface that we then have to maintain. Both of these methods got zero points in our survey of what people are using: https://docs.google.com/spreadsheets/d/1UJxH1cowexGqadU32Uei5Qak6jGhXjb18-T_QBPmDAA/edit?gid=0#gid=0

I wasn’t aware of the survey, thanks for sharing it. I’ll consider it for future work. It might also make sense to close or re-evaluate issues in the repository that correspond to methods with zero usage.

That said, I do think import-related APIs may become more valuable as zcashd is deprecated and users look for ways to migrate into the new stack.

I guess that I can see implementing z_importkey for Sapling paper wallet users.

Not only for paper wallets. If you have a legacy Sapling key (with funds) and want to import it into the new stack without relying on zcashd, this provides a viable path. I used this same RPC method in zcashd to achieve that migration.

But key export in particular I'm skeptical of - it seems like an avenue to lose funds if you trust z_exportkey for backup without realizing that there might be Orchard funds associated with the same root of spending authority.

That makes sense. The export functionality was mainly added for completeness. I’m happy to remove it, or alternatively keep it with clearer documentation highlighting the risks.

Let me know how you’d prefer to proceed. Happy to adjust the PR accordingly.

@oxarbitrage
Copy link
Copy Markdown
Contributor Author

By the way, I should have provided more context earlier. The main motivation here was recovering legacy funds that were otherwise inaccessible.

I had the mnemonic, but none of the available tools worked for recovery (zeccavator, zodl/zashi, and ywallet all failed in this case). The only viable path was to derive the Sapling key from the mnemonic (I wrote a small tool for that: https://github.com/oxarbitrage/zcash-sapling-derive) and then import it into zcashd using z_importkey.

That worked for me, but it made me realize that this recovery path would no longer be available once zcashd is deprecated. That’s what motivated porting these calls into zallet.

@nuttycom nuttycom requested review from nullcopy and nuttycom April 6, 2026 16:42
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.

rpc: Implement z_importkey rpc: Implement z_exportkey

2 participants