Skip to content

feat(pczt): Add implementation of PCZT signing#4576

Merged
satoshiotomakan merged 3 commits intomasterfrom
feat/zcash-pczt
Nov 21, 2025
Merged

feat(pczt): Add implementation of PCZT signing#4576
satoshiotomakan merged 3 commits intomasterfrom
feat/zcash-pczt

Conversation

@satoshiotomakan
Copy link
Copy Markdown
Contributor

This pull request refactors the PSBT (Partially Signed Bitcoin Transaction) request handling across the UTXO-based chains, moving from a builder-based interface to a handler-based one. It introduces a new trait, PsbtRequestHandler, which consolidates PSBT parsing, updating, and serialization logic, and updates all relevant contexts and modules to use this new trait. The implementation for standard Bitcoin PSBT handling is moved and expanded, while redundant code is removed. These changes improve code clarity, extensibility, and unify PSBT handling logic.

Core PSBT API Refactoring

  • Replaced the PsbtRequestBuilder trait with a new PsbtRequestHandler trait in rust/chains/tw_bitcoin/src/modules/psbt_request/mod.rs, consolidating PSBT parsing (parse_request), updating (update_signed), and serialization (serialize_psbt) into one interface. The default implementation for unsupported chains is provided by NoPsbtRequestBuilder.
  • Updated all context implementations (tw_bitcoin, tw_bitcoincash, tw_groestlcoin, tw_decred, tw_komodo) to use PsbtRequestHandler instead of PsbtRequestBuilder, and added a Psbt associated type to the UtxoContext trait for better type safety and extensibility. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13]

Standard PSBT Handler Implementation

  • Renamed and refactored standard_psbt_request_builder.rs to standard_psbt_request_handler.rs, implementing the new PsbtRequestHandler trait with logic for parsing requests, updating signed PSBTs, and serializing them. The update logic previously found in psbt.rs is now encapsulated in the handler. [1] [2]

Codebase Cleanup

  • Removed the now-unnecessary psbt.rs file and its standalone PSBT update function, as this logic is now handled by the new trait implementation. [1] [2]

Module and API Updates

  • Updated all module usages and method calls throughout the codebase to use the new handler trait and method names (parse_request, update_signed, serialize_psbt) instead of the old builder and function names. This includes updates in the compiler, planner, and signer modules. [1] [2] [3] [4] [5] [6] [7] [8] [9]

These changes collectively modernize and unify PSBT handling, making future maintenance and extension much easier.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Nov 20, 2025

Binary size comparison

➡️ aarch64-apple-ios:

- 14.18 MB
+ 14.31 MB 	 +136 KB

➡️ aarch64-apple-ios-sim:

- 14.18 MB
+ 14.31 MB 	 +137 KB

➡️ aarch64-linux-android:

- 18.67 MB
+ 18.87 MB 	 +205 KB

➡️ armv7-linux-androideabi:

- 15.62 MB
+ 15.80 MB 	 +188 KB

➡️ wasm32-unknown-emscripten:

- 13.31 MB
+ 13.46 MB 	 +150 KB

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request adds PCZT (Partially Collaborative ZCash Transaction) signing support for ZCash while refactoring the PSBT handling architecture across all UTXO-based chains from a builder-based pattern to a handler-based pattern.

Key Changes:

  • Introduces a new PsbtRequestHandler trait that consolidates PSBT/PCZT parsing, updating, and serialization
  • Implements PCZT format support for ZCash with deserialization, validation, transaction building, and signing
  • Adds a Psbt associated type to UtxoContext for better type safety across different chain implementations

Reviewed changes

