Merge pull request #1668 from zcash/pczt-improvements

PCZT improvements
This commit is contained in:
Jack Grigg 2024-12-19 13:59:50 +13:00 committed by GitHub
commit eee19d90c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 81 additions and 16 deletions

View File

@ -139,6 +139,9 @@ impl Signer {
/// Signs the Sapling spend at the given index with the given spend authorizing key. /// Signs the Sapling spend at the given index with the given spend authorizing key.
/// ///
/// Requires the spend's `proof_generation_key` field to be set (because the API does
/// not take an FVK).
///
/// It is the caller's responsibility to perform any semantic validity checks on the /// It is the caller's responsibility to perform any semantic validity checks on the
/// PCZT (for example, comfirming that the change amounts are correct) before calling /// PCZT (for example, comfirming that the change amounts are correct) before calling
/// this method. /// this method.
@ -179,6 +182,8 @@ impl Signer {
/// Signs the Orchard spend at the given index with the given spend authorizing key. /// Signs the Orchard spend at the given index with the given spend authorizing key.
/// ///
/// Requires the spend's `fvk` field to be set (because the API does not take an FVK).
///
/// It is the caller's responsibility to perform any semantic validity checks on the /// It is the caller's responsibility to perform any semantic validity checks on the
/// PCZT (for example, comfirming that the change amounts are correct) before calling /// PCZT (for example, comfirming that the change amounts are correct) before calling
/// this method. /// this method.

View File

@ -11,6 +11,9 @@ and this library adheres to Rust's notion of
- `zcash_transparent::keys::AccountPubKey::derive_pubkey_at_bip32_path` now - `zcash_transparent::keys::AccountPubKey::derive_pubkey_at_bip32_path` now
returns the correct result for valid paths instead of an error or panic. returns the correct result for valid paths instead of an error or panic.
### Added
- `zcash_transparent::pczt::Bip32Derivation::extract_bip_44_fields`
## [0.1.0] - 2024-12-16 ## [0.1.0] - 2024-12-16
The entries below are relative to the `zcash_primitives` crate as of the tag The entries below are relative to the `zcash_primitives` crate as of the tag

View File

@ -1,19 +1,20 @@
//! Transparent key components. //! Transparent key components.
use alloc::string::ToString; use bip32::ChildNumber;
use alloc::vec::Vec;
use bip32::{
ChildNumber, ExtendedKey, ExtendedKeyAttrs, ExtendedPrivateKey, ExtendedPublicKey, Prefix,
};
use secp256k1::PublicKey;
use sha2::{Digest, Sha256};
use subtle::{Choice, ConstantTimeEq}; use subtle::{Choice, ConstantTimeEq};
use zcash_protocol::consensus::{self, NetworkConstants}; #[cfg(feature = "transparent-inputs")]
use zcash_spec::PrfExpand; use {
use zip32::AccountId; crate::address::TransparentAddress,
alloc::string::ToString,
use crate::address::TransparentAddress; alloc::vec::Vec,
bip32::{ExtendedKey, ExtendedKeyAttrs, ExtendedPrivateKey, ExtendedPublicKey, Prefix},
secp256k1::PublicKey,
sha2::{Digest, Sha256},
zcash_protocol::consensus::{self, NetworkConstants},
zcash_spec::PrfExpand,
zip32::AccountId,
};
/// The scope of a transparent key. /// The scope of a transparent key.
/// ///
@ -123,8 +124,10 @@ impl From<NonHardenedChildIndex> for ChildNumber {
/// ///
/// [BIP44]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki /// [BIP44]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
#[cfg(feature = "transparent-inputs")]
pub struct AccountPrivKey(ExtendedPrivateKey<secp256k1::SecretKey>); pub struct AccountPrivKey(ExtendedPrivateKey<secp256k1::SecretKey>);
#[cfg(feature = "transparent-inputs")]
impl AccountPrivKey { impl AccountPrivKey {
/// Performs derivation of the extended private key for the BIP44 path: /// Performs derivation of the extended private key for the BIP44 path:
/// `m/44'/<coin_type>'/<account>'`. /// `m/44'/<coin_type>'/<account>'`.
@ -221,9 +224,11 @@ impl AccountPrivKey {
/// full viewing key. /// full viewing key.
/// ///
/// [BIP44]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki /// [BIP44]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
#[cfg(feature = "transparent-inputs")]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct AccountPubKey(ExtendedPublicKey<PublicKey>); pub struct AccountPubKey(ExtendedPublicKey<PublicKey>);
#[cfg(feature = "transparent-inputs")]
impl AccountPubKey { impl AccountPubKey {
/// Derives the BIP44 public key at the external "change level" path /// Derives the BIP44 public key at the external "change level" path
/// `m/44'/<coin_type>'/<account>'/0`. /// `m/44'/<coin_type>'/<account>'/0`.
@ -344,6 +349,7 @@ impl AccountPubKey {
} }
/// Derives the P2PKH transparent address corresponding to the given pubkey. /// Derives the P2PKH transparent address corresponding to the given pubkey.
#[cfg(feature = "transparent-inputs")]
#[deprecated(note = "This function will be removed from the public API in an upcoming refactor.")] #[deprecated(note = "This function will be removed from the public API in an upcoming refactor.")]
pub fn pubkey_to_address(pubkey: &secp256k1::PublicKey) -> TransparentAddress { pub fn pubkey_to_address(pubkey: &secp256k1::PublicKey) -> TransparentAddress {
TransparentAddress::PublicKeyHash( TransparentAddress::PublicKeyHash(
@ -351,6 +357,7 @@ pub fn pubkey_to_address(pubkey: &secp256k1::PublicKey) -> TransparentAddress {
) )
} }
#[cfg(feature = "transparent-inputs")]
pub(crate) mod private { pub(crate) mod private {
use super::TransparentKeyScope; use super::TransparentKeyScope;
use bip32::ExtendedPublicKey; use bip32::ExtendedPublicKey;
@ -377,6 +384,7 @@ pub(crate) mod private {
/// ///
/// [BIP32]: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki /// [BIP32]: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
/// [BIP44]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki /// [BIP44]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
#[cfg(feature = "transparent-inputs")]
pub trait IncomingViewingKey: private::SealedChangeLevelKey + core::marker::Sized { pub trait IncomingViewingKey: private::SealedChangeLevelKey + core::marker::Sized {
/// Derives a transparent address at the provided child index. /// Derives a transparent address at the provided child index.
#[allow(deprecated)] #[allow(deprecated)]
@ -438,9 +446,11 @@ pub trait IncomingViewingKey: private::SealedChangeLevelKey + core::marker::Size
/// This allows derivation of child addresses that may be provided to external parties. /// This allows derivation of child addresses that may be provided to external parties.
/// ///
/// [BIP44]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki /// [BIP44]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
#[cfg(feature = "transparent-inputs")]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct ExternalIvk(ExtendedPublicKey<PublicKey>); pub struct ExternalIvk(ExtendedPublicKey<PublicKey>);
#[cfg(feature = "transparent-inputs")]
impl private::SealedChangeLevelKey for ExternalIvk { impl private::SealedChangeLevelKey for ExternalIvk {
const SCOPE: TransparentKeyScope = TransparentKeyScope(0); const SCOPE: TransparentKeyScope = TransparentKeyScope(0);
@ -453,6 +463,7 @@ impl private::SealedChangeLevelKey for ExternalIvk {
} }
} }
#[cfg(feature = "transparent-inputs")]
impl IncomingViewingKey for ExternalIvk {} impl IncomingViewingKey for ExternalIvk {}
/// An incoming viewing key at the [BIP44] "internal" path /// An incoming viewing key at the [BIP44] "internal" path
@ -462,9 +473,11 @@ impl IncomingViewingKey for ExternalIvk {}
/// not be shared with external parties. /// not be shared with external parties.
/// ///
/// [BIP44]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki /// [BIP44]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
#[cfg(feature = "transparent-inputs")]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct InternalIvk(ExtendedPublicKey<PublicKey>); pub struct InternalIvk(ExtendedPublicKey<PublicKey>);
#[cfg(feature = "transparent-inputs")]
impl private::SealedChangeLevelKey for InternalIvk { impl private::SealedChangeLevelKey for InternalIvk {
const SCOPE: TransparentKeyScope = TransparentKeyScope(1); const SCOPE: TransparentKeyScope = TransparentKeyScope(1);
@ -477,12 +490,14 @@ impl private::SealedChangeLevelKey for InternalIvk {
} }
} }
#[cfg(feature = "transparent-inputs")]
impl IncomingViewingKey for InternalIvk {} impl IncomingViewingKey for InternalIvk {}
/// An incoming viewing key at the "ephemeral" path /// An incoming viewing key at the "ephemeral" path
/// `m/44'/<coin_type>'/<account>'/2`. /// `m/44'/<coin_type>'/<account>'/2`.
/// ///
/// This allows derivation of ephemeral addresses for use within the wallet. /// This allows derivation of ephemeral addresses for use within the wallet.
#[cfg(feature = "transparent-inputs")]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct EphemeralIvk(ExtendedPublicKey<PublicKey>); pub struct EphemeralIvk(ExtendedPublicKey<PublicKey>);

View File

@ -9,12 +9,10 @@
pub mod address; pub mod address;
pub mod builder; pub mod builder;
pub mod bundle; pub mod bundle;
pub mod keys;
pub mod pczt; pub mod pczt;
pub mod sighash; pub mod sighash;
#[cfg(feature = "transparent-inputs")]
pub mod keys;
#[cfg(test)] #[cfg(test)]
mod test_vectors; mod test_vectors;

View File

@ -8,7 +8,11 @@ use bip32::ChildNumber;
use getset::Getters; use getset::Getters;
use zcash_protocol::{value::Zatoshis, TxId}; use zcash_protocol::{value::Zatoshis, TxId};
use crate::{address::Script, sighash::SighashType}; use crate::{
address::Script,
keys::{NonHardenedChildIndex, TransparentKeyScope},
sighash::SighashType,
};
mod parse; mod parse;
pub use parse::ParseError; pub use parse::ParseError;
@ -230,3 +234,43 @@ pub struct Bip32Derivation {
/// The sequence of indices corresponding to the HD path. /// The sequence of indices corresponding to the HD path.
derivation_path: Vec<ChildNumber>, derivation_path: Vec<ChildNumber>,
} }
impl Bip32Derivation {
/// Extracts the BIP 44 account index, scope, and address index from this derivation
/// path.
///
/// Returns `None` if the seed fingerprints don't match, or if this is a non-standard
/// derivation path.
pub fn extract_bip_44_fields(
&self,
seed_fp: &zip32::fingerprint::SeedFingerprint,
expected_coin_type: ChildNumber,
) -> Option<(zip32::AccountId, TransparentKeyScope, NonHardenedChildIndex)> {
if self.seed_fingerprint == seed_fp.to_bytes() {
match &self.derivation_path[..] {
[purpose, coin_type, account_index, scope, address_index]
if purpose == &ChildNumber(44 | ChildNumber::HARDENED_FLAG)
&& coin_type.is_hardened()
&& coin_type == &expected_coin_type
&& account_index.is_hardened()
&& !scope.is_hardened()
&& !address_index.is_hardened() =>
{
let account_index = zip32::AccountId::try_from(account_index.index())
.expect("account_index is hardened");
let scope =
TransparentKeyScope::custom(scope.index()).expect("scope is not hardened");
let address_index = NonHardenedChildIndex::from_index(address_index.index())
.expect("address_index is not hardened");
Some((account_index, scope, address_index))
}
_ => None,
}
} else {
None
}
}
}