Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions zcash_client_backend/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ workspace.
- `Note::receiver`
- `impl From<sapling_crypto::Note> for Note`
- `impl From<orchard::Note> for Note`
- Coin tracking for ZIP 48 multisig accounts (no spend ability)
- Create `zip-48` feature
- `zcash_client_backend::data_api::AccountSource::Zip48`
- `zcash_client_backend::data_api::WalletWrite::import_account_zip48_multisig()`
- `zcash_client_backend::data_api::WalletWrite::get_next_zip48_multisig_address()`
- `zcash_client_backend::data_api::WalletWrite::get_zip48_multisig_address_for_index()`
- ZIP 48 tests in `zcash_client_backend::data_api::testing::transparent`

### Changed
- `zcash_client_backend::data_api::wallet::create_proposed_transactions` now takes
Expand Down
21 changes: 17 additions & 4 deletions zcash_client_backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -187,12 +187,25 @@ transparent-inputs = [
"zcash_primitives/transparent-inputs",
]

# Enables operations that permit arbitrary transparent pubkeys to be associated
# with an account in the wallet. Applications that rely on this feature must
# maintain spending keys corresponding to all imported pubkeys for any account
# from which it permits spending.
## Enables operations that permit arbitrary transparent pubkeys to be associated
## with an account in the wallet.
##
## Applications that enable this feature **MUST** also enable the
## `transparent-inputs` feature of the crate they are using that implements the
## `zcash_client_backend` traits.
##
## Applications that rely on this feature must maintain spending keys
## corresponding to all imported pubkeys for any account from which it permits
## spending.
transparent-key-import = ["transparent-inputs"]

## Enables tracking of ZIP 48 transparent multisig wallets.
##
## Applications that enable this feature **MUST** also enable the
## `transparent-inputs` feature of the crate they are using that implements the
## `zcash_client_backend` traits.
zip-48 = ["transparent-inputs"]

## Enables receiving and spending Orchard funds.
orchard = ["dep:orchard", "dep:pasta_curves", "zcash_keys/orchard"]

Expand Down
87 changes: 87 additions & 0 deletions zcash_client_backend/src/data_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,10 @@ pub enum AccountSource {
purpose: AccountPurpose,
key_source: Option<String>,
},

/// A ZIP 48 transparent multisig account.
#[cfg(feature = "zip-48")]
Zip48,
}

impl AccountSource {
Expand All @@ -500,6 +504,8 @@ impl AccountSource {
purpose: AccountPurpose::Spending { derivation },
..
} => derivation.as_ref(),
#[cfg(feature = "zip-48")]
AccountSource::Zip48 => None,
_ => None,
}
}
Expand All @@ -509,6 +515,8 @@ impl AccountSource {
match self {
AccountSource::Derived { key_source, .. } => key_source.as_ref().map(|s| s.as_str()),
AccountSource::Imported { key_source, .. } => key_source.as_ref().map(|s| s.as_str()),
#[cfg(feature = "zip-48")]
AccountSource::Zip48 => None,
}
}
}
Expand Down Expand Up @@ -549,6 +557,8 @@ pub trait Account {
derivation: Some(derivation.clone()),
},
AccountSource::Imported { purpose, .. } => purpose.clone(),
#[cfg(feature = "zip-48")]
AccountSource::Zip48 => AccountPurpose::ViewOnly,
}
}

Expand Down Expand Up @@ -2744,6 +2754,10 @@ impl AccountBirthday {
/// - [`WalletWrite::create_account`]
/// - [`WalletWrite::import_account_hd`]
/// - [`WalletWrite::import_account_ufvk`]
#[cfg_attr(
feature = "zip-48",
doc = "/// - [`WalletWrite::import_account_zip48_multisig`]"
)]
///
/// All of these methods take an [`AccountBirthday`]. The birthday height is defined as
/// the minimum block height that will be scanned for funds belonging to the wallet. If
Expand Down Expand Up @@ -2950,6 +2964,29 @@ pub trait WalletWrite: WalletRead {
key_source: Option<&str>,
) -> Result<Self::Account, Self::Error>;

/// Imports a ZIP 48 transparent multisig wallet for tracking.
///
/// This creates a new account of kind `account_kind = 2` (ZIP 48 multisig) with the
/// provided full viewing key. The account can track received funds and derive addresses.
///
/// # Parameters
/// - `name`: A human-readable name for the account.
/// - `fvk`: The ZIP 48 full viewing key containing threshold and participant public keys.
/// - `birthday`: The account birthday, used for determining scan start height.
///
/// Returns details about the imported account.
#[cfg(feature = "zip-48")]
fn import_account_zip48_multisig(
&mut self,
_name: &str,
_fvk: &transparent::zip48::FullViewingKey,
_birthday: &AccountBirthday,
) -> Result<Self::Account, Self::Error> {
unimplemented!(
"WalletWrite::import_account_zip48_multisig must be overridden for wallets to use the `zip-48` feature"
)
}

/// Deletes the specified account, and all transactions that exclusively involve it, from the
/// wallet database.
///
Expand Down Expand Up @@ -3032,6 +3069,56 @@ pub trait WalletWrite: WalletRead {
request: UnifiedAddressRequest,
) -> Result<Option<UnifiedAddress>, Self::Error>;

/// Generates, persists, and marks as exposed the next available address for a ZIP 48
/// multisig account.
///
/// The address and its redeem script are derived from the account's full viewing key
/// at the next available index for the specified scope. The redeem script is stored
/// alongside the address for later use in transaction construction.
///
/// # Parameters
/// - `account`: The identifier for the ZIP 48 multisig account.
/// - `scope`: The derivation scope (`External` for receiving, `Internal` for change).
///
/// Returns `Ok(None)` if the account identifier does not correspond to a known
/// ZIP 48 multisig account.
#[cfg(feature = "zip-48")]
fn get_next_zip48_multisig_address(
&mut self,
_account: Self::AccountId,
_scope: zip32::Scope,
) -> Result<Option<(TransparentAddress, TransparentAddressMetadata)>, Self::Error> {
unimplemented!(
"WalletWrite::get_next_zip48_multisig_address must be overridden for wallets to use the `zip-48` feature"
)
}

/// Generates, persists, and marks as exposed an address at a specific index for a ZIP 48
/// multisig account.
///
/// This method allows generating addresses at specific indices, which may be useful for
/// wallet recovery or coordinating with external systems.
///
/// # Parameters
/// - `account`: The identifier for the ZIP 48 multisig account.
/// - `scope`: The derivation scope (`External` for receiving, `Internal` for change).
/// - `address_index`: The specific index at which to derive the address.
///
/// # Warning
/// If the chosen index is outside the wallet's gap limit, funds sent to this address
/// may not be discovered on recovery from the viewing key alone.
#[cfg(feature = "zip-48")]
fn get_zip48_multisig_address_for_index(
&mut self,
_account: Self::AccountId,
_scope: zip32::Scope,
_address_index: NonHardenedChildIndex,
) -> Result<Option<(TransparentAddress, TransparentAddressMetadata)>, Self::Error> {
unimplemented!(
"WalletWrite::get_zip48_multisig_address_for_index must be overridden for wallets to use the `zip-48` feature"
)
}

/// Updates the wallet's view of the blockchain.
///
/// This method is used to provide the wallet with information about the state of the
Expand Down
Loading
Loading