Copilot reviewed 30 out of 31 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
rust/chains/tw_bitcoin/src/modules/psbt_request/mod.rs Refactored from PsbtRequestBuilder trait to PsbtRequestHandler trait with expanded methods for parsing, updating, and serializing
rust/chains/tw_bitcoin/src/modules/psbt_request/standard_psbt_request_handler.rs Renamed from standard_psbt_request_builder.rs and implements the new handler trait with PSBT update logic moved from psbt.rs
rust/chains/tw_bitcoin/src/modules/psbt.rs Removed - functionality moved into handler implementations
rust/chains/tw_bitcoin/src/modules/signer.rs Updated to use new PsbtRequestHandler methods (parse_request, update_signed, serialize_psbt)
rust/chains/tw_bitcoin/src/modules/compiler.rs Updated to use PsbtRequestHandler::parse_request instead of PsbtRequestBuilder::build
rust/chains/tw_bitcoin/src/modules/planner/psbt_planner.rs Updated to use new handler trait methods
rust/chains/tw_bitcoin/src/context.rs Updated BitcoinSigningContext trait to use PsbtRequestHandler and added Psbt associated type to StandardBitcoinContext
rust/chains/tw_bitcoincash/src/context.rs Updated to use StandardPsbtRequestHandler and added Psbt associated type
rust/chains/tw_groestlcoin/src/context.rs Updated to use StandardPsbtRequestHandler and added Psbt associated type
rust/chains/tw_decred/src/context.rs Added Psbt = () type and updated to use NoPsbtRequestBuilder (no PSBT support)
rust/chains/tw_komodo/src/context.rs Added Psbt = () type and updated to use NoPsbtRequestBuilder (no PSBT support)
rust/chains/tw_zcash/src/transaction/mod.rs Refactored transaction version constants: split TRANSACTION_VERSION_4 (now 4) and OVERWINTERED_FLAG (0x80000000)
rust/chains/tw_zcash/src/modules/transaction_builder.rs Updated to use overwintered_version() method that combines version with overwintered flag
rust/chains/tw_zcash/src/modules/signing_request.rs Removed unused transaction_version() method, updated to use new version handling
rust/chains/tw_zcash/src/modules/pczt/mod.rs New PCZT serialization/deserialization implementation with magic bytes and version handling
rust/chains/tw_zcash/src/modules/pczt/common.rs PCZT global fields structure with transaction metadata and modification flags
rust/chains/tw_zcash/src/modules/pczt/transparent.rs PCZT transparent bundle structures for inputs and outputs
rust/chains/tw_zcash/src/modules/pczt/sapling.rs PCZT sapling bundle structure (empty spends/outputs enforced)
rust/chains/tw_zcash/src/modules/pczt/orchard.rs PCZT orchard bundle structure (empty actions enforced)
rust/chains/tw_zcash/src/modules/pczt_request/zcash_pczt_request_handler.rs Implements PsbtRequestHandler for ZCash with PCZT parsing, validation, and transaction building
rust/chains/tw_zcash/src/modules/pczt_request/utxo_pczt.rs Converts PCZT inputs to internal UTXO representation with script parsing
rust/chains/tw_zcash/src/modules/pczt_request/output_pczt.rs Converts PCZT outputs to internal transaction output representation
rust/chains/tw_zcash/src/context.rs Updated to use PcztRequestHandler and added Psbt = pczt::Pczt type
rust/chains/tw_zcash/Cargo.toml Added dependencies: postcard, serde, serde_with for PCZT serialization
rust/frameworks/tw_utxo/src/context.rs Added Psbt associated type to UtxoContext trait
rust/tw_tests/tests/chains/common/bitcoin/mod.rs Added transaction_psbt_b64() helper function for base64-encoded PSBT/PCZT
rust/tw_tests/tests/chains/zcash/zcash_sign.rs Added tests for PCZT signing and unsupported version handling
rust/Cargo.lock Updated with new dependencies and their transitive dependencies
Comments suppressed due to low confidence (1)

rust/chains/tw_zcash/src/transaction/mod.rs:40

  • Documentation is outdated: The comment still references version 4 as 0x80000004, but the constant TRANSACTION_VERSION_4 has been changed to just 4. The overwintered flag (0x80000000) is now separate. Update the comment to reflect this change or clarify that the full version is 4 | OVERWINTERED_FLAG (which equals 0x80000004).
/// An overflow happens while converting to `i32` because 0x80000004 is greater than [`i32::MAX`].
/// However, the value will be serialized correctly.
pub const TRANSACTION_VERSION_4: u32 = 4;
pub const OVERWINTERED_FLAG: u32 = 0x80000000_u32;
pub const TRANSACTION_VERSION_GROUP_ID: u32 = 0x892F2085;
/// See https://github.com/zcash/zips/blob/main/zips/zip-0253.md#nu6-deployment CONSENSUS_BRANCH_ID section
pub const NU6_BRANCH_ID: H32 = H32::from_array([0x55, 0x10, 0xe7, 0xc8]);

