From a98010a6841659c1a7cabefabee123c4e583a9fe Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 6 Jan 2022 22:33:17 +0800 Subject: [PATCH 1/8] Derive Sapling internal spending key. Co-authored-by: Daira Hopwood --- zcash_primitives/src/zip32.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/zcash_primitives/src/zip32.rs b/zcash_primitives/src/zip32.rs index 406fb9271..3bf7d2328 100644 --- a/zcash_primitives/src/zip32.rs +++ b/zcash_primitives/src/zip32.rs @@ -6,6 +6,7 @@ use aes::Aes256; use blake2b_simd::Params as Blake2bParams; use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt}; use fpe::ff1::{BinaryNumeralString, FF1}; +use std::convert::TryInto; use std::ops::AddAssign; use crate::{ @@ -20,6 +21,7 @@ use crate::sapling::keys::{ pub const ZIP32_SAPLING_MASTER_PERSONALIZATION: &[u8; 16] = b"ZcashIP32Sapling"; pub const ZIP32_SAPLING_FVFP_PERSONALIZATION: &[u8; 16] = b"ZcashSaplingFVFP"; +pub const ZIP32_SAPLING_INT_PERSONALIZATION: &[u8; 16] = b"Zcash_SaplingInt"; // Common helper functions @@ -409,6 +411,35 @@ impl ExtendedSpendingKey { pub fn default_address(&self) -> (DiversifierIndex, PaymentAddress) { ExtendedFullViewingKey::from(self).default_address() } + + pub fn derive_internal(&self) -> Self { + let i = { + let fvk = FullViewingKey::from_expanded_spending_key(&self.expsk); + Blake2bParams::new() + .hash_length(64) + .personal(crate::zip32::ZIP32_SAPLING_INT_PERSONALIZATION) + .hash(&fvk.to_bytes()) + }; + let i_nsk = jubjub::Fr::from_bytes_wide(prf_expand(i.as_bytes(), &[0x17]).as_array()); + let r = prf_expand(i.as_bytes(), &[0x18]); + let r = r.as_bytes(); + let nsk_internal = i_nsk + self.expsk.nsk; + let dk_internal = DiversifierKey(r[..32].try_into().unwrap()); + let ovk_internal = OutgoingViewingKey(r[32..].try_into().unwrap()); + + ExtendedSpendingKey { + depth: self.depth, + parent_fvk_tag: self.parent_fvk_tag, + child_index: self.child_index, + chain_code: self.chain_code, + expsk: ExpandedSpendingKey { + ask: self.expsk.ask, + nsk: nsk_internal, + ovk: ovk_internal, + }, + dk: dk_internal, + } + } } impl<'a> From<&'a ExtendedSpendingKey> for ExtendedFullViewingKey { From f6f5096ae4a1199cd4b19534f955badc0e570994 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 6 Jan 2022 22:34:03 +0800 Subject: [PATCH 2/8] Derive Sapling internal full viewing key. Co-authored-by: Daira Hopwood --- zcash_primitives/src/zip32.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/zcash_primitives/src/zip32.rs b/zcash_primitives/src/zip32.rs index 3bf7d2328..9753f31e7 100644 --- a/zcash_primitives/src/zip32.rs +++ b/zcash_primitives/src/zip32.rs @@ -544,6 +544,35 @@ impl ExtendedFullViewingKey { pub fn default_address(&self) -> (DiversifierIndex, PaymentAddress) { sapling_default_address(&self.fvk, &self.dk) } + + pub fn derive_internal(&self) -> Self { + let i = Blake2bParams::new() + .hash_length(64) + .personal(crate::zip32::ZIP32_SAPLING_INT_PERSONALIZATION) + .hash(&self.fvk.to_bytes()); + let i_nsk = jubjub::Fr::from_bytes_wide(prf_expand(i.as_bytes(), &[0x17]).as_array()); + let r = prf_expand(i.as_bytes(), &[0x18]); + let r = r.as_bytes(); + // PROOF_GENERATION_KEY_GENERATOR = \mathcal{H}^Sapling + let nk_internal = PROOF_GENERATION_KEY_GENERATOR * i_nsk + self.fvk.vk.nk; + let dk_internal = DiversifierKey(r[..32].try_into().unwrap()); + let ovk_internal = OutgoingViewingKey(r[32..].try_into().unwrap()); + + ExtendedFullViewingKey { + depth: self.depth, + parent_fvk_tag: self.parent_fvk_tag, + child_index: self.child_index, + chain_code: self.chain_code, + fvk: FullViewingKey { + vk: ViewingKey { + ak: self.fvk.vk.ak, + nk: nk_internal, + }, + ovk: ovk_internal, + }, + dk: dk_internal, + } + } } #[cfg(test)] From da3833f906c490631d87273e862db6047a1ea905 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Wed, 19 Jan 2022 18:18:22 -0700 Subject: [PATCH 3/8] Fix missing use of `dk` in derivation of sapling internal FVK. Also, factor out sapling internal fvk derivation so that it only requires (fvk, dk) since we may not have the full extfvk. --- zcash_primitives/src/zip32.rs | 62 ++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/zcash_primitives/src/zip32.rs b/zcash_primitives/src/zip32.rs index 9753f31e7..2cdd597d1 100644 --- a/zcash_primitives/src/zip32.rs +++ b/zcash_primitives/src/zip32.rs @@ -235,6 +235,41 @@ pub fn sapling_default_address( sapling_find_address(fvk, dk, DiversifierIndex::new()).unwrap() } +/// Returns the internal full viewing key and diversifier key +/// for the provided external fvk and dk +pub fn sapling_derive_internal_fvk( + fvk: &FullViewingKey, + dk: &DiversifierKey, +) -> (FullViewingKey, DiversifierKey) { + let i = { + let mut h = Blake2bParams::new() + .hash_length(64) + .personal(crate::zip32::ZIP32_SAPLING_INT_PERSONALIZATION) + .to_state(); + h.update(&fvk.to_bytes()); + h.update(&dk.0); + h.finalize() + }; + let i_nsk = jubjub::Fr::from_bytes_wide(prf_expand(i.as_bytes(), &[0x17]).as_array()); + let r = prf_expand(i.as_bytes(), &[0x18]); + let r = r.as_bytes(); + // PROOF_GENERATION_KEY_GENERATOR = \mathcal{H}^Sapling + let nk_internal = PROOF_GENERATION_KEY_GENERATOR * i_nsk + fvk.vk.nk; + let dk_internal = DiversifierKey(r[..32].try_into().unwrap()); + let ovk_internal = OutgoingViewingKey(r[32..].try_into().unwrap()); + + ( + FullViewingKey { + vk: ViewingKey { + ak: fvk.vk.ak, + nk: nk_internal, + }, + ovk: ovk_internal, + }, + dk_internal, + ) +} + /// A Sapling extended spending key #[derive(Clone)] pub struct ExtendedSpendingKey { @@ -415,10 +450,13 @@ impl ExtendedSpendingKey { pub fn derive_internal(&self) -> Self { let i = { let fvk = FullViewingKey::from_expanded_spending_key(&self.expsk); - Blake2bParams::new() + let mut h = Blake2bParams::new() .hash_length(64) .personal(crate::zip32::ZIP32_SAPLING_INT_PERSONALIZATION) - .hash(&fvk.to_bytes()) + .to_state(); + h.update(&fvk.to_bytes()); + h.update(&self.dk.0); + h.finalize() }; let i_nsk = jubjub::Fr::from_bytes_wide(prf_expand(i.as_bytes(), &[0x17]).as_array()); let r = prf_expand(i.as_bytes(), &[0x18]); @@ -546,30 +584,14 @@ impl ExtendedFullViewingKey { } pub fn derive_internal(&self) -> Self { - let i = Blake2bParams::new() - .hash_length(64) - .personal(crate::zip32::ZIP32_SAPLING_INT_PERSONALIZATION) - .hash(&self.fvk.to_bytes()); - let i_nsk = jubjub::Fr::from_bytes_wide(prf_expand(i.as_bytes(), &[0x17]).as_array()); - let r = prf_expand(i.as_bytes(), &[0x18]); - let r = r.as_bytes(); - // PROOF_GENERATION_KEY_GENERATOR = \mathcal{H}^Sapling - let nk_internal = PROOF_GENERATION_KEY_GENERATOR * i_nsk + self.fvk.vk.nk; - let dk_internal = DiversifierKey(r[..32].try_into().unwrap()); - let ovk_internal = OutgoingViewingKey(r[32..].try_into().unwrap()); + let (fvk_internal, dk_internal) = sapling_derive_internal_fvk(&self.fvk, &self.dk); ExtendedFullViewingKey { depth: self.depth, parent_fvk_tag: self.parent_fvk_tag, child_index: self.child_index, chain_code: self.chain_code, - fvk: FullViewingKey { - vk: ViewingKey { - ak: self.fvk.vk.ak, - nk: nk_internal, - }, - ovk: ovk_internal, - }, + fvk: fvk_internal, dk: dk_internal, } } From 82c1d87dcdaf520fdab9fd0dc662f3d66a9694e3 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Thu, 20 Jan 2022 18:08:01 -0700 Subject: [PATCH 4/8] Fix incorrect length of blake2b hashes for internal key derivation. Co-authored-by: str4d --- zcash_primitives/src/zip32.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zcash_primitives/src/zip32.rs b/zcash_primitives/src/zip32.rs index 2cdd597d1..d70b2595c 100644 --- a/zcash_primitives/src/zip32.rs +++ b/zcash_primitives/src/zip32.rs @@ -243,7 +243,7 @@ pub fn sapling_derive_internal_fvk( ) -> (FullViewingKey, DiversifierKey) { let i = { let mut h = Blake2bParams::new() - .hash_length(64) + .hash_length(32) .personal(crate::zip32::ZIP32_SAPLING_INT_PERSONALIZATION) .to_state(); h.update(&fvk.to_bytes()); @@ -451,7 +451,7 @@ impl ExtendedSpendingKey { let i = { let fvk = FullViewingKey::from_expanded_spending_key(&self.expsk); let mut h = Blake2bParams::new() - .hash_length(64) + .hash_length(32) .personal(crate::zip32::ZIP32_SAPLING_INT_PERSONALIZATION) .to_state(); h.update(&fvk.to_bytes()); From eb80138cf9cb1fab1bdb1006cdd5013068d580f8 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Fri, 21 Jan 2022 10:49:17 +0800 Subject: [PATCH 5/8] Document new APIs for deriving internal keys. --- zcash_primitives/src/zip32.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/zcash_primitives/src/zip32.rs b/zcash_primitives/src/zip32.rs index d70b2595c..4a4b85ad8 100644 --- a/zcash_primitives/src/zip32.rs +++ b/zcash_primitives/src/zip32.rs @@ -236,7 +236,10 @@ pub fn sapling_default_address( } /// Returns the internal full viewing key and diversifier key -/// for the provided external fvk and dk +/// for the provided external FVK = (ak, nk, ovk) and dk encoded +/// in a [Unified FVK]. +/// +/// [Unified FVK]: https://zips.z.cash/zip-0316#encoding-of-unified-full-incoming-viewing-keys pub fn sapling_derive_internal_fvk( fvk: &FullViewingKey, dk: &DiversifierKey, @@ -447,6 +450,9 @@ impl ExtendedSpendingKey { ExtendedFullViewingKey::from(self).default_address() } + /// Derives an internal spending key given an external spending key. + /// + /// Specified in [ZIP 32](https://zips.z.cash/zip-0032#deriving-a-sapling-internal-spending-key). pub fn derive_internal(&self) -> Self { let i = { let fvk = FullViewingKey::from_expanded_spending_key(&self.expsk); @@ -583,6 +589,12 @@ impl ExtendedFullViewingKey { sapling_default_address(&self.fvk, &self.dk) } + /// Derives an internal full viewing key used for internal operations such + /// as change and auto-shielding. The internal FVK has the same spend authority + /// (the private key corresponding to ak) as the original, but viewing authority + /// only for internal transfers. + /// + /// Specified in [ZIP 32](https://zips.z.cash/zip-0032#deriving-a-sapling-internal-full-viewing-key). pub fn derive_internal(&self) -> Self { let (fvk_internal, dk_internal) = sapling_derive_internal_fvk(&self.fvk, &self.dk); From 54cca8081b60ab4d50481caaad1618d00ede9317 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Sat, 22 Jan 2022 19:35:01 -0700 Subject: [PATCH 6/8] Update zcash_primitives/CHANGELOG.md with change key derivation methods. --- zcash_primitives/CHANGELOG.md | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/zcash_primitives/CHANGELOG.md b/zcash_primitives/CHANGELOG.md index 9158f694e..57c35c4c9 100644 --- a/zcash_primitives/CHANGELOG.md +++ b/zcash_primitives/CHANGELOG.md @@ -31,8 +31,8 @@ and this library adheres to Rust's notion of Orchard). This type makes it possible to encode a type-safe state machine for the application of authorizing data to a transaction; implementations of this trait represent different states of the authorization process. -- New bundle types under the `zcash_primitives::transaction` submodules, one for - each Zcash sub-protocol. These are now used instead of bare fields +- New bundle types under the `zcash_primitives::transaction` submodules, one for + each Zcash sub-protocol. These are now used instead of bare fields within the `TransactionData` type. - `components::sapling::Bundle` bundle of Sapling transaction elements. This new struct is parameterized by a @@ -58,7 +58,7 @@ and this library adheres to Rust's notion of diversifier index space, whereas `sapling_diversifier` just attempts to use the provided diversifier index and returns `None` if it does not produce a valid diversifier. -- `zcash_primitives::zip32::DiversifierKey::diversifier` has been renamed to +- `zcash_primitives::zip32::DiversifierKey::diversifier` has been renamed to `find_diversifier` and the `diversifier` method has new semantics. `find_diversifier` searches the diversifier index space to find a diversifier index which produces a valid diversifier, whereas `diversifier` just attempts @@ -71,6 +71,23 @@ and this library adheres to Rust's notion of just attempts to create an address corresponding to the diversifier derived from the provided diversifier index and returns `None` if the provided index does not produce a valid diversifier. +- `zcash_primitives::zip32::ExtendedSpendingKey.derive_internal` has been + added to facilitate the derivation of an internal (change) spending key. + This spending key can be used to spend change sent to an internal address + corresponding to the associated full viewing key as specified in + [ZIP 316](https://zips.z.cash/zip-0316#encoding-of-unified-full-incoming-viewing-keys).. +- `zcash_primitives::zip32::ExtendedFullViewingKey.derive_internal` has been + added to facilitate the derivation of an internal (change) spending key. + This spending key can be used to spend change sent to an internal address + corresponding to the associated full viewing key as specified in + [ZIP 32](https://zips.z.cash/zip-0032#deriving-a-sapling-internal-spending-key). +- `zcash_primitives::zip32::sapling_derive_internal_fvk` provides the + internal implementation of `ExtendedFullViewingKey.derive_internal` + but does not require a complete extended full viewing key, just + the full viewing key and the diversifier key. In the future, this + function will likely be refactored to become a member function of + a new `DiversifiableFullViewingKey` type, which represents the ability + to derive IVKs, OVKs, and addresses, but not child viewing keys. ### Changed - MSRV is now 1.51.0. From e9ecb19143ea392e58372c49b6fc503de433a35f Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 1 Feb 2022 14:17:53 +0000 Subject: [PATCH 7/8] Restrict cargo to `protobuf >=2.20,<2.26` `protobuf 2.26.0` bumped its MSRV to 1.52.1, which is incompatible with our current MSRV. --- zcash_client_backend/Cargo.toml | 4 ++-- zcash_client_sqlite/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index e096cd4a7..572d9a180 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -24,7 +24,7 @@ jubjub = "0.8" nom = "7" percent-encoding = "2.1.0" proptest = { version = "1.0.0", optional = true } -protobuf = "2.20" +protobuf = ">=2.20,<2.26" # protobuf 2.26 bumped MSRV to 1.52.1 rand_core = "0.6" subtle = "2.2.3" time = "0.2" @@ -32,7 +32,7 @@ zcash_note_encryption = { version = "0.1", path = "../components/zcash_note_encr zcash_primitives = { version = "0.5", path = "../zcash_primitives" } [build-dependencies] -protobuf-codegen-pure = "2.20" +protobuf-codegen-pure = ">=2.20,<2.26" # protobuf 2.26 bumped MSRV to 1.52.1 [dev-dependencies] gumdrop = "0.8" diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index 469b58b0f..5f0aef86c 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -18,7 +18,7 @@ bs58 = { version = "0.4", features = ["check"] } ff = "0.11" group = "0.11" jubjub = "0.8" -protobuf = "2.20" +protobuf = ">=2.20,<2.26" # protobuf 2.26 bumped MSRV to 1.52.1 rand_core = "0.6" rusqlite = { version = "0.24", features = ["bundled", "time"] } time = "0.2" From 9940d275fb0361b71b9a2a2dc37864d94289569f Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 1 Feb 2022 07:34:49 -0700 Subject: [PATCH 8/8] Use clippy beta instead of nightly --- .github/workflows/ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cecf52870..9b18f582e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -105,8 +105,8 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} args: --all-features --all-targets -- -D warnings - clippy-nightly: - name: Clippy (nightly) + clippy-beta: + name: Clippy (beta) timeout-minutes: 30 runs-on: ubuntu-latest continue-on-error: true @@ -114,14 +114,14 @@ jobs: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: - toolchain: nightly + toolchain: beta components: clippy override: true - - name: Run Clippy (nightly) + - name: Run Clippy (beta) uses: actions-rs/clippy-check@v1 continue-on-error: true with: - name: Clippy (nightly) + name: Clippy (beta) token: ${{ secrets.GITHUB_TOKEN }} args: --all-features --all-targets -- -W clippy::all