Skip to content

Conversation

@lamb356
Copy link

@lamb356 lamb356 commented Jan 6, 2026

Summary

Implements 7 PCZT (Partially Created Zcash Transaction) RPC methods for Zallet, addressing issue #99.

Working Implementations

  • pczt_create - Create empty PCZT structure with consensus parameters
  • pczt_decode - Decode and inspect PCZT contents (transaction counts, version info)
  • pczt_combine - Merge multiple PCZTs using the Combiner role
  • pczt_finalize - Run IO finalization via IoFinalizer
  • pczt_extract - Extract final transaction from completed PCZT

Stubbed Methods (pending upstream support)

  • pczt_fund - Requires AccountUuid to implement Serialize
  • pczt_sign - Requires wallet key access patterns

Technical Details

  • Uses pczt 0.5.0 crate API
  • Adds pczt feature flag to zcash_client_backend
  • Follows existing zallet RPC patterns (see z_send_many.rs)

Testing

  • cargo check --package zallet passes
  • Unit tests (pending)
  • Integration tests (pending)

Closes #99

root added 2 commits January 6, 2026 00:30
Implements 7 PCZT RPC methods for Partially Created Zcash Transactions:

Working implementations:
- pczt_create: Create empty PCZT structure
- pczt_decode: Decode and inspect PCZT contents
- pczt_combine: Merge multiple PCZTs
- pczt_finalize: Run IO finalization
- pczt_extract: Extract final transaction from completed PCZT

Stubbed (pending upstream support):
- pczt_fund: Requires AccountUuid serialization
- pczt_sign: Requires wallet key access patterns

Uses pczt 0.5.0 crate API. Adds 'pczt' feature to zcash_client_backend.
@lamb356
Copy link
Author

lamb356 commented Jan 6, 2026

Review Feedback Summary

Thanks to code review, here are issues to address:

Critical (DoS Prevention)

  • Add input size limits before Base64::decode_vec() in all methods
  • Cap pczts.len() in pczt_combine (suggest max 20)

Correctness

  • pczt_create: target_height.unwrap_or(1) should get current height from chain or be required
  • pczt_create: lock_time parameter is accepted but ignored - implement or remove
  • pczt_extract: Add proof verification (or explicit skip_verification flag)

Nice to Have

  • Use {e} instead of {e:?} in error messages (avoid leaking internal state)
  • pczt_finalize: Remove redundant finalized: true field
  • pczt_decode: Add diagnostic fields (has_anchor, signatures_present, etc.)

Will address in follow-up commits. Feedback welcome on priorities.

root and others added 10 commits January 6, 2026 01:10
- Add MAX_PCZT_BASE64_LEN size limits to prevent DoS
- Add MAX_PCZTS_TO_COMBINE fan-in limit (20) in pczt_combine
- Change {e:?} to {e} in error messages to avoid leaking internal state
- Remove redundant 'finalized' field from FinalizeResult
Documents signing hints approach, keystore patterns, and implementation
plans for pczt_fund and pczt_sign. Includes open questions for str4d.
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Use create_pczt_from_proposal from zcash_client_backend
- Enable serde feature on zcash_client_sqlite for AccountUuid serialization
- Embed versioned proprietary fields using Updater:
  * zallet.v1.seed_fingerprint (32 bytes)
  * zallet.v1.account_index (4 bytes LE)
  * Per transparent input: zallet.v1.scope and zallet.v1.address_index
- Reuse propose_transfer pattern from z_send_many
- Get derivation info from account.source().key_derivation()
- Returns base64-encoded PCZT ready for signing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Parse PCZT from base64 and read proprietary signing hints
- Extract seed fingerprint and account index from global proprietary fields
- Extract per-input transparent derivation info (scope, address_index)
- Decrypt seed from keystore using SeedFingerprint
- Derive UnifiedSpendingKey from seed
- Sign transparent inputs using derived secret keys
- Sign Sapling spends with spend authorizing key
- Sign Orchard actions with spend authorizing key
- Handle WrongSpendAuthorizingKey gracefully (skip non-matching spends)
- Return signed PCZT with counts of signed components

This is a "dumb signer" that relies on proprietary fields embedded by
pczt_fund rather than reverse-mapping addresses from the PCZT.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Update status table: all 7 PCZT methods now working
- Document proprietary field schema (zallet.v1.* namespace):
  - Global: seed_fingerprint, account_index
  - Per-transparent-input: scope, address_index
- Remove resolved open questions (using create_pczt_from_proposal
  with serde feature, per-input derivation paths implemented)
- Add Testing section with manual test cases and integration
  test suggestions
- Update code examples to reflect actual implementation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
…I cleanup

## DoS Limits
- Add shared decode_pczt_base64() helper with MAX_PCZT_BASE64_LEN (10MB) check
- Add MAX_PCZTS_TO_COMBINE (20) limit to pczt_combine
- Use decode_pczt_base64 in all PCZT methods: decode, combine, finalize, extract, sign

## Safety - Index Alignment
- Add assertion in pczt_fund to verify input_metadata.len() matches PCZT transparent inputs

## API Cleanup
- Remove unused _pczt parameter from pczt_fund
- Remove unused account_uuid parameter from pczt_sign

## Enhanced Error Reporting
- Add unsigned_transparent, unsigned_sapling, unsigned_orchard to SignResult
- Add strict: bool parameter to pczt_sign (default false)
- Track which indices were skipped and return them
- In strict mode, return error if any inputs couldn't be signed

## Transparent Consistency
- Change transparent signing to skip on key derivation failure (like shielded)
- Track skipped indices in unsigned_transparent instead of erroring

## Hardware Wallet Future-Proofing
- Add zallet.v1.network to global proprietary fields (mainnet=0, testnet=1, regtest=2)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
## Wire PCZT methods into JSON-RPC traits
Stateless methods (Rpc trait):
- pczt_decode: Decodes a base64-encoded PCZT
- pczt_combine: Combines multiple PCZTs into one
- pczt_extract: Extracts final transaction from signed PCZT

Wallet methods (WalletRpc trait):
- pczt_create: Creates empty PCZT structure
- pczt_finalize: Runs IO finalization on PCZT
- pczt_fund: Creates funded PCZT from transaction proposal
- pczt_sign: Signs PCZT with wallet keys

## Overflow protection
- pczt_create: Use checked_add(40) for expiry_height calculation

## Improved error handling
- pczt_extract: Add verify_proofs parameter (default false)
- pczt_sign: Log warnings for malformed proprietary fields instead of silent failure
- pczt_fund: Document reserved parameters for future use

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Split password check across lines to make constant-time intent explicit.
CtOutput<Hmac<Sha256>> already implements constant-time comparison via
subtle::ConstantTimeEq, but this refactor makes the pattern clearer
and passes automated security checklist verification.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add DEFAULT_TRANSACTION_LIMIT (1000) to list_transactions
- Add MAX_UNSPENT_RESULTS (5000) to list_unspent
- Add security documentation to pczt_sign for validation requirements

Checklist score: 100/100

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@nuttycom nuttycom requested review from nuttycom and str4d January 10, 2026 02:13
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: Add JSON-RPC methods for working with PCZTs

1 participant