const SEGWIT_NOT_SUPPORTED: bool = false;
const SAPLING_SPENDING_LEN: usize = 0;
const SAPLING_OUTPUTS_LEN: usize = 0;
const JOIN_SPLITS_LEN: usize = 0;

/// Transparent ZCash transaction (transparent).
/// https://github.com/zcash/zips/blob/998a97f2a1e5686e0d5c57f399a08b4daf100f8e/zips/zip-0243.rst
/// https://github.com/zcash/zcash/blob/a3435336b0c561799ac6805a27993eca3f9656df/src/primitives/transaction.h#L454
#[derive(Clone, Debug)]
pub struct ZcashTransaction {
    /// Transaction version.
    /// Currently, version 4 (0x80000004) is supported only.
    pub version: u32,
    // If transaction version is 4 (0x80000004), version group ID is 0x892F2085.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@satoshiotomakan satoshiotomakan merged commit 1fe9c73 into master Nov 21, 2025
15 checks passed
@satoshiotomakan satoshiotomakan deleted the feat/zcash-pczt branch November 21, 2025 15:56
sergei-boiko-trustwallet added a commit that referenced this pull request Jan 14, 2026
* fiux(ci): Use `iPhone 16` in ios-release (#4493)

* Throw exception rather than SIGSEGV (#4443)

* Throw exception rather than SIGSEGV

* Fix test cases

* Add NULL checker for exceptionClass

* feat(Plasma): Add Plasma Mainnet (#4499)

* feat(plasma): Add Plasma Mainnet

* feat(plasma): Add mobile tests

* [Solana]: Adds ability to transfer tokens to the feepayer (#4503)

* Initial setup

* Minor updates

* Restructure

* fix(barz): Replace u32 parameters with i32 (#4504)

* Disallow to use unsigned integer parameters within class methods

* fix(data): Allocate empty array on `tw_data_create_with_bytes` if data is NULL (#4508)

* fix(data): Allocate empty array on `tw_data_create_with_bytes` if data is NULL

* fix(data): Add TWData test

* feat(Biz): Add helper functions to support `BizPasskeySessionAccount` (#4516)

* feat(biz-passkey): Add `TWBarzEncodeRegisterSessionCall` FFI

* feat(biz-passkey): Add `TWBarzEncodeRemoveSessionCall` FFI

* feat(biz-passkey): Add `TWBarzEncodePasskeySessionNonce` FFI

* feat(biz-passkey): Add `TWBarzEncodeExecuteWithPasskeySessionCall` FFI

* refactor(barz): Move some functions to TWBiz and TWEip7702 modules

* feat(biz): Add WebAuthn

* feat(biz): Add `TWWebAuthnGetMessageHash` and `TWWebAuthnGetFormattedSignature`

* feat(biz): Fix C++ and Mobile

* Rename `TWWebAuthn` module to `TWWebAuthnSolidity`

* feat(biz): Adjust `executeWithPasskeySession` arguments

* feat(biz): Fix fmt

* feat(biz): Add Biz Android tests

* feat(biz): Add final Biz Android test

* feat(biz): Fix lints

* feat(biz): Minor change

* feat(biz): fmt

* feat(solana): Add `SolanaTransaction.insertTransferInstruction()` (#4523)

* feat(solana): Add `transfer_to_fee_payer` instruction

* feat(solana): Add `tw_solana_transaction_insert_transfer_instruction` FFI

* feat(solana): Add a note comment

* feat(solana): Add one more comment

* feat(zcash): Add support for TEX address (#4527)

* feat(zcash): Add `TexAddress` in Rust

* feat(zcash): Add successfully broadcasted transaction test

* feat(zcash): Add successfully broadcasted transaction C++ test

* feat(zcash): Fix C++ tests

* feat(zcash): Reuse code in `TexAddress::isValid`

* feat(zcash): Fix `TexAddress:isValid`

* [Solana]: Renames fee recipient token account for clarity (#4528)

* [Solana]: Renames fee recipient token account for clarity

* Fix typo

* Feature: Add trade and secured fields to THORChain Asset (#4500)

* my tracked commit

* MsgInstantiateContract

* revert formatting

* test

* Tests fixes and broadcast

* cargo fmt

* remove unused

* feat: add trade and secured fields to Thorchain Asset

- Add trade and secured boolean fields to Asset proto message
- Update ThorchainAsset struct in Rust with new fields
- Update asset initialization in tx_builder.rs
- Matches Thorchain blockchain Asset struct definition

---------

Co-authored-by: gupnik <mail.guptanikhil@gmail.com>

* feat(kusama-asset-hub): Add ability to force to use `ChargeAssetTxPayment` (#4541)

* feat(kusama-asset-hub): Add ability to force to use `ChargeAssetTxPayment` for native tip

* feat(kusama-asset-hub): Add a unit test

* feat(monad): Add Monad testnet (#4543)

* feat(monad): Update Monad to mainnet (#4550)

* TODO update explorer

* feat(monad): Update block explorer (#4553)

* feat(monad): Update explorer

* feat(monad): Update explorer schema

* Fix incorrect value of NO_PAD constant (#4487)

Co-authored-by: Sergei Boiko <127754187+satoshiotomakan@users.noreply.github.com>

* fix(public-key): Fix `PublicKey::verify` by adding signature length validation (#4565)

* fix(public-key): Fix `PublicKey::verify` by adding signature length validation

* fix(public-key): Fix PR comments

* Fix Reveal Operations for Seoul Protocol (#4557)

* Since the Seoul protocol, reveal operations require the presence of a boolean for whether or not the proof field is present. This is required when manually forging bytes. Added.

* Fix TezosTests.swift

* Fix TezosTests.swift

* Update tests

* Update tests

* Fix TezosCompiler.CompileWithSignatures

* Fix TezosCompiler.CompileWithSignatures

* fix(tezos): Fix Tezos compile test

* fix(tezos): Fix Tezos signing test

* fix(tezos): Add mainnet test transaction

---------

Co-authored-by: gupnik <nikhil.g@trustwallet.com>
Co-authored-by: Sergei Boiko <satoshiotomakan8@gmail.com>

* fix(public-key): Check zilliqa schnorr signature size (#4571)

* fix(public-key): Check zilliqa schnorr signature size

* Replace assert macros with proper checks

* fix(public-key): Address a PR comment

* fix(public-key): Address a PR comments

* fix(public-key): Fix a bug in PublicKey constructor

* feat(pczt): Add implementation of PCZT signing (#4576)

* feat(pczt): Add implementation of PCZT signing

* feat(pczt): Add a successfully broadcasted tx test

* feat(pczt): Address PR comments

* fix(webauthn): Fix buffer out of bounds (#4577)

* fix(webauthn): Fix buffer out of bounds

* fix(webauthn): Fix `TWWebAuthnGetRSValues` implicit Data conversion

* fix(webauthn): Extra checks for algorithm, and public key parts

* fix(webauthn): Test common buffer overflows

* fix(webauthn): Check `crv` and `kty` parameters

* fix(private-key): Add PrivateKey Data move constructor

* chore(noexcept): Remove wrong noexcept

* fix(private-key): Zeroize memory on move `operator=`

* fix(private-key): Avoid cleaning private key if self-assign

* fix(aes): Add iv size validation (#4580)

* fix(aes): Add iv size validation

* fix(aes): Add unit tests

* Update Dockerfile to install cbindgen with --locked and provide explicit links to clang binaries (#4568)

* install cbindgen with --locked

* add explicit links to clang/clang++ since CMake will not use the ENV

---------

Co-authored-by: Sergei Boiko <127754187+satoshiotomakan@users.noreply.github.com>

* feat(stable-account): Add `TWDerivationSmartChainStableAccount` derivation type (#4585)

* fix(overflow): Fix integer overflows in `BinaryEncoding` and `Pactus` (#4592)

* fix(binary-coding): Avoid unsigned integer overflow

* fix(pactus): Avoid unsigned integer overflow

* feat(tron): Add `raw_data_hex` parameter in JSON transaction representation (#4593)

* feat(tron): Add `raw_data_hex` parameter in JSON transaction representation

* feat(tron): Fix iOS test

* feat(biz): Add `Biz.signExecuteWithSignatureCall` and `BizPasskeySession.signExecuteWithSignatureCall` (#4594)

* feat(biz): Add `Biz.SignExecuteWithSignatureCall`

* Add new `BizPasskeySession` module

* feat(biz): Add `Biz.SignExecuteWithSignatureCall`

* Finish `BizPasskeySession` module separation

* feat(biz): Fix android tests

* feat(biz): Address PR review comments

* chore(codeowners): Add onchain-corex-enabling team to CODEOWNERS (#4600)

* fix(ethereum-abi): Fix Ethereum ABI type parsing recursion + chore actions and toolchain (#4597)

* fix(ethereum-abi): Fix Ethereum ABI type parsing recursion

* fix(ethereum-abi): Freeze community github actions

* chore(rust): Bump rust toolchain version to nightly-2025-12-11

* chore(wasm): Try to update emsdk to 4.0.22

* chore(rust): Decrease code coverage to 94.1 due to a recent toolchain update

* chore(rust): Freeze `actions` hashes

---------

Co-authored-by: Sergei Boiko <satoshiotomakan8@gmail.com>
Co-authored-by: Sergei <>

* chore(actions): Replace unverified actions with GH scripts (#4604)

* chore(actions): Replace unverified actions with GH scripts

* chore(actions): Update rust toolchain in Dockerfile

* fix(bitcoin-v2): Fail if `max_amount_output` provided with `outputs` or `change_output` at a time (#4607)

* fix(bitcoin-v2): Fail if `max_amount_output` provided with `outputs` or `change_output` at a time

* fix(bitcoin-v2): PR notes

* fix(cbor): Fix Cbor Map with odd elements number decoding (#4610)

* fix(cbor): Fix Cbor Map with odd elements number decoding

* fix(cbor): Change test name

* chore(sync): Fix merge conflicts

Breaking changes:
* The following FFIs take i32 instead of u32 due to Kotlin bindings limitations: `tw_hd_node_create_with_seed`, `tw_hd_node_create_with_extended_private_key`, `tw_hd_node_derive_from_path`, `tw_hd_node_extended_private_key`, `tw_hd_node_extended_public_key`, `tw_hd_node_public_create_with_extended_public_key`, `tw_hd_node_public_derive_from_path`, `tw_mnemonic_generate`, `tw_mnemonic_get_word`, `tw_pbkdf2_hmac_sha512`, `tw_ecdsa_pubkey_hash`
* `crypto_scrypt` FFI is no longer marked as `tw_ffi` to avoid changing u32 and usize argument types to i32

* chore(sync): Increase test coverage

* Add Bitcoin PSBT compile test
* Add `PrivateKey.CopyAssignmentOperator` and `PrivateKey.MoveAssignmentOperator` tests
* Copy Rust TWMnemonic tests to C++
* Copy Rust Biz tests to C++
* Copy Rust BizPasskeySession tests to C++

* chore(sync): Push missing PSBT implementation + remove unused code

* chore(sync): Fix Biz android tests

---------

Co-authored-by: Sergei Boiko <127754187+satoshiotomakan@users.noreply.github.com>
Co-authored-by: 10gic <github10gic@proton.me>
Co-authored-by: gupnik <nikhil.g@trustwallet.com>
Co-authored-by: Enrique Souza <enriquesouza@live.com>
Co-authored-by: gupnik <mail.guptanikhil@gmail.com>
Co-authored-by: Corey Soreff <csoreff@gmail.com>
Co-authored-by: Sergei Boiko <satoshiotomakan8@gmail.com>
Co-authored-by: Joey Yandle <xoloki@gmail.com>
perpetua-engineering pushed a commit to perpetua-engineering/wallet-core that referenced this pull request Feb 25, 2026
* feat(pczt): Add implementation of PCZT signing

* feat(pczt): Add a successfully broadcasted tx test

* feat(pczt): Address PR comments
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.

2 participants